Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-11-03 14:50:19 +01:00
Merge branch 'floodgate-2.0' of https://github.com/GeyserMC/Geyser into floodgate-2.0
Dieser Commit ist enthalten in:
Commit
25600ebc1c
57
.github/ISSUE_TEMPLATE/bug_report.md
vendored
57
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,57 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--- DELETING THIS TEMPLATE WILL GET YOUR ISSUE CLOSED! --->
|
|
||||||
|
|
||||||
<!--- Please follow this format COMPLETELY and make sure the bug you are reporting has not been reported yet. Reports should contain as much information or context as possible to help us find the problem. Simply creating an issue on a vague topic will not help us at all, and if you are unsure if something should belong here, please contact us on [Discord](http://discord.geysermc.org).-->
|
|
||||||
|
|
||||||
<!--- Issues pertaining to connection problems, or anything of that covered on the [Common Issues](https://github.com/GeyserMC/Geyser/wiki/Common-Issues) do not belong here and only clutter this issue tracker. -->
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. See error
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Screenshots / Videos**
|
|
||||||
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Server Version and Plugins**
|
|
||||||
|
|
||||||
If you just run Geyser-Spigot, you can leave this area blank as the next section covers this information.
|
|
||||||
|
|
||||||
If you're running a multi-server instance, or using Geyser Standalone:
|
|
||||||
|
|
||||||
- Give us the exact output from `/version` on all servers involved. Saying "latest" does not help us at all.
|
|
||||||
- Please list all plugins on all servers involved.
|
|
||||||
|
|
||||||
If this bug occurs on a server you do not control, please fill this in to the best of your knowledge.
|
|
||||||
|
|
||||||
**Geyser Dump**
|
|
||||||
|
|
||||||
If Geyser starts correctly, please also include the link to a dump by using `/geyser dump`. If you use the Standalone GUI, the option can be found under `Commands` => `Dump`. This provides us information about your server that we can use to debug your issue.
|
|
||||||
|
|
||||||
**Minecraft: Bedrock Edition Version**
|
|
||||||
|
|
||||||
The version of your Minecraft: Bedrock Edition client you tested with, along with your device type (e.g. Windows 10, Switch...).
|
|
||||||
|
|
||||||
**Additional Context**
|
|
||||||
|
|
||||||
Add any other context about the problem here.
|
|
64
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normale Datei
64
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normale Datei
@ -0,0 +1,64 @@
|
|||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for taking the time to fill out this bug report for Geyser! Fill out the following form to your best ability to help us fix the problem.
|
||||||
|
Only use this if you're absolutely sure that you found a bug and can reproduce it. For anything else, use: [our Discord server](https://discord.gg/geysermc), [the FAQ](https://github.com/GeyserMC/Geyser/wiki/FAQ) or the [Common Issues](https://github.com/GeyserMC/Geyser/wiki/Common-Issues).
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Describe the bug
|
||||||
|
description: A clear and concise description of what the bug is.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: To Reproduce
|
||||||
|
description: Steps to reproduce this behaviour
|
||||||
|
placeholder: |
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '...'
|
||||||
|
3. Scroll down to '...'
|
||||||
|
4. See error
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Expected behaviour
|
||||||
|
description: A clear and concise description of what you expected to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Screenshots / Videos
|
||||||
|
description: If applicable, add screenshots to help explain your problem.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Server Version and Plugins
|
||||||
|
description: |
|
||||||
|
If you just run Geyser-Spigot, you can leave this area blank as the next section covers this information.
|
||||||
|
If you're running a multi-server instance or using Geyser Standalone:
|
||||||
|
* Give us the exact output from `/version` on all servers involved. Saying "latest" does not help us at all.
|
||||||
|
* Please list all plugins on all servers involved.
|
||||||
|
If this bug occurs on a server you do not control, please full this in to the best of your knowledge.
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Geyser Dump
|
||||||
|
description: If Geyser starts correctly, please also include the link to a dump by using `/geyser dump`. If you're using the Standalone GUI, the option can be found under `Commands` => `Dump`. This provides us information about your server that we can use to debug your issue.
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Geyser Version
|
||||||
|
description: What version of Geyser are you running?
|
||||||
|
placeholder: "For example: 1.2.0-SNAPSHOT (git-master-2d9baf1)"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: "Minecraft: Bedrock Edition Version"
|
||||||
|
description: "What version of Minecraft: Bedrock Edition are you using? Leave empty if the bug happens before you can connect with Minecraft: Bedrock Edition."
|
||||||
|
placeholder: "For example: 1.16.201"
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Additional Context
|
||||||
|
description: Add any other context about the problem here
|
10
.github/ISSUE_TEMPLATE/config.yml
vendored
10
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,5 +1,11 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: GeyserMC Discord
|
- name: Common Issues
|
||||||
url: http://discord.geysermc.org/
|
url: https://github.com/GeyserMC/Geyser/wiki/Common-Issues
|
||||||
|
about: Check the common issues to see if you are not alone with that issue and see how you can fix them.
|
||||||
|
- name: Frequently Asked Questions
|
||||||
|
url: https://github.com/GeyserMC/Geyser/wiki/FAQ
|
||||||
|
about: Look at the FAQ page for answers for frequently asked questions.
|
||||||
|
- name: Get help on the GeyserMC Discord server
|
||||||
|
url: https://discord.gg/geysermc
|
||||||
about: If your issue seems like it could possibly be an easy fix due to configuration, please hop on our Discord.
|
about: If your issue seems like it could possibly be an easy fix due to configuration, please hop on our Discord.
|
||||||
|
14
.github/ISSUE_TEMPLATE/feature_request.md
vendored
14
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**What feature do you want?**
|
|
||||||
Add a description
|
|
||||||
|
|
||||||
**Alternatives?**
|
|
||||||
List any alternatives you might have tried
|
|
21
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normale Datei
21
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normale Datei
@ -0,0 +1,21 @@
|
|||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
labels: "Feature Request"
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for taking the time to fill out this feature request for Geyser! Fill out the following form to your best ability to help us understand your feature request and greately improve the change of it getting added.
|
||||||
|
For anything else than a feature request, use: [our Discord server](https://discord.gg/geysermc), [the FAQ](https://github.com/GeyserMC/Geyser/wiki/FAQ) or [the Common Issues](https://github.com/GeyserMC/Geyser/wiki/Common-Issues).
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: What feature do you want to see added?
|
||||||
|
description: A clear and concise description of your feature request.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Are there any alternatives?
|
||||||
|
description: List any alternatives you might have tried
|
||||||
|
validations:
|
||||||
|
required: true
|
7
Jenkinsfile
vendored
7
Jenkinsfile
vendored
@ -22,7 +22,10 @@ pipeline {
|
|||||||
|
|
||||||
stage ('Deploy') {
|
stage ('Deploy') {
|
||||||
when {
|
when {
|
||||||
|
anyOf {
|
||||||
branch "master"
|
branch "master"
|
||||||
|
branch "floodgate-2.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
steps {
|
steps {
|
||||||
@ -35,8 +38,8 @@ pipeline {
|
|||||||
rtMavenResolver(
|
rtMavenResolver(
|
||||||
id: "maven-resolver",
|
id: "maven-resolver",
|
||||||
serverId: "opencollab-artifactory",
|
serverId: "opencollab-artifactory",
|
||||||
releaseRepo: "release",
|
releaseRepo: "maven-deploy-release",
|
||||||
snapshotRepo: "snapshot"
|
snapshotRepo: "maven-deploy-snapshot"
|
||||||
)
|
)
|
||||||
rtMavenRun(
|
rtMavenRun(
|
||||||
pom: 'pom.xml',
|
pom: 'pom.xml',
|
||||||
|
14
README.md
14
README.md
@ -18,7 +18,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
|
|||||||
|
|
||||||
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have now joined us here!
|
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have now joined us here!
|
||||||
|
|
||||||
### Currently supporting Minecraft Bedrock v1.16.100 - v1.16.201 and Minecraft Java v1.16.4 - v1.16.5.
|
### Currently supporting Minecraft Bedrock v1.16.100 - v1.16.210 and Minecraft Java v1.16.4 - v1.16.5.
|
||||||
|
|
||||||
## Setting Up
|
## Setting Up
|
||||||
Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set up Geyser.
|
Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set up Geyser.
|
||||||
@ -34,20 +34,10 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set
|
|||||||
- Test Server: `test.geysermc.org` port `25565` for Java and `19132` for Bedrock
|
- Test Server: `test.geysermc.org` port `25565` for Java and `19132` for Bedrock
|
||||||
|
|
||||||
## What's Left to be Added/Fixed
|
## What's Left to be Added/Fixed
|
||||||
- Lecterns
|
|
||||||
- Near-perfect movement (to the point where anticheat on large servers is unlikely to ban you)
|
- Near-perfect movement (to the point where anticheat on large servers is unlikely to ban you)
|
||||||
- Resource pack conversion/CustomModelData
|
- Resource pack conversion/CustomModelData
|
||||||
- Some Entity Flags
|
- Some Entity Flags
|
||||||
- The Following Inventories
|
- Structure block UI
|
||||||
- Enchantment Table (as a proper GUI)
|
|
||||||
- Beacon
|
|
||||||
- Cartography Table
|
|
||||||
- Stonecutter
|
|
||||||
- Structure Block
|
|
||||||
- Horse Inventory
|
|
||||||
- Loom
|
|
||||||
- Smithing Table
|
|
||||||
- Grindstone
|
|
||||||
|
|
||||||
## What can't be fixed
|
## What can't be fixed
|
||||||
The following things can't be fixed because of Bedrock limitations. They might be fixable in the future, but not as of now.
|
The following things can't be fixed because of Bedrock limitations. They might be fixable in the future, but not as of now.
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.geysermc</groupId>
|
<groupId>org.geysermc</groupId>
|
||||||
<artifactId>bootstrap-parent</artifactId>
|
<artifactId>bootstrap-parent</artifactId>
|
||||||
<version>1.2.0-SNAPSHOT</version>
|
<version>1.3.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>bootstrap-bungeecord</artifactId>
|
<artifactId>bootstrap-bungeecord</artifactId>
|
||||||
|
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.geysermc</groupId>
|
<groupId>org.geysermc</groupId>
|
||||||
<artifactId>connector</artifactId>
|
<artifactId>connector</artifactId>
|
||||||
<version>1.2.0-SNAPSHOT</version>
|
<version>1.3.0-SNAPSHOT</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.geysermc</groupId>
|
<groupId>org.geysermc</groupId>
|
||||||
<artifactId>geyser-parent</artifactId>
|
<artifactId>geyser-parent</artifactId>
|
||||||
<version>1.2.0-SNAPSHOT</version>
|
<version>1.3.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>bootstrap-parent</artifactId>
|
<artifactId>bootstrap-parent</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
@ -16,13 +16,9 @@
|
|||||||
<id>spigot-public</id>
|
<id>spigot-public</id>
|
||||||
<url>https://hub.spigotmc.org/nexus/content/repositories/public/</url>
|
<url>https://hub.spigotmc.org/nexus/content/repositories/public/</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
|
||||||
<id>bukkit-public</id>
|
|
||||||
<url>https://repo.md-5.net/content/repositories/public/</url>
|
|
||||||
</repository>
|
|
||||||
<repository>
|
<repository>
|
||||||
<id>sponge-repo</id>
|
<id>sponge-repo</id>
|
||||||
<url>https://repo.spongepowered.org/maven</url>
|
<url>https://repo.spongepowered.org/repository/maven-public/</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>bungeecord-repo</id>
|
<id>bungeecord-repo</id>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.geysermc</groupId>
|
<groupId>org.geysermc</groupId>
|
||||||
<artifactId>bootstrap-parent</artifactId>
|
<artifactId>bootstrap-parent</artifactId>
|
||||||
<version>1.2.0-SNAPSHOT</version>
|
<version>1.3.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>bootstrap-spigot</artifactId>
|
<artifactId>bootstrap-spigot</artifactId>
|
||||||
|
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.geysermc</groupId>
|
<groupId>org.geysermc</groupId>
|
||||||
<artifactId>connector</artifactId>
|
<artifactId>connector</artifactId>
|
||||||
<version>1.2.0-SNAPSHOT</version>
|
<version>1.3.0-SNAPSHOT</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -30,9 +30,9 @@
|
|||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.geysermc.adapters</groupId>
|
<groupId>org.geysermc.geyser.adapters</groupId>
|
||||||
<artifactId>spigot-all</artifactId>
|
<artifactId>spigot-all</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.1-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
|
@ -28,7 +28,6 @@ package org.geysermc.platform.spigot;
|
|||||||
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import org.geysermc.adapters.spigot.SpigotAdapters;
|
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.common.PlatformType;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
||||||
@ -40,9 +39,11 @@ import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
|
|||||||
import org.geysermc.connector.ping.IGeyserPingPassthrough;
|
import org.geysermc.connector.ping.IGeyserPingPassthrough;
|
||||||
import org.geysermc.connector.utils.FileUtils;
|
import org.geysermc.connector.utils.FileUtils;
|
||||||
import org.geysermc.connector.utils.LanguageUtils;
|
import org.geysermc.connector.utils.LanguageUtils;
|
||||||
|
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
||||||
import org.geysermc.platform.spigot.command.GeyserSpigotCommandExecutor;
|
import org.geysermc.platform.spigot.command.GeyserSpigotCommandExecutor;
|
||||||
import org.geysermc.platform.spigot.command.GeyserSpigotCommandManager;
|
import org.geysermc.platform.spigot.command.GeyserSpigotCommandManager;
|
||||||
import org.geysermc.platform.spigot.command.SpigotCommandSender;
|
import org.geysermc.platform.spigot.command.SpigotCommandSender;
|
||||||
|
import org.geysermc.platform.spigot.world.GeyserSpigot1_11CraftingListener;
|
||||||
import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener;
|
import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener;
|
||||||
import org.geysermc.platform.spigot.world.manager.*;
|
import org.geysermc.platform.spigot.world.manager.*;
|
||||||
import us.myles.ViaVersion.api.Pair;
|
import us.myles.ViaVersion.api.Pair;
|
||||||
@ -69,6 +70,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||||||
|
|
||||||
private GeyserConnector connector;
|
private GeyserConnector connector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Minecraft server version, formatted as <code>1.#.#</code>
|
||||||
|
*/
|
||||||
|
private String minecraftVersion;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
// This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed
|
// This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed
|
||||||
@ -118,6 +124,9 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||||||
|
|
||||||
geyserConfig.loadFloodgate(this);
|
geyserConfig.loadFloodgate(this);
|
||||||
|
|
||||||
|
// Turn "(MC: 1.16.4)" into 1.16.4.
|
||||||
|
this.minecraftVersion = Bukkit.getServer().getVersion().split("\\(MC: ")[1].split("\\)")[0];
|
||||||
|
|
||||||
this.connector = GeyserConnector.start(PlatformType.SPIGOT, this);
|
this.connector = GeyserConnector.start(PlatformType.SPIGOT, this);
|
||||||
|
|
||||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||||
@ -146,8 +155,9 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||||||
geyserLogger.debug("Legacy version of Minecraft (1.15.2 or older) detected; not using 3D biomes.");
|
geyserLogger.debug("Legacy version of Minecraft (1.15.2 or older) detected; not using 3D biomes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isPre1_12 = !isCompatible(Bukkit.getServer().getVersion(), "1.12.0");
|
||||||
// Set if we need to use a different method for getting a player's locale
|
// Set if we need to use a different method for getting a player's locale
|
||||||
SpigotCommandSender.setUseLegacyLocaleMethod(!isCompatible(Bukkit.getServer().getVersion(), "1.12.0"));
|
SpigotCommandSender.setUseLegacyLocaleMethod(isPre1_12);
|
||||||
|
|
||||||
if (connector.getConfig().isUseAdapters()) {
|
if (connector.getConfig().isUseAdapters()) {
|
||||||
try {
|
try {
|
||||||
@ -157,14 +167,14 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||||||
if (isViaVersion && isViaVersionNeeded()) {
|
if (isViaVersion && isViaVersionNeeded()) {
|
||||||
if (isLegacy) {
|
if (isLegacy) {
|
||||||
// Pre-1.13
|
// Pre-1.13
|
||||||
this.geyserWorldManager = new GeyserSpigot1_12NativeWorldManager();
|
this.geyserWorldManager = new GeyserSpigot1_12NativeWorldManager(this);
|
||||||
} else {
|
} else {
|
||||||
// Post-1.13
|
// Post-1.13
|
||||||
this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this, use3dBiomes);
|
this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this, use3dBiomes);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No ViaVersion
|
// No ViaVersion
|
||||||
this.geyserWorldManager = new GeyserSpigotNativeWorldManager(use3dBiomes);
|
this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this, use3dBiomes);
|
||||||
}
|
}
|
||||||
geyserLogger.debug("Using NMS adapter: " + this.geyserWorldManager.getClass() + ", " + nmsVersion);
|
geyserLogger.debug("Using NMS adapter: " + this.geyserWorldManager.getClass() + ", " + nmsVersion);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -180,20 +190,24 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||||||
// No NMS adapter
|
// No NMS adapter
|
||||||
if (isLegacy && isViaVersion) {
|
if (isLegacy && isViaVersion) {
|
||||||
// Use ViaVersion for converting pre-1.13 block states
|
// Use ViaVersion for converting pre-1.13 block states
|
||||||
this.geyserWorldManager = new GeyserSpigot1_12WorldManager();
|
this.geyserWorldManager = new GeyserSpigot1_12WorldManager(this);
|
||||||
} else if (isLegacy) {
|
} else if (isLegacy) {
|
||||||
// Not sure how this happens - without ViaVersion, we don't know any block states, so just assume everything is air
|
// Not sure how this happens - without ViaVersion, we don't know any block states, so just assume everything is air
|
||||||
this.geyserWorldManager = new GeyserSpigotFallbackWorldManager();
|
this.geyserWorldManager = new GeyserSpigotFallbackWorldManager(this);
|
||||||
} else {
|
} else {
|
||||||
// Post-1.13
|
// Post-1.13
|
||||||
this.geyserWorldManager = new GeyserSpigotWorldManager(use3dBiomes);
|
this.geyserWorldManager = new GeyserSpigotWorldManager(this, use3dBiomes);
|
||||||
}
|
}
|
||||||
geyserLogger.debug("Using default world manager: " + this.geyserWorldManager.getClass());
|
geyserLogger.debug("Using default world manager: " + this.geyserWorldManager.getClass());
|
||||||
}
|
}
|
||||||
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(connector, this.geyserWorldManager);
|
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(connector, this.geyserWorldManager);
|
||||||
|
|
||||||
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
|
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
|
||||||
|
|
||||||
|
if (isPre1_12) {
|
||||||
|
// Register events needed to send all recipes to the client
|
||||||
|
Bukkit.getServer().getPluginManager().registerEvents(new GeyserSpigot1_11CraftingListener(connector), this);
|
||||||
|
}
|
||||||
|
|
||||||
this.getCommand("geyser").setExecutor(new GeyserSpigotCommandExecutor(connector));
|
this.getCommand("geyser").setExecutor(new GeyserSpigotCommandExecutor(connector));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,6 +253,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||||||
return new GeyserSpigotDumpInfo();
|
return new GeyserSpigotDumpInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMinecraftServerVersion() {
|
||||||
|
return this.minecraftVersion;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isCompatible(String version, String whichVersion) {
|
public boolean isCompatible(String version, String whichVersion) {
|
||||||
int[] currentVersion = parseVersion(version);
|
int[] currentVersion = parseVersion(version);
|
||||||
int[] otherVersion = parseVersion(whichVersion);
|
int[] otherVersion = parseVersion(whichVersion);
|
||||||
@ -277,10 +296,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||||||
* @return the server version before ViaVersion finishes initializing
|
* @return the server version before ViaVersion finishes initializing
|
||||||
*/
|
*/
|
||||||
public ProtocolVersion getServerProtocolVersion() {
|
public ProtocolVersion getServerProtocolVersion() {
|
||||||
String bukkitVersion = Bukkit.getServer().getVersion();
|
return ProtocolVersion.getClosest(this.minecraftVersion);
|
||||||
// Turn "(MC: 1.16.4)" into 1.16.4.
|
|
||||||
String version = bukkitVersion.split("\\(MC: ")[1].split("\\)")[0];
|
|
||||||
return ProtocolVersion.getClosest(version);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.platform.spigot.world;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient;
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType;
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData;
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.CraftingData;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
|
import org.bukkit.inventory.Recipe;
|
||||||
|
import org.bukkit.inventory.ShapedRecipe;
|
||||||
|
import org.bukkit.inventory.ShapelessRecipe;
|
||||||
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||||
|
import org.geysermc.connector.network.translators.item.RecipeRegistry;
|
||||||
|
import us.myles.ViaVersion.api.Pair;
|
||||||
|
import us.myles.ViaVersion.api.data.MappingData;
|
||||||
|
import us.myles.ViaVersion.api.protocol.Protocol;
|
||||||
|
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
|
||||||
|
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
|
||||||
|
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to send all available recipes from the server to the client, as a valid recipe book packet won't be sent by the server.
|
||||||
|
* Requires ViaVersion.
|
||||||
|
*/
|
||||||
|
public class GeyserSpigot1_11CraftingListener implements Listener {
|
||||||
|
|
||||||
|
private final GeyserConnector connector;
|
||||||
|
/**
|
||||||
|
* Specific mapping data for 1.12 to 1.13. Used to convert the 1.12 item into 1.13.
|
||||||
|
*/
|
||||||
|
private final MappingData mappingData1_12to1_13;
|
||||||
|
/**
|
||||||
|
* The list of all protocols from the client's version to 1.13.
|
||||||
|
*/
|
||||||
|
private final List<Pair<Integer, Protocol>> protocolList;
|
||||||
|
|
||||||
|
public GeyserSpigot1_11CraftingListener(GeyserConnector connector) {
|
||||||
|
this.connector = connector;
|
||||||
|
this.mappingData1_12to1_13 = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData();
|
||||||
|
this.protocolList = ProtocolRegistry.getProtocolPath(MinecraftConstants.PROTOCOL_VERSION,
|
||||||
|
ProtocolVersion.v1_13.getVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||||
|
GeyserSession session = null;
|
||||||
|
for (GeyserSession otherSession : connector.getPlayers()) {
|
||||||
|
if (otherSession.getName().equals(event.getPlayer().getName())) {
|
||||||
|
session = otherSession;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (session == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendServerRecipes(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendServerRecipes(GeyserSession session) {
|
||||||
|
int netId = RecipeRegistry.LAST_RECIPE_NET_ID;
|
||||||
|
|
||||||
|
CraftingDataPacket craftingDataPacket = new CraftingDataPacket();
|
||||||
|
craftingDataPacket.setCleanRecipes(true);
|
||||||
|
|
||||||
|
Iterator<Recipe> recipeIterator = Bukkit.getServer().recipeIterator();
|
||||||
|
while (recipeIterator.hasNext()) {
|
||||||
|
Recipe recipe = recipeIterator.next();
|
||||||
|
|
||||||
|
Pair<ItemStack, ItemData> outputs = translateToBedrock(session, recipe.getResult());
|
||||||
|
ItemStack javaOutput = outputs.getKey();
|
||||||
|
ItemData output = outputs.getValue();
|
||||||
|
if (output.getId() == 0) continue; // If items make air we don't want that
|
||||||
|
|
||||||
|
boolean isNotAllAir = false; // Check for all-air recipes
|
||||||
|
if (recipe instanceof ShapedRecipe) {
|
||||||
|
ShapedRecipe shapedRecipe = (ShapedRecipe) recipe;
|
||||||
|
int size = shapedRecipe.getShape().length * shapedRecipe.getShape()[0].length();
|
||||||
|
Ingredient[] ingredients = new Ingredient[size];
|
||||||
|
ItemData[] input = new ItemData[size];
|
||||||
|
for (int i = 0; i < input.length; i++) {
|
||||||
|
// Index is converting char to integer, adding i then converting back to char based on ASCII code
|
||||||
|
Pair<ItemStack, ItemData> result = translateToBedrock(session, shapedRecipe.getIngredientMap().get((char) ('a' + i)));
|
||||||
|
ingredients[i] = new Ingredient(new ItemStack[]{result.getKey()});
|
||||||
|
input[i] = result.getValue();
|
||||||
|
isNotAllAir |= input[i].getId() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNotAllAir) continue;
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
// Add recipe to our internal cache
|
||||||
|
ShapedRecipeData data = new ShapedRecipeData(shapedRecipe.getShape()[0].length(), shapedRecipe.getShape().length,
|
||||||
|
"", ingredients, javaOutput);
|
||||||
|
session.getCraftingRecipes().put(netId,
|
||||||
|
new com.github.steveice10.mc.protocol.data.game.recipe.Recipe(RecipeType.CRAFTING_SHAPED, uuid.toString(), data));
|
||||||
|
|
||||||
|
// Add recipe for Bedrock
|
||||||
|
craftingDataPacket.getCraftingData().add(CraftingData.fromShaped(uuid.toString(),
|
||||||
|
shapedRecipe.getShape()[0].length(), shapedRecipe.getShape().length, Arrays.asList(input),
|
||||||
|
Collections.singletonList(output), uuid, "crafting_table", 0, netId++));
|
||||||
|
} else if (recipe instanceof ShapelessRecipe) {
|
||||||
|
ShapelessRecipe shapelessRecipe = (ShapelessRecipe) recipe;
|
||||||
|
Ingredient[] ingredients = new Ingredient[shapelessRecipe.getIngredientList().size()];
|
||||||
|
ItemData[] input = new ItemData[shapelessRecipe.getIngredientList().size()];
|
||||||
|
|
||||||
|
for (int i = 0; i < input.length; i++) {
|
||||||
|
Pair<ItemStack, ItemData> result = translateToBedrock(session, shapelessRecipe.getIngredientList().get(i));
|
||||||
|
ingredients[i] = new Ingredient(new ItemStack[]{result.getKey()});
|
||||||
|
input[i] = result.getValue();
|
||||||
|
isNotAllAir |= input[i].getId() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNotAllAir) continue;
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
// Add recipe to our internal cache
|
||||||
|
ShapelessRecipeData data = new ShapelessRecipeData("", ingredients, javaOutput);
|
||||||
|
session.getCraftingRecipes().put(netId,
|
||||||
|
new com.github.steveice10.mc.protocol.data.game.recipe.Recipe(RecipeType.CRAFTING_SHAPELESS, uuid.toString(), data));
|
||||||
|
|
||||||
|
// Add recipe for Bedrock
|
||||||
|
craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(),
|
||||||
|
Arrays.asList(input), Collections.singletonList(output), uuid, "crafting_table", 0, netId++));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session.sendUpstreamPacket(craftingDataPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private Pair<ItemStack, ItemData> translateToBedrock(GeyserSession session, org.bukkit.inventory.ItemStack itemStack) {
|
||||||
|
if (itemStack != null && itemStack.getData() != null) {
|
||||||
|
if (itemStack.getType().getId() == 0) {
|
||||||
|
return new Pair<>(null, ItemData.AIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
int legacyId = (itemStack.getType().getId() << 4) | (itemStack.getData().getData() & 0xFFFF);
|
||||||
|
|
||||||
|
if (itemStack.getType().getId() == 355 && itemStack.getData().getData() == (byte) 0) { // Handle bed color since the server will always be pre-1.12
|
||||||
|
legacyId = (itemStack.getType().getId() << 4) | ((byte) 14 & 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
// old version -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 and so on
|
||||||
|
int itemId;
|
||||||
|
if (mappingData1_12to1_13.getItemMappings().containsKey(legacyId)) {
|
||||||
|
itemId = mappingData1_12to1_13.getNewItemId(legacyId);
|
||||||
|
} else if (mappingData1_12to1_13.getItemMappings().containsKey((itemStack.getType().getId() << 4) | (0))) {
|
||||||
|
itemId = mappingData1_12to1_13.getNewItemId((itemStack.getType().getId() << 4) | (0));
|
||||||
|
} else {
|
||||||
|
// No ID found, just send back air
|
||||||
|
return new Pair<>(null, ItemData.AIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = protocolList.size() - 1; i >= 0; i--) {
|
||||||
|
MappingData mappingData = protocolList.get(i).getValue().getMappingData();
|
||||||
|
if (mappingData != null) {
|
||||||
|
itemId = mappingData.getNewItemId(itemId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemStack mcItemStack = new ItemStack(itemId, itemStack.getAmount());
|
||||||
|
ItemData finalData = ItemTranslator.translateToBedrock(session, mcItemStack);
|
||||||
|
return new Pair<>(mcItemStack, finalData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty slot, most likely
|
||||||
|
return new Pair<>(null, ItemData.AIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -53,11 +53,11 @@ public class GeyserSpigotBlockPlaceListener implements Listener {
|
|||||||
placeBlockSoundPacket.setPosition(Vector3f.from(event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ()));
|
placeBlockSoundPacket.setPosition(Vector3f.from(event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ()));
|
||||||
placeBlockSoundPacket.setBabySound(false);
|
placeBlockSoundPacket.setBabySound(false);
|
||||||
if (worldManager.isLegacy()) {
|
if (worldManager.isLegacy()) {
|
||||||
placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(worldManager.getBlockAt(session,
|
placeBlockSoundPacket.setExtraData(session.getBlockTranslator().getBedrockBlockId(worldManager.getBlockAt(session,
|
||||||
event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ())));
|
event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ())));
|
||||||
} else {
|
} else {
|
||||||
String javaBlockId = event.getBlockPlaced().getBlockData().getAsString();
|
String javaBlockId = event.getBlockPlaced().getBlockData().getAsString();
|
||||||
placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().getOrDefault(javaBlockId, BlockTranslator.JAVA_AIR_ID)));
|
placeBlockSoundPacket.setExtraData(session.getBlockTranslator().getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().getOrDefault(javaBlockId, BlockTranslator.JAVA_AIR_ID)));
|
||||||
}
|
}
|
||||||
placeBlockSoundPacket.setIdentifier(":");
|
placeBlockSoundPacket.setIdentifier(":");
|
||||||
session.sendUpstreamPacket(placeBlockSoundPacket);
|
session.sendUpstreamPacket(placeBlockSoundPacket);
|
||||||
|
@ -27,10 +27,11 @@ package org.geysermc.platform.spigot.world.manager;
|
|||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.geysermc.adapters.spigot.SpigotAdapters;
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.geysermc.adapters.spigot.SpigotWorldAdapter;
|
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
|
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
||||||
|
import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter;
|
||||||
import us.myles.ViaVersion.api.Via;
|
import us.myles.ViaVersion.api.Via;
|
||||||
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
|
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
|
||||||
|
|
||||||
@ -40,7 +41,8 @@ import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
|
|||||||
public class GeyserSpigot1_12NativeWorldManager extends GeyserSpigot1_12WorldManager {
|
public class GeyserSpigot1_12NativeWorldManager extends GeyserSpigot1_12WorldManager {
|
||||||
private final SpigotWorldAdapter adapter;
|
private final SpigotWorldAdapter adapter;
|
||||||
|
|
||||||
public GeyserSpigot1_12NativeWorldManager() {
|
public GeyserSpigot1_12NativeWorldManager(Plugin plugin) {
|
||||||
|
super(plugin);
|
||||||
this.adapter = SpigotAdapters.getWorldAdapter();
|
this.adapter = SpigotAdapters.getWorldAdapter();
|
||||||
// Unlike post-1.13, we can't build up a cache of block states, because block entities need some special conversion
|
// Unlike post-1.13, we can't build up a cache of block states, because block entities need some special conversion
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import org.bukkit.Bukkit;
|
|||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
import us.myles.ViaVersion.api.Pair;
|
import us.myles.ViaVersion.api.Pair;
|
||||||
@ -61,8 +62,8 @@ public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager {
|
|||||||
*/
|
*/
|
||||||
private final List<Pair<Integer, Protocol>> protocolList;
|
private final List<Pair<Integer, Protocol>> protocolList;
|
||||||
|
|
||||||
public GeyserSpigot1_12WorldManager() {
|
public GeyserSpigot1_12WorldManager(Plugin plugin) {
|
||||||
super(false);
|
super(plugin, false);
|
||||||
this.mappingData1_12to1_13 = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData();
|
this.mappingData1_12to1_13 = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData();
|
||||||
this.protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION,
|
this.protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION,
|
||||||
ProtocolVersion.v1_13.getVersion());
|
ProtocolVersion.v1_13.getVersion());
|
||||||
@ -75,6 +76,10 @@ public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager {
|
|||||||
if (player == null) {
|
if (player == null) {
|
||||||
return BlockTranslator.JAVA_AIR_ID;
|
return BlockTranslator.JAVA_AIR_ID;
|
||||||
}
|
}
|
||||||
|
if (!player.getWorld().isChunkLoaded(x >> 4, z >> 4)) {
|
||||||
|
// Prevent nasty async errors if a player is loading in
|
||||||
|
return BlockTranslator.JAVA_AIR_ID;
|
||||||
|
}
|
||||||
// Get block entity storage
|
// Get block entity storage
|
||||||
BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class);
|
BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class);
|
||||||
Block block = player.getWorld().getBlockAt(x, y, z);
|
Block block = player.getWorld().getBlockAt(x, y, z);
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
package org.geysermc.platform.spigot.world.manager;
|
package org.geysermc.platform.spigot.world.manager;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
|
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
|
|
||||||
@ -35,9 +36,9 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
|||||||
* If this occurs to you somehow, please let us know!!
|
* If this occurs to you somehow, please let us know!!
|
||||||
*/
|
*/
|
||||||
public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager {
|
public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager {
|
||||||
public GeyserSpigotFallbackWorldManager() {
|
public GeyserSpigotFallbackWorldManager(Plugin plugin) {
|
||||||
// Since this is pre-1.13 (and thus pre-1.15), there will never be 3D biomes.
|
// Since this is pre-1.13 (and thus pre-1.15), there will never be 3D biomes.
|
||||||
super(false);
|
super(plugin, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -51,7 +52,7 @@ public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasMoreBlockDataThanChunkCache() {
|
public boolean hasOwnChunkCache() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorl
|
|||||||
private final Int2IntMap oldToNewBlockId;
|
private final Int2IntMap oldToNewBlockId;
|
||||||
|
|
||||||
public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin, boolean use3dBiomes) {
|
public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin, boolean use3dBiomes) {
|
||||||
super(use3dBiomes);
|
super(plugin, use3dBiomes);
|
||||||
IntList allBlockStates = adapter.getAllBlockStates();
|
IntList allBlockStates = adapter.getAllBlockStates();
|
||||||
oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size());
|
oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size());
|
||||||
ProtocolVersion serverVersion = plugin.getServerProtocolVersion();
|
ProtocolVersion serverVersion = plugin.getServerProtocolVersion();
|
||||||
|
@ -27,16 +27,17 @@ package org.geysermc.platform.spigot.world.manager;
|
|||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.geysermc.adapters.spigot.SpigotAdapters;
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.geysermc.adapters.spigot.SpigotWorldAdapter;
|
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
|
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
||||||
|
import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter;
|
||||||
|
|
||||||
public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
|
public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
|
||||||
protected final SpigotWorldAdapter adapter;
|
protected final SpigotWorldAdapter adapter;
|
||||||
|
|
||||||
public GeyserSpigotNativeWorldManager(boolean use3dBiomes) {
|
public GeyserSpigotNativeWorldManager(Plugin plugin, boolean use3dBiomes) {
|
||||||
super(use3dBiomes);
|
super(plugin, use3dBiomes);
|
||||||
adapter = SpigotAdapters.getWorldAdapter();
|
adapter = SpigotAdapters.getWorldAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,23 +28,35 @@ package org.geysermc.platform.spigot.world.manager;
|
|||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
||||||
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
|
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
|
||||||
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
|
import com.nukkitx.nbt.NbtMap;
|
||||||
|
import com.nukkitx.nbt.NbtMapBuilder;
|
||||||
|
import com.nukkitx.nbt.NbtType;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.Lectern;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.BookMeta;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
import org.geysermc.connector.network.translators.inventory.translators.LecternInventoryTranslator;
|
||||||
import org.geysermc.connector.network.translators.world.GeyserWorldManager;
|
import org.geysermc.connector.network.translators.world.GeyserWorldManager;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
|
import org.geysermc.connector.utils.BlockEntityUtils;
|
||||||
import org.geysermc.connector.utils.FileUtils;
|
import org.geysermc.connector.utils.FileUtils;
|
||||||
import org.geysermc.connector.utils.GameRule;
|
import org.geysermc.connector.utils.GameRule;
|
||||||
import org.geysermc.connector.utils.LanguageUtils;
|
import org.geysermc.connector.utils.LanguageUtils;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base world manager to use when there is no supported NMS revision
|
* The base world manager to use when there is no supported NMS revision
|
||||||
@ -72,8 +84,11 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
|||||||
*/
|
*/
|
||||||
private final Int2IntMap biomeToIdMap = new Int2IntOpenHashMap(Biome.values().length);
|
private final Int2IntMap biomeToIdMap = new Int2IntOpenHashMap(Biome.values().length);
|
||||||
|
|
||||||
public GeyserSpigotWorldManager(boolean use3dBiomes) {
|
private final Plugin plugin;
|
||||||
|
|
||||||
|
public GeyserSpigotWorldManager(Plugin plugin, boolean use3dBiomes) {
|
||||||
this.use3dBiomes = use3dBiomes;
|
this.use3dBiomes = use3dBiomes;
|
||||||
|
this.plugin = plugin;
|
||||||
|
|
||||||
// Load the values into the biome-to-ID map
|
// Load the values into the biome-to-ID map
|
||||||
InputStream biomeStream = FileUtils.getResource("biomes.json");
|
InputStream biomeStream = FileUtils.getResource("biomes.json");
|
||||||
@ -125,16 +140,13 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasMoreBlockDataThanChunkCache() {
|
public boolean hasOwnChunkCache() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public int[] getBiomeDataAt(GeyserSession session, int x, int z) {
|
public int[] getBiomeDataAt(GeyserSession session, int x, int z) {
|
||||||
if (session.getPlayerEntity() == null) {
|
|
||||||
return new int[1024];
|
|
||||||
}
|
|
||||||
int[] biomeData = new int[1024];
|
int[] biomeData = new int[1024];
|
||||||
World world = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld();
|
World world = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld();
|
||||||
int chunkX = x << 4;
|
int chunkX = x << 4;
|
||||||
@ -167,6 +179,77 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
|||||||
return biomeData;
|
return biomeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) {
|
||||||
|
// Run as a task to prevent async issues
|
||||||
|
Runnable lecternInfoGet = () -> {
|
||||||
|
Player bukkitPlayer;
|
||||||
|
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
|
||||||
|
if (!(block.getState() instanceof Lectern)) {
|
||||||
|
session.getConnector().getLogger().error("Lectern expected at: " + Vector3i.from(x, y, z).toString() + " but was not! " + block.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lectern lectern = (Lectern) block.getState();
|
||||||
|
ItemStack itemStack = lectern.getInventory().getItem(0);
|
||||||
|
if (itemStack == null || !(itemStack.getItemMeta() instanceof BookMeta)) {
|
||||||
|
if (!isChunkLoad) {
|
||||||
|
// We need to update the lectern since it's not going to be updated otherwise
|
||||||
|
BlockEntityUtils.updateBlockEntity(session, LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(), Vector3i.from(x, y, z));
|
||||||
|
}
|
||||||
|
// We don't care; return
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BookMeta bookMeta = (BookMeta) itemStack.getItemMeta();
|
||||||
|
// On the count: allow the book to show/open even there are no pages. We know there is a book here, after all, and this matches Java behavior
|
||||||
|
boolean hasBookPages = bookMeta.getPageCount() > 0;
|
||||||
|
NbtMapBuilder lecternTag = LecternInventoryTranslator.getBaseLecternTag(x, y, z, hasBookPages ? bookMeta.getPageCount() : 1);
|
||||||
|
lecternTag.putInt("page", lectern.getPage() / 2);
|
||||||
|
NbtMapBuilder bookTag = NbtMap.builder()
|
||||||
|
.putByte("Count", (byte) itemStack.getAmount())
|
||||||
|
.putShort("Damage", (short) 0)
|
||||||
|
.putString("Name", "minecraft:writable_book");
|
||||||
|
List<NbtMap> pages = new ArrayList<>(bookMeta.getPageCount());
|
||||||
|
if (hasBookPages) {
|
||||||
|
for (String page : bookMeta.getPages()) {
|
||||||
|
NbtMapBuilder pageBuilder = NbtMap.builder()
|
||||||
|
.putString("photoname", "")
|
||||||
|
.putString("text", page);
|
||||||
|
pages.add(pageBuilder.build());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Empty page
|
||||||
|
NbtMapBuilder pageBuilder = NbtMap.builder()
|
||||||
|
.putString("photoname", "")
|
||||||
|
.putString("text", "");
|
||||||
|
pages.add(pageBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
bookTag.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, pages).build());
|
||||||
|
lecternTag.putCompound("book", bookTag.build());
|
||||||
|
NbtMap blockEntityTag = lecternTag.build();
|
||||||
|
BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isChunkLoad) {
|
||||||
|
// Delay to ensure the chunk is sent first, and then the lectern data
|
||||||
|
Bukkit.getScheduler().runTaskLater(this.plugin, lecternInfoGet, 5);
|
||||||
|
} else {
|
||||||
|
Bukkit.getScheduler().runTask(this.plugin, lecternInfoGet);
|
||||||
|
}
|
||||||
|
return LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(); // Will be updated later
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldExpectLecternHandled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public Boolean getGameRuleBool(GeyserSession session, GameRule gameRule) {
|
public Boolean getGameRuleBool(GeyserSession session, GameRule gameRule) {
|
||||||
return Boolean.parseBoolean(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getGameRuleValue(gameRule.getJavaID()));
|
return Boolean.parseBoolean(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getGameRuleValue(gameRule.getJavaID()));
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ name: ${outputName}-Spigot
|
|||||||
author: ${project.organization.name}
|
author: ${project.organization.name}
|
||||||
website: ${project.organization.url}
|
website: ${project.organization.url}
|
||||||
version: ${project.version}
|
version: ${project.version}
|
||||||
softdepend: ["ViaVersion"]
|
softdepend: ["ViaVersion", "floodgate"]
|
||||||
api-version: 1.13
|
api-version: 1.13
|
||||||
commands:
|
commands:
|
||||||
geyser:
|
geyser:
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.geysermc</groupId>
|
<groupId>org.geysermc</groupId>
|
||||||
<artifactId>bootstrap-parent</artifactId>
|
<artifactId>bootstrap-parent</artifactId>
|
||||||
<version>1.2.0-SNAPSHOT</version>
|
<version>1.3.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>bootstrap-sponge</artifactId>
|
<artifactId>bootstrap-sponge</artifactId>
|
||||||
|
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.geysermc</groupId>
|
<groupId>org.geysermc</groupId>
|
||||||
<artifactId>connector</artifactId>
|
<artifactId>connector</artifactId>
|
||||||
<version>1.2.0-SNAPSHOT</version>
|
<version>1.3.0-SNAPSHOT</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -163,4 +163,9 @@ public class GeyserSpongePlugin implements GeyserBootstrap {
|
|||||||
public BootstrapDumpInfo getDumpInfo() {
|
public BootstrapDumpInfo getDumpInfo() {
|
||||||
return new GeyserSpongeDumpInfo();
|
return new GeyserSpongeDumpInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMinecraftServerVersion() {
|
||||||
|
return Sponge.getPlatform().getMinecraftVersion().getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.geysermc</groupId>
|
<groupId>org.geysermc</groupId>
|
||||||
<artifactId>bootstrap-parent</artifactId>
|
<artifactId>bootstrap-parent</artifactId>
|
||||||
<version>1.2.0-SNAPSHOT</version>
|
<version>1.3.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>bootstrap-standalone</artifactId>
|
<artifactId>bootstrap-standalone</artifactId>
|
||||||
|
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.geysermc</groupId>
|
<groupId>org.geysermc</groupId>
|
||||||
<artifactId>connector</artifactId>
|
<artifactId>connector</artifactId>
|
||||||
<version>1.2.0-SNAPSHOT</version>
|
<version>1.3.0-SNAPSHOT</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -32,6 +32,7 @@ import com.fasterxml.jackson.databind.introspect.AnnotatedField;
|
|||||||
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
|
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.minecrell.terminalconsole.TerminalConsoleAppender;
|
import net.minecrell.terminalconsole.TerminalConsoleAppender;
|
||||||
|
import org.apache.logging.log4j.Level;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.core.Appender;
|
import org.apache.logging.log4j.core.Appender;
|
||||||
import org.apache.logging.log4j.core.Logger;
|
import org.apache.logging.log4j.core.Logger;
|
||||||
@ -167,11 +168,6 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||||||
this.onEnable();
|
this.onEnable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onEnable(boolean useGui) {
|
|
||||||
this.useGui = useGui;
|
|
||||||
this.onEnable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
Logger logger = (Logger) LogManager.getRootLogger();
|
Logger logger = (Logger) LogManager.getRootLogger();
|
||||||
@ -213,6 +209,9 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||||||
}
|
}
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
|
|
||||||
|
// Allow libraries like Protocol to have their debug information passthrough
|
||||||
|
logger.get().setLevel(geyserConfig.isDebugMode() ? Level.DEBUG : Level.INFO);
|
||||||
|
|
||||||
connector = GeyserConnector.start(PlatformType.STANDALONE, this);
|
connector = GeyserConnector.start(PlatformType.STANDALONE, this);
|
||||||
geyserCommandManager = new GeyserCommandManager(connector);
|
geyserCommandManager = new GeyserCommandManager(connector);
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ public class LoopbackUtil {
|
|||||||
|
|
||||||
if (!result.contains("minecraftuwp")) {
|
if (!result.contains("minecraftuwp")) {
|
||||||
Files.write(Paths.get(System.getenv("temp") + "/loopback_minecraft.bat"), loopbackCommand.getBytes(), new OpenOption[0]);
|
Files.write(Paths.get(System.getenv("temp") + "/loopback_minecraft.bat"), loopbackCommand.getBytes(), new OpenOption[0]);
|
||||||
process = Runtime.getRuntime().exec(startScript);
|
Runtime.getRuntime().exec(startScript);
|
||||||
|
|
||||||
geyserLogger.info(ChatColor.AQUA + LanguageUtils.getLocaleStringLog("geyser.bootstrap.loopback.added"));
|
geyserLogger.info(ChatColor.AQUA + LanguageUtils.getLocaleStringLog("geyser.bootstrap.loopback.added"));
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.geysermc</groupId>
|
<groupId>org.geysermc</groupId>
|
||||||
<artifactId>bootstrap-parent</artifactId>
|
<artifactId>bootstrap-parent</artifactId>
|
||||||
<version>1.2.0-SNAPSHOT</version>
|
<version>1.3.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>bootstrap-velocity</artifactId>
|
<artifactId>bootstrap-velocity</artifactId>
|
||||||
|
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.geysermc</groupId>
|
<groupId>org.geysermc</groupId>
|
||||||
<artifactId>connector</artifactId>
|
<artifactId>connector</artifactId>
|
||||||
<version>1.2.0-SNAPSHOT</version>
|
<version>1.3.0-SNAPSHOT</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.geysermc</groupId>
|
<groupId>org.geysermc</groupId>
|
||||||
<artifactId>geyser-parent</artifactId>
|
<artifactId>geyser-parent</artifactId>
|
||||||
<version>1.2.0-SNAPSHOT</version>
|
<version>1.3.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
|
|
||||||
|
@ -26,8 +26,8 @@
|
|||||||
package org.geysermc.floodgate.util;
|
package org.geysermc.floodgate.util;
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class contains the raw data send by Geyser to Floodgate or from Floodgate to Floodgate. This
|
* This class contains the raw data send by Geyser to Floodgate or from Floodgate to Floodgate. This
|
||||||
@ -35,9 +35,9 @@ import lombok.Getter;
|
|||||||
* present in the API module of the Floodgate repo)
|
* present in the API module of the Floodgate repo)
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
public final class BedrockData implements Cloneable {
|
public final class BedrockData implements Cloneable {
|
||||||
public static final int EXPECTED_LENGTH = 10;
|
public static final int EXPECTED_LENGTH = 13;
|
||||||
|
|
||||||
private final String version;
|
private final String version;
|
||||||
private final String username;
|
private final String username;
|
||||||
@ -50,19 +50,26 @@ public final class BedrockData implements Cloneable {
|
|||||||
private final LinkedPlayer linkedPlayer;
|
private final LinkedPlayer linkedPlayer;
|
||||||
private final boolean fromProxy;
|
private final boolean fromProxy;
|
||||||
|
|
||||||
|
private final int subscribeId;
|
||||||
|
private final String verifyCode;
|
||||||
|
|
||||||
|
private final long timestamp;
|
||||||
private final int dataLength;
|
private final int dataLength;
|
||||||
|
|
||||||
public static BedrockData of(String version, String username, String xuid, int deviceOs,
|
public static BedrockData of(String version, String username, String xuid, int deviceOs,
|
||||||
String languageCode, int uiProfile, int inputMode, String ip,
|
String languageCode, int uiProfile, int inputMode, String ip,
|
||||||
LinkedPlayer linkedPlayer, boolean fromProxy) {
|
LinkedPlayer linkedPlayer, boolean fromProxy, int subscribeId,
|
||||||
|
String verifyCode) {
|
||||||
return new BedrockData(version, username, xuid, deviceOs, languageCode, inputMode,
|
return new BedrockData(version, username, xuid, deviceOs, languageCode, inputMode,
|
||||||
uiProfile, ip, linkedPlayer, fromProxy, EXPECTED_LENGTH);
|
uiProfile, ip, linkedPlayer, fromProxy, subscribeId, verifyCode,
|
||||||
|
System.currentTimeMillis(), EXPECTED_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BedrockData of(String version, String username, String xuid, int deviceOs,
|
public static BedrockData of(String version, String username, String xuid, int deviceOs,
|
||||||
String languageCode, int uiProfile, int inputMode, String ip) {
|
String languageCode, int uiProfile, int inputMode, String ip,
|
||||||
return of(version, username, xuid, deviceOs, languageCode,
|
int subscribeId, String verifyCode) {
|
||||||
uiProfile, inputMode, ip, null, false);
|
return of(version, username, xuid, deviceOs, languageCode, uiProfile, inputMode, ip, null,
|
||||||
|
false, subscribeId, verifyCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BedrockData fromString(String data) {
|
public static BedrockData fromString(String data) {
|
||||||
@ -75,13 +82,14 @@ public final class BedrockData implements Cloneable {
|
|||||||
// The format is the same as the order of the fields in this class
|
// The format is the same as the order of the fields in this class
|
||||||
return new BedrockData(
|
return new BedrockData(
|
||||||
split[0], split[1], split[2], Integer.parseInt(split[3]), split[4],
|
split[0], split[1], split[2], Integer.parseInt(split[3]), split[4],
|
||||||
Integer.parseInt(split[5]), Integer.parseInt(split[6]), split[7],
|
Integer.parseInt(split[5]), Integer.parseInt(split[6]), split[7], linkedPlayer,
|
||||||
linkedPlayer, "1".equals(split[8]), split.length
|
"1".equals(split[9]), Integer.parseInt(split[10]), split[11], Long.parseLong(split[12]), split.length
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BedrockData emptyData(int dataLength) {
|
private static BedrockData emptyData(int dataLength) {
|
||||||
return new BedrockData(null, null, null, -1, null, -1, -1, null, null, false, dataLength);
|
return new BedrockData(null, null, null, -1, null, -1, -1, null, null, false, -1, null, -1,
|
||||||
|
dataLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasPlayerLink() {
|
public boolean hasPlayerLink() {
|
||||||
@ -94,7 +102,8 @@ public final class BedrockData implements Cloneable {
|
|||||||
return version + '\0' + username + '\0' + xuid + '\0' + deviceOs + '\0' +
|
return version + '\0' + username + '\0' + xuid + '\0' + deviceOs + '\0' +
|
||||||
languageCode + '\0' + uiProfile + '\0' + inputMode + '\0' + ip + '\0' +
|
languageCode + '\0' + uiProfile + '\0' + inputMode + '\0' + ip + '\0' +
|
||||||
(fromProxy ? 1 : 0) + '\0' +
|
(fromProxy ? 1 : 0) + '\0' +
|
||||||
(linkedPlayer != null ? linkedPlayer.toString() : "null");
|
(linkedPlayer != null ? linkedPlayer.toString() : "null") + '\0' +
|
||||||
|
subscribeId + '\0' + verifyCode + '\0' + timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -29,7 +29,7 @@ package org.geysermc.floodgate.util;
|
|||||||
public enum InputMode {
|
public enum InputMode {
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
KEYBOARD_MOUSE,
|
KEYBOARD_MOUSE,
|
||||||
TOUCH, // I guess Touch?
|
TOUCH,
|
||||||
CONTROLLER,
|
CONTROLLER,
|
||||||
VR;
|
VR;
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ public final class LinkedPlayer implements Cloneable {
|
|||||||
return new LinkedPlayer(javaUsername, javaUniqueId, bedrockId);
|
return new LinkedPlayer(javaUsername, javaUniqueId, bedrockId);
|
||||||
}
|
}
|
||||||
|
|
||||||
static LinkedPlayer fromString(String data) {
|
public static LinkedPlayer fromString(String data) {
|
||||||
String[] split = data.split(";");
|
String[] split = data.split(";");
|
||||||
if (split.length != 3) {
|
if (split.length != 3) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -1,106 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
* @author GeyserMC
|
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.geysermc.floodgate.util;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
import static java.lang.String.format;
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
|
||||||
public final class RawSkin {
|
|
||||||
public int width;
|
|
||||||
public int height;
|
|
||||||
public byte[] data;
|
|
||||||
public boolean alex;
|
|
||||||
|
|
||||||
private RawSkin() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RawSkin decode(byte[] data, int offset) throws InvalidFormatException {
|
|
||||||
if (data == null || offset < 0 || data.length <= offset) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (offset == 0) {
|
|
||||||
return decode(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] rawSkin = new byte[data.length - offset];
|
|
||||||
System.arraycopy(data, offset, rawSkin, 0, rawSkin.length);
|
|
||||||
return decode(rawSkin);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RawSkin decode(byte[] data) throws InvalidFormatException {
|
|
||||||
// offset is an amount of bytes before the Base64 starts
|
|
||||||
if (data == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
int maxEncodedLength = Base64Utils.getEncodedLength(64 * 64 * 4 + 9);
|
|
||||||
// if the RawSkin is longer then the max Java Edition skin length
|
|
||||||
if (data.length > maxEncodedLength) {
|
|
||||||
throw new InvalidFormatException(format(
|
|
||||||
"Encoded data cannot be longer then %s bytes! Got %s",
|
|
||||||
maxEncodedLength, data.length
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the encoded data doesn't even contain the width, height (8 bytes, 2 ints) and isAlex
|
|
||||||
if (data.length < Base64Utils.getEncodedLength(9)) {
|
|
||||||
throw new InvalidFormatException("Encoded data must be at least 16 bytes long!");
|
|
||||||
}
|
|
||||||
|
|
||||||
data = Base64.getDecoder().decode(data);
|
|
||||||
|
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
|
||||||
|
|
||||||
RawSkin skin = new RawSkin();
|
|
||||||
skin.width = buffer.getInt();
|
|
||||||
skin.height = buffer.getInt();
|
|
||||||
if (buffer.remaining() - 1 != (skin.width * skin.height * 4)) {
|
|
||||||
throw new InvalidFormatException(format(
|
|
||||||
"Expected skin length to be %s, got %s",
|
|
||||||
(skin.width * skin.height * 4), buffer.remaining()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
skin.data = new byte[buffer.remaining() - 1];
|
|
||||||
buffer.get(skin.data);
|
|
||||||
skin.alex = buffer.get() == 1;
|
|
||||||
return skin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] encode() {
|
|
||||||
// 2 x int + 1 = 9 bytes
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(9 + data.length);
|
|
||||||
buffer.putInt(width);
|
|
||||||
buffer.putInt(height);
|
|
||||||
buffer.put(data);
|
|
||||||
buffer.put((byte) (alex ? 1 : 0));
|
|
||||||
return Base64.getEncoder().encode(buffer.array());
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.floodgate.util;
|
||||||
|
|
||||||
|
public enum WebsocketEventType {
|
||||||
|
SUBSCRIBER_CREATED,
|
||||||
|
SUBSCRIBERS_COUNT,
|
||||||
|
ADDED_TO_QUEUE,
|
||||||
|
SKIN_UPLOADED,
|
||||||
|
CREATOR_DISCONNECTED;
|
||||||
|
|
||||||
|
public static final WebsocketEventType[] VALUES = values();
|
||||||
|
|
||||||
|
public static WebsocketEventType getById(int id) {
|
||||||
|
return VALUES.length > id ? VALUES[id] : null;
|
||||||
|
}
|
||||||
|
}
|
@ -6,39 +6,46 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.geysermc</groupId>
|
<groupId>org.geysermc</groupId>
|
||||||
<artifactId>geyser-parent</artifactId>
|
<artifactId>geyser-parent</artifactId>
|
||||||
<version>1.2.0-SNAPSHOT</version>
|
<version>1.3.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>connector</artifactId>
|
<artifactId>connector</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<netty.version>4.1.59.Final</netty.version>
|
||||||
|
<fastutil.version>8.5.2</fastutil.version>
|
||||||
|
<adventure.version>4.7.0</adventure.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.geysermc</groupId>
|
<groupId>org.geysermc</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>1.2.0-SNAPSHOT</version>
|
<version>1.3.0-SNAPSHOT</version>
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
|
||||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
|
||||||
<version>2.9.8</version>
|
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||||
<version>2.9.8</version>
|
<version>2.10.2</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||||
|
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||||
|
<version>2.10.2</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.java-websocket</groupId>
|
||||||
|
<artifactId>Java-WebSocket</artifactId>
|
||||||
|
<version>1.5.1</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.CloudburstMC.Protocol</groupId>
|
<groupId>com.github.CloudburstMC.Protocol</groupId>
|
||||||
<artifactId>bedrock-v422</artifactId>
|
<artifactId>bedrock-v428</artifactId>
|
||||||
<version>d41b84e86c</version>
|
<version>42da92f</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
|
||||||
<groupId>net.sf.trove4j</groupId>
|
|
||||||
<artifactId>trove</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
<!-- Stay on the older version of Network while it's rewritten -->
|
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>com.nukkitx.network</groupId>
|
<groupId>com.nukkitx.network</groupId>
|
||||||
<artifactId>raknet</artifactId>
|
<artifactId>raknet</artifactId>
|
||||||
@ -48,67 +55,73 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.nukkitx.network</groupId>
|
<groupId>com.nukkitx.network</groupId>
|
||||||
<artifactId>raknet</artifactId>
|
<artifactId>raknet</artifactId>
|
||||||
<version>1.6.20</version>
|
<version>1.6.26-20210217.205834-2</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>*</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.nukkitx.fastutil</groupId>
|
<groupId>com.nukkitx.fastutil</groupId>
|
||||||
<artifactId>fastutil-int-int-maps</artifactId>
|
<artifactId>fastutil-int-int-maps</artifactId>
|
||||||
<version>8.3.1</version>
|
<version>${fastutil.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.nukkitx.fastutil</groupId>
|
<groupId>com.nukkitx.fastutil</groupId>
|
||||||
<artifactId>fastutil-int-float-maps</artifactId>
|
<artifactId>fastutil-int-float-maps</artifactId>
|
||||||
<version>8.3.1</version>
|
<version>${fastutil.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.nukkitx.fastutil</groupId>
|
<groupId>com.nukkitx.fastutil</groupId>
|
||||||
<artifactId>fastutil-long-long-maps</artifactId>
|
<artifactId>fastutil-long-long-maps</artifactId>
|
||||||
<version>8.3.1</version>
|
<version>${fastutil.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.nukkitx.fastutil</groupId>
|
<groupId>com.nukkitx.fastutil</groupId>
|
||||||
<artifactId>fastutil-object-long-maps</artifactId>
|
<artifactId>fastutil-object-long-maps</artifactId>
|
||||||
<version>8.3.1</version>
|
<version>${fastutil.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.nukkitx.fastutil</groupId>
|
<groupId>com.nukkitx.fastutil</groupId>
|
||||||
<artifactId>fastutil-int-byte-maps</artifactId>
|
<artifactId>fastutil-int-byte-maps</artifactId>
|
||||||
<version>8.3.1</version>
|
<version>${fastutil.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.nukkitx.fastutil</groupId>
|
<groupId>com.nukkitx.fastutil</groupId>
|
||||||
<artifactId>fastutil-int-double-maps</artifactId>
|
<artifactId>fastutil-int-double-maps</artifactId>
|
||||||
<version>8.3.1</version>
|
<version>${fastutil.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.nukkitx.fastutil</groupId>
|
<groupId>com.nukkitx.fastutil</groupId>
|
||||||
<artifactId>fastutil-int-boolean-maps</artifactId>
|
<artifactId>fastutil-int-boolean-maps</artifactId>
|
||||||
<version>8.3.1</version>
|
<version>${fastutil.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.nukkitx.fastutil</groupId>
|
<groupId>com.nukkitx.fastutil</groupId>
|
||||||
<artifactId>fastutil-object-int-maps</artifactId>
|
<artifactId>fastutil-object-int-maps</artifactId>
|
||||||
<version>8.3.1</version>
|
<version>${fastutil.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.nukkitx.fastutil</groupId>
|
<groupId>com.nukkitx.fastutil</groupId>
|
||||||
<artifactId>fastutil-object-byte-maps</artifactId>
|
<artifactId>fastutil-object-byte-maps</artifactId>
|
||||||
<version>8.3.1</version>
|
<version>${fastutil.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.nukkitx.fastutil</groupId>
|
<groupId>com.nukkitx.fastutil</groupId>
|
||||||
<artifactId>fastutil-object-object-maps</artifactId>
|
<artifactId>fastutil-object-object-maps</artifactId>
|
||||||
<version>8.3.1</version>
|
<version>${fastutil.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -138,9 +151,9 @@
|
|||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.steveice10</groupId>
|
<groupId>com.github.GeyserMC</groupId>
|
||||||
<artifactId>PacketLib</artifactId>
|
<artifactId>PacketLib</artifactId>
|
||||||
<version>54f761c</version>
|
<version>b77a427</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion> <!-- Move this exclusion back to MCProtocolLib it gets the latest PacketLib -->
|
<exclusion> <!-- Move this exclusion back to MCProtocolLib it gets the latest PacketLib -->
|
||||||
@ -152,15 +165,51 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.netty</groupId>
|
<groupId>io.netty</groupId>
|
||||||
<artifactId>netty-resolver-dns</artifactId>
|
<artifactId>netty-resolver-dns</artifactId>
|
||||||
<version>4.1.43.Final</version>
|
<version>${netty.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-resolver-dns-native-macos</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<classifier>osx-x86_64</classifier>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.netty</groupId>
|
<groupId>io.netty</groupId>
|
||||||
<artifactId>netty-codec-haproxy</artifactId>
|
<artifactId>netty-codec-haproxy</artifactId>
|
||||||
<version>4.1.56.Final</version>
|
<version>${netty.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- Network dependencies we are updating ourselves -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-handler</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-transport-native-epoll</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<classifier>linux-x86_64</classifier>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-transport-native-epoll</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<classifier>linux-aarch_64</classifier>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-transport-native-kqueue</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<classifier>osx-x86_64</classifier>
|
||||||
|
</dependency>
|
||||||
|
<!-- End -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.reflections</groupId>
|
<groupId>org.reflections</groupId>
|
||||||
<artifactId>reflections</artifactId>
|
<artifactId>reflections</artifactId>
|
||||||
@ -174,25 +223,25 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.kyori</groupId>
|
<groupId>net.kyori</groupId>
|
||||||
<artifactId>adventure-api</artifactId>
|
<artifactId>adventure-api</artifactId>
|
||||||
<version>4.3.0</version>
|
<version>${adventure.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.kyori</groupId>
|
<groupId>net.kyori</groupId>
|
||||||
<artifactId>adventure-text-serializer-gson</artifactId>
|
<artifactId>adventure-text-serializer-gson</artifactId>
|
||||||
<version>4.3.0</version>
|
<version>${adventure.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.kyori</groupId>
|
<groupId>net.kyori</groupId>
|
||||||
<artifactId>adventure-text-serializer-legacy</artifactId>
|
<artifactId>adventure-text-serializer-legacy</artifactId>
|
||||||
<version>4.3.0</version>
|
<version>${adventure.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.kyori</groupId>
|
<groupId>net.kyori</groupId>
|
||||||
<artifactId>adventure-text-serializer-gson-legacy-impl</artifactId>
|
<artifactId>adventure-text-serializer-gson-legacy-impl</artifactId>
|
||||||
<version>4.3.0</version>
|
<version>${adventure.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -33,9 +33,18 @@ import java.nio.file.Path;
|
|||||||
|
|
||||||
public class FloodgateKeyLoader {
|
public class FloodgateKeyLoader {
|
||||||
public static Path getKeyPath(GeyserJacksonConfiguration config, Object floodgate, Path floodgateDataFolder, Path geyserDataFolder, GeyserLogger logger) {
|
public static Path getKeyPath(GeyserJacksonConfiguration config, Object floodgate, Path floodgateDataFolder, Path geyserDataFolder, GeyserLogger logger) {
|
||||||
|
if (!config.getRemote().getAuthType().equals("floodgate")) {
|
||||||
|
return geyserDataFolder.resolve(config.getFloodgateKeyFile());
|
||||||
|
}
|
||||||
|
|
||||||
Path floodgateKey = geyserDataFolder.resolve(config.getFloodgateKeyFile());
|
Path floodgateKey = geyserDataFolder.resolve(config.getFloodgateKeyFile());
|
||||||
|
|
||||||
if (!Files.exists(floodgateKey) && config.getRemote().getAuthType().equals("floodgate")) {
|
if (config.getFloodgateKeyFile().equals("public-key.pem")) {
|
||||||
|
logger.info("Floodgate 2.0 doesn't use a public/private key system anymore. We'll search for key.pem instead");
|
||||||
|
floodgateKey = geyserDataFolder.resolve("key.pem");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Files.exists(floodgateKey)) {
|
||||||
if (floodgate != null) {
|
if (floodgate != null) {
|
||||||
Path autoKey = floodgateDataFolder.resolve("key.pem");
|
Path autoKey = floodgateDataFolder.resolve("key.pem");
|
||||||
if (Files.exists(autoKey)) {
|
if (Files.exists(autoKey)) {
|
||||||
|
@ -29,6 +29,7 @@ import com.fasterxml.jackson.core.JsonParser;
|
|||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.nukkitx.network.raknet.RakNetConstants;
|
import com.nukkitx.network.raknet.RakNetConstants;
|
||||||
|
import com.nukkitx.network.util.EventLoops;
|
||||||
import com.nukkitx.protocol.bedrock.BedrockServer;
|
import com.nukkitx.protocol.bedrock.BedrockServer;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@ -39,7 +40,6 @@ import org.geysermc.connector.common.AuthType;
|
|||||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||||
import org.geysermc.connector.metrics.Metrics;
|
import org.geysermc.connector.metrics.Metrics;
|
||||||
import org.geysermc.connector.network.ConnectorServerEventHandler;
|
import org.geysermc.connector.network.ConnectorServerEventHandler;
|
||||||
import org.geysermc.connector.network.remote.RemoteServer;
|
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.BiomeTranslator;
|
import org.geysermc.connector.network.translators.BiomeTranslator;
|
||||||
import org.geysermc.connector.network.translators.EntityIdentifierRegistry;
|
import org.geysermc.connector.network.translators.EntityIdentifierRegistry;
|
||||||
@ -56,10 +56,8 @@ import org.geysermc.connector.network.translators.world.WorldManager;
|
|||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
|
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
|
||||||
import org.geysermc.connector.network.translators.world.block.entity.SkullBlockEntityTranslator;
|
import org.geysermc.connector.network.translators.world.block.entity.SkullBlockEntityTranslator;
|
||||||
import org.geysermc.connector.utils.DimensionUtils;
|
import org.geysermc.connector.skin.FloodgateSkinUploader;
|
||||||
import org.geysermc.connector.utils.LanguageUtils;
|
import org.geysermc.connector.utils.*;
|
||||||
import org.geysermc.connector.utils.LocaleUtils;
|
|
||||||
import org.geysermc.connector.utils.ResourcePack;
|
|
||||||
import org.geysermc.floodgate.crypto.AesCipher;
|
import org.geysermc.floodgate.crypto.AesCipher;
|
||||||
import org.geysermc.floodgate.crypto.AesKeyProducer;
|
import org.geysermc.floodgate.crypto.AesKeyProducer;
|
||||||
import org.geysermc.floodgate.crypto.Base64Topping;
|
import org.geysermc.floodgate.crypto.Base64Topping;
|
||||||
@ -85,7 +83,8 @@ public class GeyserConnector {
|
|||||||
.enable(JsonParser.Feature.IGNORE_UNDEFINED)
|
.enable(JsonParser.Feature.IGNORE_UNDEFINED)
|
||||||
.enable(JsonParser.Feature.ALLOW_COMMENTS)
|
.enable(JsonParser.Feature.ALLOW_COMMENTS)
|
||||||
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
|
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
|
||||||
.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES);
|
.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES)
|
||||||
|
.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
|
||||||
|
|
||||||
public static final String NAME = "Geyser";
|
public static final String NAME = "Geyser";
|
||||||
public static final String GIT_VERSION = "DEV"; // A fallback for running in IDEs
|
public static final String GIT_VERSION = "DEV"; // A fallback for running in IDEs
|
||||||
@ -103,11 +102,11 @@ public class GeyserConnector {
|
|||||||
|
|
||||||
private static GeyserConnector instance;
|
private static GeyserConnector instance;
|
||||||
|
|
||||||
private RemoteServer remoteServer;
|
|
||||||
@Setter
|
@Setter
|
||||||
private AuthType authType;
|
private AuthType defaultAuthType;
|
||||||
|
|
||||||
private FloodgateCipher cipher;
|
private FloodgateCipher cipher;
|
||||||
|
private FloodgateSkinUploader skinUploader;
|
||||||
|
|
||||||
private boolean shuttingDown = false;
|
private boolean shuttingDown = false;
|
||||||
|
|
||||||
@ -174,7 +173,7 @@ public class GeyserConnector {
|
|||||||
String remoteAddress = config.getRemote().getAddress();
|
String remoteAddress = config.getRemote().getAddress();
|
||||||
int remotePort = config.getRemote().getPort();
|
int remotePort = config.getRemote().getPort();
|
||||||
// Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry.
|
// Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry.
|
||||||
if ((config.isLegacyPingPassthrough() || platformType == PlatformType.STANDALONE) && !remoteAddress.matches(IP_REGEX) && !remoteAddress.equalsIgnoreCase("localhost")) {
|
if (!remoteAddress.matches(IP_REGEX) && !remoteAddress.equalsIgnoreCase("localhost")) {
|
||||||
try {
|
try {
|
||||||
// Searches for a server address and a port from a SRV record of the specified host name
|
// Searches for a server address and a port from a SRV record of the specified host name
|
||||||
InitialDirContext ctx = new InitialDirContext();
|
InitialDirContext ctx = new InitialDirContext();
|
||||||
@ -194,20 +193,21 @@ public class GeyserConnector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteServer = new RemoteServer(config.getRemote().getAddress(), remotePort);
|
defaultAuthType = AuthType.getByName(config.getRemote().getAuthType());
|
||||||
authType = AuthType.getByName(config.getRemote().getAuthType());
|
|
||||||
|
|
||||||
if (authType == AuthType.FLOODGATE) {
|
if (defaultAuthType == AuthType.FLOODGATE) {
|
||||||
try {
|
try {
|
||||||
Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath());
|
Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath());
|
||||||
cipher = new AesCipher(new Base64Topping());
|
cipher = new AesCipher(new Base64Topping());
|
||||||
cipher.init(key);
|
cipher.init(key);
|
||||||
logger.info(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.loaded_key"));
|
logger.info(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.loaded_key"));
|
||||||
|
skinUploader = new FloodgateSkinUploader(this).start();
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
logger.severe(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.bad_key"), exception);
|
logger.severe(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.bad_key"), exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CooldownUtils.setShowCooldown(config.isShowCooldown());
|
||||||
DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether
|
DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether
|
||||||
SkullBlockEntityTranslator.ALLOW_CUSTOM_SKULLS = config.isAllowCustomSkulls();
|
SkullBlockEntityTranslator.ALLOW_CUSTOM_SKULLS = config.isAllowCustomSkulls();
|
||||||
|
|
||||||
@ -215,7 +215,13 @@ public class GeyserConnector {
|
|||||||
RakNetConstants.MAXIMUM_MTU_SIZE = (short) config.getMtu();
|
RakNetConstants.MAXIMUM_MTU_SIZE = (short) config.getMtu();
|
||||||
logger.debug("Setting MTU to " + config.getMtu());
|
logger.debug("Setting MTU to " + config.getMtu());
|
||||||
|
|
||||||
bedrockServer = new BedrockServer(new InetSocketAddress(config.getBedrock().getAddress(), config.getBedrock().getPort()));
|
boolean enableProxyProtocol = config.getBedrock().isEnableProxyProtocol();
|
||||||
|
bedrockServer = new BedrockServer(
|
||||||
|
new InetSocketAddress(config.getBedrock().getAddress(), config.getBedrock().getPort()),
|
||||||
|
1,
|
||||||
|
EventLoops.commonGroup(),
|
||||||
|
enableProxyProtocol
|
||||||
|
);
|
||||||
bedrockServer.setHandler(new ConnectorServerEventHandler(this));
|
bedrockServer.setHandler(new ConnectorServerEventHandler(this));
|
||||||
bedrockServer.bind().whenComplete((avoid, throwable) -> {
|
bedrockServer.bind().whenComplete((avoid, throwable) -> {
|
||||||
if (throwable == null) {
|
if (throwable == null) {
|
||||||
@ -262,6 +268,20 @@ public class GeyserConnector {
|
|||||||
}
|
}
|
||||||
return valueMap;
|
return valueMap;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
String minecraftVersion = bootstrap.getMinecraftServerVersion();
|
||||||
|
if (minecraftVersion != null) {
|
||||||
|
Map<String, Map<String, Integer>> versionMap = new HashMap<>();
|
||||||
|
Map<String, Integer> platformMap = new HashMap<>();
|
||||||
|
platformMap.put(platformType.getPlatformName(), 1);
|
||||||
|
versionMap.put(minecraftVersion, platformMap);
|
||||||
|
|
||||||
|
metrics.addCustomChart(new Metrics.DrilldownPie("minecraftServerVersion", () -> {
|
||||||
|
// By the end, we should return, for example:
|
||||||
|
// 1.16.5 => (Spigot, 1)
|
||||||
|
return versionMap;
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isGui = false;
|
boolean isGui = false;
|
||||||
@ -332,8 +352,7 @@ public class GeyserConnector {
|
|||||||
generalThreadPool.shutdown();
|
generalThreadPool.shutdown();
|
||||||
bedrockServer.close();
|
bedrockServer.close();
|
||||||
players.clear();
|
players.clear();
|
||||||
remoteServer = null;
|
defaultAuthType = null;
|
||||||
authType = null;
|
|
||||||
this.getCommandManager().getCommands().clear();
|
this.getCommandManager().getCommands().clear();
|
||||||
|
|
||||||
bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown.done"));
|
bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown.done"));
|
||||||
@ -369,6 +388,7 @@ public class GeyserConnector {
|
|||||||
* @param xuid the Xbox user identifier
|
* @param xuid the Xbox user identifier
|
||||||
* @return the player or <code>null</code> if there is no player online with this xuid
|
* @return the player or <code>null</code> if there is no player online with this xuid
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused") // API usage
|
||||||
public GeyserSession getPlayerByXuid(String xuid) {
|
public GeyserSession getPlayerByXuid(String xuid) {
|
||||||
for (GeyserSession session : players) {
|
for (GeyserSession session : players) {
|
||||||
if (session.getAuthData() != null && session.getAuthData().getXboxUUID().equals(xuid)) {
|
if (session.getAuthData() != null && session.getAuthData().getXboxUUID().equals(xuid)) {
|
||||||
|
@ -33,6 +33,7 @@ import org.geysermc.connector.command.CommandManager;
|
|||||||
import org.geysermc.connector.network.translators.world.GeyserWorldManager;
|
import org.geysermc.connector.network.translators.world.GeyserWorldManager;
|
||||||
import org.geysermc.connector.network.translators.world.WorldManager;
|
import org.geysermc.connector.network.translators.world.WorldManager;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public interface GeyserBootstrap {
|
public interface GeyserBootstrap {
|
||||||
@ -99,4 +100,18 @@ public interface GeyserBootstrap {
|
|||||||
* @return The info about the bootstrap
|
* @return The info about the bootstrap
|
||||||
*/
|
*/
|
||||||
BootstrapDumpInfo getDumpInfo();
|
BootstrapDumpInfo getDumpInfo();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Minecraft version currently being used on the server. This should be only be implemented on platforms
|
||||||
|
* that have direct server access - platforms such as proxies always have to be on their latest version to support
|
||||||
|
* the newest Minecraft version, but older servers can use ViaVersion to enable newer versions to join.
|
||||||
|
* <br>
|
||||||
|
* If used, this should not be null before {@link org.geysermc.connector.GeyserConnector} initialization.
|
||||||
|
*
|
||||||
|
* @return the Minecraft version being used on the server, or <code>null</code> if not applicable
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
default String getMinecraftServerVersion() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ public class IGeyserMain {
|
|||||||
* @return The formatted message
|
* @return The formatted message
|
||||||
*/
|
*/
|
||||||
private String createMessage() {
|
private String createMessage() {
|
||||||
String message = "";
|
StringBuilder message = new StringBuilder();
|
||||||
|
|
||||||
InputStream helpStream = IGeyserMain.class.getClassLoader().getResourceAsStream("languages/run-help/" + Locale.getDefault().toString() + ".txt");
|
InputStream helpStream = IGeyserMain.class.getClassLoader().getResourceAsStream("languages/run-help/" + Locale.getDefault().toString() + ".txt");
|
||||||
|
|
||||||
@ -68,10 +68,10 @@ public class IGeyserMain {
|
|||||||
line = line.replace("${plugin_type}", this.getPluginType());
|
line = line.replace("${plugin_type}", this.getPluginType());
|
||||||
line = line.replace("${plugin_folder}", this.getPluginFolder());
|
line = line.replace("${plugin_folder}", this.getPluginFolder());
|
||||||
|
|
||||||
message += line + "\n";
|
message.append(line).append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return message;
|
return message.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,9 +27,11 @@ package org.geysermc.connector.configuration;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import org.geysermc.connector.GeyserLogger;
|
import org.geysermc.connector.GeyserLogger;
|
||||||
|
import org.geysermc.connector.network.CIDRMatcher;
|
||||||
import org.geysermc.connector.utils.LanguageUtils;
|
import org.geysermc.connector.utils.LanguageUtils;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public interface GeyserConfiguration {
|
public interface GeyserConfiguration {
|
||||||
@ -59,6 +61,8 @@ public interface GeyserConfiguration {
|
|||||||
|
|
||||||
int getPingPassthroughInterval();
|
int getPingPassthroughInterval();
|
||||||
|
|
||||||
|
boolean isForwardPlayerPing();
|
||||||
|
|
||||||
int getMaxPlayers();
|
int getMaxPlayers();
|
||||||
|
|
||||||
boolean isDebugMode();
|
boolean isDebugMode();
|
||||||
@ -104,6 +108,15 @@ public interface GeyserConfiguration {
|
|||||||
String getMotd2();
|
String getMotd2();
|
||||||
|
|
||||||
String getServerName();
|
String getServerName();
|
||||||
|
|
||||||
|
boolean isEnableProxyProtocol();
|
||||||
|
|
||||||
|
List<String> getProxyProtocolWhitelistedIPs();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Unmodifiable list of {@link CIDRMatcher}s from {@link #getProxyProtocolWhitelistedIPs()}
|
||||||
|
*/
|
||||||
|
List<CIDRMatcher> getWhitelistedIPsMatchers();
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRemoteConfiguration {
|
interface IRemoteConfiguration {
|
||||||
|
@ -25,16 +25,21 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.configuration;
|
package org.geysermc.connector.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.common.serializer.AsteriskSerializer;
|
import org.geysermc.connector.common.serializer.AsteriskSerializer;
|
||||||
|
import org.geysermc.connector.network.CIDRMatcher;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
@ -74,6 +79,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||||||
@JsonProperty("ping-passthrough-interval")
|
@JsonProperty("ping-passthrough-interval")
|
||||||
private int pingPassthroughInterval = 3;
|
private int pingPassthroughInterval = 3;
|
||||||
|
|
||||||
|
@JsonProperty("forward-player-ping")
|
||||||
|
private boolean forwardPlayerPing = false;
|
||||||
|
|
||||||
@JsonProperty("max-players")
|
@JsonProperty("max-players")
|
||||||
private int maxPlayers = 100;
|
private int maxPlayers = 100;
|
||||||
|
|
||||||
@ -119,6 +127,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||||||
private MetricsInfo metrics = new MetricsInfo();
|
private MetricsInfo metrics = new MetricsInfo();
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public static class BedrockConfiguration implements IBedrockConfiguration {
|
public static class BedrockConfiguration implements IBedrockConfiguration {
|
||||||
@AsteriskSerializer.Asterisk(sensitive = true)
|
@AsteriskSerializer.Asterisk(sensitive = true)
|
||||||
private String address = "0.0.0.0";
|
private String address = "0.0.0.0";
|
||||||
@ -134,9 +143,33 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||||||
|
|
||||||
@JsonProperty("server-name")
|
@JsonProperty("server-name")
|
||||||
private String serverName = GeyserConnector.NAME;
|
private String serverName = GeyserConnector.NAME;
|
||||||
|
|
||||||
|
@JsonProperty("enable-proxy-protocol")
|
||||||
|
private boolean enableProxyProtocol = false;
|
||||||
|
|
||||||
|
@JsonProperty("proxy-protocol-whitelisted-ips")
|
||||||
|
private List<String> proxyProtocolWhitelistedIPs = Collections.emptyList();
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private List<CIDRMatcher> whitelistedIPsMatchers = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CIDRMatcher> getWhitelistedIPsMatchers() {
|
||||||
|
// Effective Java, Third Edition; Item 83: Use lazy initialization judiciously
|
||||||
|
List<CIDRMatcher> matchers = this.whitelistedIPsMatchers;
|
||||||
|
if (matchers == null) {
|
||||||
|
synchronized (this) {
|
||||||
|
this.whitelistedIPsMatchers = matchers = proxyProtocolWhitelistedIPs.stream()
|
||||||
|
.map(CIDRMatcher::new)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(matchers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public static class RemoteConfiguration implements IRemoteConfiguration {
|
public static class RemoteConfiguration implements IRemoteConfiguration {
|
||||||
@Setter
|
@Setter
|
||||||
@AsteriskSerializer.Asterisk(sensitive = true)
|
@AsteriskSerializer.Asterisk(sensitive = true)
|
||||||
@ -170,6 +203,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public static class MetricsInfo implements IMetricsInfo {
|
public static class MetricsInfo implements IMetricsInfo {
|
||||||
private boolean enabled = true;
|
private boolean enabled = true;
|
||||||
|
|
||||||
|
@ -35,6 +35,8 @@ public class AbstractArrowEntity extends Entity {
|
|||||||
|
|
||||||
public AbstractArrowEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
public AbstractArrowEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||||
|
|
||||||
|
setMotion(motion);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -47,4 +49,20 @@ public class AbstractArrowEntity extends Entity {
|
|||||||
|
|
||||||
super.updateBedrockMetadata(entityMetadata, session);
|
super.updateBedrockMetadata(entityMetadata, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRotation(Vector3f rotation) {
|
||||||
|
// Ignore the rotation sent by the Java server since the
|
||||||
|
// Java client calculates the rotation from the motion
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMotion(Vector3f motion) {
|
||||||
|
super.setMotion(motion);
|
||||||
|
|
||||||
|
double horizontalSpeed = Math.sqrt(motion.getX() * motion.getX() + motion.getZ() * motion.getZ());
|
||||||
|
float yaw = (float) Math.toDegrees(Math.atan2(motion.getX(), motion.getZ()));
|
||||||
|
float pitch = (float) Math.toDegrees(Math.atan2(motion.getY(), horizontalSpeed));
|
||||||
|
rotation = Vector3f.from(yaw, pitch, yaw);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ package org.geysermc.connector.entity;
|
|||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||||
import com.nukkitx.math.vector.Vector3f;
|
import com.nukkitx.math.vector.Vector3f;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.AnimatePacket;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ public class BoatEntity extends Entity {
|
|||||||
*/
|
*/
|
||||||
private static final String BUOYANCY_DATA = "{\"apply_gravity\":true,\"base_buoyancy\":1.0,\"big_wave_probability\":0.02999999932944775," +
|
private static final String BUOYANCY_DATA = "{\"apply_gravity\":true,\"base_buoyancy\":1.0,\"big_wave_probability\":0.02999999932944775," +
|
||||||
"\"big_wave_speed\":10.0,\"drag_down_on_buoyancy_removed\":0.0,\"liquid_blocks\":[\"minecraft:water\"," +
|
"\"big_wave_speed\":10.0,\"drag_down_on_buoyancy_removed\":0.0,\"liquid_blocks\":[\"minecraft:water\"," +
|
||||||
"\"minecraft:flowing_water\"],\"simulate_waves\":false}}";
|
"\"minecraft:flowing_water\"],\"simulate_waves\":false}";
|
||||||
|
|
||||||
private boolean isPaddlingLeft;
|
private boolean isPaddlingLeft;
|
||||||
private float paddleTimeLeft;
|
private float paddleTimeLeft;
|
||||||
@ -105,28 +106,39 @@ public class BoatEntity extends Entity {
|
|||||||
metadata.put(EntityData.VARIANT, entityMetadata.getValue());
|
metadata.put(EntityData.VARIANT, entityMetadata.getValue());
|
||||||
} else if (entityMetadata.getId() == 11) {
|
} else if (entityMetadata.getId() == 11) {
|
||||||
isPaddlingLeft = (boolean) entityMetadata.getValue();
|
isPaddlingLeft = (boolean) entityMetadata.getValue();
|
||||||
if (!isPaddlingLeft) {
|
if (isPaddlingLeft) {
|
||||||
metadata.put(EntityData.ROW_TIME_LEFT, 0f);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Java sends simply "true" and "false" (is_paddling_left), Bedrock keeps sending packets as you're rowing
|
// Java sends simply "true" and "false" (is_paddling_left), Bedrock keeps sending packets as you're rowing
|
||||||
// This is an asynchronous method that emulates Bedrock rowing until "false" is sent.
|
// This is an asynchronous method that emulates Bedrock rowing until "false" is sent.
|
||||||
paddleTimeLeft = 0f;
|
paddleTimeLeft = 0f;
|
||||||
|
if (!this.passengers.isEmpty()) {
|
||||||
|
// Get the entity by the first stored passenger and convey motion in this manner
|
||||||
|
Entity entity = session.getEntityCache().getEntityByJavaId(this.passengers.iterator().nextLong());
|
||||||
|
if (entity != null) {
|
||||||
session.getConnector().getGeneralThreadPool().execute(() ->
|
session.getConnector().getGeneralThreadPool().execute(() ->
|
||||||
updateLeftPaddle(session, entityMetadata)
|
updateLeftPaddle(session, entity)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Indicate that the row position should be reset
|
||||||
|
metadata.put(EntityData.ROW_TIME_LEFT, 0.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (entityMetadata.getId() == 12) {
|
else if (entityMetadata.getId() == 12) {
|
||||||
isPaddlingRight = (boolean) entityMetadata.getValue();
|
isPaddlingRight = (boolean) entityMetadata.getValue();
|
||||||
if (!isPaddlingRight) {
|
if (isPaddlingRight) {
|
||||||
metadata.put(EntityData.ROW_TIME_RIGHT, 0f);
|
|
||||||
} else {
|
|
||||||
paddleTimeRight = 0f;
|
paddleTimeRight = 0f;
|
||||||
|
if (!this.passengers.isEmpty()) {
|
||||||
|
Entity entity = session.getEntityCache().getEntityByJavaId(this.passengers.iterator().nextLong());
|
||||||
|
if (entity != null) {
|
||||||
session.getConnector().getGeneralThreadPool().execute(() ->
|
session.getConnector().getGeneralThreadPool().execute(() ->
|
||||||
updateRightPaddle(session, entityMetadata)
|
updateRightPaddle(session, entity)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
metadata.put(EntityData.ROW_TIME_RIGHT, 0.0f);
|
||||||
|
}
|
||||||
} else if (entityMetadata.getId() == 13) {
|
} else if (entityMetadata.getId() == 13) {
|
||||||
// Possibly - I don't think this does anything?
|
// Possibly - I don't think this does anything?
|
||||||
metadata.put(EntityData.BOAT_BUBBLE_TIME, entityMetadata.getValue());
|
metadata.put(EntityData.BOAT_BUBBLE_TIME, entityMetadata.getValue());
|
||||||
@ -135,27 +147,46 @@ public class BoatEntity extends Entity {
|
|||||||
super.updateBedrockMetadata(entityMetadata, session);
|
super.updateBedrockMetadata(entityMetadata, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateLeftPaddle(GeyserSession session, EntityMetadata entityMetadata) {
|
@Override
|
||||||
|
public void updateBedrockMetadata(GeyserSession session) {
|
||||||
|
super.updateBedrockMetadata(session);
|
||||||
|
|
||||||
|
// As these indicate to reset rowing, remove them until it is time to send them out again.
|
||||||
|
metadata.remove(EntityData.ROW_TIME_LEFT);
|
||||||
|
metadata.remove(EntityData.ROW_TIME_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateLeftPaddle(GeyserSession session, Entity rower) {
|
||||||
if (isPaddlingLeft) {
|
if (isPaddlingLeft) {
|
||||||
paddleTimeLeft += ROWING_SPEED;
|
paddleTimeLeft += ROWING_SPEED;
|
||||||
metadata.put(EntityData.ROW_TIME_LEFT, paddleTimeLeft);
|
sendAnimationPacket(session, rower, AnimatePacket.Action.ROW_LEFT, paddleTimeLeft);
|
||||||
super.updateBedrockMetadata(entityMetadata, session);
|
|
||||||
session.getConnector().getGeneralThreadPool().schedule(() ->
|
session.getConnector().getGeneralThreadPool().schedule(() ->
|
||||||
updateLeftPaddle(session, entityMetadata),
|
updateLeftPaddle(session, rower),
|
||||||
100,
|
100,
|
||||||
TimeUnit.MILLISECONDS
|
TimeUnit.MILLISECONDS
|
||||||
);
|
);
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void updateRightPaddle(GeyserSession session, EntityMetadata entityMetadata) {
|
private void updateRightPaddle(GeyserSession session, Entity rower) {
|
||||||
if (isPaddlingRight) {
|
if (isPaddlingRight) {
|
||||||
paddleTimeRight += ROWING_SPEED;
|
paddleTimeRight += ROWING_SPEED;
|
||||||
metadata.put(EntityData.ROW_TIME_RIGHT, paddleTimeRight);
|
sendAnimationPacket(session, rower, AnimatePacket.Action.ROW_RIGHT, paddleTimeRight);
|
||||||
super.updateBedrockMetadata(entityMetadata, session);
|
|
||||||
session.getConnector().getGeneralThreadPool().schedule(() ->
|
session.getConnector().getGeneralThreadPool().schedule(() ->
|
||||||
updateRightPaddle(session, entityMetadata),
|
updateRightPaddle(session, rower),
|
||||||
100,
|
100,
|
||||||
TimeUnit.MILLISECONDS
|
TimeUnit.MILLISECONDS
|
||||||
);
|
);
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendAnimationPacket(GeyserSession session, Entity rower, AnimatePacket.Action action, float rowTime) {
|
||||||
|
AnimatePacket packet = new AnimatePacket();
|
||||||
|
packet.setRuntimeEntityId(rower.getGeyserId());
|
||||||
|
packet.setAction(action);
|
||||||
|
packet.setRowingTime(rowTime);
|
||||||
|
session.sendUpstreamPacket(packet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,6 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
|||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
|
||||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||||
|
|
||||||
public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity {
|
public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity {
|
||||||
@ -60,8 +59,8 @@ public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity {
|
|||||||
* By default, the command block shown is purple on Bedrock, which does not match Java Edition's orange.
|
* By default, the command block shown is purple on Bedrock, which does not match Java Edition's orange.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void updateDefaultBlockMetadata() {
|
public void updateDefaultBlockMetadata(GeyserSession session) {
|
||||||
metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.BEDROCK_RUNTIME_COMMAND_BLOCK_ID);
|
metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockRuntimeCommandBlockId());
|
||||||
metadata.put(EntityData.DISPLAY_OFFSET, 6);
|
metadata.put(EntityData.DISPLAY_OFFSET, 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ import com.nukkitx.math.vector.Vector3f;
|
|||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is used as a base for minecarts with a default block to display like furnaces and spawners
|
* This class is used as a base for minecarts with a default block to display like furnaces and spawners
|
||||||
@ -44,10 +43,15 @@ public class DefaultBlockMinecartEntity extends MinecartEntity {
|
|||||||
public DefaultBlockMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
public DefaultBlockMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||||
|
|
||||||
updateDefaultBlockMetadata();
|
|
||||||
metadata.put(EntityData.CUSTOM_DISPLAY, (byte) 1);
|
metadata.put(EntityData.CUSTOM_DISPLAY, (byte) 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void spawnEntity(GeyserSession session) {
|
||||||
|
updateDefaultBlockMetadata(session);
|
||||||
|
super.spawnEntity(session);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||||
|
|
||||||
@ -56,7 +60,7 @@ public class DefaultBlockMinecartEntity extends MinecartEntity {
|
|||||||
customBlock = (int) entityMetadata.getValue();
|
customBlock = (int) entityMetadata.getValue();
|
||||||
|
|
||||||
if (showCustomBlock) {
|
if (showCustomBlock) {
|
||||||
metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(customBlock));
|
metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(customBlock));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,16 +77,16 @@ public class DefaultBlockMinecartEntity extends MinecartEntity {
|
|||||||
if (entityMetadata.getId() == 12) {
|
if (entityMetadata.getId() == 12) {
|
||||||
if ((boolean) entityMetadata.getValue()) {
|
if ((boolean) entityMetadata.getValue()) {
|
||||||
showCustomBlock = true;
|
showCustomBlock = true;
|
||||||
metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(customBlock));
|
metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(customBlock));
|
||||||
metadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset);
|
metadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset);
|
||||||
} else {
|
} else {
|
||||||
showCustomBlock = false;
|
showCustomBlock = false;
|
||||||
updateDefaultBlockMetadata();
|
updateDefaultBlockMetadata(session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.updateBedrockMetadata(entityMetadata, session);
|
super.updateBedrockMetadata(entityMetadata, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateDefaultBlockMetadata() { }
|
public void updateDefaultBlockMetadata(GeyserSession session) { }
|
||||||
}
|
}
|
||||||
|
@ -28,12 +28,6 @@ package org.geysermc.connector.entity;
|
|||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
|
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket;
|
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket;
|
|
||||||
import com.nukkitx.math.vector.Vector3f;
|
import com.nukkitx.math.vector.Vector3f;
|
||||||
import com.nukkitx.protocol.bedrock.data.AttributeData;
|
import com.nukkitx.protocol.bedrock.data.AttributeData;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
@ -51,9 +45,8 @@ import org.geysermc.connector.entity.living.ArmorStandEntity;
|
|||||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
|
||||||
import org.geysermc.connector.utils.AttributeUtils;
|
|
||||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||||
|
import org.geysermc.connector.utils.AttributeUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -273,35 +266,9 @@ public class Entity {
|
|||||||
metadata.getFlags().setFlag(EntityFlag.SWIMMING, ((xd & 0x10) == 0x10) && metadata.getFlags().getFlag(EntityFlag.SPRINTING)); // Otherwise swimming is enabled on older servers
|
metadata.getFlags().setFlag(EntityFlag.SWIMMING, ((xd & 0x10) == 0x10) && metadata.getFlags().getFlag(EntityFlag.SPRINTING)); // Otherwise swimming is enabled on older servers
|
||||||
metadata.getFlags().setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80);
|
metadata.getFlags().setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80);
|
||||||
|
|
||||||
if ((xd & 0x20) == 0x20) {
|
|
||||||
// Armour stands are handled in their own class
|
// Armour stands are handled in their own class
|
||||||
if (!this.is(ArmorStandEntity.class)) {
|
if (!this.is(ArmorStandEntity.class)) {
|
||||||
metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true);
|
metadata.getFlags().setFlag(EntityFlag.INVISIBLE, (xd & 0x20) == 0x20);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
metadata.getFlags().setFlag(EntityFlag.INVISIBLE, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shield code
|
|
||||||
if (session.getPlayerEntity().getEntityId() == entityId && metadata.getFlags().getFlag(EntityFlag.SNEAKING)) {
|
|
||||||
if ((session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() == ItemRegistry.SHIELD.getJavaId()) ||
|
|
||||||
(session.getInventoryCache().getPlayerInventory().getItem(45) != null && session.getInventoryCache().getPlayerInventory().getItem(45).getId() == ItemRegistry.SHIELD.getJavaId())) {
|
|
||||||
ClientPlayerUseItemPacket useItemPacket;
|
|
||||||
metadata.getFlags().setFlag(EntityFlag.BLOCKING, true);
|
|
||||||
if (session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() == ItemRegistry.SHIELD.getJavaId()) {
|
|
||||||
useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
|
|
||||||
}
|
|
||||||
// Else we just assume it's the offhand, to simplify logic and to assure the packet gets sent
|
|
||||||
else {
|
|
||||||
useItemPacket = new ClientPlayerUseItemPacket(Hand.OFF_HAND);
|
|
||||||
}
|
|
||||||
session.sendDownstreamPacket(useItemPacket);
|
|
||||||
}
|
|
||||||
} else if (session.getPlayerEntity().getEntityId() == entityId && !metadata.getFlags().getFlag(EntityFlag.SNEAKING) && metadata.getFlags().getFlag(EntityFlag.BLOCKING)) {
|
|
||||||
metadata.getFlags().setFlag(EntityFlag.BLOCKING, false);
|
|
||||||
metadata.getFlags().setFlag(EntityFlag.IS_AVOIDING_BLOCK, true);
|
|
||||||
ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, new Position(0, 0, 0), BlockFace.DOWN);
|
|
||||||
session.sendDownstreamPacket(releaseItemPacket);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -333,15 +300,12 @@ public class Entity {
|
|||||||
case 6: // Pose change
|
case 6: // Pose change
|
||||||
if (entityMetadata.getValue().equals(Pose.SLEEPING)) {
|
if (entityMetadata.getValue().equals(Pose.SLEEPING)) {
|
||||||
metadata.getFlags().setFlag(EntityFlag.SLEEPING, true);
|
metadata.getFlags().setFlag(EntityFlag.SLEEPING, true);
|
||||||
// Has to be a byte or it does not work
|
|
||||||
metadata.put(EntityData.PLAYER_FLAGS, (byte) 2);
|
|
||||||
metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.2f);
|
metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.2f);
|
||||||
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.2f);
|
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.2f);
|
||||||
} else if (metadata.getFlags().getFlag(EntityFlag.SLEEPING)) {
|
} else if (metadata.getFlags().getFlag(EntityFlag.SLEEPING)) {
|
||||||
metadata.getFlags().setFlag(EntityFlag.SLEEPING, false);
|
metadata.getFlags().setFlag(EntityFlag.SLEEPING, false);
|
||||||
metadata.put(EntityData.BOUNDING_BOX_WIDTH, getEntityType().getWidth());
|
metadata.put(EntityData.BOUNDING_BOX_WIDTH, getEntityType().getWidth());
|
||||||
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, getEntityType().getHeight());
|
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, getEntityType().getHeight());
|
||||||
metadata.put(EntityData.PLAYER_FLAGS, (byte) 0);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -31,14 +31,19 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
|||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
|
||||||
|
|
||||||
public class FallingBlockEntity extends Entity {
|
public class FallingBlockEntity extends Entity {
|
||||||
|
private final int javaId;
|
||||||
|
|
||||||
public FallingBlockEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, int javaId) {
|
public FallingBlockEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, int javaId) {
|
||||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||||
|
this.javaId = javaId;
|
||||||
|
}
|
||||||
|
|
||||||
this.metadata.put(EntityData.VARIANT, BlockTranslator.getBedrockBlockId(javaId));
|
@Override
|
||||||
|
public void spawnEntity(GeyserSession session) {
|
||||||
|
this.metadata.put(EntityData.VARIANT, session.getBlockTranslator().getBedrockBlockId(javaId));
|
||||||
|
super.spawnEntity(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -26,43 +26,163 @@
|
|||||||
package org.geysermc.connector.entity;
|
package org.geysermc.connector.entity;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.object.ProjectileData;
|
|
||||||
import com.nukkitx.math.vector.Vector3f;
|
import com.nukkitx.math.vector.Vector3f;
|
||||||
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.PlaySoundPacket;
|
||||||
|
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
import org.geysermc.connector.network.translators.collision.BoundingBox;
|
||||||
|
import org.geysermc.connector.network.translators.collision.CollisionManager;
|
||||||
|
import org.geysermc.connector.network.translators.collision.CollisionTranslator;
|
||||||
|
import org.geysermc.connector.network.translators.collision.translators.BlockCollision;
|
||||||
|
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
|
||||||
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
|
|
||||||
public class FishingHookEntity extends Entity {
|
import java.util.List;
|
||||||
public FishingHookEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, ProjectileData data) {
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
|
public class FishingHookEntity extends ThrowableEntity {
|
||||||
|
|
||||||
|
private boolean hooked = false;
|
||||||
|
|
||||||
|
private final BoundingBox boundingBox;
|
||||||
|
|
||||||
|
private boolean inWater = false;
|
||||||
|
|
||||||
|
public FishingHookEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, PlayerEntity owner) {
|
||||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||||
|
|
||||||
for (GeyserSession session : GeyserConnector.getInstance().getPlayers()) {
|
this.boundingBox = new BoundingBox(0.125, 0.125, 0.125, 0.25, 0.25, 0.25);
|
||||||
Entity entity = session.getEntityCache().getEntityByJavaId(data.getOwnerId());
|
|
||||||
if (entity == null && session.getPlayerEntity().getEntityId() == data.getOwnerId()) {
|
|
||||||
entity = session.getPlayerEntity();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity != null) {
|
// In Java, the splash sound depends on the entity's velocity, but in Bedrock the volume doesn't change.
|
||||||
this.metadata.put(EntityData.OWNER_EID, entity.getGeyserId());
|
// This splash can be confused with the sound from catching a fish. This silences the splash from Bedrock,
|
||||||
return;
|
// so that it can be handled by moveAbsoluteImmediate.
|
||||||
}
|
this.metadata.putFloat(EntityData.BOUNDING_BOX_HEIGHT, 128);
|
||||||
}
|
|
||||||
|
this.metadata.put(EntityData.OWNER_EID, owner.getGeyserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||||
if (entityMetadata.getId() == 7) {
|
if (entityMetadata.getId() == 7) { // Hooked entity
|
||||||
Entity entity = session.getEntityCache().getEntityByJavaId((Integer) entityMetadata.getValue() - 1);
|
int hookedEntityId = (int) entityMetadata.getValue() - 1;
|
||||||
if (entity == null && session.getPlayerEntity().getEntityId() == (Integer) entityMetadata.getValue() - 1) {
|
Entity entity = session.getEntityCache().getEntityByJavaId(hookedEntityId);
|
||||||
|
if (entity == null && session.getPlayerEntity().getEntityId() == hookedEntityId) {
|
||||||
entity = session.getPlayerEntity();
|
entity = session.getPlayerEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
metadata.put(EntityData.TARGET_EID, entity.getGeyserId());
|
metadata.put(EntityData.TARGET_EID, entity.getGeyserId());
|
||||||
|
hooked = true;
|
||||||
|
} else {
|
||||||
|
hooked = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.updateBedrockMetadata(entityMetadata, session);
|
super.updateBedrockMetadata(entityMetadata, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void moveAbsoluteImmediate(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
|
||||||
|
boundingBox.setMiddleX(position.getX());
|
||||||
|
boundingBox.setMiddleY(position.getY() + boundingBox.getSizeY() / 2);
|
||||||
|
boundingBox.setMiddleZ(position.getZ());
|
||||||
|
|
||||||
|
CollisionManager collisionManager = session.getCollisionManager();
|
||||||
|
List<Vector3i> collidableBlocks = collisionManager.getCollidableBlocks(boundingBox);
|
||||||
|
boolean touchingWater = false;
|
||||||
|
boolean collided = false;
|
||||||
|
for (Vector3i blockPos : collidableBlocks) {
|
||||||
|
int blockID = session.getConnector().getWorldManager().getBlockAt(session, blockPos);
|
||||||
|
BlockCollision blockCollision = CollisionTranslator.getCollision(blockID, blockPos.getX(), blockPos.getY(), blockPos.getZ());
|
||||||
|
if (blockCollision != null && blockCollision.checkIntersection(boundingBox)) {
|
||||||
|
// TODO Push bounding box out of collision to improve movement
|
||||||
|
collided = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int waterLevel = BlockStateValues.getWaterLevel(blockID);
|
||||||
|
if (BlockTranslator.isWaterlogged(blockID)) {
|
||||||
|
waterLevel = 0;
|
||||||
|
}
|
||||||
|
if (waterLevel >= 0) {
|
||||||
|
double waterMaxY = blockPos.getY() + 1 - (waterLevel + 1) / 9.0;
|
||||||
|
// Falling water is a full block
|
||||||
|
if (waterLevel >= 8) {
|
||||||
|
waterMaxY = blockPos.getY() + 1;
|
||||||
|
}
|
||||||
|
if (position.getY() <= waterMaxY) {
|
||||||
|
touchingWater = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inWater && touchingWater) {
|
||||||
|
sendSplashSound(session);
|
||||||
|
}
|
||||||
|
inWater = touchingWater;
|
||||||
|
|
||||||
|
if (!collided) {
|
||||||
|
super.moveAbsoluteImmediate(session, position, rotation, isOnGround, teleported);
|
||||||
|
} else {
|
||||||
|
super.moveAbsoluteImmediate(session, this.position, rotation, true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendSplashSound(GeyserSession session) {
|
||||||
|
if (!metadata.getFlags().getFlag(EntityFlag.SILENT)) {
|
||||||
|
float volume = (float) (0.2f * Math.sqrt(0.2 * (motion.getX() * motion.getX() + motion.getZ() * motion.getZ()) + motion.getY() * motion.getY()));
|
||||||
|
if (volume > 1) {
|
||||||
|
volume = 1;
|
||||||
|
}
|
||||||
|
PlaySoundPacket playSoundPacket = new PlaySoundPacket();
|
||||||
|
playSoundPacket.setSound("random.splash");
|
||||||
|
playSoundPacket.setPosition(position);
|
||||||
|
playSoundPacket.setVolume(volume);
|
||||||
|
playSoundPacket.setPitch(1f + ThreadLocalRandom.current().nextFloat() * 0.3f);
|
||||||
|
session.sendUpstreamPacket(playSoundPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick(GeyserSession session) {
|
||||||
|
if (hooked || !isInAir(session) && !isInWater(session) || isOnGround()) {
|
||||||
|
motion = Vector3f.ZERO;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float gravity = getGravity(session);
|
||||||
|
motion = motion.down(gravity);
|
||||||
|
|
||||||
|
moveAbsoluteImmediate(session, position.add(motion), rotation, onGround, false);
|
||||||
|
|
||||||
|
float drag = getDrag(session);
|
||||||
|
motion = motion.mul(drag);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float getGravity(GeyserSession session) {
|
||||||
|
if (!isInWater(session) && !onGround) {
|
||||||
|
return 0.03f;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param session the session of the Bedrock client.
|
||||||
|
* @return true if this entity is currently in air.
|
||||||
|
*/
|
||||||
|
protected boolean isInAir(GeyserSession session) {
|
||||||
|
if (session.getConnector().getConfig().isCacheChunks()) {
|
||||||
|
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
|
||||||
|
return block == BlockTranslator.JAVA_AIR_ID;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float getDrag(GeyserSession session) {
|
||||||
|
return 0.92f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,15 +44,15 @@ public class FurnaceMinecartEntity extends DefaultBlockMinecartEntity {
|
|||||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||||
if (entityMetadata.getId() == 13 && !showCustomBlock) {
|
if (entityMetadata.getId() == 13 && !showCustomBlock) {
|
||||||
hasFuel = (boolean) entityMetadata.getValue();
|
hasFuel = (boolean) entityMetadata.getValue();
|
||||||
updateDefaultBlockMetadata();
|
updateDefaultBlockMetadata(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
super.updateBedrockMetadata(entityMetadata, session);
|
super.updateBedrockMetadata(entityMetadata, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateDefaultBlockMetadata() {
|
public void updateDefaultBlockMetadata(GeyserSession session) {
|
||||||
metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(hasFuel ? BlockTranslator.JAVA_RUNTIME_FURNACE_LIT_ID : BlockTranslator.JAVA_RUNTIME_FURNACE_ID));
|
metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(hasFuel ? BlockTranslator.JAVA_RUNTIME_FURNACE_LIT_ID : BlockTranslator.JAVA_RUNTIME_FURNACE_ID));
|
||||||
metadata.put(EntityData.DISPLAY_OFFSET, 6);
|
metadata.put(EntityData.DISPLAY_OFFSET, 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,6 @@ import org.geysermc.connector.network.session.GeyserSession;
|
|||||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@ -49,15 +48,19 @@ import java.util.concurrent.TimeUnit;
|
|||||||
*/
|
*/
|
||||||
public class ItemFrameEntity extends Entity {
|
public class ItemFrameEntity extends Entity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to construct the block entity tag on spawning.
|
||||||
|
*/
|
||||||
|
private final HangingDirection direction;
|
||||||
/**
|
/**
|
||||||
* Used for getting the Bedrock block position.
|
* Used for getting the Bedrock block position.
|
||||||
* Blocks deal with integers whereas entities deal with floats.
|
* Blocks deal with integers whereas entities deal with floats.
|
||||||
*/
|
*/
|
||||||
private final Vector3i bedrockPosition;
|
private Vector3i bedrockPosition;
|
||||||
/**
|
/**
|
||||||
* Specific block 'state' we are emulating in Bedrock.
|
* Specific block 'state' we are emulating in Bedrock.
|
||||||
*/
|
*/
|
||||||
private final int bedrockRuntimeId;
|
private int bedrockRuntimeId;
|
||||||
/**
|
/**
|
||||||
* Rotation of item in frame.
|
* Rotation of item in frame.
|
||||||
*/
|
*/
|
||||||
@ -69,19 +72,21 @@ public class ItemFrameEntity extends Entity {
|
|||||||
|
|
||||||
public ItemFrameEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, HangingDirection direction) {
|
public ItemFrameEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, HangingDirection direction) {
|
||||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||||
NbtMapBuilder blockBuilder = NbtMap.builder()
|
this.direction = direction;
|
||||||
.putString("name", "minecraft:frame")
|
|
||||||
.putInt("version", BlockTranslator.getBlockStateVersion());
|
|
||||||
blockBuilder.put("states", NbtMap.builder()
|
|
||||||
.putInt("facing_direction", direction.ordinal())
|
|
||||||
.putByte("item_frame_map_bit", (byte) 0)
|
|
||||||
.build());
|
|
||||||
bedrockRuntimeId = BlockTranslator.getItemFrame(blockBuilder.build());
|
|
||||||
bedrockPosition = Vector3i.from(position.getFloorX(), position.getFloorY(), position.getFloorZ());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void spawnEntity(GeyserSession session) {
|
public void spawnEntity(GeyserSession session) {
|
||||||
|
NbtMapBuilder blockBuilder = NbtMap.builder()
|
||||||
|
.putString("name", "minecraft:frame")
|
||||||
|
.putInt("version", session.getBlockTranslator().getBlockStateVersion());
|
||||||
|
blockBuilder.put("states", NbtMap.builder()
|
||||||
|
.putInt("facing_direction", direction.ordinal())
|
||||||
|
.putByte("item_frame_map_bit", (byte) 0)
|
||||||
|
.build());
|
||||||
|
bedrockRuntimeId = session.getBlockTranslator().getItemFrame(blockBuilder.build());
|
||||||
|
bedrockPosition = Vector3i.from(position.getFloorX(), position.getFloorY(), position.getFloorZ());
|
||||||
|
|
||||||
session.getItemFrameCache().put(bedrockPosition, entityId);
|
session.getItemFrameCache().put(bedrockPosition, entityId);
|
||||||
// Delay is required, or else loading in frames on chunk load is sketchy at best
|
// Delay is required, or else loading in frames on chunk load is sketchy at best
|
||||||
session.getConnector().getGeneralThreadPool().schedule(() -> {
|
session.getConnector().getGeneralThreadPool().schedule(() -> {
|
||||||
@ -136,7 +141,7 @@ public class ItemFrameEntity extends Entity {
|
|||||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||||
updateBlockPacket.setDataLayer(0);
|
updateBlockPacket.setDataLayer(0);
|
||||||
updateBlockPacket.setBlockPosition(bedrockPosition);
|
updateBlockPacket.setBlockPosition(bedrockPosition);
|
||||||
updateBlockPacket.setRuntimeId(BlockTranslator.BEDROCK_AIR_ID);
|
updateBlockPacket.setRuntimeId(session.getBlockTranslator().getBedrockAirId());
|
||||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY);
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY);
|
||||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
|
||||||
|
@ -32,19 +32,44 @@ import org.geysermc.connector.network.session.GeyserSession;
|
|||||||
public class ItemedFireballEntity extends ThrowableEntity {
|
public class ItemedFireballEntity extends ThrowableEntity {
|
||||||
private final Vector3f acceleration;
|
private final Vector3f acceleration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of ticks to advance movement before sending to Bedrock
|
||||||
|
*/
|
||||||
|
protected int futureTicks = 3;
|
||||||
|
|
||||||
public ItemedFireballEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
public ItemedFireballEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||||
super(entityId, geyserId, entityType, position, Vector3f.ZERO, rotation);
|
super(entityId, geyserId, entityType, position, Vector3f.ZERO, rotation);
|
||||||
acceleration = motion;
|
|
||||||
|
float magnitude = motion.length();
|
||||||
|
if (magnitude != 0) {
|
||||||
|
acceleration = motion.div(magnitude).mul(0.1f);
|
||||||
|
} else {
|
||||||
|
acceleration = Vector3f.ZERO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector3f tickMovement(GeyserSession session, Vector3f position) {
|
||||||
|
position = position.add(motion);
|
||||||
|
float drag = getDrag(session);
|
||||||
|
motion = motion.add(acceleration).mul(drag);
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void moveAbsoluteImmediate(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
|
||||||
|
// Advance the position by a few ticks before sending it to Bedrock
|
||||||
|
Vector3f lastMotion = motion;
|
||||||
|
Vector3f newPosition = position;
|
||||||
|
for (int i = 0; i < futureTicks; i++) {
|
||||||
|
newPosition = tickMovement(session, newPosition);
|
||||||
|
}
|
||||||
|
super.moveAbsoluteImmediate(session, newPosition, rotation, isOnGround, teleported);
|
||||||
|
this.position = position;
|
||||||
|
this.motion = lastMotion;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void tick(GeyserSession session) {
|
public void tick(GeyserSession session) {
|
||||||
position = position.add(motion);
|
moveAbsoluteImmediate(session, tickMovement(session, position), rotation, false, false);
|
||||||
// TODO: While this reduces latency in position updating (needed for better fireball reflecting),
|
|
||||||
// TODO: movement is incredibly stiff.
|
|
||||||
// TODO: Only use this laggy movement for fireballs that be reflected
|
|
||||||
moveAbsoluteImmediate(session, position, rotation, false, true);
|
|
||||||
float drag = getDrag(session);
|
|
||||||
motion = motion.add(acceleration).mul(drag);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,13 @@ public class LivingEntity extends Entity {
|
|||||||
// Bed has to be updated, or else player is floating in the air
|
// Bed has to be updated, or else player is floating in the air
|
||||||
ChunkUtils.updateBlock(session, bed, bedPosition);
|
ChunkUtils.updateBlock(session, bed, bedPosition);
|
||||||
}
|
}
|
||||||
|
// Indicate that the player should enter the sleep cycle
|
||||||
|
// Has to be a byte or it does not work
|
||||||
|
// (Bed position is what actually triggers sleep - "pose" is only optional)
|
||||||
|
metadata.put(EntityData.PLAYER_FLAGS, (byte) 2);
|
||||||
|
} else {
|
||||||
|
// Player is no longer sleeping
|
||||||
|
metadata.put(EntityData.PLAYER_FLAGS, (byte) 0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ import com.nukkitx.math.vector.Vector3f;
|
|||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
|
||||||
|
|
||||||
public class MinecartEntity extends Entity {
|
public class MinecartEntity extends Entity {
|
||||||
|
|
||||||
@ -58,7 +57,7 @@ public class MinecartEntity extends Entity {
|
|||||||
if (!(this instanceof DefaultBlockMinecartEntity)) { // Handled in the DefaultBlockMinecartEntity class
|
if (!(this instanceof DefaultBlockMinecartEntity)) { // Handled in the DefaultBlockMinecartEntity class
|
||||||
// Custom block
|
// Custom block
|
||||||
if (entityMetadata.getId() == 10) {
|
if (entityMetadata.getId() == 10) {
|
||||||
metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId((int) entityMetadata.getValue()));
|
metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId((int) entityMetadata.getValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom block offset
|
// Custom block offset
|
||||||
|
@ -28,6 +28,7 @@ package org.geysermc.connector.entity;
|
|||||||
import com.nukkitx.math.vector.Vector3f;
|
import com.nukkitx.math.vector.Vector3f;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
|
|
||||||
public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity {
|
public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity {
|
||||||
@ -37,8 +38,8 @@ public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateDefaultBlockMetadata() {
|
public void updateDefaultBlockMetadata(GeyserSession session) {
|
||||||
metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(BlockTranslator.JAVA_RUNTIME_SPAWNER_ID));
|
metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(BlockTranslator.JAVA_RUNTIME_SPAWNER_ID));
|
||||||
metadata.put(EntityData.DISPLAY_OFFSET, 6);
|
metadata.put(EntityData.DISPLAY_OFFSET, 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,20 +29,21 @@ import com.nukkitx.math.vector.Vector3f;
|
|||||||
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||||
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
|
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.MoveEntityDeltaPacket;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used as a class for any object-like entity that moves as a projectile
|
* Used as a class for any object-like entity that moves as a projectile
|
||||||
*/
|
*/
|
||||||
public class ThrowableEntity extends Entity implements Tickable {
|
public class ThrowableEntity extends Entity implements Tickable {
|
||||||
|
|
||||||
private Vector3f lastPosition;
|
protected Vector3f lastJavaPosition;
|
||||||
|
|
||||||
public ThrowableEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
public ThrowableEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||||
this.lastPosition = position;
|
this.lastJavaPosition = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,22 +53,65 @@ public class ThrowableEntity extends Entity implements Tickable {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void tick(GeyserSession session) {
|
public void tick(GeyserSession session) {
|
||||||
super.moveRelative(session, motion.getX(), motion.getY(), motion.getZ(), rotation, onGround);
|
moveAbsoluteImmediate(session, position.add(motion), rotation, onGround, false);
|
||||||
float drag = getDrag(session);
|
float drag = getDrag(session);
|
||||||
float gravity = getGravity();
|
float gravity = getGravity(session);
|
||||||
motion = motion.mul(drag).down(gravity);
|
motion = motion.mul(drag).down(gravity);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void moveAbsoluteImmediate(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
|
protected void moveAbsoluteImmediate(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
|
||||||
super.moveAbsolute(session, position, rotation, isOnGround, teleported);
|
MoveEntityDeltaPacket moveEntityDeltaPacket = new MoveEntityDeltaPacket();
|
||||||
|
moveEntityDeltaPacket.setRuntimeEntityId(geyserId);
|
||||||
|
|
||||||
|
if (isOnGround) {
|
||||||
|
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.ON_GROUND);
|
||||||
|
}
|
||||||
|
setOnGround(isOnGround);
|
||||||
|
|
||||||
|
if (teleported) {
|
||||||
|
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.TELEPORTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.position.getX() != position.getX()) {
|
||||||
|
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_X);
|
||||||
|
moveEntityDeltaPacket.setX(position.getX());
|
||||||
|
}
|
||||||
|
if (this.position.getY() != position.getY()) {
|
||||||
|
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Y);
|
||||||
|
moveEntityDeltaPacket.setY(position.getY());
|
||||||
|
}
|
||||||
|
if (this.position.getZ() != position.getZ()) {
|
||||||
|
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Z);
|
||||||
|
moveEntityDeltaPacket.setZ(position.getZ());
|
||||||
|
}
|
||||||
|
setPosition(position);
|
||||||
|
|
||||||
|
if (this.rotation.getX() != rotation.getX()) {
|
||||||
|
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_YAW);
|
||||||
|
moveEntityDeltaPacket.setYaw(rotation.getX());
|
||||||
|
}
|
||||||
|
if (this.rotation.getY() != rotation.getY()) {
|
||||||
|
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH);
|
||||||
|
moveEntityDeltaPacket.setPitch(rotation.getY());
|
||||||
|
}
|
||||||
|
if (this.rotation.getZ() != rotation.getZ()) {
|
||||||
|
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_HEAD_YAW);
|
||||||
|
moveEntityDeltaPacket.setHeadYaw(rotation.getZ());
|
||||||
|
}
|
||||||
|
setRotation(rotation);
|
||||||
|
|
||||||
|
if (!moveEntityDeltaPacket.getFlags().isEmpty()) {
|
||||||
|
session.sendUpstreamPacket(moveEntityDeltaPacket);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the gravity of this entity type. Used for applying gravity while the entity is in motion.
|
* Get the gravity of this entity type. Used for applying gravity while the entity is in motion.
|
||||||
*
|
*
|
||||||
|
* @param session the session of the Bedrock client.
|
||||||
* @return the amount of gravity to apply to this entity while in motion.
|
* @return the amount of gravity to apply to this entity while in motion.
|
||||||
*/
|
*/
|
||||||
protected float getGravity() {
|
protected float getGravity(GeyserSession session) {
|
||||||
if (metadata.getFlags().getFlag(EntityFlag.HAS_GRAVITY)) {
|
if (metadata.getFlags().getFlag(EntityFlag.HAS_GRAVITY)) {
|
||||||
switch (entityType) {
|
switch (entityType) {
|
||||||
case THROWN_POTION:
|
case THROWN_POTION:
|
||||||
@ -76,11 +120,14 @@ public class ThrowableEntity extends Entity implements Tickable {
|
|||||||
case THROWN_EXP_BOTTLE:
|
case THROWN_EXP_BOTTLE:
|
||||||
return 0.07f;
|
return 0.07f;
|
||||||
case FIREBALL:
|
case FIREBALL:
|
||||||
|
case SHULKER_BULLET:
|
||||||
return 0;
|
return 0;
|
||||||
case SNOWBALL:
|
case SNOWBALL:
|
||||||
case THROWN_EGG:
|
case THROWN_EGG:
|
||||||
case THROWN_ENDERPEARL:
|
case THROWN_ENDERPEARL:
|
||||||
return 0.03f;
|
return 0.03f;
|
||||||
|
case LLAMA_SPIT:
|
||||||
|
return 0.06f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -101,11 +148,14 @@ public class ThrowableEntity extends Entity implements Tickable {
|
|||||||
case SNOWBALL:
|
case SNOWBALL:
|
||||||
case THROWN_EGG:
|
case THROWN_EGG:
|
||||||
case THROWN_ENDERPEARL:
|
case THROWN_ENDERPEARL:
|
||||||
|
case LLAMA_SPIT:
|
||||||
return 0.99f;
|
return 0.99f;
|
||||||
case FIREBALL:
|
case FIREBALL:
|
||||||
case SMALL_FIREBALL:
|
case SMALL_FIREBALL:
|
||||||
case DRAGON_FIREBALL:
|
case DRAGON_FIREBALL:
|
||||||
return 0.95f;
|
return 0.95f;
|
||||||
|
case SHULKER_BULLET:
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
@ -117,8 +167,10 @@ public class ThrowableEntity extends Entity implements Tickable {
|
|||||||
*/
|
*/
|
||||||
protected boolean isInWater(GeyserSession session) {
|
protected boolean isInWater(GeyserSession session) {
|
||||||
if (session.getConnector().getConfig().isCacheChunks()) {
|
if (session.getConnector().getConfig().isCacheChunks()) {
|
||||||
|
if (0 <= position.getFloorY() && position.getFloorY() <= 255) {
|
||||||
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
|
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
|
||||||
return block == BlockTranslator.BEDROCK_WATER_ID;
|
return BlockStateValues.getWaterLevel(block) != -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -136,14 +188,13 @@ public class ThrowableEntity extends Entity implements Tickable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) {
|
public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) {
|
||||||
position = lastPosition;
|
moveAbsoluteImmediate(session, lastJavaPosition.add(relX, relY, relZ), rotation, isOnGround, false);
|
||||||
super.moveRelative(session, relX, relY, relZ, rotation, isOnGround);
|
lastJavaPosition = position;
|
||||||
lastPosition = position;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
|
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
|
||||||
super.moveAbsolute(session, position, rotation, isOnGround, teleported);
|
moveAbsoluteImmediate(session, position, rotation, isOnGround, teleported);
|
||||||
lastPosition = position;
|
lastJavaPosition = position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,8 @@ public class WitherSkullEntity extends ItemedFireballEntity {
|
|||||||
|
|
||||||
public WitherSkullEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
public WitherSkullEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||||
|
|
||||||
|
this.futureTicks = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -29,6 +29,9 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadat
|
|||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
|
||||||
import com.nukkitx.math.vector.Vector3f;
|
import com.nukkitx.math.vector.Vector3f;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.geysermc.connector.entity.LivingEntity;
|
import org.geysermc.connector.entity.LivingEntity;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
@ -42,15 +45,68 @@ public class ArmorStandEntity extends LivingEntity {
|
|||||||
private boolean isInvisible = false;
|
private boolean isInvisible = false;
|
||||||
private boolean isSmall = false;
|
private boolean isSmall = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On Java Edition, armor stands always show their name. Invisibility hides the name on Bedrock.
|
||||||
|
* By having a second entity, we can allow an invisible entity with the name tag.
|
||||||
|
* (This lets armor on armor stands still show)
|
||||||
|
*/
|
||||||
|
private ArmorStandEntity secondEntity = null;
|
||||||
|
/**
|
||||||
|
* Whether this is the primary armor stand that holds the armor and not the name tag.
|
||||||
|
*/
|
||||||
|
private boolean primaryEntity = true;
|
||||||
|
/**
|
||||||
|
* Whether the entity's position must be updated to included the offset.
|
||||||
|
*
|
||||||
|
* This should be true when the Java server marks the armor stand as invisible, but we shrink the entity
|
||||||
|
* to allow the nametag to appear. Basically:
|
||||||
|
* - Is visible: this is irrelevant (false)
|
||||||
|
* - Has armor, no name: false
|
||||||
|
* - Has armor, has name: false, with a second entity
|
||||||
|
* - No armor, no name: false
|
||||||
|
* - No armor, yes name: true
|
||||||
|
*/
|
||||||
|
private boolean positionRequiresOffset = false;
|
||||||
|
/**
|
||||||
|
* Whether we should update the position of this armor stand after metadata updates.
|
||||||
|
*/
|
||||||
|
private boolean positionUpdateRequired = false;
|
||||||
|
private GeyserSession session;
|
||||||
|
|
||||||
public ArmorStandEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
public ArmorStandEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void spawnEntity(GeyserSession session) {
|
||||||
|
this.session = session;
|
||||||
|
this.rotation = Vector3f.from(rotation.getX(), rotation.getX(), rotation.getX());
|
||||||
|
super.spawnEntity(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean despawnEntity(GeyserSession session) {
|
||||||
|
if (secondEntity != null) {
|
||||||
|
secondEntity.despawnEntity(session);
|
||||||
|
}
|
||||||
|
return super.despawnEntity(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) {
|
||||||
|
if (secondEntity != null) {
|
||||||
|
secondEntity.moveRelative(session, relX, relY, relZ, rotation, isOnGround);
|
||||||
|
}
|
||||||
|
super.moveRelative(session, relX, relY, relZ, rotation, isOnGround);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
|
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
|
||||||
|
if (secondEntity != null) {
|
||||||
|
secondEntity.moveAbsolute(session, applyOffsetToPosition(position), rotation, isOnGround, teleported);
|
||||||
|
} else if (positionRequiresOffset) {
|
||||||
// Fake the height to be above where it is so the nametag appears in the right location for invisible non-marker armour stands
|
// Fake the height to be above where it is so the nametag appears in the right location for invisible non-marker armour stands
|
||||||
if (!isMarker && isInvisible && passengers.isEmpty()) {
|
position = applyOffsetToPosition(position);
|
||||||
position = position.add(0d, entityType.getHeight() * (isSmall ? 0.55d : 1d), 0d);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super.moveAbsolute(session, position, Vector3f.from(rotation.getX(), rotation.getX(), rotation.getX()), isOnGround, teleported);
|
super.moveAbsolute(session, position, Vector3f.from(rotation.getX(), rotation.getX(), rotation.getX()), isOnGround, teleported);
|
||||||
@ -58,47 +114,250 @@ public class ArmorStandEntity extends LivingEntity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||||
|
super.updateBedrockMetadata(entityMetadata, session);
|
||||||
if (entityMetadata.getId() == 0 && entityMetadata.getType() == MetadataType.BYTE) {
|
if (entityMetadata.getId() == 0 && entityMetadata.getType() == MetadataType.BYTE) {
|
||||||
byte xd = (byte) entityMetadata.getValue();
|
byte xd = (byte) entityMetadata.getValue();
|
||||||
|
|
||||||
// Check if the armour stand is invisible and store accordingly
|
// Check if the armour stand is invisible and store accordingly
|
||||||
if ((xd & 0x20) == 0x20) {
|
if (primaryEntity) {
|
||||||
metadata.put(EntityData.SCALE, 0.0f);
|
isInvisible = (xd & 0x20) == 0x20;
|
||||||
isInvisible = true;
|
updateSecondEntityStatus(false);
|
||||||
}
|
}
|
||||||
|
} else if (entityMetadata.getId() == 2) {
|
||||||
|
updateSecondEntityStatus(false);
|
||||||
} else if (entityMetadata.getId() == 14 && entityMetadata.getType() == MetadataType.BYTE) {
|
} else if (entityMetadata.getId() == 14 && entityMetadata.getType() == MetadataType.BYTE) {
|
||||||
byte xd = (byte) entityMetadata.getValue();
|
byte xd = (byte) entityMetadata.getValue();
|
||||||
|
|
||||||
// isSmall
|
// isSmall
|
||||||
if ((xd & 0x01) == 0x01) {
|
boolean newIsSmall = (xd & 0x01) == 0x01;
|
||||||
isSmall = true;
|
if (newIsSmall != isSmall) {
|
||||||
|
if (positionRequiresOffset) {
|
||||||
if (metadata.getFloat(EntityData.SCALE) != 0.55f && metadata.getFloat(EntityData.SCALE) != 0.0f) {
|
// Fix new inconsistency with offset
|
||||||
metadata.put(EntityData.SCALE, 0.55f);
|
this.position = fixOffsetForSize(position, newIsSmall);
|
||||||
|
positionUpdateRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metadata.get(EntityData.BOUNDING_BOX_WIDTH) != null && metadata.get(EntityData.BOUNDING_BOX_WIDTH).equals(0.5f)) {
|
isSmall = newIsSmall;
|
||||||
metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.25f);
|
if (!isMarker) {
|
||||||
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.9875f);
|
toggleSmallStatus();
|
||||||
}
|
}
|
||||||
} else if (metadata.get(EntityData.BOUNDING_BOX_WIDTH) != null && metadata.get(EntityData.BOUNDING_BOX_WIDTH).equals(0.25f)) {
|
|
||||||
metadata.put(EntityData.BOUNDING_BOX_WIDTH, entityType.getWidth());
|
|
||||||
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, entityType.getHeight());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// setMarker
|
// setMarker
|
||||||
if ((xd & 0x10) == 0x10 && (metadata.get(EntityData.BOUNDING_BOX_WIDTH) == null || !metadata.get(EntityData.BOUNDING_BOX_WIDTH).equals(0.0f))) {
|
boolean oldIsMarker = isMarker;
|
||||||
|
isMarker = (xd & 0x10) == 0x10;
|
||||||
|
if (oldIsMarker != isMarker) {
|
||||||
|
if (isMarker) {
|
||||||
metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.0f);
|
metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.0f);
|
||||||
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f);
|
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f);
|
||||||
isMarker = true;
|
metadata.put(EntityData.SCALE, 0f);
|
||||||
|
} else {
|
||||||
|
toggleSmallStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSecondEntityStatus(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.updateBedrockMetadata(entityMetadata, session);
|
if (secondEntity != null) {
|
||||||
|
secondEntity.updateBedrockMetadata(entityMetadata, session);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void spawnEntity(GeyserSession session) {
|
public void updateBedrockMetadata(GeyserSession session) {
|
||||||
this.rotation = Vector3f.from(rotation.getX(), rotation.getX(), rotation.getX());
|
if (secondEntity != null) {
|
||||||
super.spawnEntity(session);
|
secondEntity.updateBedrockMetadata(session);
|
||||||
|
}
|
||||||
|
super.updateBedrockMetadata(session);
|
||||||
|
if (positionUpdateRequired) {
|
||||||
|
positionUpdateRequired = false;
|
||||||
|
updatePosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHelmet(ItemData helmet) {
|
||||||
|
super.setHelmet(helmet);
|
||||||
|
updateSecondEntityStatus(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setChestplate(ItemData chestplate) {
|
||||||
|
super.setChestplate(chestplate);
|
||||||
|
updateSecondEntityStatus(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLeggings(ItemData leggings) {
|
||||||
|
super.setLeggings(leggings);
|
||||||
|
updateSecondEntityStatus(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBoots(ItemData boots) {
|
||||||
|
super.setBoots(boots);
|
||||||
|
updateSecondEntityStatus(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHand(ItemData hand) {
|
||||||
|
super.setHand(hand);
|
||||||
|
updateSecondEntityStatus(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOffHand(ItemData offHand) {
|
||||||
|
super.setOffHand(offHand);
|
||||||
|
updateSecondEntityStatus(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if we need to load or unload the second entity.
|
||||||
|
*
|
||||||
|
* @param sendMetadata whether to send a metadata update after a change.
|
||||||
|
*/
|
||||||
|
private void updateSecondEntityStatus(boolean sendMetadata) {
|
||||||
|
// A secondary entity always has to have the offset applied, so it remains invisible and the nametag shows.
|
||||||
|
if (!primaryEntity) return;
|
||||||
|
if (!isInvisible || isMarker) {
|
||||||
|
// It is either impossible to show armor, or the armor stand isn't invisible. We good.
|
||||||
|
metadata.getFlags().setFlag(EntityFlag.INVISIBLE, false);
|
||||||
|
updateOffsetRequirement(false);
|
||||||
|
if (positionUpdateRequired) {
|
||||||
|
positionUpdateRequired = false;
|
||||||
|
updatePosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secondEntity != null) {
|
||||||
|
secondEntity.despawnEntity(session);
|
||||||
|
secondEntity = null;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//boolean isNametagEmpty = metadata.getString(EntityData.NAMETAG).isEmpty() || metadata.getByte(EntityData.NAMETAG_ALWAYS_SHOW, (byte) -1) == (byte) 0; - may not be necessary?
|
||||||
|
boolean isNametagEmpty = metadata.getString(EntityData.NAMETAG).isEmpty();
|
||||||
|
if (!isNametagEmpty && (!helmet.equals(ItemData.AIR) || !chestplate.equals(ItemData.AIR) || !leggings.equals(ItemData.AIR)
|
||||||
|
|| !boots.equals(ItemData.AIR) || !hand.equals(ItemData.AIR) || !offHand.equals(ItemData.AIR))) {
|
||||||
|
// If the second entity exists, no need to recreate it.
|
||||||
|
// We can't stuff this check above or else it'll fall into another else case and delete the second entity
|
||||||
|
if (secondEntity != null) return;
|
||||||
|
|
||||||
|
// Create the second entity. It doesn't need to worry about the items, but it does need to worry about
|
||||||
|
// the metadata as it will hold the name tag.
|
||||||
|
secondEntity = new ArmorStandEntity(0, session.getEntityCache().getNextEntityId().incrementAndGet(),
|
||||||
|
EntityType.ARMOR_STAND, position, motion, rotation);
|
||||||
|
secondEntity.primaryEntity = false;
|
||||||
|
if (!this.positionRequiresOffset) {
|
||||||
|
// Ensure the offset is applied for the 0 scale
|
||||||
|
secondEntity.position = secondEntity.applyOffsetToPosition(secondEntity.position);
|
||||||
|
}
|
||||||
|
// Copy metadata
|
||||||
|
secondEntity.isSmall = isSmall;
|
||||||
|
secondEntity.getMetadata().putAll(metadata);
|
||||||
|
// Copy the flags so they aren't the same object in memory
|
||||||
|
secondEntity.getMetadata().putFlags(metadata.getFlags().copy());
|
||||||
|
// Guarantee this copy is NOT invisible
|
||||||
|
secondEntity.getMetadata().getFlags().setFlag(EntityFlag.INVISIBLE, false);
|
||||||
|
// Scale to 0 to show nametag
|
||||||
|
secondEntity.getMetadata().put(EntityData.SCALE, 0.0f);
|
||||||
|
// No bounding box as we don't want to interact with this entity
|
||||||
|
secondEntity.getMetadata().put(EntityData.BOUNDING_BOX_WIDTH, 0.0f);
|
||||||
|
secondEntity.getMetadata().put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f);
|
||||||
|
secondEntity.spawnEntity(session);
|
||||||
|
|
||||||
|
// Reset scale of the proper armor stand
|
||||||
|
this.metadata.put(EntityData.SCALE, isSmall ? 0.55f : 1f);
|
||||||
|
// Set the proper armor stand to invisible to show armor
|
||||||
|
this.metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true);
|
||||||
|
// Update the position of the armor stand
|
||||||
|
updateOffsetRequirement(false);
|
||||||
|
} else if (isNametagEmpty) {
|
||||||
|
// We can just make an invisible entity
|
||||||
|
// Reset scale of the proper armor stand
|
||||||
|
metadata.put(EntityData.SCALE, isSmall ? 0.55f : 1f);
|
||||||
|
// Set the proper armor stand to invisible to show armor
|
||||||
|
metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true);
|
||||||
|
// Update offset
|
||||||
|
updateOffsetRequirement(false);
|
||||||
|
|
||||||
|
if (secondEntity != null) {
|
||||||
|
secondEntity.despawnEntity(session);
|
||||||
|
secondEntity = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Nametag is not empty and there is no armor
|
||||||
|
// We don't need to make a new entity
|
||||||
|
metadata.getFlags().setFlag(EntityFlag.INVISIBLE, false);
|
||||||
|
metadata.put(EntityData.SCALE, 0.0f);
|
||||||
|
// As the above is applied, we need an offset
|
||||||
|
updateOffsetRequirement(true);
|
||||||
|
|
||||||
|
if (secondEntity != null) {
|
||||||
|
secondEntity.despawnEntity(session);
|
||||||
|
secondEntity = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sendMetadata) {
|
||||||
|
this.updateBedrockMetadata(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this armor stand is not a marker, set its bounding box size and scale.
|
||||||
|
*/
|
||||||
|
private void toggleSmallStatus() {
|
||||||
|
metadata.put(EntityData.BOUNDING_BOX_WIDTH, isSmall ? 0.25f : entityType.getWidth());
|
||||||
|
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, isSmall ? 0.9875f : entityType.getHeight());
|
||||||
|
metadata.put(EntityData.SCALE, isSmall ? 0.55f : 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the selected position with the position offset applied.
|
||||||
|
*/
|
||||||
|
private Vector3f applyOffsetToPosition(Vector3f position) {
|
||||||
|
return position.add(0d, entityType.getHeight() * (isSmall ? 0.55d : 1d), 0d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return an adjusted offset for the new small status.
|
||||||
|
*/
|
||||||
|
private Vector3f fixOffsetForSize(Vector3f position, boolean isNowSmall) {
|
||||||
|
position = removeOffsetFromPosition(position);
|
||||||
|
return position.add(0d, entityType.getHeight() * (isNowSmall ? 0.55d : 1d), 0d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the selected position with the position offset removed.
|
||||||
|
*/
|
||||||
|
private Vector3f removeOffsetFromPosition(Vector3f position) {
|
||||||
|
return position.sub(0d, entityType.getHeight() * (isSmall ? 0.55d : 1d), 0d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the offset to a new value; if it changed, update the position, too.
|
||||||
|
*/
|
||||||
|
private void updateOffsetRequirement(boolean newValue) {
|
||||||
|
if (newValue != positionRequiresOffset) {
|
||||||
|
this.positionRequiresOffset = newValue;
|
||||||
|
if (positionRequiresOffset) {
|
||||||
|
this.position = applyOffsetToPosition(position);
|
||||||
|
} else {
|
||||||
|
this.position = removeOffsetFromPosition(position);
|
||||||
|
}
|
||||||
|
positionUpdateRequired = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates position without calling movement code.
|
||||||
|
*/
|
||||||
|
private void updatePosition() {
|
||||||
|
MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket();
|
||||||
|
moveEntityPacket.setRuntimeEntityId(geyserId);
|
||||||
|
moveEntityPacket.setPosition(position);
|
||||||
|
moveEntityPacket.setRotation(Vector3f.from(rotation.getX(), rotation.getX(), rotation.getX()));
|
||||||
|
moveEntityPacket.setOnGround(onGround);
|
||||||
|
moveEntityPacket.setTeleported(false);
|
||||||
|
session.sendUpstreamPacket(moveEntityPacket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import com.nukkitx.math.vector.Vector3f;
|
|||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
||||||
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
||||||
import org.geysermc.connector.entity.living.animal.AnimalEntity;
|
import org.geysermc.connector.entity.living.animal.AnimalEntity;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
@ -40,6 +41,9 @@ public class AbstractHorseEntity extends AnimalEntity {
|
|||||||
|
|
||||||
public AbstractHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
public AbstractHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||||
|
|
||||||
|
// Specifies the size of the entity's inventory. Required to place slots in the entity.
|
||||||
|
metadata.put(EntityData.CONTAINER_BASE_SIZE, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -75,6 +79,9 @@ public class AbstractHorseEntity extends AnimalEntity {
|
|||||||
entityEventPacket.setData(ItemRegistry.WHEAT.getBedrockId() << 16);
|
entityEventPacket.setData(ItemRegistry.WHEAT.getBedrockId() << 16);
|
||||||
session.sendUpstreamPacket(entityEventPacket);
|
session.sendUpstreamPacket(entityEventPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set container type if tamed
|
||||||
|
metadata.put(EntityData.CONTAINER_TYPE, ((xd & 0x02) == 0x02) ? (byte) ContainerType.HORSE.getId() : (byte) 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Needed to control horses
|
// Needed to control horses
|
||||||
|
@ -27,6 +27,7 @@ package org.geysermc.connector.entity.living.animal.horse;
|
|||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||||
import com.nukkitx.math.vector.Vector3f;
|
import com.nukkitx.math.vector.Vector3f;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
@ -35,6 +36,8 @@ public class ChestedHorseEntity extends AbstractHorseEntity {
|
|||||||
|
|
||||||
public ChestedHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
public ChestedHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||||
|
|
||||||
|
metadata.put(EntityData.CONTAINER_BASE_SIZE, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -38,6 +38,8 @@ public class LlamaEntity extends ChestedHorseEntity {
|
|||||||
|
|
||||||
public LlamaEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
public LlamaEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||||
|
|
||||||
|
metadata.put(EntityData.CONTAINER_STRENGTH_MODIFIER, 3); // Presumably 3 slots for every 1 strength
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
package org.geysermc.connector.entity.living.merchant;
|
package org.geysermc.connector.entity.living.merchant;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData;
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData;
|
||||||
import com.nukkitx.math.vector.Vector3f;
|
import com.nukkitx.math.vector.Vector3f;
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
@ -101,11 +100,17 @@ public class VillagerEntity extends AbstractMerchantEntity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) {
|
public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) {
|
||||||
|
if (!metadata.getFlags().getFlag(EntityFlag.SLEEPING)) {
|
||||||
|
// No need to worry about extra processing to compensate for sleeping
|
||||||
|
super.moveRelative(session, relX, relY, relZ, rotation, isOnGround);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int z = 0;
|
int z = 0;
|
||||||
int bedId = 0;
|
int bedId = 0;
|
||||||
float bedPositionSubtractorW = 0;
|
float bedPositionSubtractorW = 0;
|
||||||
float bedPositionSubtractorN = 0;
|
float bedPositionSubtractorN = 0;
|
||||||
Vector3i bedPosition = metadata.getPos(EntityData.BED_POSITION);
|
Vector3i bedPosition = metadata.getPos(EntityData.BED_POSITION, null);
|
||||||
if (session.getConnector().getConfig().isCacheChunks() && bedPosition != null) {
|
if (session.getConnector().getConfig().isCacheChunks() && bedPosition != null) {
|
||||||
bedId = session.getConnector().getWorldManager().getBlockAt(session, bedPosition);
|
bedId = session.getConnector().getWorldManager().getBlockAt(session, bedPosition);
|
||||||
}
|
}
|
||||||
@ -117,11 +122,6 @@ public class VillagerEntity extends AbstractMerchantEntity {
|
|||||||
MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket();
|
MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket();
|
||||||
moveEntityPacket.setRuntimeEntityId(geyserId);
|
moveEntityPacket.setRuntimeEntityId(geyserId);
|
||||||
//Sets Villager position and rotation when sleeping
|
//Sets Villager position and rotation when sleeping
|
||||||
if (!metadata.getFlags().getFlag(EntityFlag.SLEEPING)) {
|
|
||||||
moveEntityPacket.setPosition(position);
|
|
||||||
moveEntityPacket.setRotation(getBedrockRotation());
|
|
||||||
} else {
|
|
||||||
//String Setup
|
|
||||||
Pattern r = Pattern.compile("facing=([a-z]+)");
|
Pattern r = Pattern.compile("facing=([a-z]+)");
|
||||||
Matcher m = r.matcher(bedRotationZ);
|
Matcher m = r.matcher(bedRotationZ);
|
||||||
if (m.find()) {
|
if (m.find()) {
|
||||||
@ -149,7 +149,6 @@ public class VillagerEntity extends AbstractMerchantEntity {
|
|||||||
}
|
}
|
||||||
moveEntityPacket.setRotation(Vector3f.from(0, 0, z));
|
moveEntityPacket.setRotation(Vector3f.from(0, 0, z));
|
||||||
moveEntityPacket.setPosition(Vector3f.from(position.getX() + bedPositionSubtractorW, position.getY(), position.getZ() + bedPositionSubtractorN));
|
moveEntityPacket.setPosition(Vector3f.from(position.getX() + bedPositionSubtractorW, position.getY(), position.getZ() + bedPositionSubtractorN));
|
||||||
}
|
|
||||||
moveEntityPacket.setOnGround(isOnGround);
|
moveEntityPacket.setOnGround(isOnGround);
|
||||||
moveEntityPacket.setTeleported(false);
|
moveEntityPacket.setTeleported(false);
|
||||||
session.sendUpstreamPacket(moveEntityPacket);
|
session.sendUpstreamPacket(moveEntityPacket);
|
||||||
|
@ -33,7 +33,6 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
|||||||
import com.nukkitx.protocol.bedrock.packet.LevelSoundEvent2Packet;
|
import com.nukkitx.protocol.bedrock.packet.LevelSoundEvent2Packet;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
|
||||||
|
|
||||||
public class EndermanEntity extends MonsterEntity {
|
public class EndermanEntity extends MonsterEntity {
|
||||||
|
|
||||||
@ -45,7 +44,7 @@ public class EndermanEntity extends MonsterEntity {
|
|||||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||||
// Held block
|
// Held block
|
||||||
if (entityMetadata.getId() == 15) {
|
if (entityMetadata.getId() == 15) {
|
||||||
metadata.put(EntityData.CARRIED_BLOCK, BlockTranslator.getBedrockBlockId((int) entityMetadata.getValue()));
|
metadata.put(EntityData.CARRIED_BLOCK, session.getBlockTranslator().getBedrockBlockId((int) entityMetadata.getValue()));
|
||||||
}
|
}
|
||||||
// "Is screaming" - controls sound
|
// "Is screaming" - controls sound
|
||||||
if (entityMetadata.getId() == 16) {
|
if (entityMetadata.getId() == 16) {
|
||||||
|
@ -46,7 +46,7 @@ public class WitherEntity extends MonsterEntity {
|
|||||||
|
|
||||||
if (entityMetadata.getId() >= 15 && entityMetadata.getId() <= 17) {
|
if (entityMetadata.getId() >= 15 && entityMetadata.getId() <= 17) {
|
||||||
Entity entity = session.getEntityCache().getEntityByJavaId((int) entityMetadata.getValue());
|
Entity entity = session.getEntityCache().getEntityByJavaId((int) entityMetadata.getValue());
|
||||||
if (entity == null && session.getPlayerEntity().getEntityId() == (Integer) entityMetadata.getValue()) {
|
if (entity == null && session.getPlayerEntity().getEntityId() == (int) entityMetadata.getValue()) {
|
||||||
entity = session.getPlayerEntity();
|
entity = session.getPlayerEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ public class WitherEntity extends MonsterEntity {
|
|||||||
} else if (entityMetadata.getId() == 17) {
|
} else if (entityMetadata.getId() == 17) {
|
||||||
metadata.put(EntityData.WITHER_TARGET_3, targetID);
|
metadata.put(EntityData.WITHER_TARGET_3, targetID);
|
||||||
} else if (entityMetadata.getId() == 18) {
|
} else if (entityMetadata.getId() == 18) {
|
||||||
metadata.put(EntityData.WITHER_INVULNERABLE_TICKS, (int) entityMetadata.getValue());
|
metadata.put(EntityData.WITHER_INVULNERABLE_TICKS, entityMetadata.getValue());
|
||||||
|
|
||||||
// Show the shield for the first few seconds of spawning (like Java)
|
// Show the shield for the first few seconds of spawning (like Java)
|
||||||
if ((int) entityMetadata.getValue() >= 165) {
|
if ((int) entityMetadata.getValue() >= 165) {
|
||||||
|
@ -30,8 +30,11 @@ import org.geysermc.connector.entity.*;
|
|||||||
import org.geysermc.connector.entity.living.*;
|
import org.geysermc.connector.entity.living.*;
|
||||||
import org.geysermc.connector.entity.living.animal.*;
|
import org.geysermc.connector.entity.living.animal.*;
|
||||||
import org.geysermc.connector.entity.living.animal.horse.*;
|
import org.geysermc.connector.entity.living.animal.horse.*;
|
||||||
import org.geysermc.connector.entity.living.animal.tameable.*;
|
import org.geysermc.connector.entity.living.animal.tameable.CatEntity;
|
||||||
import org.geysermc.connector.entity.living.merchant.*;
|
import org.geysermc.connector.entity.living.animal.tameable.ParrotEntity;
|
||||||
|
import org.geysermc.connector.entity.living.animal.tameable.WolfEntity;
|
||||||
|
import org.geysermc.connector.entity.living.merchant.AbstractMerchantEntity;
|
||||||
|
import org.geysermc.connector.entity.living.merchant.VillagerEntity;
|
||||||
import org.geysermc.connector.entity.living.monster.*;
|
import org.geysermc.connector.entity.living.monster.*;
|
||||||
import org.geysermc.connector.entity.living.monster.raid.AbstractIllagerEntity;
|
import org.geysermc.connector.entity.living.monster.raid.AbstractIllagerEntity;
|
||||||
import org.geysermc.connector.entity.living.monster.raid.PillagerEntity;
|
import org.geysermc.connector.entity.living.monster.raid.PillagerEntity;
|
||||||
@ -39,6 +42,9 @@ import org.geysermc.connector.entity.living.monster.raid.RaidParticipantEntity;
|
|||||||
import org.geysermc.connector.entity.living.monster.raid.SpellcasterIllagerEntity;
|
import org.geysermc.connector.entity.living.monster.raid.SpellcasterIllagerEntity;
|
||||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public enum EntityType {
|
public enum EntityType {
|
||||||
|
|
||||||
@ -112,7 +118,7 @@ public enum EntityType {
|
|||||||
TRIDENT(TridentEntity.class, 73, 0f, 0f, 0f, 0f, "minecraft:thrown_trident"),
|
TRIDENT(TridentEntity.class, 73, 0f, 0f, 0f, 0f, "minecraft:thrown_trident"),
|
||||||
TURTLE(TurtleEntity.class, 74, 0.4f, 1.2f),
|
TURTLE(TurtleEntity.class, 74, 0.4f, 1.2f),
|
||||||
CAT(CatEntity.class, 75, 0.35f, 0.3f),
|
CAT(CatEntity.class, 75, 0.35f, 0.3f),
|
||||||
SHULKER_BULLET(Entity.class, 76, 0.3125f),
|
SHULKER_BULLET(ThrowableEntity.class, 76, 0.3125f),
|
||||||
FISHING_BOBBER(FishingHookEntity.class, 77, 0f, 0f, 0f, 0f, "minecraft:fishing_hook"),
|
FISHING_BOBBER(FishingHookEntity.class, 77, 0f, 0f, 0f, 0f, "minecraft:fishing_hook"),
|
||||||
CHALKBOARD(Entity.class, 78, 0f),
|
CHALKBOARD(Entity.class, 78, 0f),
|
||||||
DRAGON_FIREBALL(ItemedFireballEntity.class, 79, 1.0f),
|
DRAGON_FIREBALL(ItemedFireballEntity.class, 79, 1.0f),
|
||||||
@ -139,7 +145,7 @@ public enum EntityType {
|
|||||||
MINECART_SPAWNER(SpawnerMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"),
|
MINECART_SPAWNER(SpawnerMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"),
|
||||||
MINECART_COMMAND_BLOCK(CommandBlockMinecartEntity.class, 100, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:command_block_minecart"),
|
MINECART_COMMAND_BLOCK(CommandBlockMinecartEntity.class, 100, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:command_block_minecart"),
|
||||||
LINGERING_POTION(ThrowableEntity.class, 101, 0f),
|
LINGERING_POTION(ThrowableEntity.class, 101, 0f),
|
||||||
LLAMA_SPIT(Entity.class, 102, 0.25f),
|
LLAMA_SPIT(ThrowableEntity.class, 102, 0.25f),
|
||||||
EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f, 0.5f, 0f, "minecraft:evocation_fang"),
|
EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f, 0.5f, 0f, "minecraft:evocation_fang"),
|
||||||
EVOKER(SpellcasterIllagerEntity.class, 104, 1.95f, 0.6f, 0.6f, 0f, "minecraft:evocation_illager"),
|
EVOKER(SpellcasterIllagerEntity.class, 104, 1.95f, 0.6f, 0.6f, 0f, "minecraft:evocation_illager"),
|
||||||
VEX(VexEntity.class, 105, 0.8f, 0.4f),
|
VEX(VexEntity.class, 105, 0.8f, 0.4f),
|
||||||
@ -174,17 +180,33 @@ public enum EntityType {
|
|||||||
*/
|
*/
|
||||||
ENDER_DRAGON_PART(EnderDragonPartEntity.class, 32, 0, 0, 0, 0, "minecraft:armor_stand");
|
ENDER_DRAGON_PART(EnderDragonPartEntity.class, 32, 0, 0, 0, 0, "minecraft:armor_stand");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of all Java identifiers for use with command suggestions
|
||||||
|
*/
|
||||||
|
public static final String[] ALL_JAVA_IDENTIFIERS;
|
||||||
private static final EntityType[] VALUES = values();
|
private static final EntityType[] VALUES = values();
|
||||||
|
|
||||||
private Class<? extends Entity> entityClass;
|
static {
|
||||||
|
List<String> allJavaIdentifiers = new ArrayList<>();
|
||||||
|
for (EntityType type : values()) {
|
||||||
|
if (type == AGENT || type == BALLOON || type == CHALKBOARD || type == NPC || type == TRIPOD_CAMERA || type == ENDER_DRAGON_PART) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
allJavaIdentifiers.add("minecraft:" + type.name().toLowerCase());
|
||||||
|
}
|
||||||
|
ALL_JAVA_IDENTIFIERS = allJavaIdentifiers.toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Class<? extends Entity> entityClass;
|
||||||
private final int type;
|
private final int type;
|
||||||
private final float height;
|
private final float height;
|
||||||
private final float width;
|
private final float width;
|
||||||
private final float length;
|
private final float length;
|
||||||
private final float offset;
|
private final float offset;
|
||||||
private String identifier;
|
private final String identifier;
|
||||||
|
|
||||||
EntityType(Class<? extends Entity> entityClass, int type, float height) {
|
EntityType(Class<? extends Entity> entityClass, int type, float height) {
|
||||||
|
//noinspection SuspiciousNameCombination
|
||||||
this(entityClass, type, height, height);
|
this(entityClass, type, height, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,8 +220,6 @@ public enum EntityType {
|
|||||||
|
|
||||||
EntityType(Class<? extends Entity> entityClass, int type, float height, float width, float length, float offset) {
|
EntityType(Class<? extends Entity> entityClass, int type, float height, float width, float length, float offset) {
|
||||||
this(entityClass, type, height, width, length, offset, null);
|
this(entityClass, type, height, width, length, offset, null);
|
||||||
|
|
||||||
this.identifier = "minecraft:" + name().toLowerCase();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityType(Class<? extends Entity> entityClass, int type, float height, float width, float length, float offset, String identifier) {
|
EntityType(Class<? extends Entity> entityClass, int type, float height, float width, float length, float offset, String identifier) {
|
||||||
@ -209,7 +229,7 @@ public enum EntityType {
|
|||||||
this.width = width;
|
this.width = width;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
this.offset = offset + 0.00001f;
|
this.offset = offset + 0.00001f;
|
||||||
this.identifier = identifier;
|
this.identifier = identifier == null ? "minecraft:" + name().toLowerCase() : identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EntityType getFromIdentifier(String identifier) {
|
public static EntityType getFromIdentifier(String identifier) {
|
||||||
|
@ -23,16 +23,15 @@
|
|||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.connector.network.translators.inventory.action;
|
package org.geysermc.connector.inventory;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.window.ClickItemParam;
|
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
import com.github.steveice10.mc.protocol.data.game.window.WindowActionParam;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
/**
|
||||||
enum Click {
|
* Used to determine if rename packets should be sent.
|
||||||
LEFT(ClickItemParam.LEFT_CLICK),
|
*/
|
||||||
RIGHT(ClickItemParam.RIGHT_CLICK);
|
public class AnvilContainer extends Container {
|
||||||
|
public AnvilContainer(String title, int id, int size, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
public final WindowActionParam actionParam;
|
super(title, id, size, windowType, playerInventory);
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.connector.inventory;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class BeaconContainer extends Container {
|
||||||
|
private int primaryId;
|
||||||
|
private int secondaryId;
|
||||||
|
|
||||||
|
public BeaconContainer(String title, int id, int size, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
super(title, id, size, windowType, playerInventory);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.connector.inventory;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
|
|
||||||
|
public class CartographyContainer extends Container {
|
||||||
|
public CartographyContainer(String title, int id, int size, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
super(title, id, size, windowType, playerInventory);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.connector.inventory;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combination of {@link Inventory} and {@link PlayerInventory}
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public class Container extends Inventory {
|
||||||
|
private final PlayerInventory playerInventory;
|
||||||
|
private final int containerSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether we are using a real block when opening this inventory.
|
||||||
|
*/
|
||||||
|
private boolean isUsingRealBlock = false;
|
||||||
|
|
||||||
|
public Container(String title, int id, int size, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
super(title, id, size, windowType);
|
||||||
|
this.playerInventory = playerInventory;
|
||||||
|
this.containerSize = this.size + InventoryTranslator.PLAYER_INVENTORY_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GeyserItemStack getItem(int slot) {
|
||||||
|
if (slot < this.size) {
|
||||||
|
return super.getItem(slot);
|
||||||
|
} else {
|
||||||
|
return playerInventory.getItem(slot - this.size + InventoryTranslator.PLAYER_INVENTORY_OFFSET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) {
|
||||||
|
if (slot < this.size) {
|
||||||
|
super.setItem(slot, newItem, session);
|
||||||
|
} else {
|
||||||
|
playerInventory.setItem(slot - this.size + InventoryTranslator.PLAYER_INVENTORY_OFFSET, newItem, session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSize() {
|
||||||
|
return this.containerSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will be overwritten for droppers.
|
||||||
|
*
|
||||||
|
* @param usingRealBlock whether this container is using a real container or not
|
||||||
|
* @param javaBlockId the Java block string of the block, if real
|
||||||
|
*/
|
||||||
|
public void setUsingRealBlock(boolean usingRealBlock, String javaBlockId) {
|
||||||
|
isUsingRealBlock = usingRealBlock;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.connector.inventory;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.EnchantOptionData;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public class EnchantingContainer extends Container {
|
||||||
|
/**
|
||||||
|
* A cache of what Bedrock sees
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
private final EnchantOptionData[] enchantOptions;
|
||||||
|
/**
|
||||||
|
* A mutable cache of what the server sends us
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
private final GeyserEnchantOption[] geyserEnchantOptions;
|
||||||
|
|
||||||
|
public EnchantingContainer(String title, int id, int size, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
super(title, id, size, windowType, playerInventory);
|
||||||
|
|
||||||
|
enchantOptions = new EnchantOptionData[3];
|
||||||
|
geyserEnchantOptions = new GeyserEnchantOption[3];
|
||||||
|
for (int i = 0; i < geyserEnchantOptions.length; i++) {
|
||||||
|
geyserEnchantOptions[i] = new GeyserEnchantOption(i);
|
||||||
|
// Options cannot be null, so we build initial options
|
||||||
|
// GeyserSession can be safely null here because it's only needed for net IDs
|
||||||
|
enchantOptions[i] = geyserEnchantOptions[i].build(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.connector.inventory;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
|
||||||
|
public class Generic3X3Container extends Container {
|
||||||
|
/**
|
||||||
|
* Whether we need to set the container type as {@link com.nukkitx.protocol.bedrock.data.inventory.ContainerType#DROPPER}.
|
||||||
|
*
|
||||||
|
* Used at {@link org.geysermc.connector.network.translators.inventory.translators.Generic3X3InventoryTranslator#openInventory(GeyserSession, Inventory)}
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
private boolean isDropper = false;
|
||||||
|
|
||||||
|
public Generic3X3Container(String title, int id, int size, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
super(title, id, size, windowType, playerInventory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsingRealBlock(boolean usingRealBlock, String javaBlockId) {
|
||||||
|
super.setUsingRealBlock(usingRealBlock, javaBlockId);
|
||||||
|
if (usingRealBlock) {
|
||||||
|
isDropper = javaBlockId.startsWith("minecraft:dropper");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.connector.inventory;
|
||||||
|
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.EnchantData;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.EnchantOptionData;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mutable "wrapper" around {@link EnchantOptionData}
|
||||||
|
*/
|
||||||
|
@Setter
|
||||||
|
public class GeyserEnchantOption {
|
||||||
|
private static final List<EnchantData> EMPTY = Collections.emptyList();
|
||||||
|
/**
|
||||||
|
* This: https://cdn.discordapp.com/attachments/613168850925649981/791030657169227816/unknown.png
|
||||||
|
* is controlled by the server.
|
||||||
|
* So, of course, we have to throw in some easter eggs. ;)
|
||||||
|
*/
|
||||||
|
private static final List<String> ENCHANT_NAMES = Arrays.asList("tougher armor", "lukeeey", "fall better",
|
||||||
|
"explode less", "camo toy", "breathe better", "rtm five one six", "armor stab", "water walk", "you are elsa",
|
||||||
|
"tim two zero three", "fast walk nether", "oof ouch owie", "enemy on fire", "spider sad", "aj ferguson", "redned",
|
||||||
|
"more items thx", "long sword reach", "fast tool", "give me block", "less breaky break", "cube craft",
|
||||||
|
"strong arrow", "fist arrow", "spicy arrow", "many many arrows", "geyser", "come here fish", "i like this",
|
||||||
|
"stabby stab", "supreme mortal", "avatar i guess", "more arrows", "fly finder seventeen", "in and out",
|
||||||
|
"xp heals tools", "dragon proxy waz here");
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final int javaIndex;
|
||||||
|
|
||||||
|
private int xpCost = 0;
|
||||||
|
private int javaEnchantIndex = -1;
|
||||||
|
private int bedrockEnchantIndex = -1;
|
||||||
|
private int enchantLevel = -1;
|
||||||
|
|
||||||
|
public GeyserEnchantOption(int javaIndex) {
|
||||||
|
this.javaIndex = javaIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnchantOptionData build(GeyserSession session) {
|
||||||
|
return new EnchantOptionData(xpCost, javaIndex + 16, EMPTY,
|
||||||
|
enchantLevel == -1 ? EMPTY : Collections.singletonList(new EnchantData(bedrockEnchantIndex, enchantLevel)), EMPTY,
|
||||||
|
javaEnchantIndex == -1 ? "unknown" : ENCHANT_NAMES.get(javaEnchantIndex), enchantLevel == -1 ? 0 : session.getNextItemNetId());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.connector.inventory;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||||
|
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||||
|
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class GeyserItemStack {
|
||||||
|
public static final GeyserItemStack EMPTY = new GeyserItemStack(0, 0, null);
|
||||||
|
|
||||||
|
private final int javaId;
|
||||||
|
private int amount;
|
||||||
|
private CompoundTag nbt;
|
||||||
|
private int netId;
|
||||||
|
|
||||||
|
public GeyserItemStack(int javaId) {
|
||||||
|
this(javaId, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeyserItemStack(int javaId, int amount) {
|
||||||
|
this(javaId, amount, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeyserItemStack(int javaId, int amount, CompoundTag nbt) {
|
||||||
|
this(javaId, amount, nbt, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeyserItemStack(int javaId, int amount, CompoundTag nbt, int netId) {
|
||||||
|
this.javaId = javaId;
|
||||||
|
this.amount = amount;
|
||||||
|
this.nbt = nbt;
|
||||||
|
this.netId = netId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GeyserItemStack from(ItemStack itemStack) {
|
||||||
|
return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getNbt());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getJavaId() {
|
||||||
|
return isEmpty() ? 0 : javaId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAmount() {
|
||||||
|
return isEmpty() ? 0 : amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompoundTag getNbt() {
|
||||||
|
return isEmpty() ? null : nbt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNetId() {
|
||||||
|
return isEmpty() ? 0 : netId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(int add) {
|
||||||
|
amount += add;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sub(int sub) {
|
||||||
|
amount -= sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack getItemStack() {
|
||||||
|
return getItemStack(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack getItemStack(int newAmount) {
|
||||||
|
return isEmpty() ? null : new ItemStack(javaId, newAmount, nbt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemData getItemData(GeyserSession session) {
|
||||||
|
ItemData itemData = ItemTranslator.translateToBedrock(session, getItemStack());
|
||||||
|
itemData.setNetId(getNetId());
|
||||||
|
return itemData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemEntry getItemEntry() {
|
||||||
|
return ItemRegistry.ITEM_ENTRIES.get(getJavaId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return amount <= 0 || javaId == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeyserItemStack copy() {
|
||||||
|
return copy(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeyserItemStack copy(int newAmount) {
|
||||||
|
return isEmpty() ? EMPTY : new GeyserItemStack(javaId, newAmount, nbt == null ? null : nbt.clone(), netId);
|
||||||
|
}
|
||||||
|
}
|
@ -25,36 +25,39 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.inventory;
|
package org.geysermc.connector.inventory;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.NonNull;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class Inventory {
|
public class Inventory {
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
protected int id;
|
protected final int id;
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
protected boolean open;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
protected WindowType windowType;
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
protected final int size;
|
protected final int size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for smooth transitions between two windows of the same type.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
protected final WindowType windowType;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
protected String title;
|
protected String title;
|
||||||
|
|
||||||
@Setter
|
protected GeyserItemStack[] items;
|
||||||
protected ItemStack[] items;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The location of the inventory block. Will either be a fake block above the player's head, or the actual block location
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
protected Vector3i holderPosition = Vector3i.ZERO;
|
protected Vector3i holderPosition = Vector3i.ZERO;
|
||||||
@ -64,27 +67,67 @@ public class Inventory {
|
|||||||
protected long holderId = -1;
|
protected long holderId = -1;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
protected AtomicInteger transactionId = new AtomicInteger(1);
|
protected short transactionId = 0;
|
||||||
|
|
||||||
public Inventory(int id, WindowType windowType, int size) {
|
@Getter
|
||||||
this("Inventory", id, windowType, size);
|
@Setter
|
||||||
|
private boolean pending = false;
|
||||||
|
|
||||||
|
protected Inventory(int id, int size, WindowType windowType) {
|
||||||
|
this("Inventory", id, size, windowType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Inventory(String title, int id, WindowType windowType, int size) {
|
protected Inventory(String title, int id, int size, WindowType windowType) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.windowType = windowType;
|
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.items = new ItemStack[size];
|
this.windowType = windowType;
|
||||||
|
this.items = new GeyserItemStack[size];
|
||||||
|
Arrays.fill(items, GeyserItemStack.EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemStack getItem(int slot) {
|
public GeyserItemStack getItem(int slot) {
|
||||||
|
if (slot > this.size) {
|
||||||
|
GeyserConnector.getInstance().getLogger().debug("Tried to get an item out of bounds! " + this.toString());
|
||||||
|
return GeyserItemStack.EMPTY;
|
||||||
|
}
|
||||||
return items[slot];
|
return items[slot];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setItem(int slot, ItemStack item) {
|
public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) {
|
||||||
if (item != null && (item.getId() == 0 || item.getAmount() < 1))
|
if (slot > this.size) {
|
||||||
item = null;
|
session.getConnector().getLogger().debug("Tried to set an item out of bounds! " + this.toString());
|
||||||
items[slot] = item;
|
return;
|
||||||
|
}
|
||||||
|
GeyserItemStack oldItem = items[slot];
|
||||||
|
updateItemNetId(oldItem, newItem, session);
|
||||||
|
items[slot] = newItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void updateItemNetId(GeyserItemStack oldItem, GeyserItemStack newItem, GeyserSession session) {
|
||||||
|
if (!newItem.isEmpty()) {
|
||||||
|
if (newItem.getItemData(session).equals(oldItem.getItemData(session), false, false, false)) {
|
||||||
|
newItem.setNetId(oldItem.getNetId());
|
||||||
|
} else {
|
||||||
|
newItem.setNetId(session.getNextItemNetId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getNextTransactionId() {
|
||||||
|
return ++transactionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Inventory{" +
|
||||||
|
"id=" + id +
|
||||||
|
", size=" + size +
|
||||||
|
", title='" + title + '\'' +
|
||||||
|
", items=" + Arrays.toString(items) +
|
||||||
|
", holderPosition=" + holderPosition +
|
||||||
|
", holderId=" + holderId +
|
||||||
|
", transactionId=" + transactionId +
|
||||||
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.connector.inventory;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
|
import com.nukkitx.nbt.NbtMap;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
public class LecternContainer extends Container {
|
||||||
|
@Getter @Setter
|
||||||
|
private int currentBedrockPage = 0;
|
||||||
|
@Getter @Setter
|
||||||
|
private NbtMap blockEntityTag;
|
||||||
|
@Getter @Setter
|
||||||
|
private Vector3i position;
|
||||||
|
|
||||||
|
public LecternContainer(String title, int id, int size, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
super(title, id, size, windowType, playerInventory);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.connector.inventory;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.geysermc.connector.entity.Entity;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class MerchantContainer extends Container {
|
||||||
|
private Entity villager;
|
||||||
|
private VillagerTrade[] villagerTrades;
|
||||||
|
|
||||||
|
public MerchantContainer(String title, int id, int size, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
|
super(title, id, size, windowType, playerInventory);
|
||||||
|
}
|
||||||
|
}
|
@ -25,9 +25,11 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.inventory;
|
package org.geysermc.connector.inventory;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.NonNull;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
|
||||||
public class PlayerInventory extends Inventory {
|
public class PlayerInventory extends Inventory {
|
||||||
|
|
||||||
@ -40,20 +42,36 @@ public class PlayerInventory extends Inventory {
|
|||||||
private int heldItemSlot;
|
private int heldItemSlot;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private ItemStack cursor;
|
@NonNull
|
||||||
|
private GeyserItemStack cursor = GeyserItemStack.EMPTY;
|
||||||
|
|
||||||
public PlayerInventory() {
|
public PlayerInventory() {
|
||||||
super(0, null, 46);
|
super(0, 46, null);
|
||||||
heldItemSlot = 0;
|
heldItemSlot = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCursor(ItemStack stack) {
|
public void setCursor(@NonNull GeyserItemStack newCursor, GeyserSession session) {
|
||||||
if (stack != null && (stack.getId() == 0 || stack.getAmount() < 1))
|
updateItemNetId(cursor, newCursor, session);
|
||||||
stack = null;
|
cursor = newCursor;
|
||||||
cursor = stack;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemStack getItemInHand() {
|
public GeyserItemStack getItemInHand() {
|
||||||
|
if (36 + heldItemSlot > this.size) {
|
||||||
|
GeyserConnector.getInstance().getLogger().debug("Held item slot was larger than expected!");
|
||||||
|
return GeyserItemStack.EMPTY;
|
||||||
|
}
|
||||||
return items[36 + heldItemSlot];
|
return items[36 + heldItemSlot];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setItemInHand(@NonNull GeyserItemStack item) {
|
||||||
|
if (36 + heldItemSlot > this.size) {
|
||||||
|
GeyserConnector.getInstance().getLogger().debug("Held item slot was larger than expected!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
items[36 + heldItemSlot] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeyserItemStack getOffhand() {
|
||||||
|
return items[45];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,39 +23,32 @@
|
|||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.connector.network.session.cache;
|
package org.geysermc.connector.inventory;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.NonNull;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
|
||||||
public class InventoryCache {
|
public class StonecutterContainer extends Container {
|
||||||
|
/**
|
||||||
private GeyserSession session;
|
* The button that has currently been pressed Java-side
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private Inventory openInventory;
|
private int stonecutterButton = -1;
|
||||||
|
|
||||||
@Getter
|
public StonecutterContainer(String title, int id, int size, WindowType windowType, PlayerInventory playerInventory) {
|
||||||
private Int2ObjectMap<Inventory> inventories = new Int2ObjectOpenHashMap<>();
|
super(title, id, size, windowType, playerInventory);
|
||||||
|
|
||||||
public InventoryCache(GeyserSession session) {
|
|
||||||
this.session = session;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Inventory getPlayerInventory() {
|
@Override
|
||||||
return inventories.get(0);
|
public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) {
|
||||||
|
if (slot == 0 && newItem.getJavaId() != items[slot].getJavaId()) {
|
||||||
|
// The pressed stonecutter button output resets whenever the input item changes
|
||||||
|
this.stonecutterButton = -1;
|
||||||
}
|
}
|
||||||
|
super.setItem(slot, newItem, session);
|
||||||
public void cacheInventory(Inventory inventory) {
|
|
||||||
inventories.put(inventory.getId(), inventory);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void uncacheInventory(int id) {
|
|
||||||
inventories.remove(id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -36,6 +36,7 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -75,7 +76,7 @@ public class Metrics {
|
|||||||
|
|
||||||
private final static ObjectMapper mapper = new ObjectMapper();
|
private final static ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
private GeyserConnector connector;
|
private final GeyserConnector connector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class constructor.
|
* Class constructor.
|
||||||
@ -156,6 +157,7 @@ public class Metrics {
|
|||||||
String osName = System.getProperty("os.name");
|
String osName = System.getProperty("os.name");
|
||||||
String osArch = System.getProperty("os.arch");
|
String osArch = System.getProperty("os.arch");
|
||||||
String osVersion = System.getProperty("os.version");
|
String osVersion = System.getProperty("os.version");
|
||||||
|
String javaVersion = System.getProperty("java.version");
|
||||||
int coreCount = Runtime.getRuntime().availableProcessors();
|
int coreCount = Runtime.getRuntime().availableProcessors();
|
||||||
|
|
||||||
ObjectNode data = mapper.createObjectNode();
|
ObjectNode data = mapper.createObjectNode();
|
||||||
@ -163,6 +165,7 @@ public class Metrics {
|
|||||||
data.put("serverUUID", serverUUID);
|
data.put("serverUUID", serverUUID);
|
||||||
|
|
||||||
data.put("playerAmount", playerAmount);
|
data.put("playerAmount", playerAmount);
|
||||||
|
data.put("javaVersion", javaVersion);
|
||||||
data.put("osName", osName);
|
data.put("osName", osName);
|
||||||
data.put("osArch", osArch);
|
data.put("osArch", osArch);
|
||||||
data.put("osVersion", osVersion);
|
data.put("osVersion", osVersion);
|
||||||
@ -241,7 +244,7 @@ public class Metrics {
|
|||||||
}
|
}
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
|
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
|
||||||
gzip.write(str.getBytes("UTF-8"));
|
gzip.write(str.getBytes(StandardCharsets.UTF_8));
|
||||||
gzip.close();
|
gzip.close();
|
||||||
return outputStream.toByteArray();
|
return outputStream.toByteArray();
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ package org.geysermc.connector.network;
|
|||||||
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
|
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
|
||||||
import com.nukkitx.protocol.bedrock.v419.Bedrock_v419;
|
import com.nukkitx.protocol.bedrock.v419.Bedrock_v419;
|
||||||
import com.nukkitx.protocol.bedrock.v422.Bedrock_v422;
|
import com.nukkitx.protocol.bedrock.v422.Bedrock_v422;
|
||||||
|
import com.nukkitx.protocol.bedrock.v428.Bedrock_v428;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -40,9 +41,7 @@ public class BedrockProtocol {
|
|||||||
* Default Bedrock codec that should act as a fallback. Should represent the latest available
|
* Default Bedrock codec that should act as a fallback. Should represent the latest available
|
||||||
* release of the game that Geyser supports.
|
* release of the game that Geyser supports.
|
||||||
*/
|
*/
|
||||||
public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v422.V422_CODEC.toBuilder()
|
public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v428.V428_CODEC;
|
||||||
.minecraftVersion("1.16.201")
|
|
||||||
.build();
|
|
||||||
/**
|
/**
|
||||||
* A list of all supported Bedrock versions that can join Geyser
|
* A list of all supported Bedrock versions that can join Geyser
|
||||||
*/
|
*/
|
||||||
@ -52,9 +51,10 @@ public class BedrockProtocol {
|
|||||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v419.V419_CODEC.toBuilder()
|
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v419.V419_CODEC.toBuilder()
|
||||||
.minecraftVersion("1.16.100/1.16.101") // We change this as 1.16.100.60 is a beta
|
.minecraftVersion("1.16.100/1.16.101") // We change this as 1.16.100.60 is a beta
|
||||||
.build());
|
.build());
|
||||||
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder()
|
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v422.V422_CODEC.toBuilder()
|
||||||
.minecraftVersion("1.16.200/1.16.201")
|
.minecraftVersion("1.16.200/1.16.201")
|
||||||
.build());
|
.build());
|
||||||
|
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.connector.network;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Taken & modified from TCPShield, licensed under MIT. See https://github.com/TCPShield/RealIP/blob/master/LICENSE
|
||||||
|
*
|
||||||
|
* https://github.com/TCPShield/RealIP/blob/32d422a9523cb6e25b571072851f3306bb8bbc4f/src/main/java/net/tcpshield/tcpshield/validation/cidr/CIDRMatcher.java
|
||||||
|
*/
|
||||||
|
public class CIDRMatcher {
|
||||||
|
private final int maskBits;
|
||||||
|
private final int maskBytes;
|
||||||
|
private final boolean simpleCIDR;
|
||||||
|
private final InetAddress cidrAddress;
|
||||||
|
|
||||||
|
public CIDRMatcher(String ipAddress) {
|
||||||
|
String[] split = ipAddress.split("/", 2);
|
||||||
|
|
||||||
|
String parsedIPAddress;
|
||||||
|
if (split.length == 2) {
|
||||||
|
parsedIPAddress = split[0];
|
||||||
|
|
||||||
|
this.maskBits = Integer.parseInt(split[1]);
|
||||||
|
this.simpleCIDR = maskBits == 32;
|
||||||
|
} else {
|
||||||
|
parsedIPAddress = ipAddress;
|
||||||
|
|
||||||
|
this.maskBits = -1;
|
||||||
|
this.simpleCIDR = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.maskBytes = simpleCIDR ? -1 : maskBits / 8;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cidrAddress = InetAddress.getByName(parsedIPAddress);
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matches(InetAddress inetAddress) {
|
||||||
|
// check if IP is IPv4 or IPv6
|
||||||
|
if (cidrAddress.getClass() != inetAddress.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for equality if it's a simple CIDR
|
||||||
|
if (simpleCIDR) {
|
||||||
|
return inetAddress.equals(cidrAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] inetAddressBytes = inetAddress.getAddress();
|
||||||
|
byte[] requiredAddressBytes = cidrAddress.getAddress();
|
||||||
|
|
||||||
|
byte finalByte = (byte) (0xFF00 >> (maskBits & 0x07));
|
||||||
|
|
||||||
|
for (int i = 0; i < maskBytes; i++) {
|
||||||
|
if (inetAddressBytes[i] != requiredAddressBytes[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finalByte != 0) {
|
||||||
|
return (inetAddressBytes[maskBytes] & finalByte) == (requiredAddressBytes[maskBytes] & finalByte);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -40,8 +40,18 @@ import org.geysermc.connector.utils.LanguageUtils;
|
|||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class ConnectorServerEventHandler implements BedrockServerEventHandler {
|
public class ConnectorServerEventHandler implements BedrockServerEventHandler {
|
||||||
|
/*
|
||||||
|
The following constants are all used to ensure the ping does not reach a length where it is unparsable by the Bedrock client
|
||||||
|
*/
|
||||||
|
private static final int MINECRAFT_VERSION_BYTES_LENGTH = BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion().getBytes(StandardCharsets.UTF_8).length;
|
||||||
|
private static final int BRAND_BYTES_LENGTH = GeyserConnector.NAME.getBytes(StandardCharsets.UTF_8).length;
|
||||||
|
/**
|
||||||
|
* The MOTD, sub-MOTD and Minecraft version ({@link #MINECRAFT_VERSION_BYTES_LENGTH}) combined cannot reach this length.
|
||||||
|
*/
|
||||||
|
private static final int MAGIC_RAKNET_LENGTH = 338;
|
||||||
|
|
||||||
private final GeyserConnector connector;
|
private final GeyserConnector connector;
|
||||||
|
|
||||||
@ -51,6 +61,21 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onConnectionRequest(InetSocketAddress inetSocketAddress) {
|
public boolean onConnectionRequest(InetSocketAddress inetSocketAddress) {
|
||||||
|
List<String> allowedProxyIPs = connector.getConfig().getBedrock().getProxyProtocolWhitelistedIPs();
|
||||||
|
if (connector.getConfig().getBedrock().isEnableProxyProtocol() && !allowedProxyIPs.isEmpty()) {
|
||||||
|
boolean isWhitelistedIP = false;
|
||||||
|
for (CIDRMatcher matcher : connector.getConfig().getBedrock().getWhitelistedIPsMatchers()) {
|
||||||
|
if (matcher.matches(inetSocketAddress.getAddress())) {
|
||||||
|
isWhitelistedIP = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isWhitelistedIP) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.attempt_connect", inetSocketAddress));
|
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.attempt_connect", inetSocketAddress));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -69,16 +94,16 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
|
|||||||
|
|
||||||
BedrockPong pong = new BedrockPong();
|
BedrockPong pong = new BedrockPong();
|
||||||
pong.setEdition("MCPE");
|
pong.setEdition("MCPE");
|
||||||
pong.setGameType("Default");
|
pong.setGameType("Survival"); // Can only be Survival or Creative as of 1.16.210.59
|
||||||
pong.setNintendoLimited(false);
|
pong.setNintendoLimited(false);
|
||||||
pong.setProtocolVersion(BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion());
|
pong.setProtocolVersion(BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion());
|
||||||
pong.setVersion(null); // Server tries to connect either way and it looks better
|
pong.setVersion(BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()); // Required to not be empty as of 1.16.210.59. Can only contain . and numbers.
|
||||||
pong.setIpv4Port(config.getBedrock().getPort());
|
pong.setIpv4Port(config.getBedrock().getPort());
|
||||||
|
|
||||||
if (config.isPassthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) {
|
if (config.isPassthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) {
|
||||||
String[] motd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n");
|
String[] motd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n");
|
||||||
String mainMotd = motd[0]; // First line of the motd.
|
String mainMotd = motd[0]; // First line of the motd.
|
||||||
String subMotd = (motd.length != 1) ? motd[1] : ""; // Second line of the motd if present, otherwise blank.
|
String subMotd = (motd.length != 1) ? motd[1] : GeyserConnector.NAME; // Second line of the motd if present, otherwise default.
|
||||||
|
|
||||||
pong.setMotd(mainMotd.trim());
|
pong.setMotd(mainMotd.trim());
|
||||||
pong.setSubMotd(subMotd.trim()); // Trimmed to shift it to the left, prevents the universe from collapsing on us just because we went 2 characters over the text box's limit.
|
pong.setSubMotd(subMotd.trim()); // Trimmed to shift it to the left, prevents the universe from collapsing on us just because we went 2 characters over the text box's limit.
|
||||||
@ -95,15 +120,28 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
|
|||||||
pong.setMaximumPlayerCount(config.getMaxPlayers());
|
pong.setMaximumPlayerCount(config.getMaxPlayers());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallbacks to prevent errors and allow Bedrock to see the server
|
||||||
|
if (pong.getMotd() == null || pong.getMotd().trim().isEmpty()) {
|
||||||
|
pong.setMotd(GeyserConnector.NAME);
|
||||||
|
}
|
||||||
|
if (pong.getSubMotd() == null || pong.getSubMotd().trim().isEmpty()) {
|
||||||
|
// Sub-MOTD cannot be empty as of 1.16.210.59
|
||||||
|
pong.setSubMotd(GeyserConnector.NAME);
|
||||||
|
}
|
||||||
|
|
||||||
// The ping will not appear if the MOTD + sub-MOTD is of a certain length.
|
// The ping will not appear if the MOTD + sub-MOTD is of a certain length.
|
||||||
// We don't know why, though
|
// We don't know why, though
|
||||||
byte[] motdArray = pong.getMotd().getBytes(StandardCharsets.UTF_8);
|
byte[] motdArray = pong.getMotd().getBytes(StandardCharsets.UTF_8);
|
||||||
if (motdArray.length + pong.getSubMotd().getBytes(StandardCharsets.UTF_8).length > 338) {
|
int subMotdLength = pong.getSubMotd().getBytes(StandardCharsets.UTF_8).length;
|
||||||
// Remove the sub-MOTD first since that only appears locally
|
if (motdArray.length + subMotdLength > (MAGIC_RAKNET_LENGTH - MINECRAFT_VERSION_BYTES_LENGTH)) {
|
||||||
pong.setSubMotd("");
|
// Shorten the sub-MOTD first since that only appears locally
|
||||||
if (motdArray.length > 338) {
|
if (subMotdLength > BRAND_BYTES_LENGTH) {
|
||||||
|
pong.setSubMotd(GeyserConnector.NAME);
|
||||||
|
subMotdLength = BRAND_BYTES_LENGTH;
|
||||||
|
}
|
||||||
|
if (motdArray.length > (MAGIC_RAKNET_LENGTH - MINECRAFT_VERSION_BYTES_LENGTH - subMotdLength)) {
|
||||||
// If the top MOTD is still too long, we chop it down
|
// If the top MOTD is still too long, we chop it down
|
||||||
byte[] newMotdArray = new byte[339];
|
byte[] newMotdArray = new byte[MAGIC_RAKNET_LENGTH - MINECRAFT_VERSION_BYTES_LENGTH - subMotdLength];
|
||||||
System.arraycopy(motdArray, 0, newMotdArray, 0, newMotdArray.length);
|
System.arraycopy(motdArray, 0, newMotdArray, 0, newMotdArray.length);
|
||||||
pong.setMotd(new String(newMotdArray, StandardCharsets.UTF_8));
|
pong.setMotd(new String(newMotdArray, StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
|
@ -26,15 +26,18 @@
|
|||||||
package org.geysermc.connector.network;
|
package org.geysermc.connector.network;
|
||||||
|
|
||||||
import com.nukkitx.protocol.bedrock.BedrockPacket;
|
import com.nukkitx.protocol.bedrock.BedrockPacket;
|
||||||
import com.nukkitx.protocol.bedrock.data.ResourcePackType;
|
|
||||||
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
|
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.ResourcePackType;
|
||||||
import com.nukkitx.protocol.bedrock.packet.*;
|
import com.nukkitx.protocol.bedrock.packet.*;
|
||||||
|
import com.nukkitx.protocol.bedrock.v428.Bedrock_v428;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.common.AuthType;
|
import org.geysermc.connector.common.AuthType;
|
||||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.session.cache.AdvancementsCache;
|
import org.geysermc.connector.network.session.cache.AdvancementsCache;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
||||||
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator1_16_100;
|
||||||
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator1_16_210;
|
||||||
import org.geysermc.connector.utils.LanguageUtils;
|
import org.geysermc.connector.utils.LanguageUtils;
|
||||||
import org.geysermc.connector.utils.LoginEncryptionUtils;
|
import org.geysermc.connector.utils.LoginEncryptionUtils;
|
||||||
import org.geysermc.connector.utils.MathUtils;
|
import org.geysermc.connector.utils.MathUtils;
|
||||||
@ -72,6 +75,10 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||||||
|
|
||||||
session.getUpstream().getSession().setPacketCodec(packetCodec);
|
session.getUpstream().getSession().setPacketCodec(packetCodec);
|
||||||
|
|
||||||
|
// Set the block translation based off of version
|
||||||
|
session.setBlockTranslator(packetCodec.getProtocolVersion() >= Bedrock_v428.V428_CODEC.getProtocolVersion()
|
||||||
|
? BlockTranslator1_16_210.INSTANCE : BlockTranslator1_16_100.INSTANCE);
|
||||||
|
|
||||||
LoginEncryptionUtils.encryptPlayerConnection(connector, session, loginPacket);
|
LoginEncryptionUtils.encryptPlayerConnection(connector, session, loginPacket);
|
||||||
|
|
||||||
PlayStatusPacket playStatus = new PlayStatusPacket();
|
PlayStatusPacket playStatus = new PlayStatusPacket();
|
||||||
@ -94,7 +101,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||||||
public boolean handle(ResourcePackClientResponsePacket packet) {
|
public boolean handle(ResourcePackClientResponsePacket packet) {
|
||||||
switch (packet.getStatus()) {
|
switch (packet.getStatus()) {
|
||||||
case COMPLETED:
|
case COMPLETED:
|
||||||
session.connect(connector.getRemoteServer());
|
session.connect();
|
||||||
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.connect", session.getAuthData().getName()));
|
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.connect", session.getAuthData().getName()));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -169,7 +176,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||||||
public boolean handle(SetLocalPlayerAsInitializedPacket packet) {
|
public boolean handle(SetLocalPlayerAsInitializedPacket packet) {
|
||||||
LanguageUtils.loadGeyserLocale(session.getLocale());
|
LanguageUtils.loadGeyserLocale(session.getLocale());
|
||||||
|
|
||||||
if (!session.isLoggedIn() && !session.isLoggingIn() && session.getConnector().getAuthType() == AuthType.ONLINE) {
|
if (!session.isLoggedIn() && !session.isLoggingIn() && session.getRemoteAuthType() == AuthType.ONLINE) {
|
||||||
// TODO it is safer to key authentication on something that won't change (UUID, not username)
|
// TODO it is safer to key authentication on something that won't change (UUID, not username)
|
||||||
if (!couldLoginUserByName(session.getAuthData().getName())) {
|
if (!couldLoginUserByName(session.getAuthData().getName())) {
|
||||||
LoginEncryptionUtils.buildAndShowLoginWindow(session);
|
LoginEncryptionUtils.buildAndShowLoginWindow(session);
|
||||||
|
@ -36,12 +36,13 @@ import com.github.steveice10.mc.protocol.MinecraftConstants;
|
|||||||
import com.github.steveice10.mc.protocol.MinecraftProtocol;
|
import com.github.steveice10.mc.protocol.MinecraftProtocol;
|
||||||
import com.github.steveice10.mc.protocol.data.SubProtocol;
|
import com.github.steveice10.mc.protocol.data.SubProtocol;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.recipe.Recipe;
|
||||||
import com.github.steveice10.mc.protocol.data.game.statistic.Statistic;
|
import com.github.steveice10.mc.protocol.data.game.statistic.Statistic;
|
||||||
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
|
|
||||||
import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket;
|
import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionPacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket;
|
||||||
|
import com.github.steveice10.mc.protocol.packet.login.client.LoginPluginResponsePacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.login.server.LoginSuccessPacket;
|
import com.github.steveice10.mc.protocol.packet.login.server.LoginSuccessPacket;
|
||||||
import com.github.steveice10.packetlib.BuiltinFlags;
|
import com.github.steveice10.packetlib.BuiltinFlags;
|
||||||
import com.github.steveice10.packetlib.Client;
|
import com.github.steveice10.packetlib.Client;
|
||||||
@ -57,24 +58,27 @@ import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
|||||||
import com.nukkitx.protocol.bedrock.packet.*;
|
import com.nukkitx.protocol.bedrock.packet.*;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||||
|
import lombok.AccessLevel;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.entity.Tickable;
|
|
||||||
import org.geysermc.connector.command.CommandSender;
|
import org.geysermc.connector.command.CommandSender;
|
||||||
import org.geysermc.connector.common.AuthType;
|
import org.geysermc.connector.common.AuthType;
|
||||||
import org.geysermc.connector.entity.Entity;
|
import org.geysermc.connector.entity.Entity;
|
||||||
|
import org.geysermc.connector.entity.Tickable;
|
||||||
import org.geysermc.connector.entity.player.SessionPlayerEntity;
|
import org.geysermc.connector.entity.player.SessionPlayerEntity;
|
||||||
import org.geysermc.connector.entity.player.SkullPlayerEntity;
|
import org.geysermc.connector.entity.player.SkullPlayerEntity;
|
||||||
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
import org.geysermc.connector.inventory.PlayerInventory;
|
import org.geysermc.connector.inventory.PlayerInventory;
|
||||||
import org.geysermc.connector.network.remote.RemoteServer;
|
|
||||||
import org.geysermc.connector.network.session.auth.AuthData;
|
import org.geysermc.connector.network.session.auth.AuthData;
|
||||||
import org.geysermc.connector.network.session.auth.BedrockClientData;
|
import org.geysermc.connector.network.session.auth.BedrockClientData;
|
||||||
import org.geysermc.connector.network.session.cache.*;
|
import org.geysermc.connector.network.session.cache.*;
|
||||||
@ -83,8 +87,10 @@ import org.geysermc.connector.network.translators.EntityIdentifierRegistry;
|
|||||||
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
||||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||||
import org.geysermc.connector.network.translators.collision.CollisionManager;
|
import org.geysermc.connector.network.translators.collision.CollisionManager;
|
||||||
import org.geysermc.connector.network.translators.inventory.EnchantmentInventoryTranslator;
|
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||||
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
|
import org.geysermc.connector.skin.FloodgateSkinUploader;
|
||||||
import org.geysermc.connector.skin.SkinManager;
|
import org.geysermc.connector.skin.SkinManager;
|
||||||
import org.geysermc.connector.utils.*;
|
import org.geysermc.connector.utils.*;
|
||||||
import org.geysermc.cumulus.Form;
|
import org.geysermc.cumulus.Form;
|
||||||
@ -92,47 +98,83 @@ import org.geysermc.cumulus.util.FormBuilder;
|
|||||||
import org.geysermc.floodgate.crypto.FloodgateCipher;
|
import org.geysermc.floodgate.crypto.FloodgateCipher;
|
||||||
import org.geysermc.floodgate.util.BedrockData;
|
import org.geysermc.floodgate.util.BedrockData;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class GeyserSession implements CommandSender {
|
public class GeyserSession implements CommandSender {
|
||||||
|
|
||||||
private final GeyserConnector connector;
|
private final GeyserConnector connector;
|
||||||
private final UpstreamSession upstream;
|
private final UpstreamSession upstream;
|
||||||
private RemoteServer remoteServer;
|
|
||||||
private Client downstream;
|
private Client downstream;
|
||||||
@Setter
|
@Setter
|
||||||
private AuthData authData;
|
private AuthData authData;
|
||||||
@Setter
|
@Setter
|
||||||
private BedrockClientData clientData;
|
private BedrockClientData clientData;
|
||||||
|
|
||||||
|
/* Setter for GeyserConnect */
|
||||||
|
@Setter
|
||||||
|
private String remoteAddress;
|
||||||
|
@Setter
|
||||||
|
private int remotePort;
|
||||||
|
@Setter
|
||||||
|
private AuthType remoteAuthType;
|
||||||
|
/* Setter for GeyserConnect */
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Setter
|
@Setter
|
||||||
private boolean microsoftAccount;
|
private boolean microsoftAccount;
|
||||||
|
|
||||||
private final SessionPlayerEntity playerEntity;
|
private final SessionPlayerEntity playerEntity;
|
||||||
private PlayerInventory inventory;
|
|
||||||
|
|
||||||
private AdvancementsCache advancementsCache;
|
private AdvancementsCache advancementsCache;
|
||||||
private BookEditCache bookEditCache;
|
private BookEditCache bookEditCache;
|
||||||
private ChunkCache chunkCache;
|
private ChunkCache chunkCache;
|
||||||
private EntityCache entityCache;
|
private EntityCache entityCache;
|
||||||
private EntityEffectCache effectCache;
|
private EntityEffectCache effectCache;
|
||||||
private InventoryCache inventoryCache;
|
|
||||||
private WorldCache worldCache;
|
private WorldCache worldCache;
|
||||||
private FormCache formCache;
|
private FormCache formCache;
|
||||||
private final Int2ObjectMap<TeleportCache> teleportMap = new Int2ObjectOpenHashMap<>();
|
private final Int2ObjectMap<TeleportCache> teleportMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
|
private final PlayerInventory playerInventory;
|
||||||
|
@Setter
|
||||||
|
private Inventory openInventory;
|
||||||
|
@Setter
|
||||||
|
private boolean closingInventory;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private InventoryTranslator inventoryTranslator = InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use {@link #getNextItemNetId()} instead for consistency
|
||||||
|
*/
|
||||||
|
@Getter(AccessLevel.NONE)
|
||||||
|
private final AtomicInteger itemNetId = new AtomicInteger(2);
|
||||||
|
|
||||||
|
@Getter(AccessLevel.NONE)
|
||||||
|
private final Object inventoryLock = new Object();
|
||||||
|
@Getter(AccessLevel.NONE)
|
||||||
|
private CompletableFuture<Void> inventoryFuture;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private ScheduledFuture<?> craftingGridFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores session collision
|
* Stores session collision
|
||||||
*/
|
*/
|
||||||
private final CollisionManager collisionManager;
|
private final CollisionManager collisionManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the block translations for this specific version.
|
||||||
|
*/
|
||||||
|
@Setter
|
||||||
|
private BlockTranslator blockTranslator;
|
||||||
|
|
||||||
private final Map<Vector3i, SkullPlayerEntity> skullCache = new ConcurrentHashMap<>();
|
private final Map<Vector3i, SkullPlayerEntity> skullCache = new ConcurrentHashMap<>();
|
||||||
private final Long2ObjectMap<ClientboundMapItemDataPacket> storedMaps = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
|
private final Long2ObjectMap<ClientboundMapItemDataPacket> storedMaps = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
|
||||||
|
|
||||||
@ -142,6 +184,16 @@ public class GeyserSession implements CommandSender {
|
|||||||
*/
|
*/
|
||||||
private final Object2LongMap<Vector3i> itemFrameCache = new Object2LongOpenHashMap<>();
|
private final Object2LongMap<Vector3i> itemFrameCache = new Object2LongOpenHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a list of all lectern locations and their block entity tags.
|
||||||
|
* See {@link org.geysermc.connector.network.translators.world.WorldManager#getLecternDataAt(GeyserSession, int, int, int, boolean)}
|
||||||
|
* for more information.
|
||||||
|
*/
|
||||||
|
private final Set<Vector3i> lecternCache = new ObjectOpenHashSet<>();
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private boolean droppingLecternBook;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private Vector2i lastChunkPosition = null;
|
private Vector2i lastChunkPosition = null;
|
||||||
private int renderDistance;
|
private int renderDistance;
|
||||||
@ -167,6 +219,9 @@ public class GeyserSession implements CommandSender {
|
|||||||
@Setter
|
@Setter
|
||||||
private boolean sprinting;
|
private boolean sprinting;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not updated if cache chunks is enabled.
|
||||||
|
*/
|
||||||
@Setter
|
@Setter
|
||||||
private boolean jumping;
|
private boolean jumping;
|
||||||
|
|
||||||
@ -195,26 +250,36 @@ public class GeyserSession implements CommandSender {
|
|||||||
* Initialized as (0, 0, 0) so it is always not-null.
|
* Initialized as (0, 0, 0) so it is always not-null.
|
||||||
*/
|
*/
|
||||||
@Setter
|
@Setter
|
||||||
private Vector3i lastInteractionPosition = Vector3i.ZERO;
|
private Vector3i lastInteractionBlockPosition = Vector3i.ZERO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the position of the player the last time they interacted.
|
||||||
|
* Used to verify that the player did not move since their last interaction. <br>
|
||||||
|
* Initialized as (0, 0, 0) so it is always not-null.
|
||||||
|
*/
|
||||||
|
@Setter
|
||||||
|
private Vector3f lastInteractionPlayerPosition = Vector3f.ZERO;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private Entity ridingVehicleEntity;
|
private Entity ridingVehicleEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entity that the client is currently looking at.
|
||||||
|
*/
|
||||||
@Setter
|
@Setter
|
||||||
private int craftSlot = 0;
|
private Entity mouseoverEntity;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private long lastWindowCloseTime = 0;
|
private Int2ObjectMap<Recipe> craftingRecipes;
|
||||||
|
private final Set<String> unlockedRecipes;
|
||||||
@Setter
|
private final AtomicInteger lastRecipeNetId;
|
||||||
private VillagerTrade[] villagerTrades;
|
|
||||||
@Setter
|
|
||||||
private long lastInteractedVillagerEid;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the enchantment information the client has received if they are in an enchantment table GUI
|
* Saves a list of all stonecutter recipes, for use in a stonecutter inventory.
|
||||||
|
* The key is the Java ID of the item; the values are all the possible outputs' Java IDs sorted by their string identifier
|
||||||
*/
|
*/
|
||||||
private final EnchantmentInventoryTranslator.EnchantmentSlotData[] enchantmentSlotData = new EnchantmentInventoryTranslator.EnchantmentSlotData[3];
|
@Setter
|
||||||
|
private Int2ObjectMap<IntList> stonecutterRecipes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current attack speed of the player. Used for sending proper cooldown timings.
|
* The current attack speed of the player. Used for sending proper cooldown timings.
|
||||||
@ -350,35 +415,43 @@ public class GeyserSession implements CommandSender {
|
|||||||
this.chunkCache = new ChunkCache(this);
|
this.chunkCache = new ChunkCache(this);
|
||||||
this.entityCache = new EntityCache(this);
|
this.entityCache = new EntityCache(this);
|
||||||
this.effectCache = new EntityEffectCache();
|
this.effectCache = new EntityEffectCache();
|
||||||
this.inventoryCache = new InventoryCache(this);
|
|
||||||
this.worldCache = new WorldCache(this);
|
this.worldCache = new WorldCache(this);
|
||||||
this.formCache = new FormCache(this);
|
this.formCache = new FormCache(this);
|
||||||
|
|
||||||
this.collisionManager = new CollisionManager(this);
|
this.collisionManager = new CollisionManager(this);
|
||||||
|
|
||||||
this.playerEntity = new SessionPlayerEntity(this);
|
this.playerEntity = new SessionPlayerEntity(this);
|
||||||
this.inventory = new PlayerInventory();
|
|
||||||
|
this.playerInventory = new PlayerInventory();
|
||||||
|
this.openInventory = null;
|
||||||
|
this.inventoryFuture = CompletableFuture.completedFuture(null);
|
||||||
|
this.craftingRecipes = new Int2ObjectOpenHashMap<>();
|
||||||
|
this.unlockedRecipes = new ObjectOpenHashSet<>();
|
||||||
|
this.lastRecipeNetId = new AtomicInteger(1);
|
||||||
|
|
||||||
this.spawned = false;
|
this.spawned = false;
|
||||||
this.loggedIn = false;
|
this.loggedIn = false;
|
||||||
|
|
||||||
this.inventoryCache.getInventories().put(0, inventory);
|
|
||||||
|
|
||||||
// Make a copy to prevent ConcurrentModificationException
|
// Make a copy to prevent ConcurrentModificationException
|
||||||
final List<GeyserSession> tmpPlayers = new ArrayList<>(connector.getPlayers());
|
final List<GeyserSession> tmpPlayers = new ArrayList<>(connector.getPlayers());
|
||||||
tmpPlayers.forEach(player -> this.emotes.addAll(player.getEmotes()));
|
tmpPlayers.forEach(player -> this.emotes.addAll(player.getEmotes()));
|
||||||
|
|
||||||
bedrockServerSession.addDisconnectHandler(disconnectReason -> {
|
bedrockServerSession.addDisconnectHandler(disconnectReason -> {
|
||||||
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.disconnect", bedrockServerSession.getAddress().getAddress(), disconnectReason));
|
InetAddress address = bedrockServerSession.getRealAddress().getAddress();
|
||||||
|
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.disconnect", address, disconnectReason));
|
||||||
|
|
||||||
disconnect(disconnectReason.name());
|
disconnect(disconnectReason.name());
|
||||||
connector.removePlayer(this);
|
connector.removePlayer(this);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(RemoteServer remoteServer) {
|
/**
|
||||||
|
* Send all necessary packets to load Bedrock into the server
|
||||||
|
*/
|
||||||
|
public void connect() {
|
||||||
startGame();
|
startGame();
|
||||||
this.remoteServer = remoteServer;
|
this.remoteAddress = connector.getConfig().getRemote().getAddress();
|
||||||
|
this.remotePort = connector.getConfig().getRemote().getPort();
|
||||||
|
this.remoteAuthType = connector.getDefaultAuthType();
|
||||||
|
|
||||||
// Set the hardcoded shield ID to the ID we just defined in StartGamePacket
|
// Set the hardcoded shield ID to the ID we just defined in StartGamePacket
|
||||||
upstream.getSession().getHardcodedBlockingId().set(ItemRegistry.SHIELD.getBedrockId());
|
upstream.getSession().getHardcodedBlockingId().set(ItemRegistry.SHIELD.getBedrockId());
|
||||||
@ -423,8 +496,8 @@ public class GeyserSession implements CommandSender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void login() {
|
public void login() {
|
||||||
if (connector.getAuthType() != AuthType.ONLINE) {
|
if (this.remoteAuthType != AuthType.ONLINE) {
|
||||||
if (connector.getAuthType() == AuthType.OFFLINE) {
|
if (this.remoteAuthType == AuthType.OFFLINE) {
|
||||||
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.auth.login.offline"));
|
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.auth.login.offline"));
|
||||||
} else {
|
} else {
|
||||||
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.auth.login.floodgate"));
|
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.auth.login.floodgate"));
|
||||||
@ -533,18 +606,21 @@ public class GeyserSession implements CommandSender {
|
|||||||
* After getting whatever credentials needed, we attempt to join the Java server.
|
* After getting whatever credentials needed, we attempt to join the Java server.
|
||||||
*/
|
*/
|
||||||
private void connectDownstream() {
|
private void connectDownstream() {
|
||||||
boolean floodgate = connector.getAuthType() == AuthType.FLOODGATE;
|
boolean floodgate = this.remoteAuthType == AuthType.FLOODGATE;
|
||||||
|
|
||||||
// Start ticking
|
// Start ticking
|
||||||
tickThread = connector.getGeneralThreadPool().scheduleAtFixedRate(this::tick, 50, 50, TimeUnit.MILLISECONDS);
|
tickThread = connector.getGeneralThreadPool().scheduleAtFixedRate(this::tick, 50, 50, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
downstream = new Client(remoteServer.getAddress(), remoteServer.getPort(), protocol, new TcpSessionFactory());
|
downstream = new Client(this.remoteAddress, this.remotePort, protocol, new TcpSessionFactory());
|
||||||
|
disableSrvResolving();
|
||||||
if (connector.getConfig().getRemote().isUseProxyProtocol()) {
|
if (connector.getConfig().getRemote().isUseProxyProtocol()) {
|
||||||
downstream.getSession().setFlag(BuiltinFlags.ENABLE_CLIENT_PROXY_PROTOCOL, true);
|
downstream.getSession().setFlag(BuiltinFlags.ENABLE_CLIENT_PROXY_PROTOCOL, true);
|
||||||
downstream.getSession().setFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS, upstream.getAddress());
|
downstream.getSession().setFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS, upstream.getAddress());
|
||||||
}
|
}
|
||||||
|
if (connector.getConfig().isForwardPlayerPing()) {
|
||||||
// Let Geyser handle sending the keep alive
|
// Let Geyser handle sending the keep alive
|
||||||
downstream.getSession().setFlag(MinecraftConstants.AUTOMATIC_KEEP_ALIVE_MANAGEMENT, false);
|
downstream.getSession().setFlag(MinecraftConstants.AUTOMATIC_KEEP_ALIVE_MANAGEMENT, false);
|
||||||
|
}
|
||||||
downstream.getSession().addListener(new SessionAdapter() {
|
downstream.getSession().addListener(new SessionAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void packetSending(PacketSendingEvent event) {
|
public void packetSending(PacketSendingEvent event) {
|
||||||
@ -553,7 +629,9 @@ public class GeyserSession implements CommandSender {
|
|||||||
byte[] encryptedData;
|
byte[] encryptedData;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
FloodgateSkinUploader skinUploader = connector.getSkinUploader();
|
||||||
FloodgateCipher cipher = connector.getCipher();
|
FloodgateCipher cipher = connector.getCipher();
|
||||||
|
|
||||||
encryptedData = cipher.encryptFromString(BedrockData.of(
|
encryptedData = cipher.encryptFromString(BedrockData.of(
|
||||||
clientData.getGameVersion(),
|
clientData.getGameVersion(),
|
||||||
authData.getName(),
|
authData.getName(),
|
||||||
@ -562,7 +640,9 @@ public class GeyserSession implements CommandSender {
|
|||||||
clientData.getLanguageCode(),
|
clientData.getLanguageCode(),
|
||||||
clientData.getUiProfile().ordinal(),
|
clientData.getUiProfile().ordinal(),
|
||||||
clientData.getCurrentInputMode().ordinal(),
|
clientData.getCurrentInputMode().ordinal(),
|
||||||
upstream.getSession().getAddress().getAddress().getHostAddress()
|
upstream.getAddress().getAddress().getHostAddress(),
|
||||||
|
skinUploader.getId(),
|
||||||
|
skinUploader.getVerifyCode()
|
||||||
).toString());
|
).toString());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.encrypt_fail"), e);
|
connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.encrypt_fail"), e);
|
||||||
@ -570,13 +650,7 @@ public class GeyserSession implements CommandSender {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] rawSkin = clientData.getAndTransformImage("Skin").encode();
|
String finalDataString = new String(encryptedData, StandardCharsets.UTF_8);
|
||||||
byte[] finalData = new byte[encryptedData.length + rawSkin.length + 1];
|
|
||||||
System.arraycopy(encryptedData, 0, finalData, 0, encryptedData.length);
|
|
||||||
finalData[encryptedData.length] = 0x21; // splitter
|
|
||||||
System.arraycopy(rawSkin, 0, finalData, encryptedData.length + 1, rawSkin.length);
|
|
||||||
|
|
||||||
String finalDataString = new String(finalData, StandardCharsets.UTF_8);
|
|
||||||
|
|
||||||
HandshakePacket handshakePacket = event.getPacket();
|
HandshakePacket handshakePacket = event.getPacket();
|
||||||
event.setPacket(new HandshakePacket(
|
event.setPacket(new HandshakePacket(
|
||||||
@ -597,7 +671,7 @@ public class GeyserSession implements CommandSender {
|
|||||||
disconnect(LanguageUtils.getPlayerLocaleString("geyser.network.remote.invalid_account", clientData.getLanguageCode()));
|
disconnect(LanguageUtils.getPlayerLocaleString("geyser.network.remote.invalid_account", clientData.getLanguageCode()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.remote.connect", authData.getName(), protocol.getProfile().getName(), remoteServer.getAddress()));
|
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.remote.connect", authData.getName(), protocol.getProfile().getName(), remoteAddress));
|
||||||
playerEntity.setUuid(protocol.getProfile().getId());
|
playerEntity.setUuid(protocol.getProfile().getId());
|
||||||
playerEntity.setUsername(protocol.getProfile().getName());
|
playerEntity.setUsername(protocol.getProfile().getName());
|
||||||
|
|
||||||
@ -618,7 +692,7 @@ public class GeyserSession implements CommandSender {
|
|||||||
public void disconnected(DisconnectedEvent event) {
|
public void disconnected(DisconnectedEvent event) {
|
||||||
loggingIn = false;
|
loggingIn = false;
|
||||||
loggedIn = false;
|
loggedIn = false;
|
||||||
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.remote.disconnect", authData.getName(), remoteServer.getAddress(), event.getReason()));
|
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.remote.disconnect", authData.getName(), remoteAddress, event.getReason()));
|
||||||
if (event.getCause() != null) {
|
if (event.getCause() != null) {
|
||||||
event.getCause().printStackTrace();
|
event.getCause().printStackTrace();
|
||||||
}
|
}
|
||||||
@ -636,9 +710,13 @@ public class GeyserSession implements CommandSender {
|
|||||||
playerEntity.setUuid(profile.getId());
|
playerEntity.setUuid(profile.getId());
|
||||||
|
|
||||||
// Check if they are not using a linked account
|
// Check if they are not using a linked account
|
||||||
if (connector.getAuthType() == AuthType.OFFLINE || playerEntity.getUuid().getMostSignificantBits() == 0) {
|
if (remoteAuthType == AuthType.OFFLINE || playerEntity.getUuid().getMostSignificantBits() == 0) {
|
||||||
SkinManager.handleBedrockSkin(playerEntity, clientData);
|
SkinManager.handleBedrockSkin(playerEntity, clientData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We'll send the skin upload a bit after the handshake packet (aka this packet),
|
||||||
|
// because otherwise the global server returns the data too fast.
|
||||||
|
getAuthData().upload(connector);
|
||||||
}
|
}
|
||||||
|
|
||||||
PacketTranslatorRegistry.JAVA_TRANSLATOR.translate(event.getPacket().getClass(), event.getPacket(), GeyserSession.this);
|
PacketTranslatorRegistry.JAVA_TRANSLATOR.translate(event.getPacket().getClass(), event.getPacket(), GeyserSession.this);
|
||||||
@ -683,7 +761,6 @@ public class GeyserSession implements CommandSender {
|
|||||||
this.entityCache = null;
|
this.entityCache = null;
|
||||||
this.effectCache = null;
|
this.effectCache = null;
|
||||||
this.worldCache = null;
|
this.worldCache = null;
|
||||||
this.inventoryCache = null;
|
|
||||||
this.formCache = null;
|
this.formCache = null;
|
||||||
|
|
||||||
closed = true;
|
closed = true;
|
||||||
@ -723,6 +800,18 @@ public class GeyserSession implements CommandSender {
|
|||||||
this.sneaking = sneaking;
|
this.sneaking = sneaking;
|
||||||
collisionManager.updatePlayerBoundingBox();
|
collisionManager.updatePlayerBoundingBox();
|
||||||
collisionManager.updateScaffoldingFlags();
|
collisionManager.updateScaffoldingFlags();
|
||||||
|
|
||||||
|
if (mouseoverEntity != null) {
|
||||||
|
// Horses, etc can change their property depending on if you're sneaking
|
||||||
|
InteractiveTagManager.updateTag(this, mouseoverEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will be overwritten for GeyserConnect.
|
||||||
|
*/
|
||||||
|
protected void disableSrvResolving() {
|
||||||
|
this.downstream.getSession().setFlag(BuiltinFlags.ATTEMPT_SRV_RESOLVE, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -823,10 +912,58 @@ public class GeyserSession implements CommandSender {
|
|||||||
startGamePacket.setMultiplayerCorrelationId("");
|
startGamePacket.setMultiplayerCorrelationId("");
|
||||||
startGamePacket.setItemEntries(ItemRegistry.ITEMS);
|
startGamePacket.setItemEntries(ItemRegistry.ITEMS);
|
||||||
startGamePacket.setVanillaVersion("*");
|
startGamePacket.setVanillaVersion("*");
|
||||||
startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.CLIENT);
|
startGamePacket.setInventoriesServerAuthoritative(true);
|
||||||
|
startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.CLIENT); // can be removed once 1.16.200 support is dropped
|
||||||
|
|
||||||
|
SyncedPlayerMovementSettings settings = new SyncedPlayerMovementSettings();
|
||||||
|
settings.setMovementMode(AuthoritativeMovementMode.CLIENT);
|
||||||
|
settings.setRewindHistorySize(0);
|
||||||
|
settings.setServerAuthoritativeBlockBreaking(false);
|
||||||
|
startGamePacket.setPlayerMovementSettings(settings);
|
||||||
|
|
||||||
upstream.sendPacket(startGamePacket);
|
upstream.sendPacket(startGamePacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new inventory task.
|
||||||
|
* Inventory tasks are executed one at a time, in order.
|
||||||
|
*
|
||||||
|
* @param task the task to run
|
||||||
|
*/
|
||||||
|
public void addInventoryTask(Runnable task) {
|
||||||
|
synchronized (inventoryLock) {
|
||||||
|
inventoryFuture = inventoryFuture.thenRun(task).exceptionally(throwable -> {
|
||||||
|
GeyserConnector.getInstance().getLogger().error("Error processing inventory task", throwable.getCause());
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new inventory task with a delay.
|
||||||
|
* The delay is achieved by scheduling with the Geyser general thread pool.
|
||||||
|
* Inventory tasks are executed one at a time, in order.
|
||||||
|
*
|
||||||
|
* @param task the delayed task to run
|
||||||
|
* @param delayMillis delay in milliseconds
|
||||||
|
*/
|
||||||
|
public void addInventoryTask(Runnable task, long delayMillis) {
|
||||||
|
synchronized (inventoryLock) {
|
||||||
|
Executor delayedExecutor = command -> GeyserConnector.getInstance().getGeneralThreadPool().schedule(command, delayMillis, TimeUnit.MILLISECONDS);
|
||||||
|
inventoryFuture = inventoryFuture.thenRunAsync(task, delayedExecutor).exceptionally(throwable -> {
|
||||||
|
GeyserConnector.getInstance().getLogger().error("Error processing inventory task", throwable.getCause());
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the next Bedrock item network ID to use for a new item
|
||||||
|
*/
|
||||||
|
public int getNextItemNetId() {
|
||||||
|
return itemNetId.getAndIncrement();
|
||||||
|
}
|
||||||
|
|
||||||
public void addTeleport(TeleportCache teleportCache) {
|
public void addTeleport(TeleportCache teleportCache) {
|
||||||
teleportMap.put(teleportCache.getTeleportConfirmId(), teleportCache);
|
teleportMap.put(teleportCache.getTeleportConfirmId(), teleportCache);
|
||||||
|
|
||||||
@ -931,7 +1068,7 @@ public class GeyserSession implements CommandSender {
|
|||||||
* @param packet the java edition packet from MCProtocolLib
|
* @param packet the java edition packet from MCProtocolLib
|
||||||
*/
|
*/
|
||||||
public void sendDownstreamPacket(Packet packet) {
|
public void sendDownstreamPacket(Packet packet) {
|
||||||
if (downstream != null && downstream.getSession() != null && protocol.getSubProtocol().equals(SubProtocol.GAME)) {
|
if (downstream != null && downstream.getSession() != null && (protocol.getSubProtocol().equals(SubProtocol.GAME) || packet.getClass() == LoginPluginResponsePacket.class)) {
|
||||||
downstream.getSession().send(packet);
|
downstream.getSession().send(packet);
|
||||||
} else {
|
} else {
|
||||||
connector.getLogger().debug("Tried to send downstream packet " + packet.getClass().getSimpleName() + " before connected to the server");
|
connector.getLogger().debug("Tried to send downstream packet " + packet.getClass().getSimpleName() + " before connected to the server");
|
||||||
|
@ -61,6 +61,6 @@ public class UpstreamSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public InetSocketAddress getAddress() {
|
public InetSocketAddress getAddress() {
|
||||||
return session.getAddress();
|
return session.getRealAddress();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,16 +25,26 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.network.session.auth;
|
package org.geysermc.connector.network.session.auth;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Getter
|
@RequiredArgsConstructor
|
||||||
@AllArgsConstructor
|
|
||||||
public class AuthData {
|
public class AuthData {
|
||||||
|
@Getter private final String name;
|
||||||
|
@Getter private final UUID UUID;
|
||||||
|
@Getter private final String xboxUUID;
|
||||||
|
|
||||||
private String name;
|
private final JsonNode certChainData;
|
||||||
private UUID UUID;
|
private final String clientData;
|
||||||
private String xboxUUID;
|
|
||||||
|
public void upload(GeyserConnector connector) {
|
||||||
|
// we can't upload the skin in LoginEncryptionUtil since the global server would return
|
||||||
|
// the skin too fast, that's why we upload it after we know for sure that the target server
|
||||||
|
// is ready to handle the result of the global server
|
||||||
|
connector.getSkinUploader().uploadSkin(certChainData, clientData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,10 +34,8 @@ import lombok.Getter;
|
|||||||
import org.geysermc.connector.skin.SkinProvider;
|
import org.geysermc.connector.skin.SkinProvider;
|
||||||
import org.geysermc.floodgate.util.DeviceOs;
|
import org.geysermc.floodgate.util.DeviceOs;
|
||||||
import org.geysermc.floodgate.util.InputMode;
|
import org.geysermc.floodgate.util.InputMode;
|
||||||
import org.geysermc.floodgate.util.RawSkin;
|
|
||||||
import org.geysermc.floodgate.util.UiProfile;
|
import org.geysermc.floodgate.util.UiProfile;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ -114,26 +112,8 @@ public final class BedrockClientData {
|
|||||||
private String skinColor;
|
private String skinColor;
|
||||||
@JsonProperty(value = "ThirdPartyNameOnly")
|
@JsonProperty(value = "ThirdPartyNameOnly")
|
||||||
private boolean thirdPartyNameOnly;
|
private boolean thirdPartyNameOnly;
|
||||||
|
@JsonProperty(value = "PlayFabId")
|
||||||
private static RawSkin getLegacyImage(byte[] imageData, boolean alex) {
|
private String playFabId;
|
||||||
if (imageData == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// width * height * 4 (rgba)
|
|
||||||
switch (imageData.length) {
|
|
||||||
case 8192:
|
|
||||||
return new RawSkin(64, 32, imageData, alex);
|
|
||||||
case 16384:
|
|
||||||
return new RawSkin(64, 64, imageData, alex);
|
|
||||||
case 32768:
|
|
||||||
return new RawSkin(64, 128, imageData, alex);
|
|
||||||
case 65536:
|
|
||||||
return new RawSkin(128, 128, imageData, alex);
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unknown legacy skin size");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setJsonData(JsonNode data) {
|
public void setJsonData(JsonNode data) {
|
||||||
if (this.jsonData == null && data != null) {
|
if (this.jsonData == null && data != null) {
|
||||||
@ -141,51 +121,6 @@ public final class BedrockClientData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Taken from https://github.com/NukkitX/Nukkit/blob/master/src/main/java/cn/nukkit/network/protocol/LoginPacket.java<br>
|
|
||||||
* Internally only used for Skins, but can be used for Capes too
|
|
||||||
*/
|
|
||||||
public RawSkin getImage(String name) {
|
|
||||||
if (jsonData == null || !jsonData.has(name + "Data")) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean alex = false;
|
|
||||||
if (name.equals("Skin")) {
|
|
||||||
alex = isAlex();
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] image = Base64.getDecoder().decode(jsonData.get(name + "Data").asText());
|
|
||||||
if (jsonData.has(name + "ImageWidth") && jsonData.has(name + "ImageHeight")) {
|
|
||||||
return new RawSkin(
|
|
||||||
jsonData.get(name + "ImageWidth").asInt(),
|
|
||||||
jsonData.get(name + "ImageHeight").asInt(),
|
|
||||||
image, alex
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return getLegacyImage(image, alex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RawSkin getAndTransformImage(String name) {
|
|
||||||
RawSkin skin = getImage(name);
|
|
||||||
if (skin != null && (skin.width > 64 || skin.height > 64)) {
|
|
||||||
BufferedImage scaledImage =
|
|
||||||
SkinProvider.imageDataToBufferedImage(skin.data, skin.width, skin.height);
|
|
||||||
|
|
||||||
int max = Math.max(skin.width, skin.height);
|
|
||||||
while (max > 64) {
|
|
||||||
max /= 2;
|
|
||||||
scaledImage = SkinProvider.scale(scaledImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] skinData = SkinProvider.bufferedImageToImageData(scaledImage);
|
|
||||||
skin.width = scaledImage.getWidth();
|
|
||||||
skin.height = scaledImage.getHeight();
|
|
||||||
skin.data = skinData;
|
|
||||||
}
|
|
||||||
return skin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAlex() {
|
public boolean isAlex() {
|
||||||
try {
|
try {
|
||||||
byte[] bytes = Base64.getDecoder().decode(geometryName.getBytes(Charsets.UTF_8));
|
byte[] bytes = Base64.getDecoder().decode(geometryName.getBytes(Charsets.UTF_8));
|
||||||
|
@ -25,9 +25,9 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.network.session.cache;
|
package org.geysermc.connector.network.session.cache;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientEditBookPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientEditBookPacket;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.geysermc.connector.inventory.GeyserItemStack;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||||
|
|
||||||
@ -63,8 +63,8 @@ public class BookEditCache {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Don't send the update if the player isn't not holding a book, shouldn't happen if we catch all interactions
|
// Don't send the update if the player isn't not holding a book, shouldn't happen if we catch all interactions
|
||||||
ItemStack itemStack = session.getInventory().getItemInHand();
|
GeyserItemStack itemStack = session.getPlayerInventory().getItemInHand();
|
||||||
if (itemStack == null || itemStack.getId() != ItemRegistry.WRITABLE_BOOK.getJavaId()) {
|
if (itemStack == null || itemStack.getJavaId() != ItemRegistry.WRITABLE_BOOK.getJavaId()) {
|
||||||
packet = null;
|
packet = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -29,23 +29,24 @@ import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
|
|||||||
import com.github.steveice10.mc.protocol.data.game.chunk.Column;
|
import com.github.steveice10.mc.protocol.data.game.chunk.Column;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
import org.geysermc.connector.utils.MathUtils;
|
import org.geysermc.connector.utils.MathUtils;
|
||||||
|
|
||||||
public class ChunkCache {
|
public class ChunkCache {
|
||||||
|
private static final int MINIMUM_WORLD_HEIGHT = 0;
|
||||||
|
|
||||||
private final boolean cache;
|
private final boolean cache;
|
||||||
|
|
||||||
private final Long2ObjectMap<Column> chunks = new Long2ObjectOpenHashMap<>();
|
private final Long2ObjectMap<Column> chunks;
|
||||||
|
|
||||||
public ChunkCache(GeyserSession session) {
|
public ChunkCache(GeyserSession session) {
|
||||||
if (session.getConnector().getWorldManager().getClass() == GeyserBootstrap.DEFAULT_CHUNK_MANAGER.getClass()) {
|
if (session.getConnector().getWorldManager().hasOwnChunkCache()) {
|
||||||
this.cache = session.getConnector().getConfig().isCacheChunks();
|
|
||||||
} else {
|
|
||||||
this.cache = false; // To prevent Spigot from initializing
|
this.cache = false; // To prevent Spigot from initializing
|
||||||
|
} else {
|
||||||
|
this.cache = session.getConnector().getConfig().isCacheChunks();
|
||||||
}
|
}
|
||||||
|
chunks = cache ? new Long2ObjectOpenHashMap<>() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Column addToCache(Column chunk) {
|
public Column addToCache(Column chunk) {
|
||||||
@ -86,6 +87,11 @@ public class ChunkCache {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (y < MINIMUM_WORLD_HEIGHT || (y >> 4) > column.getChunks().length - 1) {
|
||||||
|
// Y likely goes above or below the height limit of this world
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Chunk chunk = column.getChunks()[y >> 4];
|
Chunk chunk = column.getChunks()[y >> 4];
|
||||||
if (chunk != null) {
|
if (chunk != null) {
|
||||||
chunk.set(x & 0xF, y & 0xF, z & 0xF, block);
|
chunk.set(x & 0xF, y & 0xF, z & 0xF, block);
|
||||||
@ -102,6 +108,11 @@ public class ChunkCache {
|
|||||||
return BlockTranslator.JAVA_AIR_ID;
|
return BlockTranslator.JAVA_AIR_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (y < MINIMUM_WORLD_HEIGHT || (y >> 4) > column.getChunks().length - 1) {
|
||||||
|
// Y likely goes above or below the height limit of this world
|
||||||
|
return BlockTranslator.JAVA_AIR_ID;
|
||||||
|
}
|
||||||
|
|
||||||
Chunk chunk = column.getChunks()[y >> 4];
|
Chunk chunk = column.getChunks()[y >> 4];
|
||||||
if (chunk != null) {
|
if (chunk != null) {
|
||||||
return chunk.get(x & 0xF, y & 0xF, z & 0xF);
|
return chunk.get(x & 0xF, y & 0xF, z & 0xF);
|
||||||
|
@ -38,7 +38,7 @@ import java.io.InputStream;
|
|||||||
*/
|
*/
|
||||||
public class EntityIdentifierRegistry {
|
public class EntityIdentifierRegistry {
|
||||||
|
|
||||||
public static NbtMap ENTITY_IDENTIFIERS;
|
public static final NbtMap ENTITY_IDENTIFIERS;
|
||||||
|
|
||||||
private EntityIdentifierRegistry() {
|
private EntityIdentifierRegistry() {
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdate
|
|||||||
import com.github.steveice10.packetlib.packet.Packet;
|
import com.github.steveice10.packetlib.packet.Packet;
|
||||||
import com.nukkitx.protocol.bedrock.BedrockPacket;
|
import com.nukkitx.protocol.bedrock.BedrockPacket;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
|
import org.geysermc.common.PlatformType;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.utils.FileUtils;
|
import org.geysermc.connector.utils.FileUtils;
|
||||||
@ -89,13 +90,16 @@ public class PacketTranslatorRegistry<T> {
|
|||||||
public <P extends T> boolean translate(Class<? extends P> clazz, P packet, GeyserSession session) {
|
public <P extends T> boolean translate(Class<? extends P> clazz, P packet, GeyserSession session) {
|
||||||
if (!session.getUpstream().isClosed() && !session.isClosed()) {
|
if (!session.getUpstream().isClosed() && !session.isClosed()) {
|
||||||
try {
|
try {
|
||||||
if (translators.containsKey(clazz)) {
|
PacketTranslator<P> translator = (PacketTranslator<P>) translators.get(clazz);
|
||||||
((PacketTranslator<P>) translators.get(clazz)).translate(packet, session);
|
if (translator != null) {
|
||||||
|
translator.translate(packet, session);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if (!IGNORED_PACKETS.contains(clazz))
|
if ((GeyserConnector.getInstance().getPlatformType() != PlatformType.STANDALONE || !(packet instanceof BedrockPacket)) && !IGNORED_PACKETS.contains(clazz)) {
|
||||||
|
// Other debug logs already take care of Bedrock packets for us if on standalone
|
||||||
GeyserConnector.getInstance().getLogger().debug("Could not find packet for " + (packet.toString().length() > 25 ? packet.getClass().getSimpleName() : packet));
|
GeyserConnector.getInstance().getLogger().debug("Could not find packet for " + (packet.toString().length() > 25 ? packet.getClass().getSimpleName() : packet));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.packet.failed", packet.getClass().getSimpleName()), ex);
|
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.packet.failed", packet.getClass().getSimpleName()), ex);
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
|
@ -26,17 +26,16 @@
|
|||||||
package org.geysermc.connector.network.translators.bedrock;
|
package org.geysermc.connector.network.translators.bedrock;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientEditBookPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientEditBookPacket;
|
||||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||||
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||||
import com.nukkitx.protocol.bedrock.packet.BookEditPacket;
|
import com.nukkitx.protocol.bedrock.packet.BookEditPacket;
|
||||||
|
import org.geysermc.connector.inventory.GeyserItemStack;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@ -47,15 +46,13 @@ public class BedrockBookEditTranslator extends PacketTranslator<BookEditPacket>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(BookEditPacket packet, GeyserSession session) {
|
public void translate(BookEditPacket packet, GeyserSession session) {
|
||||||
ItemStack itemStack = session.getInventory().getItemInHand();
|
GeyserItemStack itemStack = session.getPlayerInventory().getItemInHand();
|
||||||
if (itemStack != null) {
|
if (itemStack != null) {
|
||||||
CompoundTag tag = itemStack.getNbt() != null ? itemStack.getNbt() : new CompoundTag("");
|
CompoundTag tag = itemStack.getNbt() != null ? itemStack.getNbt() : new CompoundTag("");
|
||||||
ItemStack bookItem = new ItemStack(itemStack.getId(), itemStack.getAmount(), tag);
|
ItemStack bookItem = new ItemStack(itemStack.getJavaId(), itemStack.getAmount(), tag);
|
||||||
List<Tag> pages = tag.contains("pages") ? new LinkedList<>(((ListTag) tag.get("pages")).getValue()) : new LinkedList<>();
|
List<Tag> pages = tag.contains("pages") ? new LinkedList<>(((ListTag) tag.get("pages")).getValue()) : new LinkedList<>();
|
||||||
|
|
||||||
int page = packet.getPageNumber();
|
int page = packet.getPageNumber();
|
||||||
// Creative edits the NBT for us
|
|
||||||
if (session.getGameMode() != GameMode.CREATIVE) {
|
|
||||||
switch (packet.getAction()) {
|
switch (packet.getAction()) {
|
||||||
case ADD_PAGE: {
|
case ADD_PAGE: {
|
||||||
// Add empty pages in between
|
// Add empty pages in between
|
||||||
@ -99,7 +96,6 @@ public class BedrockBookEditTranslator extends PacketTranslator<BookEditPacket>
|
|||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Remove empty pages at the end
|
// Remove empty pages at the end
|
||||||
while (pages.size() > 0) {
|
while (pages.size() > 0) {
|
||||||
StringTag currentPage = (StringTag) pages.get(pages.size() - 1);
|
StringTag currentPage = (StringTag) pages.get(pages.size() - 1);
|
||||||
@ -110,10 +106,10 @@ public class BedrockBookEditTranslator extends PacketTranslator<BookEditPacket>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tag.put(new ListTag("pages", pages));
|
tag.put(new ListTag("pages", pages));
|
||||||
session.getInventory().setItem(36 + session.getInventory().getHeldItemSlot(), bookItem);
|
session.getPlayerInventory().setItem(36 + session.getPlayerInventory().getHeldItemSlot(), GeyserItemStack.from(bookItem), session);
|
||||||
InventoryTranslator.INVENTORY_TRANSLATORS.get(null).updateInventory(session, session.getInventory());
|
session.getInventoryTranslator().updateInventory(session, session.getPlayerInventory());
|
||||||
|
|
||||||
session.getBookEditCache().setPacket(new ClientEditBookPacket(bookItem, packet.getAction() == BookEditPacket.Action.SIGN_BOOK, session.getInventory().getHeldItemSlot()));
|
session.getBookEditCache().setPacket(new ClientEditBookPacket(bookItem, packet.getAction() == BookEditPacket.Action.SIGN_BOOK, session.getPlayerInventory().getHeldItemSlot()));
|
||||||
// There won't be any more book updates after this, so we can try sending the edit packet immediately
|
// There won't be any more book updates after this, so we can try sending the edit packet immediately
|
||||||
if (packet.getAction() == BookEditPacket.Action.SIGN_BOOK) {
|
if (packet.getAction() == BookEditPacket.Action.SIGN_BOOK) {
|
||||||
session.getBookEditCache().checkForSend();
|
session.getBookEditCache().checkForSend();
|
||||||
|
@ -28,6 +28,7 @@ package org.geysermc.connector.network.translators.bedrock;
|
|||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
|
import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
|
import org.geysermc.connector.inventory.MerchantContainer;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
@ -38,24 +39,29 @@ public class BedrockContainerCloseTranslator extends PacketTranslator<ContainerC
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ContainerClosePacket packet, GeyserSession session) {
|
public void translate(ContainerClosePacket packet, GeyserSession session) {
|
||||||
session.setLastWindowCloseTime(0);
|
session.addInventoryTask(() -> {
|
||||||
byte windowId = packet.getId();
|
byte windowId = packet.getId();
|
||||||
Inventory openInventory = session.getInventoryCache().getOpenInventory();
|
|
||||||
if (windowId == -1) { //player inventory or crafting table
|
|
||||||
if (openInventory != null) {
|
|
||||||
windowId = (byte) openInventory.getId();
|
|
||||||
} else {
|
|
||||||
windowId = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (windowId == 0 || (openInventory != null && openInventory.getId() == windowId)) {
|
|
||||||
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId);
|
|
||||||
session.getDownstream().getSession().send(closeWindowPacket);
|
|
||||||
InventoryUtils.closeInventory(session, windowId);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Client wants close confirmation
|
//Client wants close confirmation
|
||||||
session.sendUpstreamPacket(packet);
|
session.sendUpstreamPacket(packet);
|
||||||
|
session.setClosingInventory(false);
|
||||||
|
|
||||||
|
if (windowId == -1 && session.getOpenInventory() instanceof MerchantContainer) {
|
||||||
|
// 1.16.200 - window ID is always -1 sent from Bedrock
|
||||||
|
windowId = (byte) session.getOpenInventory().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
Inventory openInventory = session.getOpenInventory();
|
||||||
|
if (openInventory != null) {
|
||||||
|
if (windowId == openInventory.getId()) {
|
||||||
|
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId);
|
||||||
|
session.sendDownstreamPacket(closeWindowPacket);
|
||||||
|
InventoryUtils.closeInventory(session, windowId, false);
|
||||||
|
} else if (openInventory.isPending()) {
|
||||||
|
InventoryUtils.displayInventory(session, openInventory);
|
||||||
|
openInventory.setPending(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,10 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.network.translators.bedrock;
|
package org.geysermc.connector.network.translators.bedrock;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientRenameItemPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.FilterTextPacket;
|
import com.nukkitx.protocol.bedrock.packet.FilterTextPacket;
|
||||||
|
import org.geysermc.connector.inventory.AnvilContainer;
|
||||||
|
import org.geysermc.connector.inventory.CartographyContainer;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
@ -39,7 +42,17 @@ public class BedrockFilterTextTranslator extends PacketTranslator<FilterTextPack
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(FilterTextPacket packet, GeyserSession session) {
|
public void translate(FilterTextPacket packet, GeyserSession session) {
|
||||||
|
if (session.getOpenInventory() instanceof CartographyContainer) {
|
||||||
|
// We don't want to be able to rename in the cartography table
|
||||||
|
return;
|
||||||
|
}
|
||||||
packet.setFromServer(true);
|
packet.setFromServer(true);
|
||||||
session.sendUpstreamPacket(packet);
|
session.sendUpstreamPacket(packet);
|
||||||
|
|
||||||
|
if (session.getOpenInventory() instanceof AnvilContainer) {
|
||||||
|
// Java Edition sends a packet every time an item is renamed even slightly in GUI. Fortunately, this works out for us now
|
||||||
|
ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(packet.getText());
|
||||||
|
session.sendDownstreamPacket(renameItemPacket);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.network.translators.bedrock;
|
package org.geysermc.connector.network.translators.bedrock;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||||
@ -43,23 +42,22 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
|||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlags;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityFlags;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
|
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.InventorySource;
|
||||||
import com.nukkitx.protocol.bedrock.packet.*;
|
import com.nukkitx.protocol.bedrock.packet.*;
|
||||||
import org.geysermc.connector.entity.CommandBlockMinecartEntity;
|
import org.geysermc.connector.entity.CommandBlockMinecartEntity;
|
||||||
import org.geysermc.connector.entity.Entity;
|
import org.geysermc.connector.entity.Entity;
|
||||||
import org.geysermc.connector.entity.ItemFrameEntity;
|
import org.geysermc.connector.entity.ItemFrameEntity;
|
||||||
import org.geysermc.connector.entity.living.merchant.AbstractMerchantEntity;
|
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.inventory.GeyserItemStack;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
|
||||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||||
import org.geysermc.connector.network.translators.sound.EntitySoundInteractionHandler;
|
import org.geysermc.connector.network.translators.sound.EntitySoundInteractionHandler;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
import org.geysermc.connector.utils.BlockUtils;
|
import org.geysermc.connector.utils.BlockUtils;
|
||||||
import org.geysermc.connector.utils.InventoryUtils;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@ -82,15 +80,35 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||||||
|
|
||||||
switch (packet.getTransactionType()) {
|
switch (packet.getTransactionType()) {
|
||||||
case NORMAL:
|
case NORMAL:
|
||||||
Inventory inventory = session.getInventoryCache().getOpenInventory();
|
if (packet.getActions().size() == 2) {
|
||||||
if (inventory == null) inventory = session.getInventory();
|
InventoryActionData worldAction = packet.getActions().get(0);
|
||||||
InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType()).translateActions(session, inventory, packet.getActions());
|
InventoryActionData containerAction = packet.getActions().get(1);
|
||||||
|
if (worldAction.getSource().getType() == InventorySource.Type.WORLD_INTERACTION
|
||||||
|
&& worldAction.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
|
||||||
|
session.addInventoryTask(() -> {
|
||||||
|
if (session.getPlayerInventory().getHeldItemSlot() != containerAction.getSlot() ||
|
||||||
|
session.getPlayerInventory().getItemInHand().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean dropAll = worldAction.getToItem().getCount() > 1;
|
||||||
|
ClientPlayerActionPacket dropAllPacket = new ClientPlayerActionPacket(
|
||||||
|
dropAll ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM,
|
||||||
|
BlockUtils.POSITION_ZERO,
|
||||||
|
BlockFace.DOWN
|
||||||
|
);
|
||||||
|
session.sendDownstreamPacket(dropAllPacket);
|
||||||
|
|
||||||
|
if (dropAll) {
|
||||||
|
session.getPlayerInventory().setItemInHand(GeyserItemStack.EMPTY);
|
||||||
|
} else {
|
||||||
|
session.getPlayerInventory().getItemInHand().sub(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case INVENTORY_MISMATCH:
|
case INVENTORY_MISMATCH:
|
||||||
Inventory inv = session.getInventoryCache().getOpenInventory();
|
|
||||||
if (inv == null) inv = session.getInventory();
|
|
||||||
InventoryTranslator.INVENTORY_TRANSLATORS.get(inv.getWindowType()).updateInventory(session, inv);
|
|
||||||
InventoryUtils.updateCursor(session);
|
|
||||||
break;
|
break;
|
||||||
case ITEM_USE:
|
case ITEM_USE:
|
||||||
switch (packet.getActionType()) {
|
switch (packet.getActionType()) {
|
||||||
@ -98,8 +116,9 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||||||
// Check to make sure the client isn't spamming interaction
|
// Check to make sure the client isn't spamming interaction
|
||||||
// Based on Nukkit 1.0, with changes to ensure holding down still works
|
// Based on Nukkit 1.0, with changes to ensure holding down still works
|
||||||
boolean hasAlreadyClicked = System.currentTimeMillis() - session.getLastInteractionTime() < 110.0 &&
|
boolean hasAlreadyClicked = System.currentTimeMillis() - session.getLastInteractionTime() < 110.0 &&
|
||||||
packet.getBlockPosition().distanceSquared(session.getLastInteractionPosition()) < 0.00001;
|
packet.getBlockPosition().distanceSquared(session.getLastInteractionBlockPosition()) < 0.00001;
|
||||||
session.setLastInteractionPosition(packet.getBlockPosition());
|
session.setLastInteractionBlockPosition(packet.getBlockPosition());
|
||||||
|
session.setLastInteractionPlayerPosition(session.getPlayerEntity().getPosition());
|
||||||
if (hasAlreadyClicked) {
|
if (hasAlreadyClicked) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
@ -108,7 +127,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bedrock sends block interact code for a Java entity so we send entity code back to Java
|
// Bedrock sends block interact code for a Java entity so we send entity code back to Java
|
||||||
if (BlockTranslator.isItemFrame(packet.getBlockRuntimeId()) &&
|
if (session.getBlockTranslator().isItemFrame(packet.getBlockRuntimeId()) &&
|
||||||
session.getEntityCache().getEntityByJavaId(ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition())) != null) {
|
session.getEntityCache().getEntityByJavaId(ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition())) != null) {
|
||||||
Vector3f vector = packet.getClickPosition();
|
Vector3f vector = packet.getClickPosition();
|
||||||
ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition()),
|
ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition()),
|
||||||
@ -192,7 +211,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||||||
if (packet.getActions().isEmpty()) {
|
if (packet.getActions().isEmpty()) {
|
||||||
if (session.getOpPermissionLevel() >= 2 && session.getGameMode() == GameMode.CREATIVE) {
|
if (session.getOpPermissionLevel() >= 2 && session.getGameMode() == GameMode.CREATIVE) {
|
||||||
// Otherwise insufficient permissions
|
// Otherwise insufficient permissions
|
||||||
int blockState = BlockTranslator.getJavaBlockState(packet.getBlockRuntimeId());
|
int blockState = session.getBlockTranslator().getJavaBlockState(packet.getBlockRuntimeId());
|
||||||
String blockName = BlockTranslator.getJavaIdBlockMap().inverse().getOrDefault(blockState, "");
|
String blockName = BlockTranslator.getJavaIdBlockMap().inverse().getOrDefault(blockState, "");
|
||||||
// In the future this can be used for structure blocks too, however not all elements
|
// In the future this can be used for structure blocks too, however not all elements
|
||||||
// are available in each GUI
|
// are available in each GUI
|
||||||
@ -215,9 +234,8 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||||||
session.setInteracting(true);
|
session.setInteracting(true);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
ItemStack shieldSlot = session.getInventory().getItem(session.getInventory().getHeldItemSlot() + 36);
|
|
||||||
// Handled in Entity.java
|
// Handled in Entity.java
|
||||||
if (shieldSlot != null && shieldSlot.getId() == ItemRegistry.SHIELD.getJavaId()) {
|
if (session.getPlayerInventory().getItemInHand().getJavaId() == ItemRegistry.SHIELD.getJavaId()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,7 +271,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||||||
LevelEventPacket blockBreakPacket = new LevelEventPacket();
|
LevelEventPacket blockBreakPacket = new LevelEventPacket();
|
||||||
blockBreakPacket.setType(LevelEventType.PARTICLE_DESTROY_BLOCK);
|
blockBreakPacket.setType(LevelEventType.PARTICLE_DESTROY_BLOCK);
|
||||||
blockBreakPacket.setPosition(packet.getBlockPosition().toFloat());
|
blockBreakPacket.setPosition(packet.getBlockPosition().toFloat());
|
||||||
blockBreakPacket.setData(BlockTranslator.getBedrockBlockId(blockState));
|
blockBreakPacket.setData(session.getBlockTranslator().getBedrockBlockId(blockState));
|
||||||
session.sendUpstreamPacket(blockBreakPacket);
|
session.sendUpstreamPacket(blockBreakPacket);
|
||||||
session.setBreakingBlock(BlockTranslator.JAVA_AIR_ID);
|
session.setBreakingBlock(BlockTranslator.JAVA_AIR_ID);
|
||||||
|
|
||||||
@ -274,7 +292,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||||||
case ITEM_RELEASE:
|
case ITEM_RELEASE:
|
||||||
if (packet.getActionType() == 0) {
|
if (packet.getActionType() == 0) {
|
||||||
// Followed to the Minecraft Protocol specification outlined at wiki.vg
|
// Followed to the Minecraft Protocol specification outlined at wiki.vg
|
||||||
ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, new Position(0,0,0),
|
ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, BlockUtils.POSITION_ZERO,
|
||||||
BlockFace.DOWN);
|
BlockFace.DOWN);
|
||||||
session.sendDownstreamPacket(releaseItemPacket);
|
session.sendDownstreamPacket(releaseItemPacket);
|
||||||
}
|
}
|
||||||
@ -308,10 +326,6 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||||||
session.sendDownstreamPacket(interactAtPacket);
|
session.sendDownstreamPacket(interactAtPacket);
|
||||||
|
|
||||||
EntitySoundInteractionHandler.handleEntityInteraction(session, vector, entity);
|
EntitySoundInteractionHandler.handleEntityInteraction(session, vector, entity);
|
||||||
|
|
||||||
if (entity instanceof AbstractMerchantEntity) {
|
|
||||||
session.setLastInteractedVillagerEid(packet.getRuntimeEntityId());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 1: //Attack
|
case 1: //Attack
|
||||||
if (entity.getEntityType() == EntityType.ENDER_DRAGON) {
|
if (entity.getEntityType() == EntityType.ENDER_DRAGON) {
|
||||||
@ -342,14 +356,14 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||||
updateBlockPacket.setDataLayer(0);
|
updateBlockPacket.setDataLayer(0);
|
||||||
updateBlockPacket.setBlockPosition(blockPos);
|
updateBlockPacket.setBlockPosition(blockPos);
|
||||||
updateBlockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(javaBlockState));
|
updateBlockPacket.setRuntimeId(session.getBlockTranslator().getBedrockBlockId(javaBlockState));
|
||||||
updateBlockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
|
updateBlockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
|
||||||
session.sendUpstreamPacket(updateBlockPacket);
|
session.sendUpstreamPacket(updateBlockPacket);
|
||||||
|
|
||||||
UpdateBlockPacket updateWaterPacket = new UpdateBlockPacket();
|
UpdateBlockPacket updateWaterPacket = new UpdateBlockPacket();
|
||||||
updateWaterPacket.setDataLayer(1);
|
updateWaterPacket.setDataLayer(1);
|
||||||
updateWaterPacket.setBlockPosition(blockPos);
|
updateWaterPacket.setBlockPosition(blockPos);
|
||||||
updateWaterPacket.setRuntimeId(BlockTranslator.isWaterlogged(javaBlockState) ? BlockTranslator.BEDROCK_WATER_ID : BlockTranslator.BEDROCK_AIR_ID);
|
updateWaterPacket.setRuntimeId(BlockTranslator.isWaterlogged(javaBlockState) ? session.getBlockTranslator().getBedrockWaterId() : session.getBlockTranslator().getBedrockAirId());
|
||||||
updateWaterPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
|
updateWaterPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
|
||||||
session.sendUpstreamPacket(updateWaterPacket);
|
session.sendUpstreamPacket(updateWaterPacket);
|
||||||
|
|
||||||
|
@ -28,20 +28,23 @@ package org.geysermc.connector.network.translators.bedrock;
|
|||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction;
|
import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket;
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
|
||||||
import com.nukkitx.protocol.bedrock.packet.ItemFrameDropItemPacket;
|
import com.nukkitx.protocol.bedrock.packet.ItemFrameDropItemPacket;
|
||||||
import org.geysermc.connector.entity.ItemFrameEntity;
|
import org.geysermc.connector.entity.ItemFrameEntity;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-1.16.210: used for both survival and creative item frame item removal
|
||||||
|
*
|
||||||
|
* 1.16.210: only used in creative.
|
||||||
|
*/
|
||||||
@Translator(packet = ItemFrameDropItemPacket.class)
|
@Translator(packet = ItemFrameDropItemPacket.class)
|
||||||
public class BedrockItemFrameDropItemTranslator extends PacketTranslator<ItemFrameDropItemPacket> {
|
public class BedrockItemFrameDropItemTranslator extends PacketTranslator<ItemFrameDropItemPacket> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ItemFrameDropItemPacket packet, GeyserSession session) {
|
public void translate(ItemFrameDropItemPacket packet, GeyserSession session) {
|
||||||
Vector3i position = Vector3i.from(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ());
|
ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition()),
|
||||||
ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) ItemFrameEntity.getItemFrameEntityId(session, position),
|
|
||||||
InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking());
|
InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking());
|
||||||
session.sendDownstreamPacket(interactPacket);
|
session.sendDownstreamPacket(interactPacket);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.connector.network.translators.bedrock;
|
||||||
|
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.ItemStackRequestPacket;
|
||||||
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The packet sent for server-authoritative-style inventory transactions.
|
||||||
|
*/
|
||||||
|
@Translator(packet = ItemStackRequestPacket.class)
|
||||||
|
public class BedrockItemStackRequestTranslator extends PacketTranslator<ItemStackRequestPacket> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translate(ItemStackRequestPacket packet, GeyserSession session) {
|
||||||
|
Inventory inventory = session.getOpenInventory();
|
||||||
|
if (inventory == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
InventoryTranslator translator = session.getInventoryTranslator();
|
||||||
|
session.addInventoryTask(() -> translator.translateRequests(session, inventory, packet.getRequests()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.connector.network.translators.bedrock;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
|
||||||
|
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPlaceBlockPacket;
|
||||||
|
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientClickWindowButtonPacket;
|
||||||
|
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.LecternUpdatePacket;
|
||||||
|
import org.geysermc.connector.inventory.LecternContainer;
|
||||||
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
import org.geysermc.connector.utils.InventoryUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to translate moving pages, or closing the inventory
|
||||||
|
*/
|
||||||
|
@Translator(packet = LecternUpdatePacket.class)
|
||||||
|
public class BedrockLecternUpdateTranslator extends PacketTranslator<LecternUpdatePacket> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translate(LecternUpdatePacket packet, GeyserSession session) {
|
||||||
|
if (packet.isDroppingBook()) {
|
||||||
|
// Bedrock drops the book outside of the GUI. Java drops it in the GUI
|
||||||
|
// So, we enter the GUI and then drop it! :)
|
||||||
|
session.setDroppingLecternBook(true);
|
||||||
|
|
||||||
|
// Emulate an interact packet
|
||||||
|
ClientPlayerPlaceBlockPacket blockPacket = new ClientPlayerPlaceBlockPacket(
|
||||||
|
new Position(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()),
|
||||||
|
BlockFace.values()[0],
|
||||||
|
Hand.MAIN_HAND,
|
||||||
|
0, 0, 0, // Java doesn't care about these when dealing with a lectern
|
||||||
|
false);
|
||||||
|
session.sendDownstreamPacket(blockPacket);
|
||||||
|
} else {
|
||||||
|
// Bedrock wants to either move a page or exit
|
||||||
|
if (!(session.getOpenInventory() instanceof LecternContainer)) {
|
||||||
|
session.getConnector().getLogger().debug("Expected lectern but it wasn't open!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LecternContainer lecternContainer = (LecternContainer) session.getOpenInventory();
|
||||||
|
if (lecternContainer.getCurrentBedrockPage() == packet.getPage()) {
|
||||||
|
// The same page means Bedrock is closing the window
|
||||||
|
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(lecternContainer.getId());
|
||||||
|
session.sendDownstreamPacket(closeWindowPacket);
|
||||||
|
InventoryUtils.closeInventory(session, lecternContainer.getId(), false);
|
||||||
|
} else {
|
||||||
|
// Each "page" Bedrock gives to us actually represents two pages (think opening a book and seeing two pages)
|
||||||
|
// Each "page" on Java is just one page (think a spiral notebook folded back to only show one page)
|
||||||
|
int newJavaPage = (packet.getPage() * 2);
|
||||||
|
int currentJavaPage = (lecternContainer.getCurrentBedrockPage() * 2);
|
||||||
|
|
||||||
|
// Send as many click button packets as we need to
|
||||||
|
// Java has the option to specify exact page numbers by adding 100 to the number, but buttonId variable
|
||||||
|
// is a byte when transmitted over the network and therefore this stops us at 128
|
||||||
|
if (newJavaPage > currentJavaPage) {
|
||||||
|
for (int i = currentJavaPage; i < newJavaPage; i++) {
|
||||||
|
ClientClickWindowButtonPacket clickButtonPacket = new ClientClickWindowButtonPacket(session.getOpenInventory().getId(), 2);
|
||||||
|
session.sendDownstreamPacket(clickButtonPacket);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = currentJavaPage; i > newJavaPage; i--) {
|
||||||
|
ClientClickWindowButtonPacket clickButtonPacket = new ClientClickWindowButtonPacket(session.getOpenInventory().getId(), 1);
|
||||||
|
session.sendDownstreamPacket(clickButtonPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -25,14 +25,19 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.network.translators.bedrock;
|
package org.geysermc.connector.network.translators.bedrock;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||||
|
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerChangeHeldItemPacket;
|
||||||
|
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.MobEquipmentPacket;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerChangeHeldItemPacket;
|
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
|
|
||||||
import com.nukkitx.protocol.bedrock.packet.MobEquipmentPacket;
|
|
||||||
import org.geysermc.connector.utils.CooldownUtils;
|
import org.geysermc.connector.utils.CooldownUtils;
|
||||||
|
import org.geysermc.connector.utils.InteractiveTagManager;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Translator(packet = MobEquipmentPacket.class)
|
@Translator(packet = MobEquipmentPacket.class)
|
||||||
public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipmentPacket> {
|
public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipmentPacket> {
|
||||||
@ -40,7 +45,7 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
|
|||||||
@Override
|
@Override
|
||||||
public void translate(MobEquipmentPacket packet, GeyserSession session) {
|
public void translate(MobEquipmentPacket packet, GeyserSession session) {
|
||||||
if (!session.isSpawned() || packet.getHotbarSlot() > 8 ||
|
if (!session.isSpawned() || packet.getHotbarSlot() > 8 ||
|
||||||
packet.getContainerId() != ContainerId.INVENTORY || session.getInventory().getHeldItemSlot() == packet.getHotbarSlot()) {
|
packet.getContainerId() != ContainerId.INVENTORY || session.getPlayerInventory().getHeldItemSlot() == packet.getHotbarSlot()) {
|
||||||
// For the last condition - Don't update the slot if the slot is the same - not Java Edition behavior and messes with plugins such as Grief Prevention
|
// For the last condition - Don't update the slot if the slot is the same - not Java Edition behavior and messes with plugins such as Grief Prevention
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -48,12 +53,25 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
|
|||||||
// Send book update before switching hotbar slot
|
// Send book update before switching hotbar slot
|
||||||
session.getBookEditCache().checkForSend();
|
session.getBookEditCache().checkForSend();
|
||||||
|
|
||||||
session.getInventory().setHeldItemSlot(packet.getHotbarSlot());
|
session.getPlayerInventory().setHeldItemSlot(packet.getHotbarSlot());
|
||||||
|
|
||||||
ClientPlayerChangeHeldItemPacket changeHeldItemPacket = new ClientPlayerChangeHeldItemPacket(packet.getHotbarSlot());
|
ClientPlayerChangeHeldItemPacket changeHeldItemPacket = new ClientPlayerChangeHeldItemPacket(packet.getHotbarSlot());
|
||||||
session.sendDownstreamPacket(changeHeldItemPacket);
|
session.sendDownstreamPacket(changeHeldItemPacket);
|
||||||
|
|
||||||
|
if (session.isSneaking() && session.getPlayerInventory().getItemInHand().getJavaId() == ItemRegistry.SHIELD.getJavaId()) {
|
||||||
|
// Activate shield since we are already sneaking
|
||||||
|
// (No need to send a release item packet - Java doesn't do this when swapping items)
|
||||||
|
// Required to do it a tick later or else it doesn't register
|
||||||
|
session.getConnector().getGeneralThreadPool().schedule(() -> session.sendDownstreamPacket(new ClientPlayerUseItemPacket(Hand.MAIN_HAND)),
|
||||||
|
50, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
// Java sends a cooldown indicator whenever you switch an item
|
// Java sends a cooldown indicator whenever you switch an item
|
||||||
CooldownUtils.sendCooldown(session);
|
CooldownUtils.sendCooldown(session);
|
||||||
|
|
||||||
|
// Update the interactive tag, if an entity is present
|
||||||
|
if (session.getMouseoverEntity() != null) {
|
||||||
|
InteractiveTagManager.updateTag(session, session.getMouseoverEntity());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ import java.util.Collections;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to send the keep alive packet back to the server
|
* Used to send the forwarded keep alive packet back to the server
|
||||||
*/
|
*/
|
||||||
@Translator(packet = NetworkStackLatencyPacket.class)
|
@Translator(packet = NetworkStackLatencyPacket.class)
|
||||||
public class BedrockNetworkStackLatencyTranslator extends PacketTranslator<NetworkStackLatencyPacket> {
|
public class BedrockNetworkStackLatencyTranslator extends PacketTranslator<NetworkStackLatencyPacket> {
|
||||||
@ -60,8 +60,10 @@ public class BedrockNetworkStackLatencyTranslator extends PacketTranslator<Netwo
|
|||||||
|
|
||||||
// negative timestamps are used as hack to fix the url image loading bug
|
// negative timestamps are used as hack to fix the url image loading bug
|
||||||
if (packet.getTimestamp() > 0) {
|
if (packet.getTimestamp() > 0) {
|
||||||
|
if (session.getConnector().getConfig().isForwardPlayerPing()) {
|
||||||
ClientKeepAlivePacket keepAlivePacket = new ClientKeepAlivePacket(pingId);
|
ClientKeepAlivePacket keepAlivePacket = new ClientKeepAlivePacket(pingId);
|
||||||
session.sendDownstreamPacket(keepAlivePacket);
|
session.sendDownstreamPacket(keepAlivePacket);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,8 @@ import org.geysermc.connector.network.translators.Translator;
|
|||||||
import org.geysermc.connector.utils.SettingsUtils;
|
import org.geysermc.connector.utils.SettingsUtils;
|
||||||
import org.geysermc.cumulus.CustomForm;
|
import org.geysermc.cumulus.CustomForm;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Translator(packet = ServerSettingsRequestPacket.class)
|
@Translator(packet = ServerSettingsRequestPacket.class)
|
||||||
public class BedrockServerSettingsRequestTranslator extends PacketTranslator<ServerSettingsRequestPacket> {
|
public class BedrockServerSettingsRequestTranslator extends PacketTranslator<ServerSettingsRequestPacket> {
|
||||||
@Override
|
@Override
|
||||||
@ -40,9 +42,12 @@ public class BedrockServerSettingsRequestTranslator extends PacketTranslator<Ser
|
|||||||
CustomForm window = SettingsUtils.buildForm(session);
|
CustomForm window = SettingsUtils.buildForm(session);
|
||||||
int windowId = session.getFormCache().addForm(window);
|
int windowId = session.getFormCache().addForm(window);
|
||||||
|
|
||||||
|
// Fixes https://bugs.mojang.com/browse/MCPE-94012 because of the delay
|
||||||
|
session.getConnector().getGeneralThreadPool().schedule(() -> {
|
||||||
ServerSettingsResponsePacket serverSettingsResponsePacket = new ServerSettingsResponsePacket();
|
ServerSettingsResponsePacket serverSettingsResponsePacket = new ServerSettingsResponsePacket();
|
||||||
serverSettingsResponsePacket.setFormData(window.getJsonData());
|
serverSettingsResponsePacket.setFormData(window.getJsonData());
|
||||||
serverSettingsResponsePacket.setFormId(windowId);
|
serverSettingsResponsePacket.setFormId(windowId);
|
||||||
session.sendUpstreamPacket(serverSettingsResponsePacket);
|
session.sendUpstreamPacket(serverSettingsResponsePacket);
|
||||||
|
}, 1, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,13 @@
|
|||||||
package org.geysermc.connector.network.translators.bedrock.entity;
|
package org.geysermc.connector.network.translators.bedrock.entity;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
|
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
|
||||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientSelectTradePacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientSelectTradePacket;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
||||||
import org.geysermc.connector.entity.Entity;
|
import org.geysermc.connector.entity.Entity;
|
||||||
|
import org.geysermc.connector.inventory.GeyserItemStack;
|
||||||
import org.geysermc.connector.inventory.Inventory;
|
import org.geysermc.connector.inventory.Inventory;
|
||||||
|
import org.geysermc.connector.inventory.MerchantContainer;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
@ -47,20 +48,25 @@ public class BedrockEntityEventTranslator extends PacketTranslator<EntityEventPa
|
|||||||
session.sendUpstreamPacket(packet);
|
session.sendUpstreamPacket(packet);
|
||||||
return;
|
return;
|
||||||
case COMPLETE_TRADE:
|
case COMPLETE_TRADE:
|
||||||
|
session.addInventoryTask(() -> {
|
||||||
ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(packet.getData());
|
ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(packet.getData());
|
||||||
session.sendDownstreamPacket(selectTradePacket);
|
session.sendDownstreamPacket(selectTradePacket);
|
||||||
|
});
|
||||||
|
|
||||||
|
session.addInventoryTask(() -> {
|
||||||
Entity villager = session.getPlayerEntity();
|
Entity villager = session.getPlayerEntity();
|
||||||
Inventory openInventory = session.getInventoryCache().getOpenInventory();
|
Inventory openInventory = session.getOpenInventory();
|
||||||
if (openInventory != null && openInventory.getWindowType() == WindowType.MERCHANT) {
|
if (openInventory instanceof MerchantContainer) {
|
||||||
VillagerTrade[] trades = session.getVillagerTrades();
|
MerchantContainer merchantInventory = (MerchantContainer) openInventory;
|
||||||
|
VillagerTrade[] trades = merchantInventory.getVillagerTrades();
|
||||||
if (trades != null && packet.getData() >= 0 && packet.getData() < trades.length) {
|
if (trades != null && packet.getData() >= 0 && packet.getData() < trades.length) {
|
||||||
VillagerTrade trade = session.getVillagerTrades()[packet.getData()];
|
VillagerTrade trade = merchantInventory.getVillagerTrades()[packet.getData()];
|
||||||
openInventory.setItem(2, trade.getOutput());
|
openInventory.setItem(2, GeyserItemStack.from(trade.getOutput()), session);
|
||||||
villager.getMetadata().put(EntityData.TRADE_XP, trade.getXp() + villager.getMetadata().getInt(EntityData.TRADE_XP));
|
villager.getMetadata().put(EntityData.TRADE_XP, trade.getXp() + villager.getMetadata().getInt(EntityData.TRADE_XP));
|
||||||
villager.updateBedrockMetadata(session);
|
villager.updateBedrockMetadata(session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, 100);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
session.getConnector().getLogger().debug("Did not translate incoming EntityEventPacket: " + packet.toString());
|
session.getConnector().getLogger().debug("Did not translate incoming EntityEventPacket: " + packet.toString());
|
||||||
|
@ -25,24 +25,23 @@
|
|||||||
|
|
||||||
package org.geysermc.connector.network.translators.bedrock.entity.player;
|
package org.geysermc.connector.network.translators.bedrock.entity.player;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
import com.github.steveice10.mc.protocol.data.game.entity.player.*;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerState;
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
|
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerAbilitiesPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.player.*;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket;
|
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerStatePacket;
|
|
||||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.PlayerActionType;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||||
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
|
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket;
|
import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.PlayerActionPacket;
|
import com.nukkitx.protocol.bedrock.packet.PlayerActionPacket;
|
||||||
import org.geysermc.connector.entity.Entity;
|
import org.geysermc.connector.entity.Entity;
|
||||||
|
import org.geysermc.connector.entity.ItemFrameEntity;
|
||||||
|
import org.geysermc.connector.inventory.GeyserItemStack;
|
||||||
import org.geysermc.connector.inventory.PlayerInventory;
|
import org.geysermc.connector.inventory.PlayerInventory;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
@ -64,7 +63,7 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Send book update before any player action
|
// Send book update before any player action
|
||||||
if (packet.getAction() != PlayerActionPacket.Action.RESPAWN) {
|
if (packet.getAction() != PlayerActionType.RESPAWN) {
|
||||||
session.getBookEditCache().checkForSend();
|
session.getBookEditCache().checkForSend();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,11 +100,37 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
|||||||
case START_SNEAK:
|
case START_SNEAK:
|
||||||
ClientPlayerStatePacket startSneakPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_SNEAKING);
|
ClientPlayerStatePacket startSneakPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_SNEAKING);
|
||||||
session.sendDownstreamPacket(startSneakPacket);
|
session.sendDownstreamPacket(startSneakPacket);
|
||||||
|
|
||||||
|
// Toggle the shield, if relevant
|
||||||
|
PlayerInventory playerInv = session.getPlayerInventory();
|
||||||
|
if ((playerInv.getItemInHand().getJavaId() == ItemRegistry.SHIELD.getJavaId()) ||
|
||||||
|
(playerInv.getOffhand().getJavaId() == ItemRegistry.SHIELD.getJavaId())) {
|
||||||
|
ClientPlayerUseItemPacket useItemPacket;
|
||||||
|
if (playerInv.getItemInHand().getJavaId() == ItemRegistry.SHIELD.getJavaId()) {
|
||||||
|
useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
|
||||||
|
} else {
|
||||||
|
// Else we just assume it's the offhand, to simplify logic and to assure the packet gets sent
|
||||||
|
useItemPacket = new ClientPlayerUseItemPacket(Hand.OFF_HAND);
|
||||||
|
}
|
||||||
|
session.sendDownstreamPacket(useItemPacket);
|
||||||
|
session.getPlayerEntity().getMetadata().getFlags().setFlag(EntityFlag.BLOCKING, true);
|
||||||
|
session.getPlayerEntity().updateBedrockMetadata(session);
|
||||||
|
}
|
||||||
|
|
||||||
session.setSneaking(true);
|
session.setSneaking(true);
|
||||||
break;
|
break;
|
||||||
case STOP_SNEAK:
|
case STOP_SNEAK:
|
||||||
ClientPlayerStatePacket stopSneakPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.STOP_SNEAKING);
|
ClientPlayerStatePacket stopSneakPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.STOP_SNEAKING);
|
||||||
session.sendDownstreamPacket(stopSneakPacket);
|
session.sendDownstreamPacket(stopSneakPacket);
|
||||||
|
|
||||||
|
// Stop shield, if necessary
|
||||||
|
if (session.getPlayerEntity().getMetadata().getFlags().getFlag(EntityFlag.BLOCKING)) {
|
||||||
|
ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, BlockUtils.POSITION_ZERO, BlockFace.DOWN);
|
||||||
|
session.sendDownstreamPacket(releaseItemPacket);
|
||||||
|
session.getPlayerEntity().getMetadata().getFlags().setFlag(EntityFlag.BLOCKING, false);
|
||||||
|
session.getPlayerEntity().updateBedrockMetadata(session);
|
||||||
|
}
|
||||||
|
|
||||||
session.setSneaking(false);
|
session.setSneaking(false);
|
||||||
break;
|
break;
|
||||||
case START_SPRINT:
|
case START_SPRINT:
|
||||||
@ -143,12 +168,12 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
|||||||
LevelEventPacket startBreak = new LevelEventPacket();
|
LevelEventPacket startBreak = new LevelEventPacket();
|
||||||
startBreak.setType(LevelEventType.BLOCK_START_BREAK);
|
startBreak.setType(LevelEventType.BLOCK_START_BREAK);
|
||||||
startBreak.setPosition(vector.toFloat());
|
startBreak.setPosition(vector.toFloat());
|
||||||
PlayerInventory inventory = session.getInventory();
|
PlayerInventory inventory = session.getPlayerInventory();
|
||||||
ItemStack item = inventory.getItemInHand();
|
GeyserItemStack item = inventory.getItemInHand();
|
||||||
ItemEntry itemEntry = null;
|
ItemEntry itemEntry = null;
|
||||||
CompoundTag nbtData = new CompoundTag("");
|
CompoundTag nbtData = new CompoundTag("");
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
itemEntry = ItemRegistry.getItem(item);
|
itemEntry = item.getItemEntry();
|
||||||
nbtData = item.getNbt();
|
nbtData = item.getNbt();
|
||||||
}
|
}
|
||||||
double breakTime = Math.ceil(BlockUtils.getBreakTime(blockHardness, blockState, itemEntry, nbtData, session) * 20);
|
double breakTime = Math.ceil(BlockUtils.getBreakTime(blockHardness, blockState, itemEntry, nbtData, session) * 20);
|
||||||
@ -179,11 +204,23 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
|||||||
}
|
}
|
||||||
LevelEventPacket continueBreakPacket = new LevelEventPacket();
|
LevelEventPacket continueBreakPacket = new LevelEventPacket();
|
||||||
continueBreakPacket.setType(LevelEventType.PARTICLE_CRACK_BLOCK);
|
continueBreakPacket.setType(LevelEventType.PARTICLE_CRACK_BLOCK);
|
||||||
continueBreakPacket.setData((BlockTranslator.getBedrockBlockId(session.getBreakingBlock())) | (packet.getFace() << 24));
|
continueBreakPacket.setData((session.getBlockTranslator().getBedrockBlockId(session.getBreakingBlock())) | (packet.getFace() << 24));
|
||||||
continueBreakPacket.setPosition(vector.toFloat());
|
continueBreakPacket.setPosition(vector.toFloat());
|
||||||
session.sendUpstreamPacket(continueBreakPacket);
|
session.sendUpstreamPacket(continueBreakPacket);
|
||||||
break;
|
break;
|
||||||
case ABORT_BREAK:
|
case ABORT_BREAK:
|
||||||
|
if (session.getGameMode() != GameMode.CREATIVE) {
|
||||||
|
// As of 1.16.210: item frame items are taken out here.
|
||||||
|
// Survival also sends START_BREAK, but by attaching our process here adventure mode also works
|
||||||
|
long entityId = ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition());
|
||||||
|
if (entityId != -1) {
|
||||||
|
ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entityId,
|
||||||
|
InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking());
|
||||||
|
session.sendDownstreamPacket(interactPacket);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ClientPlayerActionPacket abortBreakingPacket = new ClientPlayerActionPacket(PlayerAction.CANCEL_DIGGING, position, BlockFace.DOWN);
|
ClientPlayerActionPacket abortBreakingPacket = new ClientPlayerActionPacket(PlayerAction.CANCEL_DIGGING, position, BlockFace.DOWN);
|
||||||
session.sendDownstreamPacket(abortBreakingPacket);
|
session.sendDownstreamPacket(abortBreakingPacket);
|
||||||
LevelEventPacket stopBreak = new LevelEventPacket();
|
LevelEventPacket stopBreak = new LevelEventPacket();
|
||||||
@ -205,10 +242,11 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
|||||||
session.getEntityCache().updateBossBars();
|
session.getEntityCache().updateBossBars();
|
||||||
break;
|
break;
|
||||||
case JUMP:
|
case JUMP:
|
||||||
|
if (!session.getConnector().getConfig().isCacheChunks()) {
|
||||||
|
// Save the jumping status for determining teleport status
|
||||||
session.setJumping(true);
|
session.setJumping(true);
|
||||||
session.getConnector().getGeneralThreadPool().schedule(() -> {
|
session.getConnector().getGeneralThreadPool().schedule(() -> session.setJumping(false), 1, TimeUnit.SECONDS);
|
||||||
session.setJumping(false);
|
}
|
||||||
}, 1, TimeUnit.SECONDS);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren