mirror of
https://github.com/Nan1t/NanoLimbo.git
synced 2025-07-22 08:42:28 +02:00
Compare commits
238 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a5b9e3510d | ||
|
155e6cc6c3 | ||
|
b99a533e1a | ||
|
94edb65dac | ||
|
48346762bf | ||
|
402484c838 | ||
|
32b75fb9d8 | ||
|
3f7c8bb1df | ||
|
e9b6c888bc | ||
|
6bbde1f578 | ||
|
f36f8dac6c | ||
|
f137495c4e | ||
|
04a646b271 | ||
|
cd42e91cd1 | ||
|
e3e12db006 | ||
|
a750bc50d2 | ||
|
e54d8d6ed3 | ||
|
f5684107c8 | ||
|
ebe9c19a05 | ||
|
72774fbff9 | ||
|
0489f883d1 | ||
|
6919420916 | ||
|
dfa6cf3862 | ||
|
93ad159833 | ||
|
f257426173 | ||
|
4fd0389356 | ||
|
23bcfc7b20 | ||
|
9dc7027a22 | ||
|
145d57679e | ||
|
ff84c8f564 | ||
|
b5605f8d12 | ||
|
5615ec2321 | ||
|
9490dc3ef2 | ||
|
7309ca3b41 | ||
|
dceb977a75 | ||
|
9ce3146045 | ||
|
fb15336b42 | ||
|
ddb27d421b | ||
|
b301617f99 | ||
|
e77a42ebc9 | ||
|
3ef5683222 | ||
|
064150a171 | ||
|
6c1f7e4e2f | ||
|
ab1844131b | ||
|
9558c2357b | ||
|
8d135c0804 | ||
|
0ddac89204 | ||
|
97e4b44920 | ||
|
dbeb7cc037 | ||
|
c96668f4d7 | ||
|
37813a1fed | ||
|
bbafbcc959 | ||
|
593e137b57 | ||
|
21b74e8093 | ||
|
2fdbd3e63a | ||
|
77a68efdc8 | ||
|
8210e61989 | ||
|
0af7a4c63d | ||
|
3c9bb88e19 | ||
|
05ef0435e5 | ||
|
04c1730da0 | ||
|
8fee9a4426 | ||
|
d5c61e8a8d | ||
|
f5003b1126 | ||
|
353533a72a | ||
|
68846b0e0d | ||
|
68ea7c020c | ||
|
b36decb3dd | ||
|
a327f7bc98 | ||
|
714ef11fa8 | ||
|
bc22bdd4ed | ||
|
5d08856bca | ||
|
af304da0f0 | ||
|
747da18b72 | ||
|
4c8668f222 | ||
|
699996e267 | ||
|
e7257894e3 | ||
|
b19fa9db37 | ||
|
d474498ef6 | ||
|
b221d88d67 | ||
|
90364e4f6b | ||
|
fa63d3fff9 | ||
|
b4daf6aa60 | ||
|
59d264d9b8 | ||
|
87debafbad | ||
|
613f6e75b2 | ||
|
e0e5a4ae56 | ||
|
c8a3284c04 | ||
|
e4f90b4de7 | ||
|
1f2558602c | ||
|
70d3b58bbf | ||
|
577b7a239a | ||
|
66ec1404fd | ||
|
f81a235982 | ||
|
ad644b956a | ||
|
2548479cf4 | ||
|
c79b668a37 | ||
|
f8299d4e17 | ||
|
1da0c8bc2c | ||
|
32ea23591f | ||
|
226fd32c6d | ||
|
a3d4966150 | ||
|
8d1dca1334 | ||
|
09cc1db851 | ||
|
5d33193c65 | ||
|
1db40eb6c4 | ||
|
272c37bb79 | ||
|
ac8eda05ad | ||
|
788c9ba4f2 | ||
|
32c95cc5cd | ||
|
b08599deda | ||
|
2cec5b8208 | ||
|
dc6f85f6b7 | ||
|
c4985c5b6b | ||
|
0a98b8257f | ||
|
7fb11d2ffb | ||
|
9269751ed1 | ||
|
6aea0392d4 | ||
|
5a2049520c | ||
|
e1445c006b | ||
|
3d166a137d | ||
|
021d0a18a5 | ||
|
f726a21e2c | ||
|
4c2ad07049 | ||
|
aba8a6e1ed | ||
|
0958611aea | ||
|
647997aa5e | ||
|
416878d160 | ||
|
04c6e1b037 | ||
|
ba0292eb07 | ||
|
5e1bf15fe1 | ||
|
188d2de0df | ||
|
e089fb5584 | ||
|
b77d9b0935 | ||
|
72cbf97019 | ||
|
c914a494e8 | ||
|
9670aeecbe | ||
|
970d5cfcd2 | ||
|
04a18a5386 | ||
|
040e61ab72 | ||
|
297c1fa722 | ||
|
018d7b1e6f | ||
|
aac321e182 | ||
|
be1fa775ec | ||
|
1c3cb9b77d | ||
|
864a84b9b6 | ||
|
9f3fa80eef | ||
|
26c8dbba20 | ||
|
645e5c07ac | ||
|
088570b28e | ||
|
cb02cc498b | ||
|
4bdf856363 | ||
|
1d75272c83 | ||
|
2e69580271 | ||
|
f1f5d68dd6 | ||
|
e6bcee3e73 | ||
|
d49a16ba35 | ||
|
fd3d67bc07 | ||
|
e6be30ba29 | ||
|
d0d4d8a918 | ||
|
6ed28c18e1 | ||
|
7e467615ee | ||
|
0d377ae0e3 | ||
|
22d8e4f6ad | ||
|
5a4889fb68 | ||
|
eea304767e | ||
|
63ebb4e4af | ||
|
b88263305b | ||
|
4ac087e391 | ||
|
f23ee893fa | ||
|
d2c2987111 | ||
|
bcfbb2a719 | ||
|
974e243402 | ||
|
27ec7af95c | ||
|
016794cba8 | ||
|
0dda88d649 | ||
|
f1341ddba6 | ||
|
6d2ff1a6b9 | ||
|
3dfa56e1c5 | ||
|
4c5439082c | ||
|
a05523799d | ||
|
1d200dcfd9 | ||
|
2dfb952d71 | ||
|
37e3a73d56 | ||
|
10cfd3e625 | ||
|
38300dbe1b | ||
|
44c9aa28cd | ||
|
4c44efce05 | ||
|
4c0f206afc | ||
|
225a77a740 | ||
|
207bf16b0c | ||
|
14fd8f094c | ||
|
c0b8c5d331 | ||
|
ebd6baf7b5 | ||
|
c6fcf0bf83 | ||
|
7694a69150 | ||
|
07ee12931f | ||
|
916b10e087 | ||
|
790a1b414b | ||
|
24ccfd9050 | ||
|
b08eea13e0 | ||
|
a439aa9847 | ||
|
1e4b4389d5 | ||
|
73f1045a76 | ||
|
e45834c3bc | ||
|
d7b5101d50 | ||
|
b84720ef79 | ||
|
0527937bf0 | ||
|
5af7b9e2fc | ||
|
e2f1317dc8 | ||
|
e69e0c06f7 | ||
|
e4c2c1f472 | ||
|
515fd392c3 | ||
|
aded28cfef | ||
|
7df4db9e10 | ||
|
a70a538ae5 | ||
|
315b47ca7b | ||
|
e931773ce8 | ||
|
cc5f92086a | ||
|
574261ef1e | ||
|
a08ef52c63 | ||
|
1ee11ffc66 | ||
|
bcf8e0ebf2 | ||
|
9659b4500a | ||
|
f490e00888 | ||
|
e191c8f90b | ||
|
62fbc0e8f2 | ||
|
ff5b605ee3 | ||
|
511ce43ada | ||
|
d170bcbee2 | ||
|
1c6da4ee0a | ||
|
5c94dcffea | ||
|
4507490c5a | ||
|
ac337fa45e | ||
|
8ba8760925 | ||
|
168faf9eea | ||
|
c170aed44c | ||
|
c445b07564 |
10
.gitignore
vendored
10
.gitignore
vendored
@ -1,7 +1,3 @@
|
|||||||
/.idea/
|
.idea
|
||||||
/.gradle/
|
.gradle
|
||||||
/gradle/
|
build
|
||||||
gradlew
|
|
||||||
gradlew.bat
|
|
||||||
build/
|
|
||||||
settings.yml
|
|
99
README.md
99
README.md
@ -1,66 +1,99 @@
|
|||||||
## NanoLimbo
|
## NanoLimbo
|
||||||
|
|
||||||
This is lightweight minecraft limbo server, written on Java with Netty.
|
This is a lightweight Minecraft limbo server, written in Java with Netty.
|
||||||
The main goal of the project is maximum simplicity with a minimum number of sent and processed packets.
|
The main goal of this project is maximum simplicity with a minimum number of sent and processed packets.
|
||||||
This limbo is empty, there are no ability to set schematic building since
|
The limbo is empty; there is no ability to set a schematic building since this is not necessary.
|
||||||
this is not necessary. You can send useful information in chat or BossBar.
|
You can send useful information via chat or boss bar.
|
||||||
|
|
||||||
No plugins, no logs. The server is fully clear. It only able keep a lot of players while the main server is down.
|
The server is fully clear. It is only able to keep a lot of players while the main server is down.
|
||||||
|
|
||||||
The general features:
|
General features:
|
||||||
* High performance. The server not saves and not cached any useless (for limbo) data.
|
* High performance. The server doesn't save or cache any useless (for limbo) data.
|
||||||
|
* Doesn't spawn threads per player. Use a fixed thread pool.
|
||||||
* Support for **BungeeCord** and **Velocity** info forwarding.
|
* Support for **BungeeCord** and **Velocity** info forwarding.
|
||||||
* All necessary data are configurable.
|
* Support for [BungeeGuard](https://www.spigotmc.org/resources/79601/) handshake format.
|
||||||
* Lightweight. App size less than **2MB.**
|
* Multiple versions support.
|
||||||
|
* Fully configurable.
|
||||||
|
* Lightweight. App size around **3MB**.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Protocol support
|
### Versions support
|
||||||
|
|
||||||
The server written based on `1.16.4` minecraft protocol. You can use `ViaVersion`
|
Symbol `X` means all minor versions.
|
||||||
or another protocol hack on your **proxy server** to achieve compatibility with other versions.
|
|
||||||
However, compatibility with other versions is in the plans, but not the nearest ones, since there
|
- [x] 1.7.X
|
||||||
is a way to do this using a plugin that is installed on most networks.
|
- [x] 1.8.X
|
||||||
|
- [x] 1.9.X
|
||||||
|
- [x] 1.10.X
|
||||||
|
- [x] 1.11.X
|
||||||
|
- [x] 1.12.X
|
||||||
|
- [x] 1.13.X
|
||||||
|
- [x] 1.14.X
|
||||||
|
- [x] 1.15.X
|
||||||
|
- [x] 1.16.X
|
||||||
|
- [x] 1.17.X
|
||||||
|
- [x] 1.18.X
|
||||||
|
- [x] 1.19.X
|
||||||
|
- [x] 1.20.X
|
||||||
|
- [x] 1.21
|
||||||
|
|
||||||
|
The server **doesn't** support snapshots.
|
||||||
|
|
||||||
### Commands
|
### Commands
|
||||||
|
|
||||||
There are no commands. To close server just run `Ctrl+C` in the terminal. It will be closed correctly.
|
* `help` - Show help message
|
||||||
|
* `conn` - Display number of connections
|
||||||
|
* `mem` - Display memory usage stats
|
||||||
|
* `stop` - Stop the server
|
||||||
|
|
||||||
|
Note that the server also will be closed correctly if you just press `Ctrl+C`.
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
|
Required software: JRE 11+
|
||||||
|
|
||||||
The installation process is simple.
|
The installation process is simple.
|
||||||
|
|
||||||
1. Download the latest version of program **[here](https://github.com/Nan1t/NanoLimbo/releases)**
|
1. Download the latest version of the program [**here**](https://github.com/Nan1t/NanoLimbo/releases).
|
||||||
2. Put jar file in the folder you want.
|
2. Put the jar file in the folder you want.
|
||||||
3. Create a start script as you did it for Bukkit/Spigot/etc. with command like this:
|
3. Create a start script as you did for Bukkit or BungeeCord, with a command like this:
|
||||||
`java -jar <FileName>.jar`
|
`java -jar NanoLimbo-<version>.jar`
|
||||||
4. The server will create `settings.yml` file. It's a server configuration.
|
4. The server will create `settings.yml` file, which is the server configuration.
|
||||||
5. Configure it as you want and restart server.
|
5. Configure it as you want and restart the server.
|
||||||
|
|
||||||
I recommend you to set parameter `debugLevel` to `0` when you finish testing server and run it into production.
|
### Player info forwarding
|
||||||
This will disable some useless for production information in the console.
|
|
||||||
|
|
||||||
### About player info forwarding
|
The server supports player info forwarding from the proxy. There are several types of info forwarding:
|
||||||
|
|
||||||
The server supports player info forwarding from the proxy. There are two type of info forwarding:
|
|
||||||
|
|
||||||
* `LEGACY` - The **BungeeCord** IP forwarding.
|
* `LEGACY` - The **BungeeCord** IP forwarding.
|
||||||
* `MODERN` - **Velocity** native info forwarding type.
|
* `MODERN` - **Velocity** native info forwarding type.
|
||||||
|
* `BUNGEE_GUARD` - **BungeeGuard** forwarding type.
|
||||||
|
|
||||||
If you use BungeeCord, or Velocity with `LEGACY` forwarding, just set this type in the config.
|
If you use BungeeCord, or Velocity with `LEGACY` forwarding, just set this type in the config.
|
||||||
If you use Velocity with `MODERN` info forwarding, set this type and paste secret key from Velocity
|
If you use Velocity with `MODERN` info forwarding, set this type and paste the secret key from
|
||||||
config into `secret` field.
|
Velocity config into `secret` field.
|
||||||
|
If you installed BungeeGuard on your proxy, then use `BUNGEE_GUARD` forwarding type.
|
||||||
|
Then add your tokens to `tokens` list.
|
||||||
|
|
||||||
### Contributing
|
### Contributing
|
||||||
|
|
||||||
You can create pull request, if you found some bug, optimization ability, or you wanna add some functional,
|
Feel free to create a pull request if you find some bug or optimization opportunity, or if you want
|
||||||
which will not significantly load the server.
|
to add some functionality that is suitable for a limbo server and won't significantly load the server.
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
The project uses `Gradle` to build and minimize output .jar file.
|
Required software:
|
||||||
Run `shadowJar` task to build minimized executable jar file.
|
|
||||||
|
* JDK 11+
|
||||||
|
* Gradle 7+ (optional)
|
||||||
|
|
||||||
|
To build a minimized jar, go to the project root directory and run in the terminal:
|
||||||
|
|
||||||
|
```
|
||||||
|
./gradlew shadowJar
|
||||||
|
```
|
||||||
|
|
||||||
### Contacts
|
### Contacts
|
||||||
|
|
||||||
If you have any question or suggestion, join to [Discord server](https://discord.gg/4VGP3Gv)
|
If you have any questions or suggestions, join our [Discord server](https://discord.gg/4VGP3Gv)!
|
||||||
|
52
build.gradle
52
build.gradle
@ -1,29 +1,55 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
|
||||||
id 'java'
|
id 'java'
|
||||||
|
id 'com.github.johnrengelman.shadow' version '7.1.2'
|
||||||
|
id 'com.github.gmazzo.buildconfig' version '3.1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
group 'ru.nanit'
|
group 'ua.nanit'
|
||||||
version '1.2.2'
|
version '1.8.1'
|
||||||
|
|
||||||
|
compileJava {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile).configureEach {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.3'
|
||||||
compile files('libs/napi-configurate-yaml-1.0.jar')
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.3'
|
||||||
compile group: 'org.yaml', name: 'snakeyaml', version: '1.27'
|
|
||||||
compile group: 'io.netty', name: 'netty-all', version: '4.1.54.Final'
|
implementation 'ch.qos.logback:logback-classic:1.5.6'
|
||||||
compile group: 'net.kyori', name: 'adventure-nbt', version: '4.1.1'
|
implementation 'org.spongepowered:configurate-yaml:4.1.2'
|
||||||
|
implementation 'io.netty:netty-all:4.1.101.Final'
|
||||||
|
implementation 'net.kyori:adventure-nbt:4.14.0'
|
||||||
|
implementation 'com.grack:nanojson:1.8'
|
||||||
|
implementation 'com.google.code.gson:gson:2.10.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
buildConfig {
|
||||||
|
className("BuildConfig")
|
||||||
|
packageName("ua.nanit.limbo")
|
||||||
|
buildConfigField('String', 'LIMBO_VERSION', "\"${project.version}\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
shadowJar {
|
||||||
|
from 'LICENSE'
|
||||||
|
|
||||||
manifest {
|
manifest {
|
||||||
attributes('Main-Class': 'ru.nanit.limbo.NanoLimbo')
|
attributes('Main-Class': 'ua.nanit.limbo.NanoLimbo')
|
||||||
|
}
|
||||||
|
|
||||||
|
minimize {
|
||||||
|
exclude(dependency('ch.qos.logback:logback-classic:.*:.*'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowJar{
|
test {
|
||||||
minimize()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
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.4.2-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
234
gradlew
vendored
Executable file
234
gradlew
vendored
Executable file
@ -0,0 +1,234 @@
|
|||||||
|
#!/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 \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# 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" "$@"
|
89
gradlew.bat
vendored
Normal file
89
gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
@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%" == "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%"=="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!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
Binary file not shown.
@ -1,9 +0,0 @@
|
|||||||
package ru.nanit.limbo;
|
|
||||||
|
|
||||||
public final class LimboConstants {
|
|
||||||
|
|
||||||
public static final String VELOCITY_INFO_CHANNEL = "velocity:player_info";
|
|
||||||
|
|
||||||
private LimboConstants(){}
|
|
||||||
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
package ru.nanit.limbo;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.server.LimboServer;
|
|
||||||
import ru.nanit.limbo.util.Logger;
|
|
||||||
|
|
||||||
public final class NanoLimbo {
|
|
||||||
|
|
||||||
public static void main(String[] args){
|
|
||||||
try {
|
|
||||||
new LimboServer().start();
|
|
||||||
} catch (Exception e){
|
|
||||||
Logger.error("Cannot start server: ", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,135 +0,0 @@
|
|||||||
package ru.nanit.limbo.configuration;
|
|
||||||
|
|
||||||
import napi.configurate.Configuration;
|
|
||||||
import napi.configurate.source.ConfigSources;
|
|
||||||
import napi.configurate.yaml.YamlConfiguration;
|
|
||||||
import ru.nanit.limbo.server.data.*;
|
|
||||||
import ru.nanit.limbo.util.Colors;
|
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
public final class LimboConfig {
|
|
||||||
|
|
||||||
private final Path root;
|
|
||||||
|
|
||||||
private SocketAddress address;
|
|
||||||
private int maxPlayers;
|
|
||||||
private PingData pingData;
|
|
||||||
|
|
||||||
private String dimensionType;
|
|
||||||
private Position spawnPosition;
|
|
||||||
private int gameMode;
|
|
||||||
|
|
||||||
private boolean useJoinMessage;
|
|
||||||
private boolean useBossBar;
|
|
||||||
private String joinMessage;
|
|
||||||
private BossBar bossBar;
|
|
||||||
|
|
||||||
private InfoForwarding infoForwarding;
|
|
||||||
private long readTimeout;
|
|
||||||
private int debugLevel = 3;
|
|
||||||
|
|
||||||
private boolean useEpoll;
|
|
||||||
private int bossGroupSize;
|
|
||||||
private int workerGroupSize;
|
|
||||||
|
|
||||||
public LimboConfig(Path root){
|
|
||||||
this.root = root;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void load() throws Exception {
|
|
||||||
Configuration conf = YamlConfiguration.builder()
|
|
||||||
.source(ConfigSources.resource("/settings.yml", this).copyTo(root))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
conf.reload();
|
|
||||||
|
|
||||||
address = conf.getNode("bind").getValue(SocketAddress.class);
|
|
||||||
maxPlayers = conf.getNode("maxPlayers").getInt();
|
|
||||||
pingData = conf.getNode("ping").getValue(PingData.class);
|
|
||||||
dimensionType = conf.getNode("dimension").getString();
|
|
||||||
spawnPosition = conf.getNode("spawnPosition").getValue(Position.class);
|
|
||||||
gameMode = conf.getNode("gameMode").getInt();
|
|
||||||
useJoinMessage = conf.getNode("joinMessage", "enable").getBoolean();
|
|
||||||
useBossBar = conf.getNode("bossBar", "enable").getBoolean();
|
|
||||||
|
|
||||||
if (useJoinMessage)
|
|
||||||
joinMessage = Colors.of(conf.getNode("joinMessage", "text").getString());
|
|
||||||
|
|
||||||
if (useBossBar)
|
|
||||||
bossBar = conf.getNode("bossBar").getValue(BossBar.class);
|
|
||||||
|
|
||||||
infoForwarding = conf.getNode("infoForwarding").getValue(InfoForwarding.class);
|
|
||||||
readTimeout = conf.getNode("readTimeout").getLong();
|
|
||||||
debugLevel = conf.getNode("debugLevel").getInt();
|
|
||||||
|
|
||||||
useEpoll = conf.getNode("netty", "useEpoll").getBoolean(true);
|
|
||||||
bossGroupSize = conf.getNode("netty", "threads", "bossGroup").getInt(1);
|
|
||||||
workerGroupSize = conf.getNode("netty", "threads", "workerGroup").getInt(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SocketAddress getAddress() {
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxPlayers() {
|
|
||||||
return maxPlayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PingData getPingData() {
|
|
||||||
return pingData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDimensionType() {
|
|
||||||
return dimensionType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Position getSpawnPosition() {
|
|
||||||
return spawnPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getGameMode() {
|
|
||||||
return gameMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InfoForwarding getInfoForwarding() {
|
|
||||||
return infoForwarding;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getReadTimeout() {
|
|
||||||
return readTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDebugLevel() {
|
|
||||||
return debugLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUseJoinMessage() {
|
|
||||||
return useJoinMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUseBossBar() {
|
|
||||||
return useBossBar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getJoinMessage() {
|
|
||||||
return joinMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BossBar getBossBar() {
|
|
||||||
return bossBar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUseEpoll() {
|
|
||||||
return useEpoll;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBossGroupSize() {
|
|
||||||
return bossGroupSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getWorkerGroupSize() {
|
|
||||||
return workerGroupSize;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package ru.nanit.limbo.configuration;
|
|
||||||
|
|
||||||
import napi.configurate.data.ConfigNode;
|
|
||||||
import napi.configurate.serializing.NodeSerializer;
|
|
||||||
import napi.configurate.serializing.NodeSerializingException;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
|
|
||||||
public class SocketAddressSerializer implements NodeSerializer<SocketAddress> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SocketAddress deserialize(ConfigNode node) {
|
|
||||||
String ip = node.getNode("ip").getString();
|
|
||||||
int port = node.getNode("port").getInt();
|
|
||||||
SocketAddress address;
|
|
||||||
|
|
||||||
if (ip == null || ip.isEmpty()){
|
|
||||||
address = new InetSocketAddress(port);
|
|
||||||
} else {
|
|
||||||
address = new InetSocketAddress(ip, port);
|
|
||||||
}
|
|
||||||
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void serialize(SocketAddress socketAddress, ConfigNode configNode) throws NodeSerializingException {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package ru.nanit.limbo.connection;
|
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelInitializer;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.handler.timeout.ReadTimeoutHandler;
|
|
||||||
import ru.nanit.limbo.connection.pipeline.VarIntFrameDecoder;
|
|
||||||
import ru.nanit.limbo.connection.pipeline.PacketDecoder;
|
|
||||||
import ru.nanit.limbo.connection.pipeline.PacketEncoder;
|
|
||||||
import ru.nanit.limbo.connection.pipeline.VarIntLengthEncoder;
|
|
||||||
import ru.nanit.limbo.server.LimboServer;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class ClientChannelInitializer extends ChannelInitializer<Channel> {
|
|
||||||
|
|
||||||
private final LimboServer server;
|
|
||||||
|
|
||||||
public ClientChannelInitializer(LimboServer server){
|
|
||||||
this.server = server;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void initChannel(Channel channel) {
|
|
||||||
ChannelPipeline pipeline = channel.pipeline();
|
|
||||||
|
|
||||||
pipeline.addLast("timeout", new ReadTimeoutHandler(server.getConfig().getReadTimeout(),
|
|
||||||
TimeUnit.MILLISECONDS));
|
|
||||||
pipeline.addLast("frame_decoder", new VarIntFrameDecoder());
|
|
||||||
pipeline.addLast("frame_encoder", new VarIntLengthEncoder());
|
|
||||||
pipeline.addLast("decoder", new PacketDecoder());
|
|
||||||
pipeline.addLast("encoder", new PacketEncoder());
|
|
||||||
pipeline.addLast("handler", new ClientConnection(channel, server));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,343 +0,0 @@
|
|||||||
package ru.nanit.limbo.connection;
|
|
||||||
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
|
||||||
import ru.nanit.limbo.LimboConstants;
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
import ru.nanit.limbo.protocol.PreRenderedPacket;
|
|
||||||
import ru.nanit.limbo.protocol.packets.login.*;
|
|
||||||
import ru.nanit.limbo.protocol.packets.play.*;
|
|
||||||
import ru.nanit.limbo.connection.pipeline.PacketDecoder;
|
|
||||||
import ru.nanit.limbo.connection.pipeline.PacketEncoder;
|
|
||||||
import ru.nanit.limbo.protocol.packets.PacketHandshake;
|
|
||||||
import ru.nanit.limbo.protocol.packets.status.PacketStatusPing;
|
|
||||||
import ru.nanit.limbo.protocol.packets.status.PacketStatusRequest;
|
|
||||||
import ru.nanit.limbo.protocol.packets.status.PacketStatusResponse;
|
|
||||||
import ru.nanit.limbo.protocol.registry.State;
|
|
||||||
import ru.nanit.limbo.protocol.registry.Version;
|
|
||||||
import ru.nanit.limbo.server.LimboServer;
|
|
||||||
import ru.nanit.limbo.util.Logger;
|
|
||||||
import ru.nanit.limbo.util.UuidUtil;
|
|
||||||
|
|
||||||
import javax.crypto.Mac;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
|
||||||
|
|
||||||
public class ClientConnection extends ChannelInboundHandlerAdapter {
|
|
||||||
|
|
||||||
private static PreRenderedPacket PACKET_LOGIN_SUCCESS;
|
|
||||||
private static PreRenderedPacket PACKET_JOIN_GAME;
|
|
||||||
private static PreRenderedPacket PACKET_PLAYER_ABILITIES;
|
|
||||||
private static PreRenderedPacket PACKET_PLAYER_INFO;
|
|
||||||
private static PreRenderedPacket PACKET_DECLARE_COMMANDS;
|
|
||||||
private static PreRenderedPacket PACKET_PLAYER_POS;
|
|
||||||
private static PreRenderedPacket PACKET_JOIN_MESSAGE;
|
|
||||||
private static PreRenderedPacket PACKET_BOSS_BAR;
|
|
||||||
|
|
||||||
private final LimboServer server;
|
|
||||||
private final Channel channel;
|
|
||||||
private final GameProfile gameProfile;
|
|
||||||
|
|
||||||
private State state;
|
|
||||||
private Version clientVersion;
|
|
||||||
private SocketAddress address;
|
|
||||||
|
|
||||||
private int velocityLoginMessageId = -1;
|
|
||||||
|
|
||||||
public ClientConnection(Channel channel, LimboServer server){
|
|
||||||
this.server = server;
|
|
||||||
this.channel = channel;
|
|
||||||
this.address = channel.remoteAddress();
|
|
||||||
this.gameProfile = new GameProfile();
|
|
||||||
}
|
|
||||||
|
|
||||||
public UUID getUuid() {
|
|
||||||
return gameProfile.getUuid();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUsername(){
|
|
||||||
return gameProfile.getUsername();
|
|
||||||
}
|
|
||||||
|
|
||||||
public SocketAddress getAddress() {
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
|
||||||
if (state.equals(State.PLAY)){
|
|
||||||
server.getConnections().removeConnection(this);
|
|
||||||
}
|
|
||||||
super.channelInactive(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
|
||||||
if (channel.isActive()){
|
|
||||||
Logger.error("Unhandled exception: ", cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
|
||||||
handlePacket(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handlePacket(Object packet){
|
|
||||||
if (packet instanceof PacketHandshake){
|
|
||||||
PacketHandshake handshake = (PacketHandshake) packet;
|
|
||||||
clientVersion = handshake.getVersion();
|
|
||||||
updateState(State.getById(handshake.getNextState()));
|
|
||||||
Logger.debug("Pinged from " + address);
|
|
||||||
|
|
||||||
if (server.getConfig().getInfoForwarding().isLegacy()){
|
|
||||||
String[] split = handshake.getHost().split("\00");
|
|
||||||
|
|
||||||
if (split.length == 3 || split.length == 4){
|
|
||||||
setAddress(split[1]);
|
|
||||||
gameProfile.setUuid(UuidUtil.fromString(split[2]));
|
|
||||||
} else {
|
|
||||||
disconnectLogin("You've enabled player info forwarding. You need to connect with proxy");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet instanceof PacketStatusRequest){
|
|
||||||
sendPacket(new PacketStatusResponse(server));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet instanceof PacketStatusPing){
|
|
||||||
sendPacketAndClose(packet);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet instanceof PacketLoginStart){
|
|
||||||
if (server.getConnections().getCount() >= server.getConfig().getMaxPlayers()){
|
|
||||||
disconnectLogin("Too many players connected");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!clientVersion.equals(Version.getCurrentSupported())){
|
|
||||||
disconnectLogin("Incompatible client version");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (server.getConfig().getInfoForwarding().isModern()){
|
|
||||||
velocityLoginMessageId = ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE);
|
|
||||||
PacketLoginPluginRequest request = new PacketLoginPluginRequest();
|
|
||||||
request.setMessageId(velocityLoginMessageId);
|
|
||||||
request.setChannel(LimboConstants.VELOCITY_INFO_CHANNEL);
|
|
||||||
request.setData(Unpooled.EMPTY_BUFFER);
|
|
||||||
sendPacket(request);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!server.getConfig().getInfoForwarding().isModern()){
|
|
||||||
gameProfile.setUsername(((PacketLoginStart)packet).getUsername());
|
|
||||||
gameProfile.setUuid(UuidUtil.getOfflineModeUuid(getUsername()));
|
|
||||||
}
|
|
||||||
|
|
||||||
fireLoginSuccess();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet instanceof PacketLoginPluginResponse){
|
|
||||||
PacketLoginPluginResponse response = (PacketLoginPluginResponse) packet;
|
|
||||||
|
|
||||||
if (server.getConfig().getInfoForwarding().isModern()
|
|
||||||
&& response.getMessageId() == velocityLoginMessageId){
|
|
||||||
|
|
||||||
if (!response.isSuccessful() || response.getData() == null){
|
|
||||||
disconnectLogin("You need to connect with Velocity");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!checkVelocityKeyIntegrity(response.getData())) {
|
|
||||||
disconnectLogin("Can't verify forwarded player info");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Order is important
|
|
||||||
setAddress(response.getData().readString());
|
|
||||||
gameProfile.setUuid(response.getData().readUuid());
|
|
||||||
gameProfile.setUsername(response.getData().readString());
|
|
||||||
|
|
||||||
fireLoginSuccess();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fireLoginSuccess(){
|
|
||||||
if (server.getConfig().getInfoForwarding().isModern() && velocityLoginMessageId == -1){
|
|
||||||
disconnectLogin("You need to connect with Velocity");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
writePacket(PACKET_LOGIN_SUCCESS);
|
|
||||||
updateState(State.PLAY);
|
|
||||||
|
|
||||||
server.getConnections().addConnection(this);
|
|
||||||
|
|
||||||
writePacket(PACKET_JOIN_GAME);
|
|
||||||
writePacket(PACKET_PLAYER_ABILITIES);
|
|
||||||
writePacket(PACKET_PLAYER_POS);
|
|
||||||
writePacket(PACKET_PLAYER_INFO);
|
|
||||||
writePacket(PACKET_DECLARE_COMMANDS);
|
|
||||||
|
|
||||||
if (PACKET_BOSS_BAR != null)
|
|
||||||
writePacket(PACKET_BOSS_BAR);
|
|
||||||
|
|
||||||
if (PACKET_JOIN_MESSAGE != null)
|
|
||||||
writePacket(PACKET_JOIN_MESSAGE);
|
|
||||||
|
|
||||||
sendKeepAlive();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void disconnectLogin(String reason){
|
|
||||||
if (isConnected() && state == State.LOGIN){
|
|
||||||
PacketDisconnect disconnect = new PacketDisconnect();
|
|
||||||
disconnect.setReason(reason);
|
|
||||||
sendPacketAndClose(disconnect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendKeepAlive(){
|
|
||||||
if (state.equals(State.PLAY)){
|
|
||||||
PacketKeepAlive keepAlive = new PacketKeepAlive();
|
|
||||||
keepAlive.setId(ThreadLocalRandom.current().nextLong());
|
|
||||||
sendPacket(keepAlive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendPacket(Object packet){
|
|
||||||
if (isConnected())
|
|
||||||
channel.writeAndFlush(packet, channel.voidPromise());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendPacketAndClose(Object packet){
|
|
||||||
if (isConnected())
|
|
||||||
channel.writeAndFlush(packet).addListener(ChannelFutureListener.CLOSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writePacket(Object packet){
|
|
||||||
if (isConnected()) channel.write(packet, channel.voidPromise());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void flushPackets(){
|
|
||||||
if (isConnected()) channel.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isConnected(){
|
|
||||||
return channel.isActive();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateState(State state){
|
|
||||||
this.state = state;
|
|
||||||
channel.pipeline().get(PacketDecoder.class).updateState(state);
|
|
||||||
channel.pipeline().get(PacketEncoder.class).updateState(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setAddress(String host){
|
|
||||||
this.address = new InetSocketAddress(host, ((InetSocketAddress)this.address).getPort());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkVelocityKeyIntegrity(ByteMessage buf) {
|
|
||||||
byte[] signature = new byte[32];
|
|
||||||
buf.readBytes(signature);
|
|
||||||
byte[] data = new byte[buf.readableBytes()];
|
|
||||||
buf.getBytes(buf.readerIndex(), data);
|
|
||||||
try {
|
|
||||||
Mac mac = Mac.getInstance("HmacSHA256");
|
|
||||||
mac.init(new SecretKeySpec(server.getConfig().getInfoForwarding().getSecretKey(), "HmacSHA256"));
|
|
||||||
byte[] mySignature = mac.doFinal(data);
|
|
||||||
if (!MessageDigest.isEqual(signature, mySignature))
|
|
||||||
return false;
|
|
||||||
} catch (InvalidKeyException |java.security.NoSuchAlgorithmException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
int version = buf.readVarInt();
|
|
||||||
if (version != 1)
|
|
||||||
throw new IllegalStateException("Unsupported forwarding version " + version + ", wanted " + '\001');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void preInitPackets(LimboServer server){
|
|
||||||
final String username = server.getConfig().getPingData().getVersion();
|
|
||||||
final UUID uuid = UuidUtil.getOfflineModeUuid(username);
|
|
||||||
|
|
||||||
PacketLoginSuccess loginSuccess = new PacketLoginSuccess();
|
|
||||||
loginSuccess.setUsername(username);
|
|
||||||
loginSuccess.setUuid(uuid);
|
|
||||||
|
|
||||||
PacketJoinGame joinGame = new PacketJoinGame();
|
|
||||||
joinGame.setEntityId(0);
|
|
||||||
joinGame.setEnableRespawnScreen(true);
|
|
||||||
joinGame.setFlat(false);
|
|
||||||
joinGame.setGameMode(server.getConfig().getGameMode());
|
|
||||||
joinGame.setHardcore(false);
|
|
||||||
joinGame.setMaxPlayers(server.getConfig().getMaxPlayers());
|
|
||||||
joinGame.setPreviousGameMode(-1);
|
|
||||||
joinGame.setReducedDebugInfo(true);
|
|
||||||
joinGame.setDebug(false);
|
|
||||||
joinGame.setViewDistance(2);
|
|
||||||
joinGame.setWorldName("minecraft:world");
|
|
||||||
joinGame.setWorldNames("minecraft:world");
|
|
||||||
joinGame.setHashedSeed(0);
|
|
||||||
joinGame.setDimensionCodec(server.getDimensionRegistry().getCodec());
|
|
||||||
joinGame.setDimension(server.getDimensionRegistry().getDefaultDimension());
|
|
||||||
|
|
||||||
PacketPlayerAbilities playerAbilities = new PacketPlayerAbilities();
|
|
||||||
playerAbilities.setFlyingSpeed(0.0F);
|
|
||||||
playerAbilities.setFlags(0x02);
|
|
||||||
playerAbilities.setFieldOfView(0.1F);
|
|
||||||
|
|
||||||
PacketPlayerPositionAndLook positionAndLook = new PacketPlayerPositionAndLook();
|
|
||||||
positionAndLook.setX(server.getConfig().getSpawnPosition().getX());
|
|
||||||
positionAndLook.setY(server.getConfig().getSpawnPosition().getY());
|
|
||||||
positionAndLook.setZ(server.getConfig().getSpawnPosition().getZ());
|
|
||||||
positionAndLook.setYaw(server.getConfig().getSpawnPosition().getYaw());
|
|
||||||
positionAndLook.setPitch(server.getConfig().getSpawnPosition().getPitch());
|
|
||||||
positionAndLook.setTeleportId(ThreadLocalRandom.current().nextInt());
|
|
||||||
|
|
||||||
PacketPlayerInfo info = new PacketPlayerInfo();
|
|
||||||
info.setUsername(username);
|
|
||||||
info.setGameMode(server.getConfig().getGameMode());
|
|
||||||
info.setUuid(uuid);
|
|
||||||
|
|
||||||
PacketDeclareCommands declareCommands = new PacketDeclareCommands();
|
|
||||||
declareCommands.setCommands(Collections.singletonList("limbo"));
|
|
||||||
|
|
||||||
PACKET_LOGIN_SUCCESS = PreRenderedPacket.of(loginSuccess);
|
|
||||||
PACKET_JOIN_GAME = PreRenderedPacket.of(joinGame);
|
|
||||||
PACKET_PLAYER_ABILITIES = PreRenderedPacket.of(playerAbilities);
|
|
||||||
PACKET_PLAYER_POS = PreRenderedPacket.of(positionAndLook);
|
|
||||||
PACKET_PLAYER_INFO = PreRenderedPacket.of(info);
|
|
||||||
PACKET_DECLARE_COMMANDS = PreRenderedPacket.of(declareCommands);
|
|
||||||
|
|
||||||
if (server.getConfig().isUseJoinMessage()){
|
|
||||||
PacketChatMessage joinMessage = new PacketChatMessage();
|
|
||||||
joinMessage.setJsonData(server.getConfig().getJoinMessage());
|
|
||||||
joinMessage.setPosition(PacketChatMessage.Position.CHAT);
|
|
||||||
joinMessage.setSender(UUID.randomUUID());
|
|
||||||
PACKET_JOIN_MESSAGE = PreRenderedPacket.of(joinMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (server.getConfig().isUseBossBar()){
|
|
||||||
PacketBossBar bossBar = new PacketBossBar();
|
|
||||||
bossBar.setBossBar(server.getConfig().getBossBar());
|
|
||||||
bossBar.setUuid(UUID.randomUUID());
|
|
||||||
PACKET_BOSS_BAR = PreRenderedPacket.of(bossBar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package ru.nanit.limbo.connection;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class GameProfile {
|
|
||||||
|
|
||||||
private UUID uuid;
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
public UUID getUuid() {
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUuid(UUID uuid) {
|
|
||||||
this.uuid = uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUsername() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUsername(String username) {
|
|
||||||
this.username = username;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package ru.nanit.limbo.connection.pipeline;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
|
||||||
import ru.nanit.limbo.protocol.*;
|
|
||||||
import ru.nanit.limbo.protocol.registry.State;
|
|
||||||
import ru.nanit.limbo.util.Logger;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class PacketDecoder extends MessageToMessageDecoder<ByteBuf> {
|
|
||||||
|
|
||||||
private State.PacketRegistry mappings;
|
|
||||||
|
|
||||||
public PacketDecoder(){
|
|
||||||
updateState(State.HANDSHAKING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> out) throws Exception {
|
|
||||||
if (!ctx.channel().isActive() || mappings == null) return;
|
|
||||||
|
|
||||||
ByteMessage msg = new ByteMessage(buf);
|
|
||||||
int packetId = msg.readVarInt();
|
|
||||||
Packet packet = mappings.getPacket(packetId);
|
|
||||||
|
|
||||||
if (packet != null){
|
|
||||||
try {
|
|
||||||
packet.decode(msg);
|
|
||||||
} catch (Exception e){
|
|
||||||
Logger.warning("Cannot decode packet 0x%s: %s", Integer.toHexString(packetId), e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.fireChannelRead(packet);
|
|
||||||
} else {
|
|
||||||
Logger.warning("Undefined incoming packet: 0x" + Integer.toHexString(packetId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateState(State state){
|
|
||||||
this.mappings = state.serverBound;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
package ru.nanit.limbo.connection.pipeline;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.handler.codec.MessageToByteEncoder;
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
import ru.nanit.limbo.protocol.Packet;
|
|
||||||
import ru.nanit.limbo.protocol.PreRenderedPacket;
|
|
||||||
import ru.nanit.limbo.protocol.registry.State;
|
|
||||||
import ru.nanit.limbo.util.Logger;
|
|
||||||
|
|
||||||
public class PacketEncoder extends MessageToByteEncoder<Packet> {
|
|
||||||
|
|
||||||
private State.PacketRegistry registry;
|
|
||||||
|
|
||||||
public PacketEncoder(){
|
|
||||||
updateState(State.HANDSHAKING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void encode(ChannelHandlerContext ctx, Packet packet, ByteBuf out) throws Exception {
|
|
||||||
if (registry == null) return;
|
|
||||||
|
|
||||||
ByteMessage msg = new ByteMessage(out);
|
|
||||||
int packetId;
|
|
||||||
|
|
||||||
if (packet instanceof PreRenderedPacket){
|
|
||||||
packetId = registry.getPacketId(((PreRenderedPacket)packet).getWrappedPacket().getClass());
|
|
||||||
} else {
|
|
||||||
packetId = registry.getPacketId(packet.getClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packetId == -1){
|
|
||||||
Logger.warning("Undefined packet class: %s", packet.getClass().getName());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.writeVarInt(packetId);
|
|
||||||
|
|
||||||
try {
|
|
||||||
packet.encode(msg);
|
|
||||||
} catch (Exception e){
|
|
||||||
Logger.warning("Cannot encode packet 0x%s: %s", Integer.toHexString(packetId), e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateState(State state){
|
|
||||||
this.registry = state.clientBound;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
package ru.nanit.limbo.connection.pipeline;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.handler.codec.MessageToByteEncoder;
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
|
|
||||||
@ChannelHandler.Sharable
|
|
||||||
public class VarIntLengthEncoder extends MessageToByteEncoder<ByteBuf> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void encode(ChannelHandlerContext ctx, ByteBuf buf, ByteBuf out) {
|
|
||||||
ByteMessage msg = new ByteMessage(out);
|
|
||||||
msg.writeVarInt(buf.readableBytes());
|
|
||||||
msg.writeBytes(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) {
|
|
||||||
int anticipatedRequiredCapacity = 5 + msg.readableBytes();
|
|
||||||
return ctx.alloc().heapBuffer(anticipatedRequiredCapacity);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol;
|
|
||||||
|
|
||||||
public enum Direction {
|
|
||||||
|
|
||||||
CLIENT,
|
|
||||||
SERVER
|
|
||||||
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol;
|
|
||||||
|
|
||||||
public interface Packet {
|
|
||||||
|
|
||||||
void encode(ByteMessage msg);
|
|
||||||
|
|
||||||
void decode(ByteMessage msg);
|
|
||||||
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol;
|
|
||||||
|
|
||||||
public interface PacketIn extends Packet {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default void encode(ByteMessage msg) {
|
|
||||||
// Can be ignored for incoming packets
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol;
|
|
||||||
|
|
||||||
public interface PacketOut extends Packet {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default void decode(ByteMessage msg) {
|
|
||||||
// Can be ignored for outgoing packets
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol;
|
|
||||||
|
|
||||||
public class PreRenderedPacket implements PacketOut {
|
|
||||||
|
|
||||||
private final PacketOut packet;
|
|
||||||
private byte[] message;
|
|
||||||
|
|
||||||
public PreRenderedPacket(PacketOut packet){
|
|
||||||
this.packet = packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PacketOut getWrappedPacket(){
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PreRenderedPacket render(){
|
|
||||||
ByteMessage renderedMessage = ByteMessage.create();
|
|
||||||
packet.encode(renderedMessage);
|
|
||||||
this.message = renderedMessage.toByteArray();
|
|
||||||
renderedMessage.release();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(ByteMessage msg) {
|
|
||||||
msg.writeBytes(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PreRenderedPacket of(PacketOut packet){
|
|
||||||
return new PreRenderedPacket(packet).render();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
import ru.nanit.limbo.protocol.Packet;
|
|
||||||
import ru.nanit.limbo.protocol.registry.Version;
|
|
||||||
|
|
||||||
public class PacketHandshake implements Packet {
|
|
||||||
|
|
||||||
private Version version;
|
|
||||||
private String host;
|
|
||||||
private int port;
|
|
||||||
private int nextState;
|
|
||||||
|
|
||||||
public Version getVersion() {
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVersion(Version version) {
|
|
||||||
this.version = version;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHost() {
|
|
||||||
return host;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHost(String host) {
|
|
||||||
this.host = host;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPort() {
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPort(int port) {
|
|
||||||
this.port = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNextState() {
|
|
||||||
return nextState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNextState(int nextState) {
|
|
||||||
this.nextState = nextState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(ByteMessage msg) {
|
|
||||||
msg.writeVarInt(this.version.getProtocolNumber());
|
|
||||||
msg.writeString(host);
|
|
||||||
msg.writeShort(port);
|
|
||||||
msg.writeVarInt(nextState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void decode(ByteMessage msg) {
|
|
||||||
this.version = Version.of(msg.readVarInt());
|
|
||||||
this.host = msg.readString();
|
|
||||||
this.port = msg.readUnsignedShort();
|
|
||||||
this.nextState = msg.readVarInt();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.login;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
import ru.nanit.limbo.protocol.PacketOut;
|
|
||||||
|
|
||||||
public class PacketDisconnect implements PacketOut {
|
|
||||||
|
|
||||||
private String reason;
|
|
||||||
|
|
||||||
public void setReason(String reason) {
|
|
||||||
this.reason = reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(ByteMessage msg) {
|
|
||||||
msg.writeString(String.format("{\"text\": \"%s\"}", reason));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.login;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
import ru.nanit.limbo.protocol.PacketOut;
|
|
||||||
|
|
||||||
public class PacketLoginPluginRequest implements PacketOut {
|
|
||||||
|
|
||||||
private int messageId;
|
|
||||||
private String channel;
|
|
||||||
private ByteBuf data;
|
|
||||||
|
|
||||||
public void setMessageId(int messageId) {
|
|
||||||
this.messageId = messageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChannel(String channel) {
|
|
||||||
this.channel = channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setData(ByteBuf data) {
|
|
||||||
this.data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(ByteMessage msg) {
|
|
||||||
msg.writeVarInt(messageId);
|
|
||||||
msg.writeString(channel);
|
|
||||||
msg.writeBytes(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.login;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
import ru.nanit.limbo.protocol.PacketIn;
|
|
||||||
|
|
||||||
public class PacketLoginPluginResponse implements PacketIn {
|
|
||||||
|
|
||||||
private int messageId;
|
|
||||||
private boolean successful;
|
|
||||||
private ByteMessage data;
|
|
||||||
|
|
||||||
public int getMessageId() {
|
|
||||||
return messageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSuccessful() {
|
|
||||||
return successful;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteMessage getData() {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void decode(ByteMessage msg) {
|
|
||||||
messageId = msg.readVarInt();
|
|
||||||
successful = msg.readBoolean();
|
|
||||||
|
|
||||||
if (msg.readableBytes() > 0){
|
|
||||||
int i = msg.readableBytes();
|
|
||||||
data = new ByteMessage(msg.readBytes(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.login;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.*;
|
|
||||||
|
|
||||||
public class PacketLoginStart implements PacketIn {
|
|
||||||
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
public String getUsername() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void decode(ByteMessage msg) {
|
|
||||||
this.username = msg.readString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.login;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
import ru.nanit.limbo.protocol.PacketOut;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class PacketLoginSuccess implements PacketOut {
|
|
||||||
|
|
||||||
private UUID uuid;
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
public void setUuid(UUID uuid) {
|
|
||||||
this.uuid = uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUsername(String username) {
|
|
||||||
this.username = username;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(ByteMessage msg) {
|
|
||||||
msg.writeUuid(uuid);
|
|
||||||
msg.writeString(username);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.play;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
import ru.nanit.limbo.protocol.PacketOut;
|
|
||||||
import ru.nanit.limbo.server.data.BossBar;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class PacketBossBar implements PacketOut {
|
|
||||||
|
|
||||||
private UUID uuid;
|
|
||||||
private BossBar bossBar;
|
|
||||||
private int flags;
|
|
||||||
|
|
||||||
public void setUuid(UUID uuid) {
|
|
||||||
this.uuid = uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBossBar(BossBar bossBar) {
|
|
||||||
this.bossBar = bossBar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFlags(int flags) {
|
|
||||||
this.flags = flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(ByteMessage msg) {
|
|
||||||
msg.writeUuid(uuid);
|
|
||||||
msg.writeVarInt(0); // Create bossbar
|
|
||||||
msg.writeString(bossBar.getText());
|
|
||||||
msg.writeFloat(bossBar.getHealth());
|
|
||||||
msg.writeVarInt(bossBar.getColor().getIndex());
|
|
||||||
msg.writeVarInt(bossBar.getDivision().getIndex());
|
|
||||||
msg.writeByte(flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.play;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
import ru.nanit.limbo.protocol.PacketOut;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class PacketChatMessage implements PacketOut {
|
|
||||||
|
|
||||||
private String jsonData;
|
|
||||||
private Position position;
|
|
||||||
private UUID sender;
|
|
||||||
|
|
||||||
public void setJsonData(String jsonData) {
|
|
||||||
this.jsonData = jsonData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPosition(Position position) {
|
|
||||||
this.position = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSender(UUID sender) {
|
|
||||||
this.sender = sender;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(ByteMessage msg) {
|
|
||||||
msg.writeString(jsonData);
|
|
||||||
msg.writeByte(position.index);
|
|
||||||
msg.writeUuid(sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Position {
|
|
||||||
|
|
||||||
CHAT(0),
|
|
||||||
SYSTEM_MESSAGE(1),
|
|
||||||
ACTION_BAR(2);
|
|
||||||
|
|
||||||
private final int index;
|
|
||||||
|
|
||||||
Position(int index){
|
|
||||||
this.index = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.play;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
import ru.nanit.limbo.protocol.PacketOut;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class PacketDeclareCommands implements PacketOut {
|
|
||||||
|
|
||||||
private List<String> commands;
|
|
||||||
|
|
||||||
public void setCommands(List<String> commands) {
|
|
||||||
this.commands = commands;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(ByteMessage msg) {
|
|
||||||
msg.writeVarInt(commands.size() * 2 + 1); // +1 because declaring root node
|
|
||||||
|
|
||||||
// Declare root node
|
|
||||||
|
|
||||||
msg.writeByte(0);
|
|
||||||
msg.writeVarInt(commands.size());
|
|
||||||
|
|
||||||
for (int i = 1; i <= commands.size() * 2; i++){
|
|
||||||
msg.writeVarInt(i++);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Declare other commands
|
|
||||||
|
|
||||||
int i = 1;
|
|
||||||
for (String cmd : commands){
|
|
||||||
msg.writeByte(1 | 0x04);
|
|
||||||
msg.writeVarInt(1);
|
|
||||||
msg.writeVarInt(i + 1);
|
|
||||||
msg.writeString(cmd);
|
|
||||||
i++;
|
|
||||||
|
|
||||||
msg.writeByte(2 | 0x04 | 0x10);
|
|
||||||
msg.writeVarInt(1);
|
|
||||||
msg.writeVarInt(i);
|
|
||||||
msg.writeString("arg");
|
|
||||||
msg.writeString("brigadier:string");
|
|
||||||
msg.writeVarInt(0);
|
|
||||||
msg.writeString("minecraft:ask_server");
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.writeVarInt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,104 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.play;
|
|
||||||
|
|
||||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
import ru.nanit.limbo.protocol.PacketOut;
|
|
||||||
|
|
||||||
public class PacketJoinGame implements PacketOut {
|
|
||||||
|
|
||||||
private int entityId;
|
|
||||||
private boolean isHardcore = false;
|
|
||||||
private int gameMode = 2;
|
|
||||||
private int previousGameMode = -1;
|
|
||||||
private String[] worldNames;
|
|
||||||
private CompoundBinaryTag dimensionCodec;
|
|
||||||
private CompoundBinaryTag dimension;
|
|
||||||
private String worldName;
|
|
||||||
private long hashedSeed;
|
|
||||||
private int maxPlayers;
|
|
||||||
private int viewDistance = 2;
|
|
||||||
private boolean reducedDebugInfo;
|
|
||||||
private boolean enableRespawnScreen;
|
|
||||||
private boolean isDebug;
|
|
||||||
private boolean isFlat;
|
|
||||||
|
|
||||||
public void setEntityId(int entityId) {
|
|
||||||
this.entityId = entityId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHardcore(boolean hardcore) {
|
|
||||||
isHardcore = hardcore;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGameMode(int gameMode) {
|
|
||||||
this.gameMode = gameMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPreviousGameMode(int previousGameMode) {
|
|
||||||
this.previousGameMode = previousGameMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWorldNames(String... worldNames) {
|
|
||||||
this.worldNames = worldNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDimensionCodec(CompoundBinaryTag dimensionCodec) {
|
|
||||||
this.dimensionCodec = dimensionCodec;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDimension(CompoundBinaryTag dimension) {
|
|
||||||
this.dimension = dimension;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWorldName(String worldName) {
|
|
||||||
this.worldName = worldName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHashedSeed(long hashedSeed) {
|
|
||||||
this.hashedSeed = hashedSeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMaxPlayers(int maxPlayers) {
|
|
||||||
this.maxPlayers = maxPlayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setViewDistance(int viewDistance) {
|
|
||||||
this.viewDistance = viewDistance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReducedDebugInfo(boolean reducedDebugInfo) {
|
|
||||||
this.reducedDebugInfo = reducedDebugInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEnableRespawnScreen(boolean enableRespawnScreen) {
|
|
||||||
this.enableRespawnScreen = enableRespawnScreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDebug(boolean debug) {
|
|
||||||
isDebug = debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFlat(boolean flat) {
|
|
||||||
isFlat = flat;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(ByteMessage msg) {
|
|
||||||
msg.writeInt(entityId);
|
|
||||||
msg.writeBoolean(isHardcore);
|
|
||||||
msg.writeByte(gameMode);
|
|
||||||
msg.writeByte(previousGameMode);
|
|
||||||
msg.writeStringsArray(worldNames);
|
|
||||||
msg.writeCompoundTag(dimensionCodec);
|
|
||||||
msg.writeCompoundTag(dimension);
|
|
||||||
msg.writeString(worldName);
|
|
||||||
msg.writeLong(hashedSeed);
|
|
||||||
msg.writeVarInt(maxPlayers);
|
|
||||||
msg.writeVarInt(viewDistance);
|
|
||||||
msg.writeBoolean(reducedDebugInfo);
|
|
||||||
msg.writeBoolean(enableRespawnScreen);
|
|
||||||
msg.writeBoolean(isDebug);
|
|
||||||
msg.writeBoolean(isFlat);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.play;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
import ru.nanit.limbo.protocol.Packet;
|
|
||||||
|
|
||||||
public class PacketKeepAlive implements Packet {
|
|
||||||
|
|
||||||
private long id;
|
|
||||||
|
|
||||||
public long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(long id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(ByteMessage msg) {
|
|
||||||
msg.writeLong(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void decode(ByteMessage msg) {
|
|
||||||
this.id = msg.readLong();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.play;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
import ru.nanit.limbo.protocol.PacketOut;
|
|
||||||
|
|
||||||
public class PacketPlayerAbilities implements PacketOut {
|
|
||||||
|
|
||||||
private int flags = 0x02;
|
|
||||||
private float flyingSpeed = 0.0F;
|
|
||||||
private float fieldOfView = 0.1F;
|
|
||||||
|
|
||||||
public void setFlags(int flags) {
|
|
||||||
this.flags = flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFlyingSpeed(float flyingSpeed) {
|
|
||||||
this.flyingSpeed = flyingSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFieldOfView(float fieldOfView) {
|
|
||||||
this.fieldOfView = fieldOfView;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(ByteMessage msg) {
|
|
||||||
msg.writeByte(flags);
|
|
||||||
msg.writeFloat(flyingSpeed);
|
|
||||||
msg.writeFloat(fieldOfView);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.play;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
import ru.nanit.limbo.protocol.PacketOut;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This packet was very simplified and using only for ADD_PLAYER action
|
|
||||||
*/
|
|
||||||
public class PacketPlayerInfo implements PacketOut {
|
|
||||||
|
|
||||||
private int gameMode = 3;
|
|
||||||
private String username = "";
|
|
||||||
private UUID uuid;
|
|
||||||
|
|
||||||
public void setGameMode(int gameMode) {
|
|
||||||
this.gameMode = gameMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUsername(String username){
|
|
||||||
this.username = username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUuid(UUID uuid){
|
|
||||||
this.uuid = uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(ByteMessage msg) {
|
|
||||||
msg.writeVarInt(0);
|
|
||||||
msg.writeVarInt(1);
|
|
||||||
msg.writeUuid(uuid);
|
|
||||||
msg.writeString(username);
|
|
||||||
msg.writeVarInt(0);
|
|
||||||
msg.writeVarInt(gameMode);
|
|
||||||
msg.writeVarInt(60);
|
|
||||||
msg.writeBoolean(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.play;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
import ru.nanit.limbo.protocol.PacketOut;
|
|
||||||
|
|
||||||
public class PacketPlayerPositionAndLook implements PacketOut {
|
|
||||||
|
|
||||||
private double x;
|
|
||||||
private double y;
|
|
||||||
private double z;
|
|
||||||
private float yaw;
|
|
||||||
private float pitch;
|
|
||||||
private byte flags = 0x08;
|
|
||||||
private int teleportId;
|
|
||||||
|
|
||||||
public void setX(double x) {
|
|
||||||
this.x = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setY(double y) {
|
|
||||||
this.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setZ(double z) {
|
|
||||||
this.z = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setYaw(float yaw) {
|
|
||||||
this.yaw = yaw;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPitch(float pitch) {
|
|
||||||
this.pitch = pitch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFlags(byte flags) {
|
|
||||||
this.flags = flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTeleportId(int teleportId) {
|
|
||||||
this.teleportId = teleportId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(ByteMessage msg) {
|
|
||||||
msg.writeDouble(x);
|
|
||||||
msg.writeDouble(y);
|
|
||||||
msg.writeDouble(z);
|
|
||||||
msg.writeFloat(yaw);
|
|
||||||
msg.writeFloat(pitch);
|
|
||||||
msg.writeByte(flags);
|
|
||||||
msg.writeVarInt(teleportId);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.status;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.ByteMessage;
|
|
||||||
import ru.nanit.limbo.protocol.Packet;
|
|
||||||
|
|
||||||
public class PacketStatusPing implements Packet {
|
|
||||||
|
|
||||||
private long randomId;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(ByteMessage msg) {
|
|
||||||
msg.writeLong(randomId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void decode(ByteMessage msg) {
|
|
||||||
this.randomId = msg.readLong();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.status;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.*;
|
|
||||||
|
|
||||||
public class PacketStatusRequest implements PacketIn {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void decode(ByteMessage msg) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.packets.status;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.*;
|
|
||||||
import ru.nanit.limbo.protocol.registry.Version;
|
|
||||||
import ru.nanit.limbo.server.LimboServer;
|
|
||||||
|
|
||||||
public class PacketStatusResponse implements PacketOut {
|
|
||||||
|
|
||||||
private static final String TEMPLATE = "{ \"version\": { \"name\": \"%s\", \"protocol\": %d }, \"players\": { \"max\": %d, \"online\": %d, \"sample\": [] }, \"description\": %s }";
|
|
||||||
|
|
||||||
private LimboServer server;
|
|
||||||
|
|
||||||
public PacketStatusResponse(){ }
|
|
||||||
|
|
||||||
public PacketStatusResponse(LimboServer server){
|
|
||||||
this.server = server;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(ByteMessage msg) {
|
|
||||||
String ver = server.getConfig().getPingData().getVersion();
|
|
||||||
String desc = server.getConfig().getPingData().getDescription();
|
|
||||||
String json = getResponseJson(ver, Version.getCurrentSupported().getProtocolNumber(),
|
|
||||||
server.getConfig().getMaxPlayers(), server.getConnections().getCount(), desc);
|
|
||||||
|
|
||||||
msg.writeString(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getResponseJson(String version, int protocol, int maxPlayers, int online, String description){
|
|
||||||
return String.format(TEMPLATE, version, protocol, maxPlayers, online, description);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.registry;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.protocol.Packet;
|
|
||||||
import ru.nanit.limbo.protocol.packets.*;
|
|
||||||
import ru.nanit.limbo.protocol.packets.login.*;
|
|
||||||
import ru.nanit.limbo.protocol.packets.play.*;
|
|
||||||
import ru.nanit.limbo.protocol.packets.status.PacketStatusPing;
|
|
||||||
import ru.nanit.limbo.protocol.packets.status.PacketStatusRequest;
|
|
||||||
import ru.nanit.limbo.protocol.packets.status.PacketStatusResponse;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public enum State {
|
|
||||||
|
|
||||||
HANDSHAKING(0){
|
|
||||||
{
|
|
||||||
serverBound.register(0x00, PacketHandshake::new);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
STATUS(1){
|
|
||||||
{
|
|
||||||
serverBound.register(0x01, PacketStatusPing::new);
|
|
||||||
serverBound.register(0x00, PacketStatusRequest::new);
|
|
||||||
clientBound.register(0x00, PacketStatusResponse::new);
|
|
||||||
clientBound.register(0x01, PacketStatusPing::new);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
LOGIN(2){
|
|
||||||
{
|
|
||||||
serverBound.register(0x00, PacketLoginStart::new);
|
|
||||||
serverBound.register(0x02, PacketLoginPluginResponse::new);
|
|
||||||
clientBound.register(0x00, PacketDisconnect::new);
|
|
||||||
clientBound.register(0x02, PacketLoginSuccess::new);
|
|
||||||
clientBound.register(0x04, PacketLoginPluginRequest::new);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PLAY(3){
|
|
||||||
{
|
|
||||||
serverBound.register(0x10, PacketKeepAlive::new);
|
|
||||||
clientBound.register(0x10, PacketDeclareCommands::new);
|
|
||||||
clientBound.register(0x24, PacketJoinGame::new);
|
|
||||||
clientBound.register(0x30, PacketPlayerAbilities::new);
|
|
||||||
clientBound.register(0x34, PacketPlayerPositionAndLook::new);
|
|
||||||
clientBound.register(0x1F, PacketKeepAlive::new);
|
|
||||||
clientBound.register(0x0E, PacketChatMessage::new);
|
|
||||||
clientBound.register(0x0C, PacketBossBar::new);
|
|
||||||
clientBound.register(0x32, PacketPlayerInfo::new);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final Map<Integer, State> STATE_BY_ID = new HashMap<>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
for (State registry : values()){
|
|
||||||
STATE_BY_ID.put(registry.stateId, registry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final int stateId;
|
|
||||||
public final PacketRegistry serverBound = new PacketRegistry();
|
|
||||||
public final PacketRegistry clientBound = new PacketRegistry();
|
|
||||||
|
|
||||||
State(int stateId){
|
|
||||||
this.stateId = stateId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static State getById(int stateId){
|
|
||||||
return STATE_BY_ID.get(stateId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class PacketRegistry {
|
|
||||||
|
|
||||||
private final Map<Integer, Supplier<?>> packetsById = new HashMap<>();
|
|
||||||
private final Map<Class<?>, Integer> packetIdByClass = new HashMap<>();
|
|
||||||
|
|
||||||
public Packet getPacket(int packetId){
|
|
||||||
Supplier<?> supplier = packetsById.get(packetId);
|
|
||||||
return supplier == null ? null : (Packet) supplier.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPacketId(Class<?> packetClass){
|
|
||||||
return packetIdByClass.getOrDefault(packetClass, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void register(int packetId, Supplier<?> supplier){
|
|
||||||
packetsById.put(packetId, supplier);
|
|
||||||
packetIdByClass.put(supplier.get().getClass(), packetId);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
package ru.nanit.limbo.protocol.registry;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public enum Version {
|
|
||||||
|
|
||||||
UNDEFINED(-1),
|
|
||||||
V1_9(107),
|
|
||||||
V1_9_1(108),
|
|
||||||
V1_9_2(109),
|
|
||||||
V1_9_4(110),
|
|
||||||
V1_10(210),
|
|
||||||
V1_11(315),
|
|
||||||
V1_11_1(316),
|
|
||||||
V1_12(335),
|
|
||||||
V1_12_1(338),
|
|
||||||
V1_12_2(340),
|
|
||||||
V1_13(393),
|
|
||||||
V1_13_1(401),
|
|
||||||
V1_13_2(404),
|
|
||||||
V1_14(477),
|
|
||||||
V1_14_1(480),
|
|
||||||
V1_14_2(485),
|
|
||||||
V1_14_3(490),
|
|
||||||
V1_14_4(498),
|
|
||||||
V1_15(573),
|
|
||||||
V1_15_1(575),
|
|
||||||
V1_15_2(578),
|
|
||||||
V1_16(735),
|
|
||||||
V1_16_1(736),
|
|
||||||
V1_16_2(751),
|
|
||||||
V1_16_3(753),
|
|
||||||
V1_16_4(754);
|
|
||||||
|
|
||||||
public static final Map<Integer, Version> VERSION_MAP;
|
|
||||||
|
|
||||||
static {
|
|
||||||
VERSION_MAP = new HashMap<>();
|
|
||||||
|
|
||||||
for (Version version : values()){
|
|
||||||
VERSION_MAP.put(version.getProtocolNumber(), version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Version getCurrentSupported(){
|
|
||||||
return V1_16_4;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Version of(int protocolNumber){
|
|
||||||
return VERSION_MAP.getOrDefault(protocolNumber, UNDEFINED);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final int protocolNumber;
|
|
||||||
|
|
||||||
Version(int protocolNumber){
|
|
||||||
this.protocolNumber = protocolNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getProtocolNumber(){
|
|
||||||
return this.protocolNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package ru.nanit.limbo.server;
|
|
||||||
|
|
||||||
import ru.nanit.limbo.connection.ClientConnection;
|
|
||||||
import ru.nanit.limbo.util.Logger;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
public final class Connections {
|
|
||||||
|
|
||||||
private final Map<UUID, ClientConnection> connections;
|
|
||||||
|
|
||||||
public Connections(){
|
|
||||||
connections = new ConcurrentHashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<ClientConnection> getAllConnections(){
|
|
||||||
return Collections.unmodifiableCollection(connections.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCount(){
|
|
||||||
return connections.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addConnection(ClientConnection connection){
|
|
||||||
connections.put(connection.getUuid(), connection);
|
|
||||||
Logger.info("Player %s connected (%s)", connection.getUsername(), connection.getAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeConnection(ClientConnection connection){
|
|
||||||
connections.remove(connection.getUuid());
|
|
||||||
Logger.info("Player %s disconnected", connection.getUsername());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,119 +0,0 @@
|
|||||||
package ru.nanit.limbo.server.data;
|
|
||||||
|
|
||||||
import napi.configurate.data.ConfigNode;
|
|
||||||
import napi.configurate.serializing.NodeSerializer;
|
|
||||||
import napi.configurate.serializing.NodeSerializingException;
|
|
||||||
import ru.nanit.limbo.util.Colors;
|
|
||||||
|
|
||||||
public class BossBar {
|
|
||||||
|
|
||||||
private String text;
|
|
||||||
private float health;
|
|
||||||
private Color color;
|
|
||||||
private Division division;
|
|
||||||
|
|
||||||
public String getText() {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getHealth() {
|
|
||||||
return health;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Color getColor() {
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Division getDivision() {
|
|
||||||
return division;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setText(String text) {
|
|
||||||
this.text = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHealth(float health) {
|
|
||||||
this.health = health;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setColor(Color color) {
|
|
||||||
this.color = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDivision(Division division) {
|
|
||||||
this.division = division;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Color {
|
|
||||||
|
|
||||||
PINK(0),
|
|
||||||
BLUE(1),
|
|
||||||
RED(2),
|
|
||||||
GREEN(3),
|
|
||||||
YELLOW(4),
|
|
||||||
PURPLE(5),
|
|
||||||
WHITE(6);
|
|
||||||
|
|
||||||
private final int index;
|
|
||||||
|
|
||||||
Color(int index) {
|
|
||||||
this.index = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getIndex() {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Division {
|
|
||||||
|
|
||||||
SOLID(0),
|
|
||||||
DASHES_6(1),
|
|
||||||
DASHES_10(2),
|
|
||||||
DASHES_12(3),
|
|
||||||
DASHES_20(4);
|
|
||||||
|
|
||||||
private final int index;
|
|
||||||
|
|
||||||
Division(int index) {
|
|
||||||
this.index = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getIndex() {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Serializer implements NodeSerializer<BossBar>{
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BossBar deserialize(ConfigNode node) throws NodeSerializingException {
|
|
||||||
BossBar bossBar = new BossBar();
|
|
||||||
|
|
||||||
bossBar.setText(Colors.of(node.getNode("text").getString()));
|
|
||||||
bossBar.setHealth(node.getNode("health").getFloat());
|
|
||||||
|
|
||||||
if (bossBar.getHealth() < 0 || bossBar.getHealth() > 1)
|
|
||||||
throw new NodeSerializingException("BossBar health value must be between 0.0 and 1.0");
|
|
||||||
|
|
||||||
try {
|
|
||||||
bossBar.setColor(Color.valueOf(node.getNode("color").getString().toUpperCase()));
|
|
||||||
} catch (IllegalArgumentException e){
|
|
||||||
throw new NodeSerializingException("Invalid bossbar color");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
bossBar.setDivision(Division.valueOf(node.getNode("division").getString().toUpperCase()));
|
|
||||||
} catch (IllegalArgumentException e){
|
|
||||||
throw new NodeSerializingException("Invalid bossbar division");
|
|
||||||
}
|
|
||||||
|
|
||||||
return bossBar;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void serialize(BossBar bossBar, ConfigNode configNode) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package ru.nanit.limbo.server.data;
|
|
||||||
|
|
||||||
import napi.configurate.data.ConfigNode;
|
|
||||||
import napi.configurate.serializing.NodeSerializer;
|
|
||||||
import napi.configurate.serializing.NodeSerializingException;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
public class InfoForwarding {
|
|
||||||
|
|
||||||
private Type type;
|
|
||||||
private byte[] secretKey;
|
|
||||||
|
|
||||||
public Type getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getSecretKey() {
|
|
||||||
return secretKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isNone(){
|
|
||||||
return type == Type.NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isLegacy(){
|
|
||||||
return type == Type.LEGACY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isModern(){
|
|
||||||
return type == Type.MODERN;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Type {
|
|
||||||
NONE,
|
|
||||||
LEGACY,
|
|
||||||
MODERN
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Serializer implements NodeSerializer<InfoForwarding> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InfoForwarding deserialize(ConfigNode node) throws NodeSerializingException {
|
|
||||||
InfoForwarding forwarding = new InfoForwarding();
|
|
||||||
|
|
||||||
try {
|
|
||||||
forwarding.type = Type.valueOf(node.getNode("type").getString().toUpperCase());
|
|
||||||
} catch (IllegalArgumentException e){
|
|
||||||
throw new NodeSerializingException("Undefined info forwarding type");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forwarding.type == Type.MODERN){
|
|
||||||
forwarding.secretKey = node.getNode("secret").getString().getBytes(StandardCharsets.UTF_8);
|
|
||||||
}
|
|
||||||
|
|
||||||
return forwarding;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void serialize(InfoForwarding infoForwarding, ConfigNode configNode) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
package ru.nanit.limbo.server.data;
|
|
||||||
|
|
||||||
import napi.configurate.data.ConfigNode;
|
|
||||||
import napi.configurate.serializing.NodeSerializer;
|
|
||||||
import ru.nanit.limbo.util.Colors;
|
|
||||||
|
|
||||||
public class PingData {
|
|
||||||
|
|
||||||
private String version;
|
|
||||||
private String description;
|
|
||||||
|
|
||||||
public String getVersion() {
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVersion(String version) {
|
|
||||||
this.version = version;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDescription() {
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDescription(String description) {
|
|
||||||
this.description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Serializer implements NodeSerializer<PingData> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PingData deserialize(ConfigNode node) {
|
|
||||||
PingData pingData = new PingData();
|
|
||||||
pingData.setDescription(Colors.of(node.getNode("description").getString()));
|
|
||||||
pingData.setVersion(Colors.of(node.getNode("version").getString()));
|
|
||||||
return pingData;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void serialize(PingData pingData, ConfigNode configNode) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
package ru.nanit.limbo.server.data;
|
|
||||||
|
|
||||||
import napi.configurate.data.ConfigNode;
|
|
||||||
import napi.configurate.serializing.NodeSerializer;
|
|
||||||
|
|
||||||
public class Position {
|
|
||||||
|
|
||||||
private double x;
|
|
||||||
private double y;
|
|
||||||
private double z;
|
|
||||||
private float yaw;
|
|
||||||
private float pitch;
|
|
||||||
|
|
||||||
public double getX() {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getY() {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getZ() {
|
|
||||||
return z;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getYaw() {
|
|
||||||
return yaw;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getPitch() {
|
|
||||||
return pitch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setX(double x) {
|
|
||||||
this.x = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setY(double y) {
|
|
||||||
this.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setZ(double z) {
|
|
||||||
this.z = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setYaw(float yaw) {
|
|
||||||
this.yaw = yaw;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPitch(float pitch) {
|
|
||||||
this.pitch = pitch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Serializer implements NodeSerializer<Position> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Position deserialize(ConfigNode node) {
|
|
||||||
Position position = new Position();
|
|
||||||
position.setX(node.getNode("x").getDouble());
|
|
||||||
position.setY(node.getNode("y").getDouble());
|
|
||||||
position.setZ(node.getNode("z").getDouble());
|
|
||||||
position.setYaw(node.getNode("yaw").getFloat());
|
|
||||||
position.setPitch(node.getNode("pitch").getFloat());
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void serialize(Position position, ConfigNode configNode) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package ru.nanit.limbo.util;
|
|
||||||
|
|
||||||
public final class Colors {
|
|
||||||
|
|
||||||
private static final char CHAR_FROM = '\u0026';
|
|
||||||
private static final char CHAR_TO = '\u00A7';
|
|
||||||
|
|
||||||
private Colors(){}
|
|
||||||
|
|
||||||
public static String of(String text){
|
|
||||||
return text.replace(CHAR_FROM, CHAR_TO);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
package ru.nanit.limbo.util;
|
|
||||||
|
|
||||||
import java.time.LocalTime;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
|
|
||||||
public final class Logger {
|
|
||||||
|
|
||||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("hh:mm:ss");
|
|
||||||
private static int debugLevel = 3;
|
|
||||||
|
|
||||||
private Logger(){}
|
|
||||||
|
|
||||||
public static void setLevel(int level){
|
|
||||||
debugLevel = level;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void info(Object msg, Object... args){
|
|
||||||
print(Level.INFO, msg, null, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void debug(Object msg, Object... args){
|
|
||||||
print(Level.DEBUG, msg, null, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void warning(Object msg, Object... args){
|
|
||||||
print(Level.WARNING, msg, null, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void warning(Object msg, Throwable t, Object... args){
|
|
||||||
print(Level.WARNING, msg, t, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void error(Object msg, Object... args){
|
|
||||||
print(Level.ERROR, msg, null, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void error(Object msg, Throwable t, Object... args){
|
|
||||||
print(Level.ERROR, msg, t, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void print(Level level, Object msg, Throwable t, Object... args){
|
|
||||||
if (debugLevel >= level.getIndex()){
|
|
||||||
System.out.println(String.format("%s: %s", getPrefix(level), String.format(msg.toString(), args)));
|
|
||||||
if (t != null) t.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getPrefix(Level level){
|
|
||||||
return String.format("[%s] [%s]", getTime(), level.getDisplay());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getTime(){
|
|
||||||
return LocalTime.now().format(FORMATTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Level {
|
|
||||||
|
|
||||||
INFO ("INFO", 0),
|
|
||||||
DEBUG ("DEBUG", 1),
|
|
||||||
WARNING("WARNING", 2),
|
|
||||||
ERROR("ERROR", 3);
|
|
||||||
|
|
||||||
private final String display;
|
|
||||||
private final int index;
|
|
||||||
|
|
||||||
Level(String display, int index){
|
|
||||||
this.display = display;
|
|
||||||
this.index = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDisplay() {
|
|
||||||
return display;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getIndex() {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package ru.nanit.limbo.util;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public final class UuidUtil {
|
|
||||||
|
|
||||||
private UuidUtil(){}
|
|
||||||
|
|
||||||
public static UUID getOfflineModeUuid(String username){
|
|
||||||
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username)
|
|
||||||
.getBytes(StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UUID fromString(String str){
|
|
||||||
if(str.contains("-")) return UUID.fromString(str);
|
|
||||||
return UUID.fromString(str.replaceFirst("(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)", "$1-$2-$3-$4-$5"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
package ru.nanit.limbo.world;
|
|
||||||
|
|
||||||
import net.kyori.adventure.nbt.BinaryTagIO;
|
|
||||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
|
||||||
import net.kyori.adventure.nbt.ListBinaryTag;
|
|
||||||
import ru.nanit.limbo.server.LimboServer;
|
|
||||||
import ru.nanit.limbo.util.Logger;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
public final class DimensionRegistry {
|
|
||||||
|
|
||||||
private CompoundBinaryTag defaultDimension;
|
|
||||||
|
|
||||||
private CompoundBinaryTag codec;
|
|
||||||
private CompoundBinaryTag overWorld;
|
|
||||||
private CompoundBinaryTag theEnd;
|
|
||||||
private CompoundBinaryTag nether;
|
|
||||||
|
|
||||||
public CompoundBinaryTag getCodec(){
|
|
||||||
return codec;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompoundBinaryTag getDefaultDimension() {
|
|
||||||
return defaultDimension;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompoundBinaryTag getOverWorld() {
|
|
||||||
return overWorld;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompoundBinaryTag getTheEnd() {
|
|
||||||
return theEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompoundBinaryTag getNether() {
|
|
||||||
return nether;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void load(LimboServer server, String def) throws IOException {
|
|
||||||
InputStream in = server.getClass().getResourceAsStream("/dimension_registry.nbt");
|
|
||||||
codec = BinaryTagIO.readCompressedInputStream(in);
|
|
||||||
ListBinaryTag dimensions = codec.getCompound("minecraft:dimension_type").getList("value");
|
|
||||||
|
|
||||||
overWorld = (CompoundBinaryTag) ((CompoundBinaryTag) dimensions.get(0)).get("element");
|
|
||||||
nether = (CompoundBinaryTag) ((CompoundBinaryTag) dimensions.get(2)).get("element");
|
|
||||||
theEnd = (CompoundBinaryTag) ((CompoundBinaryTag) dimensions.get(3)).get("element");
|
|
||||||
|
|
||||||
switch (def.toLowerCase()){
|
|
||||||
case "overworld":
|
|
||||||
defaultDimension = overWorld;
|
|
||||||
break;
|
|
||||||
case "nether":
|
|
||||||
defaultDimension = nether;
|
|
||||||
break;
|
|
||||||
case "the_end":
|
|
||||||
defaultDimension = theEnd;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
defaultDimension = theEnd;
|
|
||||||
Logger.warning("Undefined dimension type: '%s'. Using THE_END as default", def);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
27
src/main/java/ua/nanit/limbo/LimboConstants.java
Normal file
27
src/main/java/ua/nanit/limbo/LimboConstants.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo;
|
||||||
|
|
||||||
|
public final class LimboConstants {
|
||||||
|
|
||||||
|
public static final String VELOCITY_INFO_CHANNEL = "velocity:player_info";
|
||||||
|
public static final String BRAND_CHANNEL = "minecraft:brand";
|
||||||
|
|
||||||
|
private LimboConstants() {}
|
||||||
|
|
||||||
|
}
|
33
src/main/java/ua/nanit/limbo/NanoLimbo.java
Normal file
33
src/main/java/ua/nanit/limbo/NanoLimbo.java
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.server.LimboServer;
|
||||||
|
import ua.nanit.limbo.server.Log;
|
||||||
|
|
||||||
|
public final class NanoLimbo {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
new LimboServer().start();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("Cannot start server: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
279
src/main/java/ua/nanit/limbo/configuration/LimboConfig.java
Normal file
279
src/main/java/ua/nanit/limbo/configuration/LimboConfig.java
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.configuration;
|
||||||
|
|
||||||
|
import org.spongepowered.configurate.ConfigurationNode;
|
||||||
|
import org.spongepowered.configurate.ConfigurationOptions;
|
||||||
|
import org.spongepowered.configurate.serialize.TypeSerializerCollection;
|
||||||
|
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||||
|
import ua.nanit.limbo.util.Colors;
|
||||||
|
import ua.nanit.limbo.server.data.BossBar;
|
||||||
|
import ua.nanit.limbo.server.data.InfoForwarding;
|
||||||
|
import ua.nanit.limbo.server.data.PingData;
|
||||||
|
import ua.nanit.limbo.server.data.Title;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public final class LimboConfig {
|
||||||
|
|
||||||
|
private final Path root;
|
||||||
|
|
||||||
|
private SocketAddress address;
|
||||||
|
private int maxPlayers;
|
||||||
|
private PingData pingData;
|
||||||
|
|
||||||
|
private String dimensionType;
|
||||||
|
private int gameMode;
|
||||||
|
|
||||||
|
private boolean useBrandName;
|
||||||
|
private boolean useJoinMessage;
|
||||||
|
private boolean useBossBar;
|
||||||
|
private boolean useTitle;
|
||||||
|
private boolean usePlayerList;
|
||||||
|
private boolean useHeaderAndFooter;
|
||||||
|
|
||||||
|
private String brandName;
|
||||||
|
private String joinMessage;
|
||||||
|
private BossBar bossBar;
|
||||||
|
private Title title;
|
||||||
|
|
||||||
|
private String playerListUsername;
|
||||||
|
private String playerListHeader;
|
||||||
|
private String playerListFooter;
|
||||||
|
|
||||||
|
private InfoForwarding infoForwarding;
|
||||||
|
private long readTimeout;
|
||||||
|
private int debugLevel;
|
||||||
|
|
||||||
|
private boolean useEpoll;
|
||||||
|
private int bossGroupSize;
|
||||||
|
private int workerGroupSize;
|
||||||
|
|
||||||
|
private boolean useTrafficLimits;
|
||||||
|
private int maxPacketSize;
|
||||||
|
private double interval;
|
||||||
|
private double maxPacketRate;
|
||||||
|
|
||||||
|
public LimboConfig(Path root) {
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load() throws Exception {
|
||||||
|
ConfigurationOptions options = ConfigurationOptions.defaults().serializers(getSerializers());
|
||||||
|
YamlConfigurationLoader loader = YamlConfigurationLoader.builder()
|
||||||
|
.source(this::getReader)
|
||||||
|
.defaultOptions(options)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ConfigurationNode conf = loader.load();
|
||||||
|
|
||||||
|
address = conf.node("bind").get(SocketAddress.class);
|
||||||
|
maxPlayers = conf.node("maxPlayers").getInt();
|
||||||
|
pingData = conf.node("ping").get(PingData.class);
|
||||||
|
dimensionType = conf.node("dimension").getString("the_end");
|
||||||
|
if (dimensionType.equalsIgnoreCase("nether")) {
|
||||||
|
dimensionType = "the_nether";
|
||||||
|
}
|
||||||
|
if (dimensionType.equalsIgnoreCase("end")) {
|
||||||
|
dimensionType = "the_end";
|
||||||
|
}
|
||||||
|
gameMode = conf.node("gameMode").getInt();
|
||||||
|
useBrandName = conf.node("brandName", "enable").getBoolean();
|
||||||
|
useJoinMessage = conf.node("joinMessage", "enable").getBoolean();
|
||||||
|
useBossBar = conf.node("bossBar", "enable").getBoolean();
|
||||||
|
useTitle = conf.node("title", "enable").getBoolean();
|
||||||
|
usePlayerList = conf.node("playerList", "enable").getBoolean();
|
||||||
|
playerListUsername = conf.node("playerList", "username").getString();
|
||||||
|
useHeaderAndFooter = conf.node("headerAndFooter", "enable").getBoolean();
|
||||||
|
|
||||||
|
if (useBrandName)
|
||||||
|
brandName = conf.node("brandName", "content").getString();
|
||||||
|
|
||||||
|
if (useJoinMessage)
|
||||||
|
joinMessage = Colors.of(conf.node("joinMessage", "text").getString(""));
|
||||||
|
|
||||||
|
if (useBossBar)
|
||||||
|
bossBar = conf.node("bossBar").get(BossBar.class);
|
||||||
|
|
||||||
|
if (useTitle)
|
||||||
|
title = conf.node("title").get(Title.class);
|
||||||
|
|
||||||
|
if (useHeaderAndFooter) {
|
||||||
|
playerListHeader = Colors.of(conf.node("headerAndFooter", "header").getString());
|
||||||
|
playerListFooter = Colors.of(conf.node("headerAndFooter", "footer").getString());
|
||||||
|
}
|
||||||
|
|
||||||
|
infoForwarding = conf.node("infoForwarding").get(InfoForwarding.class);
|
||||||
|
readTimeout = conf.node("readTimeout").getLong();
|
||||||
|
debugLevel = conf.node("debugLevel").getInt();
|
||||||
|
|
||||||
|
useEpoll = conf.node("netty", "useEpoll").getBoolean(true);
|
||||||
|
bossGroupSize = conf.node("netty", "threads", "bossGroup").getInt(1);
|
||||||
|
workerGroupSize = conf.node("netty", "threads", "workerGroup").getInt(4);
|
||||||
|
|
||||||
|
useTrafficLimits = conf.node("traffic", "enable").getBoolean(false);
|
||||||
|
maxPacketSize = conf.node("traffic", "maxPacketSize").getInt(-1);
|
||||||
|
interval = conf.node("traffic", "interval").getDouble(-1.0);
|
||||||
|
maxPacketRate = conf.node("traffic", "maxPacketRate").getDouble(-1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BufferedReader getReader() throws IOException {
|
||||||
|
String name = "settings.yml";
|
||||||
|
Path filePath = Paths.get(root.toString(), name);
|
||||||
|
|
||||||
|
if (!Files.exists(filePath)) {
|
||||||
|
InputStream stream = getClass().getResourceAsStream( "/" + name);
|
||||||
|
|
||||||
|
if (stream == null)
|
||||||
|
throw new FileNotFoundException("Cannot find settings resource file");
|
||||||
|
|
||||||
|
Files.copy(stream, filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Files.newBufferedReader(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeSerializerCollection getSerializers() {
|
||||||
|
return TypeSerializerCollection.builder()
|
||||||
|
.register(SocketAddress.class, new SocketAddressSerializer())
|
||||||
|
.register(InfoForwarding.class, new InfoForwarding.Serializer())
|
||||||
|
.register(PingData.class, new PingData.Serializer())
|
||||||
|
.register(BossBar.class, new BossBar.Serializer())
|
||||||
|
.register(Title.class, new Title.Serializer())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SocketAddress getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxPlayers() {
|
||||||
|
return maxPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PingData getPingData() {
|
||||||
|
return pingData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDimensionType() {
|
||||||
|
return dimensionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGameMode() {
|
||||||
|
return gameMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfoForwarding getInfoForwarding() {
|
||||||
|
return infoForwarding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getReadTimeout() {
|
||||||
|
return readTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDebugLevel() {
|
||||||
|
return debugLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseBrandName() {
|
||||||
|
return useBrandName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseJoinMessage() {
|
||||||
|
return useJoinMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseBossBar() {
|
||||||
|
return useBossBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseTitle() {
|
||||||
|
return useTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUsePlayerList() {
|
||||||
|
return usePlayerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseHeaderAndFooter() {
|
||||||
|
return useHeaderAndFooter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBrandName() {
|
||||||
|
return brandName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJoinMessage() {
|
||||||
|
return joinMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BossBar getBossBar() {
|
||||||
|
return bossBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Title getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPlayerListUsername() {
|
||||||
|
return playerListUsername;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPlayerListHeader() {
|
||||||
|
return playerListHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPlayerListFooter() {
|
||||||
|
return playerListFooter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseEpoll() {
|
||||||
|
return useEpoll;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBossGroupSize() {
|
||||||
|
return bossGroupSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWorkerGroupSize() {
|
||||||
|
return workerGroupSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseTrafficLimits() {
|
||||||
|
return useTrafficLimits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxPacketSize() {
|
||||||
|
return maxPacketSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getInterval() {
|
||||||
|
return interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMaxPacketRate() {
|
||||||
|
return maxPacketRate;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.configuration;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.spongepowered.configurate.ConfigurationNode;
|
||||||
|
import org.spongepowered.configurate.serialize.TypeSerializer;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
|
||||||
|
public class SocketAddressSerializer implements TypeSerializer<SocketAddress> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SocketAddress deserialize(Type type, ConfigurationNode node) {
|
||||||
|
String ip = node.node("ip").getString();
|
||||||
|
int port = node.node("port").getInt();
|
||||||
|
SocketAddress address;
|
||||||
|
|
||||||
|
if (ip == null || ip.isEmpty()) {
|
||||||
|
address = new InetSocketAddress(port);
|
||||||
|
} else {
|
||||||
|
address = new InetSocketAddress(ip, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(Type type, @Nullable SocketAddress obj, ConfigurationNode node) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.connection;
|
||||||
|
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.ChannelPipeline;
|
||||||
|
import io.netty.handler.timeout.ReadTimeoutHandler;
|
||||||
|
import ua.nanit.limbo.connection.pipeline.*;
|
||||||
|
import ua.nanit.limbo.server.LimboServer;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class ClientChannelInitializer extends ChannelInitializer<Channel> {
|
||||||
|
|
||||||
|
private final LimboServer server;
|
||||||
|
|
||||||
|
public ClientChannelInitializer(LimboServer server) {
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initChannel(Channel channel) {
|
||||||
|
ChannelPipeline pipeline = channel.pipeline();
|
||||||
|
|
||||||
|
PacketDecoder decoder = new PacketDecoder();
|
||||||
|
PacketEncoder encoder = new PacketEncoder();
|
||||||
|
ClientConnection connection = new ClientConnection(channel, server, decoder, encoder);
|
||||||
|
|
||||||
|
pipeline.addLast("timeout", new ReadTimeoutHandler(server.getConfig().getReadTimeout(),
|
||||||
|
TimeUnit.MILLISECONDS));
|
||||||
|
pipeline.addLast("frame_decoder", new VarIntFrameDecoder());
|
||||||
|
pipeline.addLast("frame_encoder", new VarIntLengthEncoder());
|
||||||
|
|
||||||
|
if (server.getConfig().isUseTrafficLimits()) {
|
||||||
|
pipeline.addLast("traffic_limit", new ChannelTrafficHandler(
|
||||||
|
server.getConfig().getMaxPacketSize(),
|
||||||
|
server.getConfig().getInterval(),
|
||||||
|
server.getConfig().getMaxPacketRate()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline.addLast("decoder", decoder);
|
||||||
|
pipeline.addLast("encoder", encoder);
|
||||||
|
pipeline.addLast("handler", connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
347
src/main/java/ua/nanit/limbo/connection/ClientConnection.java
Normal file
347
src/main/java/ua/nanit/limbo/connection/ClientConnection.java
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.connection;
|
||||||
|
|
||||||
|
import com.grack.nanojson.JsonArray;
|
||||||
|
import com.grack.nanojson.JsonObject;
|
||||||
|
import com.grack.nanojson.JsonParser;
|
||||||
|
import com.grack.nanojson.JsonParserException;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelFutureListener;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import ua.nanit.limbo.connection.pipeline.PacketDecoder;
|
||||||
|
import ua.nanit.limbo.connection.pipeline.PacketEncoder;
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.Packet;
|
||||||
|
import ua.nanit.limbo.protocol.PacketSnapshot;
|
||||||
|
import ua.nanit.limbo.protocol.packets.login.PacketDisconnect;
|
||||||
|
import ua.nanit.limbo.protocol.packets.play.PacketKeepAlive;
|
||||||
|
import ua.nanit.limbo.protocol.registry.State;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
import ua.nanit.limbo.server.LimboServer;
|
||||||
|
import ua.nanit.limbo.server.Log;
|
||||||
|
import ua.nanit.limbo.util.UuidUtil;
|
||||||
|
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class ClientConnection extends ChannelInboundHandlerAdapter {
|
||||||
|
|
||||||
|
private final LimboServer server;
|
||||||
|
private final Channel channel;
|
||||||
|
private final GameProfile gameProfile;
|
||||||
|
|
||||||
|
private final PacketDecoder decoder;
|
||||||
|
private final PacketEncoder encoder;
|
||||||
|
|
||||||
|
private State state;
|
||||||
|
private Version clientVersion;
|
||||||
|
private SocketAddress address;
|
||||||
|
|
||||||
|
private int velocityLoginMessageId = -1;
|
||||||
|
|
||||||
|
public ClientConnection(Channel channel, LimboServer server, PacketDecoder decoder, PacketEncoder encoder) {
|
||||||
|
this.server = server;
|
||||||
|
this.channel = channel;
|
||||||
|
this.decoder = decoder;
|
||||||
|
this.encoder = encoder;
|
||||||
|
this.address = channel.remoteAddress();
|
||||||
|
this.gameProfile = new GameProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getUuid() {
|
||||||
|
return gameProfile.getUuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return gameProfile.getUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SocketAddress getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Version getClientVersion() {
|
||||||
|
return clientVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameProfile getGameProfile() {
|
||||||
|
return gameProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelInactive(@NotNull ChannelHandlerContext ctx) throws Exception {
|
||||||
|
if (state.equals(State.PLAY) || state.equals(State.CONFIGURATION)) {
|
||||||
|
server.getConnections().removeConnection(this);
|
||||||
|
}
|
||||||
|
super.channelInactive(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||||
|
if (channel.isActive()) {
|
||||||
|
Log.error("Unhandled exception: ", cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelRead(@NotNull ChannelHandlerContext ctx, @NotNull Object msg) {
|
||||||
|
handlePacket(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handlePacket(Object packet) {
|
||||||
|
if (packet instanceof Packet) {
|
||||||
|
((Packet) packet).handle(this, server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fireLoginSuccess() {
|
||||||
|
if (server.getConfig().getInfoForwarding().isModern() && velocityLoginMessageId == -1) {
|
||||||
|
disconnectLogin("You need to connect with Velocity");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendPacket(PacketSnapshots.PACKET_LOGIN_SUCCESS);
|
||||||
|
|
||||||
|
server.getConnections().addConnection(this);
|
||||||
|
|
||||||
|
// Preparing for configuration mode
|
||||||
|
if (clientVersion.moreOrEqual(Version.V1_20_2)) {
|
||||||
|
updateEncoderState(State.CONFIGURATION);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spawnPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void spawnPlayer() {
|
||||||
|
updateState(State.PLAY);
|
||||||
|
|
||||||
|
Runnable sendPlayPackets = () -> {
|
||||||
|
writePacket(PacketSnapshots.PACKET_JOIN_GAME);
|
||||||
|
writePacket(PacketSnapshots.PACKET_PLAYER_ABILITIES);
|
||||||
|
|
||||||
|
if (clientVersion.less(Version.V1_9)) {
|
||||||
|
writePacket(PacketSnapshots.PACKET_PLAYER_POS_AND_LOOK_LEGACY);
|
||||||
|
} else {
|
||||||
|
writePacket(PacketSnapshots.PACKET_PLAYER_POS_AND_LOOK);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clientVersion.moreOrEqual(Version.V1_19_3))
|
||||||
|
writePacket(PacketSnapshots.PACKET_SPAWN_POSITION);
|
||||||
|
|
||||||
|
if (server.getConfig().isUsePlayerList() || clientVersion.equals(Version.V1_16_4))
|
||||||
|
writePacket(PacketSnapshots.PACKET_PLAYER_INFO);
|
||||||
|
|
||||||
|
if (clientVersion.moreOrEqual(Version.V1_13)) {
|
||||||
|
writePacket(PacketSnapshots.PACKET_DECLARE_COMMANDS);
|
||||||
|
|
||||||
|
if (PacketSnapshots.PACKET_PLUGIN_MESSAGE != null)
|
||||||
|
writePacket(PacketSnapshots.PACKET_PLUGIN_MESSAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PacketSnapshots.PACKET_BOSS_BAR != null && clientVersion.moreOrEqual(Version.V1_9))
|
||||||
|
writePacket(PacketSnapshots.PACKET_BOSS_BAR);
|
||||||
|
|
||||||
|
if (PacketSnapshots.PACKET_JOIN_MESSAGE != null)
|
||||||
|
writePacket(PacketSnapshots.PACKET_JOIN_MESSAGE);
|
||||||
|
|
||||||
|
if (PacketSnapshots.PACKET_TITLE_TITLE != null && clientVersion.moreOrEqual(Version.V1_8))
|
||||||
|
writeTitle();
|
||||||
|
|
||||||
|
if (PacketSnapshots.PACKET_HEADER_AND_FOOTER != null && clientVersion.moreOrEqual(Version.V1_8))
|
||||||
|
writePacket(PacketSnapshots.PACKET_HEADER_AND_FOOTER);
|
||||||
|
|
||||||
|
if (clientVersion.moreOrEqual(Version.V1_20_3)) {
|
||||||
|
writePacket(PacketSnapshots.PACKET_START_WAITING_CHUNKS);
|
||||||
|
|
||||||
|
for (PacketSnapshot chunk : PacketSnapshots.PACKETS_EMPTY_CHUNKS) {
|
||||||
|
writePacket(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendKeepAlive();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (clientVersion.lessOrEqual(Version.V1_7_6)) {
|
||||||
|
this.channel.eventLoop().schedule(sendPlayPackets, 100, TimeUnit.MILLISECONDS);
|
||||||
|
} else {
|
||||||
|
sendPlayPackets.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLoginAcknowledgedReceived() {
|
||||||
|
updateState(State.CONFIGURATION);
|
||||||
|
|
||||||
|
if (PacketSnapshots.PACKET_PLUGIN_MESSAGE != null)
|
||||||
|
writePacket(PacketSnapshots.PACKET_PLUGIN_MESSAGE);
|
||||||
|
|
||||||
|
if (clientVersion.moreOrEqual(Version.V1_20_5)) {
|
||||||
|
for (PacketSnapshot packet : PacketSnapshots.PACKETS_REGISTRY_DATA) {
|
||||||
|
writePacket(packet);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
writePacket(PacketSnapshots.PACKET_REGISTRY_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendPacket(PacketSnapshots.PACKET_FINISH_CONFIGURATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnectLogin(String reason) {
|
||||||
|
if (isConnected() && state == State.LOGIN) {
|
||||||
|
PacketDisconnect disconnect = new PacketDisconnect();
|
||||||
|
disconnect.setReason(reason);
|
||||||
|
sendPacketAndClose(disconnect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTitle() {
|
||||||
|
if (clientVersion.moreOrEqual(Version.V1_17)) {
|
||||||
|
writePacket(PacketSnapshots.PACKET_TITLE_TITLE);
|
||||||
|
writePacket(PacketSnapshots.PACKET_TITLE_SUBTITLE);
|
||||||
|
writePacket(PacketSnapshots.PACKET_TITLE_TIMES);
|
||||||
|
} else {
|
||||||
|
writePacket(PacketSnapshots.PACKET_TITLE_LEGACY_TITLE);
|
||||||
|
writePacket(PacketSnapshots.PACKET_TITLE_LEGACY_SUBTITLE);
|
||||||
|
writePacket(PacketSnapshots.PACKET_TITLE_LEGACY_TIMES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendKeepAlive() {
|
||||||
|
if (state.equals(State.PLAY)) {
|
||||||
|
PacketKeepAlive keepAlive = new PacketKeepAlive();
|
||||||
|
keepAlive.setId(ThreadLocalRandom.current().nextLong());
|
||||||
|
sendPacket(keepAlive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPacket(Object packet) {
|
||||||
|
if (isConnected())
|
||||||
|
channel.writeAndFlush(packet, channel.voidPromise());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPacketAndClose(Object packet) {
|
||||||
|
if (isConnected())
|
||||||
|
channel.writeAndFlush(packet).addListener(ChannelFutureListener.CLOSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writePacket(Object packet) {
|
||||||
|
if (isConnected())
|
||||||
|
channel.write(packet, channel.voidPromise());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnected() {
|
||||||
|
return channel.isActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateState(State state) {
|
||||||
|
this.state = state;
|
||||||
|
decoder.updateState(state);
|
||||||
|
encoder.updateState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateEncoderState(State state) {
|
||||||
|
encoder.updateState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateVersion(Version version) {
|
||||||
|
clientVersion = version;
|
||||||
|
decoder.updateVersion(version);
|
||||||
|
encoder.updateVersion(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddress(String host) {
|
||||||
|
this.address = new InetSocketAddress(host, ((InetSocketAddress) this.address).getPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean checkBungeeGuardHandshake(String handshake) {
|
||||||
|
String[] split = handshake.split("\00");
|
||||||
|
|
||||||
|
if (split.length != 4)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
String socketAddressHostname = split[1];
|
||||||
|
UUID uuid = UuidUtil.fromString(split[2]);
|
||||||
|
JsonArray arr;
|
||||||
|
|
||||||
|
try {
|
||||||
|
arr = JsonParser.array().from(split[3]);
|
||||||
|
} catch (JsonParserException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String token = null;
|
||||||
|
|
||||||
|
for (Object obj : arr) {
|
||||||
|
if (obj instanceof JsonObject) {
|
||||||
|
JsonObject prop = (JsonObject) obj;
|
||||||
|
if (prop.getString("name").equals("bungeeguard-token")) {
|
||||||
|
token = prop.getString("value");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server.getConfig().getInfoForwarding().hasToken(token))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
setAddress(socketAddressHostname);
|
||||||
|
gameProfile.setUuid(uuid);
|
||||||
|
|
||||||
|
Log.debug("Successfully verified BungeeGuard token");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getVelocityLoginMessageId() {
|
||||||
|
return velocityLoginMessageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setVelocityLoginMessageId(int velocityLoginMessageId) {
|
||||||
|
this.velocityLoginMessageId = velocityLoginMessageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean checkVelocityKeyIntegrity(ByteMessage buf) {
|
||||||
|
byte[] signature = new byte[32];
|
||||||
|
buf.readBytes(signature);
|
||||||
|
byte[] data = new byte[buf.readableBytes()];
|
||||||
|
buf.getBytes(buf.readerIndex(), data);
|
||||||
|
try {
|
||||||
|
Mac mac = Mac.getInstance("HmacSHA256");
|
||||||
|
mac.init(new SecretKeySpec(server.getConfig().getInfoForwarding().getSecretKey(), "HmacSHA256"));
|
||||||
|
byte[] mySignature = mac.doFinal(data);
|
||||||
|
if (!MessageDigest.isEqual(signature, mySignature))
|
||||||
|
return false;
|
||||||
|
} catch (InvalidKeyException | java.security.NoSuchAlgorithmException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
int version = buf.readVarInt();
|
||||||
|
if (version != 1)
|
||||||
|
throw new IllegalStateException("Unsupported forwarding version " + version + ", wanted " + '\001');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
42
src/main/java/ua/nanit/limbo/connection/GameProfile.java
Normal file
42
src/main/java/ua/nanit/limbo/connection/GameProfile.java
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.connection;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class GameProfile {
|
||||||
|
|
||||||
|
private UUID uuid;
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
public UUID getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUuid(UUID uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
}
|
140
src/main/java/ua/nanit/limbo/connection/PacketHandler.java
Normal file
140
src/main/java/ua/nanit/limbo/connection/PacketHandler.java
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.connection;
|
||||||
|
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import ua.nanit.limbo.LimboConstants;
|
||||||
|
import ua.nanit.limbo.protocol.packets.PacketHandshake;
|
||||||
|
import ua.nanit.limbo.protocol.packets.configuration.PacketFinishConfiguration;
|
||||||
|
import ua.nanit.limbo.protocol.packets.login.PacketLoginAcknowledged;
|
||||||
|
import ua.nanit.limbo.protocol.packets.login.PacketLoginPluginRequest;
|
||||||
|
import ua.nanit.limbo.protocol.packets.login.PacketLoginPluginResponse;
|
||||||
|
import ua.nanit.limbo.protocol.packets.login.PacketLoginStart;
|
||||||
|
import ua.nanit.limbo.protocol.packets.status.PacketStatusPing;
|
||||||
|
import ua.nanit.limbo.protocol.packets.status.PacketStatusRequest;
|
||||||
|
import ua.nanit.limbo.protocol.packets.status.PacketStatusResponse;
|
||||||
|
import ua.nanit.limbo.server.LimboServer;
|
||||||
|
import ua.nanit.limbo.server.Log;
|
||||||
|
import ua.nanit.limbo.util.UuidUtil;
|
||||||
|
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
|
public class PacketHandler {
|
||||||
|
|
||||||
|
private final LimboServer server;
|
||||||
|
|
||||||
|
public PacketHandler(LimboServer server) {
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(ClientConnection conn, PacketHandshake packet) {
|
||||||
|
conn.updateVersion(packet.getVersion());
|
||||||
|
conn.updateState(packet.getNextState());
|
||||||
|
|
||||||
|
Log.debug("Pinged from %s [%s]", conn.getAddress(),
|
||||||
|
conn.getClientVersion().toString());
|
||||||
|
|
||||||
|
if (server.getConfig().getInfoForwarding().isLegacy()) {
|
||||||
|
String[] split = packet.getHost().split("\00");
|
||||||
|
|
||||||
|
if (split.length == 3 || split.length == 4) {
|
||||||
|
conn.setAddress(split[1]);
|
||||||
|
conn.getGameProfile().setUuid(UuidUtil.fromString(split[2]));
|
||||||
|
} else {
|
||||||
|
conn.disconnectLogin("You've enabled player info forwarding. You need to connect with proxy");
|
||||||
|
}
|
||||||
|
} else if (server.getConfig().getInfoForwarding().isBungeeGuard()) {
|
||||||
|
if (!conn.checkBungeeGuardHandshake(packet.getHost())) {
|
||||||
|
conn.disconnectLogin("Invalid BungeeGuard token or handshake format");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(ClientConnection conn, PacketStatusRequest packet) {
|
||||||
|
conn.sendPacket(new PacketStatusResponse(server));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(ClientConnection conn, PacketStatusPing packet) {
|
||||||
|
conn.sendPacketAndClose(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(ClientConnection conn, PacketLoginStart packet) {
|
||||||
|
if (server.getConfig().getMaxPlayers() > 0 &&
|
||||||
|
server.getConnections().getCount() >= server.getConfig().getMaxPlayers()) {
|
||||||
|
conn.disconnectLogin("Too many players connected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conn.getClientVersion().isSupported()) {
|
||||||
|
conn.disconnectLogin("Unsupported client version");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server.getConfig().getInfoForwarding().isModern()) {
|
||||||
|
int loginId = ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE);
|
||||||
|
PacketLoginPluginRequest request = new PacketLoginPluginRequest();
|
||||||
|
|
||||||
|
request.setMessageId(loginId);
|
||||||
|
request.setChannel(LimboConstants.VELOCITY_INFO_CHANNEL);
|
||||||
|
request.setData(Unpooled.EMPTY_BUFFER);
|
||||||
|
|
||||||
|
conn.setVelocityLoginMessageId(loginId);
|
||||||
|
conn.sendPacket(request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server.getConfig().getInfoForwarding().isModern()) {
|
||||||
|
conn.getGameProfile().setUsername(packet.getUsername());
|
||||||
|
conn.getGameProfile().setUuid(UuidUtil.getOfflineModeUuid(packet.getUsername()));
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.fireLoginSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(ClientConnection conn, PacketLoginPluginResponse packet) {
|
||||||
|
if (server.getConfig().getInfoForwarding().isModern()
|
||||||
|
&& packet.getMessageId() == conn.getVelocityLoginMessageId()) {
|
||||||
|
|
||||||
|
if (!packet.isSuccessful() || packet.getData() == null) {
|
||||||
|
conn.disconnectLogin("You need to connect with Velocity");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conn.checkVelocityKeyIntegrity(packet.getData())) {
|
||||||
|
conn.disconnectLogin("Can't verify forwarded player info");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order is important
|
||||||
|
conn.setAddress(packet.getData().readString());
|
||||||
|
conn.getGameProfile().setUuid(packet.getData().readUuid());
|
||||||
|
conn.getGameProfile().setUsername(packet.getData().readString());
|
||||||
|
|
||||||
|
conn.fireLoginSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(ClientConnection conn, PacketLoginAcknowledged packet) {
|
||||||
|
conn.onLoginAcknowledgedReceived();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(ClientConnection conn, PacketFinishConfiguration packet) {
|
||||||
|
conn.spawnPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
257
src/main/java/ua/nanit/limbo/connection/PacketSnapshots.java
Normal file
257
src/main/java/ua/nanit/limbo/connection/PacketSnapshots.java
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.connection;
|
||||||
|
|
||||||
|
import net.kyori.adventure.nbt.BinaryTag;
|
||||||
|
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||||
|
import net.kyori.adventure.nbt.ListBinaryTag;
|
||||||
|
import ua.nanit.limbo.LimboConstants;
|
||||||
|
import ua.nanit.limbo.protocol.PacketSnapshot;
|
||||||
|
import ua.nanit.limbo.protocol.packets.configuration.PacketFinishConfiguration;
|
||||||
|
import ua.nanit.limbo.protocol.packets.configuration.PacketRegistryData;
|
||||||
|
import ua.nanit.limbo.protocol.packets.login.PacketLoginSuccess;
|
||||||
|
import ua.nanit.limbo.protocol.packets.play.*;
|
||||||
|
import ua.nanit.limbo.server.LimboServer;
|
||||||
|
import ua.nanit.limbo.server.data.Title;
|
||||||
|
import ua.nanit.limbo.util.NbtMessageUtil;
|
||||||
|
import ua.nanit.limbo.util.UuidUtil;
|
||||||
|
import ua.nanit.limbo.world.Dimension;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
|
public final class PacketSnapshots {
|
||||||
|
|
||||||
|
public static PacketSnapshot PACKET_LOGIN_SUCCESS;
|
||||||
|
public static PacketSnapshot PACKET_JOIN_GAME;
|
||||||
|
public static PacketSnapshot PACKET_SPAWN_POSITION;
|
||||||
|
public static PacketSnapshot PACKET_PLUGIN_MESSAGE;
|
||||||
|
public static PacketSnapshot PACKET_PLAYER_ABILITIES;
|
||||||
|
public static PacketSnapshot PACKET_PLAYER_INFO;
|
||||||
|
public static PacketSnapshot PACKET_DECLARE_COMMANDS;
|
||||||
|
public static PacketSnapshot PACKET_JOIN_MESSAGE;
|
||||||
|
public static PacketSnapshot PACKET_BOSS_BAR;
|
||||||
|
public static PacketSnapshot PACKET_HEADER_AND_FOOTER;
|
||||||
|
|
||||||
|
public static PacketSnapshot PACKET_PLAYER_POS_AND_LOOK_LEGACY;
|
||||||
|
// For 1.19 we need to spawn player outside the world to avoid stuck in terrain loading
|
||||||
|
public static PacketSnapshot PACKET_PLAYER_POS_AND_LOOK;
|
||||||
|
|
||||||
|
public static PacketSnapshot PACKET_TITLE_TITLE;
|
||||||
|
public static PacketSnapshot PACKET_TITLE_SUBTITLE;
|
||||||
|
public static PacketSnapshot PACKET_TITLE_TIMES;
|
||||||
|
|
||||||
|
public static PacketSnapshot PACKET_TITLE_LEGACY_TITLE;
|
||||||
|
public static PacketSnapshot PACKET_TITLE_LEGACY_SUBTITLE;
|
||||||
|
public static PacketSnapshot PACKET_TITLE_LEGACY_TIMES;
|
||||||
|
|
||||||
|
public static PacketSnapshot PACKET_REGISTRY_DATA;
|
||||||
|
public static List<PacketSnapshot> PACKETS_REGISTRY_DATA;
|
||||||
|
public static PacketSnapshot PACKET_FINISH_CONFIGURATION;
|
||||||
|
|
||||||
|
public static List<PacketSnapshot> PACKETS_EMPTY_CHUNKS;
|
||||||
|
public static PacketSnapshot PACKET_START_WAITING_CHUNKS;
|
||||||
|
|
||||||
|
private PacketSnapshots() { }
|
||||||
|
|
||||||
|
public static void initPackets(LimboServer server) {
|
||||||
|
final String username = server.getConfig().getPingData().getVersion();
|
||||||
|
final UUID uuid = UuidUtil.getOfflineModeUuid(username);
|
||||||
|
|
||||||
|
PacketLoginSuccess loginSuccess = new PacketLoginSuccess();
|
||||||
|
loginSuccess.setUsername(username);
|
||||||
|
loginSuccess.setUuid(uuid);
|
||||||
|
|
||||||
|
PacketJoinGame joinGame = new PacketJoinGame();
|
||||||
|
String worldName = "minecraft:" + server.getConfig().getDimensionType().toLowerCase();
|
||||||
|
joinGame.setEntityId(0);
|
||||||
|
joinGame.setEnableRespawnScreen(true);
|
||||||
|
joinGame.setFlat(false);
|
||||||
|
joinGame.setGameMode(server.getConfig().getGameMode());
|
||||||
|
joinGame.setHardcore(false);
|
||||||
|
joinGame.setMaxPlayers(server.getConfig().getMaxPlayers());
|
||||||
|
joinGame.setPreviousGameMode(-1);
|
||||||
|
joinGame.setReducedDebugInfo(true);
|
||||||
|
joinGame.setDebug(false);
|
||||||
|
joinGame.setViewDistance(0);
|
||||||
|
joinGame.setWorldName(worldName);
|
||||||
|
joinGame.setWorldNames(worldName);
|
||||||
|
joinGame.setHashedSeed(0);
|
||||||
|
joinGame.setDimensionRegistry(server.getDimensionRegistry());
|
||||||
|
|
||||||
|
PacketPlayerAbilities playerAbilities = new PacketPlayerAbilities();
|
||||||
|
playerAbilities.setFlyingSpeed(0.0F);
|
||||||
|
playerAbilities.setFlags(0x02);
|
||||||
|
playerAbilities.setFieldOfView(0.1F);
|
||||||
|
|
||||||
|
int teleportId = ThreadLocalRandom.current().nextInt();
|
||||||
|
|
||||||
|
PacketPlayerPositionAndLook positionAndLookLegacy
|
||||||
|
= new PacketPlayerPositionAndLook(0, 64, 0, 0, 0, teleportId);
|
||||||
|
|
||||||
|
PacketPlayerPositionAndLook positionAndLook
|
||||||
|
= new PacketPlayerPositionAndLook(0, 400, 0, 0, 0, teleportId);
|
||||||
|
|
||||||
|
PacketSpawnPosition packetSpawnPosition = new PacketSpawnPosition(0, 400, 0);
|
||||||
|
|
||||||
|
PacketDeclareCommands declareCommands = new PacketDeclareCommands();
|
||||||
|
declareCommands.setCommands(Collections.emptyList());
|
||||||
|
|
||||||
|
PacketPlayerInfo info = new PacketPlayerInfo();
|
||||||
|
info.setUsername(server.getConfig().getPlayerListUsername());
|
||||||
|
info.setGameMode(server.getConfig().getGameMode());
|
||||||
|
info.setUuid(uuid);
|
||||||
|
|
||||||
|
PACKET_LOGIN_SUCCESS = PacketSnapshot.of(loginSuccess);
|
||||||
|
PACKET_JOIN_GAME = PacketSnapshot.of(joinGame);
|
||||||
|
PACKET_PLAYER_POS_AND_LOOK_LEGACY = PacketSnapshot.of(positionAndLookLegacy);
|
||||||
|
PACKET_PLAYER_POS_AND_LOOK = PacketSnapshot.of(positionAndLook);
|
||||||
|
PACKET_SPAWN_POSITION = PacketSnapshot.of(packetSpawnPosition);
|
||||||
|
PACKET_PLAYER_ABILITIES = PacketSnapshot.of(playerAbilities);
|
||||||
|
PACKET_PLAYER_INFO = PacketSnapshot.of(info);
|
||||||
|
|
||||||
|
PACKET_DECLARE_COMMANDS = PacketSnapshot.of(declareCommands);
|
||||||
|
|
||||||
|
if (server.getConfig().isUseHeaderAndFooter()) {
|
||||||
|
PacketPlayerListHeader header = new PacketPlayerListHeader();
|
||||||
|
header.setHeader(NbtMessageUtil.create(server.getConfig().getPlayerListHeader()));
|
||||||
|
header.setFooter(NbtMessageUtil.create(server.getConfig().getPlayerListFooter()));
|
||||||
|
PACKET_HEADER_AND_FOOTER = PacketSnapshot.of(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server.getConfig().isUseBrandName()){
|
||||||
|
PacketPluginMessage pluginMessage = new PacketPluginMessage();
|
||||||
|
pluginMessage.setChannel(LimboConstants.BRAND_CHANNEL);
|
||||||
|
pluginMessage.setMessage(server.getConfig().getBrandName());
|
||||||
|
PACKET_PLUGIN_MESSAGE = PacketSnapshot.of(pluginMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server.getConfig().isUseJoinMessage()) {
|
||||||
|
PacketChatMessage joinMessage = new PacketChatMessage();
|
||||||
|
joinMessage.setMessage(NbtMessageUtil.create(server.getConfig().getJoinMessage()));
|
||||||
|
joinMessage.setPosition(PacketChatMessage.PositionLegacy.SYSTEM_MESSAGE);
|
||||||
|
joinMessage.setSender(UUID.randomUUID());
|
||||||
|
PACKET_JOIN_MESSAGE = PacketSnapshot.of(joinMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server.getConfig().isUseBossBar()) {
|
||||||
|
PacketBossBar bossBar = new PacketBossBar();
|
||||||
|
bossBar.setBossBar(server.getConfig().getBossBar());
|
||||||
|
bossBar.setUuid(UUID.randomUUID());
|
||||||
|
PACKET_BOSS_BAR = PacketSnapshot.of(bossBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server.getConfig().isUseTitle()) {
|
||||||
|
Title title = server.getConfig().getTitle();
|
||||||
|
|
||||||
|
PacketTitleSetTitle packetTitle = new PacketTitleSetTitle();
|
||||||
|
PacketTitleSetSubTitle packetSubtitle = new PacketTitleSetSubTitle();
|
||||||
|
PacketTitleTimes packetTimes = new PacketTitleTimes();
|
||||||
|
|
||||||
|
PacketTitleLegacy legacyTitle = new PacketTitleLegacy();
|
||||||
|
PacketTitleLegacy legacySubtitle = new PacketTitleLegacy();
|
||||||
|
PacketTitleLegacy legacyTimes = new PacketTitleLegacy();
|
||||||
|
|
||||||
|
packetTitle.setTitle(title.getTitle());
|
||||||
|
packetSubtitle.setSubtitle(title.getSubtitle());
|
||||||
|
packetTimes.setFadeIn(title.getFadeIn());
|
||||||
|
packetTimes.setStay(title.getStay());
|
||||||
|
packetTimes.setFadeOut(title.getFadeOut());
|
||||||
|
|
||||||
|
legacyTitle.setTitle(title);
|
||||||
|
legacyTitle.setAction(PacketTitleLegacy.Action.SET_TITLE);
|
||||||
|
|
||||||
|
legacySubtitle.setTitle(title);
|
||||||
|
legacySubtitle.setAction(PacketTitleLegacy.Action.SET_SUBTITLE);
|
||||||
|
|
||||||
|
legacyTimes.setTitle(title);
|
||||||
|
legacyTimes.setAction(PacketTitleLegacy.Action.SET_TIMES_AND_DISPLAY);
|
||||||
|
|
||||||
|
PACKET_TITLE_TITLE = PacketSnapshot.of(packetTitle);
|
||||||
|
PACKET_TITLE_SUBTITLE = PacketSnapshot.of(packetSubtitle);
|
||||||
|
PACKET_TITLE_TIMES = PacketSnapshot.of(packetTimes);
|
||||||
|
|
||||||
|
PACKET_TITLE_LEGACY_TITLE = PacketSnapshot.of(legacyTitle);
|
||||||
|
PACKET_TITLE_LEGACY_SUBTITLE = PacketSnapshot.of(legacySubtitle);
|
||||||
|
PACKET_TITLE_LEGACY_TIMES = PacketSnapshot.of(legacyTimes);
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketRegistryData packetRegistryData = new PacketRegistryData();
|
||||||
|
packetRegistryData.setDimensionRegistry(server.getDimensionRegistry());
|
||||||
|
|
||||||
|
PACKET_REGISTRY_DATA = PacketSnapshot.of(packetRegistryData);
|
||||||
|
|
||||||
|
Dimension dimension1_21 = server.getDimensionRegistry().getDimension_1_21();
|
||||||
|
List<PacketSnapshot> packetRegistries = new ArrayList<>();
|
||||||
|
CompoundBinaryTag dimensionTag = dimension1_21.getData();
|
||||||
|
for (String registryType : dimensionTag.keySet()) {
|
||||||
|
CompoundBinaryTag compoundRegistryType = dimensionTag.getCompound(registryType);
|
||||||
|
|
||||||
|
PacketRegistryData registryData = new PacketRegistryData();
|
||||||
|
registryData.setDimensionRegistry(server.getDimensionRegistry());
|
||||||
|
|
||||||
|
ListBinaryTag values = compoundRegistryType.getList("value");
|
||||||
|
registryData.setMetadataWriter((message, version) -> {
|
||||||
|
message.writeString(registryType);
|
||||||
|
|
||||||
|
message.writeVarInt(values.size());
|
||||||
|
for (BinaryTag entry : values) {
|
||||||
|
CompoundBinaryTag entryTag = (CompoundBinaryTag) entry;
|
||||||
|
|
||||||
|
String name = entryTag.getString("name");
|
||||||
|
CompoundBinaryTag element = entryTag.getCompound("element");
|
||||||
|
|
||||||
|
message.writeString(name);
|
||||||
|
message.writeBoolean(true);
|
||||||
|
message.writeNamelessCompoundTag(element);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
packetRegistries.add(PacketSnapshot.of(registryData));
|
||||||
|
}
|
||||||
|
|
||||||
|
PACKETS_REGISTRY_DATA = packetRegistries;
|
||||||
|
|
||||||
|
PACKET_FINISH_CONFIGURATION = PacketSnapshot.of(new PacketFinishConfiguration());
|
||||||
|
|
||||||
|
PacketGameEvent packetGameEvent = new PacketGameEvent();
|
||||||
|
packetGameEvent.setType((byte) 13); // Waiting for chunks type
|
||||||
|
packetGameEvent.setValue(0);
|
||||||
|
PACKET_START_WAITING_CHUNKS = PacketSnapshot.of(packetGameEvent);
|
||||||
|
|
||||||
|
int chunkXOffset = (int) 0 >> 4; // Default x position is 0
|
||||||
|
int chunkZOffset = (int) 0 >> 4; // Default z position is 0
|
||||||
|
int chunkEdgeSize = 1; // TODO Make configurable?
|
||||||
|
|
||||||
|
List<PacketSnapshot> emptyChunks = new ArrayList<>();
|
||||||
|
// Make multiple chunks for edges
|
||||||
|
for (int chunkX = chunkXOffset - chunkEdgeSize; chunkX <= chunkXOffset + chunkEdgeSize; ++chunkX) {
|
||||||
|
for (int chunkZ = chunkZOffset - chunkEdgeSize; chunkZ <= chunkZOffset + chunkEdgeSize; ++chunkZ) {
|
||||||
|
PacketEmptyChunk packetEmptyChunk = new PacketEmptyChunk();
|
||||||
|
packetEmptyChunk.setX(chunkX);
|
||||||
|
packetEmptyChunk.setZ(chunkZ);
|
||||||
|
|
||||||
|
emptyChunks.add(PacketSnapshot.of(packetEmptyChunk));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PACKETS_EMPTY_CHUNKS = emptyChunks;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
package ua.nanit.limbo.connection.pipeline;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import ua.nanit.limbo.server.Log;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class ChannelTrafficHandler extends ChannelInboundHandlerAdapter {
|
||||||
|
|
||||||
|
private final int maxPacketSize;
|
||||||
|
private final double maxPacketRate;
|
||||||
|
private final PacketBucket packetBucket;
|
||||||
|
|
||||||
|
public ChannelTrafficHandler(int maxPacketSize, double interval, double maxPacketRate) {
|
||||||
|
this.maxPacketSize = maxPacketSize;
|
||||||
|
this.maxPacketRate = maxPacketRate;
|
||||||
|
this.packetBucket = (interval > 0.0 && maxPacketRate > 0.0) ? new PacketBucket(interval * 1000.0, 150) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelRead(@NotNull ChannelHandlerContext ctx, @NotNull Object msg) throws Exception {
|
||||||
|
if (msg instanceof ByteBuf) {
|
||||||
|
ByteBuf in = (ByteBuf) msg;
|
||||||
|
int bytes = in.readableBytes();
|
||||||
|
|
||||||
|
if (maxPacketSize > 0 && bytes > maxPacketSize) {
|
||||||
|
closeConnection(ctx, "Closed %s due to large packet size (%d bytes)", ctx.channel().remoteAddress(), bytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packetBucket != null) {
|
||||||
|
packetBucket.incrementPackets(1);
|
||||||
|
if (packetBucket.getCurrentPacketRate() > maxPacketRate) {
|
||||||
|
closeConnection(ctx, "Closed %s due to many packets sent (%d in the last %.1f seconds)", ctx.channel().remoteAddress(), packetBucket.sum, (packetBucket.intervalTime / 1000.0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.channelRead(ctx, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeConnection(ChannelHandlerContext ctx, String reason, Object... args) {
|
||||||
|
ctx.close();
|
||||||
|
Log.info(reason, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PacketBucket {
|
||||||
|
private static final double NANOSECONDS_TO_MILLISECONDS = 1.0e-6;
|
||||||
|
private static final int MILLISECONDS_TO_SECONDS = 1000;
|
||||||
|
|
||||||
|
private final double intervalTime;
|
||||||
|
private final double intervalResolution;
|
||||||
|
private final int[] data;
|
||||||
|
private int newestData;
|
||||||
|
private double lastBucketTime;
|
||||||
|
private int sum;
|
||||||
|
|
||||||
|
public PacketBucket(final double intervalTime, final int totalBuckets) {
|
||||||
|
this.intervalTime = intervalTime;
|
||||||
|
this.intervalResolution = intervalTime / totalBuckets;
|
||||||
|
this.data = new int[totalBuckets];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incrementPackets(final int packets) {
|
||||||
|
double timeMs = System.nanoTime() * NANOSECONDS_TO_MILLISECONDS;
|
||||||
|
double timeDelta = timeMs - this.lastBucketTime;
|
||||||
|
|
||||||
|
if (timeDelta < 0.0) {
|
||||||
|
timeDelta = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeDelta < this.intervalResolution) {
|
||||||
|
this.data[this.newestData] += packets;
|
||||||
|
this.sum += packets;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bucketsToMove = (int)(timeDelta / this.intervalResolution);
|
||||||
|
double nextBucketTime = this.lastBucketTime + bucketsToMove * this.intervalResolution;
|
||||||
|
|
||||||
|
if (bucketsToMove >= this.data.length) {
|
||||||
|
Arrays.fill(this.data, 0);
|
||||||
|
this.data[0] = packets;
|
||||||
|
this.sum = packets;
|
||||||
|
this.newestData = 0;
|
||||||
|
this.lastBucketTime = timeMs;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < bucketsToMove; ++i) {
|
||||||
|
int index = (this.newestData + i) % this.data.length;
|
||||||
|
this.sum -= this.data[index];
|
||||||
|
this.data[index] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int newestDataIndex = (this.newestData + bucketsToMove) % this.data.length;
|
||||||
|
this.sum += packets - this.data[newestDataIndex];
|
||||||
|
this.data[newestDataIndex] = packets;
|
||||||
|
this.newestData = newestDataIndex;
|
||||||
|
this.lastBucketTime = nextBucketTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCurrentPacketRate() {
|
||||||
|
return this.sum / (this.intervalTime / MILLISECONDS_TO_SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.connection.pipeline;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.Packet;
|
||||||
|
import ua.nanit.limbo.protocol.registry.State;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
import ua.nanit.limbo.server.Log;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PacketDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||||
|
|
||||||
|
private State.PacketRegistry mappings;
|
||||||
|
private Version version;
|
||||||
|
|
||||||
|
public PacketDecoder() {
|
||||||
|
updateVersion(Version.getMin());
|
||||||
|
updateState(State.HANDSHAKING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> out) throws Exception {
|
||||||
|
if (!ctx.channel().isActive() || mappings == null) return;
|
||||||
|
|
||||||
|
ByteMessage msg = new ByteMessage(buf);
|
||||||
|
int packetId = msg.readVarInt();
|
||||||
|
Packet packet = mappings.getPacket(packetId);
|
||||||
|
|
||||||
|
if (packet != null) {
|
||||||
|
Log.debug("Received packet %s[0x%s] (%d bytes)", packet.toString(), Integer.toHexString(packetId), msg.readableBytes());
|
||||||
|
try {
|
||||||
|
packet.decode(msg, version);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (Log.isDebug()) {
|
||||||
|
Log.warning("Cannot decode packet 0x%s", e, Integer.toHexString(packetId));
|
||||||
|
} else {
|
||||||
|
Log.warning("Cannot decode packet 0x%s: %s", Integer.toHexString(packetId), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fireChannelRead(packet);
|
||||||
|
} else {
|
||||||
|
Log.debug("Undefined incoming packet: 0x" + Integer.toHexString(packetId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateVersion(Version version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateState(State state) {
|
||||||
|
this.mappings = state.serverBound.getRegistry(version);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.connection.pipeline;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.MessageToByteEncoder;
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.Packet;
|
||||||
|
import ua.nanit.limbo.protocol.PacketSnapshot;
|
||||||
|
import ua.nanit.limbo.protocol.registry.State;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
import ua.nanit.limbo.server.Log;
|
||||||
|
|
||||||
|
public class PacketEncoder extends MessageToByteEncoder<Packet> {
|
||||||
|
|
||||||
|
private State.PacketRegistry registry;
|
||||||
|
private Version version;
|
||||||
|
|
||||||
|
public PacketEncoder() {
|
||||||
|
updateVersion(Version.getMin());
|
||||||
|
updateState(State.HANDSHAKING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void encode(ChannelHandlerContext ctx, Packet packet, ByteBuf out) throws Exception {
|
||||||
|
if (registry == null) return;
|
||||||
|
|
||||||
|
ByteMessage msg = new ByteMessage(out);
|
||||||
|
int packetId;
|
||||||
|
|
||||||
|
if (packet instanceof PacketSnapshot) {
|
||||||
|
packetId = registry.getPacketId(((PacketSnapshot)packet).getWrappedPacket().getClass());
|
||||||
|
} else {
|
||||||
|
packetId = registry.getPacketId(packet.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packetId == -1) {
|
||||||
|
Log.warning("Undefined packet class: %s[0x%s] (%d bytes)", packet.getClass().getName(), Integer.toHexString(packetId), msg.readableBytes());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.writeVarInt(packetId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
packet.encode(msg, version);
|
||||||
|
|
||||||
|
if (Log.isDebug()) {
|
||||||
|
Log.debug("Sending %s[0x%s] packet (%d bytes)", packet.toString(), Integer.toHexString(packetId), msg.readableBytes());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.error("Cannot encode packet 0x%s: %s", Integer.toHexString(packetId), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateVersion(Version version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateState(State state) {
|
||||||
|
this.registry = state.clientBound.getRegistry(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,21 @@
|
|||||||
package ru.nanit.limbo.connection.pipeline;
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.connection.pipeline;
|
||||||
|
|
||||||
import io.netty.util.ByteProcessor;
|
import io.netty.util.ByteProcessor;
|
||||||
|
|
@ -1,9 +1,26 @@
|
|||||||
package ru.nanit.limbo.connection.pipeline;
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.connection.pipeline;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
import ru.nanit.limbo.util.Logger;
|
import ua.nanit.limbo.server.Log;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -25,18 +42,19 @@ public class VarIntFrameDecoder extends ByteToMessageDecoder {
|
|||||||
int readVarInt = reader.getReadVarInt();
|
int readVarInt = reader.getReadVarInt();
|
||||||
int bytesRead = reader.getBytesRead();
|
int bytesRead = reader.getBytesRead();
|
||||||
if (readVarInt < 0) {
|
if (readVarInt < 0) {
|
||||||
Logger.error("[VarIntFrameDecoder] Bad data length");
|
Log.error("[VarIntFrameDecoder] Bad data length");
|
||||||
} else if (readVarInt == 0) {
|
} else if (readVarInt == 0) {
|
||||||
in.readerIndex(varIntEnd + 1);
|
in.readerIndex(varIntEnd + 1);
|
||||||
} else {
|
} else {
|
||||||
int minimumRead = bytesRead + readVarInt;
|
int minimumRead = bytesRead + readVarInt;
|
||||||
|
|
||||||
if (in.isReadable(minimumRead)) {
|
if (in.isReadable(minimumRead)) {
|
||||||
out.add(in.retainedSlice(varIntEnd + 1, readVarInt));
|
out.add(in.retainedSlice(varIntEnd + 1, readVarInt));
|
||||||
in.skipBytes(minimumRead);
|
in.skipBytes(minimumRead);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (reader.getResult() == VarIntByteDecoder.DecodeResult.TOO_BIG) {
|
} else if (reader.getResult() == VarIntByteDecoder.DecodeResult.TOO_BIG) {
|
||||||
Logger.error("[VarIntFrameDecoder] Too big data");
|
Log.error("[VarIntFrameDecoder] Too big data");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.connection.pipeline;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandler;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.MessageToByteEncoder;
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
|
||||||
|
@ChannelHandler.Sharable
|
||||||
|
public class VarIntLengthEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void encode(ChannelHandlerContext ctx, ByteBuf buf, ByteBuf out) {
|
||||||
|
ByteMessage msg = new ByteMessage(out);
|
||||||
|
msg.writeVarInt(buf.readableBytes());
|
||||||
|
msg.writeBytes(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) {
|
||||||
|
int anticipatedRequiredCapacity = 5 + msg.readableBytes();
|
||||||
|
return ctx.alloc().heapBuffer(anticipatedRequiredCapacity);
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,28 @@
|
|||||||
package ru.nanit.limbo.protocol;
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol;
|
||||||
|
|
||||||
import io.netty.buffer.*;
|
import io.netty.buffer.*;
|
||||||
import io.netty.handler.codec.DecoderException;
|
import io.netty.handler.codec.DecoderException;
|
||||||
import io.netty.handler.codec.EncoderException;
|
import io.netty.handler.codec.EncoderException;
|
||||||
import io.netty.util.ByteProcessor;
|
import io.netty.util.ByteProcessor;
|
||||||
import net.kyori.adventure.nbt.BinaryTagIO;
|
import net.kyori.adventure.nbt.*;
|
||||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -17,23 +34,26 @@ import java.nio.channels.GatheringByteChannel;
|
|||||||
import java.nio.channels.ScatteringByteChannel;
|
import java.nio.channels.ScatteringByteChannel;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.BitSet;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class ByteMessage extends ByteBuf {
|
public class ByteMessage extends ByteBuf {
|
||||||
|
|
||||||
private final ByteBuf buf;
|
private final ByteBuf buf;
|
||||||
|
|
||||||
public ByteMessage(ByteBuf buf){
|
public ByteMessage(ByteBuf buf) {
|
||||||
this.buf = buf;
|
this.buf = buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] toByteArray(){
|
public byte[] toByteArray() {
|
||||||
byte[] bytes = new byte[buf.readableBytes()];
|
byte[] bytes = new byte[buf.readableBytes()];
|
||||||
buf.readBytes(bytes);
|
buf.readBytes(bytes);
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Minecraft protocol methods */
|
/* Minecraft's protocol methods */
|
||||||
|
|
||||||
public int readVarInt() {
|
public int readVarInt() {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@ -47,18 +67,43 @@ public class ByteMessage extends ByteBuf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buf.readBytes(maxRead);
|
||||||
|
|
||||||
throw new IllegalArgumentException("Cannot read VarInt");
|
throw new IllegalArgumentException("Cannot read VarInt");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeVarInt(int value){
|
public void writeVarInt(int value) {
|
||||||
while (true) {
|
// Peel the one and two byte count cases explicitly as they are the most common VarInt sizes
|
||||||
if ((value & 0xFFFFFF80) == 0) {
|
// that the proxy will write, to improve inlining.
|
||||||
buf.writeByte(value);
|
if ((value & (0xFFFFFFFF << 7)) == 0) {
|
||||||
return;
|
buf.writeByte(value);
|
||||||
}
|
} else if ((value & (0xFFFFFFFF << 14)) == 0) {
|
||||||
|
int w = (value & 0x7F | 0x80) << 8 | (value >>> 7);
|
||||||
|
buf.writeShort(w);
|
||||||
|
} else {
|
||||||
|
writeVarIntFull(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buf.writeByte(value & 0x7F | 0x80);
|
private void writeVarIntFull(final int value) {
|
||||||
value >>>= 7;
|
// See https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/
|
||||||
|
if ((value & (0xFFFFFFFF << 7)) == 0) {
|
||||||
|
buf.writeByte(value);
|
||||||
|
} else if ((value & (0xFFFFFFFF << 14)) == 0) {
|
||||||
|
int w = (value & 0x7F | 0x80) << 8 | (value >>> 7);
|
||||||
|
buf.writeShort(w);
|
||||||
|
} else if ((value & (0xFFFFFFFF << 21)) == 0) {
|
||||||
|
int w = (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14);
|
||||||
|
buf.writeMedium(w);
|
||||||
|
} else if ((value & (0xFFFFFFFF << 28)) == 0) {
|
||||||
|
int w = (value & 0x7F | 0x80) << 24 | (((value >>> 7) & 0x7F | 0x80) << 16)
|
||||||
|
| ((value >>> 14) & 0x7F | 0x80) << 8 | (value >>> 21);
|
||||||
|
buf.writeInt(w);
|
||||||
|
} else {
|
||||||
|
int w = (value & 0x7F | 0x80) << 24 | ((value >>> 7) & 0x7F | 0x80) << 16
|
||||||
|
| ((value >>> 14) & 0x7F | 0x80) << 8 | ((value >>> 21) & 0x7F | 0x80);
|
||||||
|
buf.writeInt(w);
|
||||||
|
buf.writeByte(value >>> 28);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +123,7 @@ public class ByteMessage extends ByteBuf {
|
|||||||
buf.writeCharSequence(str, StandardCharsets.UTF_8);
|
buf.writeCharSequence(str, StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] readBytesArray(){
|
public byte[] readBytesArray() {
|
||||||
int length = readVarInt();
|
int length = readVarInt();
|
||||||
byte[] array = new byte[length];
|
byte[] array = new byte[length];
|
||||||
buf.readBytes(array);
|
buf.readBytes(array);
|
||||||
@ -141,35 +186,111 @@ public class ByteMessage extends ByteBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void writeCompoundTagArray(CompoundBinaryTag[] compoundTags) {
|
public void writeCompoundTagArray(CompoundBinaryTag[] compoundTags) {
|
||||||
try {
|
try (ByteBufOutputStream stream = new ByteBufOutputStream(buf)) {
|
||||||
ByteBufOutputStream stream = new ByteBufOutputStream(buf);
|
|
||||||
|
|
||||||
writeVarInt(compoundTags.length);
|
writeVarInt(compoundTags.length);
|
||||||
|
|
||||||
for (CompoundBinaryTag tag : compoundTags){
|
for (CompoundBinaryTag tag : compoundTags) {
|
||||||
BinaryTagIO.writeDataOutput(tag, stream);
|
BinaryTagIO.writer().write(tag, (OutputStream) stream);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
}
|
||||||
|
catch (IOException e) {
|
||||||
throw new EncoderException("Cannot write NBT CompoundTag");
|
throw new EncoderException("Cannot write NBT CompoundTag");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompoundBinaryTag readCompoundTag() {
|
public CompoundBinaryTag readCompoundTag() {
|
||||||
try {
|
try (ByteBufInputStream stream = new ByteBufInputStream(buf)) {
|
||||||
return BinaryTagIO.readDataInput(new ByteBufInputStream(buf));
|
return BinaryTagIO.reader().read((InputStream) stream);
|
||||||
} catch (IOException thrown) {
|
}
|
||||||
|
catch (IOException thrown) {
|
||||||
throw new DecoderException("Cannot read NBT CompoundTag");
|
throw new DecoderException("Cannot read NBT CompoundTag");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeCompoundTag(CompoundBinaryTag compoundTag) {
|
public void writeCompoundTag(CompoundBinaryTag compoundTag) {
|
||||||
try {
|
try (ByteBufOutputStream stream = new ByteBufOutputStream(buf)) {
|
||||||
BinaryTagIO.writeDataOutput(compoundTag, new ByteBufOutputStream(buf));
|
BinaryTagIO.writer().write(compoundTag, (OutputStream) stream);
|
||||||
} catch (IOException e) {
|
}
|
||||||
|
catch (IOException e) {
|
||||||
throw new EncoderException("Cannot write NBT CompoundTag");
|
throw new EncoderException("Cannot write NBT CompoundTag");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void writeNamelessCompoundTag(BinaryTag binaryTag) {
|
||||||
|
try (ByteBufOutputStream stream = new ByteBufOutputStream(buf)) {
|
||||||
|
stream.writeByte(binaryTag.type().id());
|
||||||
|
|
||||||
|
// TODO Find a way to improve this...
|
||||||
|
if (binaryTag instanceof CompoundBinaryTag) {
|
||||||
|
CompoundBinaryTag tag = (CompoundBinaryTag) binaryTag;
|
||||||
|
tag.type().write(tag, stream);
|
||||||
|
}
|
||||||
|
else if (binaryTag instanceof ByteBinaryTag) {
|
||||||
|
ByteBinaryTag tag = (ByteBinaryTag) binaryTag;
|
||||||
|
tag.type().write(tag, stream);
|
||||||
|
}
|
||||||
|
else if (binaryTag instanceof ShortBinaryTag) {
|
||||||
|
ShortBinaryTag tag = (ShortBinaryTag) binaryTag;
|
||||||
|
tag.type().write(tag, stream);
|
||||||
|
}
|
||||||
|
else if (binaryTag instanceof IntBinaryTag) {
|
||||||
|
IntBinaryTag tag = (IntBinaryTag) binaryTag;
|
||||||
|
tag.type().write(tag, stream);
|
||||||
|
}
|
||||||
|
else if (binaryTag instanceof LongBinaryTag) {
|
||||||
|
LongBinaryTag tag = (LongBinaryTag) binaryTag;
|
||||||
|
tag.type().write(tag, stream);
|
||||||
|
}
|
||||||
|
else if (binaryTag instanceof DoubleBinaryTag) {
|
||||||
|
DoubleBinaryTag tag = (DoubleBinaryTag) binaryTag;
|
||||||
|
tag.type().write(tag, stream);
|
||||||
|
}
|
||||||
|
else if (binaryTag instanceof StringBinaryTag) {
|
||||||
|
StringBinaryTag tag = (StringBinaryTag) binaryTag;
|
||||||
|
tag.type().write(tag, stream);
|
||||||
|
}
|
||||||
|
else if (binaryTag instanceof ListBinaryTag) {
|
||||||
|
ListBinaryTag tag = (ListBinaryTag) binaryTag;
|
||||||
|
tag.type().write(tag, stream);
|
||||||
|
}
|
||||||
|
else if (binaryTag instanceof EndBinaryTag) {
|
||||||
|
EndBinaryTag tag = (EndBinaryTag) binaryTag;
|
||||||
|
tag.type().write(tag, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new EncoderException("Cannot write NBT CompoundTag");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeNbtMessage(NbtMessage nbtMessage, Version version) {
|
||||||
|
if (version.moreOrEqual(Version.V1_20_3)) {
|
||||||
|
writeNamelessCompoundTag(nbtMessage.getTag());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
writeString(nbtMessage.getJson());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E extends Enum<E>> void writeEnumSet(EnumSet<E> enumset, Class<E> oclass) {
|
||||||
|
E[] enums = oclass.getEnumConstants();
|
||||||
|
BitSet bits = new BitSet(enums.length);
|
||||||
|
|
||||||
|
for (int i = 0; i < enums.length; ++i) {
|
||||||
|
bits.set(i, enumset.contains(enums[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFixedBitSet(bits, enums.length, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeFixedBitSet(BitSet bits, int size, ByteBuf buf) {
|
||||||
|
if (bits.length() > size) {
|
||||||
|
throw new StackOverflowError("BitSet too large (expected " + size + " got " + bits.size() + ")");
|
||||||
|
}
|
||||||
|
buf.writeBytes(Arrays.copyOf(bits.toByteArray(), (size + 8) >> 3));
|
||||||
|
}
|
||||||
|
|
||||||
/* Delegated methods */
|
/* Delegated methods */
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1139,7 +1260,7 @@ public class ByteMessage extends ByteBuf {
|
|||||||
return buf.release(decrement);
|
return buf.release(decrement);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ByteMessage create(){
|
public static ByteMessage create() {
|
||||||
return new ByteMessage(Unpooled.buffer());
|
return new ByteMessage(Unpooled.buffer());
|
||||||
}
|
}
|
||||||
}
|
}
|
10
src/main/java/ua/nanit/limbo/protocol/MetadataWriter.java
Normal file
10
src/main/java/ua/nanit/limbo/protocol/MetadataWriter.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package ua.nanit.limbo.protocol;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface MetadataWriter {
|
||||||
|
|
||||||
|
void writeData(ByteMessage message, Version version);
|
||||||
|
|
||||||
|
}
|
30
src/main/java/ua/nanit/limbo/protocol/NbtMessage.java
Normal file
30
src/main/java/ua/nanit/limbo/protocol/NbtMessage.java
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package ua.nanit.limbo.protocol;
|
||||||
|
|
||||||
|
import net.kyori.adventure.nbt.BinaryTag;
|
||||||
|
|
||||||
|
public class NbtMessage {
|
||||||
|
|
||||||
|
private String json;
|
||||||
|
private BinaryTag tag;
|
||||||
|
|
||||||
|
public NbtMessage(String json, BinaryTag tag) {
|
||||||
|
this.json = json;
|
||||||
|
this.tag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJson() {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setJson(String json) {
|
||||||
|
this.json = json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BinaryTag getTag() {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTag(BinaryTag tag) {
|
||||||
|
this.tag = tag;
|
||||||
|
}
|
||||||
|
}
|
34
src/main/java/ua/nanit/limbo/protocol/Packet.java
Normal file
34
src/main/java/ua/nanit/limbo/protocol/Packet.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.connection.ClientConnection;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
import ua.nanit.limbo.server.LimboServer;
|
||||||
|
|
||||||
|
public interface Packet {
|
||||||
|
|
||||||
|
void encode(ByteMessage msg, Version version);
|
||||||
|
|
||||||
|
void decode(ByteMessage msg, Version version);
|
||||||
|
|
||||||
|
default void handle(ClientConnection conn, LimboServer server) {
|
||||||
|
// Ignored by default
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
src/main/java/ua/nanit/limbo/protocol/PacketIn.java
Normal file
29
src/main/java/ua/nanit/limbo/protocol/PacketIn.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public interface PacketIn extends Packet {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void encode(ByteMessage msg, Version version) {
|
||||||
|
// Can be ignored for incoming packets
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
src/main/java/ua/nanit/limbo/protocol/PacketOut.java
Normal file
29
src/main/java/ua/nanit/limbo/protocol/PacketOut.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public interface PacketOut extends Packet {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void decode(ByteMessage msg, Version version) {
|
||||||
|
// Can be ignored for outgoing packets
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
88
src/main/java/ua/nanit/limbo/protocol/PacketSnapshot.java
Normal file
88
src/main/java/ua/nanit/limbo/protocol/PacketSnapshot.java
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PacketSnapshot encodes a packet to byte array for each MC version.
|
||||||
|
* Some versions have the same snapshot, so there are mappings to avoid data copying
|
||||||
|
*/
|
||||||
|
public class PacketSnapshot implements PacketOut {
|
||||||
|
|
||||||
|
private final PacketOut packet;
|
||||||
|
private final Map<Version, byte[]> versionMessages = new HashMap<>();
|
||||||
|
private final Map<Version, Version> mappings = new HashMap<>();
|
||||||
|
|
||||||
|
public PacketSnapshot(PacketOut packet) {
|
||||||
|
this.packet = packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketOut getWrappedPacket() {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encode() {
|
||||||
|
Map<Integer, Version> hashes = new HashMap<>();
|
||||||
|
|
||||||
|
for (Version version : Version.values()) {
|
||||||
|
if (version.equals(Version.UNDEFINED)) continue;
|
||||||
|
|
||||||
|
ByteMessage encodedMessage = ByteMessage.create();
|
||||||
|
packet.encode(encodedMessage, version);
|
||||||
|
|
||||||
|
int hash = encodedMessage.hashCode();
|
||||||
|
Version hashed = hashes.get(hash);
|
||||||
|
|
||||||
|
if (hashed != null) {
|
||||||
|
mappings.put(version, hashed);
|
||||||
|
} else {
|
||||||
|
hashes.put(hash, version);
|
||||||
|
mappings.put(version, version);
|
||||||
|
versionMessages.put(version, encodedMessage.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
encodedMessage.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
Version mapped = mappings.get(version);
|
||||||
|
byte[] message = versionMessages.get(mapped);
|
||||||
|
|
||||||
|
if (message != null)
|
||||||
|
msg.writeBytes(message);
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException("No mappings for version " + version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return packet.getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PacketSnapshot of(PacketOut packet) {
|
||||||
|
PacketSnapshot snapshot = new PacketSnapshot(packet);
|
||||||
|
snapshot.encode();
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.connection.ClientConnection;
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketIn;
|
||||||
|
import ua.nanit.limbo.protocol.registry.State;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
import ua.nanit.limbo.server.LimboServer;
|
||||||
|
|
||||||
|
public class PacketHandshake implements PacketIn {
|
||||||
|
|
||||||
|
private Version version;
|
||||||
|
private String host;
|
||||||
|
private int port;
|
||||||
|
private State nextState;
|
||||||
|
|
||||||
|
public Version getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public State getNextState() {
|
||||||
|
return nextState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decode(ByteMessage msg, Version version) {
|
||||||
|
try {
|
||||||
|
this.version = Version.of(msg.readVarInt());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
this.version = Version.UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.host = msg.readString();
|
||||||
|
this.port = msg.readUnsignedShort();
|
||||||
|
this.nextState = State.getById(msg.readVarInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(ClientConnection conn, LimboServer server) {
|
||||||
|
server.getPacketHandler().handle(conn, this);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package ua.nanit.limbo.protocol.packets.configuration;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.connection.ClientConnection;
|
||||||
|
import ua.nanit.limbo.protocol.PacketIn;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.server.LimboServer;
|
||||||
|
|
||||||
|
public class PacketFinishConfiguration implements PacketIn, PacketOut {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(ClientConnection conn, LimboServer server) {
|
||||||
|
server.getPacketHandler().handle(conn, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package ua.nanit.limbo.protocol.packets.configuration;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.MetadataWriter;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
import ua.nanit.limbo.world.DimensionRegistry;
|
||||||
|
|
||||||
|
public class PacketRegistryData implements PacketOut {
|
||||||
|
|
||||||
|
private DimensionRegistry dimensionRegistry;
|
||||||
|
private MetadataWriter metadataWriter;
|
||||||
|
|
||||||
|
public void setDimensionRegistry(DimensionRegistry dimensionRegistry) {
|
||||||
|
this.dimensionRegistry = dimensionRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMetadataWriter(MetadataWriter metadataWriter) {
|
||||||
|
this.metadataWriter = metadataWriter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
if (metadataWriter != null) {
|
||||||
|
if (version.moreOrEqual(Version.V1_20_5)) {
|
||||||
|
metadataWriter.writeData(msg, version);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msg.writeNamelessCompoundTag(dimensionRegistry.getCodec_1_20());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.login;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketDisconnect implements PacketOut {
|
||||||
|
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
public void setReason(String reason) {
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeString(String.format("{\"text\": \"%s\"}", reason));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package ua.nanit.limbo.protocol.packets.login;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.connection.ClientConnection;
|
||||||
|
import ua.nanit.limbo.protocol.PacketIn;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.server.LimboServer;
|
||||||
|
|
||||||
|
public class PacketLoginAcknowledged implements PacketIn, PacketOut {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(ClientConnection conn, LimboServer server) {
|
||||||
|
server.getPacketHandler().handle(conn, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.login;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketLoginPluginRequest implements PacketOut {
|
||||||
|
|
||||||
|
private int messageId;
|
||||||
|
private String channel;
|
||||||
|
private ByteBuf data;
|
||||||
|
|
||||||
|
public void setMessageId(int messageId) {
|
||||||
|
this.messageId = messageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChannel(String channel) {
|
||||||
|
this.channel = channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(ByteBuf data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeVarInt(messageId);
|
||||||
|
msg.writeString(channel);
|
||||||
|
msg.writeBytes(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.login;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.connection.ClientConnection;
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketIn;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
import ua.nanit.limbo.server.LimboServer;
|
||||||
|
|
||||||
|
public class PacketLoginPluginResponse implements PacketIn {
|
||||||
|
|
||||||
|
private int messageId;
|
||||||
|
private boolean successful;
|
||||||
|
private ByteMessage data;
|
||||||
|
|
||||||
|
public int getMessageId() {
|
||||||
|
return messageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuccessful() {
|
||||||
|
return successful;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteMessage getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decode(ByteMessage msg, Version version) {
|
||||||
|
messageId = msg.readVarInt();
|
||||||
|
successful = msg.readBoolean();
|
||||||
|
|
||||||
|
if (msg.readableBytes() > 0) {
|
||||||
|
int i = msg.readableBytes();
|
||||||
|
data = new ByteMessage(msg.readBytes(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(ClientConnection conn, LimboServer server) {
|
||||||
|
server.getPacketHandler().handle(conn, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.login;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.connection.ClientConnection;
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketIn;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
import ua.nanit.limbo.server.LimboServer;
|
||||||
|
|
||||||
|
public class PacketLoginStart implements PacketIn {
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decode(ByteMessage msg, Version version) {
|
||||||
|
this.username = msg.readString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(ClientConnection conn, LimboServer server) {
|
||||||
|
server.getPacketHandler().handle(conn, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.login;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class PacketLoginSuccess implements PacketOut {
|
||||||
|
|
||||||
|
private UUID uuid;
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
public void setUuid(UUID uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
if (version.moreOrEqual(Version.V1_16)) {
|
||||||
|
msg.writeUuid(uuid);
|
||||||
|
} else if (version.moreOrEqual(Version.V1_7_6)) {
|
||||||
|
msg.writeString(uuid.toString());
|
||||||
|
} else {
|
||||||
|
msg.writeString(uuid.toString().replace("-", ""));
|
||||||
|
}
|
||||||
|
msg.writeString(username);
|
||||||
|
if (version.moreOrEqual(Version.V1_19)) {
|
||||||
|
msg.writeVarInt(0);
|
||||||
|
}
|
||||||
|
if (version.moreOrEqual(Version.V1_20_5)) {
|
||||||
|
msg.writeBoolean(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
import ua.nanit.limbo.server.data.BossBar;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet for 1.9+
|
||||||
|
*/
|
||||||
|
public class PacketBossBar implements PacketOut {
|
||||||
|
|
||||||
|
private UUID uuid;
|
||||||
|
private BossBar bossBar;
|
||||||
|
private int flags;
|
||||||
|
|
||||||
|
public void setUuid(UUID uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBossBar(BossBar bossBar) {
|
||||||
|
this.bossBar = bossBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFlags(int flags) {
|
||||||
|
this.flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeUuid(uuid);
|
||||||
|
msg.writeVarInt(0); // Create bossbar
|
||||||
|
msg.writeNbtMessage(bossBar.getText(), version);
|
||||||
|
msg.writeFloat(bossBar.getHealth());
|
||||||
|
msg.writeVarInt(bossBar.getColor().getIndex());
|
||||||
|
msg.writeVarInt(bossBar.getDivision().getIndex());
|
||||||
|
msg.writeByte(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.NbtMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class PacketChatMessage implements PacketOut {
|
||||||
|
|
||||||
|
private NbtMessage message;
|
||||||
|
private PositionLegacy position;
|
||||||
|
private UUID sender;
|
||||||
|
|
||||||
|
public void setMessage(NbtMessage message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPosition(PositionLegacy position) {
|
||||||
|
this.position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSender(UUID sender) {
|
||||||
|
this.sender = sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeNbtMessage(message, version);
|
||||||
|
if (version.moreOrEqual(Version.V1_19_1)) {
|
||||||
|
msg.writeBoolean(position.index == PositionLegacy.ACTION_BAR.index);
|
||||||
|
} else if (version.moreOrEqual(Version.V1_19)) {
|
||||||
|
msg.writeVarInt(position.index);
|
||||||
|
} else if (version.moreOrEqual(Version.V1_8)) {
|
||||||
|
msg.writeByte(position.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.moreOrEqual(Version.V1_16) && version.less(Version.V1_19))
|
||||||
|
msg.writeUuid(sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PositionLegacy {
|
||||||
|
|
||||||
|
CHAT(0),
|
||||||
|
SYSTEM_MESSAGE(1),
|
||||||
|
ACTION_BAR(2);
|
||||||
|
|
||||||
|
private final int index;
|
||||||
|
|
||||||
|
PositionLegacy(int index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet for 1.13+
|
||||||
|
*/
|
||||||
|
public class PacketDeclareCommands implements PacketOut {
|
||||||
|
|
||||||
|
private List<String> commands;
|
||||||
|
|
||||||
|
public void setCommands(List<String> commands) {
|
||||||
|
this.commands = commands;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeVarInt(commands.size() * 2 + 1); // +1 because declaring root node
|
||||||
|
|
||||||
|
// Declare root node
|
||||||
|
|
||||||
|
msg.writeByte(0);
|
||||||
|
msg.writeVarInt(commands.size());
|
||||||
|
|
||||||
|
for (int i = 1; i <= commands.size() * 2; i++) {
|
||||||
|
msg.writeVarInt(i++);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare other commands
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
for (String cmd : commands) {
|
||||||
|
msg.writeByte(1 | 0x04);
|
||||||
|
msg.writeVarInt(1);
|
||||||
|
msg.writeVarInt(i + 1);
|
||||||
|
msg.writeString(cmd);
|
||||||
|
i++;
|
||||||
|
|
||||||
|
msg.writeByte(2 | 0x04 | 0x10);
|
||||||
|
msg.writeVarInt(1);
|
||||||
|
msg.writeVarInt(i);
|
||||||
|
msg.writeString("arg");
|
||||||
|
msg.writeString("brigadier:string");
|
||||||
|
msg.writeVarInt(0);
|
||||||
|
msg.writeString("minecraft:ask_server");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.writeVarInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||||
|
import net.kyori.adventure.nbt.LongArrayBinaryTag;
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketEmptyChunk implements PacketOut {
|
||||||
|
|
||||||
|
private int x;
|
||||||
|
private int z;
|
||||||
|
|
||||||
|
public void setX(int x) {
|
||||||
|
this.x = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setZ(int z) {
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeInt(x);
|
||||||
|
msg.writeInt(z);
|
||||||
|
|
||||||
|
LongArrayBinaryTag longArrayTag = LongArrayBinaryTag.longArrayBinaryTag(new long[37]);
|
||||||
|
CompoundBinaryTag tag = CompoundBinaryTag.builder()
|
||||||
|
.put("MOTION_BLOCKING", longArrayTag).build();
|
||||||
|
CompoundBinaryTag rootTag = CompoundBinaryTag.builder()
|
||||||
|
.put("root", tag).build();
|
||||||
|
msg.writeNamelessCompoundTag(rootTag);
|
||||||
|
|
||||||
|
byte[] sectionData = new byte[]{0, 0, 0, 0, 0, 0, 1, 0};
|
||||||
|
msg.writeVarInt(sectionData.length * 16);
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
msg.writeBytes(sectionData);
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.writeVarInt(0);
|
||||||
|
|
||||||
|
byte[] lightData = new byte[]{1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 3, -1, -1, 0, 0};
|
||||||
|
msg.ensureWritable(lightData.length);
|
||||||
|
msg.writeBytes(lightData, 1, lightData.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketGameEvent implements PacketOut {
|
||||||
|
|
||||||
|
private byte type;
|
||||||
|
private float value;
|
||||||
|
|
||||||
|
public void setType(byte type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(float value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeByte(type);
|
||||||
|
msg.writeFloat(value);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,305 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
import ua.nanit.limbo.world.DimensionRegistry;
|
||||||
|
|
||||||
|
public class PacketJoinGame implements PacketOut {
|
||||||
|
|
||||||
|
private int entityId;
|
||||||
|
private boolean isHardcore = false;
|
||||||
|
private int gameMode = 2;
|
||||||
|
private int previousGameMode = -1;
|
||||||
|
private String[] worldNames;
|
||||||
|
private DimensionRegistry dimensionRegistry;
|
||||||
|
private String worldName;
|
||||||
|
private long hashedSeed;
|
||||||
|
private int maxPlayers;
|
||||||
|
private int viewDistance = 2;
|
||||||
|
private boolean reducedDebugInfo;
|
||||||
|
private boolean enableRespawnScreen;
|
||||||
|
private boolean isDebug;
|
||||||
|
private boolean isFlat;
|
||||||
|
private boolean limitedCrafting;
|
||||||
|
private boolean secureProfile;
|
||||||
|
|
||||||
|
public void setEntityId(int entityId) {
|
||||||
|
this.entityId = entityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHardcore(boolean hardcore) {
|
||||||
|
isHardcore = hardcore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGameMode(int gameMode) {
|
||||||
|
this.gameMode = gameMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreviousGameMode(int previousGameMode) {
|
||||||
|
this.previousGameMode = previousGameMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWorldNames(String... worldNames) {
|
||||||
|
this.worldNames = worldNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDimensionRegistry(DimensionRegistry dimensionRegistry) {
|
||||||
|
this.dimensionRegistry = dimensionRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWorldName(String worldName) {
|
||||||
|
this.worldName = worldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHashedSeed(long hashedSeed) {
|
||||||
|
this.hashedSeed = hashedSeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxPlayers(int maxPlayers) {
|
||||||
|
this.maxPlayers = maxPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setViewDistance(int viewDistance) {
|
||||||
|
this.viewDistance = viewDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReducedDebugInfo(boolean reducedDebugInfo) {
|
||||||
|
this.reducedDebugInfo = reducedDebugInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnableRespawnScreen(boolean enableRespawnScreen) {
|
||||||
|
this.enableRespawnScreen = enableRespawnScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDebug(boolean debug) {
|
||||||
|
isDebug = debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFlat(boolean flat) {
|
||||||
|
isFlat = flat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLimitedCrafting(boolean limitedCrafting) {
|
||||||
|
this.limitedCrafting = limitedCrafting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecureProfile(boolean secureProfile) {
|
||||||
|
this.secureProfile = secureProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeInt(entityId);
|
||||||
|
|
||||||
|
if (version.fromTo(Version.V1_7_2, Version.V1_7_6)) {
|
||||||
|
msg.writeByte(gameMode == 3 ? 1 : gameMode);
|
||||||
|
msg.writeByte(dimensionRegistry.getDefaultDimension_1_16().getId());
|
||||||
|
msg.writeByte(0); // Difficulty
|
||||||
|
msg.writeByte(maxPlayers);
|
||||||
|
msg.writeString("flat"); // Level type
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.fromTo(Version.V1_8, Version.V1_9)) {
|
||||||
|
msg.writeByte(gameMode);
|
||||||
|
msg.writeByte(dimensionRegistry.getDefaultDimension_1_16().getId());
|
||||||
|
msg.writeByte(0); // Difficulty
|
||||||
|
msg.writeByte(maxPlayers);
|
||||||
|
msg.writeString("flat"); // Level type
|
||||||
|
msg.writeBoolean(reducedDebugInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.fromTo(Version.V1_9_1, Version.V1_13_2)) {
|
||||||
|
msg.writeByte(gameMode);
|
||||||
|
msg.writeInt(dimensionRegistry.getDefaultDimension_1_16().getId());
|
||||||
|
msg.writeByte(0); // Difficulty
|
||||||
|
msg.writeByte(maxPlayers);
|
||||||
|
msg.writeString("flat"); // Level type
|
||||||
|
msg.writeBoolean(reducedDebugInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.fromTo(Version.V1_14, Version.V1_14_4)) {
|
||||||
|
msg.writeByte(gameMode);
|
||||||
|
msg.writeInt(dimensionRegistry.getDefaultDimension_1_16().getId());
|
||||||
|
msg.writeByte(maxPlayers);
|
||||||
|
msg.writeString("flat"); // Level type
|
||||||
|
msg.writeVarInt(viewDistance);
|
||||||
|
msg.writeBoolean(reducedDebugInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.fromTo(Version.V1_15, Version.V1_15_2)) {
|
||||||
|
msg.writeByte(gameMode);
|
||||||
|
msg.writeInt(dimensionRegistry.getDefaultDimension_1_16().getId());
|
||||||
|
msg.writeLong(hashedSeed);
|
||||||
|
msg.writeByte(maxPlayers);
|
||||||
|
msg.writeString("flat"); // Level type
|
||||||
|
msg.writeVarInt(viewDistance);
|
||||||
|
msg.writeBoolean(reducedDebugInfo);
|
||||||
|
msg.writeBoolean(enableRespawnScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.fromTo(Version.V1_16, Version.V1_16_1)) {
|
||||||
|
msg.writeByte(gameMode);
|
||||||
|
msg.writeByte(previousGameMode);
|
||||||
|
msg.writeStringsArray(worldNames);
|
||||||
|
msg.writeCompoundTag(dimensionRegistry.getOldCodec());
|
||||||
|
msg.writeString(dimensionRegistry.getDefaultDimension_1_16().getName());
|
||||||
|
msg.writeString(worldName);
|
||||||
|
msg.writeLong(hashedSeed);
|
||||||
|
msg.writeByte(maxPlayers);
|
||||||
|
msg.writeVarInt(viewDistance);
|
||||||
|
msg.writeBoolean(reducedDebugInfo);
|
||||||
|
msg.writeBoolean(enableRespawnScreen);
|
||||||
|
msg.writeBoolean(isDebug);
|
||||||
|
msg.writeBoolean(isFlat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.fromTo(Version.V1_16_2, Version.V1_17_1)) {
|
||||||
|
msg.writeBoolean(isHardcore);
|
||||||
|
msg.writeByte(gameMode);
|
||||||
|
msg.writeByte(previousGameMode);
|
||||||
|
msg.writeStringsArray(worldNames);
|
||||||
|
msg.writeCompoundTag(dimensionRegistry.getCodec_1_16());
|
||||||
|
msg.writeCompoundTag(dimensionRegistry.getDefaultDimension_1_16().getData());
|
||||||
|
msg.writeString(worldName);
|
||||||
|
msg.writeLong(hashedSeed);
|
||||||
|
msg.writeVarInt(maxPlayers);
|
||||||
|
msg.writeVarInt(viewDistance);
|
||||||
|
msg.writeBoolean(reducedDebugInfo);
|
||||||
|
msg.writeBoolean(enableRespawnScreen);
|
||||||
|
msg.writeBoolean(isDebug);
|
||||||
|
msg.writeBoolean(isFlat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.fromTo(Version.V1_18, Version.V1_18_2)) {
|
||||||
|
msg.writeBoolean(isHardcore);
|
||||||
|
msg.writeByte(gameMode);
|
||||||
|
msg.writeByte(previousGameMode);
|
||||||
|
msg.writeStringsArray(worldNames);
|
||||||
|
if (version.moreOrEqual(Version.V1_18_2)) {
|
||||||
|
msg.writeCompoundTag(dimensionRegistry.getCodec_1_18_2());
|
||||||
|
msg.writeCompoundTag(dimensionRegistry.getDefaultDimension_1_18_2().getData());
|
||||||
|
} else {
|
||||||
|
msg.writeCompoundTag(dimensionRegistry.getCodec_1_16());
|
||||||
|
msg.writeCompoundTag(dimensionRegistry.getDefaultDimension_1_16().getData());
|
||||||
|
}
|
||||||
|
msg.writeString(worldName);
|
||||||
|
msg.writeLong(hashedSeed);
|
||||||
|
msg.writeVarInt(maxPlayers);
|
||||||
|
msg.writeVarInt(viewDistance);
|
||||||
|
msg.writeVarInt(viewDistance); // Simulation Distance
|
||||||
|
msg.writeBoolean(reducedDebugInfo);
|
||||||
|
msg.writeBoolean(enableRespawnScreen);
|
||||||
|
msg.writeBoolean(isDebug);
|
||||||
|
msg.writeBoolean(isFlat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.fromTo(Version.V1_19, Version.V1_19_4)) {
|
||||||
|
msg.writeBoolean(isHardcore);
|
||||||
|
msg.writeByte(gameMode);
|
||||||
|
msg.writeByte(previousGameMode);
|
||||||
|
msg.writeStringsArray(worldNames);
|
||||||
|
if (version.moreOrEqual(Version.V1_19_1)) {
|
||||||
|
if (version.moreOrEqual(Version.V1_19_4)) {
|
||||||
|
msg.writeCompoundTag(dimensionRegistry.getCodec_1_19_4());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msg.writeCompoundTag(dimensionRegistry.getCodec_1_19_1());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msg.writeCompoundTag(dimensionRegistry.getCodec_1_19());
|
||||||
|
}
|
||||||
|
msg.writeString(worldName); // World type
|
||||||
|
msg.writeString(worldName);
|
||||||
|
msg.writeLong(hashedSeed);
|
||||||
|
msg.writeVarInt(maxPlayers);
|
||||||
|
msg.writeVarInt(viewDistance);
|
||||||
|
msg.writeVarInt(viewDistance); // Simulation Distance
|
||||||
|
msg.writeBoolean(reducedDebugInfo);
|
||||||
|
msg.writeBoolean(enableRespawnScreen);
|
||||||
|
msg.writeBoolean(isDebug);
|
||||||
|
msg.writeBoolean(isFlat);
|
||||||
|
msg.writeBoolean(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.equals(Version.V1_20)) {
|
||||||
|
msg.writeBoolean(isHardcore);
|
||||||
|
msg.writeByte(gameMode);
|
||||||
|
msg.writeByte(previousGameMode);
|
||||||
|
msg.writeStringsArray(worldNames);
|
||||||
|
msg.writeCompoundTag(dimensionRegistry.getCodec_1_20());
|
||||||
|
msg.writeString(worldName); // World type
|
||||||
|
msg.writeString(worldName);
|
||||||
|
msg.writeLong(hashedSeed);
|
||||||
|
msg.writeVarInt(maxPlayers);
|
||||||
|
msg.writeVarInt(viewDistance);
|
||||||
|
msg.writeVarInt(viewDistance); // Simulation Distance
|
||||||
|
msg.writeBoolean(reducedDebugInfo);
|
||||||
|
msg.writeBoolean(enableRespawnScreen);
|
||||||
|
msg.writeBoolean(isDebug);
|
||||||
|
msg.writeBoolean(isFlat);
|
||||||
|
msg.writeBoolean(false);
|
||||||
|
msg.writeVarInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.fromTo(Version.V1_20_2, Version.V1_20_3)) {
|
||||||
|
msg.writeBoolean(isHardcore);
|
||||||
|
msg.writeStringsArray(worldNames);
|
||||||
|
msg.writeVarInt(maxPlayers);
|
||||||
|
msg.writeVarInt(viewDistance);
|
||||||
|
msg.writeVarInt(viewDistance); // Simulation Distance
|
||||||
|
msg.writeBoolean(reducedDebugInfo);
|
||||||
|
msg.writeBoolean(enableRespawnScreen);
|
||||||
|
msg.writeBoolean(limitedCrafting);
|
||||||
|
msg.writeString(worldName);
|
||||||
|
msg.writeString(worldName);
|
||||||
|
msg.writeLong(hashedSeed);
|
||||||
|
msg.writeByte(gameMode);
|
||||||
|
msg.writeByte(previousGameMode);
|
||||||
|
msg.writeBoolean(isDebug);
|
||||||
|
msg.writeBoolean(isFlat);
|
||||||
|
msg.writeBoolean(false);
|
||||||
|
msg.writeVarInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.moreOrEqual(Version.V1_20_5)) {
|
||||||
|
msg.writeBoolean(isHardcore);
|
||||||
|
msg.writeStringsArray(worldNames);
|
||||||
|
msg.writeVarInt(maxPlayers);
|
||||||
|
msg.writeVarInt(viewDistance);
|
||||||
|
msg.writeVarInt(viewDistance); // Simulation Distance
|
||||||
|
msg.writeBoolean(reducedDebugInfo);
|
||||||
|
msg.writeBoolean(enableRespawnScreen);
|
||||||
|
msg.writeBoolean(limitedCrafting);
|
||||||
|
msg.writeVarInt(dimensionRegistry.getDimension_1_20_5().getId());
|
||||||
|
msg.writeString(worldName);
|
||||||
|
msg.writeLong(hashedSeed);
|
||||||
|
msg.writeByte(gameMode);
|
||||||
|
msg.writeByte(previousGameMode);
|
||||||
|
msg.writeBoolean(isDebug);
|
||||||
|
msg.writeBoolean(isFlat);
|
||||||
|
msg.writeBoolean(false);
|
||||||
|
msg.writeVarInt(0);
|
||||||
|
msg.writeBoolean(secureProfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.Packet;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketKeepAlive implements Packet {
|
||||||
|
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
if (version.moreOrEqual(Version.V1_12_2)) {
|
||||||
|
msg.writeLong(id);
|
||||||
|
} else if (version.moreOrEqual(Version.V1_8)) {
|
||||||
|
msg.writeVarInt((int) id);
|
||||||
|
} else {
|
||||||
|
msg.writeInt((int) id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decode(ByteMessage msg, Version version) {
|
||||||
|
if (version.moreOrEqual(Version.V1_12_2)) {
|
||||||
|
this.id = msg.readLong();
|
||||||
|
} else if (version.moreOrEqual(Version.V1_8)) {
|
||||||
|
this.id = msg.readVarInt();
|
||||||
|
} else {
|
||||||
|
this.id = msg.readInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketPlayerAbilities implements PacketOut {
|
||||||
|
|
||||||
|
private int flags = 0x02;
|
||||||
|
private float flyingSpeed = 0.0F;
|
||||||
|
private float fieldOfView = 0.1F;
|
||||||
|
|
||||||
|
public void setFlags(int flags) {
|
||||||
|
this.flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFlyingSpeed(float flyingSpeed) {
|
||||||
|
this.flyingSpeed = flyingSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFieldOfView(float fieldOfView) {
|
||||||
|
this.fieldOfView = fieldOfView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeByte(flags);
|
||||||
|
msg.writeFloat(flyingSpeed);
|
||||||
|
msg.writeFloat(fieldOfView);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This packet was very simplified and using only for ADD_PLAYER action
|
||||||
|
*/
|
||||||
|
public class PacketPlayerInfo implements PacketOut {
|
||||||
|
|
||||||
|
private int gameMode = 3;
|
||||||
|
private String username = "";
|
||||||
|
private UUID uuid;
|
||||||
|
|
||||||
|
public void setGameMode(int gameMode) {
|
||||||
|
this.gameMode = gameMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUuid(UUID uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
if (version.less(Version.V1_8)) {
|
||||||
|
msg.writeString(username);
|
||||||
|
msg.writeBoolean(true); // Is online
|
||||||
|
msg.writeShort(0);
|
||||||
|
} else {
|
||||||
|
if (version.moreOrEqual(Version.V1_19_3)) {
|
||||||
|
EnumSet<Action> actions = EnumSet.noneOf(Action.class);
|
||||||
|
actions.add(Action.ADD_PLAYER);
|
||||||
|
actions.add(Action.UPDATE_LISTED);
|
||||||
|
actions.add(Action.UPDATE_GAMEMODE);
|
||||||
|
msg.writeEnumSet(actions, Action.class);
|
||||||
|
|
||||||
|
msg.writeVarInt(1); // Array length (1 element)
|
||||||
|
msg.writeUuid(uuid); // UUID
|
||||||
|
msg.writeString(username); //Username
|
||||||
|
msg.writeVarInt(0); //Properties (0 is empty)
|
||||||
|
|
||||||
|
msg.writeBoolean(true); //Update listed
|
||||||
|
msg.writeVarInt(gameMode); //Gamemode
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.writeVarInt(0); // Add player action
|
||||||
|
msg.writeVarInt(1);
|
||||||
|
msg.writeUuid(uuid);
|
||||||
|
msg.writeString(username);
|
||||||
|
msg.writeVarInt(0);
|
||||||
|
msg.writeVarInt(gameMode);
|
||||||
|
msg.writeVarInt(60);
|
||||||
|
msg.writeBoolean(false);
|
||||||
|
|
||||||
|
if (version.moreOrEqual(Version.V1_19)) {
|
||||||
|
msg.writeBoolean(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static enum Action {
|
||||||
|
ADD_PLAYER,
|
||||||
|
INITIALIZE_CHAT,
|
||||||
|
UPDATE_GAMEMODE,
|
||||||
|
UPDATE_LISTED,
|
||||||
|
UPDATE_LATENCY,
|
||||||
|
UPDATE_DISPLAY_NAME;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.NbtMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketPlayerListHeader implements PacketOut {
|
||||||
|
|
||||||
|
private NbtMessage header;
|
||||||
|
private NbtMessage footer;
|
||||||
|
|
||||||
|
public void setHeader(NbtMessage header) {
|
||||||
|
this.header = header;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFooter(NbtMessage footer) {
|
||||||
|
this.footer = footer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeNbtMessage(header, version);
|
||||||
|
msg.writeNbtMessage(footer, version);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketPlayerPositionAndLook implements PacketOut {
|
||||||
|
|
||||||
|
private double x;
|
||||||
|
private double y;
|
||||||
|
private double z;
|
||||||
|
private float yaw;
|
||||||
|
private float pitch;
|
||||||
|
private int teleportId;
|
||||||
|
|
||||||
|
public PacketPlayerPositionAndLook() {}
|
||||||
|
|
||||||
|
public PacketPlayerPositionAndLook(double x, double y, double z, float yaw, float pitch, int teleportId) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
this.yaw = yaw;
|
||||||
|
this.pitch = pitch;
|
||||||
|
this.teleportId = teleportId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeDouble(x);
|
||||||
|
msg.writeDouble(y + (version.less(Version.V1_8) ? 1.62F : 0));
|
||||||
|
msg.writeDouble(z);
|
||||||
|
msg.writeFloat(yaw);
|
||||||
|
msg.writeFloat(pitch);
|
||||||
|
|
||||||
|
if (version.moreOrEqual(Version.V1_8)) {
|
||||||
|
msg.writeByte(0x08);
|
||||||
|
} else {
|
||||||
|
msg.writeBoolean(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.moreOrEqual(Version.V1_9)) {
|
||||||
|
msg.writeVarInt(teleportId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version.fromTo(Version.V1_17, Version.V1_19_3)) {
|
||||||
|
msg.writeBoolean(false); // Dismount vehicle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketPluginMessage implements PacketOut {
|
||||||
|
|
||||||
|
private String channel;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public void setChannel(String channel) {
|
||||||
|
this.channel = channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeString(channel);
|
||||||
|
msg.writeString(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketSpawnPosition implements PacketOut {
|
||||||
|
|
||||||
|
private long x;
|
||||||
|
private long y;
|
||||||
|
private long z;
|
||||||
|
|
||||||
|
public PacketSpawnPosition() { }
|
||||||
|
|
||||||
|
public PacketSpawnPosition(long x, long y, long z) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeLong(encodePosition(x, y ,z));
|
||||||
|
msg.writeFloat(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long encodePosition(long x, long y, long z) {
|
||||||
|
return ((x & 0x3FFFFFF) << 38) | ((z & 0x3FFFFFF) << 12) | (y & 0xFFF);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
import ua.nanit.limbo.server.data.Title;
|
||||||
|
|
||||||
|
public class PacketTitleLegacy implements PacketOut {
|
||||||
|
|
||||||
|
private Action action;
|
||||||
|
private final PacketTitleSetTitle title;
|
||||||
|
private final PacketTitleSetSubTitle subtitle;
|
||||||
|
private final PacketTitleTimes times;
|
||||||
|
|
||||||
|
public PacketTitleLegacy() {
|
||||||
|
this.title = new PacketTitleSetTitle();
|
||||||
|
this.subtitle = new PacketTitleSetSubTitle();
|
||||||
|
this.times = new PacketTitleTimes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAction(Action action) {
|
||||||
|
this.action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(Title title) {
|
||||||
|
this.title.setTitle(title.getTitle());
|
||||||
|
this.subtitle.setSubtitle(title.getSubtitle());
|
||||||
|
this.times.setFadeIn(title.getFadeIn());
|
||||||
|
this.times.setStay(title.getStay());
|
||||||
|
this.times.setFadeOut(title.getFadeOut());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeVarInt(action.getId(version));
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case SET_TITLE:
|
||||||
|
title.encode(msg, version);
|
||||||
|
break;
|
||||||
|
case SET_SUBTITLE:
|
||||||
|
subtitle.encode(msg, version);
|
||||||
|
break;
|
||||||
|
case SET_TIMES_AND_DISPLAY:
|
||||||
|
times.encode(msg, version);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid title action: " + action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Action {
|
||||||
|
SET_TITLE(0),
|
||||||
|
SET_SUBTITLE(1),
|
||||||
|
SET_TIMES_AND_DISPLAY(3, 2);
|
||||||
|
|
||||||
|
private final int id;
|
||||||
|
private final int legacyId;
|
||||||
|
|
||||||
|
Action(int id, int legacyId) {
|
||||||
|
this.id = id;
|
||||||
|
this.legacyId = legacyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
Action(int id) {
|
||||||
|
this(id, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId(Version version) {
|
||||||
|
return version.less(Version.V1_11) ? legacyId : id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.NbtMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketTitleSetSubTitle implements PacketOut {
|
||||||
|
|
||||||
|
private NbtMessage subtitle;
|
||||||
|
|
||||||
|
public void setSubtitle(NbtMessage subtitle) {
|
||||||
|
this.subtitle = subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeNbtMessage(subtitle, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.NbtMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketTitleSetTitle implements PacketOut {
|
||||||
|
|
||||||
|
private NbtMessage title;
|
||||||
|
|
||||||
|
public void setTitle(NbtMessage title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeNbtMessage(title, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.play;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.PacketOut;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
|
||||||
|
public class PacketTitleTimes implements PacketOut {
|
||||||
|
|
||||||
|
private int fadeIn;
|
||||||
|
private int stay;
|
||||||
|
private int fadeOut;
|
||||||
|
|
||||||
|
public void setFadeIn(int fadeIn) {
|
||||||
|
this.fadeIn = fadeIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStay(int stay) {
|
||||||
|
this.stay = stay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFadeOut(int fadeOut) {
|
||||||
|
this.fadeOut = fadeOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeInt(fadeIn);
|
||||||
|
msg.writeInt(stay);
|
||||||
|
msg.writeInt(fadeOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Nan1t
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ua.nanit.limbo.protocol.packets.status;
|
||||||
|
|
||||||
|
import ua.nanit.limbo.connection.ClientConnection;
|
||||||
|
import ua.nanit.limbo.protocol.ByteMessage;
|
||||||
|
import ua.nanit.limbo.protocol.Packet;
|
||||||
|
import ua.nanit.limbo.protocol.registry.Version;
|
||||||
|
import ua.nanit.limbo.server.LimboServer;
|
||||||
|
|
||||||
|
public class PacketStatusPing implements Packet {
|
||||||
|
|
||||||
|
private long randomId;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteMessage msg, Version version) {
|
||||||
|
msg.writeLong(randomId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decode(ByteMessage msg, Version version) {
|
||||||
|
this.randomId = msg.readLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(ClientConnection conn, LimboServer server) {
|
||||||
|
server.getPacketHandler().handle(conn, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user