3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-11-16 04:50:07 +01:00

Merge remote-tracking branch 'refs/remotes/origin/master' into feature/floodgate-merge

# Conflicts:
#	bootstrap/bungeecord/base/build.gradle.kts
#	bootstrap/spigot/base/build.gradle.kts
#	bootstrap/spigot/base/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java
#	bootstrap/velocity/base/build.gradle.kts
#	core/build.gradle.kts
#	core/src/main/java/org/geysermc/geyser/GeyserImpl.java
#	core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java
#	core/src/main/java/org/geysermc/geyser/entity/type/FireworkEntity.java
#	core/src/main/java/org/geysermc/geyser/network/netty/LocalSession.java
#	core/src/main/java/org/geysermc/geyser/session/GeyserSession.java
#	core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCustomPayloadTranslator.java
#	core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java
#	gradle.properties
#	gradle/libs.versions.toml
#	settings.gradle.kts
Dieser Commit ist enthalten in:
Tim203 2024-05-09 18:04:56 +02:00
Commit 9f32ba81b1
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
503 geänderte Dateien mit 19812 neuen und 4402 gelöschten Zeilen

Datei anzeigen

@ -1,43 +1,40 @@
name: Build Pull Request
name: Build Remote
on:
pull_request:
merge_group:
on:
workflow_call:
inputs:
repository:
required: true
description: 'The repo of the remote'
type: string
ref:
required: true
description: 'The ref of the remote'
type: string
permissions: {}
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Set up JDK 17
- name: Set Build Number
run: |
echo "BUILD_NUMBER=${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
- name: Set up JDK 21
# See https://github.com/actions/setup-java/commits
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
java-version: 17
java-version: 21
distribution: temurin
- name: Check if the author has forked the API repo
# See https://github.com/Kas-tle/find-forks-action/commits
uses: Kas-tle/find-forks-action@1b5447d1e3c7a8ed79583dd817cc5399686eed3a
id: find_forks
with:
owner: GeyserMC
repo: api
token: ${{ secrets.GITHUB_TOKEN }}
- name: Use author's API repo if it exists
if: ${{ steps.find_forks.outputs.target_branch_found == 'true' }}
env:
API_FORK_URL: ${{ steps.find_forks.outputs.user_fork_url }}
API_FORK_BRANCH: ${{ github.event.pull_request.head.ref }}
run: |
git clone "${API_FORK_URL}" --single-branch --branch "${API_FORK_BRANCH}" api
cd api
./gradlew publishToMavenLocal
- name: Checkout repository and submodules
# See https://github.com/actions/checkout/commits
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
repository: ${{ inputs.repository }}
ref: ${{ inputs.ref }}
submodules: recursive
path: geyser
@ -46,11 +43,12 @@ jobs:
uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # v2.1.1
- name: Build Geyser
# See https://github.com/gradle/gradle-build-action/commits
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 from https://github.com/gradle/actions/commits
# See https://github.com/gradle/actions/commits
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0
with:
arguments: build
build-root-directory: geyser
cache-read-only: true
- name: Archive artifacts (Geyser Fabric)
# See https://github.com/actions/upload-artifact/commits
@ -101,4 +99,4 @@ jobs:
with:
name: Geyser ViaProxy
path: geyser/bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
if-no-files-found: error
if-no-files-found: error

Datei anzeigen

@ -7,7 +7,9 @@ on:
- 'gh-readonly-queue/**'
paths-ignore:
- '.github/ISSUE_TEMPLATE/*.yml'
- '.github/actions/pullrequest.yml'
- '.github/workflows/build-remote.yml'
- '.github/workflows/preview.yml'
- '.github/workflows/pull-request.yml'
- '.idea/copyright/*.xml'
- '.gitignore'
- 'CONTRIBUTING.md'
@ -19,7 +21,16 @@ on:
jobs:
build:
runs-on: ubuntu-latest
env:
PROJECT: 'geyser'
steps:
- name: Set Build Number
env:
BUILD_JSON: ${{ vars.RELEASEACTION_PREVRELEASE }}
run: |
BUILD_NUMBER=$(echo $BUILD_JSON | jq --arg branch "${GITHUB_REF_NAME}" 'if .[$branch] == null then 1 else .[$branch] | .t | tonumber + 1 end // 1')
echo "BUILD_NUMBER=${BUILD_NUMBER:=$GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
- name: Checkout repository and submodules
# See https://github.com/actions/checkout/commits
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
@ -33,12 +44,12 @@ jobs:
# See https://github.com/actions/setup-java/commits
- uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
java-version: 17
java-version: 21
distribution: temurin
- name: Build
# See https://github.com/gradle/gradle-build-action/commits
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 from https://github.com/gradle/actions/commits
# See https://github.com/gradle/actions/commits
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0
with:
arguments: build
gradle-home-cache-cleanup: true
@ -103,6 +114,37 @@ jobs:
with:
arguments: publish
- name: Get Release Metadata
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
# See https://github.com/Kas-tle/base-release-action/releases/tag/main-11
uses: Kas-tle/base-release-action@b863fa0f89bd15267a96a72efb84aec25f168d4c # main-11
with:
appID: ${{ secrets.RELEASE_APP_ID }}
appPrivateKey: ${{ secrets.RELEASE_APP_PK }}
files: |
bungeecord:bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
fabric:bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
neoforge:bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
spigot:bootstrap/spigot/build/libs/Geyser-Spigot.jar
standalone:bootstrap/standalone/build/libs/Geyser-Standalone.jar
velocity:bootstrap/velocity/build/libs/Geyser-Velocity.jar
viaproxy:bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
releaseEnabled: false
saveMetadata: true
- name: Update Generated Metadata
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
run: |
cat metadata.json
echo
mv metadata.json metadata.json.tmp
version=$(cat gradle.properties | grep -o "version=[0-9\\.]*" | cut -d"=" -f2)
jq --arg project "${PROJECT}" --arg version "${version}" '
.
| .changes |= map({"commit", "summary", "message"})
| .downloads |= map_values({"name", "sha256"})
| {$project, "repo", $version, "number": .build, "changes", "downloads"}
' metadata.json.tmp > metadata.json
cat metadata.json
- name: Publish to Downloads API
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
shell: bash
@ -114,19 +156,13 @@ jobs:
# Save the private key to a file
echo "$DOWNLOADS_PRIVATE_KEY" > id_ecdsa
chmod 600 id_ecdsa
# Set the project
project=geyser
# Get the version from gradle.properties
version=$(cat gradle.properties | grep -o "version=[0-9\\.]*" | cut -d"=" -f2)
# Create the build folder
ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP mkdir -p "~/uploads/$project/$GITHUB_RUN_NUMBER/"
ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP mkdir -p "~/uploads/$PROJECT/$GITHUB_RUN_NUMBER/"
# Copy over artifacts
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" bootstrap/**/build/libs/Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" bootstrap/mod/**/build/libs/Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" bootstrap/**/build/libs/Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$PROJECT/$GITHUB_RUN_NUMBER/
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" bootstrap/mod/**/build/libs/Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$PROJECT/$GITHUB_RUN_NUMBER/
# Run the build script
# Push the metadata
echo "{\"project\": \"$project\", \"version\": \"$version\", \"id\": $GITHUB_RUN_NUMBER, \"commit\": \"$GITHUB_SHA\"}" > metadata.json
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" metadata.json $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" metadata.json $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$PROJECT/$GITHUB_RUN_NUMBER/
- name: Publish to Modrinth (Fabric)
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5

96
.github/workflows/preview.yml vendored Normale Datei
Datei anzeigen

@ -0,0 +1,96 @@
name: Upload Preview
on:
workflow_dispatch:
inputs:
runId:
required: true
description: 'ID of the action to pull artifacts from'
build:
required: true
description: 'Build number for the release'
version:
required: true
description: 'Version under which to upload to the Downloads API'
workflow_call:
inputs:
build:
required: true
description: 'Build number for the release'
type: string
version:
required: true
description: 'Version under which to upload to the Downloads API'
type: string
jobs:
upload:
runs-on: ubuntu-latest
env:
PROJECT: 'geyserpreview'
steps:
- name: Set Variables
id: setvars
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "BUILD=${{ github.event.inputs.build }}" >> $GITHUB_ENV
echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
echo "RUN=${{ github.event.inputs.runId }}" >> $GITHUB_OUTPUT
else
echo "BUILD=${{ inputs.build }}" >> $GITHUB_ENV
echo "VERSION=${{ inputs.version }}" >> $GITHUB_ENV
echo "RUN=${{ github.run_id }}" >> $GITHUB_OUTPUT
fi
- uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427
with:
run-id: ${{ steps.setvars.outputs.RUN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
merge-multiple: true
- name: Get Preview Metadata
if: success()
# See https://github.com/Kas-tle/base-release-action/releases/tag/main-11
uses: Kas-tle/base-release-action@664c39985eb9d0d393ce98e7eb8414d3d98e762a # main-11
with:
appID: ${{ secrets.RELEASE_APP_ID }}
appPrivateKey: ${{ secrets.RELEASE_APP_PK }}
files: |
bungeecord:Geyser-BungeeCord.jar
fabric:Geyser-Fabric.jar
neoforge:Geyser-NeoForge.jar
spigot:Geyser-Spigot.jar
standalone:Geyser-Standalone.jar
velocity:Geyser-Velocity.jar
viaproxy:Geyser-ViaProxy.jar
releaseEnabled: false
saveMetadata: true
updateReleaseData: false
- name: Update Generated Metadata
if: success()
run: |
cat metadata.json
echo
mv metadata.json metadata.json.tmp
jq --arg project "${PROJECT}" --arg version "${VERSION}" --arg number "${BUILD}" '
.
| .downloads |= map_values({"name", "sha256"})
| {$project, "repo", $version, "number": $number | tonumber, "changes": [], "downloads"}
' metadata.json.tmp > metadata.json
cat metadata.json
- name: Publish to Downloads API
if: success()
shell: bash
env:
DOWNLOADS_USERNAME: ${{ vars.DOWNLOADS_USERNAME }}
DOWNLOADS_PRIVATE_KEY: ${{ secrets.DOWNLOADS_PRIVATE_KEY }}
DOWNLOADS_SERVER_IP: ${{ secrets.DOWNLOADS_SERVER_IP }}
run: |
# Save the private key to a file
echo "$DOWNLOADS_PRIVATE_KEY" > id_ecdsa
chmod 600 id_ecdsa
# Create the build folder
ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP mkdir -p "~/uploads/$PROJECT/$BUILD/"
# Copy over artifacts
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$PROJECT/$BUILD/
# Run the build script
# Push the metadata
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" metadata.json $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$PROJECT/$BUILD/

24
.github/workflows/pull-request.yml vendored Normale Datei
Datei anzeigen

@ -0,0 +1,24 @@
name: Process Pull Request
on:
pull_request_target:
jobs:
build:
# Forbid access to secrets nor GH Token perms while building the PR
permissions: {}
secrets: {}
uses: ./.github/workflows/build-remote.yml
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.sha }}
preview:
needs: [build]
if: >-
contains(github.event.pull_request.labels.*.name, 'PR: Needs Testing')
# Allow access to secrets if we are uploading a preview
secrets: inherit
uses: ./.github/workflows/preview.yml
with:
build: ${{ github.run_number }}
version: pr.${{ github.event.pull_request.number }}

Datei anzeigen

@ -2,7 +2,7 @@
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Discord](https://img.shields.io/discord/613163671870242838.svg?color=%237289da&label=discord)](https://discord.gg/geysermc)
[![Crowdin](https://badges.crowdin.net/geyser/localized.svg)](https://translate.geysermc.org/)
[![Crowdin](https://badges.crowdin.net/e/51361b7f8a01644a238d0fe8f3bddc62/localized.svg)](https://translate.geysermc.org/)
Geyser is a bridge between Minecraft: Bedrock Edition and Minecraft: Java Edition, closing the gap from those wanting to play true cross-platform.
@ -14,7 +14,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 joined us here!
### Currently supporting Minecraft Bedrock 1.20.40 - 1.20.71 and Minecraft Java 1.20.4
### Currently supporting Minecraft Bedrock 1.20.40 - 1.20.80/81 and Minecraft Java 1.20.5/1.20.6
## Setting Up
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
@ -32,7 +32,6 @@ Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Ge
## What's Left to be Added/Fixed
- Near-perfect movement (to the point where anticheat on large servers is unlikely to ban you)
- Some Entity Flags
- Structure block UI
## What can't be fixed
There are a few things Geyser is unable to support due to various differences between Minecraft Bedrock and Java. For a list of these limitations, see the [Current Limitations](https://wiki.geysermc.org/geyser/current-limitations/) page.

Datei anzeigen

@ -32,6 +32,9 @@ import org.geysermc.geyser.api.connection.GeyserConnection;
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
import org.geysermc.geyser.api.network.RemoteServer;
import java.util.Map;
import java.util.Objects;
/**
* Called when a session has logged in, and is about to connect to a remote java server.
* This event is cancellable, and can be used to prevent the player from connecting to the remote server.
@ -40,10 +43,16 @@ public final class SessionLoginEvent extends ConnectionEvent implements Cancella
private RemoteServer remoteServer;
private boolean cancelled;
private String disconnectReason;
private Map<String, byte[]> cookies;
private boolean transferring;
public SessionLoginEvent(@NonNull GeyserConnection connection, @NonNull RemoteServer remoteServer) {
public SessionLoginEvent(@NonNull GeyserConnection connection,
@NonNull RemoteServer remoteServer,
@NonNull Map<String, byte[]> cookies) {
super(connection);
this.remoteServer = remoteServer;
this.cookies = cookies;
this.transferring = false;
}
/**
@ -124,4 +133,36 @@ public final class SessionLoginEvent extends ConnectionEvent implements Cancella
public void remoteServer(@NonNull RemoteServer remoteServer) {
this.remoteServer = remoteServer;
}
/**
* Sets a map of cookies from a possible previous session. The Java server can send and request these
* to store information on the client across server transfers.
*/
public void cookies(@NonNull Map<String, byte[]> cookies) {
Objects.requireNonNull(cookies);
this.cookies = cookies;
}
/**
* Gets a map of the sessions cookies, if set.
* @return the connections cookies
*/
public @NonNull Map<String, byte[]> cookies() {
return cookies;
}
/**
* Determines the connection intent of the connection
*/
public void transferring(boolean transferring) {
this.transferring = transferring;
}
/**
* Gets whether this login attempt to the Java server
* has the transfer intent
*/
public boolean transferring() {
return this.transferring;
}
}

Datei anzeigen

@ -0,0 +1,88 @@
/*
* Copyright (c) 2019-2024 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.geyser.api.event.connection;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.event.Cancellable;
import org.geysermc.event.Event;
import java.net.InetSocketAddress;
/**
* Called whenever a client attempts to connect to the server, before the connection is accepted.
*/
public final class ConnectionRequestEvent implements Event, Cancellable {
private boolean cancelled;
private final InetSocketAddress ip;
private final InetSocketAddress proxyIp;
public ConnectionRequestEvent(@NonNull InetSocketAddress ip, @Nullable InetSocketAddress proxyIp) {
this.ip = ip;
this.proxyIp = proxyIp;
}
/**
* The IP address of the client attempting to connect
*
* @return the IP address of the client attempting to connect
*/
@NonNull
public InetSocketAddress getInetSocketAddress() {
return ip;
}
/**
* The IP address of the proxy handling the connection. It will return null if there is no proxy.
*
* @return the IP address of the proxy handling the connection
*/
@Nullable
public InetSocketAddress getProxyIp() {
return proxyIp;
}
/**
* The cancel status of this event. If this event is cancelled, the connection will be rejected.
*
* @return the cancel status of this event
*/
@Override
public boolean isCancelled() {
return cancelled;
}
/**
* Sets the cancel status of this event. If this event is canceled, the connection will be rejected.
*
* @param cancelled the cancel status of this event.
*/
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
}

Datei anzeigen

@ -0,0 +1,125 @@
/*
* Copyright (c) 2019-2024 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.geyser.api.event.java;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.common.value.qual.IntRange;
import org.geysermc.geyser.api.connection.GeyserConnection;
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
import java.util.Map;
/**
* Fired when the Java server sends a transfer request to a different Java server.
* Geyser Extensions can listen to this event and set a target server ip/port for Bedrock players to be transferred to.
*/
public class ServerTransferEvent extends ConnectionEvent {
private final String host;
private final int port;
private String bedrockHost;
private int bedrockPort;
private final Map<String, byte[]> cookies;
public ServerTransferEvent(@NonNull GeyserConnection connection,
@NonNull String host, int port, @NonNull Map<String, byte[]> cookies) {
super(connection);
this.host = host;
this.port = port;
this.cookies = cookies;
this.bedrockHost = null;
this.bedrockPort = -1;
}
/**
* The host that the Java server requests a transfer to.
*
* @return the host
*/
public @NonNull String host() {
return this.host;
}
/**
* The port that the Java server requests a transfer to.
*
* @return the port
*/
public int port() {
return this.port;
}
/**
* The host that the Bedrock player should try and connect to.
* If this is not set, the Bedrock player will just be disconnected.
*
* @return the host where the Bedrock client will be transferred to, or null if not set.
*/
public @Nullable String bedrockHost() {
return this.bedrockHost;
}
/**
* The port that the Bedrock player should try and connect to.
* If this is not set, the Bedrock player will just be disconnected.
*
* @return the port where the Bedrock client will be transferred to, or -1 if not set.
*/
public int bedrockPort() {
return this.bedrockPort;
}
/**
* Sets the host for the Bedrock player to be transferred to
*/
public void bedrockHost(@NonNull String host) {
if (host == null || host.isBlank()) {
throw new IllegalArgumentException("Server address cannot be null or blank");
}
this.bedrockHost = host;
}
/**
* Sets the port for the Bedrock player to be transferred to
*/
public void bedrockPort(@IntRange(from = 0, to = 65535) int port) {
if (port < 0 || port > 65535) {
throw new IllegalArgumentException("Server port must be between 0 and 65535, was " + port);
}
this.bedrockPort = port;
}
/**
* Gets a map of the sessions current cookies.
*
* @return the connections cookies
*/
public @NonNull Map<String, byte[]> cookies() {
return cookies;
}
}

Datei anzeigen

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
* Copyright (c) 2019-2024 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
@ -64,6 +64,14 @@ public interface NonVanillaCustomItemData extends CustomItemData {
*/
int maxDamage();
/**
* Gets the attack damage of the item.
* This is purely visual, and only applied to tools
*
* @return the attack damage of the item
*/
int attackDamage();
/**
* Gets the tool type of the item.
*
@ -153,6 +161,13 @@ public interface NonVanillaCustomItemData extends CustomItemData {
return displayHandheld();
}
/**
* Gets the block the item places.
*
* @return the block the item places
*/
String block();
static NonVanillaCustomItemData.Builder builder() {
return GeyserApi.api().provider(NonVanillaCustomItemData.Builder.class);
}
@ -169,6 +184,8 @@ public interface NonVanillaCustomItemData extends CustomItemData {
Builder maxDamage(int maxDamage);
Builder attackDamage(int attackDamage);
Builder toolType(@Nullable String toolType);
Builder toolTier(@Nullable String toolTier);
@ -191,6 +208,8 @@ public interface NonVanillaCustomItemData extends CustomItemData {
Builder chargeable(boolean isChargeable);
Builder block(String block);
/**
* @deprecated Use {@link #displayHandheld(boolean)} instead.
*/

Datei anzeigen

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
* Copyright (c) 2019-2024 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
@ -31,11 +31,10 @@ import org.checkerframework.checker.nullness.qual.NonNull;
* Represents the creative menu categories or tabs.
*/
public enum CreativeCategory {
COMMANDS("commands", 1),
CONSTRUCTION("construction", 2),
CONSTRUCTION("construction", 1),
NATURE("nature", 2),
EQUIPMENT("equipment", 3),
ITEMS("items", 4),
NATURE("nature", 5),
NONE("none", 6);
private final String internalName;

Datei anzeigen

@ -4,8 +4,8 @@ plugins {
dependencies {
api(projects.core)
implementation(libs.adventure.text.serializer.bungeecord)
compileOnlyApi(libs.bungeecord.proxy)
implementation(libs.floodgate.bungee)
}
@ -24,6 +24,7 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
dependencies {
exclude(dependency("com.google.*:.*"))
exclude(dependency("io.netty.incubator:.*"))
exclude(dependency("io.netty:netty-transport-native-epoll:.*"))
exclude(dependency("io.netty:netty-transport-native-unix-common:.*"))
exclude(dependency("io.netty:netty-handler:.*"))

Datei anzeigen

@ -7,6 +7,8 @@ architectury {
fabric()
}
val includeTransitive: Configuration = configurations.getByName("includeTransitive")
dependencies {
modImplementation(libs.fabric.loader)
modApi(libs.fabric.api)
@ -15,14 +17,28 @@ dependencies {
shadow(project(path = ":mod", configuration = "transformProductionFabric")) {
isTransitive = false
}
shadow(projects.core) {
exclude(group = "com.google.guava", module = "guava")
exclude(group = "com.google.code.gson", module = "gson")
exclude(group = "org.slf4j")
exclude(group = "com.nukkitx.fastutil")
exclude(group = "io.netty.incubator")
}
shadow(projects.core) { isTransitive = false }
includeTransitive(projects.core)
// These are NOT transitively included, and instead shadowed + relocated.
// Avoids fabric complaining about non-SemVer versioning
shadow(libs.protocol.connection) { isTransitive = false }
shadow(libs.protocol.common) { isTransitive = false }
shadow(libs.protocol.codec) { isTransitive = false }
shadow(libs.mcauthlib) { isTransitive = false }
shadow(libs.raknet) { isTransitive = false }
// Consequences of shading + relocating mcauthlib: shadow/relocate mcpl!
shadow(libs.mcprotocollib) { isTransitive = false }
// Since we also relocate cloudburst protocol: shade erosion common
shadow(libs.erosion.common) { isTransitive = false }
// Let's shade in our own api/common module
shadow(projects.api) { isTransitive = false }
shadow(projects.common) { isTransitive = false }
// Permissions
modImplementation(libs.fabric.permissions)
include(libs.fabric.permissions)
}
@ -31,6 +47,10 @@ application {
mainClass.set("org.geysermc.geyser.platform.fabric.GeyserFabricMain")
}
relocate("org.cloudburstmc.netty")
relocate("org.cloudburstmc.protocol")
relocate("com.github.steveice10.mc.auth")
tasks {
remapJar {
archiveBaseName.set("Geyser-Fabric")

Datei anzeigen

@ -28,14 +28,15 @@ package org.geysermc.geyser.platform.fabric;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.world.entity.player.Player;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import org.geysermc.geyser.platform.mod.GeyserModUpdateListener;
import org.checkerframework.checker.nullness.qual.NonNull;
public class GeyserFabricBootstrap extends GeyserModBootstrap implements ModInitializer {
@ -45,21 +46,37 @@ public class GeyserFabricBootstrap extends GeyserModBootstrap implements ModInit
@Override
public void onInitialize() {
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER) {
if (isServer()) {
// Set as an event, so we can get the proper IP and port if needed
ServerLifecycleEvents.SERVER_STARTED.register((server) -> {
this.setServer(server);
onGeyserEnable();
});
} else {
ClientLifecycleEvents.CLIENT_STOPPING.register(($)-> {
onGeyserShutdown();
});
}
// These are only registered once
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> onGeyserShutdown());
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> {
if (isServer()) {
onGeyserShutdown();
} else {
onGeyserDisable();
}
});
ServerPlayConnectionEvents.JOIN.register((handler, $, $$) -> GeyserModUpdateListener.onPlayReady(handler.getPlayer()));
this.onGeyserInitialize();
}
@Override
public boolean isServer() {
return FabricLoader.getInstance().getEnvironmentType().equals(EnvType.SERVER);
}
@Override
public boolean hasPermission(@NonNull Player source, @NonNull String permissionNode) {
return Permissions.check(source, permissionNode);

Datei anzeigen

@ -23,9 +23,8 @@
"geyser.mixins.json"
],
"depends": {
"fabricloader": ">=0.15.2",
"fabricloader": ">=0.15.10",
"fabric": "*",
"minecraft": ">=1.20.4",
"fabric-permissions-api-v0": "*"
"minecraft": ">=1.20.5"
}
}

Datei anzeigen

@ -2,11 +2,17 @@ plugins {
application
}
// This is provided by "org.cloudburstmc.math.mutable" too, so yeet.
// NeoForge's class loader is *really* annoying.
provided("org.cloudburstmc.math", "api")
architectury {
platformSetupLoomIde()
neoForge()
}
val includeTransitive: Configuration = configurations.getByName("includeTransitive")
dependencies {
// See https://github.com/google/guava/issues/6618
modules {
@ -21,12 +27,14 @@ dependencies {
shadow(project(path = ":mod", configuration = "transformProductionNeoForge")) {
isTransitive = false
}
shadow(projects.core) {
exclude(group = "com.google.guava", module = "guava")
exclude(group = "com.google.code.gson", module = "gson")
exclude(group = "org.slf4j")
exclude(group = "io.netty.incubator")
}
shadow(projects.core) { isTransitive = false }
// Let's shade in our own api
shadow(projects.api) { isTransitive = false }
shadow(projects.common) { isTransitive = false }
// Include all transitive deps of core via JiJ
includeTransitive(projects.core)
}
application {
@ -34,10 +42,6 @@ application {
}
tasks {
shadowJar {
relocate("it.unimi.dsi.fastutil", "org.geysermc.relocate.fastutil")
}
remapJar {
archiveBaseName.set("Geyser-NeoForge")
}

Datei anzeigen

@ -27,10 +27,10 @@ package org.geysermc.geyser.platform.neoforge;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.world.entity.player.Player;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.loading.FMLLoader;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.GameShuttingDownEvent;
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
import net.neoforged.neoforge.event.server.ServerStartedEvent;
import net.neoforged.neoforge.event.server.ServerStoppingEvent;
@ -46,9 +46,11 @@ public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
public GeyserNeoForgeBootstrap() {
super(new GeyserNeoForgePlatform());
if (FMLLoader.getDist() == Dist.DEDICATED_SERVER) {
if (isServer()) {
// Set as an event so we can get the proper IP and port if needed
NeoForge.EVENT_BUS.addListener(this::onServerStarted);
} else {
NeoForge.EVENT_BUS.addListener(this::onClientStopping);
}
NeoForge.EVENT_BUS.addListener(this::onServerStopping);
@ -64,6 +66,14 @@ public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
}
private void onServerStopping(ServerStoppingEvent event) {
if (isServer()) {
this.onGeyserShutdown();
} else {
this.onGeyserDisable();
}
}
private void onClientStopping(GameShuttingDownEvent ignored) {
this.onGeyserShutdown();
}
@ -71,6 +81,11 @@ public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
GeyserModUpdateListener.onPlayReady(event.getEntity());
}
@Override
public boolean isServer() {
return FMLLoader.getDist().isDedicatedServer();
}
@Override
public boolean hasPermission(@NonNull Player source, @NonNull String permissionNode) {
return this.permissionHandler.hasPermission(source, permissionNode);

Datei anzeigen

@ -14,12 +14,12 @@ config = "geyser.mixins.json"
[[dependencies.geyser_neoforge]]
modId="neoforge"
type="required"
versionRange="[20.4.48-beta,)"
versionRange="[20.5.0-beta,)"
ordering="NONE"
side="BOTH"
[[dependencies.geyser_neoforge]]
modId="minecraft"
type="required"
versionRange="[1.20,1.21)"
versionRange="[1.20.5,1.21)"
ordering="NONE"
side="BOTH"

Datei anzeigen

@ -58,6 +58,7 @@ import org.geysermc.geyser.util.FileUtils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketAddress;
import java.nio.file.Path;
import java.util.Map;
import java.util.UUID;
@ -127,7 +128,9 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
// We want to do this late in the server startup process to allow other mods
// To do their job injecting, then connect into *that*
this.geyserInjector = new GeyserModInjector(server, this.platform);
this.geyserInjector.initializeLocalChannel(this);
if (isServer()) {
this.geyserInjector.initializeLocalChannel(this);
}
// Start command building
// Set just "geyser" as the help command
@ -241,10 +244,22 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
}
@Override
public int getServerPort() {
return ((GeyserServerPortGetter) server).geyser$getServerPort();
public SocketAddress getSocketAddress() {
return this.geyserInjector.getServerSocketAddress();
}
@Override
public int getServerPort() {
if (isServer()) {
return ((GeyserServerPortGetter) server).geyser$getServerPort();
} else {
// Set in the IntegratedServerMixin
return geyserConfig.getRemote().port();
}
}
public abstract boolean isServer();
@Override
public boolean testFloodgatePluginPresent() {
return this.platform.testFloodgatePluginPresent(this);

Datei anzeigen

@ -93,8 +93,11 @@ public class GeyserModInjector extends GeyserInjector {
protected void initChannel(@NonNull Channel ch) throws Exception {
initChannel.invoke(childHandler, ch);
int index = ch.pipeline().names().indexOf("encoder");
String baseName = index != -1 ? "encoder" : "outbound_config";
if (bootstrap.getGeyserConfig().isDisableCompression()) {
ch.pipeline().addAfter("encoder", "geyser-compression-disabler", new GeyserModCompressionDisabler());
ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserModCompressionDisabler());
}
}
})

Datei anzeigen

@ -29,6 +29,7 @@ import lombok.AllArgsConstructor;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.minecraft.core.RegistryAccess;
import net.minecraft.network.Connection;
import net.minecraft.network.PacketSendListener;
import net.minecraft.network.protocol.Packet;
@ -69,7 +70,7 @@ public class ModPingPassthrough implements IGeyserPingPassthrough {
StatusInterceptor connection = new StatusInterceptor();
ServerStatusPacketListener statusPacketListener = new ServerStatusPacketListenerImpl(status, connection);
statusPacketListener.handleStatusRequest(new ServerboundStatusRequestPacket());
statusPacketListener.handleStatusRequest(ServerboundStatusRequestPacket.INSTANCE);
// mods like MiniMOTD (that inject into the above method) have now processed the response
status = Objects.requireNonNull(connection.status, "status response");
} catch (Exception e) {
@ -79,7 +80,7 @@ public class ModPingPassthrough implements IGeyserPingPassthrough {
}
}
String jsonDescription = net.minecraft.network.chat.Component.Serializer.toJson(status.description());
String jsonDescription = net.minecraft.network.chat.Component.Serializer.toJson(status.description(), RegistryAccess.EMPTY);
String legacyDescription = LEGACY_SERIALIZER.serialize(GSON_SERIALIZER.deserializeOr(jsonDescription, Component.empty()));
return new GeyserPingInfo(

Datei anzeigen

@ -27,6 +27,7 @@ package org.geysermc.geyser.platform.mod.command;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.core.RegistryAccess;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import org.checkerframework.checker.nullness.qual.NonNull;
@ -63,7 +64,7 @@ public class ModCommandSender implements GeyserCommandSource {
public void sendMessage(net.kyori.adventure.text.Component message) {
if (source.getEntity() instanceof ServerPlayer player) {
String decoded = GsonComponentSerializer.gson().serialize(message);
player.displayClientMessage(Objects.requireNonNull(Component.Serializer.fromJson(decoded)), false);
player.displayClientMessage(Objects.requireNonNull(Component.Serializer.fromJson(decoded, RegistryAccess.EMPTY)), false);
return;
}
GeyserCommandSource.super.sendMessage(message);

Datei anzeigen

@ -54,8 +54,10 @@ public class IntegratedServerMixin implements GeyserServerPortGetter {
private void onOpenToLan(GameType gameType, boolean cheatsAllowed, int port, CallbackInfoReturnable<Boolean> cir) {
if (cir.getReturnValueZ()) {
// If the LAN is opened, starts Geyser.
GeyserModBootstrap.getInstance().setServer((MinecraftServer) (Object) this);
GeyserModBootstrap.getInstance().onGeyserEnable();
GeyserModBootstrap instance = GeyserModBootstrap.getInstance();
instance.setServer((MinecraftServer) (Object) this);
instance.getGeyserConfig().getRemote().setPort(port);
instance.onGeyserEnable();
// Ensure player locale has been loaded, in case it's different from Java system language
GeyserLocale.loadGeyserLocale(this.minecraft.options.languageCode);
// Give indication that Geyser is loaded

Datei anzeigen

@ -25,39 +25,34 @@
package org.geysermc.geyser.platform.mod.world;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.BannerPatternLayer;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.minecraft.SharedConstants;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.ByteArrayTag;
import net.minecraft.nbt.ByteTag;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.DoubleTag;
import net.minecraft.nbt.EndTag;
import net.minecraft.nbt.FloatTag;
import net.minecraft.nbt.IntArrayTag;
import net.minecraft.nbt.IntTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.LongArrayTag;
import net.minecraft.nbt.LongTag;
import net.minecraft.nbt.ShortTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.TagVisitor;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.Filterable;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.WritableBookItem;
import net.minecraft.world.item.WrittenBookItem;
import net.minecraft.world.item.component.WritableBookContent;
import net.minecraft.world.item.component.WrittenBookContent;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BannerBlockEntity;
import net.minecraft.world.level.block.entity.BannerPatternLayers;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.LecternBlockEntity;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
@ -71,11 +66,14 @@ import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.BlockEntityUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
public class GeyserModWorldManager extends GeyserWorldManager {
private static final GsonComponentSerializer GSON_SERIALIZER = GsonComponentSerializer.gson();
private static final LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.legacySection();
private final MinecraftServer server;
public GeyserModWorldManager(MinecraftServer server) {
@ -180,7 +178,7 @@ public class GeyserModWorldManager extends GeyserWorldManager {
}
ItemStack book = lectern.getBook();
int pageCount = WrittenBookItem.getPageCount(book);
int pageCount = getPageCount(book);
boolean hasBookPages = pageCount > 0;
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x, y, z, hasBookPages ? pageCount : 1);
lecternTag.putInt("page", lectern.getPage() / 2);
@ -189,11 +187,9 @@ public class GeyserModWorldManager extends GeyserWorldManager {
.putShort("Damage", (short) 0)
.putString("Name", "minecraft:writable_book");
List<NbtMap> pages = new ArrayList<>(hasBookPages ? pageCount : 1);
if (hasBookPages && WritableBookItem.makeSureTagIsValid(book.getTag())) {
ListTag listTag = book.getTag().getList("pages", 8);
for (int i = 0; i < listTag.size(); i++) {
String page = listTag.getString(i);
if (hasBookPages) {
List<String> bookPages = getPages(book);
for (String page : bookPages) {
NbtMapBuilder pageBuilder = NbtMap.builder()
.putString("photoname", "")
.putString("text", page);
@ -226,8 +222,8 @@ public class GeyserModWorldManager extends GeyserWorldManager {
@NonNull
@Override
public CompletableFuture<com.github.steveice10.opennbt.tag.builtin.CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
CompletableFuture<com.github.steveice10.opennbt.tag.builtin.CompoundTag> future = new CompletableFuture<>();
public CompletableFuture<org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addNbtData) {
CompletableFuture<org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents> future = new CompletableFuture<>();
server.execute(() -> {
ServerPlayer player = getPlayer(session);
if (player == null) {
@ -243,9 +239,23 @@ public class GeyserModWorldManager extends GeyserWorldManager {
// Potentially exposes other NBT data? But we need to get the NBT data for the banner patterns *and*
// the banner might have a custom name, both of which a Java client knows and caches
ItemStack itemStack = banner.getItem();
var tag = OpenNbtTagVisitor.convert("", itemStack.getOrCreateTag());
future.complete(tag);
org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents components =
new org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents(new HashMap<>());
components.put(DataComponentType.DAMAGE, itemStack.getDamageValue());
Component customName = itemStack.getComponents().get(DataComponents.CUSTOM_NAME);
if (customName != null) {
components.put(DataComponentType.CUSTOM_NAME, toKyoriComponent(customName));
}
BannerPatternLayers pattern = itemStack.get(DataComponents.BANNER_PATTERNS);
if (pattern != null) {
components.put(DataComponentType.BANNER_PATTERNS, toPatternList(pattern));
}
future.complete(components);
return;
}
future.complete(null);
@ -257,95 +267,52 @@ public class GeyserModWorldManager extends GeyserWorldManager {
return server.getPlayerList().getPlayer(session.getPlayerEntity().getUuid());
}
// Future considerations: option to clone; would affect arrays
private static class OpenNbtTagVisitor implements TagVisitor {
private String currentKey;
private final com.github.steveice10.opennbt.tag.builtin.CompoundTag root;
private com.github.steveice10.opennbt.tag.builtin.Tag currentTag;
OpenNbtTagVisitor(String key) {
root = new com.github.steveice10.opennbt.tag.builtin.CompoundTag(key);
}
@Override
public void visitString(StringTag stringTag) {
currentTag = new com.github.steveice10.opennbt.tag.builtin.StringTag(currentKey, stringTag.getAsString());
}
@Override
public void visitByte(ByteTag byteTag) {
currentTag = new com.github.steveice10.opennbt.tag.builtin.ByteTag(currentKey, byteTag.getAsByte());
}
@Override
public void visitShort(ShortTag shortTag) {
currentTag = new com.github.steveice10.opennbt.tag.builtin.ShortTag(currentKey, shortTag.getAsShort());
}
@Override
public void visitInt(IntTag intTag) {
currentTag = new com.github.steveice10.opennbt.tag.builtin.IntTag(currentKey, intTag.getAsInt());
}
@Override
public void visitLong(LongTag longTag) {
currentTag = new com.github.steveice10.opennbt.tag.builtin.LongTag(currentKey, longTag.getAsLong());
}
@Override
public void visitFloat(FloatTag floatTag) {
currentTag = new com.github.steveice10.opennbt.tag.builtin.FloatTag(currentKey, floatTag.getAsFloat());
}
@Override
public void visitDouble(DoubleTag doubleTag) {
currentTag = new com.github.steveice10.opennbt.tag.builtin.DoubleTag(currentKey, doubleTag.getAsDouble());
}
@Override
public void visitByteArray(ByteArrayTag byteArrayTag) {
currentTag = new com.github.steveice10.opennbt.tag.builtin.ByteArrayTag(currentKey, byteArrayTag.getAsByteArray());
}
@Override
public void visitIntArray(IntArrayTag intArrayTag) {
currentTag = new com.github.steveice10.opennbt.tag.builtin.IntArrayTag(currentKey, intArrayTag.getAsIntArray());
}
@Override
public void visitLongArray(LongArrayTag longArrayTag) {
currentTag = new com.github.steveice10.opennbt.tag.builtin.LongArrayTag(currentKey, longArrayTag.getAsLongArray());
}
@Override
public void visitList(ListTag listTag) {
var newList = new com.github.steveice10.opennbt.tag.builtin.ListTag(currentKey);
for (Tag tag : listTag) {
currentKey = "";
tag.accept(this);
newList.add(currentTag);
}
currentTag = newList;
}
@Override
public void visitCompound(@NonNull CompoundTag compoundTag) {
currentTag = convert(currentKey, compoundTag);
}
private static com.github.steveice10.opennbt.tag.builtin.CompoundTag convert(String name, CompoundTag compoundTag) {
OpenNbtTagVisitor visitor = new OpenNbtTagVisitor(name);
for (String key : compoundTag.getAllKeys()) {
visitor.currentKey = key;
Tag tag = Objects.requireNonNull(compoundTag.get(key));
tag.accept(visitor);
visitor.root.put(visitor.currentTag);
}
return visitor.root;
}
@Override
public void visitEnd(@NonNull EndTag endTag) {
private static int getPageCount(ItemStack itemStack) {
WrittenBookContent writtenBookContent = itemStack.get(DataComponents.WRITTEN_BOOK_CONTENT);
if (writtenBookContent != null) {
return writtenBookContent.pages().size();
} else {
WritableBookContent writableBookContent = itemStack.get(DataComponents.WRITABLE_BOOK_CONTENT);
return writableBookContent != null ? writableBookContent.pages().size() : 0;
}
}
private static List<String> getPages(ItemStack itemStack) {
WrittenBookContent writtenBookContent = itemStack.get(DataComponents.WRITTEN_BOOK_CONTENT);
if (writtenBookContent != null) {
return writtenBookContent.pages().stream()
.map(Filterable::raw)
.map(GeyserModWorldManager::fromComponent)
.toList();
} else {
WritableBookContent writableBookContent = itemStack.get(DataComponents.WRITABLE_BOOK_CONTENT);
if (writableBookContent == null) {
return List.of();
}
return writableBookContent.pages().stream()
.map(Filterable::raw)
.toList();
}
}
private static String fromComponent(Component component) {
String json = Component.Serializer.toJson(component, RegistryAccess.EMPTY);
return LEGACY_SERIALIZER.serialize(GSON_SERIALIZER.deserializeOr(json, net.kyori.adventure.text.Component.empty()));
}
private static net.kyori.adventure.text.Component toKyoriComponent(Component component) {
String json = Component.Serializer.toJson(component, RegistryAccess.EMPTY);
return GSON_SERIALIZER.deserializeOr(json, net.kyori.adventure.text.Component.empty());
}
private static List<BannerPatternLayer> toPatternList(BannerPatternLayers patternLayers) {
return patternLayers.layers().stream()
.map(layer -> {
BannerPatternLayer.BannerPattern pattern = new BannerPatternLayer.BannerPattern(
layer.pattern().value().assetId().toString(), layer.pattern().value().translationKey()
);
return new BannerPatternLayer(Holder.ofCustom(pattern), layer.color().getId());
})
.toList();
}
}

Datei anzeigen

@ -11,6 +11,9 @@ dependencies {
implementation(variantOf(libs.adapters.spigot) {
classifier("all") // otherwise the unshaded jar is used without the shaded NMS implementations
})
implementation(variantOf(libs.adapters.paper) {
classifier("all") // otherwise the unshaded jar is used without the shaded NMS implementations
})
implementation(libs.commodore)
@ -19,7 +22,10 @@ dependencies {
compileOnly(libs.folia.api)
compileOnly(libs.paper.mojangapi)
compileOnlyApi(libs.viaversion)
implementation(libs.floodgate.spigot)
compileOnly("com.mojang", "authlib", "1.5.21")
}
platformRelocate("it.unimi.dsi.fastutil")
@ -32,9 +38,15 @@ platformRelocate("org.yaml") // Broken as of 1.20
// These dependencies are already present on the platform
provided(libs.viaversion)
provided("com.mojang", "authlib", "1.5.21")
provided("com.mojang", "authlib")
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
// Prevents Paper 1.20.5+ from remapping Geyser
manifest {
attributes["paperweight-mappings-namespace"] = "mojang"
}
archiveBaseName.set("Geyser-Spigot")
dependencies {
@ -42,6 +54,7 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
// We cannot shade Netty, or else native libraries will not load
// Needed because older Spigot builds do not provide the haproxy module
exclude(dependency("io.netty.incubator:.*"))
exclude(dependency("io.netty:netty-transport-classes-epoll:.*"))
exclude(dependency("io.netty:netty-transport-native-epoll:.*"))
exclude(dependency("io.netty:netty-transport-native-unix-common:.*"))

Datei anzeigen

@ -25,7 +25,7 @@
package org.geysermc.geyser.platform.spigot;
import com.github.steveice10.mc.protocol.MinecraftProtocol;
import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
import com.viaversion.viaversion.bukkit.handlers.BukkitChannelInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
@ -120,8 +120,12 @@ public class GeyserSpigotInjector extends GeyserInjector {
@Override
protected void initChannel(@NonNull Channel ch) throws Exception {
initChannel.invoke(childHandler, ch);
int index = ch.pipeline().names().indexOf("encoder");
String baseName = index != -1 ? "encoder" : "outbound_config";
if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserSpigotCompressionDisabler.ENABLED) {
ch.pipeline().addAfter("encoder", "geyser-compression-disabler", new GeyserSpigotCompressionDisabler());
ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserSpigotCompressionDisabler());
}
if (GeyserImpl.getInstance().getConfig().getRemote().authType() == AuthType.FLOODGATE) {
@ -186,6 +190,7 @@ public class GeyserSpigotInjector extends GeyserInjector {
bootstrap.getGeyserConfig().getRemote().port(), this.serverSocketAddress,
InetAddress.getLoopbackAddress().getHostAddress(), protocol, protocol.createHelper());
session.connect();
session.disconnect("");
}
@Override

Datei anzeigen

@ -47,6 +47,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.Constants;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.adapters.paper.PaperAdapters;
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.api.extension.Extension;
@ -249,16 +250,27 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
if (Boolean.parseBoolean(System.getProperty("Geyser.UseDirectAdapters", "true"))) {
try {
String name = Bukkit.getServer().getClass().getPackage().getName();
String nmsVersion = name.substring(name.lastIndexOf('.') + 1);
SpigotAdapters.registerWorldAdapter(nmsVersion);
boolean isPaper = false;
try {
String name = Bukkit.getServer().getClass().getPackage().getName();
String nmsVersion = name.substring(name.lastIndexOf('.') + 1);
SpigotAdapters.registerWorldAdapter(nmsVersion);
geyserLogger.debug("Using spigot NMS adapter for nms version: " + nmsVersion);
} catch (Exception e) { // Likely running on Paper 1.20.5+
//noinspection deprecation
int protocolVersion = Bukkit.getUnsafe().getProtocolVersion();
PaperAdapters.registerClosestWorldAdapter(protocolVersion);
isPaper = true;
geyserLogger.debug("Using paper world adapter for protocol version: " + protocolVersion);
}
if (isViaVersion && isViaVersionNeeded()) {
this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this);
this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this, isPaper);
} else {
// No ViaVersion
this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this);
this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this, isPaper);
}
geyserLogger.debug("Using NMS adapter: " + this.geyserWorldManager.getClass() + ", " + nmsVersion);
geyserLogger.debug("Using world manager of type: " + this.geyserWorldManager.getClass().getSimpleName());
} catch (Exception e) {
if (geyserConfig.isDebugMode()) {
geyserLogger.debug("Error while attempting to find NMS adapter. Most likely, this can be safely ignored. :)");

Datei anzeigen

@ -25,7 +25,7 @@
package org.geysermc.geyser.platform.spigot;
import com.github.steveice10.mc.protocol.data.DefaultComponentSerializer;
import org.geysermc.mcprotocollib.protocol.data.DefaultComponentSerializer;
import net.kyori.adventure.text.Component;
import org.bukkit.command.CommandSender;
import org.checkerframework.checker.nullness.qual.Nullable;

Datei anzeigen

@ -25,7 +25,7 @@
package org.geysermc.geyser.platform.spigot.world;
import com.github.steveice10.mc.protocol.data.game.level.block.value.PistonValueType;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.PistonValueType;
import org.cloudburstmc.math.vector.Vector3i;
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;

Datei anzeigen

@ -46,8 +46,8 @@ public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorl
private final Int2IntMap oldToNewBlockId;
public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin) {
super(plugin);
public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin, boolean isPaper) {
super(plugin, isPaper);
IntList allBlockStates = adapter.getAllBlockStates();
oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size());
ProtocolVersion serverVersion = plugin.getServerProtocolVersion();
@ -58,7 +58,7 @@ public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorl
int newBlockId = oldBlockId;
// protocolList should *not* be null; we checked for that before initializing this class
for (int i = protocolList.size() - 1; i >= 0; i--) {
MappingData mappingData = protocolList.get(i).getProtocol().getMappingData();
MappingData mappingData = protocolList.get(i).protocol().getMappingData();
if (mappingData != null) {
newBlockId = mappingData.getNewBlockStateId(newBlockId);
}

Datei anzeigen

@ -26,20 +26,26 @@
package org.geysermc.geyser.platform.spigot.world.manager;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.adapters.WorldAdapter;
import org.geysermc.geyser.adapters.paper.PaperAdapters;
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.session.GeyserSession;
public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
protected final SpigotWorldAdapter adapter;
protected final WorldAdapter<World> adapter;
public GeyserSpigotNativeWorldManager(Plugin plugin) {
public GeyserSpigotNativeWorldManager(Plugin plugin, boolean isPaper) {
super(plugin);
adapter = SpigotAdapters.getWorldAdapter();
if (isPaper) {
adapter = PaperAdapters.getWorldAdapter();
} else {
adapter = SpigotAdapters.getWorldAdapter();
}
}
@Override

Datei anzeigen

@ -25,9 +25,9 @@
package org.geysermc.geyser.platform.spigot.world.manager;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
@ -39,7 +39,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.nbt.NbtMap;
import org.geysermc.erosion.bukkit.BukkitLecterns;
import org.geysermc.erosion.bukkit.BukkitUtils;
import org.geysermc.erosion.bukkit.PickBlockUtils;
import org.geysermc.erosion.bukkit.SchedulerUtils;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.level.GameRule;
@ -205,8 +204,8 @@ public class GeyserSpigotWorldManager extends WorldManager {
}
@Override
public @NonNull CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
CompletableFuture<@Nullable CompoundTag> future = new CompletableFuture<>();
public @NonNull CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addNbtData) {
CompletableFuture<@Nullable DataComponents> future = new CompletableFuture<>();
Player bukkitPlayer;
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
future.complete(null);
@ -215,7 +214,7 @@ public class GeyserSpigotWorldManager extends WorldManager {
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
// Paper 1.19.3 complains about async access otherwise.
// java.lang.IllegalStateException: Tile is null, asynchronous access?
SchedulerUtils.runTask(this.plugin, () -> future.complete(PickBlockUtils.pickBlock(block)), block);
SchedulerUtils.runTask(this.plugin, () -> future.complete(/*PickBlockUtils.pickBlock(block)*/ null), block); // TODO fix erosion once clear how to handle this
return future;
}

Datei anzeigen

@ -6,20 +6,19 @@ dependencies {
implementation(libs.floodgate.velocity)
api(projects.core)
compileOnlyApi(libs.velocity.api) {
exclude(module = "org.yaml:snakeyaml")
}
compileOnlyApi(libs.velocity.api)
}
platformRelocate("com.fasterxml.jackson")
platformRelocate("it.unimi.dsi.fastutil")
platformRelocate("net.kyori.adventure.text.serializer.gson.legacyimpl")
platformRelocate("org.yaml.snakeyaml")
platformRelocate("org.yaml")
platformRelocate("org.bstats") //todo
exclude("com.google.*:*")
// Needed because Velocity provides every dependency except netty-resolver-dns
exclude("io.netty.incubator:.*")
exclude("io.netty:netty-transport-native-epoll:*")
exclude("io.netty:netty-transport-native-unix-common:*")
exclude("io.netty:netty-transport-native-kqueue:*")
@ -40,6 +39,9 @@ exclude("net.kyori:examination-string:*")
exclude("net.kyori:adventure-text-serializer-gson:*")
exclude("net.kyori:adventure-text-serializer-legacy:*")
exclude("net.kyori:adventure-nbt:*")
// These dependencies are already present on the platform
provided(libs.velocity.api)
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
archiveBaseName.set("Geyser-Velocity")
@ -57,6 +59,7 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
exclude(dependency("io.netty:netty-transport:.*"))
exclude(dependency("io.netty:netty-codec:.*"))
exclude(dependency("io.netty:netty-codec-haproxy:.*"))
exclude(dependency("io.netty.incubator:.*"))
exclude(dependency("org.slf4j:.*"))
exclude(dependency("org.ow2.asm:.*"))
// Exclude all Kyori dependencies except the legacy NBT serializer

Datei anzeigen

@ -26,6 +26,7 @@
package org.geysermc.geyser.platform.velocity;
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
import com.velocitypowered.api.network.ProtocolState;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.InboundConnection;
import com.velocitypowered.api.proxy.ProxyServer;
@ -88,6 +89,11 @@ public class GeyserVelocityPingPassthrough implements IGeyserPingPassthrough {
public ProtocolVersion getProtocolVersion() {
return ProtocolVersion.MAXIMUM_VERSION;
}
@Override
public ProtocolState getProtocolState() {
return ProtocolState.STATUS;
}
}
}

Datei anzeigen

@ -1,5 +1,7 @@
dependencies {
api(projects.core)
compileOnlyApi(libs.viaproxy)
}
platformRelocate("net.kyori")
@ -20,6 +22,7 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
dependencies {
exclude(dependency("com.google.*:.*"))
exclude(dependency("io.netty:.*"))
exclude(dependency("io.netty.incubator:.*"))
exclude(dependency("org.slf4j:.*"))
exclude(dependency("org.ow2.asm:.*"))
}

Datei anzeigen

@ -26,7 +26,7 @@ package org.geysermc.geyser.platform.viaproxy;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import net.raphimc.vialegacy.api.LegacyProtocolVersion;
import net.raphimc.viaproxy.cli.options.Options;
import net.raphimc.viaproxy.ViaProxy;
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
import java.io.File;
@ -43,7 +43,7 @@ public class GeyserViaProxyConfiguration extends GeyserJacksonConfiguration {
@Override
public int getPingPassthroughInterval() {
int interval = super.getPingPassthroughInterval();
if (interval < 15 && Options.PROTOCOL_VERSION != null && Options.PROTOCOL_VERSION.olderThanOrEqualTo(LegacyProtocolVersion.r1_6_4)) {
if (interval < 15 && ViaProxy.getConfig().getTargetVersion() != null && ViaProxy.getConfig().getTargetVersion().olderThanOrEqualTo(LegacyProtocolVersion.r1_6_4)) {
// <= 1.6.4 servers sometimes block incoming connections from an IP address if too many connections are made
interval = 15;
}

Datei anzeigen

@ -26,7 +26,6 @@ package org.geysermc.geyser.platform.viaproxy;
import lombok.Getter;
import net.raphimc.viaproxy.ViaProxy;
import net.raphimc.viaproxy.cli.options.Options;
import net.raphimc.viaproxy.plugins.ViaProxyPlugin;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.text.AsteriskSerializer;
@ -49,8 +48,8 @@ public class GeyserViaProxyDumpInfo extends BootstrapDumpInfo {
public GeyserViaProxyDumpInfo() {
this.platformVersion = ViaProxy.VERSION;
this.onlineMode = Options.ONLINE_MODE;
if (Options.BIND_ADDRESS instanceof InetSocketAddress inetSocketAddress) {
this.onlineMode = ViaProxy.getConfig().isProxyOnlineMode();
if (ViaProxy.getConfig().getBindAddress() instanceof InetSocketAddress inetSocketAddress) {
this.serverIP = inetSocketAddress.getHostString();
this.serverPort = inetSocketAddress.getPort();
} else {

Datei anzeigen

@ -25,7 +25,6 @@
package org.geysermc.geyser.platform.viaproxy;
import net.raphimc.viaproxy.plugins.PluginManager;
import org.geysermc.geyser.GeyserMain;
public class GeyserViaProxyMain extends GeyserMain {
@ -39,7 +38,7 @@ public class GeyserViaProxyMain extends GeyserMain {
}
public String getPluginFolder() {
return PluginManager.PLUGINS_DIR.getName();
return "plugins";
}
}

Datei anzeigen

@ -27,7 +27,6 @@ package org.geysermc.geyser.platform.viaproxy;
import net.lenni0451.lambdaevents.EventHandler;
import net.raphimc.vialegacy.api.LegacyProtocolVersion;
import net.raphimc.viaproxy.ViaProxy;
import net.raphimc.viaproxy.cli.options.Options;
import net.raphimc.viaproxy.plugins.PluginManager;
import net.raphimc.viaproxy.plugins.ViaProxyPlugin;
import net.raphimc.viaproxy.plugins.events.ConsoleCommandEvent;
@ -137,7 +136,7 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
GeyserImpl.start();
if (Options.PROTOCOL_VERSION != null && Options.PROTOCOL_VERSION.newerThanOrEqualTo(LegacyProtocolVersion.b1_8tob1_8_1)) {
if (ViaProxy.getConfig().getTargetVersion() != null && ViaProxy.getConfig().getTargetVersion().newerThanOrEqualTo(LegacyProtocolVersion.b1_8tob1_8_1)) {
// Only initialize the ping passthrough if the protocol version is above beta 1.7.3, as that's when the status protocol was added
this.pingPassthrough = GeyserLegacyPingPassthrough.init(this.geyser);
}
@ -186,19 +185,19 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
@NotNull
@Override
public String getServerBindAddress() {
if (Options.BIND_ADDRESS instanceof InetSocketAddress socketAddress) {
if (ViaProxy.getConfig().getBindAddress() instanceof InetSocketAddress socketAddress) {
return socketAddress.getHostString();
} else {
throw new IllegalStateException("Unsupported bind address type: " + Options.BIND_ADDRESS.getClass().getName());
throw new IllegalStateException("Unsupported bind address type: " + ViaProxy.getConfig().getBindAddress().getClass().getName());
}
}
@Override
public int getServerPort() {
if (Options.BIND_ADDRESS instanceof InetSocketAddress socketAddress) {
if (ViaProxy.getConfig().getBindAddress() instanceof InetSocketAddress socketAddress) {
return socketAddress.getPort();
} else {
throw new IllegalStateException("Unsupported bind address type: " + Options.BIND_ADDRESS.getClass().getName());
throw new IllegalStateException("Unsupported bind address type: " + ViaProxy.getConfig().getBindAddress().getClass().getName());
}
}

Datei anzeigen

@ -2,4 +2,4 @@ name: "${name}-ViaProxy"
version: "${version}"
author: "${author}"
main: "org.geysermc.geyser.platform.viaproxy.GeyserViaProxyPlugin"
min-version: "3.2.0"
min-version: "3.2.1"

Datei anzeigen

@ -58,19 +58,20 @@ fun Project.platformRelocate(pattern: String, exclusion: String = "") {
val providedDependencies = mutableMapOf<String, MutableSet<String>>()
fun Project.provided(pattern: String, name: String, version: String, excludedOn: Int = 0b110) {
fun getProvidedDependenciesForProject(projectName: String): MutableSet<String> {
return providedDependencies.getOrDefault(projectName, emptySet()).toMutableSet()
}
fun Project.provided(pattern: String, name: String, excludedOn: Int = 0b110) {
providedDependencies.getOrPut(project.name) { mutableSetOf() }
.add("${calcExclusion(pattern, 0b100, excludedOn)}:" +
"${calcExclusion(name, 0b10, excludedOn)}:" +
calcExclusion(version, 0b1, excludedOn))
dependencies.add("compileOnlyApi", "$pattern:$name:$version")
.add("${calcExclusion(pattern, 0b100, excludedOn)}:${calcExclusion(name, 0b10, excludedOn)}")
}
fun Project.provided(dependency: ProjectDependency) =
provided(dependency.group!!, dependency.name, dependency.version!!)
provided(dependency.group!!, dependency.name)
fun Project.provided(dependency: MinimalExternalModuleDependency) =
provided(dependency.module.group, dependency.module.name, dependency.versionConstraint.requiredVersion)
provided(dependency.module.group, dependency.module.name)
fun Project.provided(provider: Provider<MinimalExternalModuleDependency>) =
provided(provider.get())

Datei anzeigen

@ -23,7 +23,7 @@ indra {
tasks {
processResources {
// Spigot, BungeeCord, Velocity, Fabric, ViaProxy, NeoForge
filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json", "fabric.mod.json", "viaproxy.yml", "META-INF/mods.toml")) {
filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json", "fabric.mod.json", "viaproxy.yml", "META-INF/neoforge.mods.toml")) {
expand(
"id" to "geyser",
"name" to "Geyser",

Datei anzeigen

@ -11,14 +11,51 @@ plugins {
id("com.modrinth.minotaur")
}
// These are provided by Minecraft/modded platforms already, no need to include them
provided("com.google.code.gson", "gson")
provided("com.google.guava", ".*")
provided("org.slf4j", "slf4j-api")
provided("com.nukkitx.fastutil", ".*")
provided("org.cloudburstmc.fastutil.maps", ".*")
provided("org.cloudburstmc.fastutil.sets", ".*")
provided("org.cloudburstmc.fastutil.commons", ".*")
provided("org.cloudburstmc.fastutil", ".*")
provided("org.checkerframework", "checker-qual")
provided("io.netty", "netty-transport-classes-epoll")
provided("io.netty", "netty-transport-native-epoll")
provided("io.netty", "netty-transport-native-unix-common")
provided("io.netty", "netty-transport-classes-kqueue")
provided("io.netty", "netty-transport-native-kqueue")
provided("io.netty.incubator", "netty-incubator-transport-native-io_uring")
provided("io.netty.incubator", "netty-incubator-transport-classes-io_uring")
provided("io.netty", "netty-handler")
provided("io.netty", "netty-common")
provided("io.netty", "netty-buffer")
provided("io.netty", "netty-resolver")
provided("io.netty", "netty-transport")
provided("io.netty", "netty-codec")
provided("io.netty", "netty-resolver-dns")
provided("io.netty", "netty-resolver-dns-native-macos")
provided("org.ow2.asm", "asm")
architectury {
minecraft = "1.20.4"
minecraft = "1.20.5"
}
loom {
silentMojangMappingsLicense()
}
indra {
javaVersions {
target(21)
}
}
configurations {
create("includeTransitive").isTransitive = true
}
tasks {
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
@ -34,28 +71,6 @@ tasks {
// The remapped shadowJar is the final desired mod jar
archiveVersion.set(project.version.toString())
archiveClassifier.set("shaded")
relocate("org.objectweb.asm", "org.geysermc.relocate.asm")
relocate("org.yaml", "org.geysermc.relocate.yaml") // https://github.com/CardboardPowered/cardboard/issues/139
relocate("com.fasterxml.jackson", "org.geysermc.relocate.jackson")
relocate("net.kyori", "org.geysermc.relocate.kyori")
dependencies {
// Exclude everything EXCEPT some DNS stuff required for HAProxy
exclude(dependency("io.netty:netty-transport-classes-epoll:.*"))
exclude(dependency("io.netty:netty-transport-native-epoll:.*"))
exclude(dependency("io.netty:netty-transport-native-unix-common:.*"))
exclude(dependency("io.netty:netty-transport-classes-kqueue:.*"))
exclude(dependency("io.netty:netty-transport-native-kqueue:.*"))
exclude(dependency("io.netty:netty-handler:.*"))
exclude(dependency("io.netty:netty-common:.*"))
exclude(dependency("io.netty:netty-buffer:.*"))
exclude(dependency("io.netty:netty-resolver:.*"))
exclude(dependency("io.netty:netty-transport:.*"))
exclude(dependency("io.netty:netty-codec:.*"))
exclude(dependency("io.netty:netty-resolver-dns:.*"))
exclude(dependency("io.netty:netty-resolver-dns-native-macos:.*"))
}
}
remapJar {
@ -73,12 +88,34 @@ tasks {
}
}
afterEvaluate {
val providedDependencies = getProvidedDependenciesForProject(project.name)
// These are shaded, no need to JiJ them
configurations["shadow"].dependencies.forEach {shadowed ->
println("Not including shadowed dependency: ${shadowed.group}:${shadowed.name}")
providedDependencies.add("${shadowed.group}:${shadowed.name}")
}
// Now: Include all transitive dependencies that aren't excluded
configurations["includeTransitive"].resolvedConfiguration.resolvedArtifacts.forEach { dep ->
if (!providedDependencies.contains("${dep.moduleVersion.id.group}:${dep.moduleVersion.id.name}")
and !providedDependencies.contains("${dep.moduleVersion.id.group}:.*")) {
println("Including dependency via JiJ: ${dep.id}")
dependencies.add("include", dep.moduleVersion.id.toString())
} else {
println("Not including ${dep.id} for ${project.name}!")
}
}
}
dependencies {
minecraft("com.mojang:minecraft:1.20.4")
minecraft("com.mojang:minecraft:1.20.5")
mappings(loom.officialMojangMappings())
}
repositories {
// mavenLocal()
maven("https://repo.opencollab.dev/maven-releases/")
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://jitpack.io")
@ -97,6 +134,6 @@ modrinth {
syncBodyFrom.set(rootProject.file("README.md").readText())
uploadFile.set(tasks.getByPath("remapModrinthJar"))
gameVersions.addAll("1.20.4")
gameVersions.addAll("1.20.5", "1.20.6")
failSilently.set(true)
}

Datei anzeigen

@ -7,3 +7,9 @@ indra {
publishSnapshotsTo("geysermc", "https://repo.opencollab.dev/maven-snapshots")
publishReleasesTo("geysermc", "https://repo.opencollab.dev/maven-releases")
}
publishing {
// skip shadow jar from publishing. Workaround for https://github.com/johnrengelman/shadow/issues/651
val javaComponent = project.components["java"] as AdhocComponentWithVariants
javaComponent.withVariantsFromConfiguration(configurations["shadowRuntimeElements"]) { skip() }
}

Datei anzeigen

@ -7,7 +7,6 @@ plugins {
tasks {
named<Jar>("jar") {
archiveClassifier.set("unshaded")
from(project.rootProject.file("LICENSE"))
}
val shadowJar = named<ShadowJar>("shadowJar") {

Datei anzeigen

@ -6,6 +6,10 @@ plugins {
}
dependencies {
constraints {
implementation(libs.raknet) // Ensure protocol does not override the RakNet version
}
api(libs.floodgate.core)
compileOnlyApi(libs.base.api)
compileOnlyApi(projects.isolation)
@ -44,6 +48,8 @@ dependencies {
implementation(libs.netty.transport.native.epoll) { artifact { classifier = "linux-x86_64" } }
implementation(libs.netty.transport.native.epoll) { artifact { classifier = "linux-aarch_64" } }
implementation(libs.netty.transport.native.kqueue) { artifact { classifier = "osx-x86_64" } }
implementation(libs.netty.transport.native.io.uring) { artifact { classifier = "linux-x86_64" } }
implementation(libs.netty.transport.native.io.uring) { artifact { classifier = "linux-aarch_64" } }
// Adventure text serialization
api(libs.bundles.adventure)
@ -64,11 +70,6 @@ dependencies {
api(libs.events)
}
configurations.api {
// This is still experimental - additionally, it could only really benefit standalone
exclude(group = "io.netty.incubator", module = "netty-incubator-transport-native-io_uring")
}
tasks.processResources {
// This is solely for backwards compatibility for other programs that used this file before the switch to gradle.
// It used to be generated by the maven Git-Commit-Id-Plugin
@ -99,7 +100,7 @@ configure<BlossomExtension> {
}
fun Project.buildNumber(): Int =
(System.getenv("GITHUB_RUN_NUMBER") ?: jenkinsBuildNumber())?.let { Integer.parseInt(it) } ?: -1
(System.getenv("BUILD_NUMBER"))?.let { Integer.parseInt(it) } ?: -1
inner class GitInfo {
val branch: String
@ -132,9 +133,6 @@ inner class GitInfo {
}
}
// todo remove this when we're not using Jenkins anymore
fun jenkinsBuildNumber(): String? = System.getenv("BUILD_NUMBER")
// Manual task to download the bedrock data files from the CloudburstMC/Data repository
// Invoke with ./gradlew :core:downloadBedrockData --suffix=1_20_70
// Set suffix to the current Bedrock version

Datei anzeigen

@ -29,7 +29,6 @@ import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.steveice10.packetlib.tcp.TcpSession;
import io.netty.channel.epoll.Epoll;
import io.netty.util.NettyRuntime;
import io.netty.util.concurrent.DefaultThreadFactory;
@ -46,7 +45,6 @@ import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.geysermc.api.Geyser;
import org.geysermc.cumulus.form.Form;
import org.geysermc.cumulus.form.util.FormBuilder;
import org.geysermc.erosion.packet.Packets;
import org.geysermc.floodgate.core.FloodgatePlatform;
import org.geysermc.geyser.api.GeyserApi;
import org.geysermc.geyser.api.command.CommandSource;
@ -86,6 +84,7 @@ import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.text.MinecraftLocale;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.*;
import org.geysermc.mcprotocollib.network.tcp.TcpSession;
import java.io.File;
import java.io.FileWriter;
@ -394,7 +393,7 @@ public class GeyserImpl implements GeyserApi {
pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.getPendingAuthenticationTimeout());
Packets.initGeyser();
//Packets.initGeyser();
if (Epoll.isAvailable()) {
this.erosionUnixListener = new UnixSocketClientListener();

Datei anzeigen

@ -25,12 +25,12 @@
package org.geysermc.geyser.command.defaults;
import com.github.steveice10.mc.protocol.data.game.ClientCommand;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundClientCommandPacket;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.ClientCommand;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundClientCommandPacket;
public class StatisticsCommand extends GeyserCommand {

Datei anzeigen

@ -40,9 +40,11 @@ import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.network.CIDRMatcher;
import org.geysermc.geyser.text.AsteriskSerializer;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.WebUtils;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
@ -245,7 +247,18 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
List<CIDRMatcher> matchers = this.whitelistedIPsMatchers;
if (matchers == null) {
synchronized (this) {
this.whitelistedIPsMatchers = matchers = proxyProtocolWhitelistedIPs.stream()
// Check if proxyProtocolWhitelistedIPs contains URLs we need to fetch and parse by line
List<String> whitelistedCIDRs = new ArrayList<>();
for (String ip: proxyProtocolWhitelistedIPs) {
if (!ip.startsWith("http")) {
whitelistedCIDRs.add(ip);
continue;
}
WebUtils.getLineStream(ip).forEach(whitelistedCIDRs::add);
}
this.whitelistedIPsMatchers = matchers = whitelistedCIDRs.stream()
.map(CIDRMatcher::new)
.collect(Collectors.toList());
}

Datei anzeigen

@ -27,8 +27,8 @@ package org.geysermc.geyser.dump;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.text.AsteriskSerializer;
import java.util.List;

Datei anzeigen

@ -55,12 +55,7 @@ import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.*;
import java.util.stream.Collectors;
@Getter
@ -77,6 +72,7 @@ public class DumpInfo {
private final GeyserConfiguration config;
private final Floodgate floodgate;
private final Object2IntMap<BedrockPlatform> userPlatforms;
private final int connectionAttempts;
private final HashInfo hashInfo;
private final RamInfo ramInfo;
private LogsInfo logsInfo;
@ -128,6 +124,8 @@ public class DumpInfo {
userPlatforms.put(device, userPlatforms.getOrDefault(device, 0) + 1);
}
this.connectionAttempts = GeyserImpl.getInstance().getGeyserServer().getConnectionAttempts();
this.bootstrapInfo = GeyserImpl.getInstance().getBootstrap().getDumpInfo();
this.flagsInfo = new FlagsInfo();

Datei anzeigen

@ -25,14 +25,15 @@
package org.geysermc.geyser.entity;
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.type.EntityType;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.entity.factory.EntityFactory;
import org.geysermc.geyser.entity.properties.GeyserEntityProperties;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.translator.entity.EntityMetadataTranslator;
@ -49,10 +50,10 @@ import java.util.function.BiConsumer;
* @param <T> the entity type this definition represents
*/
public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, EntityType entityType, String identifier,
float width, float height, float offset, List<EntityMetadataTranslator<? super T, ?, ?>> translators) {
float width, float height, float offset, GeyserEntityProperties registeredProperties, List<EntityMetadataTranslator<? super T, ?, ?>> translators) {
public static <T extends Entity> Builder<T> inherited(EntityFactory<T> factory, EntityDefinition<? super T> parent) {
return new Builder<>(factory, parent.entityType, parent.identifier, parent.width, parent.height, parent.offset, new ObjectArrayList<>(parent.translators));
return new Builder<>(factory, parent.entityType, parent.identifier, parent.width, parent.height, parent.offset, parent.registeredProperties, new ObjectArrayList<>(parent.translators));
}
public static <T extends Entity> Builder<T> builder(EntityFactory<T> factory) {
@ -87,6 +88,7 @@ public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, Entit
private float width;
private float height;
private float offset = 0.00001f;
private GeyserEntityProperties registeredProperties;
private final List<EntityMetadataTranslator<? super T, ?, ?>> translators;
private Builder(EntityFactory<T> factory) {
@ -94,13 +96,14 @@ public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, Entit
translators = new ObjectArrayList<>();
}
public Builder(EntityFactory<T> factory, EntityType type, String identifier, float width, float height, float offset, List<EntityMetadataTranslator<? super T, ?, ?>> translators) {
public Builder(EntityFactory<T> factory, EntityType type, String identifier, float width, float height, float offset, GeyserEntityProperties registeredProperties, List<EntityMetadataTranslator<? super T, ?, ?>> translators) {
this.factory = factory;
this.type = type;
this.identifier = identifier;
this.width = width;
this.height = height;
this.offset = offset;
this.registeredProperties = registeredProperties;
this.translators = translators;
}
@ -127,6 +130,11 @@ public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, Entit
return this;
}
public Builder<T> properties(GeyserEntityProperties registeredProperties) {
this.registeredProperties = registeredProperties;
return this;
}
public <U, EM extends EntityMetadata<U, ? extends MetadataType<U>>> Builder<T> addTranslator(MetadataType<U> type, BiConsumer<T, EM> translateFunction) {
translators.add(new EntityMetadataTranslator<>(type, translateFunction));
return this;
@ -149,10 +157,13 @@ public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, Entit
if (identifier == null && type != null) {
identifier = "minecraft:" + type.name().toLowerCase(Locale.ROOT);
}
EntityDefinition<T> definition = new EntityDefinition<>(factory, type, identifier, width, height, offset, translators);
EntityDefinition<T> definition = new EntityDefinition<>(factory, type, identifier, width, height, offset, registeredProperties, translators);
if (register && definition.entityType() != null) {
Registries.ENTITY_DEFINITIONS.get().putIfAbsent(definition.entityType(), definition);
Registries.JAVA_ENTITY_IDENTIFIERS.get().putIfAbsent("minecraft:" + type.name().toLowerCase(Locale.ROOT), definition);
if (definition.registeredProperties() != null) {
Registries.BEDROCK_ENTITY_PROPERTIES.get().add(definition.registeredProperties().toNbtMap(identifier));
}
}
return definition;
}

Datei anzeigen

@ -25,12 +25,14 @@
package org.geysermc.geyser.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType;
import org.geysermc.geyser.entity.type.living.monster.raid.RavagerEntity;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.properties.GeyserEntityProperties;
import org.geysermc.geyser.entity.type.*;
import org.geysermc.geyser.entity.type.living.*;
import org.geysermc.geyser.entity.type.living.animal.*;
@ -53,8 +55,9 @@ import org.geysermc.geyser.translator.text.MessageTranslator;
public final class EntityDefinitions {
public static final EntityDefinition<AllayEntity> ALLAY;
public static final EntityDefinition<AreaEffectCloudEntity> AREA_EFFECT_CLOUD;
public static final EntityDefinition<ArmadilloEntity> ARMADILLO;
public static final EntityDefinition<ArmorStandEntity> ARMOR_STAND;
public static final EntityDefinition<TippedArrowEntity> ARROW;
public static final EntityDefinition<ArrowEntity> ARROW;
public static final EntityDefinition<AxolotlEntity> AXOLOTL;
public static final EntityDefinition<BatEntity> BAT;
public static final EntityDefinition<BeeEntity> BEE;
@ -130,7 +133,7 @@ public final class EntityDefinitions {
public static final EntityDefinition<ThrownPotionEntity> POTION;
public static final EntityDefinition<PufferFishEntity> PUFFERFISH;
public static final EntityDefinition<RabbitEntity> RABBIT;
public static final EntityDefinition<RaidParticipantEntity> RAVAGER;
public static final EntityDefinition<RavagerEntity> RAVAGER;
public static final EntityDefinition<AbstractFishEntity> SALMON;
public static final EntityDefinition<SheepEntity> SHEEP;
public static final EntityDefinition<ShulkerEntity> SHULKER;
@ -200,7 +203,6 @@ public final class EntityDefinitions {
.type(EntityType.AREA_EFFECT_CLOUD)
.height(0.5f).width(1.0f)
.addTranslator(MetadataType.FLOAT, AreaEffectCloudEntity::setRadius)
.addTranslator(MetadataType.INT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityDataTypes.EFFECT_COLOR, entityMetadata.getValue()))
.addTranslator(null) // Waiting
.addTranslator(MetadataType.PARTICLE, AreaEffectCloudEntity::setParticle)
.build();
@ -376,10 +378,10 @@ public final class EntityDefinitions {
.addTranslator(MetadataType.BYTE, AbstractArrowEntity::setArrowFlags)
.addTranslator(null) // "Piercing level"
.build();
ARROW = EntityDefinition.inherited(TippedArrowEntity::new, abstractArrowBase)
ARROW = EntityDefinition.inherited(ArrowEntity::new, abstractArrowBase)
.type(EntityType.ARROW)
.heightAndWidth(0.25f)
.addTranslator(MetadataType.INT, TippedArrowEntity::setPotionEffectColor)
.addTranslator(MetadataType.INT, ArrowEntity::setPotionEffectColor)
.build();
SPECTRAL_ARROW = EntityDefinition.inherited(abstractArrowBase.factory(), abstractArrowBase)
.type(EntityType.SPECTRAL_ARROW)
@ -452,8 +454,7 @@ public final class EntityDefinitions {
EntityDefinition<LivingEntity> livingEntityBase = EntityDefinition.inherited(LivingEntity::new, entityBase)
.addTranslator(MetadataType.BYTE, LivingEntity::setLivingEntityFlags)
.addTranslator(MetadataType.FLOAT, LivingEntity::setHealth)
.addTranslator(MetadataType.INT,
(livingEntity, entityMetadata) -> livingEntity.getDirtyMetadata().put(EntityDataTypes.EFFECT_COLOR, entityMetadata.getValue()))
.addTranslator(MetadataType.PARTICLES, LivingEntity::setParticles)
.addTranslator(MetadataType.BOOLEAN,
(livingEntity, entityMetadata) -> livingEntity.getDirtyMetadata().put(EntityDataTypes.EFFECT_AMBIENCE, (byte) (((BooleanEntityMetadata) entityMetadata).getPrimitiveValue() ? 1 : 0)))
.addTranslator(null) // Arrow count
@ -672,7 +673,7 @@ public final class EntityDefinitions {
SLIME = EntityDefinition.inherited(SlimeEntity::new, mobEntityBase)
.type(EntityType.SLIME)
.heightAndWidth(0.51f)
.addTranslator(MetadataType.INT, SlimeEntity::setScale)
.addTranslator(MetadataType.INT, SlimeEntity::setSlimeScale)
.build();
MAGMA_CUBE = EntityDefinition.inherited(MagmaCubeEntity::new, SLIME)
.type(EntityType.MAGMA_CUBE)
@ -745,9 +746,9 @@ public final class EntityDefinitions {
.type(EntityType.PILLAGER)
.height(1.8f).width(0.6f)
.offset(1.62f)
.addTranslator(null) // Charging; doesn't have an equivalent on Bedrock //TODO check
.addTranslator(MetadataType.BOOLEAN, PillagerEntity::setChargingCrossbow)
.build();
RAVAGER = EntityDefinition.inherited(raidParticipantEntityBase.factory(), raidParticipantEntityBase)
RAVAGER = EntityDefinition.inherited(RavagerEntity::new, raidParticipantEntityBase)
.type(EntityType.RAVAGER)
.height(1.9f).width(1.2f)
.build();
@ -770,6 +771,20 @@ public final class EntityDefinitions {
// Extends ageable
{
ARMADILLO = EntityDefinition.inherited(ArmadilloEntity::new, ageableEntityBase)
.type(EntityType.ARMADILLO)
.height(0.65f).width(0.7f)
.properties(new GeyserEntityProperties.Builder()
.addEnum(
"minecraft:armadillo_state",
"unrolled",
"rolled_up",
"rolled_up_peeking",
"rolled_up_relaxing",
"rolled_up_unrolling")
.build())
.addTranslator(MetadataType.ARMADILLO_STATE, ArmadilloEntity::setArmadilloState)
.build();
AXOLOTL = EntityDefinition.inherited(AxolotlEntity::new, ageableEntityBase)
.type(EntityType.AXOLOTL)
.height(0.42f).width(0.7f)
@ -780,6 +795,9 @@ public final class EntityDefinitions {
BEE = EntityDefinition.inherited(BeeEntity::new, ageableEntityBase)
.type(EntityType.BEE)
.heightAndWidth(0.6f)
.properties(new GeyserEntityProperties.Builder()
.addBoolean("minecraft:has_nectar")
.build())
.addTranslator(MetadataType.BYTE, BeeEntity::setBeeFlags)
.addTranslator(MetadataType.INT, BeeEntity::setAngerTime)
.build();
@ -937,8 +955,7 @@ public final class EntityDefinitions {
LLAMA = EntityDefinition.inherited(LlamaEntity::new, chestedHorseEntityBase)
.type(EntityType.LLAMA)
.height(1.87f).width(0.9f)
.addTranslator(MetadataType.INT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityDataTypes.STRENGTH, entityMetadata.getValue()))
.addTranslator(MetadataType.INT, LlamaEntity::setCarpetedColor)
.addTranslator(MetadataType.INT, LlamaEntity::setStrength)
.addTranslator(MetadataType.INT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityDataTypes.VARIANT, entityMetadata.getValue()))
.build();
TRADER_LLAMA = EntityDefinition.inherited(TraderLlamaEntity::new, LLAMA)
@ -947,7 +964,7 @@ public final class EntityDefinitions {
.build();
}
EntityDefinition<TameableEntity> tameableEntityBase = EntityDefinition.inherited(TameableEntity::new, ageableEntityBase)
EntityDefinition<TameableEntity> tameableEntityBase = EntityDefinition.<TameableEntity>inherited(null, ageableEntityBase) // No factory, is abstract
.addTranslator(MetadataType.BYTE, TameableEntity::setTameableFlags)
.addTranslator(MetadataType.OPTIONAL_UUID, TameableEntity::setOwner)
.build();
@ -971,6 +988,7 @@ public final class EntityDefinitions {
.addTranslator(MetadataType.BOOLEAN, (wolfEntity, entityMetadata) -> wolfEntity.setFlag(EntityFlag.INTERESTED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
.addTranslator(MetadataType.INT, WolfEntity::setCollarColor)
.addTranslator(MetadataType.INT, WolfEntity::setWolfAngerTime)
.addTranslator(MetadataType.WOLF_VARIANT, WolfEntity::setWolfVariant)
.build();
// As of 1.18 these don't track entity data at all

Datei anzeigen

@ -49,9 +49,10 @@ public enum GeyserAttributeType {
ATTACK_KNOCKBACK("minecraft:generic.attack_knockback", null, 1.5f, Float.MAX_VALUE, 0f),
ATTACK_SPEED("minecraft:generic.attack_speed", null, 0f, 1024f, 4f),
MAX_HEALTH("minecraft:generic.max_health", null, 0f, 1024f, 20f),
SCALE("minecraft:generic.scale", null, 0.0625f, 16f, 1f), // Unused. Do we need this?
// Bedrock Attributes
ABSORPTION(null, "minecraft:absorption", 0f, Float.MAX_VALUE, 0f),
ABSORPTION(null, "minecraft:absorption", 0f, 1024f, 0f),
EXHAUSTION(null, "minecraft:player.exhaustion", 0f, 5f, 0f),
EXPERIENCE(null, "minecraft:player.experience", 0f, 1f, 0f),
EXPERIENCE_LEVEL(null, "minecraft:player.level", 0f, 24791.00f, 0f),
@ -66,6 +67,10 @@ public enum GeyserAttributeType {
private final float maximum;
private final float defaultValue;
public AttributeData getAttribute() {
return getAttribute(defaultValue);
}
public AttributeData getAttribute(float value) {
return getAttribute(value, maximum);
}

Datei anzeigen

@ -0,0 +1,165 @@
/*
* Copyright (c) 2019-2024 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.geyser.entity.properties;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.entity.properties.type.BooleanProperty;
import org.geysermc.geyser.entity.properties.type.EnumProperty;
import org.geysermc.geyser.entity.properties.type.FloatProperty;
import org.geysermc.geyser.entity.properties.type.IntProperty;
import org.geysermc.geyser.entity.properties.type.PropertyType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@EqualsAndHashCode
@ToString
public class GeyserEntityProperties {
private final ObjectArrayList<PropertyType> properties;
private final Object2IntMap<String> propertyIndices;
private GeyserEntityProperties(ObjectArrayList<PropertyType> properties,
Object2IntMap<String> propertyIndices) {
this.properties = properties;
this.propertyIndices = propertyIndices;
}
public NbtMap toNbtMap(String entityType) {
NbtMapBuilder mapBuilder = NbtMap.builder();
List<NbtMap> nbtProperties = new ArrayList<>();
for (PropertyType property : properties) {
nbtProperties.add(property.nbtMap());
}
mapBuilder.putList("properties", NbtType.COMPOUND, nbtProperties);
return mapBuilder.putString("type", entityType).build();
}
public @NonNull List<PropertyType> getProperties() {
return properties;
}
public int getPropertyIndex(String name) {
return propertyIndices.getOrDefault(name, -1);
}
public static class Builder {
private final ObjectArrayList<PropertyType> properties = new ObjectArrayList<>();
private final Object2IntMap<String> propertyIndices = new Object2IntOpenHashMap<>();
public Builder addInt(@NonNull String name, int min, int max) {
if (propertyIndices.containsKey(name)) {
throw new IllegalArgumentException(
"Property with name " + name + " already exists on builder!");
}
PropertyType property = new IntProperty(name, min, max);
this.properties.add(property);
propertyIndices.put(name, properties.size() - 1);
return this;
}
public Builder addInt(@NonNull String name) {
if (propertyIndices.containsKey(name)) {
throw new IllegalArgumentException(
"Property with name " + name + " already exists on builder!");
}
PropertyType property = new IntProperty(name, Integer.MIN_VALUE, Integer.MAX_VALUE);
this.properties.add(property);
propertyIndices.put(name, properties.size() - 1);
return this;
}
public Builder addFloat(@NonNull String name, float min, float max) {
if (propertyIndices.containsKey(name)) {
throw new IllegalArgumentException(
"Property with name " + name + " already exists on builder!");
}
PropertyType property = new FloatProperty(name, min, max);
this.properties.add(property);
propertyIndices.put(name, properties.size() - 1);
return this;
}
public Builder addFloat(@NonNull String name) {
if (propertyIndices.containsKey(name)) {
throw new IllegalArgumentException(
"Property with name " + name + " already exists on builder!");
}
PropertyType property = new FloatProperty(name, Float.MIN_NORMAL, Float.MAX_VALUE);
this.properties.add(property);
propertyIndices.put(name, properties.size() - 1);
return this;
}
public Builder addBoolean(@NonNull String name) {
if (propertyIndices.containsKey(name)) {
throw new IllegalArgumentException(
"Property with name " + name + " already exists on builder!");
}
PropertyType property = new BooleanProperty(name);
this.properties.add(property);
propertyIndices.put(name, properties.size() - 1);
return this;
}
public Builder addEnum(@NonNull String name, List<String> values) {
if (propertyIndices.containsKey(name)) {
throw new IllegalArgumentException(
"Property with name " + name + " already exists on builder!");
}
PropertyType property = new EnumProperty(name, values);
this.properties.add(property);
propertyIndices.put(name, properties.size() - 1);
return this;
}
public Builder addEnum(@NonNull String name, String... values) {
if (propertyIndices.containsKey(name)) {
throw new IllegalArgumentException(
"Property with name " + name + " already exists on builder!");
}
List<String> valuesList = Arrays.asList(values); // Convert array to list
PropertyType property = new EnumProperty(name, valuesList);
this.properties.add(property);
propertyIndices.put(name, properties.size() - 1);
return this;
}
public GeyserEntityProperties build() {
return new GeyserEntityProperties(properties, propertyIndices);
}
}
}

Datei anzeigen

@ -0,0 +1,98 @@
/*
* Copyright (c) 2019-2023 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.geyser.entity.properties;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.cloudburstmc.protocol.bedrock.data.entity.FloatEntityProperty;
import org.cloudburstmc.protocol.bedrock.data.entity.IntEntityProperty;
import org.geysermc.geyser.entity.properties.type.EnumProperty;
import org.geysermc.geyser.entity.properties.type.PropertyType;
import java.util.List;
public class GeyserEntityPropertyManager {
private final GeyserEntityProperties properties;
private final ObjectArrayList<IntEntityProperty> intEntityProperties = new ObjectArrayList<>();
private final ObjectArrayList<FloatEntityProperty> floatEntityProperties = new ObjectArrayList<>();
public GeyserEntityPropertyManager(GeyserEntityProperties properties) {
this.properties = properties;
}
public void add(String propertyName, int value) {
int index = properties.getPropertyIndex(propertyName);
intEntityProperties.add(new IntEntityProperty(index, value));
}
public void add(String propertyName, boolean value) {
int index = properties.getPropertyIndex(propertyName);
intEntityProperties.add(new IntEntityProperty(index, value ? 1 : 0));
}
public void add(String propertyName, String value) {
int index = properties.getPropertyIndex(propertyName);
PropertyType property = properties.getProperties().get(index);
int enumIndex = ((EnumProperty) property).getIndex(value);
intEntityProperties.add(new IntEntityProperty(index, enumIndex));
}
public void add(String propertyName, float value) {
int index = properties.getPropertyIndex(propertyName);
floatEntityProperties.add(new FloatEntityProperty(index, value));
}
public boolean hasFloatProperties() {
return !this.floatEntityProperties.isEmpty();
}
public boolean hasIntProperties() {
return !this.intEntityProperties.isEmpty();
}
public boolean hasProperties() {
return hasFloatProperties() || hasIntProperties();
}
public ObjectArrayList<IntEntityProperty> intProperties() {
return this.intEntityProperties;
}
public void applyIntProperties(List<IntEntityProperty> properties) {
properties.addAll(intEntityProperties);
intEntityProperties.clear();
}
public ObjectArrayList<FloatEntityProperty> floatProperties() {
return this.floatEntityProperties;
}
public void applyFloatProperties(List<FloatEntityProperty> properties) {
properties.addAll(floatEntityProperties);
floatEntityProperties.clear();
}
}

Datei anzeigen

@ -0,0 +1,44 @@
/*
* Copyright (c) 2019-2024 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.geyser.entity.properties.type;
import org.cloudburstmc.nbt.NbtMap;
public class BooleanProperty implements PropertyType {
private final String name;
public BooleanProperty(String name) {
this.name = name;
}
@Override
public NbtMap nbtMap() {
return NbtMap.builder()
.putString("name", name)
.putInt("type", 2)
.build();
}
}

Datei anzeigen

@ -0,0 +1,61 @@
/*
* Copyright (c) 2019-2024 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.geyser.entity.properties.type;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtType;
import java.util.List;
public class EnumProperty implements PropertyType {
private final String name;
private final List<String> values;
private final Object2IntMap<String> valueIndexMap;
public EnumProperty(String name, List<String> values) {
this.name = name;
this.values = values;
this.valueIndexMap = new Object2IntOpenHashMap<>(values.size());
for (int i = 0; i < values.size(); i++) {
valueIndexMap.put(values.get(i), i);
}
}
@Override
public NbtMap nbtMap() {
return NbtMap.builder()
.putString("name", name)
.putList("enum", NbtType.STRING, values)
.putInt("type", 3)
.build();
}
public int getIndex(String value) {
return valueIndexMap.getOrDefault(value, -1);
}
}

Datei anzeigen

@ -23,34 +23,28 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.item;
package org.geysermc.geyser.entity.properties.type;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
import org.cloudburstmc.nbt.NbtMap;
public interface DyeableLeatherItem {
public class FloatProperty implements PropertyType {
private final String name;
private final float max;
private final float min;
static void translateNbtToBedrock(CompoundTag tag) {
CompoundTag displayTag = tag.get("display");
if (displayTag == null) {
return;
}
IntTag color = displayTag.remove("color");
if (color != null) {
tag.put(new IntTag("customColor", color.getValue()));
}
public FloatProperty(String name, float min, float max) {
this.name = name;
this.max = max;
this.min = min;
}
static void translateNbtToJava(CompoundTag tag) {
IntTag color = tag.get("customColor");
if (color == null) {
return;
}
CompoundTag displayTag = tag.get("display");
if (displayTag == null) {
displayTag = new CompoundTag("display");
}
displayTag.put(color);
tag.remove("customColor");
@Override
public NbtMap nbtMap() {
return NbtMap.builder()
.putString("name", name)
.putFloat("max", max)
.putFloat("min", min)
.putInt("type", 1)
.build();
}
}
}

Datei anzeigen

@ -0,0 +1,50 @@
/*
* Copyright (c) 2019-2024 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.geyser.entity.properties.type;
import org.cloudburstmc.nbt.NbtMap;
public class IntProperty implements PropertyType {
private final String name;
private final int max;
private final int min;
public IntProperty(String name, int min, int max) {
this.name = name;
this.max = max;
this.min = min;
}
@Override
public NbtMap nbtMap() {
return NbtMap.builder()
.putString("name", name)
.putInt("max", max)
.putInt("min", min)
.putInt("type", 0)
.build();
}
}

Datei anzeigen

@ -0,0 +1,32 @@
/*
* Copyright (c) 2019-2024 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.geyser.entity.properties.type;
import org.cloudburstmc.nbt.NbtMap;
public interface PropertyType {
NbtMap nbtMap();
}

Datei anzeigen

@ -25,8 +25,8 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;

Datei anzeigen

@ -25,9 +25,6 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.level.particle.Particle;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.ParticleType;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
@ -35,6 +32,11 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.MathUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.level.particle.EntityEffectParticleData;
import org.geysermc.mcprotocollib.protocol.data.game.level.particle.Particle;
import java.util.UUID;
@ -51,7 +53,7 @@ public class AreaEffectCloudEntity extends Entity {
dirtyMetadata.put(EntityDataTypes.AREA_EFFECT_CLOUD_DURATION, Integer.MAX_VALUE);
// This disabled client side shrink of the cloud
dirtyMetadata.put(EntityDataTypes.AREA_EFFECT_CLOUD_RADIUS, 0.0f);
dirtyMetadata.put(EntityDataTypes.AREA_EFFECT_CLOUD_RADIUS, 3.0f);
dirtyMetadata.put(EntityDataTypes.AREA_EFFECT_CLOUD_CHANGE_RATE, Float.MIN_VALUE);
dirtyMetadata.put(EntityDataTypes.AREA_EFFECT_CLOUD_CHANGE_ON_PICKUP, Float.MIN_VALUE);
@ -60,7 +62,7 @@ public class AreaEffectCloudEntity extends Entity {
public void setRadius(FloatEntityMetadata entityMetadata) {
// Anything less than 0.5 will cause the cloud to despawn
float value = Math.max(entityMetadata.getPrimitiveValue(), 0.5f);
float value = MathUtils.clamp(entityMetadata.getPrimitiveValue(), 0.5f, 32.0f);
dirtyMetadata.put(EntityDataTypes.AREA_EFFECT_CLOUD_RADIUS, value);
dirtyMetadata.put(EntityDataTypes.WIDTH, 2.0f * value);
}
@ -69,5 +71,9 @@ public class AreaEffectCloudEntity extends Entity {
Particle particle = entityMetadata.getValue();
Registries.PARTICLES.map(particle.getType(), p -> p.levelEventType() instanceof ParticleType particleType ? particleType : null).ifPresent(type ->
dirtyMetadata.put(EntityDataTypes.AREA_EFFECT_CLOUD_PARTICLE, type));
if (particle.getData() instanceof EntityEffectParticleData effectParticleData) {
dirtyMetadata.put(EntityDataTypes.EFFECT_COLOR, effectParticleData.getColor());
}
}
}

Datei anzeigen

@ -25,21 +25,18 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.item.TippedArrowPotion;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import java.util.UUID;
/**
* Internally this is known as TippedArrowEntity but is used with tipped arrows and normal arrows
*/
public class TippedArrowEntity extends AbstractArrowEntity {
public class ArrowEntity extends AbstractArrowEntity {
public TippedArrowEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
public ArrowEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}

Datei anzeigen

@ -25,24 +25,23 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import lombok.Getter;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket;
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import lombok.Getter;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class BoatEntity extends Entity {
public class BoatEntity extends Entity implements Tickable {
/**
* Required when IS_BUOYANT is sent in order for boats to work in the water. <br>
@ -58,6 +57,7 @@ public class BoatEntity extends Entity {
private float paddleTimeLeft;
private boolean isPaddlingRight;
private float paddleTimeRight;
private boolean doTick;
/**
* Saved for using the "pick" functionality on a boat.
@ -133,34 +133,16 @@ public class BoatEntity extends Entity {
public void setPaddlingLeft(BooleanEntityMetadata entityMetadata) {
isPaddlingLeft = entityMetadata.getPrimitiveValue();
if (isPaddlingLeft) {
// 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.
paddleTimeLeft = 0f;
if (!this.passengers.isEmpty()) {
// Get the entity by the first stored passenger and convey motion in this manner
Entity entity = this.passengers.get(0);
if (entity != null) {
updateLeftPaddle(session, entity);
}
}
} else {
// Indicate that the row position should be reset
if (!isPaddlingLeft) {
paddleTimeLeft = 0.0f;
dirtyMetadata.put(EntityDataTypes.ROW_TIME_LEFT, 0.0f);
}
}
public void setPaddlingRight(BooleanEntityMetadata entityMetadata) {
isPaddlingRight = entityMetadata.getPrimitiveValue();
if (isPaddlingRight) {
paddleTimeRight = 0f;
if (!this.passengers.isEmpty()) {
Entity entity = this.passengers.get(0);
if (entity != null) {
updateRightPaddle(session, entity);
}
}
} else {
if (!isPaddlingRight) {
paddleTimeRight = 0.0f;
dirtyMetadata.put(EntityDataTypes.ROW_TIME_RIGHT, 0.0f);
}
}
@ -186,29 +168,26 @@ public class BoatEntity extends Entity {
}
}
private void updateLeftPaddle(GeyserSession session, Entity rower) {
@Override
public void tick() {
// Java sends simply "true" and "false" (is_paddling_left), Bedrock keeps sending packets as you're rowing
doTick = !doTick; // Run every 100 ms
if (!doTick || passengers.isEmpty()) {
return;
}
Entity rower = passengers.get(0);
if (rower == null) {
return;
}
if (isPaddlingLeft) {
paddleTimeLeft += ROWING_SPEED;
sendAnimationPacket(session, rower, AnimatePacket.Action.ROW_LEFT, paddleTimeLeft);
session.scheduleInEventLoop(() ->
updateLeftPaddle(session, rower),
100,
TimeUnit.MILLISECONDS
);
}
}
private void updateRightPaddle(GeyserSession session, Entity rower) {
if (isPaddlingRight) {
paddleTimeRight += ROWING_SPEED;
sendAnimationPacket(session, rower, AnimatePacket.Action.ROW_RIGHT, paddleTimeRight);
session.scheduleInEventLoop(() ->
updateRightPaddle(session, rower),
100,
TimeUnit.MILLISECONDS
);
}
}

Datei anzeigen

@ -25,12 +25,12 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID;

Datei anzeigen

@ -25,7 +25,7 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;

Datei anzeigen

@ -25,8 +25,8 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.geysermc.geyser.entity.EntityDefinition;

Datei anzeigen

@ -25,7 +25,7 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;

Datei anzeigen

@ -25,13 +25,6 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
@ -42,27 +35,26 @@ import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket;
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityDeltaPacket;
import org.cloudburstmc.protocol.bedrock.packet.RemoveEntityPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.*;
import org.geysermc.geyser.api.entity.type.GeyserEntity;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.GeyserDirtyMetadata;
import org.geysermc.geyser.entity.properties.GeyserEntityPropertyManager;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.geyser.util.MathUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.*;
@Getter
@Setter
@ -126,6 +118,8 @@ public class Entity implements GeyserEntity {
@Setter(AccessLevel.PROTECTED) // For players
private boolean flagsDirty = false;
protected final GeyserEntityPropertyManager propertyManager;
public Entity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
this.session = session;
@ -140,6 +134,8 @@ public class Entity implements GeyserEntity {
this.valid = false;
this.propertyManager = new GeyserEntityPropertyManager(definition.registeredProperties());
setPosition(position);
setAirSupply(getMaxAir());
@ -200,11 +196,9 @@ public class Entity implements GeyserEntity {
/**
* Despawns the entity
*
* @return can be deleted
*/
public boolean despawnEntity() {
if (!valid) return true;
public void despawnEntity() {
if (!valid) return;
for (Entity passenger : passengers) { // Make sure all passengers on the despawned entity are updated
if (passenger == null) continue;
@ -218,7 +212,6 @@ public class Entity implements GeyserEntity {
session.sendUpstreamPacket(removeEntityPacket);
valid = false;
return true;
}
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, boolean isOnGround) {
@ -360,6 +353,23 @@ public class Entity implements GeyserEntity {
}
}
/**
* Sends the Bedrock entity properties to the client
*/
public void updateBedrockEntityProperties() {
if (!valid) {
return;
}
if (propertyManager.hasProperties()) {
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
entityDataPacket.setRuntimeEntityId(geyserId);
propertyManager.applyIntProperties(entityDataPacket.getProperties().getIntProperties());
propertyManager.applyFloatProperties(entityDataPacket.getProperties().getFloatProperties());
session.sendUpstreamPacket(entityDataPacket);
}
}
public void setFlags(ByteEntityMetadata entityMetadata) {
byte xd = entityMetadata.getPrimitiveValue();
setFlag(EntityFlag.ON_FIRE, ((xd & 0x01) == 0x01) && !getFlag(EntityFlag.FIRE_IMMUNE)); // Otherwise immune entities sometimes flicker onfire

Datei anzeigen

@ -25,12 +25,12 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import java.util.UUID;

Datei anzeigen

@ -72,6 +72,9 @@ public class FireballEntity extends ThrowableEntity {
@Override
public void tick() {
if (removedInVoid()) {
return;
}
moveAbsoluteImmediate(tickMovement(position), getYaw(), getPitch(), getHeadYaw(), false, false);
}
}

Datei anzeigen

@ -25,25 +25,18 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityMotionPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.level.FireworkColor;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.MathUtils;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import java.util.ArrayList;
import java.util.List;
import java.util.OptionalInt;
import java.util.UUID;
@ -58,73 +51,16 @@ public class FireworkEntity extends Entity {
if (item == null) {
return;
}
CompoundTag tag = item.getNbt();
if (tag == null) {
DataComponents components = item.getDataComponents();
if (components == null) {
return;
}
CompoundTag fireworks = tag.get("Fireworks");
if (fireworks == null) {
// Thank you Mineplex very cool
return;
}
// TODO this looked the same, so I'm going to assume it is and (keep below comment if true)
// Translate using item methods to get firework NBT for Bedrock
BedrockItemBuilder builder = new BedrockItemBuilder();
Items.FIREWORK_ROCKET.translateComponentsToBedrock(session, components, builder);
NbtMapBuilder fireworksBuilder = NbtMap.builder();
if (fireworks.get("Flight") != null) {
fireworksBuilder.putByte("Flight", MathUtils.getNbtByte(fireworks.get("Flight").getValue()));
}
List<NbtMap> explosions = new ArrayList<>();
if (fireworks.get("Explosions") != null) {
for (Tag effect : ((ListTag) fireworks.get("Explosions")).getValue()) {
CompoundTag effectData = (CompoundTag) effect;
NbtMapBuilder effectBuilder = NbtMap.builder();
if (effectData.get("Type") != null) {
effectBuilder.putByte("FireworkType", MathUtils.getNbtByte(effectData.get("Type").getValue()));
}
if (effectData.get("Colors") != null) {
int[] oldColors = (int[]) effectData.get("Colors").getValue();
byte[] colors = new byte[oldColors.length];
int i = 0;
for (int color : oldColors) {
colors[i++] = FireworkColor.fromJavaRGB(color);
}
effectBuilder.putByteArray("FireworkColor", colors);
}
if (effectData.get("FadeColors") != null) {
int[] oldColors = (int[]) effectData.get("FadeColors").getValue();
byte[] colors = new byte[oldColors.length];
int i = 0;
for (int color : oldColors) {
colors[i++] = FireworkColor.fromJavaRGB(color);
}
effectBuilder.putByteArray("FireworkFade", colors);
}
if (effectData.get("Trail") != null) {
effectBuilder.putByte("FireworkTrail", MathUtils.getNbtByte(effectData.get("Trail").getValue()));
}
if (effectData.get("Flicker") != null) {
effectBuilder.putByte("FireworkFlicker", MathUtils.getNbtByte(effectData.get("Flicker").getValue()));
}
explosions.add(effectBuilder.build());
}
}
fireworksBuilder.putList("Explosions", NbtType.COMPOUND, explosions);
NbtMapBuilder builder = NbtMap.builder();
builder.put("Fireworks", fireworksBuilder.build());
dirtyMetadata.put(EntityDataTypes.DISPLAY_FIREWORK, builder.build());
}

Datei anzeigen

@ -25,11 +25,10 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import lombok.Getter;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.packet.PlaySoundPacket;
import lombok.Getter;
import org.geysermc.erosion.util.BlockPositionIterator;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
@ -38,6 +37,7 @@ import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.collision.BlockCollision;
import org.geysermc.geyser.util.BlockUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
@ -133,6 +133,9 @@ public class FishingHookEntity extends ThrowableEntity {
@Override
public void tick() {
if (removedInVoid()) {
return;
}
if (hooked || !isInAir() && !isInWater() || isOnGround()) {
motion = Vector3f.ZERO;
return;

Datei anzeigen

@ -25,8 +25,8 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.geysermc.geyser.entity.EntityDefinition;

Datei anzeigen

@ -25,16 +25,16 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket;
import java.util.UUID;

Datei anzeigen

@ -25,8 +25,6 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
@ -37,7 +35,9 @@ import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.item.ItemTranslator;
import org.geysermc.geyser.translator.item.ItemTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@ -74,7 +74,7 @@ public class ItemEntity extends ThrowableEntity {
@Override
public void tick() {
if (isInWater()) {
if (removedInVoid() || isInWater()) {
return;
}
if (!isOnGround() || (motion.getX() * motion.getX() + motion.getZ() * motion.getZ()) > 0.00001) {

Datei anzeigen

@ -25,12 +25,7 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.object.Direction;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType;
import lombok.Getter;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
@ -39,12 +34,17 @@ import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
import lombok.Getter;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.item.ItemTranslator;
import org.geysermc.geyser.translator.item.ItemTranslator;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InventoryUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import java.util.UUID;
@ -148,7 +148,7 @@ public class ItemFrameEntity extends Entity {
}
@Override
public boolean despawnEntity() {
public void despawnEntity() {
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0);
updateBlockPacket.setBlockPosition(bedrockPosition);
@ -161,7 +161,6 @@ public class ItemFrameEntity extends Entity {
session.getItemFrameCache().remove(bedrockPosition, this);
valid = false;
return true;
}
private NbtMap getDefaultTag() {

Datei anzeigen

@ -25,11 +25,11 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID;

Datei anzeigen

@ -25,16 +25,6 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute;
import com.github.steveice10.mc.protocol.data.game.entity.attribute.AttributeType;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
@ -55,8 +45,23 @@ import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.ItemTranslator;
import org.geysermc.geyser.util.AttributeUtils;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.Attribute;
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.level.particle.EntityEffectParticleData;
import org.geysermc.mcprotocollib.protocol.data.game.level.particle.Particle;
import org.geysermc.mcprotocollib.protocol.data.game.level.particle.ParticleType;
import java.util.*;
@ -69,7 +74,7 @@ public class LivingEntity extends Entity {
protected ItemData leggings = ItemData.AIR;
protected ItemData boots = ItemData.AIR;
protected ItemData hand = ItemData.AIR;
protected ItemData offHand = ItemData.AIR;
protected ItemData offhand = ItemData.AIR;
@Getter(value = AccessLevel.NONE)
protected float health = 1f; // The default value in Java Edition before any entity metadata is sent
@ -81,12 +86,58 @@ public class LivingEntity extends Entity {
*/
private boolean isMaxFrozenState = false;
/**
* The base scale entity data, without attributes applied. Used for such cases as baby variants.
*/
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
private float scale;
/**
* The scale sent through the Java attributes packet
*/
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
private float attributeScale;
public LivingEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setHelmet(ItemStack stack) {
this.helmet = ItemTranslator.translateToBedrock(session, stack);
}
public void setChestplate(ItemStack stack) {
this.chestplate = ItemTranslator.translateToBedrock(session, stack);
}
public void setLeggings(ItemStack stack) {
this.leggings = ItemTranslator.translateToBedrock(session, stack);
}
public void setBoots(ItemStack stack) {
this.boots = ItemTranslator.translateToBedrock(session, stack);
}
public void setHand(ItemStack stack) {
this.hand = ItemTranslator.translateToBedrock(session, stack);
}
public void setOffhand(ItemStack stack) {
this.offhand = ItemTranslator.translateToBedrock(session, stack);
}
public void switchHands() {
ItemData offhand = this.offhand;
this.offhand = this.hand;
this.hand = offhand;
}
@Override
protected void initializeMetadata() {
// Initialize here so overriding classes don't have 0 values
this.scale = 1f;
this.attributeScale = 1f;
super.initializeMetadata();
// Matches Bedrock behavior; is always set to this
dirtyMetadata.put(EntityDataTypes.STRUCTURAL_INTEGRITY, 1);
@ -121,6 +172,37 @@ public class LivingEntity extends Entity {
session.sendUpstreamPacket(attributesPacket);
}
// TODO: support all particle types
public void setParticles(ObjectEntityMetadata<List<Particle>> entityMetadata) {
List<Particle> particles = entityMetadata.getValue();
float r = 0f;
float g = 0f;
float b = 0f;
int count = 0;
for (Particle particle : particles) {
if (particle.getType() != ParticleType.ENTITY_EFFECT) {
continue;
}
int color = ((EntityEffectParticleData) particle.getData()).getColor();
r += ((color >> 16) & 0xFF) / 255f;
g += ((color >> 8) & 0xFF) / 255f;
b += ((color) & 0xFF) / 255f;
count++;
}
int result = 0;
if (count > 0) {
r = r / count * 255f;
g = g / count * 255f;
b = b / count * 255f;
result = (int) r << 16 | (int) g << 8 | (int) b;
}
dirtyMetadata.put(EntityDataTypes.EFFECT_COLOR, result);
}
public @Nullable Vector3i setBedPosition(EntityMetadata<Optional<Vector3i>, ?> entityMetadata) {
Optional<Vector3i> optionalPos = entityMetadata.getValue();
if (optionalPos.isPresent()) {
@ -135,7 +217,7 @@ public class LivingEntity extends Entity {
protected boolean hasShield(boolean offhand) {
ItemMapping shieldMapping = session.getItemMappings().getStoredItems().shield();
if (offhand) {
return offHand.getDefinition().equals(shieldMapping.getBedrockDefinition());
return this.offhand.getDefinition().equals(shieldMapping.getBedrockDefinition());
} else {
return hand.getDefinition().equals(shieldMapping.getBedrockDefinition());
}
@ -164,6 +246,21 @@ public class LivingEntity extends Entity {
return freezingPercentage;
}
protected void setScale(float scale) {
this.scale = scale;
applyScale();
}
private void setAttributeScale(float scale) {
this.attributeScale = scale;
applyScale();
}
private void applyScale() {
// Take any adjustments Bedrock requires, and compute it alongside the attribute's additional changes
this.dirtyMetadata.put(EntityDataTypes.SCALE, scale * attributeScale);
}
/**
* @return a Bedrock health attribute constructed from the data sent from the server
*/
@ -194,9 +291,9 @@ public class LivingEntity extends Entity {
/**
* Checks to see if a nametag interaction would go through.
*/
// Implementation note for 1.20.5: this code was moved to the NameTag item.
protected final InteractionResult checkInteractWithNameTag(GeyserItemStack itemStack) {
CompoundTag nbt = itemStack.getNbt();
if (nbt != null && nbt.get("display") instanceof CompoundTag displayTag && displayTag.get("Name") instanceof StringTag) {
if (itemStack.getComponent(DataComponentType.CUSTOM_NAME) != null) {
// The mob shall be named
return InteractionResult.SUCCESS;
}
@ -247,7 +344,7 @@ public class LivingEntity extends Entity {
MobEquipmentPacket offHandPacket = new MobEquipmentPacket();
offHandPacket.setRuntimeEntityId(geyserId);
offHandPacket.setItem(offHand);
offHandPacket.setItem(offhand);
offHandPacket.setHotbarSlot(-1);
offHandPacket.setInventorySlot(0);
offHandPacket.setContainerId(ContainerId.OFFHAND);
@ -255,6 +352,15 @@ public class LivingEntity extends Entity {
session.sendUpstreamPacket(offHandPacket);
}
/**
* Called when a SWING_ARM animation packet is received
*
* @return true if an ATTACK_START event should be used instead
*/
public boolean useArmSwingAttack() {
return false;
}
/**
* Attributes are properties of an entity that are generally more runtime-based instead of permanent properties.
* Movement speed, current attack damage with a weapon, current knockback resistance.
@ -299,7 +405,12 @@ public class LivingEntity extends Entity {
case GENERIC_MOVEMENT_SPEED -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.MOVEMENT_SPEED));
case GENERIC_FOLLOW_RANGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FOLLOW_RANGE));
case GENERIC_KNOCKBACK_RESISTANCE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.KNOCKBACK_RESISTANCE));
case HORSE_JUMP_STRENGTH -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.HORSE_JUMP_STRENGTH));
case GENERIC_JUMP_STRENGTH -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.HORSE_JUMP_STRENGTH));
case GENERIC_SCALE -> {
// Attribute on Java, entity data on Bedrock
setAttributeScale((float) AttributeUtils.calculateValue(javaAttribute));
updateBedrockMetadata();
}
}
}
}

Datei anzeigen

@ -25,9 +25,6 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.geysermc.geyser.entity.EntityDefinition;
@ -35,6 +32,9 @@ import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID;

Datei anzeigen

@ -25,13 +25,13 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.object.Direction;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.packet.AddPaintingPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.level.PaintingType;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
import java.util.UUID;
@ -49,7 +49,7 @@ public class PaintingEntity extends Entity {
// Wait until we get the metadata needed
}
public void setPaintingType(ObjectEntityMetadata<com.github.steveice10.mc.protocol.data.game.entity.type.PaintingType> entityMetadata) {
public void setPaintingType(ObjectEntityMetadata<org.geysermc.mcprotocollib.protocol.data.game.entity.type.PaintingType> entityMetadata) {
PaintingType type = PaintingType.getByPaintingType(entityMetadata.getValue());
AddPaintingPacket addPaintingPacket = new AddPaintingPacket();
addPaintingPacket.setUniqueEntityId(geyserId);

Datei anzeigen

@ -25,13 +25,13 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import java.util.UUID;

Datei anzeigen

@ -25,14 +25,14 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import net.kyori.adventure.text.Component;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import java.util.Optional;
import java.util.UUID;

Datei anzeigen

@ -25,7 +25,6 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
@ -34,6 +33,7 @@ import org.cloudburstmc.protocol.bedrock.packet.MoveEntityDeltaPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
import java.util.UUID;
@ -55,6 +55,9 @@ public class ThrowableEntity extends Entity implements Tickable {
*/
@Override
public void tick() {
if (removedInVoid()) {
return;
}
moveAbsoluteImmediate(position.add(motion), getYaw(), getPitch(), getHeadYaw(), isOnGround(), false);
float drag = getDrag();
float gravity = getGravity();
@ -170,14 +173,14 @@ public class ThrowableEntity extends Entity implements Tickable {
}
@Override
public boolean despawnEntity() {
public void despawnEntity() {
if (definition.entityType() == EntityType.ENDER_PEARL) {
LevelEventPacket particlePacket = new LevelEventPacket();
particlePacket.setType(LevelEvent.PARTICLE_TELEPORT);
particlePacket.setPosition(position);
session.sendUpstreamPacket(particlePacket);
}
return super.despawnEntity();
super.despawnEntity();
}
@Override
@ -191,4 +194,17 @@ public class ThrowableEntity extends Entity implements Tickable {
moveAbsoluteImmediate(position, yaw, pitch, headYaw, isOnGround, teleported);
lastJavaPosition = position;
}
/**
* Removes the entity if it is 64 blocks below the world.
*
* @return true if the entity was removed
*/
public boolean removedInVoid() {
if (position.getY() < session.getDimensionType().minY() - 64) {
session.getEntityCache().removeEntity(this);
return true;
}
return false;
}
}

Datei anzeigen

@ -25,8 +25,8 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;

Datei anzeigen

@ -25,10 +25,6 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
@ -38,6 +34,11 @@ import org.geysermc.geyser.inventory.item.Potion;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.PotionContents;
import java.util.EnumSet;
import java.util.UUID;
@ -53,21 +54,22 @@ public class ThrownPotionEntity extends ThrowableItemEntity {
public void setItem(EntityMetadata<ItemStack, ?> entityMetadata) {
ItemStack itemStack = entityMetadata.getValue();
if (itemStack == null) {
dirtyMetadata.put(EntityDataTypes.EFFECT_COLOR, 0);
dirtyMetadata.put(EntityDataTypes.AUX_VALUE_DATA, (short) 0);
setFlag(EntityFlag.ENCHANTED, false);
setFlag(EntityFlag.LINGERING, false);
} else {
// As of Java 1.19.3, the server/client doesn't seem to care of the item is actually a potion?
if (itemStack.getNbt() != null) {
Tag potionTag = itemStack.getNbt().get("Potion");
if (potionTag instanceof StringTag) {
Potion potion = Potion.getByJavaIdentifier(((StringTag) potionTag).getValue());
DataComponents components = itemStack.getDataComponents();
if (components != null) {
PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS);
if (potionContents != null) {
Potion potion = Potion.getByJavaId(potionContents.getPotionId());
if (potion != null) {
dirtyMetadata.put(EntityDataTypes.EFFECT_COLOR, (int) potion.getBedrockId());
dirtyMetadata.put(EntityDataTypes.AUX_VALUE_DATA, potion.getBedrockId());
setFlag(EntityFlag.ENCHANTED, !NON_ENCHANTED_POTIONS.contains(potion));
} else {
dirtyMetadata.put(EntityDataTypes.EFFECT_COLOR, 0);
GeyserImpl.getInstance().getLogger().debug("Unknown java potion: " + potionTag.getValue());
dirtyMetadata.put(EntityDataTypes.AUX_VALUE_DATA, (short) 0);
GeyserImpl.getInstance().getLogger().debug("Unknown java potion: " + potionContents.getPotionId());
}
}

Datei anzeigen

@ -25,7 +25,7 @@
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.EntityDefinitions;

Datei anzeigen

@ -25,7 +25,6 @@
package org.geysermc.geyser.entity.type.living;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
@ -34,6 +33,7 @@ import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID;

Datei anzeigen

@ -25,12 +25,11 @@
package org.geysermc.geyser.entity.type.living;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import java.util.UUID;
@ -44,12 +43,12 @@ public class AgeableEntity extends CreatureEntity {
protected void initializeMetadata() {
super.initializeMetadata();
// Required as of 1.19.3 Java
dirtyMetadata.put(EntityDataTypes.SCALE, getAdultSize());
setScale(getAdultSize());
}
public void setBaby(BooleanEntityMetadata entityMetadata) {
boolean isBaby = entityMetadata.getPrimitiveValue();
dirtyMetadata.put(EntityDataTypes.SCALE, isBaby ? getBabySize() : getAdultSize());
setScale(isBaby ? getBabySize() : getAdultSize());
setFlag(EntityFlag.BABY, isBaby);
setBoundingBoxHeight(definition.height() * (isBaby ? getBabySize() : getAdultSize()));

Datei anzeigen

@ -25,8 +25,6 @@
package org.geysermc.geyser.entity.type.living;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
@ -36,6 +34,8 @@ import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID;

Datei anzeigen

@ -25,10 +25,6 @@
package org.geysermc.geyser.entity.type.living;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import org.cloudburstmc.math.vector.Vector3f;
@ -43,6 +39,11 @@ import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.MathUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import java.util.Optional;
import java.util.UUID;
@ -99,11 +100,11 @@ public class ArmorStandEntity extends LivingEntity {
}
@Override
public boolean despawnEntity() {
public void despawnEntity() {
if (secondEntity != null) {
secondEntity.despawnEntity();
}
return super.despawnEntity();
super.despawnEntity();
}
@Override
@ -257,38 +258,38 @@ public class ArmorStandEntity extends LivingEntity {
}
@Override
public void setHelmet(ItemData helmet) {
public void setHelmet(ItemStack helmet) {
super.setHelmet(helmet);
updateSecondEntityStatus(true);
}
@Override
public void setChestplate(ItemData chestplate) {
public void setChestplate(ItemStack chestplate) {
super.setChestplate(chestplate);
updateSecondEntityStatus(true);
}
@Override
public void setLeggings(ItemData leggings) {
public void setLeggings(ItemStack leggings) {
super.setLeggings(leggings);
updateSecondEntityStatus(true);
}
@Override
public void setBoots(ItemData boots) {
public void setBoots(ItemStack boots) {
super.setBoots(boots);
updateSecondEntityStatus(true);
}
@Override
public void setHand(ItemData hand) {
public void setHand(ItemStack hand) {
super.setHand(hand);
updateSecondEntityStatus(true);
}
@Override
public void setOffHand(ItemData offHand) {
super.setOffHand(offHand);
public void setOffhand(ItemStack offHand) {
super.setOffhand(offHand);
updateSecondEntityStatus(true);
}
@ -310,7 +311,7 @@ public class ArmorStandEntity extends LivingEntity {
if (!isInvisible) {
// The armor stand isn't invisible. We good.
setFlag(EntityFlag.INVISIBLE, false);
dirtyMetadata.put(EntityDataTypes.SCALE, getScale());
setScale(getScale());
updateOffsetRequirement(false);
if (secondEntity != null) {
@ -324,9 +325,9 @@ public class ArmorStandEntity extends LivingEntity {
}
boolean isNametagEmpty = 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))) {
|| !boots.equals(ItemData.AIR) || !hand.equals(ItemData.AIR) || !offhand.equals(ItemData.AIR))) {
// Reset scale of the proper armor stand
this.dirtyMetadata.put(EntityDataTypes.SCALE, getScale());
setScale(getScale());
// Set the proper armor stand to invisible to show armor
setFlag(EntityFlag.INVISIBLE, true);
// Update the position of the armor stand
@ -349,7 +350,7 @@ public class ArmorStandEntity extends LivingEntity {
// Guarantee this copy is NOT invisible
secondEntity.setFlag(EntityFlag.INVISIBLE, false);
// Scale to 0 to show nametag
secondEntity.getDirtyMetadata().put(EntityDataTypes.SCALE, 0.0f);
secondEntity.setScale(0f);
// No bounding box as we don't want to interact with this entity
secondEntity.getDirtyMetadata().put(EntityDataTypes.WIDTH, 0.0f);
secondEntity.getDirtyMetadata().put(EntityDataTypes.HEIGHT, 0.0f);
@ -359,7 +360,7 @@ public class ArmorStandEntity extends LivingEntity {
} else if (isNametagEmpty) {
// We can just make an invisible entity
// Reset scale of the proper armor stand
dirtyMetadata.put(EntityDataTypes.SCALE, getScale());
setScale(getScale());
// Set the proper armor stand to invisible to show armor
setFlag(EntityFlag.INVISIBLE, true);
// Update offset
@ -373,7 +374,7 @@ public class ArmorStandEntity extends LivingEntity {
// Nametag is not empty and there is no armor
// We don't need to make a new entity
setFlag(EntityFlag.INVISIBLE, false);
dirtyMetadata.put(EntityDataTypes.SCALE, 0.0f);
setScale(0f);
// As the above is applied, we need an offset
updateOffsetRequirement(!isMarker);

Datei anzeigen

@ -25,11 +25,11 @@
package org.geysermc.geyser.entity.type.living;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import java.util.UUID;

Datei anzeigen

@ -25,14 +25,15 @@
package org.geysermc.geyser.entity.type.living;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID;
@ -49,7 +50,7 @@ public class DolphinEntity extends WaterEntity {
@NonNull
@Override
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
if (!itemInHand.isEmpty() && session.getTagCache().isFish(itemInHand)) {
if (!itemInHand.isEmpty() && session.getTagCache().is(ItemTag.FISHES, itemInHand)) {
return InteractiveTag.FEED;
}
return super.testMobInteraction(hand, itemInHand);
@ -58,7 +59,7 @@ public class DolphinEntity extends WaterEntity {
@NonNull
@Override
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
if (!itemInHand.isEmpty() && session.getTagCache().isFish(itemInHand)) {
if (!itemInHand.isEmpty() && session.getTagCache().is(ItemTag.FISHES, itemInHand)) {
// Feed
return InteractionResult.SUCCESS;
}

Datei anzeigen

@ -25,7 +25,6 @@
package org.geysermc.geyser.entity.type.living;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
@ -35,6 +34,7 @@ import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID;

Datei anzeigen

@ -25,8 +25,6 @@
package org.geysermc.geyser.entity.type.living;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import lombok.Getter;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3f;
@ -40,6 +38,8 @@ import org.geysermc.geyser.item.type.SpawnEggItem;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID;

Datei anzeigen

@ -25,11 +25,10 @@
package org.geysermc.geyser.entity.type.living;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import java.util.UUID;
@ -39,8 +38,8 @@ public class SlimeEntity extends MobEntity {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setScale(IntEntityMetadata entityMetadata) {
dirtyMetadata.put(EntityDataTypes.SCALE, 0.10f + entityMetadata.getPrimitiveValue());
public void setSlimeScale(IntEntityMetadata entityMetadata) {
setScale(0.10f + entityMetadata.getPrimitiveValue());
}
@Override

Datei anzeigen

@ -25,8 +25,6 @@
package org.geysermc.geyser.entity.type.living;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
@ -36,6 +34,8 @@ import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID;

Datei anzeigen

@ -25,15 +25,15 @@
package org.geysermc.geyser.entity.type.living;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID;
@ -62,6 +62,6 @@ public class TadpoleEntity extends AbstractFishEntity {
}
private boolean isFood(GeyserItemStack itemStack) {
return itemStack.asItem() == Items.SLIME_BALL;
return session.getTagCache().is(ItemTag.FROG_FOOD, itemStack);
}
}

Datei anzeigen

@ -25,39 +25,40 @@
package org.geysermc.geyser.entity.type.living.animal;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.type.living.AgeableEntity;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID;
public class AnimalEntity extends AgeableEntity {
public abstract class AnimalEntity extends AgeableEntity {
public AnimalEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public final boolean canEat(GeyserItemStack itemStack) {
return canEat(itemStack.asItem());
protected final boolean canEat(GeyserItemStack itemStack) {
ItemTag tag = getFoodTag();
if (tag == null) {
return false;
}
return session.getTagCache().is(tag, itemStack);
}
/**
* @return true if this is a valid item to breed with for this animal.
* @return the tag associated with this animal for eating food. Null for nothing or different behavior.
*/
public boolean canEat(Item item) {
// This is what it defaults to. OK.
return item == Items.WHEAT;
}
protected abstract @Nullable ItemTag getFoodTag();
@NonNull
@Override

Datei anzeigen

@ -0,0 +1,81 @@
/*
* Copyright (c) 2019-2024 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.geyser.entity.type.living.animal;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.ArmadilloState;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class ArmadilloEntity extends AnimalEntity {
private ArmadilloState armadilloState = ArmadilloState.IDLE;
public ArmadilloEntity(GeyserSession session, int entityId, long geyserId, UUID uuid,
EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setArmadilloState(ObjectEntityMetadata<ArmadilloState> entityMetadata) {
armadilloState = entityMetadata.getValue();
switch (armadilloState) {
case IDLE -> propertyManager.add("minecraft:armadillo_state", "unrolled");
case ROLLING -> propertyManager.add("minecraft:armadillo_state", "rolled_up");
case SCARED -> propertyManager.add("minecraft:armadillo_state", "rolled_up_relaxing");
case UNROLLING -> propertyManager.add("minecraft:armadillo_state", "rolled_up_unrolling");
}
updateBedrockEntityProperties();
}
public void onPeeking() {
// Technically we should wait if not currently scared
if (armadilloState == ArmadilloState.SCARED) {
propertyManager.add("minecraft:armadillo_state", "rolled_up_peeking");
updateBedrockEntityProperties();
// Needed for consecutive peeks
session.scheduleInEventLoop(() -> {
if (armadilloState == ArmadilloState.SCARED) {
propertyManager.add("minecraft:armadillo_state", "rolled_up_relaxing");
updateBedrockEntityProperties();
}
}, 250, TimeUnit.MILLISECONDS);
}
}
@Override
@Nullable
protected ItemTag getFoodTag() {
return ItemTag.ARMADILLO_FOOD;
}
}

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen