mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Merge remote-tracking branch 'origin/master' into jwt-changes
# Conflicts: # core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java # core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java # core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java
This commit is contained in:
commit
99671960d0
141 changed files with 14840 additions and 41502 deletions
2
.github/workflows/pullrequest.yml
vendored
2
.github/workflows/pullrequest.yml
vendored
|
@ -31,7 +31,7 @@ jobs:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Use author's API repo if it exists
|
- name: Use author's API repo if it exists
|
||||||
if: steps.find_forks.outputs.target_branch_found == 'true'
|
if: ${{ steps.find_forks.outputs.target_branch_found == 'true' }}
|
||||||
env:
|
env:
|
||||||
API_FORK_URL: ${{ steps.find_forks.outputs.user_fork_url }}
|
API_FORK_URL: ${{ steps.find_forks.outputs.user_fork_url }}
|
||||||
API_FORK_BRANCH: ${{ github.event.pull_request.head.ref }}
|
API_FORK_BRANCH: ${{ github.event.pull_request.head.ref }}
|
||||||
|
|
|
@ -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!
|
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.19.40 - 1.19.81 and Minecraft Java 1.19.4.
|
### Currently supporting Minecraft Bedrock 1.19.80 - 1.20 and Minecraft Java 1.20.
|
||||||
|
|
||||||
## Setting Up
|
## Setting Up
|
||||||
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
|
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
|
||||||
|
|
|
@ -35,7 +35,9 @@ import org.geysermc.geyser.api.event.EventRegistrar;
|
||||||
import org.geysermc.geyser.api.extension.ExtensionManager;
|
import org.geysermc.geyser.api.extension.ExtensionManager;
|
||||||
import org.geysermc.geyser.api.network.BedrockListener;
|
import org.geysermc.geyser.api.network.BedrockListener;
|
||||||
import org.geysermc.geyser.api.network.RemoteServer;
|
import org.geysermc.geyser.api.network.RemoteServer;
|
||||||
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@ -107,6 +109,30 @@ public interface GeyserApi extends GeyserApiBase {
|
||||||
@NonNull
|
@NonNull
|
||||||
BedrockListener bedrockListener();
|
BedrockListener bedrockListener();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link Path} to the Geyser config directory.
|
||||||
|
*
|
||||||
|
* @return the path to the Geyser config directory
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
Path configDirectory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link Path} to the Geyser packs directory.
|
||||||
|
*
|
||||||
|
* @return the path to the Geyser packs directory
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
Path packDirectory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets {@link PlatformType} the extension is running on
|
||||||
|
*
|
||||||
|
* @return type of platform
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
PlatformType platformType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current {@link GeyserApiBase} instance.
|
* Gets the current {@link GeyserApiBase} instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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.api.event.bedrock;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||||
|
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when Geyser session connected to a Java remote server and is in a play-ready state.
|
||||||
|
*/
|
||||||
|
public final class SessionJoinEvent extends ConnectionEvent {
|
||||||
|
public SessionJoinEvent(@NonNull GeyserConnection connection) {
|
||||||
|
super(connection);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* 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.api.event.bedrock;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||||
|
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
|
||||||
|
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when Geyser initializes a session for a new Bedrock client and is in the process of sending resource packs.
|
||||||
|
*/
|
||||||
|
public abstract class SessionLoadResourcePacksEvent extends ConnectionEvent {
|
||||||
|
public SessionLoadResourcePacksEvent(@NonNull GeyserConnection connection) {
|
||||||
|
super(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an unmodifiable list of {@link ResourcePack}s that will be sent to the client.
|
||||||
|
*
|
||||||
|
* @return an unmodifiable list of resource packs that will be sent to the client.
|
||||||
|
*/
|
||||||
|
public abstract @NonNull List<ResourcePack> resourcePacks();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a {@link ResourcePack} to be sent to the client.
|
||||||
|
*
|
||||||
|
* @param resourcePack a resource pack that will be sent to the client.
|
||||||
|
* @return true if the resource pack was added successfully,
|
||||||
|
* or false if already present
|
||||||
|
*/
|
||||||
|
public abstract boolean register(@NonNull ResourcePack resourcePack);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters a resource pack from being sent to the client.
|
||||||
|
*
|
||||||
|
* @param uuid the UUID of the resource pack
|
||||||
|
* @return true whether the resource pack was removed from the list of resource packs.
|
||||||
|
*/
|
||||||
|
public abstract boolean unregister(@NonNull UUID uuid);
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* 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.api.event.bedrock;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.event.Cancellable;
|
||||||
|
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||||
|
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
|
||||||
|
import org.geysermc.geyser.api.network.RemoteServer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
public final class SessionLoginEvent extends ConnectionEvent implements Cancellable {
|
||||||
|
private RemoteServer remoteServer;
|
||||||
|
private boolean cancelled;
|
||||||
|
private String disconnectReason;
|
||||||
|
|
||||||
|
public SessionLoginEvent(@NonNull GeyserConnection connection, @NonNull RemoteServer remoteServer) {
|
||||||
|
super(connection);
|
||||||
|
this.remoteServer = remoteServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the event is cancelled.
|
||||||
|
*
|
||||||
|
* @return The cancel status of the event.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return this.cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels the login event, and disconnects the player.
|
||||||
|
* If cancelled, the player disconnects without connecting to the remote server.
|
||||||
|
* This method will use a default disconnect reason. To specify one, use {@link #setCancelled(boolean, String)}.
|
||||||
|
*
|
||||||
|
* @param cancelled If the login event should be cancelled.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setCancelled(boolean cancelled) {
|
||||||
|
this.cancelled = cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels the login event, and disconnects the player with the specified reason.
|
||||||
|
* If cancelled, the player disconnects without connecting to the remote server.
|
||||||
|
*
|
||||||
|
* @param cancelled If the login event should be cancelled.
|
||||||
|
* @param disconnectReason The reason for the cancellation.
|
||||||
|
*/
|
||||||
|
public void setCancelled(boolean cancelled, @NonNull String disconnectReason) {
|
||||||
|
this.cancelled = cancelled;
|
||||||
|
this.disconnectReason = disconnectReason;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the reason for the cancellation, or null if there is no reason given.
|
||||||
|
*
|
||||||
|
* @return The reason for the cancellation.
|
||||||
|
*/
|
||||||
|
public @Nullable String disconnectReason() {
|
||||||
|
return this.disconnectReason;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link RemoteServer} the section will attempt to connect to.
|
||||||
|
*
|
||||||
|
* @return the {@link RemoteServer} the section will attempt to connect to.
|
||||||
|
*/
|
||||||
|
public @NonNull RemoteServer remoteServer() {
|
||||||
|
return this.remoteServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link RemoteServer} to connect the session to.
|
||||||
|
*
|
||||||
|
* @param remoteServer Sets the {@link RemoteServer} to connect to.
|
||||||
|
*/
|
||||||
|
public void remoteServer(@NonNull RemoteServer remoteServer) {
|
||||||
|
this.remoteServer = remoteServer;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* 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.api.pack;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.channels.SeekableByteChannel;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a pack codec that can be used
|
||||||
|
* to provide resource packs to clients.
|
||||||
|
*/
|
||||||
|
public abstract class PackCodec {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the sha256 hash of the resource pack.
|
||||||
|
*
|
||||||
|
* @return the hash of the resource pack
|
||||||
|
*/
|
||||||
|
public abstract byte @NonNull [] sha256();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the resource pack size.
|
||||||
|
*
|
||||||
|
* @return the resource pack file size
|
||||||
|
*/
|
||||||
|
public abstract long size();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the given resource pack into a byte buffer.
|
||||||
|
*
|
||||||
|
* @param resourcePack the resource pack to serialize
|
||||||
|
* @return the serialized resource pack
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public abstract SeekableByteChannel serialize(@NonNull ResourcePack resourcePack) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new resource pack from this codec.
|
||||||
|
*
|
||||||
|
* @return the new resource pack
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
protected abstract ResourcePack create();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new pack provider from the given path.
|
||||||
|
*
|
||||||
|
* @param path the path to create the pack provider from
|
||||||
|
* @return the new pack provider
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public static PackCodec path(@NonNull Path path) {
|
||||||
|
return GeyserApi.api().provider(PathPackCodec.class, path);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -23,21 +23,23 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.common;
|
package org.geysermc.geyser.api.pack;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
@Getter
|
import java.nio.file.Path;
|
||||||
@AllArgsConstructor
|
|
||||||
public enum PlatformType {
|
|
||||||
ANDROID("Android"),
|
|
||||||
BUNGEECORD("BungeeCord"),
|
|
||||||
FABRIC("Fabric"),
|
|
||||||
SPIGOT("Spigot"),
|
|
||||||
SPONGE("Sponge"),
|
|
||||||
STANDALONE("Standalone"),
|
|
||||||
VELOCITY("Velocity");
|
|
||||||
|
|
||||||
private final String platformName;
|
/**
|
||||||
|
* Represents a pack codec that creates a resource
|
||||||
|
* pack from a path on the filesystem.
|
||||||
|
*/
|
||||||
|
public abstract class PathPackCodec extends PackCodec {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the path of the resource pack.
|
||||||
|
*
|
||||||
|
* @return the path of the resource pack
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public abstract Path path();
|
||||||
}
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* 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.api.pack;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a resource pack sent to Bedrock clients
|
||||||
|
* <p>
|
||||||
|
* This representation of a resource pack only contains what
|
||||||
|
* Geyser requires to send it to the client.
|
||||||
|
*/
|
||||||
|
public interface ResourcePack {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link PackCodec codec} for this pack.
|
||||||
|
*
|
||||||
|
* @return the codec for this pack
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
PackCodec codec();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the resource pack manifest.
|
||||||
|
*
|
||||||
|
* @return the resource pack manifest
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
ResourcePackManifest manifest();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the content key of the resource pack. Lack of a content key is represented by an empty String.
|
||||||
|
*
|
||||||
|
* @return the content key of the resource pack
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
String contentKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a resource pack with the given {@link PackCodec}.
|
||||||
|
*
|
||||||
|
* @param codec the pack codec
|
||||||
|
* @return the resource pack
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
static ResourcePack create(@NonNull PackCodec codec) {
|
||||||
|
return codec.create();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
/*
|
||||||
|
* 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.api.pack;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a resource pack manifest.
|
||||||
|
*/
|
||||||
|
public interface ResourcePackManifest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the format version of the resource pack.
|
||||||
|
*
|
||||||
|
* @return the format version
|
||||||
|
*/
|
||||||
|
int formatVersion();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the header of the resource pack.
|
||||||
|
*
|
||||||
|
* @return the header
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
Header header();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the modules of the resource pack.
|
||||||
|
*
|
||||||
|
* @return the modules
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
Collection<? extends Module> modules();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the dependencies of the resource pack.
|
||||||
|
*
|
||||||
|
* @return the dependencies
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
Collection<? extends Dependency> dependencies();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the header of a resource pack.
|
||||||
|
*/
|
||||||
|
interface Header {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the UUID of the resource pack.
|
||||||
|
*
|
||||||
|
* @return the UUID
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
UUID uuid();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the version of the resource pack.
|
||||||
|
*
|
||||||
|
* @return the version
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
Version version();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the name of the resource pack.
|
||||||
|
*
|
||||||
|
* @return the name
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
String name();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the description of the resource pack.
|
||||||
|
*
|
||||||
|
* @return the description
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
String description();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the minimum supported Minecraft version of the resource pack.
|
||||||
|
*
|
||||||
|
* @return the minimum supported Minecraft version
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
Version minimumSupportedMinecraftVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a module of a resource pack.
|
||||||
|
*/
|
||||||
|
interface Module {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the UUID of the module.
|
||||||
|
*
|
||||||
|
* @return the UUID
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
UUID uuid();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the version of the module.
|
||||||
|
*
|
||||||
|
* @return the version
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
Version version();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the type of the module.
|
||||||
|
*
|
||||||
|
* @return the type
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
String type();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the description of the module.
|
||||||
|
*
|
||||||
|
* @return the description
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
String description();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a dependency of a resource pack.
|
||||||
|
*/
|
||||||
|
interface Dependency {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the UUID of the dependency.
|
||||||
|
*
|
||||||
|
* @return the uuid
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
UUID uuid();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the version of the dependency.
|
||||||
|
*
|
||||||
|
* @return the version
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
Version version();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a version of a resource pack.
|
||||||
|
*/
|
||||||
|
interface Version {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the major version.
|
||||||
|
*
|
||||||
|
* @return the major version
|
||||||
|
*/
|
||||||
|
int major();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the minor version.
|
||||||
|
*
|
||||||
|
* @return the minor version
|
||||||
|
*/
|
||||||
|
int minor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the patch version.
|
||||||
|
*
|
||||||
|
* @return the patch version
|
||||||
|
*/
|
||||||
|
int patch();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the version formatted as a String.
|
||||||
|
*
|
||||||
|
* @return the version string
|
||||||
|
*/
|
||||||
|
@NonNull String toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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.api.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the platform Geyser is running on.
|
||||||
|
*/
|
||||||
|
public record PlatformType(String platformName) {
|
||||||
|
public static final PlatformType ANDROID = new PlatformType("Android");
|
||||||
|
public static final PlatformType BUNGEECORD = new PlatformType("BungeeCord");
|
||||||
|
public static final PlatformType FABRIC = new PlatformType("Fabric");
|
||||||
|
public static final PlatformType SPIGOT = new PlatformType("Spigot");
|
||||||
|
public static final PlatformType SPONGE = new PlatformType("Sponge");
|
||||||
|
public static final PlatformType STANDALONE = new PlatformType("Standalone");
|
||||||
|
public static final PlatformType VELOCITY = new PlatformType("Velocity");
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ platformRelocate("net.md_5.bungee.jni")
|
||||||
platformRelocate("com.fasterxml.jackson")
|
platformRelocate("com.fasterxml.jackson")
|
||||||
platformRelocate("io.netty.channel.kqueue") // This is not used because relocating breaks natives, but we must include it or else we get ClassDefNotFound
|
platformRelocate("io.netty.channel.kqueue") // This is not used because relocating breaks natives, but we must include it or else we get ClassDefNotFound
|
||||||
platformRelocate("net.kyori")
|
platformRelocate("net.kyori")
|
||||||
|
platformRelocate("org.yaml") // Broken as of 1.20
|
||||||
|
|
||||||
// These dependencies are already present on the platform
|
// These dependencies are already present on the platform
|
||||||
provided(libs.bungeecord.proxy)
|
provided(libs.bungeecord.proxy)
|
||||||
|
@ -21,7 +22,6 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
exclude(dependency("com.google.*:.*"))
|
exclude(dependency("com.google.*:.*"))
|
||||||
exclude(dependency("org.yaml:.*"))
|
|
||||||
exclude(dependency("io.netty:netty-transport-native-epoll:.*"))
|
exclude(dependency("io.netty:netty-transport-native-epoll:.*"))
|
||||||
exclude(dependency("io.netty:netty-transport-native-unix-common:.*"))
|
exclude(dependency("io.netty:netty-transport-native-unix-common:.*"))
|
||||||
exclude(dependency("io.netty:netty-handler:.*"))
|
exclude(dependency("io.netty:netty-handler:.*"))
|
||||||
|
@ -32,4 +32,4 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||||
exclude(dependency("io.netty:netty-codec:.*"))
|
exclude(dependency("io.netty:netty-codec:.*"))
|
||||||
exclude(dependency("io.netty:netty-resolver-dns:.*"))
|
exclude(dependency("io.netty:netty-resolver-dns:.*"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ import net.md_5.bungee.api.config.ListenerInfo;
|
||||||
import net.md_5.bungee.api.plugin.Plugin;
|
import net.md_5.bungee.api.plugin.Plugin;
|
||||||
import net.md_5.bungee.protocol.ProtocolConstants;
|
import net.md_5.bungee.protocol.ProtocolConstants;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.command.Command;
|
import org.geysermc.geyser.api.command.Command;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import net.fabricmc.loom.task.RemapJarTask
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("fabric-loom") version "1.0-SNAPSHOT"
|
id("fabric-loom") version "1.0-SNAPSHOT"
|
||||||
id("com.modrinth.minotaur") version "2.+"
|
id("com.modrinth.minotaur") version "2.+"
|
||||||
|
@ -89,8 +91,16 @@ tasks {
|
||||||
dependsOn(shadowJar)
|
dependsOn(shadowJar)
|
||||||
inputFile.set(shadowJar.get().archiveFile)
|
inputFile.set(shadowJar.get().archiveFile)
|
||||||
archiveBaseName.set("Geyser-Fabric")
|
archiveBaseName.set("Geyser-Fabric")
|
||||||
archiveClassifier.set("")
|
|
||||||
archiveVersion.set("")
|
archiveVersion.set("")
|
||||||
|
archiveClassifier.set("")
|
||||||
|
}
|
||||||
|
|
||||||
|
register("remapModrinthJar", RemapJarTask::class) {
|
||||||
|
dependsOn(shadowJar)
|
||||||
|
inputFile.set(shadowJar.get().archiveFile)
|
||||||
|
archiveBaseName.set("geyser-fabric")
|
||||||
|
archiveVersion.set(project.version.toString() + "+build." + System.getenv("GITHUB_RUN_NUMBER"))
|
||||||
|
archiveClassifier.set("")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,12 +113,13 @@ modrinth {
|
||||||
|
|
||||||
syncBodyFrom.set(rootProject.file("README.md").readText())
|
syncBodyFrom.set(rootProject.file("README.md").readText())
|
||||||
|
|
||||||
uploadFile.set(tasks.getByPath("remapJar"))
|
uploadFile.set(tasks.getByPath("remapModrinthJar"))
|
||||||
gameVersions.addAll("1.19", "1.19.1", "1.19.2", "1.19.3", "1.19.4")
|
gameVersions.addAll("1.20")
|
||||||
|
|
||||||
loaders.add("fabric")
|
loaders.add("fabric")
|
||||||
|
failSilently.set(true)
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
required.project("fabric-api")
|
required.project("fabric-api")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.commands.Commands;
|
import net.minecraft.commands.Commands;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.GeyserLogger;
|
import org.geysermc.geyser.GeyserLogger;
|
||||||
|
@ -217,10 +217,12 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||||
return this.server.getServerVersion();
|
return this.server.getServerVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions") // IDEA thinks that ip cannot be null
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public String getServerBindAddress() {
|
public String getServerBindAddress() {
|
||||||
return this.server.getLocalIp();
|
String ip = this.server.getLocalIp();
|
||||||
|
return ip != null ? ip : ""; // See issue #3812
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -72,7 +72,7 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LevelChunk chunk = player.getLevel().getChunk(x, z);
|
LevelChunk chunk = player.level().getChunk(x, z);
|
||||||
final int chunkBlockX = x << 4;
|
final int chunkBlockX = x << 4;
|
||||||
final int chunkBlockZ = z << 4;
|
final int chunkBlockZ = z << 4;
|
||||||
for (int i = 0; i < blockEntityInfos.size(); i++) {
|
for (int i = 0; i < blockEntityInfos.size(); i++) {
|
||||||
|
@ -92,7 +92,7 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockEntity blockEntity = player.level.getBlockEntity(new BlockPos(x, y, z));
|
BlockEntity blockEntity = player.level().getBlockEntity(new BlockPos(x, y, z));
|
||||||
sendLecternData(session, blockEntity, false);
|
sendLecternData(session, blockEntity, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
|
||||||
|
|
||||||
BlockPos pos = new BlockPos(x, y, z);
|
BlockPos pos = new BlockPos(x, y, z);
|
||||||
// Don't create a new block entity if invalid
|
// Don't create a new block entity if invalid
|
||||||
BlockEntity blockEntity = player.level.getChunkAt(pos).getBlockEntity(pos);
|
BlockEntity blockEntity = player.level().getChunkAt(pos).getBlockEntity(pos);
|
||||||
if (blockEntity instanceof BannerBlockEntity banner) {
|
if (blockEntity instanceof BannerBlockEntity banner) {
|
||||||
// Potentially exposes other NBT data? But we need to get the NBT data for the banner patterns *and*
|
// 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
|
// the banner might have a custom name, both of which a Java client knows and caches
|
||||||
|
|
|
@ -23,9 +23,9 @@
|
||||||
"geyser-fabric.mixins.json"
|
"geyser-fabric.mixins.json"
|
||||||
],
|
],
|
||||||
"depends": {
|
"depends": {
|
||||||
"fabricloader": ">=0.14.8",
|
"fabricloader": ">=0.14.21",
|
||||||
"fabric": "*",
|
"fabric": "*",
|
||||||
"minecraft": ">=1.19",
|
"minecraft": ">=1.20",
|
||||||
"fabric-permissions-api-v0": "*"
|
"fabric-permissions-api-v0": "*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ platformRelocate("com.fasterxml.jackson")
|
||||||
platformRelocate("net.kyori", "net.kyori.adventure.text.logger.slf4j.ComponentLogger")
|
platformRelocate("net.kyori", "net.kyori.adventure.text.logger.slf4j.ComponentLogger")
|
||||||
platformRelocate("org.objectweb.asm")
|
platformRelocate("org.objectweb.asm")
|
||||||
platformRelocate("me.lucko.commodore")
|
platformRelocate("me.lucko.commodore")
|
||||||
|
platformRelocate("org.yaml") // Broken as of 1.20
|
||||||
|
|
||||||
// These dependencies are already present on the platform
|
// These dependencies are already present on the platform
|
||||||
provided(libs.viaversion)
|
provided(libs.viaversion)
|
||||||
|
@ -42,7 +43,6 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
exclude(dependency("com.google.*:.*"))
|
exclude(dependency("com.google.*:.*"))
|
||||||
exclude(dependency("org.yaml:.*"))
|
|
||||||
|
|
||||||
// We cannot shade Netty, or else native libraries will not load
|
// We cannot shade Netty, or else native libraries will not load
|
||||||
// Needed because older Spigot builds do not provide the haproxy module
|
// Needed because older Spigot builds do not provide the haproxy module
|
||||||
|
@ -64,4 +64,4 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||||
// Commodore includes Brigadier
|
// Commodore includes Brigadier
|
||||||
exclude(dependency("com.mojang:.*"))
|
exclude(dependency("com.mojang:.*"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ import org.bukkit.permissions.Permission;
|
||||||
import org.bukkit.permissions.PermissionDefault;
|
import org.bukkit.permissions.PermissionDefault;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.Constants;
|
import org.geysermc.geyser.Constants;
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
|
|
@ -27,7 +27,7 @@ package org.geysermc.geyser.platform.sponge;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.command.Command;
|
import org.geysermc.geyser.api.command.Command;
|
||||||
|
|
|
@ -7,10 +7,8 @@ dependencies {
|
||||||
api(projects.core)
|
api(projects.core)
|
||||||
|
|
||||||
implementation(libs.terminalconsoleappender) {
|
implementation(libs.terminalconsoleappender) {
|
||||||
exclude("org.apache.logging.log4j", "log4j-core")
|
exclude("org.apache.logging.log4j")
|
||||||
exclude("org.jline", "jline-reader")
|
exclude("org.jline")
|
||||||
exclude("org.jline", "jline-terminal")
|
|
||||||
exclude("org.jline", "jline-terminal-jna")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation(libs.bundles.jline)
|
implementation(libs.bundles.jline)
|
||||||
|
|
|
@ -38,7 +38,7 @@ import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.core.Appender;
|
import org.apache.logging.log4j.core.Appender;
|
||||||
import org.apache.logging.log4j.core.Logger;
|
import org.apache.logging.log4j.core.Logger;
|
||||||
import org.apache.logging.log4j.core.appender.ConsoleAppender;
|
import org.apache.logging.log4j.core.appender.ConsoleAppender;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||||
|
@ -181,14 +181,14 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.geyserLogger = new GeyserStandaloneLogger();
|
||||||
|
|
||||||
if (useGui && gui == null) {
|
if (useGui && gui == null) {
|
||||||
gui = new GeyserStandaloneGUI();
|
gui = new GeyserStandaloneGUI(geyserLogger);
|
||||||
gui.redirectSystemStreams();
|
gui.redirectSystemStreams();
|
||||||
gui.startUpdateThread();
|
gui.startUpdateThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
geyserLogger = new GeyserStandaloneLogger();
|
|
||||||
|
|
||||||
LoopbackUtil.checkAndApplyLoopback(geyserLogger);
|
LoopbackUtil.checkAndApplyLoopback(geyserLogger);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -224,7 +224,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
geyserCommandManager.init();
|
geyserCommandManager.init();
|
||||||
|
|
||||||
if (gui != null) {
|
if (gui != null) {
|
||||||
gui.setupInterface(geyserLogger, geyserCommandManager);
|
gui.enableCommands(geyser.getScheduledThread(), geyserCommandManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||||
|
|
|
@ -92,6 +92,7 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements Gey
|
||||||
Configurator.setLevel(log.getName(), debug ? Level.DEBUG : Level.INFO);
|
Configurator.setLevel(log.getName(), debug ? Level.DEBUG : Level.INFO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isDebug() {
|
public boolean isDebug() {
|
||||||
return log.isDebugEnabled();
|
return log.isDebugEnabled();
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ public class ColorPane extends JTextPane {
|
||||||
while (stillSearching) {
|
while (stillSearching) {
|
||||||
mIndex = addString.indexOf("m", aPos); // find the end of the escape sequence
|
mIndex = addString.indexOf("m", aPos); // find the end of the escape sequence
|
||||||
if (mIndex < 0) { // the buffer ends halfway through the ansi string!
|
if (mIndex < 0) { // the buffer ends halfway through the ansi string!
|
||||||
remaining = addString.substring(aPos, addString.length());
|
remaining = addString.substring(aPos);
|
||||||
stillSearching = false;
|
stillSearching = false;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
@ -99,7 +99,7 @@ public class ColorPane extends JTextPane {
|
||||||
aIndex = addString.indexOf("\u001B", aPos);
|
aIndex = addString.indexOf("\u001B", aPos);
|
||||||
|
|
||||||
if (aIndex == -1) { // if that was the last sequence of the input, send remaining text
|
if (aIndex == -1) { // if that was the last sequence of the input, send remaining text
|
||||||
tmpString = addString.substring(aPos, addString.length());
|
tmpString = addString.substring(aPos);
|
||||||
append(colorCurrent, tmpString);
|
append(colorCurrent, tmpString);
|
||||||
stillSearching = false;
|
stillSearching = false;
|
||||||
continue; // jump out of loop early, as the whole string has been sent now
|
continue; // jump out of loop early, as the whole string has been sent now
|
||||||
|
|
|
@ -26,10 +26,8 @@
|
||||||
package org.geysermc.geyser.platform.standalone.gui;
|
package org.geysermc.geyser.platform.standalone.gui;
|
||||||
|
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.command.Command;
|
import org.geysermc.geyser.GeyserLogger;
|
||||||
import org.geysermc.geyser.command.GeyserCommand;
|
|
||||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||||
import org.geysermc.geyser.platform.standalone.GeyserStandaloneLogger;
|
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
|
|
||||||
|
@ -45,28 +43,37 @@ import java.io.PrintStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class GeyserStandaloneGUI {
|
public class GeyserStandaloneGUI {
|
||||||
|
|
||||||
private static final DefaultTableModel playerTableModel = new DefaultTableModel();
|
|
||||||
private static final List<Integer> ramValues = new ArrayList<>();
|
|
||||||
|
|
||||||
private static final ColorPane consolePane = new ColorPane();
|
|
||||||
private static final GraphPanel ramGraph = new GraphPanel();
|
|
||||||
private static final JTable playerTable = new JTable(playerTableModel);
|
|
||||||
private static final int originalFontSize = consolePane.getFont().getSize();
|
|
||||||
|
|
||||||
private static final long MEGABYTE = 1024L * 1024L;
|
private static final long MEGABYTE = 1024L * 1024L;
|
||||||
|
|
||||||
private final JMenu commandsMenu;
|
private final GeyserLogger logger;
|
||||||
private final JMenu optionsMenu;
|
|
||||||
|
private final ColorPane consolePane = new ColorPane();
|
||||||
|
private final int originalFontSize = consolePane.getFont().getSize();
|
||||||
|
private final JTextField commandInput = new JTextField();
|
||||||
|
private final CommandListener commandListener = new CommandListener();
|
||||||
|
|
||||||
|
private final GraphPanel ramGraph = new GraphPanel();
|
||||||
|
private final List<Integer> ramValues = new ArrayList<>();
|
||||||
|
|
||||||
|
private final DefaultTableModel playerTableModel = new DefaultTableModel();
|
||||||
|
private final JTable playerTable = new JTable(playerTableModel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and show the Geyser-Standalone GUI
|
||||||
|
*
|
||||||
|
* @param logger the logger for determining debug mode, and executing commands from the console
|
||||||
|
*/
|
||||||
|
public GeyserStandaloneGUI(GeyserLogger logger) {
|
||||||
|
this.logger = logger;
|
||||||
|
|
||||||
public GeyserStandaloneGUI() {
|
|
||||||
// Create the frame and setup basic settings
|
// Create the frame and setup basic settings
|
||||||
JFrame frame = new JFrame(GeyserLocale.getLocaleStringLog("geyser.gui.title"));
|
JFrame frame = new JFrame(GeyserLocale.getLocaleStringLog("geyser.gui.title"));
|
||||||
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||||
|
@ -81,8 +88,7 @@ public class GeyserStandaloneGUI {
|
||||||
// Show a confirm dialog on close
|
// Show a confirm dialog on close
|
||||||
frame.addWindowListener(new WindowAdapter() {
|
frame.addWindowListener(new WindowAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void windowClosing(WindowEvent we)
|
public void windowClosing(WindowEvent we) {
|
||||||
{
|
|
||||||
String[] buttons = {GeyserLocale.getLocaleStringLog("geyser.gui.exit.confirm"), GeyserLocale.getLocaleStringLog("geyser.gui.exit.deny")};
|
String[] buttons = {GeyserLocale.getLocaleStringLog("geyser.gui.exit.confirm"), GeyserLocale.getLocaleStringLog("geyser.gui.exit.deny")};
|
||||||
int result = JOptionPane.showOptionDialog(frame, GeyserLocale.getLocaleStringLog("geyser.gui.exit.message"), frame.getTitle(), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, buttons, buttons[1]);
|
int result = JOptionPane.showOptionDialog(frame, GeyserLocale.getLocaleStringLog("geyser.gui.exit.message"), frame.getTitle(), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, buttons, buttons[1]);
|
||||||
if (result == JOptionPane.YES_OPTION) {
|
if (result == JOptionPane.YES_OPTION) {
|
||||||
|
@ -100,91 +106,41 @@ public class GeyserStandaloneGUI {
|
||||||
frame.setIconImage(icon.getImage());
|
frame.setIconImage(icon.getImage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// File, View, Options, etc
|
||||||
|
setupMenuBar(frame);
|
||||||
|
|
||||||
// Setup the split pane and event listeners
|
// Setup the split pane and event listeners
|
||||||
JSplitPane splitPane = new JSplitPane();
|
JSplitPane splitPane = new JSplitPane();
|
||||||
splitPane.setDividerLocation(600);
|
splitPane.setDividerLocation(600);
|
||||||
splitPane.addPropertyChangeListener("dividerLocation", e -> splitPaneLimit((JSplitPane)e.getSource()));
|
splitPane.addPropertyChangeListener("dividerLocation", e -> splitPaneLimit((JSplitPane) e.getSource()));
|
||||||
splitPane.addComponentListener(new ComponentAdapter() {
|
splitPane.addComponentListener(new ComponentAdapter() {
|
||||||
public void componentResized(ComponentEvent e) {
|
public void componentResized(ComponentEvent e) {
|
||||||
splitPaneLimit((JSplitPane)e.getSource());
|
splitPaneLimit((JSplitPane) e.getSource());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cp.add(splitPane, BorderLayout.CENTER);
|
cp.add(splitPane, BorderLayout.CENTER);
|
||||||
|
|
||||||
// Set the background and disable input for the text pane
|
// Holds console and command input components
|
||||||
|
JPanel leftPane = new JPanel(new BorderLayout());
|
||||||
|
splitPane.setLeftComponent(leftPane);
|
||||||
|
|
||||||
|
// Set the background and disable editing of the console
|
||||||
consolePane.setBackground(Color.BLACK);
|
consolePane.setBackground(Color.BLACK);
|
||||||
consolePane.setEditable(false);
|
consolePane.setEditable(false);
|
||||||
|
|
||||||
// Wrap the text pane in a scroll pane and add it to the form
|
// Wrap the text pane in a scroll pane and add it to the form
|
||||||
JScrollPane consoleScrollPane = new JScrollPane(consolePane);
|
JScrollPane consoleScrollPane = new JScrollPane(consolePane);
|
||||||
//cp.add(consoleScrollPane, BorderLayout.CENTER);
|
leftPane.add(consoleScrollPane, BorderLayout.CENTER);
|
||||||
splitPane.setLeftComponent(consoleScrollPane);
|
|
||||||
|
|
||||||
// Create a new menu bar for the top of the frame
|
// a bit taller than the default layout - width is ignored fortunately
|
||||||
JMenuBar menuBar = new JMenuBar();
|
commandInput.setPreferredSize(new Dimension(100, 25));
|
||||||
|
commandInput.setEnabled(false); // disabled until command handler is initialized
|
||||||
// Create 'File'
|
commandInput.addActionListener(commandListener);
|
||||||
JMenu fileMenu = new JMenu(GeyserLocale.getLocaleStringLog("geyser.gui.menu.file"));
|
leftPane.add(commandInput, BorderLayout.SOUTH);
|
||||||
fileMenu.setMnemonic(KeyEvent.VK_F);
|
|
||||||
menuBar.add(fileMenu);
|
|
||||||
|
|
||||||
// 'Open Geyser folder' button
|
|
||||||
JMenuItem openButton = new JMenuItem(GeyserLocale.getLocaleStringLog("geyser.gui.menu.file.open_folder"), KeyEvent.VK_O);
|
|
||||||
openButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK));
|
|
||||||
openButton.addActionListener(e -> {
|
|
||||||
try {
|
|
||||||
Desktop.getDesktop().open(new File("./"));
|
|
||||||
} catch (IOException ignored) { }
|
|
||||||
});
|
|
||||||
fileMenu.add(openButton);
|
|
||||||
|
|
||||||
fileMenu.addSeparator();
|
|
||||||
|
|
||||||
// 'Exit' button
|
|
||||||
JMenuItem exitButton = new JMenuItem(GeyserLocale.getLocaleStringLog("geyser.gui.menu.file.exit"), KeyEvent.VK_X);
|
|
||||||
exitButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.ALT_MASK));
|
|
||||||
exitButton.addActionListener(e -> System.exit(0));
|
|
||||||
fileMenu.add(exitButton);
|
|
||||||
|
|
||||||
// Create 'Commands'
|
|
||||||
commandsMenu = new JMenu(GeyserLocale.getLocaleStringLog("geyser.gui.menu.commands"));
|
|
||||||
commandsMenu.setMnemonic(KeyEvent.VK_C);
|
|
||||||
menuBar.add(commandsMenu);
|
|
||||||
|
|
||||||
// Create 'View'
|
|
||||||
JMenu viewMenu = new JMenu(GeyserLocale.getLocaleStringLog("geyser.gui.menu.view"));
|
|
||||||
viewMenu.setMnemonic(KeyEvent.VK_V);
|
|
||||||
menuBar.add(viewMenu);
|
|
||||||
|
|
||||||
// 'Zoom in' button
|
|
||||||
JMenuItem zoomInButton = new JMenuItem(GeyserLocale.getLocaleStringLog("geyser.gui.menu.view.zoom_in"));
|
|
||||||
zoomInButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, InputEvent.CTRL_DOWN_MASK));
|
|
||||||
zoomInButton.addActionListener(e -> consolePane.setFont(new Font(consolePane.getFont().getName(), consolePane.getFont().getStyle(), consolePane.getFont().getSize() + 1)));
|
|
||||||
viewMenu.add(zoomInButton);
|
|
||||||
|
|
||||||
// 'Zoom in' button
|
|
||||||
JMenuItem zoomOutButton = new JMenuItem(GeyserLocale.getLocaleStringLog("geyser.gui.menu.view.zoom_out"));
|
|
||||||
zoomOutButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, InputEvent.CTRL_DOWN_MASK));
|
|
||||||
zoomOutButton.addActionListener(e -> consolePane.setFont(new Font(consolePane.getFont().getName(), consolePane.getFont().getStyle(), consolePane.getFont().getSize() - 1)));
|
|
||||||
viewMenu.add(zoomOutButton);
|
|
||||||
|
|
||||||
// 'Reset Zoom' button
|
|
||||||
JMenuItem resetZoomButton = new JMenuItem(GeyserLocale.getLocaleStringLog("geyser.gui.menu.view.reset_zoom"));
|
|
||||||
resetZoomButton.addActionListener(e -> consolePane.setFont(new Font(consolePane.getFont().getName(), consolePane.getFont().getStyle(), originalFontSize)));
|
|
||||||
viewMenu.add(resetZoomButton);
|
|
||||||
|
|
||||||
// create 'Options'
|
|
||||||
optionsMenu = new JMenu(GeyserLocale.getLocaleStringLog("geyser.gui.menu.options"));
|
|
||||||
viewMenu.setMnemonic(KeyEvent.VK_O);
|
|
||||||
menuBar.add(optionsMenu);
|
|
||||||
|
|
||||||
// Set the frames menu bar
|
|
||||||
frame.setJMenuBar(menuBar);
|
|
||||||
|
|
||||||
JPanel rightPane = new JPanel();
|
JPanel rightPane = new JPanel();
|
||||||
rightPane.setLayout(new CardLayout(5, 5));
|
rightPane.setLayout(new CardLayout(5, 5));
|
||||||
//cp.add(rightPane, BorderLayout.EAST);
|
|
||||||
splitPane.setRightComponent(rightPane);
|
splitPane.setRightComponent(rightPane);
|
||||||
|
|
||||||
JPanel rightContentPane = new JPanel();
|
JPanel rightContentPane = new JPanel();
|
||||||
|
@ -209,12 +165,75 @@ public class GeyserStandaloneGUI {
|
||||||
frame.setVisible(true);
|
frame.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupMenuBar(JFrame frame) {
|
||||||
|
// Create a new menu bar for the top of the frame
|
||||||
|
JMenuBar menuBar = new JMenuBar();
|
||||||
|
|
||||||
|
// Create 'File'
|
||||||
|
JMenu fileMenu = new JMenu(GeyserLocale.getLocaleStringLog("geyser.gui.menu.file"));
|
||||||
|
fileMenu.setMnemonic(KeyEvent.VK_F);
|
||||||
|
menuBar.add(fileMenu);
|
||||||
|
|
||||||
|
// 'Open Geyser folder' button
|
||||||
|
JMenuItem openButton = new JMenuItem(GeyserLocale.getLocaleStringLog("geyser.gui.menu.file.open_folder"), KeyEvent.VK_O);
|
||||||
|
openButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK));
|
||||||
|
openButton.addActionListener(e -> {
|
||||||
|
try {
|
||||||
|
Desktop.getDesktop().open(new File("./"));
|
||||||
|
} catch (IOException ignored) { }
|
||||||
|
});
|
||||||
|
fileMenu.add(openButton);
|
||||||
|
|
||||||
|
fileMenu.addSeparator();
|
||||||
|
|
||||||
|
// 'Exit' button
|
||||||
|
JMenuItem exitButton = new JMenuItem(GeyserLocale.getLocaleStringLog("geyser.gui.menu.file.exit"), KeyEvent.VK_X);
|
||||||
|
exitButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.ALT_DOWN_MASK));
|
||||||
|
exitButton.addActionListener(e -> System.exit(0));
|
||||||
|
fileMenu.add(exitButton);
|
||||||
|
|
||||||
|
// Create 'View'
|
||||||
|
JMenu viewMenu = new JMenu(GeyserLocale.getLocaleStringLog("geyser.gui.menu.view"));
|
||||||
|
viewMenu.setMnemonic(KeyEvent.VK_V);
|
||||||
|
menuBar.add(viewMenu);
|
||||||
|
|
||||||
|
// 'Zoom in' button
|
||||||
|
JMenuItem zoomInButton = new JMenuItem(GeyserLocale.getLocaleStringLog("geyser.gui.menu.view.zoom_in"));
|
||||||
|
zoomInButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, InputEvent.CTRL_DOWN_MASK));
|
||||||
|
zoomInButton.addActionListener(e -> consolePane.setFont(new Font(consolePane.getFont().getName(), consolePane.getFont().getStyle(), consolePane.getFont().getSize() + 1)));
|
||||||
|
viewMenu.add(zoomInButton);
|
||||||
|
|
||||||
|
// 'Zoom in' button
|
||||||
|
JMenuItem zoomOutButton = new JMenuItem(GeyserLocale.getLocaleStringLog("geyser.gui.menu.view.zoom_out"));
|
||||||
|
zoomOutButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, InputEvent.CTRL_DOWN_MASK));
|
||||||
|
zoomOutButton.addActionListener(e -> consolePane.setFont(new Font(consolePane.getFont().getName(), consolePane.getFont().getStyle(), consolePane.getFont().getSize() - 1)));
|
||||||
|
viewMenu.add(zoomOutButton);
|
||||||
|
|
||||||
|
// 'Reset Zoom' button
|
||||||
|
JMenuItem resetZoomButton = new JMenuItem(GeyserLocale.getLocaleStringLog("geyser.gui.menu.view.reset_zoom"));
|
||||||
|
resetZoomButton.addActionListener(e -> consolePane.setFont(new Font(consolePane.getFont().getName(), consolePane.getFont().getStyle(), originalFontSize)));
|
||||||
|
viewMenu.add(resetZoomButton);
|
||||||
|
|
||||||
|
// create 'Options'
|
||||||
|
JMenu optionsMenu = new JMenu(GeyserLocale.getLocaleStringLog("geyser.gui.menu.options"));
|
||||||
|
viewMenu.setMnemonic(KeyEvent.VK_O);
|
||||||
|
menuBar.add(optionsMenu);
|
||||||
|
|
||||||
|
JCheckBoxMenuItem debugMode = new JCheckBoxMenuItem(GeyserLocale.getLocaleStringLog("geyser.gui.menu.options.toggle_debug_mode"));
|
||||||
|
debugMode.setSelected(logger.isDebug());
|
||||||
|
debugMode.addActionListener(e -> logger.setDebug(debugMode.getState()));
|
||||||
|
optionsMenu.add(debugMode);
|
||||||
|
|
||||||
|
// Set the frames menu bar
|
||||||
|
frame.setJMenuBar(menuBar);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queue up an update to the text pane so we don't block the main thread
|
* Queue up an update to the text pane so we don't block the main thread
|
||||||
*
|
*
|
||||||
* @param text The text to append
|
* @param text The text to append
|
||||||
*/
|
*/
|
||||||
private void updateTextPane(final String text) {
|
private void appendConsole(final String text) {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
consolePane.appendANSI(text);
|
consolePane.appendANSI(text);
|
||||||
Document doc = consolePane.getDocument();
|
Document doc = consolePane.getDocument();
|
||||||
|
@ -230,12 +249,12 @@ public class GeyserStandaloneGUI {
|
||||||
OutputStream out = new OutputStream() {
|
OutputStream out = new OutputStream() {
|
||||||
@Override
|
@Override
|
||||||
public void write(final int b) {
|
public void write(final int b) {
|
||||||
updateTextPane(String.valueOf((char) b));
|
appendConsole(String.valueOf((char) b));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(byte[] b, int off, int len) {
|
public void write(byte[] b, int off, int len) {
|
||||||
updateTextPane(new String(b, off, len));
|
appendConsole(new String(b, off, len));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -251,50 +270,17 @@ public class GeyserStandaloneGUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add all the Geyser commands to the commands menu, and setup the debug mode toggle
|
* Enable the command input box.
|
||||||
*
|
*
|
||||||
* @param geyserStandaloneLogger The current logger
|
* @param executor the executor for running commands off the GUI thread
|
||||||
* @param geyserCommandManager The commands manager
|
* @param commandManager the command manager to delegate commands to
|
||||||
*/
|
*/
|
||||||
public void setupInterface(GeyserStandaloneLogger geyserStandaloneLogger, GeyserCommandManager geyserCommandManager) {
|
public void enableCommands(ScheduledExecutorService executor, GeyserCommandManager commandManager) {
|
||||||
commandsMenu.removeAll();
|
// we don't want to block the GUI thread with the command execution
|
||||||
optionsMenu.removeAll();
|
// todo: once cloud is used, an AsynchronousCommandExecutionCoordinator can be used to avoid this scheduler
|
||||||
|
commandListener.handler = cmd -> executor.schedule(() -> commandManager.runCommand(logger, cmd), 0, TimeUnit.SECONDS);
|
||||||
for (Map.Entry<String, Command> entry : geyserCommandManager.getCommands().entrySet()) {
|
commandInput.setEnabled(true);
|
||||||
// Remove the offhand command and any alias commands to prevent duplicates in the list
|
commandInput.requestFocusInWindow();
|
||||||
if (!entry.getValue().isExecutableOnConsole() || entry.getValue().aliases().contains(entry.getKey())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
GeyserCommand command = (GeyserCommand) entry.getValue();
|
|
||||||
// Create the button that runs the command
|
|
||||||
boolean hasSubCommands = !entry.getValue().subCommands().isEmpty();
|
|
||||||
// Add an extra menu if there are more commands that can be run
|
|
||||||
JMenuItem commandButton = hasSubCommands ? new JMenu(entry.getValue().name()) : new JMenuItem(entry.getValue().name());
|
|
||||||
commandButton.getAccessibleContext().setAccessibleDescription(entry.getValue().description());
|
|
||||||
if (!hasSubCommands) {
|
|
||||||
commandButton.addActionListener(e -> command.execute(null, geyserStandaloneLogger, new String[]{ }));
|
|
||||||
} else {
|
|
||||||
// Add a submenu that's the same name as the menu can't be pressed
|
|
||||||
JMenuItem otherCommandButton = new JMenuItem(entry.getValue().name());
|
|
||||||
otherCommandButton.getAccessibleContext().setAccessibleDescription(entry.getValue().description());
|
|
||||||
otherCommandButton.addActionListener(e -> command.execute(null, geyserStandaloneLogger, new String[]{ }));
|
|
||||||
commandButton.add(otherCommandButton);
|
|
||||||
// Add a menu option for all possible subcommands
|
|
||||||
for (String subCommandName : entry.getValue().subCommands()) {
|
|
||||||
JMenuItem item = new JMenuItem(subCommandName);
|
|
||||||
item.addActionListener(e -> command.execute(null, geyserStandaloneLogger, new String[]{subCommandName}));
|
|
||||||
commandButton.add(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
commandsMenu.add(commandButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 'Debug Mode' toggle
|
|
||||||
JCheckBoxMenuItem debugMode = new JCheckBoxMenuItem(GeyserLocale.getLocaleStringLog("geyser.gui.menu.options.toggle_debug_mode"));
|
|
||||||
debugMode.setSelected(geyserStandaloneLogger.isDebug());
|
|
||||||
debugMode.addActionListener(e -> geyserStandaloneLogger.setDebug(!geyserStandaloneLogger.isDebug()));
|
|
||||||
optionsMenu.add(debugMode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -322,14 +308,14 @@ public class GeyserStandaloneGUI {
|
||||||
// Update ram graph
|
// Update ram graph
|
||||||
final long freeMemory = Runtime.getRuntime().freeMemory();
|
final long freeMemory = Runtime.getRuntime().freeMemory();
|
||||||
final long totalMemory = Runtime.getRuntime().totalMemory();
|
final long totalMemory = Runtime.getRuntime().totalMemory();
|
||||||
final int freePercent = (int)(freeMemory * 100.0 / totalMemory + 0.5);
|
final int freePercent = (int) (freeMemory * 100.0 / totalMemory + 0.5);
|
||||||
ramValues.add(100 - freePercent);
|
ramValues.add(100 - freePercent);
|
||||||
|
|
||||||
ramGraph.setXLabel(GeyserLocale.getLocaleStringLog("geyser.gui.graph.usage", String.format("%,d", (totalMemory - freeMemory) / MEGABYTE), freePercent));
|
ramGraph.setXLabel(GeyserLocale.getLocaleStringLog("geyser.gui.graph.usage", String.format("%,d", (totalMemory - freeMemory) / MEGABYTE), freePercent));
|
||||||
|
|
||||||
// Trim the list
|
// Trim the list
|
||||||
int k = ramValues.size();
|
int k = ramValues.size();
|
||||||
if ( k > 10 )
|
if (k > 10)
|
||||||
ramValues.subList(0, k - 10).clear();
|
ramValues.subList(0, k - 10).clear();
|
||||||
|
|
||||||
// Update the graph
|
// Update the graph
|
||||||
|
@ -354,4 +340,17 @@ public class GeyserStandaloneGUI {
|
||||||
splitPane.setDividerLocation(frame.getWidth() - 200);
|
splitPane.setDividerLocation(frame.getWidth() - 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class CommandListener implements ActionListener {
|
||||||
|
|
||||||
|
private Consumer<String> handler;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
String command = commandInput.getText();
|
||||||
|
appendConsole(command + "\n"); // show what was run in the console
|
||||||
|
handler.accept(command); // run the command
|
||||||
|
commandInput.setText(""); // clear the input
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
<Configuration status="WARN">
|
<Configuration status="WARN">
|
||||||
<Appenders>
|
<Appenders>
|
||||||
<TerminalConsole name="TerminalConsole">
|
<TerminalConsole name="TerminalConsole">
|
||||||
<PatternLayout pattern="[%d{HH:mm:ss} %style{%highlight{%level}{FATAL=red dark, ERROR=red, WARN=yellow bright, INFO=cyan bright, DEBUG=green, TRACE=white}}] %minecraftFormatting{%msg}%n"/>
|
<PatternLayout pattern="[%d{HH:mm:ss} %style{%highlight{%level}{FATAL=red, ERROR=red, WARN=yellow bright, INFO=cyan bright, DEBUG=green, TRACE=white}}] %minecraftFormatting{%msg}%n"/>
|
||||||
</TerminalConsole>
|
</TerminalConsole>
|
||||||
<Console name="Console" target="SYSTEM_OUT" follow="true">
|
<Console name="Console" target="SYSTEM_OUT" follow="true">
|
||||||
<PatternLayout pattern="[%d{HH:mm:ss} %style{%highlight{%level}{FATAL=red dark, ERROR=red, WARN=yellow bright, INFO=cyan bright, DEBUG=green, TRACE=white}}] %minecraftFormatting{%msg}%n"/>
|
<PatternLayout pattern="[%d{HH:mm:ss} %style{%highlight{%level}{FATAL=red, ERROR=red, WARN=yellow bright, INFO=cyan bright, DEBUG=green, TRACE=white}}] %minecraftFormatting{%msg}%n"/>
|
||||||
</Console>
|
</Console>
|
||||||
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
|
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
|
||||||
<PatternLayout pattern="[%d{HH:mm:ss.SSS} %t/%level] %minecraftFormatting{%msg}{strip}%n"/>
|
<PatternLayout pattern="[%d{HH:mm:ss.SSS} %t/%level] %minecraftFormatting{%msg}{strip}%n"/>
|
||||||
|
|
|
@ -36,7 +36,7 @@ import com.velocitypowered.api.plugin.Plugin;
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.kyori.adventure.util.Codec;
|
import net.kyori.adventure.util.Codec;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.command.Command;
|
import org.geysermc.geyser.api.command.Command;
|
||||||
|
|
|
@ -6,7 +6,7 @@ plugins {
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group = "org.geysermc.geyser"
|
group = "org.geysermc.geyser"
|
||||||
version = "2.1.0-SNAPSHOT"
|
version = "2.1.1-SNAPSHOT"
|
||||||
description = "Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers."
|
description = "Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers."
|
||||||
|
|
||||||
tasks.withType<JavaCompile> {
|
tasks.withType<JavaCompile> {
|
||||||
|
|
|
@ -30,7 +30,7 @@ dependencies {
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation(libs.raknet) {
|
implementation(libs.raknet) {
|
||||||
exclude("io.netty", "*");
|
exclude("io.netty", "*")
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation(libs.netty.resolver.dns)
|
implementation(libs.netty.resolver.dns)
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
package org.geysermc.connector;
|
package org.geysermc.connector;
|
||||||
|
|
||||||
import org.geysermc.api.Geyser;
|
import org.geysermc.api.Geyser;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.geysermc.api.Geyser;
|
import org.geysermc.api.Geyser;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.cumulus.form.Form;
|
import org.geysermc.cumulus.form.Form;
|
||||||
import org.geysermc.cumulus.form.util.FormBuilder;
|
import org.geysermc.cumulus.form.util.FormBuilder;
|
||||||
import org.geysermc.erosion.packet.Packets;
|
import org.geysermc.erosion.packet.Packets;
|
||||||
|
@ -69,9 +69,9 @@ import org.geysermc.geyser.event.GeyserEventBus;
|
||||||
import org.geysermc.geyser.extension.GeyserExtensionManager;
|
import org.geysermc.geyser.extension.GeyserExtensionManager;
|
||||||
import org.geysermc.geyser.level.WorldManager;
|
import org.geysermc.geyser.level.WorldManager;
|
||||||
import org.geysermc.geyser.network.netty.GeyserServer;
|
import org.geysermc.geyser.network.netty.GeyserServer;
|
||||||
import org.geysermc.geyser.pack.ResourcePack;
|
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.registry.Registries;
|
import org.geysermc.geyser.registry.Registries;
|
||||||
|
import org.geysermc.geyser.registry.loader.RegistryLoaders;
|
||||||
import org.geysermc.geyser.scoreboard.ScoreboardUpdater;
|
import org.geysermc.geyser.scoreboard.ScoreboardUpdater;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.session.PendingMicrosoftAuthentication;
|
import org.geysermc.geyser.session.PendingMicrosoftAuthentication;
|
||||||
|
@ -90,6 +90,7 @@ import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -217,25 +218,9 @@ public class GeyserImpl implements GeyserApi {
|
||||||
|
|
||||||
GeyserConfiguration config = bootstrap.getGeyserConfig();
|
GeyserConfiguration config = bootstrap.getGeyserConfig();
|
||||||
|
|
||||||
boolean isGui = false;
|
|
||||||
// This will check if we are in standalone and get the 'useGui' variable from there
|
|
||||||
if (platformType == PlatformType.STANDALONE) {
|
|
||||||
try {
|
|
||||||
Class<?> cls = Class.forName("org.geysermc.geyser.platform.standalone.GeyserStandaloneBootstrap");
|
|
||||||
isGui = (boolean) cls.getMethod("isUseGui").invoke(cls.cast(bootstrap));
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.debug("Failed detecting if standalone is using a GUI; if this is a GeyserConnect instance this can be safely ignored.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double completeTime = (System.currentTimeMillis() - startupTime) / 1000D;
|
double completeTime = (System.currentTimeMillis() - startupTime) / 1000D;
|
||||||
String message = GeyserLocale.getLocaleStringLog("geyser.core.finish.done", new DecimalFormat("#.###").format(completeTime)) + " ";
|
String message = GeyserLocale.getLocaleStringLog("geyser.core.finish.done", new DecimalFormat("#.###").format(completeTime));
|
||||||
if (isGui) {
|
message += " " + GeyserLocale.getLocaleStringLog("geyser.core.finish.console");
|
||||||
message += GeyserLocale.getLocaleStringLog("geyser.core.finish.gui");
|
|
||||||
} else {
|
|
||||||
message += GeyserLocale.getLocaleStringLog("geyser.core.finish.console");
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(message);
|
logger.info(message);
|
||||||
|
|
||||||
if (platformType == PlatformType.STANDALONE) {
|
if (platformType == PlatformType.STANDALONE) {
|
||||||
|
@ -258,7 +243,7 @@ public class GeyserImpl implements GeyserApi {
|
||||||
|
|
||||||
SkinProvider.registerCacheImageTask(this);
|
SkinProvider.registerCacheImageTask(this);
|
||||||
|
|
||||||
ResourcePack.loadPacks();
|
Registries.RESOURCE_PACKS.load();
|
||||||
|
|
||||||
String geyserUdpPort = System.getProperty("geyserUdpPort", "");
|
String geyserUdpPort = System.getProperty("geyserUdpPort", "");
|
||||||
String pluginUdpPort = geyserUdpPort.isEmpty() ? System.getProperty("pluginUdpPort", "") : geyserUdpPort;
|
String pluginUdpPort = geyserUdpPort.isEmpty() ? System.getProperty("pluginUdpPort", "") : geyserUdpPort;
|
||||||
|
@ -410,7 +395,7 @@ public class GeyserImpl implements GeyserApi {
|
||||||
metrics.addCustomChart(new Metrics.SingleLineChart("players", sessionManager::size));
|
metrics.addCustomChart(new Metrics.SingleLineChart("players", sessionManager::size));
|
||||||
// Prevent unwanted words best we can
|
// Prevent unwanted words best we can
|
||||||
metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().authType().toString().toLowerCase(Locale.ROOT)));
|
metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().authType().toString().toLowerCase(Locale.ROOT)));
|
||||||
metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::getPlatformName));
|
metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::platformName));
|
||||||
metrics.addCustomChart(new Metrics.SimplePie("defaultLocale", GeyserLocale::getDefaultLocale));
|
metrics.addCustomChart(new Metrics.SimplePie("defaultLocale", GeyserLocale::getDefaultLocale));
|
||||||
metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserImpl.VERSION));
|
metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserImpl.VERSION));
|
||||||
metrics.addCustomChart(new Metrics.AdvancedPie("playerPlatform", () -> {
|
metrics.addCustomChart(new Metrics.AdvancedPie("playerPlatform", () -> {
|
||||||
|
@ -446,7 +431,7 @@ public class GeyserImpl implements GeyserApi {
|
||||||
if (minecraftVersion != null) {
|
if (minecraftVersion != null) {
|
||||||
Map<String, Map<String, Integer>> versionMap = new HashMap<>();
|
Map<String, Map<String, Integer>> versionMap = new HashMap<>();
|
||||||
Map<String, Integer> platformMap = new HashMap<>();
|
Map<String, Integer> platformMap = new HashMap<>();
|
||||||
platformMap.put(platformType.getPlatformName(), 1);
|
platformMap.put(platformType.platformName(), 1);
|
||||||
versionMap.put(minecraftVersion, platformMap);
|
versionMap.put(minecraftVersion, platformMap);
|
||||||
|
|
||||||
metrics.addCustomChart(new Metrics.DrilldownPie("minecraftServerVersion", () -> {
|
metrics.addCustomChart(new Metrics.DrilldownPie("minecraftServerVersion", () -> {
|
||||||
|
@ -622,7 +607,7 @@ public class GeyserImpl implements GeyserApi {
|
||||||
this.erosionUnixListener.close();
|
this.erosionUnixListener.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourcePack.PACKS.clear();
|
Registries.RESOURCE_PACKS.get().clear();
|
||||||
|
|
||||||
this.eventBus.fire(new GeyserShutdownEvent(this.extensionManager, this.eventBus));
|
this.eventBus.fire(new GeyserShutdownEvent(this.extensionManager, this.eventBus));
|
||||||
this.extensionManager.disableExtensions();
|
this.extensionManager.disableExtensions();
|
||||||
|
@ -681,6 +666,24 @@ public class GeyserImpl implements GeyserApi {
|
||||||
return getConfig().getBedrock();
|
return getConfig().getBedrock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public Path configDirectory() {
|
||||||
|
return bootstrap.getConfigFolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public Path packDirectory() {
|
||||||
|
return bootstrap.getConfigFolder().resolve("packs");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public PlatformType platformType() {
|
||||||
|
return platformType;
|
||||||
|
}
|
||||||
|
|
||||||
public int buildNumber() {
|
public int buildNumber() {
|
||||||
if (!this.isProductionEnvironment()) {
|
if (!this.isProductionEnvironment()) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -29,7 +29,7 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.command.Command;
|
import org.geysermc.geyser.api.command.Command;
|
||||||
import org.geysermc.geyser.api.command.CommandExecutor;
|
import org.geysermc.geyser.api.command.CommandExecutor;
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
package org.geysermc.geyser.command.defaults;
|
package org.geysermc.geyser.command.defaults;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.command.GeyserCommand;
|
import org.geysermc.geyser.command.GeyserCommand;
|
||||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||||
|
|
|
@ -30,7 +30,7 @@ import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.command.GeyserCommand;
|
import org.geysermc.geyser.command.GeyserCommand;
|
||||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.command.defaults;
|
package org.geysermc.geyser.command.defaults;
|
||||||
|
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.command.Command;
|
import org.geysermc.geyser.api.command.Command;
|
||||||
import org.geysermc.geyser.command.GeyserCommand;
|
import org.geysermc.geyser.command.GeyserCommand;
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.command.defaults;
|
package org.geysermc.geyser.command.defaults;
|
||||||
|
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.command.GeyserCommand;
|
import org.geysermc.geyser.command.GeyserCommand;
|
||||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.command.defaults;
|
package org.geysermc.geyser.command.defaults;
|
||||||
|
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.command.GeyserCommand;
|
import org.geysermc.geyser.command.GeyserCommand;
|
||||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
package org.geysermc.geyser.command.defaults;
|
package org.geysermc.geyser.command.defaults;
|
||||||
|
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.command.GeyserCommand;
|
import org.geysermc.geyser.command.GeyserCommand;
|
||||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||||
|
|
|
@ -27,7 +27,7 @@ package org.geysermc.geyser.dump;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.text.AsteriskSerializer;
|
import org.geysermc.geyser.text.AsteriskSerializer;
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,7 @@ public final class EntityDefinitions {
|
||||||
public static final EntityDefinition<HorseEntity> HORSE;
|
public static final EntityDefinition<HorseEntity> HORSE;
|
||||||
public static final EntityDefinition<ZombieEntity> HUSK;
|
public static final EntityDefinition<ZombieEntity> HUSK;
|
||||||
public static final EntityDefinition<SpellcasterIllagerEntity> ILLUSIONER; // Not present on Bedrock
|
public static final EntityDefinition<SpellcasterIllagerEntity> ILLUSIONER; // Not present on Bedrock
|
||||||
|
public static final EntityDefinition<InteractionEntity> INTERACTION;
|
||||||
public static final EntityDefinition<IronGolemEntity> IRON_GOLEM;
|
public static final EntityDefinition<IronGolemEntity> IRON_GOLEM;
|
||||||
public static final EntityDefinition<ItemEntity> ITEM;
|
public static final EntityDefinition<ItemEntity> ITEM;
|
||||||
public static final EntityDefinition<ItemFrameEntity> ITEM_FRAME;
|
public static final EntityDefinition<ItemFrameEntity> ITEM_FRAME;
|
||||||
|
@ -133,6 +134,7 @@ public final class EntityDefinitions {
|
||||||
public static final EntityDefinition<AbstractFishEntity> SALMON;
|
public static final EntityDefinition<AbstractFishEntity> SALMON;
|
||||||
public static final EntityDefinition<SheepEntity> SHEEP;
|
public static final EntityDefinition<SheepEntity> SHEEP;
|
||||||
public static final EntityDefinition<ShulkerEntity> SHULKER;
|
public static final EntityDefinition<ShulkerEntity> SHULKER;
|
||||||
|
public static final EntityDefinition<SnifferEntity> SNIFFER;
|
||||||
public static final EntityDefinition<ThrowableEntity> SHULKER_BULLET;
|
public static final EntityDefinition<ThrowableEntity> SHULKER_BULLET;
|
||||||
public static final EntityDefinition<MonsterEntity> SILVERFISH;
|
public static final EntityDefinition<MonsterEntity> SILVERFISH;
|
||||||
public static final EntityDefinition<SkeletonEntity> SKELETON;
|
public static final EntityDefinition<SkeletonEntity> SKELETON;
|
||||||
|
@ -235,7 +237,7 @@ public final class EntityDefinitions {
|
||||||
.type(EntityType.EXPERIENCE_ORB)
|
.type(EntityType.EXPERIENCE_ORB)
|
||||||
.identifier("minecraft:xp_orb")
|
.identifier("minecraft:xp_orb")
|
||||||
.build();
|
.build();
|
||||||
EVOKER_FANGS = EntityDefinition.builder(EvokerFangsEntity::new) // No entity metadata to listen to as of 1.18.1
|
EVOKER_FANGS = EntityDefinition.inherited(EvokerFangsEntity::new, entityBase)
|
||||||
.type(EntityType.EVOKER_FANGS)
|
.type(EntityType.EVOKER_FANGS)
|
||||||
.height(0.8f).width(0.5f)
|
.height(0.8f).width(0.5f)
|
||||||
.identifier("minecraft:evocation_fang")
|
.identifier("minecraft:evocation_fang")
|
||||||
|
@ -318,6 +320,15 @@ public final class EntityDefinitions {
|
||||||
.addTranslator(MetadataType.CHAT, TextDisplayEntity::setText)
|
.addTranslator(MetadataType.CHAT, TextDisplayEntity::setText)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
INTERACTION = EntityDefinition.inherited(InteractionEntity::new, entityBase)
|
||||||
|
.type(EntityType.INTERACTION)
|
||||||
|
.heightAndWidth(1.0f) // default size until server specifies otherwise
|
||||||
|
.identifier("minecraft:armor_stand")
|
||||||
|
.addTranslator(MetadataType.FLOAT, InteractionEntity::setWidth)
|
||||||
|
.addTranslator(MetadataType.FLOAT, InteractionEntity::setHeight)
|
||||||
|
.addTranslator(MetadataType.BOOLEAN, InteractionEntity::setResponse)
|
||||||
|
.build();
|
||||||
|
|
||||||
EntityDefinition<FireballEntity> fireballBase = EntityDefinition.inherited(FireballEntity::new, entityBase)
|
EntityDefinition<FireballEntity> fireballBase = EntityDefinition.inherited(FireballEntity::new, entityBase)
|
||||||
.addTranslator(null) // Item
|
.addTranslator(null) // Item
|
||||||
.build();
|
.build();
|
||||||
|
@ -842,6 +853,12 @@ public final class EntityDefinitions {
|
||||||
.height(1.3f).width(0.9f)
|
.height(1.3f).width(0.9f)
|
||||||
.addTranslator(MetadataType.BYTE, SheepEntity::setSheepFlags)
|
.addTranslator(MetadataType.BYTE, SheepEntity::setSheepFlags)
|
||||||
.build();
|
.build();
|
||||||
|
SNIFFER = EntityDefinition.inherited(SnifferEntity::new, ageableEntityBase)
|
||||||
|
.type(EntityType.SNIFFER)
|
||||||
|
.height(1.75f).width(1.9f)
|
||||||
|
.addTranslator(MetadataType.SNIFFER_STATE, SnifferEntity::setSnifferState)
|
||||||
|
.addTranslator(null) // Integer, drop seed at tick
|
||||||
|
.build();
|
||||||
STRIDER = EntityDefinition.inherited(StriderEntity::new, ageableEntityBase)
|
STRIDER = EntityDefinition.inherited(StriderEntity::new, ageableEntityBase)
|
||||||
.type(EntityType.STRIDER)
|
.type(EntityType.STRIDER)
|
||||||
.height(1.7f).width(0.9f)
|
.height(1.7f).width(0.9f)
|
||||||
|
@ -884,7 +901,6 @@ public final class EntityDefinitions {
|
||||||
.build();
|
.build();
|
||||||
CAMEL = EntityDefinition.inherited(CamelEntity::new, abstractHorseEntityBase)
|
CAMEL = EntityDefinition.inherited(CamelEntity::new, abstractHorseEntityBase)
|
||||||
.type(EntityType.CAMEL)
|
.type(EntityType.CAMEL)
|
||||||
.identifier("minecraft:llama") // todo 1.20
|
|
||||||
.height(2.375f).width(1.7f)
|
.height(2.375f).width(1.7f)
|
||||||
.addTranslator(MetadataType.BOOLEAN, CamelEntity::setDashing)
|
.addTranslator(MetadataType.BOOLEAN, CamelEntity::setDashing)
|
||||||
.addTranslator(null) // Last pose change tick
|
.addTranslator(null) // Last pose change tick
|
||||||
|
|
|
@ -125,8 +125,8 @@ public class BoatEntity extends Entity {
|
||||||
public void setVariant(IntEntityMetadata entityMetadata) {
|
public void setVariant(IntEntityMetadata entityMetadata) {
|
||||||
variant = entityMetadata.getPrimitiveValue();
|
variant = entityMetadata.getPrimitiveValue();
|
||||||
dirtyMetadata.put(EntityDataTypes.VARIANT, switch (variant) {
|
dirtyMetadata.put(EntityDataTypes.VARIANT, switch (variant) {
|
||||||
case 6, 7 -> variant - 1; // Dark oak and mangrove
|
case 6, 7, 8 -> variant - 1; // dark_oak, mangrove, bamboo
|
||||||
case 5, 8 -> 0; // TODO temp until 1.20. Cherry and bamboo
|
case 5 -> 8; // cherry
|
||||||
default -> variant;
|
default -> variant;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -493,9 +493,10 @@ public class Entity implements GeyserEntity {
|
||||||
* Update the mount offsets of each passenger on this vehicle
|
* Update the mount offsets of each passenger on this vehicle
|
||||||
*/
|
*/
|
||||||
protected void updatePassengerOffsets() {
|
protected void updatePassengerOffsets() {
|
||||||
for (Entity passenger : passengers) {
|
for (int i = 0; i < passengers.size(); i++) {
|
||||||
|
Entity passenger = passengers.get(i);
|
||||||
if (passenger != null) {
|
if (passenger != null) {
|
||||||
boolean rider = passengers.get(0) == this;
|
boolean rider = i == 0;
|
||||||
EntityUtils.updateMountOffset(passenger, this, rider, true, passengers.size() > 1);
|
EntityUtils.updateMountOffset(passenger, this, rider, true, passengers.size() > 1);
|
||||||
passenger.updateBedrockMetadata();
|
passenger.updateBedrockMetadata();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* 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.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 java.util.UUID;
|
||||||
|
|
||||||
|
public class InteractionEntity extends Entity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* true - java client hears swing sound when attacking, and arm swings when right-clicking
|
||||||
|
* false - java client hears no swing sound when attacking, and arm does not swing when right-clicking
|
||||||
|
*/
|
||||||
|
private boolean response = false;
|
||||||
|
|
||||||
|
public InteractionEntity(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initializeMetadata() {
|
||||||
|
super.initializeMetadata();
|
||||||
|
|
||||||
|
// hide the armor stand but keep the hitbox active
|
||||||
|
setFlag(EntityFlag.INVISIBLE, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InteractionResult interact(Hand hand) {
|
||||||
|
// these InteractionResults do mirror the java client
|
||||||
|
// but the bedrock client won't arm swing itself because of our armor stand workaround
|
||||||
|
if (response) {
|
||||||
|
AnimatePacket animatePacket = new AnimatePacket();
|
||||||
|
animatePacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
|
||||||
|
animatePacket.setAction(AnimatePacket.Action.SWING_ARM);
|
||||||
|
session.sendUpstreamPacket(animatePacket);
|
||||||
|
|
||||||
|
session.sendDownstreamPacket(new ServerboundSwingPacket(hand));
|
||||||
|
return InteractionResult.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return InteractionResult.CONSUME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWidth(FloatEntityMetadata width) {
|
||||||
|
setBoundingBoxWidth(width.getPrimitiveValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeight(FloatEntityMetadata height) {
|
||||||
|
setBoundingBoxHeight(height.getPrimitiveValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResponse(BooleanEntityMetadata response) {
|
||||||
|
this.response = response.getPrimitiveValue();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* 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.type.living.animal;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.SnifferState;
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
|
||||||
|
import org.cloudburstmc.math.vector.Vector3f;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||||
|
import org.geysermc.geyser.entity.EntityDefinition;
|
||||||
|
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||||
|
import org.geysermc.geyser.entity.type.Tickable;
|
||||||
|
import org.geysermc.geyser.item.type.Item;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class SnifferEntity extends AnimalEntity implements Tickable {
|
||||||
|
private static final float DIGGING_HEIGHT = EntityDefinitions.SNIFFER.height() - 0.4f;
|
||||||
|
private static final int DIG_END = 120;
|
||||||
|
private static final int DIG_START = DIG_END - 34;
|
||||||
|
|
||||||
|
private Pose pose = Pose.STANDING; // Needed to call setDimensions for DIGGING state
|
||||||
|
private int digTicks;
|
||||||
|
|
||||||
|
public SnifferEntity(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPose(Pose pose) {
|
||||||
|
this.pose = pose;
|
||||||
|
super.setPose(pose);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setDimensions(Pose pose) {
|
||||||
|
if (getFlag(EntityFlag.DIGGING)) {
|
||||||
|
setBoundingBoxHeight(DIGGING_HEIGHT);
|
||||||
|
setBoundingBoxWidth(definition.width());
|
||||||
|
} else {
|
||||||
|
super.setDimensions(pose);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canEat(Item item) {
|
||||||
|
return session.getTagCache().isSnifferFood(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSnifferState(ObjectEntityMetadata<SnifferState> entityMetadata) {
|
||||||
|
SnifferState snifferState = entityMetadata.getValue();
|
||||||
|
|
||||||
|
// SnifferState.SCENTING and SnifferState.IDLING not used in bedrock
|
||||||
|
// The bedrock client does the scenting animation and sound on its own
|
||||||
|
setFlag(EntityFlag.FEELING_HAPPY, snifferState == SnifferState.FEELING_HAPPY);
|
||||||
|
setFlag(EntityFlag.SCENTING, snifferState == SnifferState.SNIFFING); // SnifferState.SNIFFING -> EntityFlag.SCENTING
|
||||||
|
setFlag(EntityFlag.SEARCHING, snifferState == SnifferState.SEARCHING);
|
||||||
|
setFlag(EntityFlag.DIGGING, snifferState == SnifferState.DIGGING);
|
||||||
|
setFlag(EntityFlag.RISING, snifferState == SnifferState.RISING);
|
||||||
|
|
||||||
|
setDimensions(pose);
|
||||||
|
|
||||||
|
if (getFlag(EntityFlag.DIGGING)) {
|
||||||
|
digTicks = DIG_END;
|
||||||
|
} else {
|
||||||
|
// Handles situations where the DIGGING state is exited earlier than expected,
|
||||||
|
// such as hitting the sniffer or joining the game while it is digging
|
||||||
|
digTicks = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
// The java client renders digging particles on its own, but bedrock does not
|
||||||
|
if (digTicks > 0 && --digTicks < DIG_START && digTicks % 5 == 0) {
|
||||||
|
Vector3f rot = Vector3f.createDirectionDeg(0, -getYaw()).mul(2.25f);
|
||||||
|
Vector3f pos = getPosition().add(rot).up(0.2f).floor(); // Handle non-full blocks
|
||||||
|
int blockId = session.getBlockMappings().getBedrockBlockId(session.getGeyser().getWorldManager().getBlockAt(session, pos.toInt().down()));
|
||||||
|
|
||||||
|
LevelEventPacket levelEventPacket = new LevelEventPacket();
|
||||||
|
levelEventPacket.setType(LevelEvent.PARTICLE_DESTROY_BLOCK_NO_SOUND);
|
||||||
|
levelEventPacket.setPosition(pos);
|
||||||
|
levelEventPacket.setData(blockId);
|
||||||
|
session.sendUpstreamPacket(levelEventPacket);
|
||||||
|
|
||||||
|
if (digTicks % 10 == 0) {
|
||||||
|
LevelSoundEventPacket levelSoundEventPacket = new LevelSoundEventPacket();
|
||||||
|
levelSoundEventPacket.setSound(SoundEvent.HIT);
|
||||||
|
levelSoundEventPacket.setPosition(pos);
|
||||||
|
levelSoundEventPacket.setExtraData(blockId);
|
||||||
|
levelSoundEventPacket.setIdentifier(":");
|
||||||
|
session.sendUpstreamPacket(levelSoundEventPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,8 +27,13 @@ package org.geysermc.geyser.entity.type.living.animal.horse;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
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 org.cloudburstmc.math.vector.Vector3f;
|
import org.cloudburstmc.math.vector.Vector3f;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
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.data.inventory.ContainerType;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
|
||||||
import org.geysermc.geyser.entity.EntityDefinition;
|
import org.geysermc.geyser.entity.EntityDefinition;
|
||||||
import org.geysermc.geyser.item.Items;
|
import org.geysermc.geyser.item.Items;
|
||||||
import org.geysermc.geyser.item.type.Item;
|
import org.geysermc.geyser.item.type.Item;
|
||||||
|
@ -38,16 +43,50 @@ import java.util.UUID;
|
||||||
|
|
||||||
public class CamelEntity extends AbstractHorseEntity {
|
public class CamelEntity extends AbstractHorseEntity {
|
||||||
|
|
||||||
private static final float SITTING_HEIGHT_DIFFERENCE = 1.43F;
|
public static final float SITTING_HEIGHT_DIFFERENCE = 1.43F;
|
||||||
|
|
||||||
public CamelEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
public CamelEntity(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);
|
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||||
|
|
||||||
|
dirtyMetadata.put(EntityDataTypes.CONTAINER_TYPE, (byte) ContainerType.HORSE.getId());
|
||||||
|
|
||||||
|
// Always tamed, but not indicated in horse flags
|
||||||
|
setFlag(EntityFlag.TAMED, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setHorseFlags(ByteEntityMetadata entityMetadata) {
|
||||||
protected void initializeMetadata() {
|
byte xd = entityMetadata.getPrimitiveValue();
|
||||||
super.initializeMetadata();
|
boolean saddled = (xd & 0x04) == 0x04;
|
||||||
this.dirtyMetadata.put(EntityDataTypes.VARIANT, 2); // Closest llama colour to camel
|
setFlag(EntityFlag.SADDLED, saddled);
|
||||||
|
setFlag(EntityFlag.EATING, (xd & 0x10) == 0x10);
|
||||||
|
setFlag(EntityFlag.STANDING, (xd & 0x20) == 0x20);
|
||||||
|
|
||||||
|
// HorseFlags
|
||||||
|
// Bred 0x10
|
||||||
|
// Eating 0x20
|
||||||
|
// Open mouth 0x80
|
||||||
|
int horseFlags = 0x0;
|
||||||
|
horseFlags = (xd & 0x40) == 0x40 ? horseFlags | 0x80 : horseFlags;
|
||||||
|
|
||||||
|
// Only set eating when we don't have mouth open so a player interaction doesn't trigger the eating animation
|
||||||
|
horseFlags = (xd & 0x10) == 0x10 && (xd & 0x40) != 0x40 ? horseFlags | 0x20 : horseFlags;
|
||||||
|
|
||||||
|
// Set the flags into the horse flags
|
||||||
|
dirtyMetadata.put(EntityDataTypes.HORSE_FLAGS, horseFlags);
|
||||||
|
|
||||||
|
// Send the eating particles
|
||||||
|
// We use the wheat metadata as static particles since Java
|
||||||
|
// doesn't send over what item was used to feed the horse
|
||||||
|
if ((xd & 0x40) == 0x40) {
|
||||||
|
EntityEventPacket entityEventPacket = new EntityEventPacket();
|
||||||
|
entityEventPacket.setRuntimeEntityId(geyserId);
|
||||||
|
entityEventPacket.setType(EntityEventType.EATING_ITEM);
|
||||||
|
entityEventPacket.setData(session.getItemMappings().getStoredItems().wheat().getBedrockDefinition().getRuntimeId() << 16);
|
||||||
|
session.sendUpstreamPacket(entityEventPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shows the dash meter
|
||||||
|
setFlag(EntityFlag.CAN_DASH, saddled);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -55,10 +94,16 @@ public class CamelEntity extends AbstractHorseEntity {
|
||||||
return item == Items.CACTUS;
|
return item == Items.CACTUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPose(Pose pose) {
|
||||||
|
setFlag(EntityFlag.SITTING, pose == Pose.SITTING);
|
||||||
|
super.setPose(pose);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setDimensions(Pose pose) {
|
protected void setDimensions(Pose pose) {
|
||||||
if (pose == Pose.SITTING) {
|
if (pose == Pose.SITTING) {
|
||||||
setBoundingBoxWidth(definition.height() - SITTING_HEIGHT_DIFFERENCE);
|
setBoundingBoxHeight(definition.height() - SITTING_HEIGHT_DIFFERENCE);
|
||||||
setBoundingBoxWidth(definition.width());
|
setBoundingBoxWidth(definition.width());
|
||||||
} else {
|
} else {
|
||||||
super.setDimensions(pose);
|
super.setDimensions(pose);
|
||||||
|
|
|
@ -67,10 +67,6 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
private boolean isRidingInFront;
|
private boolean isRidingInFront;
|
||||||
/**
|
|
||||||
* Used for villager inventory emulation.
|
|
||||||
*/
|
|
||||||
private int fakeTradeXp;
|
|
||||||
|
|
||||||
public SessionPlayerEntity(GeyserSession session) {
|
public SessionPlayerEntity(GeyserSession session) {
|
||||||
super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null);
|
super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null);
|
||||||
|
@ -175,11 +171,6 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||||
this.isRidingInFront = position != null && position.getX() > 0;
|
this.isRidingInFront = position != null && position.getX() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addFakeTradeExperience(int tradeXp) {
|
|
||||||
fakeTradeXp += tradeXp;
|
|
||||||
dirtyMetadata.put(EntityDataTypes.TRADE_EXPERIENCE, fakeTradeXp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AttributeData createHealthAttribute() {
|
public AttributeData createHealthAttribute() {
|
||||||
// Max health must be divisible by two in bedrock
|
// Max health must be divisible by two in bedrock
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* 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.event.type;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.api.event.bedrock.SessionLoadResourcePacksEvent;
|
||||||
|
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksEvent {
|
||||||
|
|
||||||
|
private final Map<String, ResourcePack> packs;
|
||||||
|
|
||||||
|
public SessionLoadResourcePacksEventImpl(GeyserSession session, Map<String, ResourcePack> packMap) {
|
||||||
|
super(session);
|
||||||
|
this.packs = packMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull Map<String, ResourcePack> getPacks() {
|
||||||
|
return packs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull List<ResourcePack> resourcePacks() {
|
||||||
|
return List.copyOf(packs.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean register(@NonNull ResourcePack resourcePack) {
|
||||||
|
String packID = resourcePack.manifest().header().uuid().toString();
|
||||||
|
if (packs.containsValue(resourcePack) || packs.containsKey(packID)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
packs.put(resourcePack.manifest().header().uuid().toString(), resourcePack);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean unregister(@NonNull UUID uuid) {
|
||||||
|
return packs.remove(uuid.toString()) != null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.extension.event;
|
||||||
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.event.Event;
|
import org.geysermc.event.Event;
|
||||||
|
import org.geysermc.event.FireResult;
|
||||||
import org.geysermc.event.PostOrder;
|
import org.geysermc.event.PostOrder;
|
||||||
import org.geysermc.event.subscribe.Subscriber;
|
import org.geysermc.event.subscribe.Subscriber;
|
||||||
import org.geysermc.geyser.api.event.EventBus;
|
import org.geysermc.geyser.api.event.EventBus;
|
||||||
|
@ -47,10 +48,15 @@ public record GeyserExtensionEventBus(EventBus<EventRegistrar> eventBus, Extensi
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean fire(@NonNull Event event) {
|
public FireResult fire(@NonNull Event event) {
|
||||||
return eventBus.fire(event);
|
return eventBus.fire(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FireResult fireSilently(@NonNull Event event) {
|
||||||
|
return eventBus.fireSilently(event);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull <T extends Event> Set<? extends EventSubscriber<EventRegistrar, T>> subscribers(@NonNull Class<T> eventClass) {
|
public @NonNull <T extends Event> Set<? extends EventSubscriber<EventRegistrar, T>> subscribers(@NonNull Class<T> eventClass) {
|
||||||
return eventBus.subscribers(eventClass);
|
return eventBus.subscribers(eventClass);
|
||||||
|
|
|
@ -30,6 +30,7 @@ import com.github.steveice10.mc.protocol.data.game.inventory.VillagerTrade;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.inventory.ClientboundMerchantOffersPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.inventory.ClientboundMerchantOffersPacket;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||||
import org.geysermc.geyser.entity.type.Entity;
|
import org.geysermc.geyser.entity.type.Entity;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
|
||||||
|
@ -40,6 +41,8 @@ public class MerchantContainer extends Container {
|
||||||
private VillagerTrade[] villagerTrades;
|
private VillagerTrade[] villagerTrades;
|
||||||
@Getter @Setter
|
@Getter @Setter
|
||||||
private ClientboundMerchantOffersPacket pendingOffersPacket;
|
private ClientboundMerchantOffersPacket pendingOffersPacket;
|
||||||
|
@Getter @Setter
|
||||||
|
private int tradeExperience;
|
||||||
|
|
||||||
public MerchantContainer(String title, int id, int size, ContainerType containerType, PlayerInventory playerInventory) {
|
public MerchantContainer(String title, int id, int size, ContainerType containerType, PlayerInventory playerInventory) {
|
||||||
super(title, id, size, containerType, playerInventory);
|
super(title, id, size, containerType, playerInventory);
|
||||||
|
@ -49,9 +52,10 @@ public class MerchantContainer extends Container {
|
||||||
if (villagerTrades != null && slot >= 0 && slot < villagerTrades.length) {
|
if (villagerTrades != null && slot >= 0 && slot < villagerTrades.length) {
|
||||||
VillagerTrade trade = villagerTrades[slot];
|
VillagerTrade trade = villagerTrades[slot];
|
||||||
setItem(2, GeyserItemStack.from(trade.getOutput()), session);
|
setItem(2, GeyserItemStack.from(trade.getOutput()), session);
|
||||||
// TODO this logic doesn't add up
|
|
||||||
session.getPlayerEntity().addFakeTradeExperience(trade.getXp());
|
tradeExperience += trade.getXp();
|
||||||
session.getPlayerEntity().updateBedrockMetadata();
|
villager.getDirtyMetadata().put(EntityDataTypes.TRADE_EXPERIENCE, tradeExperience);
|
||||||
|
villager.updateBedrockMetadata();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ public class StoredItemMappings {
|
||||||
private final ItemMapping egg;
|
private final ItemMapping egg;
|
||||||
private final ItemMapping shield;
|
private final ItemMapping shield;
|
||||||
private final ItemMapping wheat;
|
private final ItemMapping wheat;
|
||||||
|
private final ItemMapping writableBook;
|
||||||
|
|
||||||
public StoredItemMappings(Map<Item, ItemMapping> itemMappings) {
|
public StoredItemMappings(Map<Item, ItemMapping> itemMappings) {
|
||||||
this.bamboo = load(itemMappings, Items.BAMBOO);
|
this.bamboo = load(itemMappings, Items.BAMBOO);
|
||||||
|
@ -64,6 +65,7 @@ public class StoredItemMappings {
|
||||||
this.egg = load(itemMappings, Items.EGG);
|
this.egg = load(itemMappings, Items.EGG);
|
||||||
this.shield = load(itemMappings, Items.SHIELD);
|
this.shield = load(itemMappings, Items.SHIELD);
|
||||||
this.wheat = load(itemMappings, Items.WHEAT);
|
this.wheat = load(itemMappings, Items.WHEAT);
|
||||||
|
this.writableBook = load(itemMappings, Items.WRITABLE_BOOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* 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.inventory.recipe;
|
||||||
|
|
||||||
|
import org.cloudburstmc.protocol.bedrock.data.TrimMaterial;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.data.TrimPattern;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemTagDescriptor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hardcoded recipe information about armor trims until further improvements can be made. This information was scraped
|
||||||
|
* from BDS 1.19.81 with a world with the next_major_update and sniffer features enabled, using ProxyPass.
|
||||||
|
*/
|
||||||
|
public class TrimRecipe {
|
||||||
|
|
||||||
|
// For TrimDataPacket, which BDS sends just before the CraftingDataPacket
|
||||||
|
public static final List<TrimPattern> PATTERNS;
|
||||||
|
public static final List<TrimMaterial> MATERIALS;
|
||||||
|
|
||||||
|
// For CraftingDataPacket
|
||||||
|
public static final String ID = "minecraft:smithing_armor_trim";
|
||||||
|
public static final ItemDescriptorWithCount BASE = tagDescriptor("minecraft:trimmable_armors");
|
||||||
|
public static final ItemDescriptorWithCount ADDITION = tagDescriptor("minecraft:trim_materials");
|
||||||
|
public static final ItemDescriptorWithCount TEMPLATE = tagDescriptor("minecraft:trim_templates");
|
||||||
|
|
||||||
|
static {
|
||||||
|
List<TrimPattern> patterns = new ArrayList<>(16);
|
||||||
|
patterns.add(new TrimPattern("minecraft:ward_armor_trim_smithing_template", "ward"));
|
||||||
|
patterns.add(new TrimPattern("minecraft:sentry_armor_trim_smithing_template", "sentry"));
|
||||||
|
patterns.add(new TrimPattern("minecraft:snout_armor_trim_smithing_template", "snout"));
|
||||||
|
patterns.add(new TrimPattern("minecraft:dune_armor_trim_smithing_template", "dune"));
|
||||||
|
patterns.add(new TrimPattern("minecraft:spire_armor_trim_smithing_template", "spire"));
|
||||||
|
patterns.add(new TrimPattern("minecraft:tide_armor_trim_smithing_template", "tide"));
|
||||||
|
patterns.add(new TrimPattern("minecraft:wild_armor_trim_smithing_template", "wild"));
|
||||||
|
patterns.add(new TrimPattern("minecraft:rib_armor_trim_smithing_template", "rib"));
|
||||||
|
patterns.add(new TrimPattern("minecraft:coast_armor_trim_smithing_template", "coast"));
|
||||||
|
patterns.add(new TrimPattern("minecraft:shaper_armor_trim_smithing_template", "shaper"));
|
||||||
|
patterns.add(new TrimPattern("minecraft:eye_armor_trim_smithing_template", "eye"));
|
||||||
|
patterns.add(new TrimPattern("minecraft:vex_armor_trim_smithing_template", "vex"));
|
||||||
|
patterns.add(new TrimPattern("minecraft:silence_armor_trim_smithing_template", "silence"));
|
||||||
|
patterns.add(new TrimPattern("minecraft:wayfinder_armor_trim_smithing_template", "wayfinder"));
|
||||||
|
patterns.add(new TrimPattern("minecraft:raiser_armor_trim_smithing_template", "raiser"));
|
||||||
|
patterns.add(new TrimPattern("minecraft:host_armor_trim_smithing_template", "host"));
|
||||||
|
PATTERNS = Collections.unmodifiableList(patterns);
|
||||||
|
|
||||||
|
List<TrimMaterial> materials = new ArrayList<>(10);
|
||||||
|
materials.add(new TrimMaterial("quartz", "§h", "minecraft:quartz"));
|
||||||
|
materials.add(new TrimMaterial("iron", "§i", "minecraft:iron_ingot"));
|
||||||
|
materials.add(new TrimMaterial("netherite", "§j", "minecraft:netherite_ingot"));
|
||||||
|
materials.add(new TrimMaterial("redstone", "§m", "minecraft:redstone"));
|
||||||
|
materials.add(new TrimMaterial("copper", "§n", "minecraft:copper_ingot"));
|
||||||
|
materials.add(new TrimMaterial("gold", "§p", "minecraft:gold_ingot"));
|
||||||
|
materials.add(new TrimMaterial("emerald", "§q", "minecraft:emerald"));
|
||||||
|
materials.add(new TrimMaterial("diamond", "§s", "minecraft:diamond"));
|
||||||
|
materials.add(new TrimMaterial("lapis", "§t", "minecraft:lapis_lazuli"));
|
||||||
|
materials.add(new TrimMaterial("amethyst", "§u", "minecraft:amethyst_shard"));
|
||||||
|
MATERIALS = Collections.unmodifiableList(materials);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TrimRecipe() {
|
||||||
|
//no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ItemDescriptorWithCount tagDescriptor(String tag) {
|
||||||
|
return new ItemDescriptorWithCount(new ItemTagDescriptor(tag), 1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -84,6 +84,7 @@ public final class Items {
|
||||||
public static final Item BEDROCK = register(new BlockItem("bedrock", builder()));
|
public static final Item BEDROCK = register(new BlockItem("bedrock", builder()));
|
||||||
public static final Item SAND = register(new BlockItem("sand", builder()));
|
public static final Item SAND = register(new BlockItem("sand", builder()));
|
||||||
public static final Item SUSPICIOUS_SAND = register(new BlockItem("suspicious_sand", builder()));
|
public static final Item SUSPICIOUS_SAND = register(new BlockItem("suspicious_sand", builder()));
|
||||||
|
public static final Item SUSPICIOUS_GRAVEL = register(new BlockItem("suspicious_gravel", builder()));
|
||||||
public static final Item RED_SAND = register(new BlockItem("red_sand", builder()));
|
public static final Item RED_SAND = register(new BlockItem("red_sand", builder()));
|
||||||
public static final Item GRAVEL = register(new BlockItem("gravel", builder()));
|
public static final Item GRAVEL = register(new BlockItem("gravel", builder()));
|
||||||
public static final Item COAL_ORE = register(new BlockItem("coal_ore", builder()));
|
public static final Item COAL_ORE = register(new BlockItem("coal_ore", builder()));
|
||||||
|
@ -247,6 +248,7 @@ public final class Items {
|
||||||
public static final Item LILY_OF_THE_VALLEY = register(new FlowerItem("lily_of_the_valley", builder()));
|
public static final Item LILY_OF_THE_VALLEY = register(new FlowerItem("lily_of_the_valley", builder()));
|
||||||
public static final Item WITHER_ROSE = register(new FlowerItem("wither_rose", builder()));
|
public static final Item WITHER_ROSE = register(new FlowerItem("wither_rose", builder()));
|
||||||
public static final Item TORCHFLOWER = register(new FlowerItem("torchflower", builder()));
|
public static final Item TORCHFLOWER = register(new FlowerItem("torchflower", builder()));
|
||||||
|
public static final Item PITCHER_PLANT = register(new BlockItem("pitcher_plant", builder()));
|
||||||
public static final Item SPORE_BLOSSOM = register(new BlockItem("spore_blossom", builder()));
|
public static final Item SPORE_BLOSSOM = register(new BlockItem("spore_blossom", builder()));
|
||||||
public static final Item BROWN_MUSHROOM = register(new BlockItem("brown_mushroom", builder()));
|
public static final Item BROWN_MUSHROOM = register(new BlockItem("brown_mushroom", builder()));
|
||||||
public static final Item RED_MUSHROOM = register(new BlockItem("red_mushroom", builder()));
|
public static final Item RED_MUSHROOM = register(new BlockItem("red_mushroom", builder()));
|
||||||
|
@ -302,7 +304,7 @@ public final class Items {
|
||||||
public static final Item BRICKS = register(new BlockItem("bricks", builder()));
|
public static final Item BRICKS = register(new BlockItem("bricks", builder()));
|
||||||
public static final Item BOOKSHELF = register(new BlockItem("bookshelf", builder()));
|
public static final Item BOOKSHELF = register(new BlockItem("bookshelf", builder()));
|
||||||
public static final Item CHISELED_BOOKSHELF = register(new BlockItem("chiseled_bookshelf", builder()));
|
public static final Item CHISELED_BOOKSHELF = register(new BlockItem("chiseled_bookshelf", builder()));
|
||||||
public static final Item DECORATED_POT = register(new BlockItem("decorated_pot", builder().stackSize(1)));
|
public static final Item DECORATED_POT = register(new DecoratedPotItem("decorated_pot", builder().stackSize(1)));
|
||||||
public static final Item MOSSY_COBBLESTONE = register(new BlockItem("mossy_cobblestone", builder()));
|
public static final Item MOSSY_COBBLESTONE = register(new BlockItem("mossy_cobblestone", builder()));
|
||||||
public static final Item OBSIDIAN = register(new BlockItem("obsidian", builder()));
|
public static final Item OBSIDIAN = register(new BlockItem("obsidian", builder()));
|
||||||
public static final Item TORCH = register(new BlockItem("torch", builder()));
|
public static final Item TORCH = register(new BlockItem("torch", builder()));
|
||||||
|
@ -313,7 +315,7 @@ public final class Items {
|
||||||
public static final Item PURPUR_PILLAR = register(new BlockItem("purpur_pillar", builder()));
|
public static final Item PURPUR_PILLAR = register(new BlockItem("purpur_pillar", builder()));
|
||||||
public static final Item PURPUR_STAIRS = register(new BlockItem("purpur_stairs", builder()));
|
public static final Item PURPUR_STAIRS = register(new BlockItem("purpur_stairs", builder()));
|
||||||
public static final Item SPAWNER = register(new BlockItem("spawner", builder()));
|
public static final Item SPAWNER = register(new BlockItem("spawner", builder()));
|
||||||
public static final Item CHEST = register(new BlockItem("chest", builder()));
|
public static final Item CHEST = register(new ChestItem("chest", builder()));
|
||||||
public static final Item CRAFTING_TABLE = register(new BlockItem("crafting_table", builder()));
|
public static final Item CRAFTING_TABLE = register(new BlockItem("crafting_table", builder()));
|
||||||
public static final Item FARMLAND = register(new BlockItem("farmland", builder()));
|
public static final Item FARMLAND = register(new BlockItem("farmland", builder()));
|
||||||
public static final Item FURNACE = register(new BlockItem("furnace", builder()));
|
public static final Item FURNACE = register(new BlockItem("furnace", builder()));
|
||||||
|
@ -395,7 +397,7 @@ public final class Items {
|
||||||
public static final Item END_STONE_BRICKS = register(new BlockItem("end_stone_bricks", builder()));
|
public static final Item END_STONE_BRICKS = register(new BlockItem("end_stone_bricks", builder()));
|
||||||
public static final Item DRAGON_EGG = register(new BlockItem("dragon_egg", builder()));
|
public static final Item DRAGON_EGG = register(new BlockItem("dragon_egg", builder()));
|
||||||
public static final Item SANDSTONE_STAIRS = register(new BlockItem("sandstone_stairs", builder()));
|
public static final Item SANDSTONE_STAIRS = register(new BlockItem("sandstone_stairs", builder()));
|
||||||
public static final Item ENDER_CHEST = register(new BlockItem("ender_chest", builder()));
|
public static final Item ENDER_CHEST = register(new ChestItem("ender_chest", builder()));
|
||||||
public static final Item EMERALD_BLOCK = register(new BlockItem("emerald_block", builder()));
|
public static final Item EMERALD_BLOCK = register(new BlockItem("emerald_block", builder()));
|
||||||
public static final Item OAK_STAIRS = register(new BlockItem("oak_stairs", builder()));
|
public static final Item OAK_STAIRS = register(new BlockItem("oak_stairs", builder()));
|
||||||
public static final Item SPRUCE_STAIRS = register(new BlockItem("spruce_stairs", builder()));
|
public static final Item SPRUCE_STAIRS = register(new BlockItem("spruce_stairs", builder()));
|
||||||
|
@ -602,6 +604,7 @@ public final class Items {
|
||||||
public static final Item RED_CONCRETE_POWDER = register(new BlockItem("red_concrete_powder", builder()));
|
public static final Item RED_CONCRETE_POWDER = register(new BlockItem("red_concrete_powder", builder()));
|
||||||
public static final Item BLACK_CONCRETE_POWDER = register(new BlockItem("black_concrete_powder", builder()));
|
public static final Item BLACK_CONCRETE_POWDER = register(new BlockItem("black_concrete_powder", builder()));
|
||||||
public static final Item TURTLE_EGG = register(new BlockItem("turtle_egg", builder()));
|
public static final Item TURTLE_EGG = register(new BlockItem("turtle_egg", builder()));
|
||||||
|
public static final Item SNIFFER_EGG = register(new BlockItem("sniffer_egg", builder()));
|
||||||
public static final Item DEAD_TUBE_CORAL_BLOCK = register(new BlockItem("dead_tube_coral_block", builder()));
|
public static final Item DEAD_TUBE_CORAL_BLOCK = register(new BlockItem("dead_tube_coral_block", builder()));
|
||||||
public static final Item DEAD_BRAIN_CORAL_BLOCK = register(new BlockItem("dead_brain_coral_block", builder()));
|
public static final Item DEAD_BRAIN_CORAL_BLOCK = register(new BlockItem("dead_brain_coral_block", builder()));
|
||||||
public static final Item DEAD_BUBBLE_CORAL_BLOCK = register(new BlockItem("dead_bubble_coral_block", builder()));
|
public static final Item DEAD_BUBBLE_CORAL_BLOCK = register(new BlockItem("dead_bubble_coral_block", builder()));
|
||||||
|
@ -689,8 +692,9 @@ public final class Items {
|
||||||
public static final Item LIGHTNING_ROD = register(new BlockItem("lightning_rod", builder()));
|
public static final Item LIGHTNING_ROD = register(new BlockItem("lightning_rod", builder()));
|
||||||
public static final Item DAYLIGHT_DETECTOR = register(new BlockItem("daylight_detector", builder()));
|
public static final Item DAYLIGHT_DETECTOR = register(new BlockItem("daylight_detector", builder()));
|
||||||
public static final Item SCULK_SENSOR = register(new BlockItem("sculk_sensor", builder()));
|
public static final Item SCULK_SENSOR = register(new BlockItem("sculk_sensor", builder()));
|
||||||
|
public static final Item CALIBRATED_SCULK_SENSOR = register(new BlockItem("calibrated_sculk_sensor", builder()));
|
||||||
public static final Item TRIPWIRE_HOOK = register(new BlockItem("tripwire_hook", builder()));
|
public static final Item TRIPWIRE_HOOK = register(new BlockItem("tripwire_hook", builder()));
|
||||||
public static final Item TRAPPED_CHEST = register(new BlockItem("trapped_chest", builder()));
|
public static final Item TRAPPED_CHEST = register(new ChestItem("trapped_chest", builder()));
|
||||||
public static final Item TNT = register(new BlockItem("tnt", builder()));
|
public static final Item TNT = register(new BlockItem("tnt", builder()));
|
||||||
public static final Item REDSTONE_LAMP = register(new BlockItem("redstone_lamp", builder()));
|
public static final Item REDSTONE_LAMP = register(new BlockItem("redstone_lamp", builder()));
|
||||||
public static final Item NOTE_BLOCK = register(new BlockItem("note_block", builder()));
|
public static final Item NOTE_BLOCK = register(new BlockItem("note_block", builder()));
|
||||||
|
@ -1080,8 +1084,8 @@ public final class Items {
|
||||||
public static final Item ZOMBIFIED_PIGLIN_SPAWN_EGG = register(new SpawnEggItem("zombified_piglin_spawn_egg", builder()));
|
public static final Item ZOMBIFIED_PIGLIN_SPAWN_EGG = register(new SpawnEggItem("zombified_piglin_spawn_egg", builder()));
|
||||||
public static final Item EXPERIENCE_BOTTLE = register(new Item("experience_bottle", builder()));
|
public static final Item EXPERIENCE_BOTTLE = register(new Item("experience_bottle", builder()));
|
||||||
public static final Item FIRE_CHARGE = register(new Item("fire_charge", builder()));
|
public static final Item FIRE_CHARGE = register(new Item("fire_charge", builder()));
|
||||||
public static final Item WRITABLE_BOOK = register(new ReadableBookItem("writable_book", builder().stackSize(1)));
|
public static final Item WRITABLE_BOOK = register(new WritableBookItem("writable_book", builder().stackSize(1)));
|
||||||
public static final Item WRITTEN_BOOK = register(new ReadableBookItem("written_book", builder().stackSize(16)));
|
public static final Item WRITTEN_BOOK = register(new WrittenBookItem("written_book", builder().stackSize(16)));
|
||||||
public static final Item ITEM_FRAME = register(new Item("item_frame", builder()));
|
public static final Item ITEM_FRAME = register(new Item("item_frame", builder()));
|
||||||
public static final Item GLOW_ITEM_FRAME = register(new Item("glow_item_frame", builder()));
|
public static final Item GLOW_ITEM_FRAME = register(new Item("glow_item_frame", builder()));
|
||||||
public static final Item FLOWER_POT = register(new BlockItem("flower_pot", builder()));
|
public static final Item FLOWER_POT = register(new BlockItem("flower_pot", builder()));
|
||||||
|
@ -1141,6 +1145,7 @@ public final class Items {
|
||||||
public static final Item CHORUS_FRUIT = register(new Item("chorus_fruit", builder()));
|
public static final Item CHORUS_FRUIT = register(new Item("chorus_fruit", builder()));
|
||||||
public static final Item POPPED_CHORUS_FRUIT = register(new Item("popped_chorus_fruit", builder()));
|
public static final Item POPPED_CHORUS_FRUIT = register(new Item("popped_chorus_fruit", builder()));
|
||||||
public static final Item TORCHFLOWER_SEEDS = register(new BlockItem("torchflower_seeds", builder()));
|
public static final Item TORCHFLOWER_SEEDS = register(new BlockItem("torchflower_seeds", builder()));
|
||||||
|
public static final Item PITCHER_POD = register(new BlockItem("pitcher_pod", builder()));
|
||||||
public static final Item BEETROOT = register(new Item("beetroot", builder()));
|
public static final Item BEETROOT = register(new Item("beetroot", builder()));
|
||||||
public static final Item BEETROOT_SEEDS = register(new BlockItem("beetroot_seeds", builder()));
|
public static final Item BEETROOT_SEEDS = register(new BlockItem("beetroot_seeds", builder()));
|
||||||
public static final Item BEETROOT_SOUP = register(new Item("beetroot_soup", builder().stackSize(1)));
|
public static final Item BEETROOT_SOUP = register(new Item("beetroot_soup", builder().stackSize(1)));
|
||||||
|
@ -1168,6 +1173,7 @@ public final class Items {
|
||||||
public static final Item MUSIC_DISC_11 = register(new Item("music_disc_11", builder().stackSize(1)));
|
public static final Item MUSIC_DISC_11 = register(new Item("music_disc_11", builder().stackSize(1)));
|
||||||
public static final Item MUSIC_DISC_WAIT = register(new Item("music_disc_wait", builder().stackSize(1)));
|
public static final Item MUSIC_DISC_WAIT = register(new Item("music_disc_wait", builder().stackSize(1)));
|
||||||
public static final Item MUSIC_DISC_OTHERSIDE = register(new Item("music_disc_otherside", builder().stackSize(1)));
|
public static final Item MUSIC_DISC_OTHERSIDE = register(new Item("music_disc_otherside", builder().stackSize(1)));
|
||||||
|
public static final Item MUSIC_DISC_RELIC = register(new Item("music_disc_relic", builder().stackSize(1)));
|
||||||
public static final Item MUSIC_DISC_5 = register(new Item("music_disc_5", builder().stackSize(1)));
|
public static final Item MUSIC_DISC_5 = register(new Item("music_disc_5", builder().stackSize(1)));
|
||||||
public static final Item MUSIC_DISC_PIGSTEP = register(new Item("music_disc_pigstep", builder().stackSize(1)));
|
public static final Item MUSIC_DISC_PIGSTEP = register(new Item("music_disc_pigstep", builder().stackSize(1)));
|
||||||
public static final Item DISC_FRAGMENT_5 = register(new Item("disc_fragment_5", builder()));
|
public static final Item DISC_FRAGMENT_5 = register(new Item("disc_fragment_5", builder()));
|
||||||
|
@ -1186,7 +1192,7 @@ public final class Items {
|
||||||
public static final Item PIGLIN_BANNER_PATTERN = register(new Item("piglin_banner_pattern", builder().stackSize(1)));
|
public static final Item PIGLIN_BANNER_PATTERN = register(new Item("piglin_banner_pattern", builder().stackSize(1)));
|
||||||
public static final Item GOAT_HORN = register(new GoatHornItem("goat_horn", builder().stackSize(1)));
|
public static final Item GOAT_HORN = register(new GoatHornItem("goat_horn", builder().stackSize(1)));
|
||||||
public static final Item COMPOSTER = register(new BlockItem("composter", builder()));
|
public static final Item COMPOSTER = register(new BlockItem("composter", builder()));
|
||||||
public static final Item BARREL = register(new BlockItem("barrel", builder()));
|
public static final Item BARREL = register(new ChestItem("barrel", builder()));
|
||||||
public static final Item SMOKER = register(new BlockItem("smoker", builder()));
|
public static final Item SMOKER = register(new BlockItem("smoker", builder()));
|
||||||
public static final Item BLAST_FURNACE = register(new BlockItem("blast_furnace", builder()));
|
public static final Item BLAST_FURNACE = register(new BlockItem("blast_furnace", builder()));
|
||||||
public static final Item CARTOGRAPHY_TABLE = register(new BlockItem("cartography_table", builder()));
|
public static final Item CARTOGRAPHY_TABLE = register(new BlockItem("cartography_table", builder()));
|
||||||
|
@ -1262,10 +1268,31 @@ public final class Items {
|
||||||
public static final Item SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("snout_armor_trim_smithing_template", builder()));
|
public static final Item SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("snout_armor_trim_smithing_template", builder()));
|
||||||
public static final Item RIB_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("rib_armor_trim_smithing_template", builder()));
|
public static final Item RIB_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("rib_armor_trim_smithing_template", builder()));
|
||||||
public static final Item SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("spire_armor_trim_smithing_template", builder()));
|
public static final Item SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("spire_armor_trim_smithing_template", builder()));
|
||||||
public static final Item POTTERY_SHARD_ARCHER = register(new Item("pottery_shard_archer", builder()));
|
public static final Item WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("wayfinder_armor_trim_smithing_template", builder()));
|
||||||
public static final Item POTTERY_SHARD_PRIZE = register(new Item("pottery_shard_prize", builder()));
|
public static final Item SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("shaper_armor_trim_smithing_template", builder()));
|
||||||
public static final Item POTTERY_SHARD_ARMS_UP = register(new Item("pottery_shard_arms_up", builder()));
|
public static final Item SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("silence_armor_trim_smithing_template", builder()));
|
||||||
public static final Item POTTERY_SHARD_SKULL = register(new Item("pottery_shard_skull", builder()));
|
public static final Item RAISER_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("raiser_armor_trim_smithing_template", builder()));
|
||||||
|
public static final Item HOST_ARMOR_TRIM_SMITHING_TEMPLATE = register(new Item("host_armor_trim_smithing_template", builder()));
|
||||||
|
public static final Item ANGLER_POTTERY_SHERD = register(new Item("angler_pottery_sherd", builder()));
|
||||||
|
public static final Item ARCHER_POTTERY_SHERD = register(new Item("archer_pottery_sherd", builder()));
|
||||||
|
public static final Item ARMS_UP_POTTERY_SHERD = register(new Item("arms_up_pottery_sherd", builder()));
|
||||||
|
public static final Item BLADE_POTTERY_SHERD = register(new Item("blade_pottery_sherd", builder()));
|
||||||
|
public static final Item BREWER_POTTERY_SHERD = register(new Item("brewer_pottery_sherd", builder()));
|
||||||
|
public static final Item BURN_POTTERY_SHERD = register(new Item("burn_pottery_sherd", builder()));
|
||||||
|
public static final Item DANGER_POTTERY_SHERD = register(new Item("danger_pottery_sherd", builder()));
|
||||||
|
public static final Item EXPLORER_POTTERY_SHERD = register(new Item("explorer_pottery_sherd", builder()));
|
||||||
|
public static final Item FRIEND_POTTERY_SHERD = register(new Item("friend_pottery_sherd", builder()));
|
||||||
|
public static final Item HEART_POTTERY_SHERD = register(new Item("heart_pottery_sherd", builder()));
|
||||||
|
public static final Item HEARTBREAK_POTTERY_SHERD = register(new Item("heartbreak_pottery_sherd", builder()));
|
||||||
|
public static final Item HOWL_POTTERY_SHERD = register(new Item("howl_pottery_sherd", builder()));
|
||||||
|
public static final Item MINER_POTTERY_SHERD = register(new Item("miner_pottery_sherd", builder()));
|
||||||
|
public static final Item MOURNER_POTTERY_SHERD = register(new Item("mourner_pottery_sherd", builder()));
|
||||||
|
public static final Item PLENTY_POTTERY_SHERD = register(new Item("plenty_pottery_sherd", builder()));
|
||||||
|
public static final Item PRIZE_POTTERY_SHERD = register(new Item("prize_pottery_sherd", builder()));
|
||||||
|
public static final Item SHEAF_POTTERY_SHERD = register(new Item("sheaf_pottery_sherd", builder()));
|
||||||
|
public static final Item SHELTER_POTTERY_SHERD = register(new Item("shelter_pottery_sherd", builder()));
|
||||||
|
public static final Item SKULL_POTTERY_SHERD = register(new Item("skull_pottery_sherd", builder()));
|
||||||
|
public static final Item SNORT_POTTERY_SHERD = register(new Item("snort_pottery_sherd", builder()));
|
||||||
|
|
||||||
private static <T extends Item> T register(T item) {
|
private static <T extends Item> T register(T item) {
|
||||||
return register(item, Registries.JAVA_ITEMS.get().size());
|
return register(item, Registries.JAVA_ITEMS.get().size());
|
||||||
|
|
|
@ -25,7 +25,12 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.item.type;
|
package org.geysermc.geyser.item.type;
|
||||||
|
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.geyser.item.ArmorMaterial;
|
import org.geysermc.geyser.item.ArmorMaterial;
|
||||||
|
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
|
||||||
public class ArmorItem extends Item {
|
public class ArmorItem extends Item {
|
||||||
private final ArmorMaterial material;
|
private final ArmorMaterial material;
|
||||||
|
@ -35,8 +40,42 @@ public class ArmorItem extends Item {
|
||||||
this.material = material;
|
this.material = material;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translateNbtToBedrock(@NonNull GeyserSession session, @NonNull CompoundTag tag) {
|
||||||
|
super.translateNbtToBedrock(session, tag);
|
||||||
|
|
||||||
|
if (tag.get("Trim") instanceof CompoundTag trim) {
|
||||||
|
StringTag material = trim.remove("material");
|
||||||
|
StringTag pattern = trim.remove("pattern");
|
||||||
|
// bedrock has an uppercase first letter key, and the value is not namespaced
|
||||||
|
trim.put(new StringTag("Material", stripNamespace(material.getValue())));
|
||||||
|
trim.put(new StringTag("Pattern", stripNamespace(pattern.getValue())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translateNbtToJava(@NonNull CompoundTag tag, @NonNull ItemMapping mapping) {
|
||||||
|
super.translateNbtToJava(tag, mapping);
|
||||||
|
|
||||||
|
if (tag.get("Trim") instanceof CompoundTag trim) {
|
||||||
|
StringTag material = trim.remove("Material");
|
||||||
|
StringTag pattern = trim.remove("Pattern");
|
||||||
|
// java has a lowercase key, and namespaced value
|
||||||
|
trim.put(new StringTag("material", "minecraft:" + material.getValue()));
|
||||||
|
trim.put(new StringTag("pattern", "minecraft:" + pattern.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValidRepairItem(Item other) {
|
public boolean isValidRepairItem(Item other) {
|
||||||
return material.getRepairIngredient() == other;
|
return material.getRepairIngredient() == other;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String stripNamespace(String identifier) {
|
||||||
|
int i = identifier.indexOf(':');
|
||||||
|
if (i >= 0) {
|
||||||
|
return identifier.substring(i + 1);
|
||||||
|
}
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -23,42 +23,31 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.translator.inventory.item;
|
package org.geysermc.geyser.item.type;
|
||||||
|
|
||||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
import org.geysermc.geyser.item.type.Item;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
|
||||||
public abstract class NbtItemStackTranslator {
|
public class ChestItem extends BlockItem {
|
||||||
|
|
||||||
/**
|
|
||||||
* Translate the item NBT to Bedrock
|
|
||||||
* @param session the client's current session
|
|
||||||
* @param itemTag the item's CompoundTag (cloned from Geyser's cached copy)
|
|
||||||
* @param mapping Geyser's item mapping
|
|
||||||
*/
|
|
||||||
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) {
|
|
||||||
|
|
||||||
|
public ChestItem(String javaIdentifier, Builder builder) {
|
||||||
|
super(javaIdentifier, builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Translate the item NBT to Java.
|
public void translateNbtToBedrock(@NonNull GeyserSession session, @NonNull CompoundTag tag) {
|
||||||
* @param itemTag the item's CompoundTag
|
super.translateNbtToBedrock(session, tag);
|
||||||
* @param mapping Geyser's item mapping
|
|
||||||
*/
|
|
||||||
public void translateToJava(CompoundTag itemTag, ItemMapping mapping) {
|
|
||||||
|
|
||||||
|
// Strip the BlockEntityTag from the chests contents
|
||||||
|
// sent to the client. The client does not parse this
|
||||||
|
// or use it for anything, as this tag is fully
|
||||||
|
// server-side, so we remove it to reduce bandwidth and
|
||||||
|
// solve potential issues with very large tags.
|
||||||
|
|
||||||
|
// There was a problem in the past where this would strip
|
||||||
|
// NBT data in creative mode, however with the new server
|
||||||
|
// authoritative inventories, this is no longer a concern.
|
||||||
|
tag.remove("BlockEntityTag");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets whether this nbt translator takes in this item.
|
|
||||||
*
|
|
||||||
* @param item Geyser's item mapping
|
|
||||||
* @return if the item should be processed under this class
|
|
||||||
*/
|
|
||||||
public boolean acceptItem(Item item) {
|
|
||||||
return true;
|
|
||||||
} // TODO
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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.item.type;
|
||||||
|
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
|
||||||
|
public class DecoratedPotItem extends BlockItem {
|
||||||
|
|
||||||
|
public DecoratedPotItem(String javaIdentifier, Builder builder) {
|
||||||
|
super(javaIdentifier, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translateNbtToBedrock(@NonNull GeyserSession session, @NonNull CompoundTag tag) {
|
||||||
|
super.translateNbtToBedrock(session, tag);
|
||||||
|
|
||||||
|
if (tag.remove("BlockEntityTag") instanceof CompoundTag blockEntityTag) {
|
||||||
|
if (blockEntityTag.remove("sherds") instanceof ListTag sherds) {
|
||||||
|
// bedrock wants it on the root level
|
||||||
|
tag.put(sherds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -144,7 +144,7 @@ public class Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes NBT from Java Edition and converts any value that Bedrock parses differently. <br>
|
* Takes NBT from Bedrock Edition and converts any value that Java parses differently. <br>
|
||||||
* Do note that this method is, these days, only called in three places (as of 2023/~1.19):
|
* Do note that this method is, these days, only called in three places (as of 2023/~1.19):
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Extra recipe loading</li>
|
* <li>Extra recipe loading</li>
|
||||||
|
|
|
@ -25,13 +25,40 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.item.type;
|
package org.geysermc.geyser.item.type;
|
||||||
|
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.IntTag;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.geyser.item.components.ToolTier;
|
import org.geysermc.geyser.item.components.ToolTier;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
|
||||||
public class ShieldItem extends Item {
|
public class ShieldItem extends Item {
|
||||||
public ShieldItem(String javaIdentifier, Builder builder) {
|
public ShieldItem(String javaIdentifier, Builder builder) {
|
||||||
super(javaIdentifier, builder);
|
super(javaIdentifier, builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translateNbtToBedrock(@NonNull GeyserSession session, @NonNull CompoundTag tag) {
|
||||||
|
super.translateNbtToBedrock(session, tag);
|
||||||
|
|
||||||
|
if (tag.remove("BlockEntityTag") instanceof CompoundTag blockEntityTag) {
|
||||||
|
if (blockEntityTag.get("Patterns") instanceof ListTag patterns) {
|
||||||
|
for (Tag pattern : patterns) {
|
||||||
|
if (((CompoundTag) pattern).get("Color") instanceof IntTag color) {
|
||||||
|
color.setValue(15 - color.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Bedrock looks for patterns at the root
|
||||||
|
tag.put(patterns);
|
||||||
|
}
|
||||||
|
if (blockEntityTag.get("Base") instanceof IntTag base) {
|
||||||
|
base.setValue(15 - base.getValue());
|
||||||
|
tag.put(base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValidRepairItem(Item other) {
|
public boolean isValidRepairItem(Item other) {
|
||||||
// Java Edition 1.19.3 checks the tag, but TODO check to see if we want it or are simulating what Bedrock is doing
|
// Java Edition 1.19.3 checks the tag, but TODO check to see if we want it or are simulating what Bedrock is doing
|
||||||
|
|
|
@ -77,9 +77,17 @@ public class ShulkerBoxItem extends BlockItem {
|
||||||
itemsList.add(boxItemTag);
|
itemsList.add(boxItemTag);
|
||||||
}
|
}
|
||||||
tag.put(itemsList);
|
tag.put(itemsList);
|
||||||
// Don't actually bother with removing the block entity tag. Too risky to translate
|
|
||||||
// if the user is on creative and messing with a shulker box
|
// Strip the BlockEntityTag from the chests contents
|
||||||
//itemTag.remove("BlockEntityTag");
|
// sent to the client. The client does not parse this
|
||||||
|
// or use it for anything, as this tag is fully
|
||||||
|
// server-side, so we remove it to reduce bandwidth and
|
||||||
|
// solve potential issues with very large tags.
|
||||||
|
|
||||||
|
// There was a problem in the past where this would strip
|
||||||
|
// NBT data in creative mode, however with the new server
|
||||||
|
// authoritative inventories, this is no longer a concern.
|
||||||
|
tag.remove("BlockEntityTag");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -37,11 +37,8 @@ import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
public class WritableBookItem extends Item {
|
||||||
* Encapsulates written books and writable books. Customly named class to share common code.
|
public WritableBookItem(String javaIdentifier, Builder builder) {
|
||||||
*/
|
|
||||||
public class ReadableBookItem extends Item {
|
|
||||||
public ReadableBookItem(String javaIdentifier, Builder builder) {
|
|
||||||
super(javaIdentifier, builder);
|
super(javaIdentifier, builder);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.item.type;
|
||||||
|
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class WrittenBookItem extends WritableBookItem {
|
||||||
|
public static final int MAXIMUM_PAGE_EDIT_LENGTH = 1024;
|
||||||
|
public static final int MAXIMUM_PAGE_LENGTH = 32768;
|
||||||
|
public static final int MAXIMUM_PAGE_COUNT = 100; // Java edition limit. Bedrock edition has a limit of 50 pages.
|
||||||
|
public static final int MAXIMUM_TITLE_LENGTH = 16;
|
||||||
|
|
||||||
|
public WrittenBookItem(String javaIdentifier, Builder builder) {
|
||||||
|
super(javaIdentifier, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translateNbtToBedrock(@NonNull GeyserSession session, @NonNull CompoundTag tag) {
|
||||||
|
boolean isValid = isValidWrittenBook(tag);
|
||||||
|
if (!isValid) {
|
||||||
|
tag.remove("pages");
|
||||||
|
}
|
||||||
|
|
||||||
|
super.translateNbtToBedrock(session, tag);
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
CompoundTag invalidTagPage = new CompoundTag("");
|
||||||
|
invalidTagPage.put(new StringTag("photoname", ""));
|
||||||
|
invalidTagPage.put(new StringTag(
|
||||||
|
"text",
|
||||||
|
MessageTranslator.convertMessage(
|
||||||
|
Component.translatable("book.invalid.tag", NamedTextColor.DARK_RED),
|
||||||
|
session.locale()
|
||||||
|
)
|
||||||
|
));
|
||||||
|
tag.put(new ListTag("pages", List.of(invalidTagPage)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidWrittenBook(CompoundTag tag) {
|
||||||
|
if (!(tag.get("title") instanceof StringTag title)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (title.getValue().length() > (MAXIMUM_TITLE_LENGTH * 2)) {
|
||||||
|
// Java rejects books with titles more than 2x the maximum length allowed in the input box
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(tag.get("author") instanceof StringTag)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(tag.get("pages") instanceof ListTag pages)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (Tag pageTag : pages) {
|
||||||
|
if (pageTag instanceof StringTag page) {
|
||||||
|
if (page.getValue().length() > MAXIMUM_PAGE_LENGTH) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,6 +47,7 @@ public final class BlockStateValues {
|
||||||
private static final IntSet ALL_CAULDRONS = new IntOpenHashSet();
|
private static final IntSet ALL_CAULDRONS = new IntOpenHashSet();
|
||||||
private static final Int2IntMap BANNER_COLORS = new FixedInt2IntMap();
|
private static final Int2IntMap BANNER_COLORS = new FixedInt2IntMap();
|
||||||
private static final Int2ByteMap BED_COLORS = new FixedInt2ByteMap();
|
private static final Int2ByteMap BED_COLORS = new FixedInt2ByteMap();
|
||||||
|
private static final Int2IntMap BRUSH_PROGRESS = new Int2IntOpenHashMap();
|
||||||
private static final Int2ByteMap COMMAND_BLOCK_VALUES = new Int2ByteOpenHashMap();
|
private static final Int2ByteMap COMMAND_BLOCK_VALUES = new Int2ByteOpenHashMap();
|
||||||
private static final Int2ObjectMap<DoubleChestValue> DOUBLE_CHEST_VALUES = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<DoubleChestValue> DOUBLE_CHEST_VALUES = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<String> FLOWER_POT_VALUES = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<String> FLOWER_POT_VALUES = new Int2ObjectOpenHashMap<>();
|
||||||
|
@ -98,6 +99,15 @@ public final class BlockStateValues {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JsonNode bedrockStates = blockData.get("bedrock_states");
|
||||||
|
if (bedrockStates != null) {
|
||||||
|
JsonNode brushedProgress = bedrockStates.get("brushed_progress");
|
||||||
|
if (brushedProgress != null) {
|
||||||
|
BRUSH_PROGRESS.put(javaBlockState, brushedProgress.intValue());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (javaId.contains("command_block")) {
|
if (javaId.contains("command_block")) {
|
||||||
COMMAND_BLOCK_VALUES.put(javaBlockState, javaId.contains("conditional=true") ? (byte) 1 : (byte) 0);
|
COMMAND_BLOCK_VALUES.put(javaBlockState, javaId.contains("conditional=true") ? (byte) 1 : (byte) 0);
|
||||||
return;
|
return;
|
||||||
|
@ -225,6 +235,17 @@ public final class BlockStateValues {
|
||||||
return BED_COLORS.getOrDefault(state, (byte) -1);
|
return BED_COLORS.getOrDefault(state, (byte) -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The brush progress of suspicious sand/gravel is not sent by the java server when it updates the block entity.
|
||||||
|
* Although brush progress is part of the bedrock block state, it must be included in the block entity update.
|
||||||
|
*
|
||||||
|
* @param state BlockState of the block
|
||||||
|
* @return brush progress or 0 if the lookup failed
|
||||||
|
*/
|
||||||
|
public static int getBrushProgress(int state) {
|
||||||
|
return BRUSH_PROGRESS.getOrDefault(state, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Non-water cauldrons (since Bedrock 1.18.30) must have a block entity packet sent on chunk load to fix rendering issues.
|
* Non-water cauldrons (since Bedrock 1.18.30) must have a block entity packet sent on chunk load to fix rendering issues.
|
||||||
*
|
*
|
||||||
|
|
|
@ -28,12 +28,8 @@ package org.geysermc.geyser.network;
|
||||||
import com.github.steveice10.mc.protocol.codec.MinecraftCodec;
|
import com.github.steveice10.mc.protocol.codec.MinecraftCodec;
|
||||||
import com.github.steveice10.mc.protocol.codec.PacketCodec;
|
import com.github.steveice10.mc.protocol.codec.PacketCodec;
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v557.Bedrock_v557;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v560.Bedrock_v560;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v567.Bedrock_v567;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v568.Bedrock_v568;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v575.Bedrock_v575;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v582.Bedrock_v582;
|
import org.cloudburstmc.protocol.bedrock.codec.v582.Bedrock_v582;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.codec.v589.Bedrock_v589;
|
||||||
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec;
|
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
|
||||||
|
@ -49,9 +45,8 @@ public final class GameProtocol {
|
||||||
* Default Bedrock codec that should act as a fallback. Should represent the latest available
|
* Default Bedrock codec that should act as a fallback. Should represent the latest available
|
||||||
* release of the game that Geyser supports.
|
* release of the game that Geyser supports.
|
||||||
*/
|
*/
|
||||||
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = Bedrock_v582.CODEC.toBuilder()
|
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = Bedrock_v589.CODEC;
|
||||||
.minecraftVersion("1.19.81")
|
|
||||||
.build();
|
|
||||||
/**
|
/**
|
||||||
* A list of all supported Bedrock versions that can join Geyser
|
* A list of all supported Bedrock versions that can join Geyser
|
||||||
*/
|
*/
|
||||||
|
@ -64,20 +59,10 @@ public final class GameProtocol {
|
||||||
private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC;
|
private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v557.CODEC.toBuilder()
|
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v582.CODEC.toBuilder()
|
||||||
.minecraftVersion("1.19.40/1.19.41")
|
|
||||||
.build());
|
|
||||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v560.CODEC.toBuilder()
|
|
||||||
.minecraftVersion("1.19.50/1.19.51")
|
|
||||||
.build());
|
|
||||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v567.CODEC);
|
|
||||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v568.CODEC);
|
|
||||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v575.CODEC.toBuilder()
|
|
||||||
.minecraftVersion("1.19.70/1.19.71/1.19.73")
|
|
||||||
.build());
|
|
||||||
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder()
|
|
||||||
.minecraftVersion("1.19.80/1.19.81")
|
.minecraftVersion("1.19.80/1.19.81")
|
||||||
.build());
|
.build());
|
||||||
|
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,16 +81,8 @@ public final class GameProtocol {
|
||||||
|
|
||||||
/* Bedrock convenience methods to gatekeep features and easily remove the check on version removal */
|
/* Bedrock convenience methods to gatekeep features and easily remove the check on version removal */
|
||||||
|
|
||||||
public static boolean supports1_19_50(GeyserSession session) {
|
public static boolean isPre1_20(GeyserSession session) {
|
||||||
return session.getUpstream().getProtocolVersion() >= Bedrock_v560.CODEC.getProtocolVersion();
|
return session.getUpstream().getProtocolVersion() < Bedrock_v589.CODEC.getProtocolVersion();
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean supports1_19_60(GeyserSession session) {
|
|
||||||
return session.getUpstream().getProtocolVersion() >= Bedrock_v567.CODEC.getProtocolVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean supports1_19_80(GeyserSession session) {
|
|
||||||
return session.getUpstream().getProtocolVersion() >= Bedrock_v582.CODEC.getProtocolVersion();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -47,11 +47,6 @@ public class GeyserServerInitializer extends BedrockServerInitializer {
|
||||||
this.geyser = geyser;
|
this.geyser = geyser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void postInitChannel(Channel channel) throws Exception {
|
|
||||||
super.postInitChannel(channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initSession(@Nonnull BedrockServerSession bedrockServerSession) {
|
public void initSession(@Nonnull BedrockServerSession bedrockServerSession) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -28,8 +28,6 @@ package org.geysermc.geyser.network;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import org.cloudburstmc.protocol.bedrock.BedrockDisconnectReasons;
|
import org.cloudburstmc.protocol.bedrock.BedrockDisconnectReasons;
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v567.Bedrock_v567;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v568.Bedrock_v568;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.data.ExperimentData;
|
import org.cloudburstmc.protocol.bedrock.data.ExperimentData;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.PacketCompressionAlgorithm;
|
import org.cloudburstmc.protocol.bedrock.data.PacketCompressionAlgorithm;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.ResourcePackType;
|
import org.cloudburstmc.protocol.bedrock.data.ResourcePackType;
|
||||||
|
@ -51,9 +49,12 @@ import org.cloudburstmc.protocol.common.PacketSignal;
|
||||||
import org.geysermc.geyser.Constants;
|
import org.geysermc.geyser.Constants;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.network.AuthType;
|
import org.geysermc.geyser.api.network.AuthType;
|
||||||
|
import org.geysermc.geyser.api.pack.PackCodec;
|
||||||
|
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||||
|
import org.geysermc.geyser.api.pack.ResourcePackManifest;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||||
import org.geysermc.geyser.pack.ResourcePack;
|
import org.geysermc.geyser.event.type.SessionLoadResourcePacksEventImpl;
|
||||||
import org.geysermc.geyser.pack.ResourcePackManifest;
|
import org.geysermc.geyser.pack.GeyserResourcePack;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.registry.Registries;
|
import org.geysermc.geyser.registry.Registries;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
@ -63,15 +64,20 @@ import org.geysermc.geyser.util.LoginEncryptionUtils;
|
||||||
import org.geysermc.geyser.util.MathUtils;
|
import org.geysermc.geyser.util.MathUtils;
|
||||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.SeekableByteChannel;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.OptionalInt;
|
import java.util.OptionalInt;
|
||||||
|
|
||||||
public class UpstreamPacketHandler extends LoggingPacketHandler {
|
public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
|
|
||||||
private Deque<String> packsToSent = new ArrayDeque<>();
|
private boolean networkSettingsRequested = false;
|
||||||
|
private final Deque<String> packsToSent = new ArrayDeque<>();
|
||||||
|
|
||||||
|
private SessionLoadResourcePacksEventImpl resourcePackLoadEvent;
|
||||||
|
|
||||||
public UpstreamPacketHandler(GeyserImpl geyser, GeyserSession session) {
|
public UpstreamPacketHandler(GeyserImpl geyser, GeyserSession session) {
|
||||||
super(geyser, session);
|
super(geyser, session);
|
||||||
|
@ -87,8 +93,6 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
return translateAndDefault(packet);
|
return translateAndDefault(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean newProtocol = false; // TEMPORARY
|
|
||||||
|
|
||||||
private boolean setCorrectCodec(int protocolVersion) {
|
private boolean setCorrectCodec(int protocolVersion) {
|
||||||
BedrockCodec packetCodec = GameProtocol.getBedrockCodec(protocolVersion);
|
BedrockCodec packetCodec = GameProtocol.getBedrockCodec(protocolVersion);
|
||||||
if (packetCodec == null) {
|
if (packetCodec == null) {
|
||||||
|
@ -127,9 +131,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PacketSignal handle(RequestNetworkSettingsPacket packet) {
|
public PacketSignal handle(RequestNetworkSettingsPacket packet) {
|
||||||
if (setCorrectCodec(packet.getProtocolVersion())) {
|
if (!setCorrectCodec(packet.getProtocolVersion())) {
|
||||||
newProtocol = true;
|
|
||||||
} else {
|
|
||||||
return PacketSignal.HANDLED;
|
return PacketSignal.HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,6 +145,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
|
|
||||||
session.getUpstream().getSession().setCompression(algorithm);
|
session.getUpstream().getSession().setCompression(algorithm);
|
||||||
session.getUpstream().getSession().setCompressionLevel(this.geyser.getConfig().getBedrock().getCompressionLevel());
|
session.getUpstream().getSession().setCompressionLevel(this.geyser.getConfig().getBedrock().getCompressionLevel());
|
||||||
|
networkSettingsRequested = true;
|
||||||
return PacketSignal.HANDLED;
|
return PacketSignal.HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,12 +157,9 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
return PacketSignal.HANDLED;
|
return PacketSignal.HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// session.getUpstream().getSession().getCodec() == null
|
if (!networkSettingsRequested) {
|
||||||
|
session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.client", GameProtocol.getAllSupportedBedrockVersions()));
|
||||||
if (!newProtocol) {
|
return PacketSignal.HANDLED;
|
||||||
if (!setCorrectCodec(loginPacket.getProtocolVersion())) { // REMOVE WHEN ONLY 1.19.30 IS SUPPORTED OR 1.20
|
|
||||||
return PacketSignal.HANDLED;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the block translation based off of version
|
// Set the block translation based off of version
|
||||||
|
@ -173,23 +173,22 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
return PacketSignal.HANDLED;
|
return PacketSignal.HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hack for... whatever this is
|
|
||||||
if (loginPacket.getProtocolVersion() == Bedrock_v567.CODEC.getProtocolVersion() && !session.getClientData().getGameVersion().equals("1.19.60")) {
|
|
||||||
session.getUpstream().getSession().setCodec(Bedrock_v568.CODEC);
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayStatusPacket playStatus = new PlayStatusPacket();
|
PlayStatusPacket playStatus = new PlayStatusPacket();
|
||||||
playStatus.setStatus(PlayStatusPacket.Status.LOGIN_SUCCESS);
|
playStatus.setStatus(PlayStatusPacket.Status.LOGIN_SUCCESS);
|
||||||
session.sendUpstreamPacket(playStatus);
|
session.sendUpstreamPacket(playStatus);
|
||||||
|
|
||||||
geyser.getSessionManager().addPendingSession(session);
|
geyser.getSessionManager().addPendingSession(session);
|
||||||
|
|
||||||
|
this.resourcePackLoadEvent = new SessionLoadResourcePacksEventImpl(session, new HashMap<>(Registries.RESOURCE_PACKS.get()));
|
||||||
|
this.geyser.eventBus().fire(this.resourcePackLoadEvent);
|
||||||
|
|
||||||
ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket();
|
ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket();
|
||||||
for(ResourcePack resourcePack : ResourcePack.PACKS.values()) {
|
for (ResourcePack pack : this.resourcePackLoadEvent.resourcePacks()) {
|
||||||
ResourcePackManifest.Header header = resourcePack.getManifest().getHeader();
|
PackCodec codec = pack.codec();
|
||||||
|
ResourcePackManifest.Header header = pack.manifest().header();
|
||||||
resourcePacksInfo.getResourcePackInfos().add(new ResourcePacksInfoPacket.Entry(
|
resourcePacksInfo.getResourcePackInfos().add(new ResourcePacksInfoPacket.Entry(
|
||||||
header.getUuid().toString(), header.getVersionString(), resourcePack.getFile().length(),
|
header.uuid().toString(), header.version().toString(), codec.size(), pack.contentKey(),
|
||||||
resourcePack.getContentKey(), "", header.getUuid().toString(), false, false));
|
"", header.uuid().toString(), false, false));
|
||||||
}
|
}
|
||||||
resourcePacksInfo.setForcedToAccept(GeyserImpl.getInstance().getConfig().isForceResourcePacks());
|
resourcePacksInfo.setForcedToAccept(GeyserImpl.getInstance().getConfig().isForceResourcePacks());
|
||||||
session.sendUpstreamPacket(resourcePacksInfo);
|
session.sendUpstreamPacket(resourcePacksInfo);
|
||||||
|
@ -222,9 +221,9 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
stackPacket.setForcedToAccept(false); // Leaving this as false allows the player to choose to download or not
|
stackPacket.setForcedToAccept(false); // Leaving this as false allows the player to choose to download or not
|
||||||
stackPacket.setGameVersion(session.getClientData().getGameVersion());
|
stackPacket.setGameVersion(session.getClientData().getGameVersion());
|
||||||
|
|
||||||
for (ResourcePack pack : ResourcePack.PACKS.values()) {
|
for (ResourcePack pack : this.resourcePackLoadEvent.resourcePacks()) {
|
||||||
ResourcePackManifest.Header header = pack.getManifest().getHeader();
|
ResourcePackManifest.Header header = pack.manifest().header();
|
||||||
stackPacket.getResourcePacks().add(new ResourcePackStackPacket.Entry(header.getUuid().toString(), header.getVersionString(), ""));
|
stackPacket.getResourcePacks().add(new ResourcePackStackPacket.Entry(header.uuid().toString(), header.version().toString(), ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) {
|
if (GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) {
|
||||||
|
@ -232,6 +231,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
stackPacket.getExperiments().add(new ExperimentData("data_driven_items", true));
|
stackPacket.getExperiments().add(new ExperimentData("data_driven_items", true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GameProtocol.isPre1_20(session)) {
|
||||||
|
stackPacket.getExperiments().add(new ExperimentData("next_major_update", true));
|
||||||
|
stackPacket.getExperiments().add(new ExperimentData("sniffer", true));
|
||||||
|
}
|
||||||
|
|
||||||
session.sendUpstreamPacket(stackPacket);
|
session.sendUpstreamPacket(stackPacket);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -298,21 +302,22 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
@Override
|
@Override
|
||||||
public PacketSignal handle(ResourcePackChunkRequestPacket packet) {
|
public PacketSignal handle(ResourcePackChunkRequestPacket packet) {
|
||||||
ResourcePackChunkDataPacket data = new ResourcePackChunkDataPacket();
|
ResourcePackChunkDataPacket data = new ResourcePackChunkDataPacket();
|
||||||
ResourcePack pack = ResourcePack.PACKS.get(packet.getPackId().toString());
|
ResourcePack pack = this.resourcePackLoadEvent.getPacks().get(packet.getPackId().toString());
|
||||||
|
PackCodec codec = pack.codec();
|
||||||
|
|
||||||
data.setChunkIndex(packet.getChunkIndex());
|
data.setChunkIndex(packet.getChunkIndex());
|
||||||
data.setProgress(packet.getChunkIndex() * ResourcePack.CHUNK_SIZE);
|
data.setProgress((long) packet.getChunkIndex() * GeyserResourcePack.CHUNK_SIZE);
|
||||||
data.setPackVersion(packet.getPackVersion());
|
data.setPackVersion(packet.getPackVersion());
|
||||||
data.setPackId(packet.getPackId());
|
data.setPackId(packet.getPackId());
|
||||||
|
|
||||||
int offset = packet.getChunkIndex() * ResourcePack.CHUNK_SIZE;
|
int offset = packet.getChunkIndex() * GeyserResourcePack.CHUNK_SIZE;
|
||||||
long remainingSize = pack.getFile().length() - offset;
|
long remainingSize = codec.size() - offset;
|
||||||
byte[] packData = new byte[(int) MathUtils.constrain(remainingSize, 0, ResourcePack.CHUNK_SIZE)];
|
byte[] packData = new byte[(int) MathUtils.constrain(remainingSize, 0, GeyserResourcePack.CHUNK_SIZE)];
|
||||||
|
|
||||||
try (InputStream inputStream = new FileInputStream(pack.getFile())) {
|
try (SeekableByteChannel channel = codec.serialize(pack)) {
|
||||||
inputStream.skip(offset);
|
channel.position(offset);
|
||||||
inputStream.read(packData, 0, packData.length);
|
channel.read(ByteBuffer.wrap(packData, 0, packData.length));
|
||||||
} catch (Exception e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,7 +326,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
session.sendUpstreamPacket(data);
|
session.sendUpstreamPacket(data);
|
||||||
|
|
||||||
// Check if it is the last chunk and send next pack in queue when available.
|
// Check if it is the last chunk and send next pack in queue when available.
|
||||||
if (remainingSize <= ResourcePack.CHUNK_SIZE && !packsToSent.isEmpty()) {
|
if (remainingSize <= GeyserResourcePack.CHUNK_SIZE && !packsToSent.isEmpty()) {
|
||||||
sendPackDataInfo(packsToSent.pop());
|
sendPackDataInfo(packsToSent.pop());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,15 +336,16 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
private void sendPackDataInfo(String id) {
|
private void sendPackDataInfo(String id) {
|
||||||
ResourcePackDataInfoPacket data = new ResourcePackDataInfoPacket();
|
ResourcePackDataInfoPacket data = new ResourcePackDataInfoPacket();
|
||||||
String[] packID = id.split("_");
|
String[] packID = id.split("_");
|
||||||
ResourcePack pack = ResourcePack.PACKS.get(packID[0]);
|
ResourcePack pack = this.resourcePackLoadEvent.getPacks().get(packID[0]);
|
||||||
ResourcePackManifest.Header header = pack.getManifest().getHeader();
|
PackCodec codec = pack.codec();
|
||||||
|
ResourcePackManifest.Header header = pack.manifest().header();
|
||||||
|
|
||||||
data.setPackId(header.getUuid());
|
data.setPackId(header.uuid());
|
||||||
int chunkCount = (int) Math.ceil((int) pack.getFile().length() / (double) ResourcePack.CHUNK_SIZE);
|
int chunkCount = (int) Math.ceil(codec.size() / (double) GeyserResourcePack.CHUNK_SIZE);
|
||||||
data.setChunkCount(chunkCount);
|
data.setChunkCount(chunkCount);
|
||||||
data.setCompressedPackSize(pack.getFile().length());
|
data.setCompressedPackSize(codec.size());
|
||||||
data.setMaxChunkSize(ResourcePack.CHUNK_SIZE);
|
data.setMaxChunkSize(GeyserResourcePack.CHUNK_SIZE);
|
||||||
data.setHash(pack.getSha256());
|
data.setHash(codec.sha256());
|
||||||
data.setPackVersion(packID[1]);
|
data.setPackVersion(packID[1]);
|
||||||
data.setPremium(false);
|
data.setPremium(false);
|
||||||
data.setType(ResourcePackType.RESOURCES);
|
data.setType(ResourcePackType.RESOURCES);
|
||||||
|
|
|
@ -187,7 +187,16 @@ public final class GeyserServer {
|
||||||
|
|
||||||
public BedrockPong onQuery(InetSocketAddress inetSocketAddress) {
|
public BedrockPong onQuery(InetSocketAddress inetSocketAddress) {
|
||||||
if (geyser.getConfig().isDebugMode() && PRINT_DEBUG_PINGS) {
|
if (geyser.getConfig().isDebugMode() && PRINT_DEBUG_PINGS) {
|
||||||
String ip = geyser.getConfig().isLogPlayerIpAddresses() ? inetSocketAddress.toString() : "<IP address withheld>";
|
String ip;
|
||||||
|
if (geyser.getConfig().isLogPlayerIpAddresses()) {
|
||||||
|
if (geyser.getConfig().getBedrock().isEnableProxyProtocol()) {
|
||||||
|
ip = this.proxiedAddresses.getOrDefault(inetSocketAddress, inetSocketAddress).toString();
|
||||||
|
} else {
|
||||||
|
ip = inetSocketAddress.toString();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ip = "<IP address withheld>";
|
||||||
|
}
|
||||||
geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.network.pinged", ip));
|
geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.network.pinged", ip));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,10 +73,10 @@ public class ProxyServerHandler extends SimpleChannelInboundHandler<DatagramPack
|
||||||
presentAddress = new InetSocketAddress(decoded.sourceAddress(), decoded.sourcePort());
|
presentAddress = new InetSocketAddress(decoded.sourceAddress(), decoded.sourcePort());
|
||||||
log.debug("Got PROXY header: (from {}) {}", packet.sender(), presentAddress);
|
log.debug("Got PROXY header: (from {}) {}", packet.sender(), presentAddress);
|
||||||
GeyserImpl.getInstance().getGeyserServer().getProxiedAddresses().put(packet.sender(), presentAddress);
|
GeyserImpl.getInstance().getGeyserServer().getProxiedAddresses().put(packet.sender(), presentAddress);
|
||||||
return;
|
} else {
|
||||||
|
log.trace("Reusing PROXY header: (from {}) {}", packet.sender(), presentAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.trace("Reusing PROXY header: (from {}) {}", packet.sender(), presentAddress);
|
|
||||||
ctx.fireChannelRead(packet.retain());
|
ctx.fireChannelRead(packet.retain());
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* 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.pack;
|
||||||
|
|
||||||
|
import org.geysermc.geyser.api.pack.PackCodec;
|
||||||
|
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||||
|
import org.geysermc.geyser.api.pack.ResourcePackManifest;
|
||||||
|
|
||||||
|
public record GeyserResourcePack(PackCodec codec, ResourcePackManifest manifest, String contentKey) implements ResourcePack {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of each chunk to use when sending the resource packs to clients in bytes
|
||||||
|
*/
|
||||||
|
public static final int CHUNK_SIZE = 102400;
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* 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.pack;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import org.geysermc.geyser.api.pack.ResourcePackManifest;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record GeyserResourcePackManifest(@JsonProperty("format_version") int formatVersion, Header header, Collection<Module> modules, Collection<Dependency> dependencies) implements ResourcePackManifest {
|
||||||
|
|
||||||
|
public record Header(UUID uuid, Version version, String name, String description, @JsonProperty("min_engine_version") Version minimumSupportedMinecraftVersion) implements ResourcePackManifest.Header { }
|
||||||
|
|
||||||
|
public record Module(UUID uuid, Version version, String type, String description) implements ResourcePackManifest.Module { }
|
||||||
|
|
||||||
|
public record Dependency(UUID uuid, Version version) implements ResourcePackManifest.Dependency { }
|
||||||
|
|
||||||
|
@JsonDeserialize(using = Version.VersionDeserializer.class)
|
||||||
|
public record Version(int major, int minor, int patch) implements ResourcePackManifest.Version {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull String toString() {
|
||||||
|
return major + "." + minor + "." + patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class VersionDeserializer extends JsonDeserializer<Version> {
|
||||||
|
@Override
|
||||||
|
public Version deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||||
|
int[] version = ctxt.readValue(p, int[].class);
|
||||||
|
return new Version(version[0], version[1], version[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,160 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019-2022 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.pack;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
|
||||||
import org.geysermc.geyser.api.event.lifecycle.GeyserLoadResourcePacksEvent;
|
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
|
||||||
import org.geysermc.geyser.util.FileUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
import java.util.zip.ZipFile;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This represents a resource pack and all the data relevant to it
|
|
||||||
*/
|
|
||||||
public class ResourcePack {
|
|
||||||
/**
|
|
||||||
* The list of loaded resource packs
|
|
||||||
*/
|
|
||||||
public static final Map<String, ResourcePack> PACKS = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The size of each chunk to use when sending the resource packs to clients in bytes
|
|
||||||
*/
|
|
||||||
public static final int CHUNK_SIZE = 102400;
|
|
||||||
|
|
||||||
private byte[] sha256;
|
|
||||||
private File file;
|
|
||||||
private ResourcePackManifest manifest;
|
|
||||||
private ResourcePackManifest.Version version;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private String contentKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loop through the packs directory and locate valid resource pack files
|
|
||||||
*/
|
|
||||||
public static void loadPacks() {
|
|
||||||
Path directory = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("packs");
|
|
||||||
|
|
||||||
if (!Files.exists(directory)) {
|
|
||||||
try {
|
|
||||||
Files.createDirectory(directory);
|
|
||||||
} catch (IOException e) {
|
|
||||||
GeyserImpl.getInstance().getLogger().error("Could not create packs directory", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// As we just created the directory it will be empty
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Path> resourcePacks;
|
|
||||||
try {
|
|
||||||
resourcePacks = Files.walk(directory).collect(Collectors.toList());
|
|
||||||
} catch (IOException e) {
|
|
||||||
GeyserImpl.getInstance().getLogger().error("Could not list packs directory", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GeyserLoadResourcePacksEvent event = new GeyserLoadResourcePacksEvent(resourcePacks);
|
|
||||||
GeyserImpl.getInstance().eventBus().fire(event);
|
|
||||||
|
|
||||||
for (Path path : event.resourcePacks()) {
|
|
||||||
File file = path.toFile();
|
|
||||||
|
|
||||||
if (file.getName().endsWith(".zip") || file.getName().endsWith(".mcpack")) {
|
|
||||||
ResourcePack pack = new ResourcePack();
|
|
||||||
|
|
||||||
pack.sha256 = FileUtils.calculateSHA256(file);
|
|
||||||
|
|
||||||
try (ZipFile zip = new ZipFile(file);
|
|
||||||
Stream<? extends ZipEntry> stream = zip.stream()) {
|
|
||||||
stream.forEach((x) -> {
|
|
||||||
String name = x.getName();
|
|
||||||
if (name.length() >= 80) {
|
|
||||||
GeyserImpl.getInstance().getLogger().warning("The resource pack " + file.getName()
|
|
||||||
+ " has a file in it that meets or exceeds 80 characters in its path (" + name
|
|
||||||
+ ", " + name.length() + " characters long). This will cause problems on some Bedrock platforms." +
|
|
||||||
" Please rename it to be shorter, or reduce the amount of folders needed to get to the file.");
|
|
||||||
}
|
|
||||||
if (name.contains("manifest.json")) {
|
|
||||||
try {
|
|
||||||
ResourcePackManifest manifest = FileUtils.loadJson(zip.getInputStream(x), ResourcePackManifest.class);
|
|
||||||
// Sometimes a pack_manifest file is present and not in a valid format,
|
|
||||||
// but a manifest file is, so we null check through that one
|
|
||||||
if (manifest.getHeader().getUuid() != null) {
|
|
||||||
pack.file = file;
|
|
||||||
pack.manifest = manifest;
|
|
||||||
pack.version = ResourcePackManifest.Version.fromArray(manifest.getHeader().getVersion());
|
|
||||||
|
|
||||||
PACKS.put(pack.getManifest().getHeader().getUuid().toString(), pack);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check if a file exists with the same name as the resource pack suffixed by .key,
|
|
||||||
// and set this as content key. (e.g. test.zip, key file would be test.zip.key)
|
|
||||||
File keyFile = new File(file.getParentFile(), file.getName() + ".key");
|
|
||||||
pack.contentKey = keyFile.exists() ? Files.readString(keyFile.toPath(), StandardCharsets.UTF_8) : "";
|
|
||||||
} catch (Exception e) {
|
|
||||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.resource_pack.broken", file.getName()));
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getSha256() {
|
|
||||||
return sha256;
|
|
||||||
}
|
|
||||||
|
|
||||||
public File getFile() {
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResourcePackManifest getManifest() {
|
|
||||||
return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResourcePackManifest.Version getVersion() {
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,117 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019-2022 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.pack;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.ToString;
|
|
||||||
import lombok.Value;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* author: NukkitX
|
|
||||||
* Nukkit Project
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
@EqualsAndHashCode
|
|
||||||
public class ResourcePackManifest {
|
|
||||||
@JsonProperty("format_version")
|
|
||||||
private Integer formatVersion;
|
|
||||||
private Header header;
|
|
||||||
private Collection<Module> modules;
|
|
||||||
protected Collection<Dependency> dependencies;
|
|
||||||
|
|
||||||
public Collection<Module> getModules() {
|
|
||||||
return Collections.unmodifiableCollection(modules);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@ToString
|
|
||||||
public static class Header {
|
|
||||||
private String description;
|
|
||||||
private String name;
|
|
||||||
private UUID uuid;
|
|
||||||
private int[] version;
|
|
||||||
@JsonProperty("min_engine_version")
|
|
||||||
private int[] minimumSupportedMinecraftVersion;
|
|
||||||
|
|
||||||
public String getVersionString() {
|
|
||||||
return version[0] + "." + version[1] + "." + version[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@ToString
|
|
||||||
public static class Module {
|
|
||||||
private String description;
|
|
||||||
private String name;
|
|
||||||
private UUID uuid;
|
|
||||||
private int[] version;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@ToString
|
|
||||||
public static class Dependency {
|
|
||||||
private UUID uuid;
|
|
||||||
private int[] version;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Value
|
|
||||||
public static class Version {
|
|
||||||
private final int major;
|
|
||||||
private final int minor;
|
|
||||||
private final int patch;
|
|
||||||
|
|
||||||
public static Version fromString(String ver) {
|
|
||||||
String[] split = ver.replace(']', ' ')
|
|
||||||
.replace('[', ' ')
|
|
||||||
.replaceAll(" ", "").split(",");
|
|
||||||
|
|
||||||
return new Version(Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2]));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Version fromArray(int[] ver) {
|
|
||||||
return new Version(ver[0], ver[1], ver[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Version(int major, int minor, int patch) {
|
|
||||||
this.major = major;
|
|
||||||
this.minor = minor;
|
|
||||||
this.patch = patch;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return major + "." + minor + "." + patch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* 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.pack.path;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
import org.geysermc.geyser.api.pack.PathPackCodec;
|
||||||
|
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||||
|
import org.geysermc.geyser.registry.loader.ResourcePackLoader;
|
||||||
|
import org.geysermc.geyser.util.FileUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.channels.SeekableByteChannel;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.attribute.FileTime;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GeyserPathPackCodec extends PathPackCodec {
|
||||||
|
private final Path path;
|
||||||
|
private FileTime lastModified;
|
||||||
|
|
||||||
|
private byte[] sha256;
|
||||||
|
private long size = -1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull Path path() {
|
||||||
|
this.checkLastModified();
|
||||||
|
return this.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte @NonNull [] sha256() {
|
||||||
|
this.checkLastModified();
|
||||||
|
if (this.sha256 != null) {
|
||||||
|
return this.sha256;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sha256 = FileUtils.calculateSHA256(this.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long size() {
|
||||||
|
this.checkLastModified();
|
||||||
|
if (this.size != -1) {
|
||||||
|
return this.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return this.size = Files.size(this.path);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Could not get file size of path " + this.path, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull SeekableByteChannel serialize(@NonNull ResourcePack resourcePack) throws IOException {
|
||||||
|
return FileChannel.open(this.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NonNull ResourcePack create() {
|
||||||
|
return ResourcePackLoader.readPack(this.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkLastModified() {
|
||||||
|
try {
|
||||||
|
FileTime lastModified = Files.getLastModifiedTime(this.path);
|
||||||
|
if (this.lastModified == null) {
|
||||||
|
this.lastModified = lastModified;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastModified.toInstant().isAfter(this.lastModified.toInstant())) {
|
||||||
|
GeyserImpl.getInstance().getLogger().warning("Detected a change in the resource pack " + path + ". This is likely to cause undefined behavior for new clients joining. It is suggested you restart Geyser.");
|
||||||
|
this.lastModified = lastModified;
|
||||||
|
this.sha256 = null;
|
||||||
|
this.size = -1;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,184 @@
|
||||||
|
/*
|
||||||
|
* 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.registry;
|
||||||
|
|
||||||
|
import org.geysermc.geyser.registry.loader.RegistryLoader;
|
||||||
|
import org.geysermc.geyser.registry.loader.RegistryLoaders;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A deferred registry is a registry that is not loaded until it is needed.
|
||||||
|
* This is useful for registries that are not needed until after other parts
|
||||||
|
* of the lifecycle have been completed.
|
||||||
|
* <p>
|
||||||
|
* This class is slightly different from other registries in that it acts as
|
||||||
|
* a wrapper around another registry. This is to allow for any kind of registry
|
||||||
|
* type to be deferred.
|
||||||
|
*
|
||||||
|
* @param <M> the value being held by the registry
|
||||||
|
*/
|
||||||
|
public final class DeferredRegistry<M> implements IRegistry<M> {
|
||||||
|
private final Registry<M> backingRegistry;
|
||||||
|
private final Supplier<M> loader;
|
||||||
|
|
||||||
|
private boolean loaded;
|
||||||
|
|
||||||
|
private <I> DeferredRegistry(Function<RegistryLoader<I, M>, Registry<M>> registryLoader, RegistryLoader<I, M> deferredLoader) {
|
||||||
|
this.backingRegistry = registryLoader.apply(RegistryLoaders.uninitialized());
|
||||||
|
this.loader = () -> deferredLoader.load(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <I> DeferredRegistry(Function<RegistryLoader<I, M>, Registry<M>> registryLoader, Supplier<RegistryLoader<I, M>> deferredLoader) {
|
||||||
|
this.backingRegistry = registryLoader.apply(RegistryLoaders.uninitialized());
|
||||||
|
this.loader = () -> deferredLoader.get().load(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <I> DeferredRegistry(I input, RegistryInitializer<M> registryInitializer, RegistryLoader<I, M> deferredLoader) {
|
||||||
|
this.backingRegistry = registryInitializer.initialize(input, RegistryLoaders.uninitialized());
|
||||||
|
this.loader = () -> deferredLoader.load(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <I> DeferredRegistry(I input, RegistryInitializer<M> registryInitializer, Supplier<RegistryLoader<I, M>> deferredLoader) {
|
||||||
|
this.backingRegistry = registryInitializer.initialize(input, RegistryLoaders.uninitialized());
|
||||||
|
this.loader = () -> deferredLoader.get().load(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the underlying value held by this registry.
|
||||||
|
*
|
||||||
|
* @return the underlying value held by this registry
|
||||||
|
* @throws IllegalStateException if this deferred registry has not been loaded yet
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public M get() {
|
||||||
|
if (!this.loaded) {
|
||||||
|
throw new IllegalStateException("Registry has not been loaded yet!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.backingRegistry.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(M mappings) {
|
||||||
|
this.backingRegistry.set(mappings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers what is specified in the given {@link Consumer} into the underlying value.
|
||||||
|
*
|
||||||
|
* @param consumer the consumer
|
||||||
|
* @throws IllegalStateException if this deferred registry has not been loaded yet
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void register(Consumer<M> consumer) {
|
||||||
|
if (!this.loaded) {
|
||||||
|
throw new IllegalStateException("Registry has not been loaded yet!");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.backingRegistry.register(consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the registry.
|
||||||
|
*/
|
||||||
|
public void load() {
|
||||||
|
this.backingRegistry.set(this.loader.get());
|
||||||
|
this.loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new deferred registry.
|
||||||
|
*
|
||||||
|
* @param registryLoader the registry loader
|
||||||
|
* @param deferredLoader the deferred loader
|
||||||
|
* @param <I> the input type
|
||||||
|
* @param <M> the registry type
|
||||||
|
* @return the new deferred registry
|
||||||
|
*/
|
||||||
|
public static <I, M> DeferredRegistry<M> create(Function<RegistryLoader<I, M>, Registry<M>> registryLoader, RegistryLoader<I, M> deferredLoader) {
|
||||||
|
return new DeferredRegistry<>(registryLoader, deferredLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new deferred registry.
|
||||||
|
*
|
||||||
|
* @param registryLoader the registry loader
|
||||||
|
* @param deferredLoader the deferred loader
|
||||||
|
* @param <I> the input type
|
||||||
|
* @param <M> the registry type
|
||||||
|
* @return the new deferred registry
|
||||||
|
*/
|
||||||
|
public static <I, M> DeferredRegistry<M> create(Function<RegistryLoader<I, M>, Registry<M>> registryLoader, Supplier<RegistryLoader<I, M>> deferredLoader) {
|
||||||
|
return new DeferredRegistry<>(registryLoader, deferredLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new deferred registry.
|
||||||
|
*
|
||||||
|
* @param registryInitializer the registry initializer
|
||||||
|
* @param deferredLoader the deferred loader
|
||||||
|
* @param <I> the input type
|
||||||
|
* @param <M> the registry type
|
||||||
|
* @return the new deferred registry
|
||||||
|
*/
|
||||||
|
public static <I, M> DeferredRegistry<M> create(I input, RegistryInitializer<M> registryInitializer, RegistryLoader<I, M> deferredLoader) {
|
||||||
|
return new DeferredRegistry<>(input, registryInitializer, deferredLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new deferred registry.
|
||||||
|
*
|
||||||
|
* @param registryInitializer the registry initializer
|
||||||
|
* @param deferredLoader the deferred loader
|
||||||
|
* @param <I> the input type
|
||||||
|
* @param <M> the registry type
|
||||||
|
* @return the new deferred registry
|
||||||
|
*/
|
||||||
|
public static <I, M> DeferredRegistry<M> create(I input, RegistryInitializer<M> registryInitializer, Supplier<RegistryLoader<I, M>> deferredLoader) {
|
||||||
|
return new DeferredRegistry<>(input, registryInitializer, deferredLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A registry initializer.
|
||||||
|
*
|
||||||
|
* @param <M> the registry type
|
||||||
|
*/
|
||||||
|
interface RegistryInitializer<M> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the registry.
|
||||||
|
*
|
||||||
|
* @param input the input
|
||||||
|
* @param registryLoader the registry loader
|
||||||
|
* @param <I> the input type
|
||||||
|
* @return the initialized registry
|
||||||
|
*/
|
||||||
|
<I> Registry<M> initialize(I input, RegistryLoader<I, M> registryLoader);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* 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.registry;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a registry.
|
||||||
|
*
|
||||||
|
* @param <M> the value being held by the registry
|
||||||
|
*/
|
||||||
|
interface IRegistry<M> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the underlying value held by this registry.
|
||||||
|
*
|
||||||
|
* @return the underlying value held by this registry.
|
||||||
|
*/
|
||||||
|
M get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the underlying value held by this registry.
|
||||||
|
* Clears any existing data associated with the previous
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* @param mappings the underlying value held by this registry
|
||||||
|
*/
|
||||||
|
void set(M mappings);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers what is specified in the given
|
||||||
|
* {@link Consumer} into the underlying value.
|
||||||
|
*
|
||||||
|
* @param consumer the consumer
|
||||||
|
*/
|
||||||
|
void register(Consumer<M> consumer);
|
||||||
|
}
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.registry;
|
package org.geysermc.geyser.registry;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundDelimiterPacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundTabListPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundTabListPacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundLightUpdatePacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundLightUpdatePacket;
|
||||||
import io.netty.channel.EventLoop;
|
import io.netty.channel.EventLoop;
|
||||||
|
@ -44,6 +45,7 @@ public class PacketTranslatorRegistry<T> extends AbstractMappedRegistry<Class<?
|
||||||
static {
|
static {
|
||||||
IGNORED_PACKETS.add(ClientboundLightUpdatePacket.class); // Light is handled on Bedrock for us
|
IGNORED_PACKETS.add(ClientboundLightUpdatePacket.class); // Light is handled on Bedrock for us
|
||||||
IGNORED_PACKETS.add(ClientboundTabListPacket.class); // Cant be implemented in Bedrock
|
IGNORED_PACKETS.add(ClientboundTabListPacket.class); // Cant be implemented in Bedrock
|
||||||
|
IGNORED_PACKETS.add(ClientboundDelimiterPacket.class); // Not implemented, spams logs
|
||||||
}
|
}
|
||||||
|
|
||||||
protected PacketTranslatorRegistry() {
|
protected PacketTranslatorRegistry() {
|
||||||
|
|
|
@ -41,6 +41,8 @@ import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.PotionMixData;
|
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.PotionMixData;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.RecipeData;
|
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.RecipeData;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
|
||||||
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||||
import org.geysermc.geyser.entity.EntityDefinition;
|
import org.geysermc.geyser.entity.EntityDefinition;
|
||||||
import org.geysermc.geyser.inventory.item.Enchantment.JavaEnchantment;
|
import org.geysermc.geyser.inventory.item.Enchantment.JavaEnchantment;
|
||||||
import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
|
import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
|
||||||
|
@ -158,6 +160,11 @@ public final class Registries {
|
||||||
*/
|
*/
|
||||||
public static final IntMappedRegistry<org.cloudburstmc.protocol.bedrock.data.SoundEvent> RECORDS = IntMappedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new));
|
public static final IntMappedRegistry<org.cloudburstmc.protocol.bedrock.data.SoundEvent> RECORDS = IntMappedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mapped registry holding {@link ResourcePack}'s with the pack uuid as keys.
|
||||||
|
*/
|
||||||
|
public static final DeferredRegistry<Map<String, ResourcePack>> RESOURCE_PACKS = DeferredRegistry.create(GeyserImpl.getInstance().packDirectory(), SimpleMappedRegistry::create, RegistryLoaders.RESOURCE_PACKS);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapped registry holding sound identifiers to their corresponding {@link SoundMapping}.
|
* A mapped registry holding sound identifiers to their corresponding {@link SoundMapping}.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -64,7 +64,7 @@ import java.util.function.Consumer;
|
||||||
*
|
*
|
||||||
* @param <M> the value being held by the registry
|
* @param <M> the value being held by the registry
|
||||||
*/
|
*/
|
||||||
public abstract class Registry<M> {
|
public abstract class Registry<M> implements IRegistry<M> {
|
||||||
protected M mappings;
|
protected M mappings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,6 +85,7 @@ public abstract class Registry<M> {
|
||||||
*
|
*
|
||||||
* @return the underlying value held by this registry.
|
* @return the underlying value held by this registry.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public M get() {
|
public M get() {
|
||||||
return this.mappings;
|
return this.mappings;
|
||||||
}
|
}
|
||||||
|
@ -96,6 +97,7 @@ public abstract class Registry<M> {
|
||||||
*
|
*
|
||||||
* @param mappings the underlying value held by this registry
|
* @param mappings the underlying value held by this registry
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void set(M mappings) {
|
public void set(M mappings) {
|
||||||
this.mappings = mappings;
|
this.mappings = mappings;
|
||||||
}
|
}
|
||||||
|
@ -106,6 +108,7 @@ public abstract class Registry<M> {
|
||||||
*
|
*
|
||||||
* @param consumer the consumer
|
* @param consumer the consumer
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void register(Consumer<M> consumer) {
|
public void register(Consumer<M> consumer) {
|
||||||
consumer.accept(this.mappings);
|
consumer.accept(this.mappings);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,13 +31,16 @@ import org.geysermc.geyser.api.extension.Extension;
|
||||||
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
||||||
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
||||||
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
|
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
|
||||||
|
import org.geysermc.geyser.api.pack.PathPackCodec;
|
||||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||||
import org.geysermc.geyser.event.GeyserEventRegistrar;
|
import org.geysermc.geyser.event.GeyserEventRegistrar;
|
||||||
import org.geysermc.geyser.item.GeyserCustomItemData;
|
import org.geysermc.geyser.item.GeyserCustomItemData;
|
||||||
import org.geysermc.geyser.item.GeyserCustomItemOptions;
|
import org.geysermc.geyser.item.GeyserCustomItemOptions;
|
||||||
import org.geysermc.geyser.item.GeyserNonVanillaCustomItemData;
|
import org.geysermc.geyser.item.GeyserNonVanillaCustomItemData;
|
||||||
|
import org.geysermc.geyser.pack.path.GeyserPathPackCodec;
|
||||||
import org.geysermc.geyser.registry.provider.ProviderSupplier;
|
import org.geysermc.geyser.registry.provider.ProviderSupplier;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,11 +50,15 @@ public class ProviderRegistryLoader implements RegistryLoader<Map<Class<?>, Prov
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Class<?>, ProviderSupplier> load(Map<Class<?>, ProviderSupplier> providers) {
|
public Map<Class<?>, ProviderSupplier> load(Map<Class<?>, ProviderSupplier> providers) {
|
||||||
|
// misc
|
||||||
providers.put(Command.Builder.class, args -> new GeyserCommandManager.CommandBuilder<>((Extension) args[0]));
|
providers.put(Command.Builder.class, args -> new GeyserCommandManager.CommandBuilder<>((Extension) args[0]));
|
||||||
|
providers.put(EventRegistrar.class, args -> new GeyserEventRegistrar(args[0]));
|
||||||
|
providers.put(PathPackCodec.class, args -> new GeyserPathPackCodec((Path) args[0]));
|
||||||
|
|
||||||
|
// items
|
||||||
providers.put(CustomItemData.Builder.class, args -> new GeyserCustomItemData.CustomItemDataBuilder());
|
providers.put(CustomItemData.Builder.class, args -> new GeyserCustomItemData.CustomItemDataBuilder());
|
||||||
providers.put(CustomItemOptions.Builder.class, args -> new GeyserCustomItemOptions.CustomItemOptionsBuilder());
|
providers.put(CustomItemOptions.Builder.class, args -> new GeyserCustomItemOptions.CustomItemOptionsBuilder());
|
||||||
providers.put(NonVanillaCustomItemData.Builder.class, args -> new GeyserNonVanillaCustomItemData.NonVanillaCustomItemDataBuilder());
|
providers.put(NonVanillaCustomItemData.Builder.class, args -> new GeyserNonVanillaCustomItemData.NonVanillaCustomItemDataBuilder());
|
||||||
providers.put(EventRegistrar.class, args -> new GeyserEventRegistrar(args[0]));
|
|
||||||
|
|
||||||
return providers;
|
return providers;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,12 @@ public final class RegistryLoaders {
|
||||||
/**
|
/**
|
||||||
* The {@link RegistryLoader} responsible for loading NBT.
|
* The {@link RegistryLoader} responsible for loading NBT.
|
||||||
*/
|
*/
|
||||||
public static NbtRegistryLoader NBT = new NbtRegistryLoader();
|
public static final NbtRegistryLoader NBT = new NbtRegistryLoader();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link RegistryLoader} responsible for loading resource packs.
|
||||||
|
*/
|
||||||
|
public static final ResourcePackLoader RESOURCE_PACKS = new ResourcePackLoader();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps the surrounding {@link Supplier} in a {@link RegistryLoader} which does
|
* Wraps the surrounding {@link Supplier} in a {@link RegistryLoader} which does
|
||||||
|
@ -51,10 +56,14 @@ public final class RegistryLoaders {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Returns a {@link RegistryLoader} which has not taken
|
||||||
|
* in any input value.
|
||||||
|
*
|
||||||
|
* @param <I> the input
|
||||||
* @param <V> the value
|
* @param <V> the value
|
||||||
* @return a RegistryLoader that is yet to contain a value.
|
* @return a RegistryLoader that is yet to contain a value.
|
||||||
*/
|
*/
|
||||||
public static <V> RegistryLoader<Object, V> uninitialized() {
|
public static <I, V> RegistryLoader<I, V> uninitialized() {
|
||||||
return input -> null;
|
return input -> null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
/*
|
||||||
|
* 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.registry.loader;
|
||||||
|
|
||||||
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
import org.geysermc.geyser.api.event.lifecycle.GeyserLoadResourcePacksEvent;
|
||||||
|
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||||
|
import org.geysermc.geyser.pack.GeyserResourcePack;
|
||||||
|
import org.geysermc.geyser.pack.GeyserResourcePackManifest;
|
||||||
|
import org.geysermc.geyser.pack.path.GeyserPathPackCodec;
|
||||||
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
|
import org.geysermc.geyser.util.FileUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.FileSystems;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.PathMatcher;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads {@link ResourcePack}s within a {@link Path} directory, firing the {@link GeyserLoadResourcePacksEvent}.
|
||||||
|
*/
|
||||||
|
public class ResourcePackLoader implements RegistryLoader<Path, Map<String, ResourcePack>> {
|
||||||
|
|
||||||
|
static final PathMatcher PACK_MATCHER = FileSystems.getDefault().getPathMatcher("glob:**.{zip,mcpack}");
|
||||||
|
|
||||||
|
private static final boolean SHOW_RESOURCE_PACK_LENGTH_WARNING = Boolean.parseBoolean(System.getProperty("Geyser.ShowResourcePackLengthWarning", "true"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loop through the packs directory and locate valid resource pack files
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Map<String, ResourcePack> load(Path directory) {
|
||||||
|
Map<String, ResourcePack> packMap = new HashMap<>();
|
||||||
|
|
||||||
|
if (!Files.exists(directory)) {
|
||||||
|
try {
|
||||||
|
Files.createDirectory(directory);
|
||||||
|
} catch (IOException e) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error("Could not create packs directory", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Path> resourcePacks;
|
||||||
|
try (Stream<Path> stream = Files.walk(directory)) {
|
||||||
|
resourcePacks = stream.filter(PACK_MATCHER::matches)
|
||||||
|
.collect(Collectors.toCollection(ArrayList::new)); // toList() does not guarantee mutability
|
||||||
|
} catch (Exception e) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error("Could not list packs directory", e);
|
||||||
|
|
||||||
|
// Ensure the event is fired even if there was an issue reading
|
||||||
|
// from our own resource pack directory. External projects may have
|
||||||
|
// resource packs located at different locations.
|
||||||
|
resourcePacks = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
GeyserLoadResourcePacksEvent event = new GeyserLoadResourcePacksEvent(resourcePacks);
|
||||||
|
GeyserImpl.getInstance().eventBus().fire(event);
|
||||||
|
|
||||||
|
for (Path path : event.resourcePacks()) {
|
||||||
|
try {
|
||||||
|
GeyserResourcePack pack = readPack(path);
|
||||||
|
packMap.put(pack.manifest().header().uuid().toString(), pack);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return packMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a resource pack at the given file. Also searches for a file in the same directory, with the same name
|
||||||
|
* but suffixed by ".key", containing the content key. If such file does not exist, no content key is stored.
|
||||||
|
*
|
||||||
|
* @param path the file to read from, in ZIP format
|
||||||
|
* @return a {@link ResourcePack} representation
|
||||||
|
* @throws IllegalArgumentException if the pack manifest was invalid or there was any processing exception
|
||||||
|
*/
|
||||||
|
public static GeyserResourcePack readPack(Path path) throws IllegalArgumentException {
|
||||||
|
if (!path.getFileName().toString().endsWith(".mcpack") && !path.getFileName().toString().endsWith(".zip")) {
|
||||||
|
throw new IllegalArgumentException("Resource pack " + path.getFileName() + " must be a .zip or .mcpack file!");
|
||||||
|
}
|
||||||
|
|
||||||
|
AtomicReference<GeyserResourcePackManifest> manifestReference = new AtomicReference<>();
|
||||||
|
|
||||||
|
try (ZipFile zip = new ZipFile(path.toFile());
|
||||||
|
Stream<? extends ZipEntry> stream = zip.stream()) {
|
||||||
|
stream.forEach(x -> {
|
||||||
|
String name = x.getName();
|
||||||
|
if (SHOW_RESOURCE_PACK_LENGTH_WARNING && name.length() >= 80) {
|
||||||
|
GeyserImpl.getInstance().getLogger().warning("The resource pack " + path.getFileName()
|
||||||
|
+ " has a file in it that meets or exceeds 80 characters in its path (" + name
|
||||||
|
+ ", " + name.length() + " characters long). This will cause problems on some Bedrock platforms." +
|
||||||
|
" Please rename it to be shorter, or reduce the amount of folders needed to get to the file.");
|
||||||
|
}
|
||||||
|
if (name.contains("manifest.json")) {
|
||||||
|
try {
|
||||||
|
GeyserResourcePackManifest manifest = FileUtils.loadJson(zip.getInputStream(x), GeyserResourcePackManifest.class);
|
||||||
|
if (manifest.header().uuid() != null) {
|
||||||
|
manifestReference.set(manifest);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
GeyserResourcePackManifest manifest = manifestReference.get();
|
||||||
|
if (manifest == null) {
|
||||||
|
throw new IllegalArgumentException(path.getFileName() + " does not contain a valid pack_manifest.json or manifest.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a file exists with the same name as the resource pack suffixed by .key,
|
||||||
|
// and set this as content key. (e.g. test.zip, key file would be test.zip.key)
|
||||||
|
Path keyFile = path.resolveSibling(path.getFileName().toString() + ".key");
|
||||||
|
String contentKey = Files.exists(keyFile) ? Files.readString(path, StandardCharsets.UTF_8) : "";
|
||||||
|
|
||||||
|
return new GeyserResourcePack(new GeyserPathPackCodec(path), manifest, contentKey);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException(GeyserLocale.getLocaleStringLog("geyser.resource_pack.broken", path.getFileName()), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,11 +32,8 @@ import com.google.common.collect.Interner;
|
||||||
import com.google.common.collect.Interners;
|
import com.google.common.collect.Interners;
|
||||||
import it.unimi.dsi.fastutil.objects.*;
|
import it.unimi.dsi.fastutil.objects.*;
|
||||||
import org.cloudburstmc.nbt.*;
|
import org.cloudburstmc.nbt.*;
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v544.Bedrock_v544;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v560.Bedrock_v560;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v567.Bedrock_v567;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v575.Bedrock_v575;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v582.Bedrock_v582;
|
import org.cloudburstmc.protocol.bedrock.codec.v582.Bedrock_v582;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.codec.v589.Bedrock_v589;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
|
@ -70,41 +67,54 @@ public final class BlockRegistryPopulator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerBedrockBlocks() {
|
private static void registerBedrockBlocks() {
|
||||||
BiFunction<String, NbtMapBuilder, String> woolMapper = (bedrockIdentifier, statesBuilder) -> {
|
BiFunction<String, NbtMapBuilder, String> emptyMapper = (bedrockIdentifier, statesBuilder) -> null;
|
||||||
if (bedrockIdentifier.equals("minecraft:wool")) {
|
|
||||||
String color = (String) statesBuilder.remove("color");
|
// We are using mappings that directly support 1.20, so this maps it back to 1.19.80
|
||||||
if ("silver".equals(color)) {
|
BiFunction<String, NbtMapBuilder, String> legacyMapper = (bedrockIdentifier, statesBuilder) -> {
|
||||||
color = "light_gray";
|
if (bedrockIdentifier.endsWith("pumpkin")) {
|
||||||
|
String direction = statesBuilder.remove("minecraft:cardinal_direction").toString();
|
||||||
|
statesBuilder.putInt("direction", switch (direction) {
|
||||||
|
case "north" -> 2;
|
||||||
|
case "east" -> 3;
|
||||||
|
case "west" -> 1;
|
||||||
|
default -> 0; // south
|
||||||
|
});
|
||||||
|
} else if (bedrockIdentifier.endsWith("carpet") && !bedrockIdentifier.startsWith("minecraft:moss")) {
|
||||||
|
String color = bedrockIdentifier.replace("minecraft:", "").replace("_carpet", "");
|
||||||
|
if (color.equals("light_gray")) {
|
||||||
|
color = "silver";
|
||||||
}
|
}
|
||||||
return "minecraft:" + color + "_wool";
|
statesBuilder.putString("color", color);
|
||||||
|
return "minecraft:carpet";
|
||||||
|
} else if (bedrockIdentifier.equals("minecraft:sniffer_egg")) {
|
||||||
|
statesBuilder.remove("cracked_state");
|
||||||
|
return "minecraft:dragon_egg";
|
||||||
|
} else if (bedrockIdentifier.endsWith("coral")) {
|
||||||
|
statesBuilder.putString("coral_color", "blue"); // all blue
|
||||||
|
statesBuilder.putBoolean("dead_bit", bedrockIdentifier.startsWith("minecraft:dead"));
|
||||||
|
return "minecraft:coral";
|
||||||
|
} else if (bedrockIdentifier.endsWith("sculk_sensor")) {
|
||||||
|
int phase = (int) statesBuilder.remove("sculk_sensor_phase");
|
||||||
|
statesBuilder.putBoolean("powered_bit", phase != 0);
|
||||||
|
} else if (bedrockIdentifier.endsWith("pitcher_plant")) {
|
||||||
|
statesBuilder.putString("double_plant_type", "sunflower");
|
||||||
|
return "minecraft:double_plant";
|
||||||
|
} else if (bedrockIdentifier.endsWith("pitcher_crop")) {
|
||||||
|
statesBuilder.remove("growth");
|
||||||
|
if (((byte) statesBuilder.remove("upper_block_bit")) == 1){
|
||||||
|
statesBuilder.putString("flower_type", "orchid");
|
||||||
|
return "minecraft:red_flower"; // top
|
||||||
|
}
|
||||||
|
statesBuilder.putBoolean("update_bit", false);
|
||||||
|
return "minecraft:flower_pot"; // bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
BiFunction<String, NbtMapBuilder, String> emptyMapper = (bedrockIdentifier, statesBuilder) -> null;
|
|
||||||
ImmutableMap<ObjectIntPair<String>, BiFunction<String, NbtMapBuilder, String>> blockMappers = ImmutableMap.<ObjectIntPair<String>, BiFunction<String, NbtMapBuilder, String>>builder()
|
ImmutableMap<ObjectIntPair<String>, BiFunction<String, NbtMapBuilder, String>> blockMappers = ImmutableMap.<ObjectIntPair<String>, BiFunction<String, NbtMapBuilder, String>>builder()
|
||||||
.put(ObjectIntPair.of("1_19_20", Bedrock_v544.CODEC.getProtocolVersion()), emptyMapper)
|
.put(ObjectIntPair.of("1_19_80", Bedrock_v582.CODEC.getProtocolVersion()), legacyMapper)
|
||||||
.put(ObjectIntPair.of("1_19_50", Bedrock_v560.CODEC.getProtocolVersion()), emptyMapper)
|
.put(ObjectIntPair.of("1_20_0", Bedrock_v589.CODEC.getProtocolVersion()), emptyMapper)
|
||||||
.put(ObjectIntPair.of("1_19_60", Bedrock_v567.CODEC.getProtocolVersion()), emptyMapper)
|
|
||||||
.put(ObjectIntPair.of("1_19_70", Bedrock_v575.CODEC.getProtocolVersion()), woolMapper)
|
|
||||||
.put(ObjectIntPair.of("1_19_80", Bedrock_v582.CODEC.getProtocolVersion()), (bedrockIdentifier, statesBuilder) -> {
|
|
||||||
String identifier = woolMapper.apply(bedrockIdentifier, statesBuilder);
|
|
||||||
if (identifier != null) {
|
|
||||||
return identifier;
|
|
||||||
}
|
|
||||||
switch (bedrockIdentifier) {
|
|
||||||
case "minecraft:log", "minecraft:log2" -> {
|
|
||||||
String woodType = (String) statesBuilder.remove(bedrockIdentifier.equals("minecraft:log") ? "old_log_type" : "new_log_type");
|
|
||||||
return "minecraft:" + woodType + "_log";
|
|
||||||
}
|
|
||||||
case "minecraft:fence" -> {
|
|
||||||
String woodType = (String) statesBuilder.remove("wood_type");
|
|
||||||
return "minecraft:" + woodType + "_fence";
|
|
||||||
}
|
|
||||||
default -> {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// We can keep this strong as nothing should be garbage collected
|
// We can keep this strong as nothing should be garbage collected
|
||||||
|
@ -167,8 +177,8 @@ public final class BlockRegistryPopulator {
|
||||||
|
|
||||||
GeyserBedrockBlock bedrockDefinition = blockStateOrderedMap.get(buildBedrockState(entry.getValue(), stateVersion, stateMapper));
|
GeyserBedrockBlock bedrockDefinition = blockStateOrderedMap.get(buildBedrockState(entry.getValue(), stateVersion, stateMapper));
|
||||||
if (bedrockDefinition == null) {
|
if (bedrockDefinition == null) {
|
||||||
throw new RuntimeException("Unable to find " + javaId + " Bedrock BlockDefinition! Built NBT tag: \n" +
|
throw new RuntimeException("Unable to find " + javaId + " Bedrock BlockDefinition on version "
|
||||||
buildBedrockState(entry.getValue(), stateVersion, stateMapper));
|
+ palette.getKey().key() + "! Built NBT tag: \n" + buildBedrockState(entry.getValue(), stateVersion, stateMapper));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (javaId) {
|
switch (javaId) {
|
||||||
|
|
|
@ -54,18 +54,18 @@ public class CreativeItemRegistryPopulator {
|
||||||
(identifier, data) -> identifier.equals("minecraft:bordure_indented_banner_pattern") || identifier.equals("minecraft:field_masoned_banner_pattern")
|
(identifier, data) -> identifier.equals("minecraft:bordure_indented_banner_pattern") || identifier.equals("minecraft:field_masoned_banner_pattern")
|
||||||
);
|
);
|
||||||
|
|
||||||
static void populate(Map.Entry<String, ItemRegistryPopulator.PaletteVersion> version, Map<String, ItemDefinition> definitions, Consumer<ItemData.Builder> itemConsumer) {
|
static void populate(ItemRegistryPopulator.PaletteVersion palette, Map<String, ItemDefinition> definitions, Consumer<ItemData.Builder> itemConsumer) {
|
||||||
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
|
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
|
||||||
|
|
||||||
// Load creative items
|
// Load creative items
|
||||||
JsonNode creativeItemEntries;
|
JsonNode creativeItemEntries;
|
||||||
try (InputStream stream = bootstrap.getResource(String.format("bedrock/creative_items.%s.json", version.getKey()))) {
|
try (InputStream stream = bootstrap.getResource(String.format("bedrock/creative_items.%s.json", palette.version()))) {
|
||||||
creativeItemEntries = GeyserImpl.JSON_MAPPER.readTree(stream).get("items");
|
creativeItemEntries = GeyserImpl.JSON_MAPPER.readTree(stream).get("items");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new AssertionError("Unable to load creative items", e);
|
throw new AssertionError("Unable to load creative items", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockMappings blockMappings = BlockRegistries.BLOCKS.forVersion(version.getValue().protocolVersion());
|
BlockMappings blockMappings = BlockRegistries.BLOCKS.forVersion(palette.protocolVersion());
|
||||||
for (JsonNode itemNode : creativeItemEntries) {
|
for (JsonNode itemNode : creativeItemEntries) {
|
||||||
ItemData.Builder itemBuilder = createItemData(itemNode, blockMappings, definitions);
|
ItemData.Builder itemBuilder = createItemData(itemNode, blockMappings, definitions);
|
||||||
if (itemBuilder == null) {
|
if (itemBuilder == null) {
|
||||||
|
|
|
@ -34,14 +34,12 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||||
import it.unimi.dsi.fastutil.objects.*;
|
import it.unimi.dsi.fastutil.objects.*;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||||
import org.cloudburstmc.nbt.NbtType;
|
import org.cloudburstmc.nbt.NbtType;
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v544.Bedrock_v544;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v560.Bedrock_v560;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v567.Bedrock_v567;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v575.Bedrock_v575;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.codec.v582.Bedrock_v582;
|
import org.cloudburstmc.protocol.bedrock.codec.v582.Bedrock_v582;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.codec.v589.Bedrock_v589;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
|
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.definitions.SimpleItemDefinition;
|
import org.cloudburstmc.protocol.bedrock.data.definitions.SimpleItemDefinition;
|
||||||
|
@ -70,22 +68,40 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
*/
|
*/
|
||||||
public class ItemRegistryPopulator {
|
public class ItemRegistryPopulator {
|
||||||
|
|
||||||
record PaletteVersion(int protocolVersion, Map<Item, String> additionalTranslatedItems) {
|
record PaletteVersion(String version, int protocolVersion, Map<Item, String> javaOnlyItems, Remapper remapper) {
|
||||||
|
|
||||||
|
public PaletteVersion(String version, int protocolVersion) {
|
||||||
|
this(version, protocolVersion, Collections.emptyMap(), (item, mapping) -> mapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
interface Remapper {
|
||||||
|
@NonNull
|
||||||
|
GeyserMappingItem remap(Item item, GeyserMappingItem mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void populate() {
|
public static void populate() {
|
||||||
Map<Item, String> manualFallback = new HashMap<>();
|
Map<Item, String> legacyJavaOnly = new HashMap<>();
|
||||||
manualFallback.put(Items.ENDER_DRAGON_SPAWN_EGG, "minecraft:enderman_spawn_egg");
|
legacyJavaOnly.put(Items.MUSIC_DISC_RELIC, "minecraft:music_disc_wait");
|
||||||
manualFallback.put(Items.WITHER_SPAWN_EGG, "minecraft:wither_skeleton_spawn_egg");
|
legacyJavaOnly.put(Items.PITCHER_PLANT, "minecraft:chorus_flower");
|
||||||
manualFallback.put(Items.SNOW_GOLEM_SPAWN_EGG, "minecraft:polar_bear_spawn_egg");
|
legacyJavaOnly.put(Items.PITCHER_POD, "minecraft:beetroot");
|
||||||
manualFallback.put(Items.IRON_GOLEM_SPAWN_EGG, "minecraft:villager_spawn_egg");
|
legacyJavaOnly.put(Items.SNIFFER_EGG, "minecraft:sniffer_spawn_egg"); // the BlockItem of the sniffer egg block
|
||||||
|
|
||||||
Map<String, PaletteVersion> paletteVersions = new Object2ObjectOpenHashMap<>();
|
List<PaletteVersion> paletteVersions = new ArrayList<>(2);
|
||||||
paletteVersions.put("1_19_20", new PaletteVersion(Bedrock_v544.CODEC.getProtocolVersion(), manualFallback));
|
paletteVersions.add(new PaletteVersion("1_19_80", Bedrock_v582.CODEC.getProtocolVersion(), legacyJavaOnly, (item, mapping) -> {
|
||||||
paletteVersions.put("1_19_50", new PaletteVersion(Bedrock_v560.CODEC.getProtocolVersion(), manualFallback));
|
String id = item.javaIdentifier();
|
||||||
paletteVersions.put("1_19_60", new PaletteVersion(Bedrock_v567.CODEC.getProtocolVersion(), Collections.emptyMap()));
|
if (id.endsWith("pottery_sherd")) {
|
||||||
paletteVersions.put("1_19_70", new PaletteVersion(Bedrock_v575.CODEC.getProtocolVersion(), Collections.emptyMap()));
|
return mapping.withBedrockIdentifier(id.replace("sherd", "shard"));
|
||||||
paletteVersions.put("1_19_80", new PaletteVersion(Bedrock_v582.CODEC.getProtocolVersion(), Collections.emptyMap()));
|
} else if (id.endsWith("carpet") && !id.startsWith("minecraft:moss")) {
|
||||||
|
return mapping.withBedrockIdentifier("minecraft:carpet");
|
||||||
|
} else if (id.endsWith("coral")) {
|
||||||
|
return mapping.withBedrockIdentifier("minecraft:coral");
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapping;
|
||||||
|
}));
|
||||||
|
paletteVersions.add(new PaletteVersion("1_20_0", Bedrock_v589.CODEC.getProtocolVersion()));
|
||||||
|
|
||||||
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
|
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
|
||||||
|
|
||||||
|
@ -115,11 +131,11 @@ public class ItemRegistryPopulator {
|
||||||
boolean firstMappingsPass = true;
|
boolean firstMappingsPass = true;
|
||||||
|
|
||||||
/* Load item palette */
|
/* Load item palette */
|
||||||
for (Map.Entry<String, PaletteVersion> palette : paletteVersions.entrySet()) {
|
for (PaletteVersion palette : paletteVersions) {
|
||||||
TypeReference<List<PaletteItem>> paletteEntriesType = new TypeReference<>() {};
|
TypeReference<List<PaletteItem>> paletteEntriesType = new TypeReference<>() {};
|
||||||
|
|
||||||
List<PaletteItem> itemEntries;
|
List<PaletteItem> itemEntries;
|
||||||
try (InputStream stream = bootstrap.getResource(String.format("bedrock/runtime_item_states.%s.json", palette.getKey()))) {
|
try (InputStream stream = bootstrap.getResource(String.format("bedrock/runtime_item_states.%s.json", palette.version()))) {
|
||||||
itemEntries = GeyserImpl.JSON_MAPPER.readValue(stream, paletteEntriesType);
|
itemEntries = GeyserImpl.JSON_MAPPER.readValue(stream, paletteEntriesType);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new AssertionError("Unable to load Bedrock runtime item IDs", e);
|
throw new AssertionError("Unable to load Bedrock runtime item IDs", e);
|
||||||
|
@ -177,22 +193,16 @@ public class ItemRegistryPopulator {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
BlockMappings blockMappings = BlockRegistries.BLOCKS.forVersion(palette.getValue().protocolVersion());
|
BlockMappings blockMappings = BlockRegistries.BLOCKS.forVersion(palette.protocolVersion());
|
||||||
|
|
||||||
Set<Item> javaOnlyItems = new ObjectOpenHashSet<>();
|
Set<Item> javaOnlyItems = new ObjectOpenHashSet<>();
|
||||||
Collections.addAll(javaOnlyItems, Items.SPECTRAL_ARROW, Items.DEBUG_STICK,
|
Collections.addAll(javaOnlyItems, Items.SPECTRAL_ARROW, Items.DEBUG_STICK,
|
||||||
Items.KNOWLEDGE_BOOK, Items.TIPPED_ARROW, Items.BUNDLE);
|
Items.KNOWLEDGE_BOOK, Items.TIPPED_ARROW, Items.BUNDLE);
|
||||||
// these spawn eggs exist in 1.19.60+;
|
|
||||||
if (palette.getValue().protocolVersion() < Bedrock_v567.CODEC.getProtocolVersion()) {
|
|
||||||
Collections.addAll(javaOnlyItems, Items.IRON_GOLEM_SPAWN_EGG, Items.SNOW_GOLEM_SPAWN_EGG,
|
|
||||||
Items.WITHER_SPAWN_EGG, Items.ENDER_DRAGON_SPAWN_EGG);
|
|
||||||
}
|
|
||||||
javaOnlyItems.add(Items.DECORATED_POT);
|
|
||||||
if (!customItemsAllowed) {
|
if (!customItemsAllowed) {
|
||||||
javaOnlyItems.add(Items.FURNACE_MINECART);
|
javaOnlyItems.add(Items.FURNACE_MINECART);
|
||||||
}
|
}
|
||||||
// Java-only items for this version
|
// Java-only items for this version
|
||||||
javaOnlyItems.addAll(palette.getValue().additionalTranslatedItems().keySet());
|
javaOnlyItems.addAll(palette.javaOnlyItems().keySet());
|
||||||
|
|
||||||
Int2ObjectMap<String> customIdMappings = new Int2ObjectOpenHashMap<>();
|
Int2ObjectMap<String> customIdMappings = new Int2ObjectOpenHashMap<>();
|
||||||
Set<String> registeredItemNames = new ObjectOpenHashSet<>(); // This is used to check for duplicate item names
|
Set<String> registeredItemNames = new ObjectOpenHashSet<>(); // This is used to check for duplicate item names
|
||||||
|
@ -203,12 +213,12 @@ public class ItemRegistryPopulator {
|
||||||
throw new RuntimeException("Extra item in mappings? " + entry.getKey());
|
throw new RuntimeException("Extra item in mappings? " + entry.getKey());
|
||||||
}
|
}
|
||||||
GeyserMappingItem mappingItem;
|
GeyserMappingItem mappingItem;
|
||||||
String replacementItem = palette.getValue().additionalTranslatedItems().get(javaItem);
|
String replacementItem = palette.javaOnlyItems().get(javaItem);
|
||||||
if (replacementItem != null) {
|
if (replacementItem != null) {
|
||||||
mappingItem = items.get(replacementItem);
|
mappingItem = items.get(replacementItem); // java only item, a java id fallback has been provided
|
||||||
} else {
|
} else {
|
||||||
// This items has a mapping specifically for this version of the game
|
// check if any mapping changes need to be made on this version
|
||||||
mappingItem = entry.getValue();
|
mappingItem = palette.remapper().remap(javaItem, entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (customItemsAllowed && javaItem == Items.FURNACE_MINECART) {
|
if (customItemsAllowed && javaItem == Items.FURNACE_MINECART) {
|
||||||
|
@ -217,26 +227,10 @@ public class ItemRegistryPopulator {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String bedrockIdentifier;
|
String bedrockIdentifier = mappingItem.getBedrockIdentifier();
|
||||||
// 1.19.70+
|
|
||||||
if (palette.getValue().protocolVersion() >= Bedrock_v575.CODEC.getProtocolVersion() && mappingItem.getBedrockIdentifier().equals("minecraft:wool")) {
|
|
||||||
bedrockIdentifier = javaItem.javaIdentifier();
|
|
||||||
} else {
|
|
||||||
bedrockIdentifier = mappingItem.getBedrockIdentifier();
|
|
||||||
}
|
|
||||||
|
|
||||||
//1.19.80+
|
|
||||||
if (palette.getValue().protocolVersion >= Bedrock_v582.CODEC.getProtocolVersion()) {
|
|
||||||
if (mappingItem.getBedrockIdentifier().equals("minecraft:log") ||
|
|
||||||
mappingItem.getBedrockIdentifier().equals("minecraft:log2") ||
|
|
||||||
mappingItem.getBedrockIdentifier().equals("minecraft:fence")) {
|
|
||||||
bedrockIdentifier = javaItem.javaIdentifier();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemDefinition definition = definitions.get(bedrockIdentifier);
|
ItemDefinition definition = definitions.get(bedrockIdentifier);
|
||||||
if (definition == null) {
|
if (definition == null) {
|
||||||
throw new RuntimeException("Missing Bedrock ItemDefinition in mappings: " + bedrockIdentifier);
|
throw new RuntimeException("Missing Bedrock ItemDefinition in version " + palette.version() + " for mapping: " + mappingItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockDefinition bedrockBlock = null;
|
BlockDefinition bedrockBlock = null;
|
||||||
|
@ -430,7 +424,7 @@ public class ItemRegistryPopulator {
|
||||||
} else if (javaItem.javaIdentifier().startsWith("minecraft:music_disc_")) {
|
} else if (javaItem.javaIdentifier().startsWith("minecraft:music_disc_")) {
|
||||||
// The Java record level event uses the item ID as the "key" to play the record
|
// The Java record level event uses the item ID as the "key" to play the record
|
||||||
Registries.RECORDS.register(javaItem.javaId(), SoundEvent.valueOf("RECORD_" +
|
Registries.RECORDS.register(javaItem.javaId(), SoundEvent.valueOf("RECORD_" +
|
||||||
javaItem.javaIdentifier().replace("minecraft:music_disc_", "").toUpperCase(Locale.ENGLISH)));
|
mapping.getBedrockIdentifier().replace("minecraft:music_disc_", "").toUpperCase(Locale.ENGLISH)));
|
||||||
}
|
}
|
||||||
|
|
||||||
mappings.add(mapping);
|
mappings.add(mapping);
|
||||||
|
@ -521,7 +515,7 @@ public class ItemRegistryPopulator {
|
||||||
.customIdMappings(customIdMappings)
|
.customIdMappings(customIdMappings)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Registries.ITEMS.register(palette.getValue().protocolVersion(), itemMappings);
|
Registries.ITEMS.register(palette.protocolVersion(), itemMappings);
|
||||||
|
|
||||||
firstMappingsPass = false;
|
firstMappingsPass = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,12 +26,22 @@
|
||||||
package org.geysermc.geyser.registry.type;
|
package org.geysermc.geyser.registry.type;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import lombok.Data;
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.ToString;
|
||||||
|
import lombok.With;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents Geyser's own serialized item information before being processed per-version
|
* Represents Geyser's own serialized item information before being processed per-version
|
||||||
*/
|
*/
|
||||||
@Data
|
@ToString
|
||||||
|
@EqualsAndHashCode
|
||||||
|
@Getter
|
||||||
|
@With
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
public class GeyserMappingItem {
|
public class GeyserMappingItem {
|
||||||
@JsonProperty("bedrock_identifier") String bedrockIdentifier;
|
@JsonProperty("bedrock_identifier") String bedrockIdentifier;
|
||||||
@JsonProperty("bedrock_data") int bedrockData;
|
@JsonProperty("bedrock_data") int bedrockData;
|
||||||
|
|
|
@ -56,7 +56,11 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.Server
|
||||||
import com.github.steveice10.mc.protocol.packet.login.serverbound.ServerboundCustomQueryPacket;
|
import com.github.steveice10.mc.protocol.packet.login.serverbound.ServerboundCustomQueryPacket;
|
||||||
import com.github.steveice10.packetlib.BuiltinFlags;
|
import com.github.steveice10.packetlib.BuiltinFlags;
|
||||||
import com.github.steveice10.packetlib.Session;
|
import com.github.steveice10.packetlib.Session;
|
||||||
import com.github.steveice10.packetlib.event.session.*;
|
import com.github.steveice10.packetlib.event.session.ConnectedEvent;
|
||||||
|
import com.github.steveice10.packetlib.event.session.DisconnectedEvent;
|
||||||
|
import com.github.steveice10.packetlib.event.session.PacketErrorEvent;
|
||||||
|
import com.github.steveice10.packetlib.event.session.PacketSendingEvent;
|
||||||
|
import com.github.steveice10.packetlib.event.session.SessionAdapter;
|
||||||
import com.github.steveice10.packetlib.packet.Packet;
|
import com.github.steveice10.packetlib.packet.Packet;
|
||||||
import com.github.steveice10.packetlib.tcp.TcpClientSession;
|
import com.github.steveice10.packetlib.tcp.TcpClientSession;
|
||||||
import com.github.steveice10.packetlib.tcp.TcpSession;
|
import com.github.steveice10.packetlib.tcp.TcpSession;
|
||||||
|
@ -81,6 +85,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.checkerframework.common.value.qual.IntRange;
|
import org.checkerframework.common.value.qual.IntRange;
|
||||||
import org.cloudburstmc.math.vector.*;
|
import org.cloudburstmc.math.vector.*;
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.BedrockDisconnectReasons;
|
||||||
import org.cloudburstmc.protocol.bedrock.BedrockServerSession;
|
import org.cloudburstmc.protocol.bedrock.BedrockServerSession;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.*;
|
import org.cloudburstmc.protocol.bedrock.data.*;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData;
|
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData;
|
||||||
|
@ -93,7 +98,7 @@ import org.cloudburstmc.protocol.common.util.OptionalBoolean;
|
||||||
import org.geysermc.api.util.BedrockPlatform;
|
import org.geysermc.api.util.BedrockPlatform;
|
||||||
import org.geysermc.api.util.InputMode;
|
import org.geysermc.api.util.InputMode;
|
||||||
import org.geysermc.api.util.UiProfile;
|
import org.geysermc.api.util.UiProfile;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.cumulus.form.Form;
|
import org.geysermc.cumulus.form.Form;
|
||||||
import org.geysermc.cumulus.form.util.FormBuilder;
|
import org.geysermc.cumulus.form.util.FormBuilder;
|
||||||
import org.geysermc.floodgate.crypto.FloodgateCipher;
|
import org.geysermc.floodgate.crypto.FloodgateCipher;
|
||||||
|
@ -103,6 +108,7 @@ import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.connection.GeyserConnection;
|
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||||
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||||
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
|
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
|
||||||
|
import org.geysermc.geyser.api.event.bedrock.SessionLoginEvent;
|
||||||
import org.geysermc.geyser.api.network.AuthType;
|
import org.geysermc.geyser.api.network.AuthType;
|
||||||
import org.geysermc.geyser.api.network.RemoteServer;
|
import org.geysermc.geyser.api.network.RemoteServer;
|
||||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||||
|
@ -123,6 +129,7 @@ import org.geysermc.geyser.item.Items;
|
||||||
import org.geysermc.geyser.level.JavaDimension;
|
import org.geysermc.geyser.level.JavaDimension;
|
||||||
import org.geysermc.geyser.level.WorldManager;
|
import org.geysermc.geyser.level.WorldManager;
|
||||||
import org.geysermc.geyser.level.physics.CollisionManager;
|
import org.geysermc.geyser.level.physics.CollisionManager;
|
||||||
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
import org.geysermc.geyser.network.netty.LocalSession;
|
import org.geysermc.geyser.network.netty.LocalSession;
|
||||||
import org.geysermc.geyser.registry.Registries;
|
import org.geysermc.geyser.registry.Registries;
|
||||||
import org.geysermc.geyser.registry.type.BlockMappings;
|
import org.geysermc.geyser.registry.type.BlockMappings;
|
||||||
|
@ -877,6 +884,16 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||||
* After getting whatever credentials needed, we attempt to join the Java server.
|
* After getting whatever credentials needed, we attempt to join the Java server.
|
||||||
*/
|
*/
|
||||||
private void connectDownstream() {
|
private void connectDownstream() {
|
||||||
|
SessionLoginEvent loginEvent = new SessionLoginEvent(this, remoteServer);
|
||||||
|
GeyserImpl.getInstance().eventBus().fire(loginEvent);
|
||||||
|
if (loginEvent.isCancelled()) {
|
||||||
|
String disconnectReason = loginEvent.disconnectReason() == null ?
|
||||||
|
BedrockDisconnectReasons.DISCONNECTED : loginEvent.disconnectReason();
|
||||||
|
disconnect(disconnectReason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.remoteServer = loginEvent.remoteServer();
|
||||||
boolean floodgate = this.remoteServer.authType() == AuthType.FLOODGATE;
|
boolean floodgate = this.remoteServer.authType() == AuthType.FLOODGATE;
|
||||||
|
|
||||||
// Start ticking
|
// Start ticking
|
||||||
|
@ -1539,6 +1556,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||||
startGamePacket.setRewindHistorySize(0);
|
startGamePacket.setRewindHistorySize(0);
|
||||||
startGamePacket.setServerAuthoritativeBlockBreaking(false);
|
startGamePacket.setServerAuthoritativeBlockBreaking(false);
|
||||||
|
|
||||||
|
if (GameProtocol.isPre1_20(this)) {
|
||||||
|
startGamePacket.getExperiments().add(new ExperimentData("next_major_update", true));
|
||||||
|
startGamePacket.getExperiments().add(new ExperimentData("sniffer", true));
|
||||||
|
}
|
||||||
|
|
||||||
upstream.sendPacket(startGamePacket);
|
upstream.sendPacket(startGamePacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1942,8 +1964,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
EmotePacket packet = new EmotePacket();
|
EmotePacket packet = new EmotePacket();
|
||||||
packet.setEmoteId(emoteId);
|
|
||||||
packet.setRuntimeEntityId(entity.getGeyserId());
|
packet.setRuntimeEntityId(entity.getGeyserId());
|
||||||
|
packet.setXuid("");
|
||||||
|
packet.setPlatformId(""); // BDS sends empty
|
||||||
|
packet.setEmoteId(emoteId);
|
||||||
sendUpstreamPacket(packet);
|
sendUpstreamPacket(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ public class BookEditCache {
|
||||||
if ((System.currentTimeMillis() - lastBookUpdate) < 1000) {
|
if ((System.currentTimeMillis() - lastBookUpdate) < 1000) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Don't send the update if the player isn't not holding a book, shouldn't happen if we catch all interactions
|
// Don't send the update if the player is not holding a book, shouldn't happen if we catch all interactions
|
||||||
GeyserItemStack itemStack = session.getPlayerInventory().getItemInHand();
|
GeyserItemStack itemStack = session.getPlayerInventory().getItemInHand();
|
||||||
if (itemStack == null || itemStack.asItem() != Items.WRITABLE_BOOK) {
|
if (itemStack == null || itemStack.asItem() != Items.WRITABLE_BOOK) {
|
||||||
packet = null;
|
packet = null;
|
||||||
|
|
|
@ -64,6 +64,7 @@ public class TagCache {
|
||||||
private IntList foxFood;
|
private IntList foxFood;
|
||||||
private IntList piglinLoved;
|
private IntList piglinLoved;
|
||||||
private IntList smallFlowers;
|
private IntList smallFlowers;
|
||||||
|
private IntList snifferFood;
|
||||||
|
|
||||||
public TagCache() {
|
public TagCache() {
|
||||||
// Ensure all lists are non-null
|
// Ensure all lists are non-null
|
||||||
|
@ -101,6 +102,7 @@ public class TagCache {
|
||||||
this.foxFood = IntList.of(itemTags.get("minecraft:fox_food"));
|
this.foxFood = IntList.of(itemTags.get("minecraft:fox_food"));
|
||||||
this.piglinLoved = IntList.of(itemTags.get("minecraft:piglin_loved"));
|
this.piglinLoved = IntList.of(itemTags.get("minecraft:piglin_loved"));
|
||||||
this.smallFlowers = IntList.of(itemTags.get("minecraft:small_flowers"));
|
this.smallFlowers = IntList.of(itemTags.get("minecraft:small_flowers"));
|
||||||
|
this.snifferFood = load(itemTags.get("minecraft:sniffer_food"));
|
||||||
|
|
||||||
// Hack btw
|
// Hack btw
|
||||||
boolean emulatePost1_13Logic = itemTags.get("minecraft:signs").length > 1;
|
boolean emulatePost1_13Logic = itemTags.get("minecraft:signs").length > 1;
|
||||||
|
@ -137,6 +139,7 @@ public class TagCache {
|
||||||
this.foxFood = IntLists.emptyList();
|
this.foxFood = IntLists.emptyList();
|
||||||
this.piglinLoved = IntLists.emptyList();
|
this.piglinLoved = IntLists.emptyList();
|
||||||
this.smallFlowers = IntLists.emptyList();
|
this.smallFlowers = IntLists.emptyList();
|
||||||
|
this.snifferFood = IntLists.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAxolotlTemptItem(Item item) {
|
public boolean isAxolotlTemptItem(Item item) {
|
||||||
|
@ -167,6 +170,10 @@ public class TagCache {
|
||||||
return smallFlowers.contains(itemStack.getJavaId());
|
return smallFlowers.contains(itemStack.getJavaId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSnifferFood(Item item) {
|
||||||
|
return snifferFood.contains(item.javaId());
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isAxeEffective(BlockMapping blockMapping) {
|
public boolean isAxeEffective(BlockMapping blockMapping) {
|
||||||
return axeEffective.contains(blockMapping.getJavaBlockId());
|
return axeEffective.contains(blockMapping.getJavaBlockId());
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,10 @@ public final class WorldCache {
|
||||||
private int currentSequence;
|
private int currentSequence;
|
||||||
private final Object2IntMap<Vector3i> unverifiedPredictions = new Object2IntOpenHashMap<>(1);
|
private final Object2IntMap<Vector3i> unverifiedPredictions = new Object2IntOpenHashMap<>(1);
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private boolean editingSignOnFront;
|
||||||
|
|
||||||
public WorldCache(GeyserSession session) {
|
public WorldCache(GeyserSession session) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.scoreboard = new Scoreboard(session);
|
this.scoreboard = new Scoreboard(session);
|
||||||
|
|
|
@ -30,10 +30,9 @@ import org.geysermc.geyser.util.AssetUtils;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
@ -80,14 +79,14 @@ public final class ProvidedSkins {
|
||||||
.resolve(slim ? "slim" : "wide");
|
.resolve(slim ? "slim" : "wide");
|
||||||
String assetName = asset.substring(asset.lastIndexOf('/') + 1);
|
String assetName = asset.substring(asset.lastIndexOf('/') + 1);
|
||||||
|
|
||||||
File location = folder.resolve(assetName).toFile();
|
Path location = folder.resolve(assetName);
|
||||||
AssetUtils.addTask(!location.exists(), new AssetUtils.ClientJarTask("assets/minecraft/" + asset,
|
AssetUtils.addTask(!Files.exists(location), new AssetUtils.ClientJarTask("assets/minecraft/" + asset,
|
||||||
(stream) -> AssetUtils.saveFile(location, stream),
|
(stream) -> AssetUtils.saveFile(location, stream),
|
||||||
() -> {
|
() -> {
|
||||||
try {
|
try {
|
||||||
// TODO lazy initialize?
|
// TODO lazy initialize?
|
||||||
BufferedImage image;
|
BufferedImage image;
|
||||||
try (InputStream stream = new FileInputStream(location)) {
|
try (InputStream stream = Files.newInputStream(location)) {
|
||||||
image = ImageIO.read(stream);
|
image = ImageIO.read(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.geysermc.geyser.util.WebUtils;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -57,8 +58,8 @@ public class MinecraftLocale {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ensureEN_US() {
|
public static void ensureEN_US() {
|
||||||
File localeFile = getFile("en_us");
|
Path localeFile = getPath("en_us");
|
||||||
AssetUtils.addTask(!localeFile.exists(), new AssetUtils.ClientJarTask("assets/minecraft/lang/en_us.json",
|
AssetUtils.addTask(!Files.exists(localeFile), new AssetUtils.ClientJarTask("assets/minecraft/lang/en_us.json",
|
||||||
(stream) -> AssetUtils.saveFile(localeFile, stream),
|
(stream) -> AssetUtils.saveFile(localeFile, stream),
|
||||||
() -> {
|
() -> {
|
||||||
if ("en_us".equals(GeyserLocale.getDefaultLocale())) {
|
if ("en_us".equals(GeyserLocale.getDefaultLocale())) {
|
||||||
|
@ -106,10 +107,10 @@ public class MinecraftLocale {
|
||||||
if (locale.equals("en_us")) {
|
if (locale.equals("en_us")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
File localeFile = getFile(locale);
|
Path localeFile = getPath(locale);
|
||||||
|
|
||||||
// Check if we have already downloaded the locale file
|
// Check if we have already downloaded the locale file
|
||||||
if (localeFile.exists()) {
|
if (Files.exists(localeFile)) {
|
||||||
String curHash = byteArrayToHexString(FileUtils.calculateSHA1(localeFile));
|
String curHash = byteArrayToHexString(FileUtils.calculateSHA1(localeFile));
|
||||||
String targetHash = AssetUtils.getAsset("minecraft/lang/" + locale + ".json").getHash();
|
String targetHash = AssetUtils.getAsset("minecraft/lang/" + locale + ".json").getHash();
|
||||||
|
|
||||||
|
@ -130,8 +131,8 @@ public class MinecraftLocale {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static File getFile(String locale) {
|
private static Path getPath(String locale) {
|
||||||
return GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("locales/" + locale + ".json").toFile();
|
return GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("locales/" + locale + ".json");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -94,7 +94,7 @@ public abstract class InventoryTranslator {
|
||||||
put(ContainerType.LOOM, new LoomInventoryTranslator());
|
put(ContainerType.LOOM, new LoomInventoryTranslator());
|
||||||
put(ContainerType.MERCHANT, new MerchantInventoryTranslator());
|
put(ContainerType.MERCHANT, new MerchantInventoryTranslator());
|
||||||
put(ContainerType.SHULKER_BOX, new ShulkerInventoryTranslator());
|
put(ContainerType.SHULKER_BOX, new ShulkerInventoryTranslator());
|
||||||
put(ContainerType.LEGACY_SMITHING, new SmithingInventoryTranslator());
|
put(ContainerType.SMITHING, new SmithingInventoryTranslator());
|
||||||
put(ContainerType.STONECUTTER, new StonecutterInventoryTranslator());
|
put(ContainerType.STONECUTTER, new StonecutterInventoryTranslator());
|
||||||
|
|
||||||
/* Lectern */
|
/* Lectern */
|
||||||
|
@ -525,10 +525,28 @@ public abstract class InventoryTranslator {
|
||||||
|
|
||||||
int remainder = transferAction.getCount() % resultSize;
|
int remainder = transferAction.getCount() % resultSize;
|
||||||
int timesToCraft = transferAction.getCount() / resultSize;
|
int timesToCraft = transferAction.getCount() / resultSize;
|
||||||
for (int i = 0; i < timesToCraft; i++) {
|
|
||||||
plan.add(Click.LEFT, sourceSlot);
|
if (plan.getCursor().isEmpty()) {
|
||||||
plan.add(Click.LEFT, destSlot);
|
// No carried items - move to destination
|
||||||
|
for (int i = 0; i < timesToCraft; i++) {
|
||||||
|
plan.add(Click.LEFT, sourceSlot);
|
||||||
|
plan.add(Click.LEFT, destSlot);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GeyserItemStack cursor = session.getPlayerInventory().getCursor();
|
||||||
|
int tempSlot = findTempSlot(inventory, cursor, true, sourceSlot, destSlot);
|
||||||
|
if (tempSlot == -1) {
|
||||||
|
return rejectRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
plan.add(Click.LEFT, tempSlot); //place cursor into temp slot
|
||||||
|
for (int i = 0; i < timesToCraft; i++) {
|
||||||
|
plan.add(Click.LEFT, sourceSlot); //pick up source item
|
||||||
|
plan.add(Click.LEFT, destSlot); //place source item into dest slot
|
||||||
|
}
|
||||||
|
plan.add(Click.LEFT, tempSlot); //pick up original item
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remainder > 0) {
|
if (remainder > 0) {
|
||||||
plan.add(Click.LEFT, 0);
|
plan.add(Click.LEFT, 0);
|
||||||
for (int i = 0; i < remainder; i++) {
|
for (int i = 0; i < remainder; i++) {
|
||||||
|
|
|
@ -33,15 +33,16 @@ import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
|
||||||
|
|
||||||
public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslator {
|
public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||||
public SmithingInventoryTranslator() {
|
public SmithingInventoryTranslator() {
|
||||||
super(3, "minecraft:smithing_table", ContainerType.SMITHING_TABLE, UIInventoryUpdater.INSTANCE);
|
super(4, "minecraft:smithing_table", ContainerType.SMITHING_TABLE, UIInventoryUpdater.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int bedrockSlotToJava(ItemStackRequestSlotData slotInfoData) {
|
public int bedrockSlotToJava(ItemStackRequestSlotData slotInfoData) {
|
||||||
return switch (slotInfoData.getContainer()) {
|
return switch (slotInfoData.getContainer()) {
|
||||||
case SMITHING_TABLE_INPUT -> 0;
|
case SMITHING_TABLE_TEMPLATE -> 0;
|
||||||
case SMITHING_TABLE_MATERIAL -> 1;
|
case SMITHING_TABLE_INPUT -> 1;
|
||||||
case SMITHING_TABLE_RESULT, CREATED_OUTPUT -> 2;
|
case SMITHING_TABLE_MATERIAL -> 2;
|
||||||
|
case SMITHING_TABLE_RESULT, CREATED_OUTPUT -> 3;
|
||||||
default -> super.bedrockSlotToJava(slotInfoData);
|
default -> super.bedrockSlotToJava(slotInfoData);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -49,9 +50,10 @@ public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslato
|
||||||
@Override
|
@Override
|
||||||
public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
|
public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
|
||||||
return switch (slot) {
|
return switch (slot) {
|
||||||
case 0 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_INPUT, 51);
|
case 0 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_TEMPLATE, 53);
|
||||||
case 1 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_MATERIAL, 52);
|
case 1 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_INPUT, 51);
|
||||||
case 2 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_RESULT, 50);
|
case 2 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_MATERIAL, 52);
|
||||||
|
case 3 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_RESULT, 50);
|
||||||
default -> super.javaSlotToBedrockContainer(slot);
|
default -> super.javaSlotToBedrockContainer(slot);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -59,9 +61,10 @@ public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslato
|
||||||
@Override
|
@Override
|
||||||
public int javaSlotToBedrock(int slot) {
|
public int javaSlotToBedrock(int slot) {
|
||||||
return switch (slot) {
|
return switch (slot) {
|
||||||
case 0 -> 51;
|
case 0 -> 53;
|
||||||
case 1 -> 52;
|
case 1 -> 51;
|
||||||
case 2 -> 50;
|
case 2 -> 52;
|
||||||
|
case 3 -> 50;
|
||||||
default -> super.javaSlotToBedrock(slot);
|
default -> super.javaSlotToBedrock(slot);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,19 +62,20 @@ public class StonecutterInventoryTranslator extends AbstractBlockInventoryTransl
|
||||||
}
|
}
|
||||||
|
|
||||||
StonecutterContainer container = (StonecutterContainer) inventory;
|
StonecutterContainer container = (StonecutterContainer) inventory;
|
||||||
|
ItemStack javaOutput = craftingData.output();
|
||||||
int button = craftingData.buttonId();
|
int button = craftingData.buttonId();
|
||||||
|
|
||||||
// If we've already pressed the button with this item, no need to press it again!
|
// If we've already pressed the button with this item, no need to press it again!
|
||||||
if (container.getStonecutterButton() != button) {
|
if (container.getStonecutterButton() != button) {
|
||||||
ItemStack javaOutput = craftingData.output();
|
|
||||||
|
|
||||||
// Getting the index of the item in the Java stonecutter list
|
// Getting the index of the item in the Java stonecutter list
|
||||||
ServerboundContainerButtonClickPacket packet = new ServerboundContainerButtonClickPacket(inventory.getJavaId(), button);
|
ServerboundContainerButtonClickPacket packet = new ServerboundContainerButtonClickPacket(inventory.getJavaId(), button);
|
||||||
session.sendDownstreamPacket(packet);
|
session.sendDownstreamPacket(packet);
|
||||||
container.setStonecutterButton(button);
|
container.setStonecutterButton(button);
|
||||||
if (inventory.getItem(1).getJavaId() != javaOutput.getId()) {
|
}
|
||||||
// We don't know there is an output here, so we tell ourselves that there is
|
|
||||||
inventory.setItem(1, GeyserItemStack.from(javaOutput), session);
|
if (inventory.getItem(1).getJavaId() != javaOutput.getId()) {
|
||||||
}
|
// We don't know there is an output here, so we tell ourselves that there is
|
||||||
|
inventory.setItem(1, GeyserItemStack.from(javaOutput), session);
|
||||||
}
|
}
|
||||||
|
|
||||||
return translateRequest(session, inventory, request);
|
return translateRequest(session, inventory, request);
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* 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.translator.level.block.entity;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||||
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
|
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||||
|
import org.geysermc.geyser.item.Items;
|
||||||
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
|
import org.geysermc.geyser.registry.Registries;
|
||||||
|
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||||
|
|
||||||
|
@BlockEntity(type = BlockEntityType.BRUSHABLE_BLOCK)
|
||||||
|
public class BrushableBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
|
||||||
|
if (!(tag.remove("item") instanceof CompoundTag itemTag)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Tag hitDirection = tag.get("hit_direction");
|
||||||
|
if (hitDirection == null) {
|
||||||
|
// java server sends no direction when the item recedes back into the block (if player stops brushing)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String id = ((StringTag) itemTag.get("id")).getValue();
|
||||||
|
if (Items.AIR.javaIdentifier().equals(id)) {
|
||||||
|
return; // server sends air when the block contains nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemMapping mapping = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getMapping(id);
|
||||||
|
if (mapping == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NbtMapBuilder itemBuilder = NbtMap.builder()
|
||||||
|
.putString("Name", mapping.getBedrockIdentifier())
|
||||||
|
.putByte("Count", (byte) itemTag.get("Count").getValue());
|
||||||
|
|
||||||
|
builder.putCompound("item", itemBuilder.build());
|
||||||
|
// controls which side the item protrudes from
|
||||||
|
builder.putByte("brush_direction", ((Number) hitDirection.getValue()).byteValue());
|
||||||
|
// controls how much the item protrudes
|
||||||
|
builder.putInt("brush_count", BlockStateValues.getBrushProgress(blockState));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* 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.translator.level.block.entity;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||||
|
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||||
|
import org.cloudburstmc.nbt.NbtType;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@BlockEntity(type = BlockEntityType.DECORATED_POT)
|
||||||
|
public class DecoratedPotBlockEntityTranslator extends BlockEntityTranslator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
|
||||||
|
// exact same format
|
||||||
|
if (tag.get("sherds") instanceof ListTag sherds) {
|
||||||
|
List<String> translated = new ArrayList<>(4);
|
||||||
|
for (Tag sherd : sherds) {
|
||||||
|
translated.add(((StringTag) sherd).getValue());
|
||||||
|
}
|
||||||
|
builder.putList("sherds", NbtType.STRING, translated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* 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.translator.level.block.entity;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType;
|
||||||
|
import org.geysermc.geyser.util.SignUtils;
|
||||||
|
|
||||||
|
@BlockEntity(type = BlockEntityType.HANGING_SIGN)
|
||||||
|
public class HangingSignBlockEntityTranslator extends SignBlockEntityTranslator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int signWidthMax() {
|
||||||
|
return SignUtils.HANGING_SIGN_WIDTH_MAX; // Smaller than that for BlockEntityType.SIGN
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,13 +36,12 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
import org.geysermc.geyser.level.physics.Axis;
|
import org.geysermc.geyser.level.physics.Axis;
|
||||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||||
import org.geysermc.geyser.level.physics.CollisionManager;
|
import org.geysermc.geyser.level.physics.CollisionManager;
|
||||||
import org.geysermc.geyser.level.physics.Direction;
|
import org.geysermc.geyser.level.physics.Direction;
|
||||||
import org.geysermc.geyser.network.GameProtocol;
|
|
||||||
import org.geysermc.geyser.registry.Registries;
|
import org.geysermc.geyser.registry.Registries;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.session.cache.PistonCache;
|
import org.geysermc.geyser.session.cache.PistonCache;
|
||||||
|
@ -622,10 +621,6 @@ public class PistonBlockEntity {
|
||||||
Vector3i movement = getMovement();
|
Vector3i movement = getMovement();
|
||||||
attachedBlocks.forEach((blockPos, javaId) -> {
|
attachedBlocks.forEach((blockPos, javaId) -> {
|
||||||
blockPos = blockPos.add(movement);
|
blockPos = blockPos.add(movement);
|
||||||
if (!GameProtocol.supports1_19_50(session)) {
|
|
||||||
// Send a final block entity packet to detach blocks for clients older than 1.19.50
|
|
||||||
BlockEntityUtils.updateBlockEntity(session, buildMovingBlockTag(blockPos, javaId, Direction.DOWN.getUnitVector()), blockPos);
|
|
||||||
}
|
|
||||||
// Don't place blocks that collide with the player
|
// Don't place blocks that collide with the player
|
||||||
if (!SOLID_BOUNDING_BOX.checkIntersection(blockPos.toDouble(), session.getCollisionManager().getPlayerBoundingBox())) {
|
if (!SOLID_BOUNDING_BOX.checkIntersection(blockPos.toDouble(), session.getCollisionManager().getPlayerBoundingBox())) {
|
||||||
ChunkUtils.updateBlock(session, javaId, blockPos);
|
ChunkUtils.updateBlock(session, javaId, blockPos);
|
||||||
|
|
|
@ -27,12 +27,15 @@ package org.geysermc.geyser.translator.level.block.entity;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType;
|
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType;
|
||||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||||
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||||
|
import org.geysermc.geyser.text.ChatColor;
|
||||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||||
import org.geysermc.geyser.util.SignUtils;
|
import org.geysermc.geyser.util.SignUtils;
|
||||||
|
|
||||||
@BlockEntity(type = {BlockEntityType.SIGN, BlockEntityType.HANGING_SIGN})
|
@BlockEntity(type = BlockEntityType.SIGN)
|
||||||
public class SignBlockEntityTranslator extends BlockEntityTranslator {
|
public class SignBlockEntityTranslator extends BlockEntityTranslator {
|
||||||
/**
|
/**
|
||||||
* Maps a color stored in a sign's Color tag to its ARGB value.
|
* Maps a color stored in a sign's Color tag to its ARGB value.
|
||||||
|
@ -64,54 +67,80 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator {
|
||||||
return dyeColor | (255 << 24);
|
return dyeColor | (255 << 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int signWidthMax() {
|
||||||
|
return SignUtils.SIGN_WIDTH_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
|
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
|
||||||
StringBuilder signText = new StringBuilder();
|
builder.putCompound("FrontText", translateSide(tag.get("front_text")));
|
||||||
for (int i = 0; i < 4; i++) {
|
builder.putCompound("BackText", translateSide(tag.get("back_text")));
|
||||||
int currentLine = i + 1;
|
var waxed = tag.get("is_waxed");
|
||||||
String signLine = getOrDefault(tag.getValue().get("Text" + currentLine), "");
|
builder.putBoolean("IsWaxed", waxed != null && waxed.getValue() instanceof Number number && number.byteValue() != 0);
|
||||||
signLine = MessageTranslator.convertMessageLenient(signLine);
|
}
|
||||||
|
|
||||||
// Check the character width on the sign to ensure there is no overflow that is usually hidden
|
private NbtMap translateSide(Tag tag) {
|
||||||
// to Java Edition clients but will appear to Bedrock clients
|
if (!(tag instanceof CompoundTag signData)) {
|
||||||
int signWidth = 0;
|
return NbtMap.EMPTY;
|
||||||
StringBuilder finalSignLine = new StringBuilder();
|
}
|
||||||
boolean previousCharacterWasFormatting = false; // Color changes do not count for maximum width
|
NbtMapBuilder builder = NbtMap.builder();
|
||||||
for (char c : signLine.toCharArray()) {
|
|
||||||
if (c == '\u00a7') {
|
StringBuilder signText = new StringBuilder();
|
||||||
// Don't count this character
|
Tag messages = signData.get("messages");
|
||||||
previousCharacterWasFormatting = true;
|
if (messages instanceof ListTag listTag) {
|
||||||
} else if (previousCharacterWasFormatting) {
|
var it = listTag.iterator();
|
||||||
// Don't count this character either
|
while (it.hasNext()) {
|
||||||
previousCharacterWasFormatting = false;
|
String signLine = (String) it.next().getValue();
|
||||||
} else {
|
signLine = MessageTranslator.convertMessageLenient(signLine);
|
||||||
signWidth += SignUtils.getCharacterWidth(c);
|
|
||||||
|
// Check the character width on the sign to ensure there is no overflow that is usually hidden
|
||||||
|
// to Java Edition clients but will appear to Bedrock clients
|
||||||
|
int signWidth = 0;
|
||||||
|
StringBuilder finalSignLine = new StringBuilder();
|
||||||
|
boolean previousCharacterWasFormatting = false; // Color changes do not count for maximum width
|
||||||
|
for (char c : signLine.toCharArray()) {
|
||||||
|
if (c == ChatColor.ESCAPE) {
|
||||||
|
// Don't count this character
|
||||||
|
previousCharacterWasFormatting = true;
|
||||||
|
} else if (previousCharacterWasFormatting) {
|
||||||
|
// Don't count this character either
|
||||||
|
previousCharacterWasFormatting = false;
|
||||||
|
} else {
|
||||||
|
signWidth += SignUtils.getCharacterWidth(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signWidth <= signWidthMax()) {
|
||||||
|
finalSignLine.append(c);
|
||||||
|
} else {
|
||||||
|
// Adding the character would make Bedrock move to the next line - Java doesn't do that, so we do not want to
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo 1.20: update for hanging signs (smaller width). Currently OK because bedrock sees hanging signs as normal signs
|
signText.append(finalSignLine);
|
||||||
if (signWidth <= SignUtils.BEDROCK_CHARACTER_WIDTH_MAX) {
|
if (it.hasNext()) {
|
||||||
finalSignLine.append(c);
|
signText.append("\n");
|
||||||
} else {
|
|
||||||
// Adding the character would make Bedrock move to the next line - Java doesn't do that, so we do not want to
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
signText.append(finalSignLine);
|
// Trim extra newlines - this makes editing difficult if preserved because the cursor starts at the bottom,
|
||||||
signText.append("\n");
|
// Which can easily go over the screen
|
||||||
|
while (!signText.isEmpty() && signText.charAt(signText.length() - 1) == '\n') {
|
||||||
|
signText.deleteCharAt(signText.length() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.putString("Text", signText.toString());
|
builder.putString("Text", signText.toString());
|
||||||
|
|
||||||
// Java Edition 1.14 added the ability to change the text color of the whole sign using dye
|
// Java Edition 1.14 added the ability to change the text color of the whole sign using dye
|
||||||
Tag color = tag.get("Color");
|
Tag color = signData.get("color");
|
||||||
if (color != null) {
|
if (color != null) {
|
||||||
builder.putInt("SignTextColor", getBedrockSignColor(color.getValue().toString()));
|
builder.putInt("SignTextColor", getBedrockSignColor(color.getValue().toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Glowing text
|
// Glowing text
|
||||||
boolean isGlowing = getOrDefault(tag.getValue().get("GlowingText"), (byte) 0) != (byte) 0;
|
boolean isGlowing = getOrDefault(signData.get("has_glowing_text"), (byte) 0) != (byte) 0;
|
||||||
builder.putBoolean("IgnoreLighting", isGlowing);
|
builder.putBoolean("IgnoreLighting", isGlowing);
|
||||||
builder.putBoolean("TextIgnoreLegacyBugResolved", isGlowing); // ??? required
|
return builder.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.level.Serverb
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket;
|
||||||
import org.geysermc.geyser.network.GameProtocol;
|
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.Translator;
|
import org.geysermc.geyser.translator.protocol.Translator;
|
||||||
|
@ -44,16 +43,12 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator<BlockEnti
|
||||||
public void translate(GeyserSession session, BlockEntityDataPacket packet) {
|
public void translate(GeyserSession session, BlockEntityDataPacket packet) {
|
||||||
NbtMap tag = packet.getData();
|
NbtMap tag = packet.getData();
|
||||||
String id = tag.getString("id");
|
String id = tag.getString("id");
|
||||||
if (id.equals("Sign")) {
|
if (id.endsWith("Sign")) {
|
||||||
String text;
|
// Hanging signs are narrower
|
||||||
if (GameProtocol.supports1_19_80(session)) {
|
int widthMax = SignUtils.getSignWidthMax(id.startsWith("Hanging"));
|
||||||
// The other side is called... you guessed it... BackText
|
|
||||||
text = tag.getCompound("FrontText")
|
String text = MessageTranslator.convertToPlainText(
|
||||||
.getString("Text");
|
tag.getCompound(session.getWorldCache().isEditingSignOnFront() ? "FrontText" : "BackText").getString("Text"));
|
||||||
} else {
|
|
||||||
text = tag.getString("Text");
|
|
||||||
}
|
|
||||||
text = MessageTranslator.convertToPlainText(text);
|
|
||||||
// Note: as of 1.18.30, only one packet is sent from Bedrock when the sign is finished.
|
// Note: as of 1.18.30, only one packet is sent from Bedrock when the sign is finished.
|
||||||
// Previous versions did not have this behavior.
|
// Previous versions did not have this behavior.
|
||||||
StringBuilder newMessage = new StringBuilder();
|
StringBuilder newMessage = new StringBuilder();
|
||||||
|
@ -68,13 +63,10 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator<BlockEnti
|
||||||
for (char character : text.toCharArray()) {
|
for (char character : text.toCharArray()) {
|
||||||
widthCount += SignUtils.getCharacterWidth(character);
|
widthCount += SignUtils.getCharacterWidth(character);
|
||||||
|
|
||||||
// todo 1.20: update for hanging signs (smaller width). Currently bedrock thinks hanging signs are normal,
|
|
||||||
// so it thinks hanging signs have more width than they actually do. Seems like JE just truncates it.
|
|
||||||
|
|
||||||
// If we get a return in Bedrock, or go over the character width max, that signals to use the next line.
|
// If we get a return in Bedrock, or go over the character width max, that signals to use the next line.
|
||||||
if (character == '\n' || widthCount > SignUtils.JAVA_CHARACTER_WIDTH_MAX) {
|
if (character == '\n' || widthCount > widthMax) {
|
||||||
// We need to apply some more logic if we went over the character width max
|
// We need to apply some more logic if we went over the character width max
|
||||||
boolean wentOverMax = widthCount > SignUtils.JAVA_CHARACTER_WIDTH_MAX && character != '\n';
|
boolean wentOverMax = widthCount > widthMax && character != '\n';
|
||||||
widthCount = 0;
|
widthCount = 0;
|
||||||
// Saves if we're moving a word to the next line
|
// Saves if we're moving a word to the next line
|
||||||
String word = null;
|
String word = null;
|
||||||
|
@ -115,7 +107,7 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator<BlockEnti
|
||||||
// Put the final line on since it isn't done in the for loop
|
// Put the final line on since it isn't done in the for loop
|
||||||
if (iterator < lines.length) lines[iterator] = newMessage.toString();
|
if (iterator < lines.length) lines[iterator] = newMessage.toString();
|
||||||
Vector3i pos = Vector3i.from(tag.getInt("x"), tag.getInt("y"), tag.getInt("z"));
|
Vector3i pos = Vector3i.from(tag.getInt("x"), tag.getInt("y"), tag.getInt("z"));
|
||||||
ServerboundSignUpdatePacket signUpdatePacket = new ServerboundSignUpdatePacket(pos, lines);
|
ServerboundSignUpdatePacket signUpdatePacket = new ServerboundSignUpdatePacket(pos, lines, session.getWorldCache().isEditingSignOnFront());
|
||||||
session.sendDownstreamPacket(signUpdatePacket);
|
session.sendDownstreamPacket(signUpdatePacket);
|
||||||
|
|
||||||
} else if (id.equals("JigsawBlock")) {
|
} else if (id.equals("JigsawBlock")) {
|
||||||
|
|
|
@ -33,12 +33,12 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.BookEditPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.BookEditPacket;
|
||||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||||
|
import org.geysermc.geyser.item.type.WrittenBookItem;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.Translator;
|
import org.geysermc.geyser.translator.protocol.Translator;
|
||||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
@ -46,12 +46,10 @@ import java.util.List;
|
||||||
|
|
||||||
@Translator(packet = BookEditPacket.class)
|
@Translator(packet = BookEditPacket.class)
|
||||||
public class BedrockBookEditTranslator extends PacketTranslator<BookEditPacket> {
|
public class BedrockBookEditTranslator extends PacketTranslator<BookEditPacket> {
|
||||||
private static final int MAXIMUM_PAGE_LENGTH = 8192 * 4;
|
|
||||||
private static final int MAXIMUM_TITLE_LENGTH = 128 * 4;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(GeyserSession session, BookEditPacket packet) {
|
public void translate(GeyserSession session, BookEditPacket packet) {
|
||||||
if (packet.getText() != null && !packet.getText().isEmpty() && packet.getText().getBytes(StandardCharsets.UTF_8).length > MAXIMUM_PAGE_LENGTH) {
|
if (packet.getText() != null && !packet.getText().isEmpty() && packet.getText().length() > WrittenBookItem.MAXIMUM_PAGE_EDIT_LENGTH) {
|
||||||
session.getGeyser().getLogger().warning("Page length greater than server allowed!");
|
session.getGeyser().getLogger().warning("Page length greater than server allowed!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -63,6 +61,10 @@ public class BedrockBookEditTranslator extends PacketTranslator<BookEditPacket>
|
||||||
List<Tag> pages = tag.contains("pages") ? new LinkedList<>(((ListTag) tag.get("pages")).getValue()) : new LinkedList<>();
|
List<Tag> pages = tag.contains("pages") ? new LinkedList<>(((ListTag) tag.get("pages")).getValue()) : new LinkedList<>();
|
||||||
|
|
||||||
int page = packet.getPageNumber();
|
int page = packet.getPageNumber();
|
||||||
|
if (page < 0 || WrittenBookItem.MAXIMUM_PAGE_COUNT <= page) {
|
||||||
|
session.getGeyser().getLogger().warning("Edited page is out of acceptable bounds!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
switch (packet.getAction()) {
|
switch (packet.getAction()) {
|
||||||
case ADD_PAGE: {
|
case ADD_PAGE: {
|
||||||
// Add empty pages in between
|
// Add empty pages in between
|
||||||
|
@ -129,7 +131,7 @@ public class BedrockBookEditTranslator extends PacketTranslator<BookEditPacket>
|
||||||
if (packet.getAction() == BookEditPacket.Action.SIGN_BOOK) {
|
if (packet.getAction() == BookEditPacket.Action.SIGN_BOOK) {
|
||||||
// Add title to packet so the server knows we're signing
|
// Add title to packet so the server knows we're signing
|
||||||
title = MessageTranslator.convertToPlainText(packet.getTitle());
|
title = MessageTranslator.convertToPlainText(packet.getTitle());
|
||||||
if (title.getBytes(StandardCharsets.UTF_8).length > MAXIMUM_TITLE_LENGTH) {
|
if (title.length() > WrittenBookItem.MAXIMUM_TITLE_LENGTH) {
|
||||||
session.getGeyser().getLogger().warning("Book title larger than server allows!");
|
session.getGeyser().getLogger().warning("Book title larger than server allows!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,10 +26,9 @@
|
||||||
package org.geysermc.geyser.translator.protocol.bedrock;
|
package org.geysermc.geyser.translator.protocol.bedrock;
|
||||||
|
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.CommandRequestPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.CommandRequestPacket;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.text.ChatColor;
|
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.Translator;
|
import org.geysermc.geyser.translator.protocol.Translator;
|
||||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||||
|
@ -46,7 +45,9 @@ public class BedrockCommandRequestTranslator extends PacketTranslator<CommandReq
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
session.sendCommand(command.substring(1));
|
// running commands via Bedrock's command select menu adds a trailing whitespace which Java doesn't like
|
||||||
|
// https://github.com/GeyserMC/Geyser/issues/3877
|
||||||
|
session.sendCommand(command.substring(1).stripTrailing());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue