mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-03-23 23:39:29 +01:00
Merge remote-tracking branch 'origin/master' into feature/floodgate-merge
# Conflicts: # bootstrap/spigot/base/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java # core/src/main/java/org/geysermc/geyser/GeyserImpl.java # core/src/main/java/org/geysermc/geyser/network/netty/LocalSession.java # core/src/main/java/org/geysermc/geyser/session/GeyserSession.java # core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCustomPayloadTranslator.java # gradle.properties # gradle/libs.versions.toml
This commit is contained in:
commit
79a2b22541
387 changed files with 57141 additions and 34464 deletions
.github/ISSUE_TEMPLATE
README.mdapi/src/main/java/org/geysermc/geyser/api
block/custom/nonvanilla
connection
entity/type/player
event
bedrock
connection
java
item/custom
bootstrap
bungeecord/base/src/main/java/org/geysermc/geyser/platform/bungeecord
GeyserBungeeCompressionDisabler.javaGeyserBungeeInjector.javaGeyserBungeePingPassthrough.javaGeyserBungeePlugin.java
mod
spigot/base
build.gradle.kts
src/main/java/org/geysermc/geyser/platform/spigot
standalone
build.gradle.kts
src/main
velocity/base
build.gradle.kts
src/main/java/org/geysermc/geyser/platform/velocity
viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy
build-logic/src/main/kotlin
core/src/main/java/org/geysermc/geyser
Constants.javaGeyserImpl.java
command
entity
EntityDefinition.javaEntityDefinitions.javaGeyserDirtyMetadata.java
attribute
type
BoatEntity.javaChestBoatEntity.javaEnderEyeEntity.javaEntity.javaFireworkEntity.javaItemFrameEntity.javaLivingEntity.javaMinecartEntity.javaTextDisplayEntity.javaThrowableItemEntity.javaThrownPotionEntity.javaTickable.java
living
AgeableWaterEntity.javaArmorStandEntity.javaDolphinEntity.javaSquidEntity.java
animal
AnimalEntity.javaArmadilloEntity.javaAxolotlEntity.javaBeeEntity.javaChickenEntity.javaCowEntity.javaFoxEntity.javaFrogEntity.javaGoatEntity.javaHoglinEntity.javaOcelotEntity.javaPandaEntity.javaPigEntity.javaPolarBearEntity.javaRabbitEntity.javaSheepEntity.javaSnifferEntity.javaStriderEntity.javaTurtleEntity.java
horse
tameable
monster
player
vehicle
event/type
extension
impl/camera
inventory
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
@ -1,11 +1,12 @@
|
|||
name: Bug report
|
||||
description: Create a report to help us improve
|
||||
type: Bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report for Geyser! Fill out the following form to your best ability to help us fix the problem.
|
||||
Only use this if you're absolutely sure that you found a bug and can reproduce it. For anything else, use: [our Discord server](https://discord.gg/geysermc), [the FAQ](https://github.com/GeyserMC/Geyser/wiki/FAQ) or the [Common Issues](https://github.com/GeyserMC/Geyser/wiki/Common-Issues).
|
||||
Only use this if you're absolutely sure that you found a bug and can reproduce it. For anything else, use: [our Discord server](https://discord.gg/geysermc), [the FAQ](https://geysermc.org/wiki/geyser/faq) or the [Common Issues](https://geysermc.org/wiki/geyser/common-issues).
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
|
|
5
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
5
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
|
@ -1,12 +1,13 @@
|
|||
name: Feature request
|
||||
description: Suggest an idea for this project
|
||||
labels: "Feature Request"
|
||||
type: Feature
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this feature request for Geyser! Please fill out the following form to your best ability to help us understand your feature request and significantly improve the chance of getting added.
|
||||
For anything else than a feature request, use: [our Discord server](https://discord.gg/geysermc), [the FAQ](https://github.com/GeyserMC/Geyser/wiki/FAQ) or [the Common Issues](https://github.com/GeyserMC/Geyser/wiki/Common-Issues).
|
||||
For anything else than a feature request, use: [our Discord server](https://discord.gg/geysermc), [the FAQ](https://geysermc.org/wiki/geyser/faq) or the [Common Issues](https://geysermc.org/wiki/geyser/common-issues).
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What feature do you want to see added?
|
||||
|
@ -18,4 +19,4 @@ body:
|
|||
label: Are there any alternatives?
|
||||
description: List any alternatives you might have tried
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
|
|
|
@ -15,7 +15,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!
|
||||
|
||||
## Supported Versions
|
||||
Geyser is currently supporting Minecraft Bedrock 1.20.80 - 1.21.30 and Minecraft Java 1.21/1.21.1. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/).
|
||||
Geyser is currently supporting Minecraft Bedrock 1.21.40 - 1.21.60 and Minecraft Java 1.21.4. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/).
|
||||
|
||||
## Setting Up
|
||||
Take a look [here](https://geysermc.org/wiki/geyser/setup/) for how to set up Geyser.
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package org.geysermc.geyser.api.block.custom.nonvanilla;
|
||||
|
||||
import org.checkerframework.checker.index.qual.NonNegative;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
public record JavaBlockItem(@NonNull String identifier, @NonNegative int javaId, @NonNegative int stackSize) {
|
||||
}
|
|
@ -70,7 +70,7 @@ public interface JavaBlockState {
|
|||
@Nullable String pistonBehavior();
|
||||
|
||||
/**
|
||||
* Gets whether the block state has block entity
|
||||
* Gets whether the block state has a block entity
|
||||
*
|
||||
* @return whether the block state has block entity
|
||||
* @deprecated Does not have an effect. If you were using this to
|
||||
|
|
|
@ -70,6 +70,11 @@ public interface GeyserConnection extends Connection, CommandSource {
|
|||
*/
|
||||
void closeForm();
|
||||
|
||||
/**
|
||||
* Gets the Bedrock protocol version of the player.
|
||||
*/
|
||||
int protocolVersion();
|
||||
|
||||
/**
|
||||
* @param javaId the Java entity ID to look up.
|
||||
* @return a {@link GeyserEntity} if present in this connection's entity tracker.
|
||||
|
|
|
@ -31,9 +31,9 @@ import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
|||
public interface GeyserPlayerEntity extends GeyserEntity {
|
||||
|
||||
/**
|
||||
* Gets the position of the player.
|
||||
* Gets the position of the player, as it is known to the Java server.
|
||||
*
|
||||
* @return the position of the player.
|
||||
* @return the player's position
|
||||
*/
|
||||
Vector3f position();
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ public class SessionDisconnectEvent extends ConnectionEvent {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the disconnect reason, thereby overriding th original reason.
|
||||
* Sets the disconnect message shown to the Bedrock client.
|
||||
*
|
||||
* @param disconnectReason the reason for the disconnect
|
||||
*/
|
||||
|
|
|
@ -33,10 +33,10 @@ import org.geysermc.event.Event;
|
|||
import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* Called whenever Geyser gets pinged
|
||||
* Called whenever Geyser gets pinged by a Bedrock client.
|
||||
* <p>
|
||||
* This event allows you to modify/obtain the MOTD, maximum player count, and current number of players online,
|
||||
* Geyser will reply to the client with what was given.
|
||||
* This event allows you to modify/obtain the MOTD, maximum player count, and current number of players online.
|
||||
* Geyser will reply to the client with the information provided in this event.
|
||||
*/
|
||||
public interface GeyserBedrockPingEvent extends Event {
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ import java.util.Set;
|
|||
* <br>
|
||||
* This event is mapped to the existence of Brigadier on the server.
|
||||
*/
|
||||
public class ServerDefineCommandsEvent extends ConnectionEvent implements Cancellable {
|
||||
public final class ServerDefineCommandsEvent extends ConnectionEvent implements Cancellable {
|
||||
private final Set<? extends CommandInfo> commands;
|
||||
private boolean cancelled;
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ import java.util.Map;
|
|||
* Fired when the Java server sends a transfer request to a different Java server.
|
||||
* Geyser Extensions can listen to this event and set a target server ip/port for Bedrock players to be transferred to.
|
||||
*/
|
||||
public class ServerTransferEvent extends ConnectionEvent {
|
||||
public final class ServerTransferEvent extends ConnectionEvent {
|
||||
|
||||
private final String host;
|
||||
private final int port;
|
||||
|
|
|
@ -80,10 +80,9 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
|||
@Nullable String toolType();
|
||||
|
||||
/**
|
||||
* Gets the tool tier of the item.
|
||||
*
|
||||
* @return the tool tier of the item
|
||||
* @deprecated no longer used
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
@Nullable String toolTier();
|
||||
|
||||
/**
|
||||
|
@ -108,10 +107,9 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
|||
@Nullable String translationString();
|
||||
|
||||
/**
|
||||
* Gets the repair materials of the item.
|
||||
*
|
||||
* @return the repair materials of the item
|
||||
* @deprecated No longer used.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
@Nullable Set<String> repairMaterials();
|
||||
|
||||
/**
|
||||
|
|
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.platform.bungeecord;
|
|||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import net.md_5.bungee.netty.LengthPrependerAndCompressor;
|
||||
import net.md_5.bungee.protocol.packet.LoginSuccess;
|
||||
import net.md_5.bungee.protocol.packet.SetCompression;
|
||||
|
||||
|
@ -40,8 +41,9 @@ public class GeyserBungeeCompressionDisabler extends ChannelOutboundHandlerAdapt
|
|||
// Fixes https://github.com/GeyserMC/Geyser/issues/4281
|
||||
// The server may send a LoginDisconnect packet after compression is set.
|
||||
if (!compressionDisabled) {
|
||||
if (ctx.pipeline().get("compress") != null) {
|
||||
ctx.pipeline().remove("compress");
|
||||
LengthPrependerAndCompressor compressor = ctx.pipeline().get(LengthPrependerAndCompressor.class);
|
||||
if (compressor.isCompress()) {
|
||||
compressor.setCompress(false);
|
||||
compressionDisabled = true;
|
||||
}
|
||||
if (ctx.pipeline().get("decompress") != null) {
|
||||
|
|
|
@ -73,6 +73,14 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
|
|||
throw new UnsupportedOperationException("Geyser does not currently support multiple listeners with injection! " +
|
||||
"Please reach out to us on our Discord at https://discord.gg/GeyserMC so we can hear feedback on your setup.");
|
||||
}
|
||||
|
||||
// TODO remove
|
||||
try {
|
||||
ProxyServer.class.getMethod("unsafe");
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new UnsupportedOperationException("You're using an outdated version of BungeeCord - please update. Thank you!");
|
||||
}
|
||||
|
||||
ListenerInfo listenerInfo = proxy.getConfig().getListeners().stream().findFirst().orElseThrow(IllegalStateException::new);
|
||||
|
||||
Class<? extends ProxyServer> proxyClass = proxy.getClass();
|
||||
|
@ -138,7 +146,7 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
|
|||
if (channelInitializer == null) {
|
||||
// Proxy has finished initializing; we can safely grab this variable without fear of plugins modifying it
|
||||
// (Older versions of ViaVersion replace this to inject)
|
||||
channelInitializer = PipelineUtils.SERVER_CHILD;
|
||||
channelInitializer = proxy.unsafe().getFrontendChannelInitializer().getChannelInitializer();
|
||||
}
|
||||
initChannel.invoke(channelInitializer, ch);
|
||||
|
||||
|
|
|
@ -196,6 +196,21 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTransferred() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<byte[]> retrieveCookie(String s) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<byte[]> sendData(String s, byte[] bytes) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Unsafe unsafe() {
|
||||
throw new UnsupportedOperationException();
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.geysermc.geyser.command.CommandSourceConverter;
|
|||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
import org.geysermc.geyser.platform.bungeecord.command.BungeeCommandSource;
|
||||
|
@ -60,6 +61,7 @@ import java.net.SocketAddress;
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -82,18 +84,19 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||
public void onGeyserInitialize() {
|
||||
GeyserLocale.init(this);
|
||||
|
||||
// Copied from ViaVersion.
|
||||
// https://github.com/ViaVersion/ViaVersion/blob/b8072aad86695cc8ec6f5e4103e43baf3abf6cc5/bungee/src/main/java/us/myles/ViaVersion/BungeePlugin.java#L43
|
||||
try {
|
||||
ProtocolConstants.class.getField("MINECRAFT_1_21");
|
||||
} catch (NoSuchFieldException e) {
|
||||
geyserLogger.error(" / \\");
|
||||
geyserLogger.error(" / \\");
|
||||
geyserLogger.error(" / | \\");
|
||||
geyserLogger.error(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", getProxy().getName()));
|
||||
geyserLogger.error(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps"));
|
||||
geyserLogger.error(" / o \\");
|
||||
geyserLogger.error("/_____________\\");
|
||||
List<Integer> supportedProtocols = ProtocolConstants.SUPPORTED_VERSION_IDS;
|
||||
if (!supportedProtocols.contains(GameProtocol.getJavaProtocolVersion())) {
|
||||
geyserLogger.error(" / \\");
|
||||
geyserLogger.error(" / \\");
|
||||
geyserLogger.error(" / | \\");
|
||||
geyserLogger.error(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", getProxy().getName()));
|
||||
geyserLogger.error(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps"));
|
||||
geyserLogger.error(" / o \\");
|
||||
geyserLogger.error("/_____________\\");
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
geyserLogger.warning("Unable to check the versions supported by this proxy! " + e.getMessage());
|
||||
}
|
||||
|
||||
if (!this.loadConfig()) {
|
||||
|
|
|
@ -37,6 +37,7 @@ dependencies {
|
|||
|
||||
modImplementation(libs.cloud.fabric)
|
||||
include(libs.cloud.fabric)
|
||||
include(libs.fabric.permissions.api)
|
||||
}
|
||||
|
||||
tasks.withType<Jar> {
|
||||
|
@ -45,7 +46,6 @@ tasks.withType<Jar> {
|
|||
|
||||
relocate("org.cloudburstmc.netty")
|
||||
relocate("org.cloudburstmc.protocol")
|
||||
relocate("com.github.steveice10.mc.auth")
|
||||
|
||||
tasks {
|
||||
remapJar {
|
||||
|
|
|
@ -32,7 +32,7 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
|||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.command.CommandSourceConverter;
|
||||
|
@ -80,7 +80,7 @@ public class GeyserFabricBootstrap extends GeyserModBootstrap implements ModInit
|
|||
var sourceConverter = CommandSourceConverter.layered(
|
||||
CommandSourceStack.class,
|
||||
id -> getServer().getPlayerList().getPlayer(id),
|
||||
Player::createCommandSourceStack,
|
||||
ServerPlayer::createCommandSourceStack,
|
||||
() -> getServer().createCommandSourceStack(), // NPE if method reference is used, since server is not available yet
|
||||
ModCommandSource::new
|
||||
);
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
"geyser.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.15.11",
|
||||
"fabric": "*",
|
||||
"minecraft": ">=1.21"
|
||||
"fabricloader": ">=0.16.7",
|
||||
"fabric-api": "*",
|
||||
"minecraft": ">=1.21.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,9 @@ architectury {
|
|||
provided("org.cloudburstmc.math", "api")
|
||||
provided("com.google.errorprone", "error_prone_annotations")
|
||||
|
||||
// Jackson shipped by Minecraft is too old, so we shade & relocate our newer version
|
||||
relocate("com.fasterxml.jackson")
|
||||
|
||||
val includeTransitive: Configuration = configurations.getByName("includeTransitive")
|
||||
|
||||
dependencies {
|
||||
|
@ -31,6 +34,12 @@ dependencies {
|
|||
}
|
||||
shadow(projects.core) { isTransitive = false }
|
||||
|
||||
// Minecraft (1.21.2+) includes jackson. But an old version!
|
||||
shadow(libs.jackson.core) { isTransitive = false }
|
||||
shadow(libs.jackson.databind) { isTransitive = false }
|
||||
shadow(libs.jackson.dataformat.yaml) { isTransitive = false }
|
||||
shadow(libs.jackson.annotations) { isTransitive = false }
|
||||
|
||||
// Let's shade in our own api
|
||||
shadow(projects.api) { isTransitive = false }
|
||||
|
||||
|
@ -53,6 +62,11 @@ tasks {
|
|||
remapModrinthJar {
|
||||
archiveBaseName.set("geyser-neoforge")
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
// Without this, jackson's service files are not relocated
|
||||
mergeServiceFiles()
|
||||
}
|
||||
}
|
||||
|
||||
modrinth {
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
package org.geysermc.geyser.platform.neoforge;
|
||||
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.neoforged.bus.api.EventPriority;
|
||||
import net.neoforged.fml.ModContainer;
|
||||
import net.neoforged.fml.common.Mod;
|
||||
|
@ -72,7 +72,7 @@ public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
|
|||
var sourceConverter = CommandSourceConverter.layered(
|
||||
CommandSourceStack.class,
|
||||
id -> getServer().getPlayerList().getPlayer(id),
|
||||
Player::createCommandSourceStack,
|
||||
ServerPlayer::createCommandSourceStack,
|
||||
() -> getServer().createCommandSourceStack(),
|
||||
ModCommandSource::new
|
||||
);
|
||||
|
@ -104,7 +104,9 @@ public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
|
|||
}
|
||||
|
||||
private void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) {
|
||||
GeyserModUpdateListener.onPlayReady(event.getEntity());
|
||||
if (event.getEntity() instanceof ServerPlayer player) {
|
||||
GeyserModUpdateListener.onPlayReady(player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -28,8 +28,8 @@ package org.geysermc.geyser.platform.mod;
|
|||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import net.minecraft.network.protocol.login.ClientboundGameProfilePacket;
|
||||
import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket;
|
||||
import net.minecraft.network.protocol.login.ClientboundLoginFinishedPacket;
|
||||
|
||||
/**
|
||||
* Disables the compression packet (and the compression handlers from being added to the pipeline) for Geyser clients
|
||||
|
@ -45,7 +45,7 @@ public class GeyserModCompressionDisabler extends ChannelOutboundHandlerAdapter
|
|||
Class<?> msgClass = msg.getClass();
|
||||
// Don't let any compression packet get through
|
||||
if (!ClientboundLoginCompressionPacket.class.isAssignableFrom(msgClass)) {
|
||||
if (ClientboundGameProfilePacket.class.isAssignableFrom(msgClass)) {
|
||||
if (ClientboundLoginFinishedPacket.class.isAssignableFrom(msgClass)) {
|
||||
|
||||
// We're past the point that a compression packet can be sent, so we can safely yeet ourselves away
|
||||
ctx.channel().pipeline().remove(this);
|
||||
|
|
|
@ -25,13 +25,13 @@
|
|||
|
||||
package org.geysermc.geyser.platform.mod;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.geysermc.geyser.Permissions;
|
||||
import org.geysermc.geyser.platform.mod.command.ModCommandSource;
|
||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||
|
||||
public final class GeyserModUpdateListener {
|
||||
public static void onPlayReady(Player player) {
|
||||
public static void onPlayReady(ServerPlayer player) {
|
||||
// Should be creating this in the supplier, but we need it for the permission check.
|
||||
// Not a big deal currently because ModCommandSource doesn't load locale, so don't need to try to wait for it.
|
||||
ModCommandSource source = new ModCommandSource(player.createCommandSourceStack());
|
||||
|
|
|
@ -28,36 +28,24 @@ package org.geysermc.geyser.platform.mod.world;
|
|||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BannerBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BannerPatternLayers;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.DecoratedPotBlockEntity;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.geysermc.geyser.level.GeyserWorldManager;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.MinecraftKey;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.BannerPatternLayer;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class GeyserModWorldManager extends GeyserWorldManager {
|
||||
|
@ -84,16 +72,17 @@ public class GeyserModWorldManager extends GeyserWorldManager {
|
|||
}
|
||||
|
||||
Level level = player.level();
|
||||
if (y < level.getMinBuildHeight()) {
|
||||
if (y < level.getMinY()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ChunkAccess chunk = level.getChunkSource().getChunk(x >> 4, z >> 4, ChunkStatus.FULL, false);
|
||||
// Only loads active chunks, and doesn't delegate to main thread
|
||||
ChunkAccess chunk = ((ServerChunkCache) level.getChunkSource()).chunkMap.getChunkToSend(ChunkPos.asLong(x >> 4, z >> 4));
|
||||
if (chunk == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int worldOffset = level.getMinBuildHeight() >> 4;
|
||||
int worldOffset = level.getMinY() >> 4;
|
||||
int chunkOffset = (y >> 4) - worldOffset;
|
||||
if (chunkOffset < chunk.getSections().length) {
|
||||
LevelChunkSection section = chunk.getSections()[chunkOffset];
|
||||
|
@ -115,49 +104,6 @@ public class GeyserModWorldManager extends GeyserWorldManager {
|
|||
return GameMode.byId(server.getDefaultGameType().getId());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public CompletableFuture<org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addNbtData) {
|
||||
CompletableFuture<org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents> future = new CompletableFuture<>();
|
||||
server.execute(() -> {
|
||||
ServerPlayer player = getPlayer(session);
|
||||
if (player == null) {
|
||||
future.complete(null);
|
||||
return;
|
||||
}
|
||||
|
||||
BlockPos pos = new BlockPos(x, y, z);
|
||||
// Don't create a new block entity if invalid
|
||||
//noinspection resource - level() is just a getter
|
||||
BlockEntity blockEntity = player.level().getChunkAt(pos).getBlockEntity(pos);
|
||||
if (blockEntity instanceof BannerBlockEntity banner) {
|
||||
// Potentially exposes other NBT data? But we need to get the NBT data for the banner patterns *and*
|
||||
// the banner might have a custom name, both of which a Java client knows and caches
|
||||
ItemStack itemStack = banner.getItem();
|
||||
|
||||
org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents components =
|
||||
new org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents(new HashMap<>());
|
||||
|
||||
components.put(DataComponentType.DAMAGE, itemStack.getDamageValue());
|
||||
|
||||
Component customName = itemStack.getComponents().get(DataComponents.CUSTOM_NAME);
|
||||
if (customName != null) {
|
||||
components.put(DataComponentType.CUSTOM_NAME, toKyoriComponent(customName));
|
||||
}
|
||||
|
||||
BannerPatternLayers pattern = itemStack.get(DataComponents.BANNER_PATTERNS);
|
||||
if (pattern != null) {
|
||||
components.put(DataComponentType.BANNER_PATTERNS, toPatternList(pattern));
|
||||
}
|
||||
|
||||
future.complete(components);
|
||||
return;
|
||||
}
|
||||
future.complete(null);
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDecoratedPotData(GeyserSession session, Vector3i pos, Consumer<List<String>> apply) {
|
||||
server.execute(() -> {
|
||||
|
@ -182,20 +128,4 @@ public class GeyserModWorldManager extends GeyserWorldManager {
|
|||
private ServerPlayer getPlayer(GeyserSession session) {
|
||||
return server.getPlayerList().getPlayer(session.getPlayerEntity().getUuid());
|
||||
}
|
||||
|
||||
private static net.kyori.adventure.text.Component toKyoriComponent(Component component) {
|
||||
String json = Component.Serializer.toJson(component, RegistryAccess.EMPTY);
|
||||
return GSON_SERIALIZER.deserializeOr(json, net.kyori.adventure.text.Component.empty());
|
||||
}
|
||||
|
||||
private static List<BannerPatternLayer> toPatternList(BannerPatternLayers patternLayers) {
|
||||
return patternLayers.layers().stream()
|
||||
.map(layer -> {
|
||||
BannerPatternLayer.BannerPattern pattern = new BannerPatternLayer.BannerPattern(
|
||||
MinecraftKey.key(layer.pattern().value().assetId().toString()), layer.pattern().value().translationKey()
|
||||
);
|
||||
return new BannerPatternLayer(Holder.ofCustom(pattern), layer.color().getId());
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
plugins {
|
||||
id("geyser.platform-conventions")
|
||||
id("geyser.modrinth-uploading-conventions")
|
||||
alias(libs.plugins.runpaper)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -90,3 +91,9 @@ modrinth {
|
|||
"1.19.1", "1.19.2", "1.19.3", "1.19.4", "1.20", "1.20.1", "1.20.2", "1.20.3", "1.20.4", "1.20.5", "1.20.6")
|
||||
loaders.addAll("spigot", "paper")
|
||||
}
|
||||
|
||||
tasks {
|
||||
runServer {
|
||||
minecraftVersion(libs.versions.runpaperversion.get())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
package org.geysermc.geyser.platform.spigot;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.MinecraftConstants;
|
||||
import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
|
||||
import com.viaversion.viaversion.bukkit.handlers.BukkitChannelInitializer;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
|
@ -186,11 +187,10 @@ public class GeyserSpigotInjector extends GeyserInjector {
|
|||
*/
|
||||
private void workAroundWeirdBug(GeyserBootstrap bootstrap) {
|
||||
MinecraftProtocol protocol = new MinecraftProtocol();
|
||||
LocalSession session = new LocalSession(null, bootstrap.getGeyserConfig().getRemote().address(),
|
||||
bootstrap.getGeyserConfig().getRemote().port(), this.serverSocketAddress,
|
||||
InetAddress.getLoopbackAddress().getHostAddress(), protocol, protocol.createHelper());
|
||||
LocalSession session = new LocalSession(null, this.serverSocketAddress, InetAddress.getLoopbackAddress().getHostAddress(), protocol, Runnable::run);
|
||||
session.setFlag(MinecraftConstants.CLIENT_HOST, bootstrap.getGeyserConfig().getRemote().address());
|
||||
session.setFlag(MinecraftConstants.CLIENT_PORT, bootstrap.getGeyserConfig().getRemote().port());
|
||||
session.connect();
|
||||
session.disconnect("");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -29,6 +29,7 @@ import net.kyori.adventure.text.Component;
|
|||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.command.RemoteConsoleCommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
@ -76,7 +77,7 @@ public class SpigotCommandSource implements GeyserCommandSource {
|
|||
|
||||
@Override
|
||||
public boolean isConsole() {
|
||||
return handle instanceof ConsoleCommandSender;
|
||||
return handle instanceof ConsoleCommandSender || handle instanceof RemoteConsoleCommandSender;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,7 +33,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
import org.geysermc.geyser.adapters.WorldAdapter;
|
||||
import org.geysermc.geyser.adapters.paper.PaperAdapters;
|
||||
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
|
|
|
@ -25,18 +25,14 @@
|
|||
|
||||
package org.geysermc.geyser.platform.spigot.world.manager;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.DecoratedPot;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.geysermc.erosion.bukkit.BukkitUtils;
|
||||
import org.geysermc.erosion.bukkit.PickBlockUtils;
|
||||
import org.geysermc.erosion.bukkit.SchedulerUtils;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.level.GameRule;
|
||||
|
@ -44,7 +40,6 @@ import org.geysermc.geyser.level.WorldManager;
|
|||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
@ -128,20 +123,6 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
|||
return GameMode.byId(Bukkit.getDefaultGameMode().ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addNbtData) {
|
||||
Player bukkitPlayer;
|
||||
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
CompletableFuture<Int2ObjectMap<byte[]>> future = new CompletableFuture<>();
|
||||
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
|
||||
// Paper 1.19.3 complains about async access otherwise.
|
||||
// java.lang.IllegalStateException: Tile is null, asynchronous access?
|
||||
SchedulerUtils.runTask(this.plugin, () -> future.complete(PickBlockUtils.pickBlock(block)), block);
|
||||
return future.thenApply(RAW_TRANSFORMER);
|
||||
}
|
||||
|
||||
public void getDecoratedPotData(GeyserSession session, Vector3i pos, Consumer<List<String>> apply) {
|
||||
Player bukkitPlayer;
|
||||
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
|
||||
|
|
|
@ -5,9 +5,6 @@ plugins {
|
|||
id("geyser.platform-conventions")
|
||||
}
|
||||
|
||||
val terminalConsoleVersion = "1.2.0"
|
||||
val jlineVersion = "3.21.0"
|
||||
|
||||
dependencies {
|
||||
api(projects.api)
|
||||
api(projects.core)
|
||||
|
|
|
@ -33,10 +33,24 @@ import org.geysermc.geyser.GeyserImpl;
|
|||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.jline.reader.Candidate;
|
||||
import org.jline.reader.LineReader;
|
||||
import org.jline.reader.LineReaderBuilder;
|
||||
|
||||
@Slf4j
|
||||
public class GeyserStandaloneLogger extends SimpleTerminalConsole implements GeyserLogger, GeyserCommandSource {
|
||||
|
||||
@Override
|
||||
protected LineReader buildReader(LineReaderBuilder builder) {
|
||||
builder.completer((reader, line, candidates) -> {
|
||||
var suggestions = GeyserImpl.getInstance().commandRegistry().suggestionsFor(this, line.line());
|
||||
for (var suggestion : suggestions.list()) {
|
||||
candidates.add(new Candidate(suggestion.suggestion()));
|
||||
}
|
||||
});
|
||||
return super.buildReader(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRunning() {
|
||||
return !GeyserImpl.getInstance().isShuttingDown();
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
<AppenderRef ref="TerminalConsole"/>
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="File"/>
|
||||
<filters>
|
||||
<MarkerFilter marker="packet_logging" onMatch="DENY" onMismatch="ACCEPT" />
|
||||
</filters>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
</Configuration>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
plugins {
|
||||
id("geyser.platform-conventions")
|
||||
id("geyser.modrinth-uploading-conventions")
|
||||
alias(libs.plugins.runvelocity)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -80,3 +81,9 @@ modrinth {
|
|||
uploadFile.set(tasks.getByPath("shadowJar"))
|
||||
loaders.addAll("velocity")
|
||||
}
|
||||
|
||||
tasks {
|
||||
runVelocity {
|
||||
version(libs.versions.runvelocityversion.get())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,8 +31,10 @@ import com.velocitypowered.api.network.ProtocolVersion;
|
|||
import com.velocitypowered.api.proxy.InboundConnection;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.proxy.server.ServerPing;
|
||||
import com.velocitypowered.api.proxy.server.ServerPing.Version;
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.ping.GeyserPingInfo;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
|
||||
|
@ -51,7 +53,9 @@ public class GeyserVelocityPingPassthrough implements IGeyserPingPassthrough {
|
|||
try {
|
||||
event = server.getEventManager().fire(new ProxyPingEvent(new GeyserInboundConnection(inetSocketAddress), ServerPing.builder()
|
||||
.description(server.getConfiguration().getMotd()).onlinePlayers(server.getPlayerCount())
|
||||
.maximumPlayers(server.getConfiguration().getShowMaxPlayers()).build())).get();
|
||||
.maximumPlayers(server.getConfiguration().getShowMaxPlayers())
|
||||
.version(new Version(GameProtocol.getJavaProtocolVersion(), GameProtocol.getJavaMinecraftVersion()))
|
||||
.build())).get();
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
|
@ -24,15 +24,19 @@
|
|||
*/
|
||||
package org.geysermc.geyser.platform.viaproxy;
|
||||
|
||||
import io.netty.channel.AbstractChannel;
|
||||
import net.lenni0451.lambdaevents.EventHandler;
|
||||
import net.lenni0451.reflect.stream.RStream;
|
||||
import net.raphimc.vialegacy.api.LegacyProtocolVersion;
|
||||
import net.raphimc.viaproxy.ViaProxy;
|
||||
import net.raphimc.viaproxy.plugins.PluginManager;
|
||||
import net.raphimc.viaproxy.plugins.ViaProxyPlugin;
|
||||
import net.raphimc.viaproxy.plugins.events.Client2ProxyChannelInitializeEvent;
|
||||
import net.raphimc.viaproxy.plugins.events.ConsoleCommandEvent;
|
||||
import net.raphimc.viaproxy.plugins.events.ProxyStartEvent;
|
||||
import net.raphimc.viaproxy.plugins.events.ProxyStopEvent;
|
||||
import net.raphimc.viaproxy.plugins.events.ShouldVerifyOnlineModeEvent;
|
||||
import net.raphimc.viaproxy.plugins.events.types.ITyped;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
|
@ -56,6 +60,7 @@ import org.geysermc.geyser.util.LoopbackUtil;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
|
@ -109,6 +114,27 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
|
|||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onClient2ProxyChannelInitialize(Client2ProxyChannelInitializeEvent event) {
|
||||
if (event.getType() != ITyped.Type.POST || event.isLegacyPassthrough()) {
|
||||
return;
|
||||
}
|
||||
if (System.getProperty("geyser.viaproxy.disableIpPassthrough") != null) { // Temporary until Configurate branch is merged
|
||||
return;
|
||||
}
|
||||
|
||||
final GeyserSession session = GeyserImpl.getInstance().onlineConnections().stream()
|
||||
.filter(c -> c.getDownstream() != null)
|
||||
.filter(c -> c.getDownstream().getSession().getLocalAddress().equals(event.getChannel().remoteAddress()))
|
||||
.findAny().orElse(null);
|
||||
if (session != null) {
|
||||
final SocketAddress realAddress = session.getSocketAddress();
|
||||
if (event.getChannel() instanceof AbstractChannel) {
|
||||
RStream.of(AbstractChannel.class, event.getChannel()).fields().by("remoteAddress").set(realAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onProxyStart(final ProxyStartEvent event) {
|
||||
this.onGeyserEnable();
|
||||
|
|
|
@ -7,12 +7,13 @@ tasks.modrinth.get().dependsOn(tasks.modrinthSyncBody)
|
|||
|
||||
modrinth {
|
||||
token.set(System.getenv("MODRINTH_TOKEN") ?: "") // Even though this is the default value, apparently this prevents GitHub Actions caching the token?
|
||||
debugMode.set(System.getenv("MODRINTH_TOKEN") == null)
|
||||
projectId.set("geyser")
|
||||
versionName.set(versionName(project))
|
||||
versionNumber.set(projectVersion(project))
|
||||
versionType.set("beta")
|
||||
changelog.set(System.getenv("CHANGELOG") ?: "")
|
||||
gameVersions.addAll("1.21", libs.minecraft.get().version as String)
|
||||
gameVersions.add(libs.minecraft.get().version as String)
|
||||
failSilently.set(true)
|
||||
|
||||
syncBodyFrom.set(rootProject.file("README.md").readText())
|
||||
|
|
|
@ -36,9 +36,6 @@ public final class Constants {
|
|||
|
||||
public static final String FLOODGATE_DOWNLOAD_LOCATION = "https://geysermc.org/download#floodgate";
|
||||
public static final String GEYSER_DOWNLOAD_LOCATION = "https://geysermc.org/download";
|
||||
|
||||
@Deprecated
|
||||
static final String SAVED_REFRESH_TOKEN_FILE = "saved-refresh-tokens.json";
|
||||
static final String SAVED_AUTH_CHAINS_FILE = "saved-auth-chains.json";
|
||||
|
||||
public static final String GEYSER_CUSTOM_NAMESPACE = "geyser_custom";
|
||||
|
|
|
@ -29,7 +29,6 @@ import com.fasterxml.jackson.core.JsonParser;
|
|||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.gson.Gson;
|
||||
import io.netty.channel.epoll.Epoll;
|
||||
import io.netty.util.NettyRuntime;
|
||||
import io.netty.util.concurrent.DefaultThreadFactory;
|
||||
|
@ -39,8 +38,6 @@ import lombok.Getter;
|
|||
import lombok.Setter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.raphimc.minecraftauth.step.java.session.StepFullJavaSession;
|
||||
import net.raphimc.minecraftauth.step.msa.StepMsaToken;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
@ -70,6 +67,7 @@ import org.geysermc.geyser.configuration.GeyserConfiguration;
|
|||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.erosion.UnixSocketClientListener;
|
||||
import org.geysermc.geyser.event.GeyserEventBus;
|
||||
import org.geysermc.geyser.event.type.SessionDisconnectEventImpl;
|
||||
import org.geysermc.geyser.extension.GeyserExtensionManager;
|
||||
import org.geysermc.geyser.floodgate.FloodgateProvider;
|
||||
import org.geysermc.geyser.floodgate.IntegratedFloodgateProvider;
|
||||
|
@ -86,6 +84,7 @@ import org.geysermc.geyser.registry.provider.ProviderSupplier;
|
|||
import org.geysermc.geyser.scoreboard.ScoreboardUpdater;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.PendingMicrosoftAuthentication;
|
||||
import org.geysermc.geyser.session.SessionDisconnectListener;
|
||||
import org.geysermc.geyser.session.SessionManager;
|
||||
import org.geysermc.geyser.session.cache.RegistryCache;
|
||||
import org.geysermc.geyser.skin.BedrockSkinUploader;
|
||||
|
@ -97,10 +96,8 @@ import org.geysermc.geyser.translator.text.MessageTranslator;
|
|||
import org.geysermc.geyser.util.AssetUtils;
|
||||
import org.geysermc.geyser.util.CooldownUtils;
|
||||
import org.geysermc.geyser.util.Metrics;
|
||||
import org.geysermc.geyser.util.MinecraftAuthLogger;
|
||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||
import org.geysermc.geyser.util.WebUtils;
|
||||
import org.geysermc.mcprotocollib.network.tcp.TcpSession;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
|
@ -241,9 +238,14 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||
}
|
||||
logger.info("******************************************");
|
||||
|
||||
/* Initialize registries */
|
||||
Registries.init();
|
||||
BlockRegistries.init();
|
||||
/*
|
||||
First load the registries and then populate them.
|
||||
Both the block registries and the common registries depend on each other,
|
||||
so maintaining this order is crucial for Geyser to load.
|
||||
*/
|
||||
Registries.load();
|
||||
BlockRegistries.populate();
|
||||
Registries.populate();
|
||||
|
||||
RegistryCache.init();
|
||||
|
||||
|
@ -271,6 +273,8 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||
|
||||
// Register our general permissions when possible
|
||||
eventBus.subscribe(this, GeyserRegisterPermissionsEvent.class, Permissions::register);
|
||||
// Replace disconnect messages whenever necessary
|
||||
eventBus.subscribe(this, SessionDisconnectEventImpl.class, SessionDisconnectListener::onSessionDisconnect);
|
||||
|
||||
startInstance();
|
||||
|
||||
|
@ -420,9 +424,6 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||
}
|
||||
}
|
||||
|
||||
// Ensure that PacketLib does not create an event loop for handling packets; we'll do that ourselves
|
||||
TcpSession.USE_EVENT_LOOP_FOR_PACKETS = false;
|
||||
|
||||
pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.getPendingAuthenticationTimeout());
|
||||
|
||||
Packets.initGeyser();
|
||||
|
@ -567,53 +568,6 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||
// May be written/read to on multiple threads from each GeyserSession as well as writing the config
|
||||
savedAuthChains = new ConcurrentHashMap<>();
|
||||
|
||||
// TODO Remove after a while - just a migration help
|
||||
//noinspection deprecation
|
||||
File refreshTokensFile = bootstrap.getSavedUserLoginsFolder().resolve(Constants.SAVED_REFRESH_TOKEN_FILE).toFile();
|
||||
if (refreshTokensFile.exists()) {
|
||||
logger.info("Migrating refresh tokens to auth chains...");
|
||||
TypeReference<Map<String, String>> type = new TypeReference<>() { };
|
||||
Map<String, String> refreshTokens = null;
|
||||
try {
|
||||
refreshTokens = JSON_MAPPER.readValue(refreshTokensFile, type);
|
||||
} catch (IOException e) {
|
||||
// ignored - we'll just delete this file :))
|
||||
}
|
||||
|
||||
if (refreshTokens != null) {
|
||||
List<String> validUsers = config.getSavedUserLogins();
|
||||
final Gson gson = new Gson();
|
||||
for (Map.Entry<String, String> entry : refreshTokens.entrySet()) {
|
||||
String user = entry.getKey();
|
||||
if (!validUsers.contains(user)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Migrate refresh tokens to auth chains
|
||||
try {
|
||||
StepFullJavaSession javaSession = PendingMicrosoftAuthentication.AUTH_FLOW.apply(false, 10);
|
||||
StepFullJavaSession.FullJavaSession fullJavaSession = javaSession.getFromInput(
|
||||
MinecraftAuthLogger.INSTANCE,
|
||||
PendingMicrosoftAuthentication.AUTH_CLIENT,
|
||||
new StepMsaToken.RefreshToken(entry.getValue())
|
||||
);
|
||||
|
||||
String authChain = gson.toJson(javaSession.toJson(fullJavaSession));
|
||||
savedAuthChains.put(user, authChain);
|
||||
} catch (Exception e) {
|
||||
GeyserImpl.getInstance().getLogger().warning("Could not migrate " + entry.getKey() + " to an auth chain! " +
|
||||
"They will need to sign in the next time they join Geyser.");
|
||||
}
|
||||
|
||||
// Ensure the new additions are written to the file
|
||||
scheduleAuthChainsWrite();
|
||||
}
|
||||
}
|
||||
|
||||
// Finally: Delete it. Goodbye!
|
||||
refreshTokensFile.delete();
|
||||
}
|
||||
|
||||
File authChainsFile = bootstrap.getSavedUserLoginsFolder().resolve(Constants.SAVED_AUTH_CHAINS_FILE).toFile();
|
||||
if (authChainsFile.exists()) {
|
||||
TypeReference<Map<String, String>> type = new TypeReference<>() { };
|
||||
|
@ -726,7 +680,9 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||
runIfNonNull(skinUploader, BedrockSkinUploader::close);
|
||||
runIfNonNull(erosionUnixListener, UnixSocketClientListener::close);
|
||||
|
||||
Registries.RESOURCE_PACKS.get().clear();
|
||||
if (Registries.RESOURCE_PACKS.loaded()) {
|
||||
Registries.RESOURCE_PACKS.get().clear();
|
||||
}
|
||||
|
||||
this.setEnabled(false);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,13 @@ package org.geysermc.geyser.command;
|
|||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumConstraint;
|
||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandOverloadData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandParam;
|
||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandParamData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||
|
@ -51,14 +58,29 @@ import org.geysermc.geyser.command.defaults.StopCommand;
|
|||
import org.geysermc.geyser.command.defaults.VersionCommand;
|
||||
import org.geysermc.geyser.event.type.GeyserDefineCommandsEventImpl;
|
||||
import org.geysermc.geyser.extension.command.GeyserExtensionCommand;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.incendo.cloud.Command.Builder;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
import org.incendo.cloud.internal.CommandNode;
|
||||
import org.incendo.cloud.parser.standard.EnumParser;
|
||||
import org.incendo.cloud.parser.standard.IntegerParser;
|
||||
import org.incendo.cloud.parser.standard.LiteralParser;
|
||||
import org.incendo.cloud.parser.standard.StringArrayParser;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.incendo.cloud.suggestion.Suggestions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.geysermc.geyser.command.GeyserCommand.DEFAULT_ROOT_COMMAND;
|
||||
|
||||
|
@ -299,4 +321,90 @@ public class CommandRegistry implements EventRegistrar {
|
|||
public void runCommand(@NonNull GeyserCommandSource source, @NonNull String command) {
|
||||
cloud.commandExecutor().executeCommand(source, command);
|
||||
}
|
||||
|
||||
public Suggestions<GeyserCommandSource, ? extends Suggestion> suggestionsFor(GeyserCommandSource source, String input) {
|
||||
return cloud.suggestionFactory().suggestImmediately(source, input);
|
||||
}
|
||||
|
||||
public void export(GeyserSession session, List<CommandData> bedrockCommands, Set<String> knownAliases) {
|
||||
cloud.commandTree().rootNodes().forEach(commandTree -> {
|
||||
var command = commandTree.command();
|
||||
// Command null happens if you register an extension command with custom Cloud parameters...
|
||||
if (command == null || session.hasPermission(command.commandPermission().permissionString())) {
|
||||
var rootComponent = commandTree.component();
|
||||
String name = rootComponent.name();
|
||||
if (!knownAliases.add(name)) {
|
||||
// If the server already defined the command, let's not crash.
|
||||
return;
|
||||
}
|
||||
|
||||
LinkedHashMap<String, Set<CommandEnumConstraint>> values = new LinkedHashMap<>();
|
||||
for (String s : rootComponent.aliases()) {
|
||||
values.put(s, EnumSet.of(CommandEnumConstraint.ALLOW_ALIASES));
|
||||
}
|
||||
CommandEnumData aliases = new CommandEnumData(name + "Aliases", values, false);
|
||||
|
||||
List<CommandOverloadData> data = new ArrayList<>();
|
||||
for (var node : commandTree.children()) {
|
||||
List<List<CommandParamData>> params = createParamData(session, node);
|
||||
params.forEach(param -> data.add(new CommandOverloadData(false, param.toArray(CommandParamData[]::new))));
|
||||
}
|
||||
|
||||
CommandData bedrockCommand = new CommandData(name, rootComponent.description().textDescription(),
|
||||
Set.of(CommandData.Flag.NOT_CHEAT), CommandPermission.ANY, aliases,
|
||||
Collections.emptyList(), data.toArray(new CommandOverloadData[0]));
|
||||
bedrockCommands.add(bedrockCommand);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private List<List<CommandParamData>> createParamData(GeyserSession session, CommandNode<GeyserCommandSource> node) {
|
||||
var command = node.command();
|
||||
if (command != null && !session.hasPermission(command.commandPermission().permissionString())) {
|
||||
// Triggers with subcommands like Geyser dump, stop, etc.
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
CommandParamData data = new CommandParamData();
|
||||
var component = node.component();
|
||||
data.setName(component.name());
|
||||
data.setOptional(component.optional());
|
||||
var suggestionProvider = component.suggestionProvider();
|
||||
if (suggestionProvider instanceof LiteralParser<GeyserCommandSource> parser) {
|
||||
Map<String, Set<CommandEnumConstraint>> values = new LinkedHashMap<>();
|
||||
for (String alias : parser.aliases()) {
|
||||
values.put(alias, Set.of());
|
||||
}
|
||||
|
||||
data.setEnumData(new CommandEnumData(component.name(), values, false));
|
||||
} else if (suggestionProvider instanceof IntegerParser<GeyserCommandSource>) {
|
||||
data.setType(CommandParam.INT);
|
||||
} else if (suggestionProvider instanceof EnumParser<?,?> parser) {
|
||||
LinkedHashMap<String, Set<CommandEnumConstraint>> map = new LinkedHashMap<>();
|
||||
for (Enum<?> e : parser.acceptedValues()) {
|
||||
map.put(e.name().toLowerCase(Locale.ROOT), Set.of());
|
||||
}
|
||||
|
||||
data.setEnumData(new CommandEnumData(component.name().toLowerCase(Locale.ROOT), map, false));
|
||||
} else if (component.parser() instanceof StringArrayParser<?>) {
|
||||
data.setType(CommandParam.TEXT);
|
||||
} else {
|
||||
data.setType(CommandParam.STRING);
|
||||
}
|
||||
|
||||
var children = node.children();
|
||||
if (children.isEmpty()) {
|
||||
List<CommandParamData> list = new ArrayList<>(); // Must be mutable; parents will be added to list.
|
||||
list.add(data);
|
||||
return Collections.singletonList(list); // Safe to do; will be consumed in an addAll call.
|
||||
}
|
||||
List<List<CommandParamData>> collectiveData = new ArrayList<>();
|
||||
// If a node has multiple children, this will need to be represented
|
||||
// by creating a new list/branch for each and cloning this node down each line.
|
||||
for (var child : children) {
|
||||
collectiveData.addAll(createParamData(session, child));
|
||||
}
|
||||
collectiveData.forEach(list -> list.add(0, data));
|
||||
return collectiveData;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,9 +26,6 @@
|
|||
package org.geysermc.geyser.entity;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.BiConsumer;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
|
@ -37,11 +34,14 @@ import org.geysermc.geyser.entity.properties.GeyserEntityProperties;
|
|||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.translator.entity.EntityMetadataTranslator;
|
||||
import org.geysermc.geyser.util.EnvironmentUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Represents data for an entity. This includes properties such as height and width, as well as the list of entity
|
||||
* metadata translators needed to translate the properties sent from the server. The translators are structured in such
|
||||
|
@ -146,13 +146,8 @@ public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, Entit
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the given entity. If a testing environment has been discovered the entity is not registered,
|
||||
* otherwise it is. This is to prevent all the registries from loading, which will fail (and should
|
||||
* not be loaded) while testing
|
||||
*/
|
||||
public EntityDefinition<T> build() {
|
||||
return build(!EnvironmentUtils.isUnitTesting);
|
||||
return build(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.geysermc.geyser.entity.type.ChestBoatEntity;
|
|||
import org.geysermc.geyser.entity.type.CommandBlockMinecartEntity;
|
||||
import org.geysermc.geyser.entity.type.DisplayBaseEntity;
|
||||
import org.geysermc.geyser.entity.type.EnderCrystalEntity;
|
||||
import org.geysermc.geyser.entity.type.EnderEyeEntity;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.entity.type.EvokerFangsEntity;
|
||||
import org.geysermc.geyser.entity.type.ExpOrbEntity;
|
||||
|
@ -116,6 +117,7 @@ import org.geysermc.geyser.entity.type.living.monster.BasePiglinEntity;
|
|||
import org.geysermc.geyser.entity.type.living.monster.BlazeEntity;
|
||||
import org.geysermc.geyser.entity.type.living.monster.BoggedEntity;
|
||||
import org.geysermc.geyser.entity.type.living.monster.BreezeEntity;
|
||||
import org.geysermc.geyser.entity.type.living.monster.CreakingEntity;
|
||||
import org.geysermc.geyser.entity.type.living.monster.CreeperEntity;
|
||||
import org.geysermc.geyser.entity.type.living.monster.ElderGuardianEntity;
|
||||
import org.geysermc.geyser.entity.type.living.monster.EnderDragonEntity;
|
||||
|
@ -145,36 +147,44 @@ import org.geysermc.geyser.entity.type.living.monster.raid.VindicatorEntity;
|
|||
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.geyser.util.EnvironmentUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataTypes;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
|
||||
|
||||
public final class EntityDefinitions {
|
||||
public static final EntityDefinition<BoatEntity> ACACIA_BOAT;
|
||||
public static final EntityDefinition<ChestBoatEntity> ACACIA_CHEST_BOAT;
|
||||
public static final EntityDefinition<AllayEntity> ALLAY;
|
||||
public static final EntityDefinition<AreaEffectCloudEntity> AREA_EFFECT_CLOUD;
|
||||
public static final EntityDefinition<ArmadilloEntity> ARMADILLO;
|
||||
public static final EntityDefinition<ArmorStandEntity> ARMOR_STAND;
|
||||
public static final EntityDefinition<ArrowEntity> ARROW;
|
||||
public static final EntityDefinition<AxolotlEntity> AXOLOTL;
|
||||
public static final EntityDefinition<BoatEntity> BAMBOO_RAFT;
|
||||
public static final EntityDefinition<ChestBoatEntity> BAMBOO_CHEST_RAFT;
|
||||
public static final EntityDefinition<BatEntity> BAT;
|
||||
public static final EntityDefinition<BeeEntity> BEE;
|
||||
public static final EntityDefinition<BoatEntity> BIRCH_BOAT;
|
||||
public static final EntityDefinition<ChestBoatEntity> BIRCH_CHEST_BOAT;
|
||||
public static final EntityDefinition<BlazeEntity> BLAZE;
|
||||
public static final EntityDefinition<BoatEntity> BOAT;
|
||||
public static final EntityDefinition<BoggedEntity> BOGGED;
|
||||
public static final EntityDefinition<BreezeEntity> BREEZE;
|
||||
public static final EntityDefinition<AbstractWindChargeEntity> BREEZE_WIND_CHARGE;
|
||||
public static final EntityDefinition<CamelEntity> CAMEL;
|
||||
public static final EntityDefinition<CatEntity> CAT;
|
||||
public static final EntityDefinition<SpiderEntity> CAVE_SPIDER;
|
||||
public static final EntityDefinition<BoatEntity> CHERRY_BOAT;
|
||||
public static final EntityDefinition<ChestBoatEntity> CHERRY_CHEST_BOAT;
|
||||
public static final EntityDefinition<MinecartEntity> CHEST_MINECART;
|
||||
public static final EntityDefinition<ChickenEntity> CHICKEN;
|
||||
public static final EntityDefinition<ChestBoatEntity> CHEST_BOAT;
|
||||
public static final EntityDefinition<AbstractFishEntity> COD;
|
||||
public static final EntityDefinition<CommandBlockMinecartEntity> COMMAND_BLOCK_MINECART;
|
||||
public static final EntityDefinition<CowEntity> COW;
|
||||
public static final EntityDefinition<CreakingEntity> CREAKING;
|
||||
public static final EntityDefinition<CreeperEntity> CREEPER;
|
||||
public static final EntityDefinition<BoatEntity> DARK_OAK_BOAT;
|
||||
public static final EntityDefinition<ChestBoatEntity> DARK_OAK_CHEST_BOAT;
|
||||
public static final EntityDefinition<DolphinEntity> DOLPHIN;
|
||||
public static final EntityDefinition<ChestedHorseEntity> DONKEY;
|
||||
public static final EntityDefinition<FireballEntity> DRAGON_FIREBALL;
|
||||
|
@ -190,7 +200,7 @@ public final class EntityDefinitions {
|
|||
public static final EntityDefinition<EvokerFangsEntity> EVOKER_FANGS;
|
||||
public static final EntityDefinition<ThrowableItemEntity> EXPERIENCE_BOTTLE;
|
||||
public static final EntityDefinition<ExpOrbEntity> EXPERIENCE_ORB;
|
||||
public static final EntityDefinition<Entity> EYE_OF_ENDER;
|
||||
public static final EntityDefinition<EnderEyeEntity> EYE_OF_ENDER;
|
||||
public static final EntityDefinition<FallingBlockEntity> FALLING_BLOCK;
|
||||
public static final EntityDefinition<FireballEntity> FIREBALL;
|
||||
public static final EntityDefinition<FireworkEntity> FIREWORK_ROCKET;
|
||||
|
@ -213,16 +223,24 @@ public final class EntityDefinitions {
|
|||
public static final EntityDefinition<IronGolemEntity> IRON_GOLEM;
|
||||
public static final EntityDefinition<ItemEntity> ITEM;
|
||||
public static final EntityDefinition<ItemFrameEntity> ITEM_FRAME;
|
||||
public static final EntityDefinition<BoatEntity> JUNGLE_BOAT;
|
||||
public static final EntityDefinition<ChestBoatEntity> JUNGLE_CHEST_BOAT;
|
||||
public static final EntityDefinition<LeashKnotEntity> LEASH_KNOT;
|
||||
public static final EntityDefinition<LightningEntity> LIGHTNING_BOLT;
|
||||
public static final EntityDefinition<LlamaEntity> LLAMA;
|
||||
public static final EntityDefinition<ThrowableEntity> LLAMA_SPIT;
|
||||
public static final EntityDefinition<MagmaCubeEntity> MAGMA_CUBE;
|
||||
public static final EntityDefinition<BoatEntity> MANGROVE_BOAT;
|
||||
public static final EntityDefinition<ChestBoatEntity> MANGROVE_CHEST_BOAT;
|
||||
public static final EntityDefinition<MinecartEntity> MINECART;
|
||||
public static final EntityDefinition<MooshroomEntity> MOOSHROOM;
|
||||
public static final EntityDefinition<ChestedHorseEntity> MULE;
|
||||
public static final EntityDefinition<BoatEntity> OAK_BOAT;
|
||||
public static final EntityDefinition<ChestBoatEntity> OAK_CHEST_BOAT;
|
||||
public static final EntityDefinition<OcelotEntity> OCELOT;
|
||||
public static final EntityDefinition<PaintingEntity> PAINTING;
|
||||
public static final EntityDefinition<BoatEntity> PALE_OAK_BOAT;
|
||||
public static final EntityDefinition<ChestBoatEntity> PALE_OAK_CHEST_BOAT;
|
||||
public static final EntityDefinition<PandaEntity> PANDA;
|
||||
public static final EntityDefinition<ParrotEntity> PARROT;
|
||||
public static final EntityDefinition<PhantomEntity> PHANTOM;
|
||||
|
@ -251,6 +269,8 @@ public final class EntityDefinitions {
|
|||
public static final EntityDefinition<SpawnerMinecartEntity> SPAWNER_MINECART; // Not present on Bedrock
|
||||
public static final EntityDefinition<AbstractArrowEntity> SPECTRAL_ARROW;
|
||||
public static final EntityDefinition<SpiderEntity> SPIDER;
|
||||
public static final EntityDefinition<BoatEntity> SPRUCE_BOAT;
|
||||
public static final EntityDefinition<ChestBoatEntity> SPRUCE_CHEST_BOAT;
|
||||
public static final EntityDefinition<SquidEntity> SQUID;
|
||||
public static final EntityDefinition<AbstractSkeletonEntity> STRAY;
|
||||
public static final EntityDefinition<StriderEntity> STRIDER;
|
||||
|
@ -290,14 +310,14 @@ public final class EntityDefinitions {
|
|||
|
||||
static {
|
||||
EntityDefinition<Entity> entityBase = EntityDefinition.builder(Entity::new)
|
||||
.addTranslator(MetadataType.BYTE, Entity::setFlags)
|
||||
.addTranslator(MetadataType.INT, Entity::setAir) // Air/bubbles
|
||||
.addTranslator(MetadataType.OPTIONAL_CHAT, Entity::setDisplayName)
|
||||
.addTranslator(MetadataType.BOOLEAN, Entity::setDisplayNameVisible)
|
||||
.addTranslator(MetadataType.BOOLEAN, Entity::setSilent)
|
||||
.addTranslator(MetadataType.BOOLEAN, Entity::setGravity)
|
||||
.addTranslator(MetadataType.POSE, (entity, entityMetadata) -> entity.setPose(entityMetadata.getValue()))
|
||||
.addTranslator(MetadataType.INT, Entity::setFreezing)
|
||||
.addTranslator(MetadataTypes.BYTE, Entity::setFlags)
|
||||
.addTranslator(MetadataTypes.INT, Entity::setAir) // Air/bubbles
|
||||
.addTranslator(MetadataTypes.OPTIONAL_CHAT, Entity::setDisplayName)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, Entity::setDisplayNameVisible)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, Entity::setSilent)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, Entity::setGravity)
|
||||
.addTranslator(MetadataTypes.POSE, (entity, entityMetadata) -> entity.setPose(entityMetadata.getValue()))
|
||||
.addTranslator(MetadataTypes.INT, Entity::setFreezing)
|
||||
.build();
|
||||
|
||||
// Extends entity
|
||||
|
@ -305,26 +325,9 @@ public final class EntityDefinitions {
|
|||
AREA_EFFECT_CLOUD = EntityDefinition.inherited(AreaEffectCloudEntity::new, entityBase)
|
||||
.type(EntityType.AREA_EFFECT_CLOUD)
|
||||
.height(0.5f).width(1.0f)
|
||||
.addTranslator(MetadataType.FLOAT, AreaEffectCloudEntity::setRadius)
|
||||
.addTranslator(MetadataTypes.FLOAT, AreaEffectCloudEntity::setRadius)
|
||||
.addTranslator(null) // Waiting
|
||||
.addTranslator(MetadataType.PARTICLE, AreaEffectCloudEntity::setParticle)
|
||||
.build();
|
||||
BOAT = EntityDefinition.inherited(BoatEntity::new, entityBase)
|
||||
.type(EntityType.BOAT)
|
||||
.height(0.6f).width(1.6f)
|
||||
.offset(0.35f)
|
||||
.addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.HURT_TICKS, entityMetadata.getValue())) // Time since last hit
|
||||
.addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.HURT_DIRECTION, entityMetadata.getValue())) // Rocking direction
|
||||
.addTranslator(MetadataType.FLOAT, (boatEntity, entityMetadata) ->
|
||||
// 'Health' in Bedrock, damage taken in Java - it makes motion in Bedrock
|
||||
boatEntity.getDirtyMetadata().put(EntityDataTypes.STRUCTURAL_INTEGRITY, 40 - ((int) ((FloatEntityMetadata) entityMetadata).getPrimitiveValue())))
|
||||
.addTranslator(MetadataType.INT, BoatEntity::setVariant)
|
||||
.addTranslator(MetadataType.BOOLEAN, BoatEntity::setPaddlingLeft)
|
||||
.addTranslator(MetadataType.BOOLEAN, BoatEntity::setPaddlingRight)
|
||||
.addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.BOAT_BUBBLE_TIME, entityMetadata.getValue())) // May not actually do anything
|
||||
.build();
|
||||
CHEST_BOAT = EntityDefinition.inherited(ChestBoatEntity::new, BOAT)
|
||||
.type(EntityType.CHEST_BOAT)
|
||||
.addTranslator(MetadataTypes.PARTICLE, AreaEffectCloudEntity::setParticle)
|
||||
.build();
|
||||
DRAGON_FIREBALL = EntityDefinition.inherited(FireballEntity::new, entityBase)
|
||||
.type(EntityType.DRAGON_FIREBALL)
|
||||
|
@ -334,8 +337,8 @@ public final class EntityDefinitions {
|
|||
.type(EntityType.END_CRYSTAL)
|
||||
.heightAndWidth(2.0f)
|
||||
.identifier("minecraft:ender_crystal")
|
||||
.addTranslator(MetadataType.OPTIONAL_POSITION, EnderCrystalEntity::setBlockTarget)
|
||||
.addTranslator(MetadataType.BOOLEAN,
|
||||
.addTranslator(MetadataTypes.OPTIONAL_POSITION, EnderCrystalEntity::setBlockTarget)
|
||||
.addTranslator(MetadataTypes.BOOLEAN,
|
||||
(enderCrystalEntity, entityMetadata) -> enderCrystalEntity.setFlag(EntityFlag.SHOW_BOTTOM, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue())) // There is a base located on the ender crystal
|
||||
.build();
|
||||
EXPERIENCE_ORB = EntityDefinition.inherited(ExpOrbEntity::new, entityBase)
|
||||
|
@ -347,7 +350,7 @@ public final class EntityDefinitions {
|
|||
.height(0.8f).width(0.5f)
|
||||
.identifier("minecraft:evocation_fang")
|
||||
.build();
|
||||
EYE_OF_ENDER = EntityDefinition.inherited(Entity::new, entityBase)
|
||||
EYE_OF_ENDER = EntityDefinition.inherited(EnderEyeEntity::new, entityBase)
|
||||
.type(EntityType.EYE_OF_ENDER)
|
||||
.heightAndWidth(0.25f)
|
||||
.identifier("minecraft:eye_of_ender_signal")
|
||||
|
@ -362,21 +365,21 @@ public final class EntityDefinitions {
|
|||
.type(EntityType.FIREWORK_ROCKET)
|
||||
.heightAndWidth(0.25f)
|
||||
.identifier("minecraft:fireworks_rocket")
|
||||
.addTranslator(MetadataType.ITEM, FireworkEntity::setFireworkItem)
|
||||
.addTranslator(MetadataType.OPTIONAL_VARINT, FireworkEntity::setPlayerGliding)
|
||||
.addTranslator(MetadataTypes.ITEM, FireworkEntity::setFireworkItem)
|
||||
.addTranslator(MetadataTypes.OPTIONAL_VARINT, FireworkEntity::setPlayerGliding)
|
||||
.addTranslator(null) // Shot at angle
|
||||
.build();
|
||||
FISHING_BOBBER = EntityDefinition.<FishingHookEntity>inherited(null, entityBase)
|
||||
.type(EntityType.FISHING_BOBBER)
|
||||
.identifier("minecraft:fishing_hook")
|
||||
.addTranslator(MetadataType.INT, FishingHookEntity::setHookedEntity)
|
||||
.addTranslator(MetadataTypes.INT, FishingHookEntity::setHookedEntity)
|
||||
.addTranslator(null) // Biting TODO check
|
||||
.build();
|
||||
ITEM = EntityDefinition.inherited(ItemEntity::new, entityBase)
|
||||
.type(EntityType.ITEM)
|
||||
.heightAndWidth(0.25f)
|
||||
.offset(0.125f)
|
||||
.addTranslator(MetadataType.ITEM, ItemEntity::setItem)
|
||||
.addTranslator(MetadataTypes.ITEM, ItemEntity::setItem)
|
||||
.build();
|
||||
LEASH_KNOT = EntityDefinition.inherited(LeashKnotEntity::new, entityBase)
|
||||
.type(EntityType.LEASH_KNOT)
|
||||
|
@ -391,7 +394,7 @@ public final class EntityDefinitions {
|
|||
.build();
|
||||
PAINTING = EntityDefinition.<PaintingEntity>inherited(null, entityBase)
|
||||
.type(EntityType.PAINTING)
|
||||
.addTranslator(MetadataType.PAINTING_VARIANT, PaintingEntity::setPaintingType)
|
||||
.addTranslator(MetadataTypes.PAINTING_VARIANT, PaintingEntity::setPaintingType)
|
||||
.build();
|
||||
SHULKER_BULLET = EntityDefinition.inherited(ThrowableEntity::new, entityBase)
|
||||
.type(EntityType.SHULKER_BULLET)
|
||||
|
@ -401,14 +404,14 @@ public final class EntityDefinitions {
|
|||
.type(EntityType.TNT)
|
||||
.heightAndWidth(0.98f)
|
||||
.offset(0.49f)
|
||||
.addTranslator(MetadataType.INT, TNTEntity::setFuseLength)
|
||||
.addTranslator(MetadataTypes.INT, TNTEntity::setFuseLength)
|
||||
.build();
|
||||
|
||||
EntityDefinition<DisplayBaseEntity> displayBase = EntityDefinition.inherited(DisplayBaseEntity::new, entityBase)
|
||||
.addTranslator(null) // Interpolation delay
|
||||
.addTranslator(null) // Transformation interpolation duration
|
||||
.addTranslator(null) // Position/Rotation interpolation duration
|
||||
.addTranslator(MetadataType.VECTOR3, DisplayBaseEntity::setTranslation) // Translation
|
||||
.addTranslator(MetadataTypes.VECTOR3, DisplayBaseEntity::setTranslation) // Translation
|
||||
.addTranslator(null) // Scale
|
||||
.addTranslator(null) // Left rotation
|
||||
.addTranslator(null) // Right rotation
|
||||
|
@ -425,7 +428,7 @@ public final class EntityDefinitions {
|
|||
.type(EntityType.TEXT_DISPLAY)
|
||||
.identifier("minecraft:armor_stand")
|
||||
.offset(-0.5f)
|
||||
.addTranslator(MetadataType.CHAT, TextDisplayEntity::setText)
|
||||
.addTranslator(MetadataTypes.CHAT, TextDisplayEntity::setText)
|
||||
.addTranslator(null) // Line width
|
||||
.addTranslator(null) // Background color
|
||||
.addTranslator(null) // Text opacity
|
||||
|
@ -436,9 +439,9 @@ public final class EntityDefinitions {
|
|||
.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)
|
||||
.addTranslator(MetadataTypes.FLOAT, InteractionEntity::setWidth)
|
||||
.addTranslator(MetadataTypes.FLOAT, InteractionEntity::setHeight)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, InteractionEntity::setResponse)
|
||||
.build();
|
||||
|
||||
EntityDefinition<FireballEntity> fireballBase = EntityDefinition.inherited(FireballEntity::new, entityBase)
|
||||
|
@ -454,7 +457,7 @@ public final class EntityDefinitions {
|
|||
.build();
|
||||
|
||||
EntityDefinition<ThrowableItemEntity> throwableItemBase = EntityDefinition.inherited(ThrowableItemEntity::new, entityBase)
|
||||
.addTranslator(MetadataType.ITEM, ThrowableItemEntity::setItem)
|
||||
.addTranslator(MetadataTypes.ITEM, ThrowableItemEntity::setItem)
|
||||
.build();
|
||||
EGG = EntityDefinition.inherited(ThrowableItemEntity::new, throwableItemBase)
|
||||
.type(EntityType.EGG)
|
||||
|
@ -492,13 +495,14 @@ public final class EntityDefinitions {
|
|||
.build();
|
||||
|
||||
EntityDefinition<AbstractArrowEntity> abstractArrowBase = EntityDefinition.inherited(AbstractArrowEntity::new, entityBase)
|
||||
.addTranslator(MetadataType.BYTE, AbstractArrowEntity::setArrowFlags)
|
||||
.addTranslator(MetadataTypes.BYTE, AbstractArrowEntity::setArrowFlags)
|
||||
.addTranslator(null) // "Piercing level"
|
||||
.addTranslator(null) // If the arrow is in the ground
|
||||
.build();
|
||||
ARROW = EntityDefinition.inherited(ArrowEntity::new, abstractArrowBase)
|
||||
.type(EntityType.ARROW)
|
||||
.heightAndWidth(0.25f)
|
||||
.addTranslator(MetadataType.INT, ArrowEntity::setPotionEffectColor)
|
||||
.addTranslator(MetadataTypes.INT, ArrowEntity::setPotionEffectColor)
|
||||
.build();
|
||||
SPECTRAL_ARROW = EntityDefinition.inherited(abstractArrowBase.factory(), abstractArrowBase)
|
||||
.type(EntityType.SPECTRAL_ARROW)
|
||||
|
@ -509,14 +513,14 @@ public final class EntityDefinitions {
|
|||
.type(EntityType.TRIDENT)
|
||||
.identifier("minecraft:thrown_trident")
|
||||
.addTranslator(null) // Loyalty
|
||||
.addTranslator(MetadataType.BOOLEAN, (tridentEntity, entityMetadata) -> tridentEntity.setFlag(EntityFlag.ENCHANTED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
|
||||
.addTranslator(MetadataTypes.BOOLEAN, (tridentEntity, entityMetadata) -> tridentEntity.setFlag(EntityFlag.ENCHANTED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
|
||||
.build();
|
||||
|
||||
// Item frames are handled differently as they are blocks, not items, in Bedrock
|
||||
ITEM_FRAME = EntityDefinition.<ItemFrameEntity>inherited(null, entityBase)
|
||||
.type(EntityType.ITEM_FRAME)
|
||||
.addTranslator(MetadataType.ITEM, ItemFrameEntity::setItemInFrame)
|
||||
.addTranslator(MetadataType.INT, ItemFrameEntity::setItemRotation)
|
||||
.addTranslator(MetadataTypes.ITEM, ItemFrameEntity::setItemInFrame)
|
||||
.addTranslator(MetadataTypes.INT, ItemFrameEntity::setItemRotation)
|
||||
.build();
|
||||
GLOW_ITEM_FRAME = EntityDefinition.inherited(ITEM_FRAME.factory(), ITEM_FRAME)
|
||||
.type(EntityType.GLOW_ITEM_FRAME)
|
||||
|
@ -526,27 +530,27 @@ public final class EntityDefinitions {
|
|||
.type(EntityType.MINECART)
|
||||
.height(0.7f).width(0.98f)
|
||||
.offset(0.35f)
|
||||
.addTranslator(MetadataType.INT, (minecartEntity, entityMetadata) -> minecartEntity.getDirtyMetadata().put(EntityDataTypes.STRUCTURAL_INTEGRITY, entityMetadata.getValue()))
|
||||
.addTranslator(MetadataType.INT, (minecartEntity, entityMetadata) -> minecartEntity.getDirtyMetadata().put(EntityDataTypes.HURT_DIRECTION, entityMetadata.getValue())) // Direction in which the minecart is shaking
|
||||
.addTranslator(MetadataType.FLOAT, (minecartEntity, entityMetadata) ->
|
||||
.addTranslator(MetadataTypes.INT, (minecartEntity, entityMetadata) -> minecartEntity.getDirtyMetadata().put(EntityDataTypes.STRUCTURAL_INTEGRITY, entityMetadata.getValue()))
|
||||
.addTranslator(MetadataTypes.INT, (minecartEntity, entityMetadata) -> minecartEntity.getDirtyMetadata().put(EntityDataTypes.HURT_DIRECTION, entityMetadata.getValue())) // Direction in which the minecart is shaking
|
||||
.addTranslator(MetadataTypes.FLOAT, (minecartEntity, entityMetadata) ->
|
||||
// Power in Java, hurt ticks in Bedrock
|
||||
minecartEntity.getDirtyMetadata().put(EntityDataTypes.HURT_TICKS, Math.min((int) ((FloatEntityMetadata) entityMetadata).getPrimitiveValue(), 15)))
|
||||
.addTranslator(MetadataType.INT, MinecartEntity::setCustomBlock)
|
||||
.addTranslator(MetadataType.INT, MinecartEntity::setCustomBlockOffset)
|
||||
.addTranslator(MetadataType.BOOLEAN, MinecartEntity::setShowCustomBlock)
|
||||
.addTranslator(MetadataTypes.INT, MinecartEntity::setCustomBlock)
|
||||
.addTranslator(MetadataTypes.INT, MinecartEntity::setCustomBlockOffset)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, MinecartEntity::setShowCustomBlock)
|
||||
.build();
|
||||
CHEST_MINECART = EntityDefinition.inherited(MINECART.factory(), MINECART)
|
||||
.type(EntityType.CHEST_MINECART)
|
||||
.build();
|
||||
COMMAND_BLOCK_MINECART = EntityDefinition.inherited(CommandBlockMinecartEntity::new, MINECART)
|
||||
.type(EntityType.COMMAND_BLOCK_MINECART)
|
||||
.addTranslator(MetadataType.STRING, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityDataTypes.COMMAND_BLOCK_NAME, entityMetadata.getValue()))
|
||||
.addTranslator(MetadataType.CHAT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityDataTypes.COMMAND_BLOCK_LAST_OUTPUT, MessageTranslator.convertMessage(entityMetadata.getValue())))
|
||||
.addTranslator(MetadataTypes.STRING, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityDataTypes.COMMAND_BLOCK_NAME, entityMetadata.getValue()))
|
||||
.addTranslator(MetadataTypes.CHAT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityDataTypes.COMMAND_BLOCK_LAST_OUTPUT, MessageTranslator.convertMessage(entityMetadata.getValue())))
|
||||
.build();
|
||||
FURNACE_MINECART = EntityDefinition.inherited(FurnaceMinecartEntity::new, MINECART)
|
||||
.type(EntityType.FURNACE_MINECART)
|
||||
.identifier("minecraft:minecart")
|
||||
.addTranslator(MetadataType.BOOLEAN, FurnaceMinecartEntity::setHasFuel)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, FurnaceMinecartEntity::setHasFuel)
|
||||
.build();
|
||||
HOPPER_MINECART = EntityDefinition.inherited(MINECART.factory(), MINECART)
|
||||
.type(EntityType.HOPPER_MINECART)
|
||||
|
@ -562,48 +566,89 @@ public final class EntityDefinitions {
|
|||
WITHER_SKULL = EntityDefinition.inherited(WitherSkullEntity::new, entityBase)
|
||||
.type(EntityType.WITHER_SKULL)
|
||||
.heightAndWidth(0.3125f)
|
||||
.addTranslator(MetadataType.BOOLEAN, WitherSkullEntity::setDangerous)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, WitherSkullEntity::setDangerous)
|
||||
.build();
|
||||
WITHER_SKULL_DANGEROUS = EntityDefinition.inherited(WITHER_SKULL.factory(), WITHER_SKULL)
|
||||
.build(false);
|
||||
}
|
||||
|
||||
// Boats
|
||||
{
|
||||
EntityDefinition<BoatEntity> boatBase = EntityDefinition.<BoatEntity>inherited(null, entityBase)
|
||||
.height(0.6f).width(1.6f)
|
||||
.offset(0.35f)
|
||||
.addTranslator(MetadataTypes.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.HURT_TICKS, entityMetadata.getValue())) // Time since last hit
|
||||
.addTranslator(MetadataTypes.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.HURT_DIRECTION, entityMetadata.getValue())) // Rocking direction
|
||||
.addTranslator(MetadataTypes.FLOAT, (boatEntity, entityMetadata) ->
|
||||
// 'Health' in Bedrock, damage taken in Java - it makes motion in Bedrock
|
||||
boatEntity.getDirtyMetadata().put(EntityDataTypes.STRUCTURAL_INTEGRITY, 40 - ((int) ((FloatEntityMetadata) entityMetadata).getPrimitiveValue())))
|
||||
.addTranslator(MetadataTypes.BOOLEAN, BoatEntity::setPaddlingLeft)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, BoatEntity::setPaddlingRight)
|
||||
.addTranslator(MetadataTypes.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.BOAT_BUBBLE_TIME, entityMetadata.getValue())) // May not actually do anything
|
||||
.build();
|
||||
|
||||
ACACIA_BOAT = buildBoat(boatBase, EntityType.ACACIA_BOAT, BoatEntity.BoatVariant.ACACIA);
|
||||
BAMBOO_RAFT = buildBoat(boatBase, EntityType.BAMBOO_RAFT, BoatEntity.BoatVariant.BAMBOO);
|
||||
BIRCH_BOAT = buildBoat(boatBase, EntityType.BIRCH_BOAT, BoatEntity.BoatVariant.BIRCH);
|
||||
CHERRY_BOAT = buildBoat(boatBase, EntityType.CHERRY_BOAT, BoatEntity.BoatVariant.CHERRY);
|
||||
DARK_OAK_BOAT = buildBoat(boatBase, EntityType.DARK_OAK_BOAT, BoatEntity.BoatVariant.DARK_OAK);
|
||||
JUNGLE_BOAT = buildBoat(boatBase, EntityType.JUNGLE_BOAT, BoatEntity.BoatVariant.JUNGLE);
|
||||
MANGROVE_BOAT = buildBoat(boatBase, EntityType.MANGROVE_BOAT, BoatEntity.BoatVariant.MANGROVE);
|
||||
OAK_BOAT = buildBoat(boatBase, EntityType.OAK_BOAT, BoatEntity.BoatVariant.OAK);
|
||||
SPRUCE_BOAT = buildBoat(boatBase, EntityType.SPRUCE_BOAT, BoatEntity.BoatVariant.SPRUCE);
|
||||
PALE_OAK_BOAT = buildBoat(boatBase, EntityType.PALE_OAK_BOAT, BoatEntity.BoatVariant.PALE_OAK);
|
||||
|
||||
EntityDefinition<ChestBoatEntity> chestBoatBase = EntityDefinition.<ChestBoatEntity>inherited(null, boatBase)
|
||||
.build();
|
||||
|
||||
ACACIA_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.ACACIA_CHEST_BOAT, BoatEntity.BoatVariant.ACACIA);
|
||||
BAMBOO_CHEST_RAFT = buildChestBoat(chestBoatBase, EntityType.BAMBOO_CHEST_RAFT, BoatEntity.BoatVariant.BAMBOO);
|
||||
BIRCH_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.BIRCH_CHEST_BOAT, BoatEntity.BoatVariant.BIRCH);
|
||||
CHERRY_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.CHERRY_CHEST_BOAT, BoatEntity.BoatVariant.CHERRY);
|
||||
DARK_OAK_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.DARK_OAK_CHEST_BOAT, BoatEntity.BoatVariant.DARK_OAK);
|
||||
JUNGLE_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.JUNGLE_CHEST_BOAT, BoatEntity.BoatVariant.JUNGLE);
|
||||
MANGROVE_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.MANGROVE_CHEST_BOAT, BoatEntity.BoatVariant.MANGROVE);
|
||||
OAK_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.OAK_CHEST_BOAT, BoatEntity.BoatVariant.OAK);
|
||||
SPRUCE_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.SPRUCE_CHEST_BOAT, BoatEntity.BoatVariant.SPRUCE);
|
||||
PALE_OAK_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.PALE_OAK_CHEST_BOAT, BoatEntity.BoatVariant.PALE_OAK);
|
||||
}
|
||||
|
||||
EntityDefinition<LivingEntity> livingEntityBase = EntityDefinition.inherited(LivingEntity::new, entityBase)
|
||||
.addTranslator(MetadataType.BYTE, LivingEntity::setLivingEntityFlags)
|
||||
.addTranslator(MetadataType.FLOAT, LivingEntity::setHealth)
|
||||
.addTranslator(MetadataType.PARTICLES, LivingEntity::setParticles)
|
||||
.addTranslator(MetadataType.BOOLEAN,
|
||||
.addTranslator(MetadataTypes.BYTE, LivingEntity::setLivingEntityFlags)
|
||||
.addTranslator(MetadataTypes.FLOAT, LivingEntity::setHealth)
|
||||
.addTranslator(MetadataTypes.PARTICLES, LivingEntity::setParticles)
|
||||
.addTranslator(MetadataTypes.BOOLEAN,
|
||||
(livingEntity, entityMetadata) -> livingEntity.getDirtyMetadata().put(EntityDataTypes.EFFECT_AMBIENCE, (byte) (((BooleanEntityMetadata) entityMetadata).getPrimitiveValue() ? 1 : 0)))
|
||||
.addTranslator(null) // Arrow count
|
||||
.addTranslator(null) // Stinger count
|
||||
.addTranslator(MetadataType.OPTIONAL_POSITION, LivingEntity::setBedPosition)
|
||||
.addTranslator(MetadataTypes.OPTIONAL_POSITION, LivingEntity::setBedPosition)
|
||||
.build();
|
||||
|
||||
ARMOR_STAND = EntityDefinition.inherited(ArmorStandEntity::new, livingEntityBase)
|
||||
.type(EntityType.ARMOR_STAND)
|
||||
.height(1.975f).width(0.5f)
|
||||
.addTranslator(MetadataType.BYTE, ArmorStandEntity::setArmorStandFlags)
|
||||
.addTranslator(MetadataType.ROTATION, ArmorStandEntity::setHeadRotation)
|
||||
.addTranslator(MetadataType.ROTATION, ArmorStandEntity::setBodyRotation)
|
||||
.addTranslator(MetadataType.ROTATION, ArmorStandEntity::setLeftArmRotation)
|
||||
.addTranslator(MetadataType.ROTATION, ArmorStandEntity::setRightArmRotation)
|
||||
.addTranslator(MetadataType.ROTATION, ArmorStandEntity::setLeftLegRotation)
|
||||
.addTranslator(MetadataType.ROTATION, ArmorStandEntity::setRightLegRotation)
|
||||
.addTranslator(MetadataTypes.BYTE, ArmorStandEntity::setArmorStandFlags)
|
||||
.addTranslator(MetadataTypes.ROTATION, ArmorStandEntity::setHeadRotation)
|
||||
.addTranslator(MetadataTypes.ROTATION, ArmorStandEntity::setBodyRotation)
|
||||
.addTranslator(MetadataTypes.ROTATION, ArmorStandEntity::setLeftArmRotation)
|
||||
.addTranslator(MetadataTypes.ROTATION, ArmorStandEntity::setRightArmRotation)
|
||||
.addTranslator(MetadataTypes.ROTATION, ArmorStandEntity::setLeftLegRotation)
|
||||
.addTranslator(MetadataTypes.ROTATION, ArmorStandEntity::setRightLegRotation)
|
||||
.build();
|
||||
PLAYER = EntityDefinition.<PlayerEntity>inherited(null, livingEntityBase)
|
||||
.type(EntityType.PLAYER)
|
||||
.height(1.8f).width(0.6f)
|
||||
.offset(1.62f)
|
||||
.addTranslator(MetadataType.FLOAT, PlayerEntity::setAbsorptionHearts)
|
||||
.addTranslator(MetadataTypes.FLOAT, PlayerEntity::setAbsorptionHearts)
|
||||
.addTranslator(null) // Player score
|
||||
.addTranslator(MetadataType.BYTE, PlayerEntity::setSkinVisibility)
|
||||
.addTranslator(MetadataTypes.BYTE, PlayerEntity::setSkinVisibility)
|
||||
.addTranslator(null) // Player main hand
|
||||
.addTranslator(MetadataType.NBT_TAG, PlayerEntity::setLeftParrot)
|
||||
.addTranslator(MetadataType.NBT_TAG, PlayerEntity::setRightParrot)
|
||||
.addTranslator(MetadataTypes.NBT_TAG, PlayerEntity::setLeftParrot)
|
||||
.addTranslator(MetadataTypes.NBT_TAG, PlayerEntity::setRightParrot)
|
||||
.build();
|
||||
|
||||
EntityDefinition<MobEntity> mobEntityBase = EntityDefinition.inherited(MobEntity::new, livingEntityBase)
|
||||
.addTranslator(MetadataType.BYTE, MobEntity::setMobFlags)
|
||||
.addTranslator(MetadataTypes.BYTE, MobEntity::setMobFlags)
|
||||
.build();
|
||||
|
||||
// Extends mob
|
||||
|
@ -611,50 +656,59 @@ public final class EntityDefinitions {
|
|||
ALLAY = EntityDefinition.inherited(AllayEntity::new, mobEntityBase)
|
||||
.type(EntityType.ALLAY)
|
||||
.height(0.6f).width(0.35f)
|
||||
.addTranslator(MetadataType.BOOLEAN, AllayEntity::setDancing)
|
||||
.addTranslator(MetadataType.BOOLEAN, AllayEntity::setCanDuplicate)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, AllayEntity::setDancing)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, AllayEntity::setCanDuplicate)
|
||||
.build();
|
||||
BAT = EntityDefinition.inherited(BatEntity::new, mobEntityBase)
|
||||
.type(EntityType.BAT)
|
||||
.height(0.9f).width(0.5f)
|
||||
.addTranslator(MetadataType.BYTE, BatEntity::setBatFlags)
|
||||
.addTranslator(MetadataTypes.BYTE, BatEntity::setBatFlags)
|
||||
.build();
|
||||
BOGGED = EntityDefinition.inherited(BoggedEntity::new, mobEntityBase)
|
||||
.type(EntityType.BOGGED)
|
||||
.height(1.99f).width(0.6f)
|
||||
.addTranslator(MetadataType.BOOLEAN, BoggedEntity::setSheared)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, BoggedEntity::setSheared)
|
||||
.build();
|
||||
BLAZE = EntityDefinition.inherited(BlazeEntity::new, mobEntityBase)
|
||||
.type(EntityType.BLAZE)
|
||||
.height(1.8f).width(0.6f)
|
||||
.addTranslator(MetadataType.BYTE, BlazeEntity::setBlazeFlags)
|
||||
.addTranslator(MetadataTypes.BYTE, BlazeEntity::setBlazeFlags)
|
||||
.build();
|
||||
BREEZE = EntityDefinition.inherited(BreezeEntity::new, mobEntityBase)
|
||||
.type(EntityType.BREEZE)
|
||||
.height(1.77f).width(0.6f)
|
||||
.build();
|
||||
CREAKING = EntityDefinition.inherited(CreakingEntity::new, mobEntityBase)
|
||||
.type(EntityType.CREAKING)
|
||||
.height(2.7f).width(0.9f)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, CreakingEntity::setCanMove)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, CreakingEntity::setActive)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, CreakingEntity::setIsTearingDown)
|
||||
.addTranslator(MetadataTypes.OPTIONAL_POSITION, CreakingEntity::setHomePos)
|
||||
.properties(new GeyserEntityProperties.Builder()
|
||||
.addEnum(CreakingEntity.CREAKING_STATE,
|
||||
"neutral",
|
||||
"hostile_observed",
|
||||
"hostile_unobserved",
|
||||
"twitching",
|
||||
"crumbling")
|
||||
.addInt(CreakingEntity.CREAKING_SWAYING_TICKS, 0, 6)
|
||||
.build())
|
||||
.build();
|
||||
CREEPER = EntityDefinition.inherited(CreeperEntity::new, mobEntityBase)
|
||||
.type(EntityType.CREEPER)
|
||||
.height(1.7f).width(0.6f)
|
||||
.offset(1.62f)
|
||||
.addTranslator(MetadataType.INT, CreeperEntity::setSwelling)
|
||||
.addTranslator(MetadataType.BOOLEAN, (entity, entityMetadata) -> entity.setFlag(EntityFlag.POWERED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
|
||||
.addTranslator(MetadataType.BOOLEAN, CreeperEntity::setIgnited)
|
||||
.build();
|
||||
DOLPHIN = EntityDefinition.inherited(DolphinEntity::new, mobEntityBase)
|
||||
.type(EntityType.DOLPHIN)
|
||||
.height(0.6f).width(0.9f)
|
||||
//TODO check
|
||||
.addTranslator(null) // treasure position
|
||||
.addTranslator(null) // "got fish"
|
||||
.addTranslator(null) // "moistness level"
|
||||
.addTranslator(MetadataTypes.INT, CreeperEntity::setSwelling)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, (entity, entityMetadata) -> entity.setFlag(EntityFlag.POWERED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
|
||||
.addTranslator(MetadataTypes.BOOLEAN, CreeperEntity::setIgnited)
|
||||
.build();
|
||||
ENDERMAN = EntityDefinition.inherited(EndermanEntity::new, mobEntityBase)
|
||||
.type(EntityType.ENDERMAN)
|
||||
.height(2.9f).width(0.6f)
|
||||
.addTranslator(MetadataType.OPTIONAL_BLOCK_STATE, EndermanEntity::setCarriedBlock)
|
||||
.addTranslator(MetadataType.BOOLEAN, EndermanEntity::setScreaming)
|
||||
.addTranslator(MetadataType.BOOLEAN, EndermanEntity::setAngry)
|
||||
.addTranslator(MetadataTypes.OPTIONAL_BLOCK_STATE, EndermanEntity::setCarriedBlock)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, EndermanEntity::setScreaming)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, EndermanEntity::setAngry)
|
||||
.build();
|
||||
ENDERMITE = EntityDefinition.inherited(MonsterEntity::new, mobEntityBase)
|
||||
.type(EntityType.ENDERMITE)
|
||||
|
@ -662,12 +716,12 @@ public final class EntityDefinitions {
|
|||
.build();
|
||||
ENDER_DRAGON = EntityDefinition.inherited(EnderDragonEntity::new, mobEntityBase)
|
||||
.type(EntityType.ENDER_DRAGON)
|
||||
.addTranslator(MetadataType.INT, EnderDragonEntity::setPhase)
|
||||
.addTranslator(MetadataTypes.INT, EnderDragonEntity::setPhase)
|
||||
.build();
|
||||
GHAST = EntityDefinition.inherited(GhastEntity::new, mobEntityBase)
|
||||
.type(EntityType.GHAST)
|
||||
.heightAndWidth(4.0f)
|
||||
.addTranslator(MetadataType.BOOLEAN, GhastEntity::setGhastAttacking)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, GhastEntity::setGhastAttacking)
|
||||
.build();
|
||||
GIANT = EntityDefinition.inherited(GiantEntity::new, mobEntityBase)
|
||||
.type(EntityType.GIANT)
|
||||
|
@ -684,7 +738,7 @@ public final class EntityDefinitions {
|
|||
.type(EntityType.PHANTOM)
|
||||
.height(0.5f).width(0.9f)
|
||||
.offset(0.6f)
|
||||
.addTranslator(MetadataType.INT, PhantomEntity::setPhantomScale)
|
||||
.addTranslator(MetadataTypes.INT, PhantomEntity::setPhantomScale)
|
||||
.build();
|
||||
SILVERFISH = EntityDefinition.inherited(MonsterEntity::new, mobEntityBase)
|
||||
.type(EntityType.SILVERFISH)
|
||||
|
@ -693,35 +747,31 @@ public final class EntityDefinitions {
|
|||
SHULKER = EntityDefinition.inherited(ShulkerEntity::new, mobEntityBase)
|
||||
.type(EntityType.SHULKER)
|
||||
.heightAndWidth(1f)
|
||||
.addTranslator(MetadataType.DIRECTION, ShulkerEntity::setAttachedFace)
|
||||
.addTranslator(MetadataType.BYTE, ShulkerEntity::setShulkerHeight)
|
||||
.addTranslator(MetadataType.BYTE, ShulkerEntity::setShulkerColor)
|
||||
.addTranslator(MetadataTypes.DIRECTION, ShulkerEntity::setAttachedFace)
|
||||
.addTranslator(MetadataTypes.BYTE, ShulkerEntity::setShulkerHeight)
|
||||
.addTranslator(MetadataTypes.BYTE, ShulkerEntity::setShulkerColor)
|
||||
.build();
|
||||
SKELETON = EntityDefinition.inherited(SkeletonEntity::new, mobEntityBase)
|
||||
.type(EntityType.SKELETON)
|
||||
.height(1.8f).width(0.6f)
|
||||
.offset(1.62f)
|
||||
.addTranslator(MetadataType.BOOLEAN, SkeletonEntity::setConvertingToStray)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, SkeletonEntity::setConvertingToStray)
|
||||
.build();
|
||||
SNOW_GOLEM = EntityDefinition.inherited(SnowGolemEntity::new, mobEntityBase)
|
||||
.type(EntityType.SNOW_GOLEM)
|
||||
.height(1.9f).width(0.7f)
|
||||
.addTranslator(MetadataType.BYTE, SnowGolemEntity::setSnowGolemFlags)
|
||||
.addTranslator(MetadataTypes.BYTE, SnowGolemEntity::setSnowGolemFlags)
|
||||
.build();
|
||||
SPIDER = EntityDefinition.inherited(SpiderEntity::new, mobEntityBase)
|
||||
.type(EntityType.SPIDER)
|
||||
.height(0.9f).width(1.4f)
|
||||
.offset(1f)
|
||||
.addTranslator(MetadataType.BYTE, SpiderEntity::setSpiderFlags)
|
||||
.addTranslator(MetadataTypes.BYTE, SpiderEntity::setSpiderFlags)
|
||||
.build();
|
||||
CAVE_SPIDER = EntityDefinition.inherited(SpiderEntity::new, SPIDER)
|
||||
.type(EntityType.CAVE_SPIDER)
|
||||
.height(0.5f).width(0.7f)
|
||||
.build();
|
||||
SQUID = EntityDefinition.inherited(SquidEntity::new, mobEntityBase)
|
||||
.type(EntityType.SQUID)
|
||||
.heightAndWidth(0.8f)
|
||||
.build();
|
||||
STRAY = EntityDefinition.inherited(AbstractSkeletonEntity::new, mobEntityBase)
|
||||
.type(EntityType.STRAY)
|
||||
.height(1.8f).width(0.6f)
|
||||
|
@ -730,20 +780,20 @@ public final class EntityDefinitions {
|
|||
VEX = EntityDefinition.inherited(VexEntity::new, mobEntityBase)
|
||||
.type(EntityType.VEX)
|
||||
.height(0.8f).width(0.4f)
|
||||
.addTranslator(MetadataType.BYTE, VexEntity::setVexFlags)
|
||||
.addTranslator(MetadataTypes.BYTE, VexEntity::setVexFlags)
|
||||
.build();
|
||||
WARDEN = EntityDefinition.inherited(WardenEntity::new, mobEntityBase)
|
||||
.type(EntityType.WARDEN)
|
||||
.height(2.9f).width(0.9f)
|
||||
.addTranslator(MetadataType.INT, WardenEntity::setAngerLevel)
|
||||
.addTranslator(MetadataTypes.INT, WardenEntity::setAngerLevel)
|
||||
.build();
|
||||
WITHER = EntityDefinition.inherited(WitherEntity::new, mobEntityBase)
|
||||
.type(EntityType.WITHER)
|
||||
.height(3.5f).width(0.9f)
|
||||
.addTranslator(MetadataType.INT, WitherEntity::setTarget1)
|
||||
.addTranslator(MetadataType.INT, WitherEntity::setTarget2)
|
||||
.addTranslator(MetadataType.INT, WitherEntity::setTarget3)
|
||||
.addTranslator(MetadataType.INT, WitherEntity::setInvulnerableTicks)
|
||||
.addTranslator(MetadataTypes.INT, WitherEntity::setTarget1)
|
||||
.addTranslator(MetadataTypes.INT, WitherEntity::setTarget2)
|
||||
.addTranslator(MetadataTypes.INT, WitherEntity::setTarget3)
|
||||
.addTranslator(MetadataTypes.INT, WitherEntity::setInvulnerableTicks)
|
||||
.build();
|
||||
WITHER_SKELETON = EntityDefinition.inherited(AbstractSkeletonEntity::new, mobEntityBase)
|
||||
.type(EntityType.WITHER_SKELETON)
|
||||
|
@ -752,23 +802,23 @@ public final class EntityDefinitions {
|
|||
ZOGLIN = EntityDefinition.inherited(ZoglinEntity::new, mobEntityBase)
|
||||
.type(EntityType.ZOGLIN)
|
||||
.height(1.4f).width(1.3965f)
|
||||
.addTranslator(MetadataType.BOOLEAN, ZoglinEntity::setBaby)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, ZoglinEntity::setBaby)
|
||||
.build();
|
||||
ZOMBIE = EntityDefinition.inherited(ZombieEntity::new, mobEntityBase)
|
||||
.type(EntityType.ZOMBIE)
|
||||
.height(1.8f).width(0.6f)
|
||||
.offset(1.62f)
|
||||
.addTranslator(MetadataType.BOOLEAN, ZombieEntity::setZombieBaby)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, ZombieEntity::setZombieBaby)
|
||||
.addTranslator(null) // "set special type", doesn't do anything
|
||||
.addTranslator(MetadataType.BOOLEAN, ZombieEntity::setConvertingToDrowned)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, ZombieEntity::setConvertingToDrowned)
|
||||
.build();
|
||||
ZOMBIE_VILLAGER = EntityDefinition.inherited(ZombieVillagerEntity::new, ZOMBIE)
|
||||
.type(EntityType.ZOMBIE_VILLAGER)
|
||||
.height(1.8f).width(0.6f)
|
||||
.offset(1.62f)
|
||||
.identifier("minecraft:zombie_villager_v2")
|
||||
.addTranslator(MetadataType.BOOLEAN, ZombieVillagerEntity::setTransforming)
|
||||
.addTranslator(MetadataType.VILLAGER_DATA, ZombieVillagerEntity::setZombieVillagerData)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, ZombieVillagerEntity::setTransforming)
|
||||
.addTranslator(MetadataTypes.VILLAGER_DATA, ZombieVillagerEntity::setZombieVillagerData)
|
||||
.build();
|
||||
ZOMBIFIED_PIGLIN = EntityDefinition.inherited(ZombifiedPiglinEntity::new, ZOMBIE) //TODO test how zombie entity metadata is handled?
|
||||
.type(EntityType.ZOMBIFIED_PIGLIN)
|
||||
|
@ -789,7 +839,7 @@ public final class EntityDefinitions {
|
|||
.type(EntityType.GUARDIAN)
|
||||
.heightAndWidth(0.85f)
|
||||
.addTranslator(null) // Moving //TODO
|
||||
.addTranslator(MetadataType.INT, GuardianEntity::setGuardianTarget)
|
||||
.addTranslator(MetadataTypes.INT, GuardianEntity::setGuardianTarget)
|
||||
.build();
|
||||
ELDER_GUARDIAN = EntityDefinition.inherited(ElderGuardianEntity::new, GUARDIAN)
|
||||
.type(EntityType.ELDER_GUARDIAN)
|
||||
|
@ -799,7 +849,7 @@ public final class EntityDefinitions {
|
|||
SLIME = EntityDefinition.inherited(SlimeEntity::new, mobEntityBase)
|
||||
.type(EntityType.SLIME)
|
||||
.heightAndWidth(0.51f)
|
||||
.addTranslator(MetadataType.INT, SlimeEntity::setSlimeScale)
|
||||
.addTranslator(MetadataTypes.INT, SlimeEntity::setSlimeScale)
|
||||
.build();
|
||||
MAGMA_CUBE = EntityDefinition.inherited(MagmaCubeEntity::new, SLIME)
|
||||
.type(EntityType.MAGMA_CUBE)
|
||||
|
@ -815,11 +865,12 @@ public final class EntityDefinitions {
|
|||
PUFFERFISH = EntityDefinition.inherited(PufferFishEntity::new, abstractFishEntityBase)
|
||||
.type(EntityType.PUFFERFISH)
|
||||
.heightAndWidth(0.7f)
|
||||
.addTranslator(MetadataType.INT, PufferFishEntity::setPufferfishSize)
|
||||
.addTranslator(MetadataTypes.INT, PufferFishEntity::setPufferfishSize)
|
||||
.build();
|
||||
SALMON = EntityDefinition.inherited(abstractFishEntityBase.factory(), abstractFishEntityBase)
|
||||
.type(EntityType.SALMON)
|
||||
.height(0.5f).width(0.7f)
|
||||
.addTranslator(null) // Scale/variant - TODO
|
||||
.build();
|
||||
TADPOLE = EntityDefinition.inherited(TadpoleEntity::new, abstractFishEntityBase)
|
||||
.type(EntityType.TADPOLE)
|
||||
|
@ -829,34 +880,29 @@ public final class EntityDefinitions {
|
|||
.type(EntityType.TROPICAL_FISH)
|
||||
.heightAndWidth(0.6f)
|
||||
.identifier("minecraft:tropicalfish")
|
||||
.addTranslator(MetadataType.INT, TropicalFishEntity::setFishVariant)
|
||||
.addTranslator(MetadataTypes.INT, TropicalFishEntity::setFishVariant)
|
||||
.build();
|
||||
|
||||
EntityDefinition<BasePiglinEntity> abstractPiglinEntityBase = EntityDefinition.inherited(BasePiglinEntity::new, mobEntityBase)
|
||||
.addTranslator(MetadataType.BOOLEAN, BasePiglinEntity::setImmuneToZombification)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, BasePiglinEntity::setImmuneToZombification)
|
||||
.build();
|
||||
PIGLIN = EntityDefinition.inherited(PiglinEntity::new, abstractPiglinEntityBase)
|
||||
.type(EntityType.PIGLIN)
|
||||
.height(1.95f).width(0.6f)
|
||||
.addTranslator(MetadataType.BOOLEAN, PiglinEntity::setBaby)
|
||||
.addTranslator(MetadataType.BOOLEAN, PiglinEntity::setChargingCrossbow)
|
||||
.addTranslator(MetadataType.BOOLEAN, PiglinEntity::setDancing)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, PiglinEntity::setBaby)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, PiglinEntity::setChargingCrossbow)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, PiglinEntity::setDancing)
|
||||
.build();
|
||||
PIGLIN_BRUTE = EntityDefinition.inherited(abstractPiglinEntityBase.factory(), abstractPiglinEntityBase)
|
||||
.type(EntityType.PIGLIN_BRUTE)
|
||||
.height(1.95f).width(0.6f)
|
||||
.build();
|
||||
|
||||
GLOW_SQUID = EntityDefinition.inherited(GlowSquidEntity::new, SQUID)
|
||||
.type(EntityType.GLOW_SQUID)
|
||||
.addTranslator(null) // Set dark ticks remaining, possible TODO
|
||||
.build();
|
||||
|
||||
EntityDefinition<RaidParticipantEntity> raidParticipantEntityBase = EntityDefinition.inherited(RaidParticipantEntity::new, mobEntityBase)
|
||||
.addTranslator(null) // Celebrating //TODO
|
||||
.build();
|
||||
EntityDefinition<SpellcasterIllagerEntity> spellcasterEntityBase = EntityDefinition.inherited(SpellcasterIllagerEntity::new, raidParticipantEntityBase)
|
||||
.addTranslator(MetadataType.BYTE, SpellcasterIllagerEntity::setSpellType)
|
||||
.addTranslator(MetadataTypes.BYTE, SpellcasterIllagerEntity::setSpellType)
|
||||
.build();
|
||||
EVOKER = EntityDefinition.inherited(spellcasterEntityBase.factory(), spellcasterEntityBase)
|
||||
.type(EntityType.EVOKER)
|
||||
|
@ -872,7 +918,7 @@ public final class EntityDefinitions {
|
|||
.type(EntityType.PILLAGER)
|
||||
.height(1.8f).width(0.6f)
|
||||
.offset(1.62f)
|
||||
.addTranslator(MetadataType.BOOLEAN, PillagerEntity::setChargingCrossbow)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, PillagerEntity::setChargingCrossbow)
|
||||
.build();
|
||||
RAVAGER = EntityDefinition.inherited(RavagerEntity::new, raidParticipantEntityBase)
|
||||
.type(EntityType.RAVAGER)
|
||||
|
@ -892,7 +938,7 @@ public final class EntityDefinitions {
|
|||
}
|
||||
|
||||
EntityDefinition<AgeableEntity> ageableEntityBase = EntityDefinition.inherited(AgeableEntity::new, mobEntityBase)
|
||||
.addTranslator(MetadataType.BOOLEAN, AgeableEntity::setBaby)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, AgeableEntity::setBaby)
|
||||
.build();
|
||||
|
||||
// Extends ageable
|
||||
|
@ -909,13 +955,13 @@ public final class EntityDefinitions {
|
|||
"rolled_up_relaxing",
|
||||
"rolled_up_unrolling")
|
||||
.build())
|
||||
.addTranslator(MetadataType.ARMADILLO_STATE, ArmadilloEntity::setArmadilloState)
|
||||
.addTranslator(MetadataTypes.ARMADILLO_STATE, ArmadilloEntity::setArmadilloState)
|
||||
.build();
|
||||
AXOLOTL = EntityDefinition.inherited(AxolotlEntity::new, ageableEntityBase)
|
||||
.type(EntityType.AXOLOTL)
|
||||
.height(0.42f).width(0.7f)
|
||||
.addTranslator(MetadataType.INT, AxolotlEntity::setVariant)
|
||||
.addTranslator(MetadataType.BOOLEAN, AxolotlEntity::setPlayingDead)
|
||||
.addTranslator(MetadataTypes.INT, AxolotlEntity::setVariant)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, AxolotlEntity::setPlayingDead)
|
||||
.addTranslator(null) // From bucket
|
||||
.build();
|
||||
BEE = EntityDefinition.inherited(BeeEntity::new, ageableEntityBase)
|
||||
|
@ -924,8 +970,8 @@ public final class EntityDefinitions {
|
|||
.properties(new GeyserEntityProperties.Builder()
|
||||
.addBoolean("minecraft:has_nectar")
|
||||
.build())
|
||||
.addTranslator(MetadataType.BYTE, BeeEntity::setBeeFlags)
|
||||
.addTranslator(MetadataType.INT, BeeEntity::setAngerTime)
|
||||
.addTranslator(MetadataTypes.BYTE, BeeEntity::setBeeFlags)
|
||||
.addTranslator(MetadataTypes.INT, BeeEntity::setAngerTime)
|
||||
.build();
|
||||
CHICKEN = EntityDefinition.inherited(ChickenEntity::new, ageableEntityBase)
|
||||
.type(EntityType.CHICKEN)
|
||||
|
@ -938,89 +984,89 @@ public final class EntityDefinitions {
|
|||
FOX = EntityDefinition.inherited(FoxEntity::new, ageableEntityBase)
|
||||
.type(EntityType.FOX)
|
||||
.height(0.7f).width(0.6f)
|
||||
.addTranslator(MetadataType.INT, FoxEntity::setFoxVariant)
|
||||
.addTranslator(MetadataType.BYTE, FoxEntity::setFoxFlags)
|
||||
.addTranslator(MetadataTypes.INT, FoxEntity::setFoxVariant)
|
||||
.addTranslator(MetadataTypes.BYTE, FoxEntity::setFoxFlags)
|
||||
.addTranslator(null) // Trusted player 1
|
||||
.addTranslator(null) // Trusted player 2
|
||||
.build();
|
||||
FROG = EntityDefinition.inherited(FrogEntity::new, ageableEntityBase)
|
||||
.type(EntityType.FROG)
|
||||
.heightAndWidth(0.5f)
|
||||
.addTranslator(MetadataType.FROG_VARIANT, FrogEntity::setFrogVariant)
|
||||
.addTranslator(MetadataType.OPTIONAL_VARINT, FrogEntity::setTongueTarget)
|
||||
.addTranslator(MetadataTypes.FROG_VARIANT, FrogEntity::setFrogVariant)
|
||||
.addTranslator(MetadataTypes.OPTIONAL_VARINT, FrogEntity::setTongueTarget)
|
||||
.build();
|
||||
HOGLIN = EntityDefinition.inherited(HoglinEntity::new, ageableEntityBase)
|
||||
.type(EntityType.HOGLIN)
|
||||
.height(1.4f).width(1.3965f)
|
||||
.addTranslator(MetadataType.BOOLEAN, HoglinEntity::setImmuneToZombification)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, HoglinEntity::setImmuneToZombification)
|
||||
.build();
|
||||
GOAT = EntityDefinition.inherited(GoatEntity::new, ageableEntityBase)
|
||||
.type(EntityType.GOAT)
|
||||
.height(1.3f).width(0.9f)
|
||||
.addTranslator(MetadataType.BOOLEAN, GoatEntity::setScreamer)
|
||||
.addTranslator(MetadataType.BOOLEAN, GoatEntity::setHasLeftHorn)
|
||||
.addTranslator(MetadataType.BOOLEAN, GoatEntity::setHasRightHorn)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, GoatEntity::setScreamer)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, GoatEntity::setHasLeftHorn)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, GoatEntity::setHasRightHorn)
|
||||
.build();
|
||||
MOOSHROOM = EntityDefinition.inherited(MooshroomEntity::new, ageableEntityBase)
|
||||
.type(EntityType.MOOSHROOM)
|
||||
.height(1.4f).width(0.9f)
|
||||
.addTranslator(MetadataType.STRING, MooshroomEntity::setVariant)
|
||||
.addTranslator(MetadataTypes.STRING, MooshroomEntity::setVariant)
|
||||
.build();
|
||||
OCELOT = EntityDefinition.inherited(OcelotEntity::new, ageableEntityBase)
|
||||
.type(EntityType.OCELOT)
|
||||
.height(0.7f).width(0.6f)
|
||||
.addTranslator(MetadataType.BOOLEAN, (ocelotEntity, entityMetadata) -> ocelotEntity.setFlag(EntityFlag.TRUSTING, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
|
||||
.addTranslator(MetadataTypes.BOOLEAN, (ocelotEntity, entityMetadata) -> ocelotEntity.setFlag(EntityFlag.TRUSTING, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
|
||||
.build();
|
||||
PANDA = EntityDefinition.inherited(PandaEntity::new, ageableEntityBase)
|
||||
.type(EntityType.PANDA)
|
||||
.height(1.25f).width(1.125f)
|
||||
.addTranslator(null) // Unhappy counter
|
||||
.addTranslator(null) // Sneeze counter
|
||||
.addTranslator(MetadataType.INT, PandaEntity::setEatingCounter)
|
||||
.addTranslator(MetadataType.BYTE, PandaEntity::setMainGene)
|
||||
.addTranslator(MetadataType.BYTE, PandaEntity::setHiddenGene)
|
||||
.addTranslator(MetadataType.BYTE, PandaEntity::setPandaFlags)
|
||||
.addTranslator(MetadataTypes.INT, PandaEntity::setEatingCounter)
|
||||
.addTranslator(MetadataTypes.BYTE, PandaEntity::setMainGene)
|
||||
.addTranslator(MetadataTypes.BYTE, PandaEntity::setHiddenGene)
|
||||
.addTranslator(MetadataTypes.BYTE, PandaEntity::setPandaFlags)
|
||||
.build();
|
||||
PIG = EntityDefinition.inherited(PigEntity::new, ageableEntityBase)
|
||||
.type(EntityType.PIG)
|
||||
.heightAndWidth(0.9f)
|
||||
.addTranslator(MetadataType.BOOLEAN, (pigEntity, entityMetadata) -> pigEntity.setFlag(EntityFlag.SADDLED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
|
||||
.addTranslator(MetadataType.INT, PigEntity::setBoost)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, (pigEntity, entityMetadata) -> pigEntity.setFlag(EntityFlag.SADDLED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
|
||||
.addTranslator(MetadataTypes.INT, PigEntity::setBoost)
|
||||
.build();
|
||||
POLAR_BEAR = EntityDefinition.inherited(PolarBearEntity::new, ageableEntityBase)
|
||||
.type(EntityType.POLAR_BEAR)
|
||||
.height(1.4f).width(1.3f)
|
||||
.addTranslator(MetadataType.BOOLEAN, (entity, entityMetadata) -> entity.setFlag(EntityFlag.STANDING, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
|
||||
.addTranslator(MetadataTypes.BOOLEAN, (entity, entityMetadata) -> entity.setFlag(EntityFlag.STANDING, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
|
||||
.build();
|
||||
RABBIT = EntityDefinition.inherited(RabbitEntity::new, ageableEntityBase)
|
||||
.type(EntityType.RABBIT)
|
||||
.height(0.5f).width(0.4f)
|
||||
.addTranslator(MetadataType.INT, RabbitEntity::setRabbitVariant)
|
||||
.addTranslator(MetadataTypes.INT, RabbitEntity::setRabbitVariant)
|
||||
.build();
|
||||
SHEEP = EntityDefinition.inherited(SheepEntity::new, ageableEntityBase)
|
||||
.type(EntityType.SHEEP)
|
||||
.height(1.3f).width(0.9f)
|
||||
.addTranslator(MetadataType.BYTE, SheepEntity::setSheepFlags)
|
||||
.addTranslator(MetadataTypes.BYTE, SheepEntity::setSheepFlags)
|
||||
.build();
|
||||
SNIFFER = EntityDefinition.inherited(SnifferEntity::new, ageableEntityBase)
|
||||
.type(EntityType.SNIFFER)
|
||||
.height(1.75f).width(1.9f)
|
||||
.addTranslator(MetadataType.SNIFFER_STATE, SnifferEntity::setSnifferState)
|
||||
.addTranslator(MetadataTypes.SNIFFER_STATE, SnifferEntity::setSnifferState)
|
||||
.addTranslator(null) // Integer, drop seed at tick
|
||||
.build();
|
||||
STRIDER = EntityDefinition.inherited(StriderEntity::new, ageableEntityBase)
|
||||
.type(EntityType.STRIDER)
|
||||
.height(1.7f).width(0.9f)
|
||||
.addTranslator(MetadataType.INT, StriderEntity::setBoost)
|
||||
.addTranslator(MetadataType.BOOLEAN, StriderEntity::setCold)
|
||||
.addTranslator(MetadataType.BOOLEAN, StriderEntity::setSaddled)
|
||||
.addTranslator(MetadataTypes.INT, StriderEntity::setBoost)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, StriderEntity::setCold)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, StriderEntity::setSaddled)
|
||||
.build();
|
||||
TURTLE = EntityDefinition.inherited(TurtleEntity::new, ageableEntityBase)
|
||||
.type(EntityType.TURTLE)
|
||||
.height(0.4f).width(1.2f)
|
||||
.addTranslator(null) // Home position
|
||||
.addTranslator(MetadataType.BOOLEAN, TurtleEntity::setPregnant)
|
||||
.addTranslator(MetadataType.BOOLEAN, TurtleEntity::setLayingEgg)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, TurtleEntity::setPregnant)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, TurtleEntity::setLayingEgg)
|
||||
.addTranslator(null) // Travel position
|
||||
.addTranslator(null) // Going home
|
||||
.addTranslator(null) // Travelling
|
||||
|
@ -1034,7 +1080,7 @@ public final class EntityDefinitions {
|
|||
.height(1.8f).width(0.6f)
|
||||
.offset(1.62f)
|
||||
.identifier("minecraft:villager_v2")
|
||||
.addTranslator(MetadataType.VILLAGER_DATA, VillagerEntity::setVillagerData)
|
||||
.addTranslator(MetadataTypes.VILLAGER_DATA, VillagerEntity::setVillagerData)
|
||||
.build();
|
||||
WANDERING_TRADER = EntityDefinition.inherited(abstractVillagerEntityBase.factory(), abstractVillagerEntityBase)
|
||||
.type(EntityType.WANDERING_TRADER)
|
||||
|
@ -1043,21 +1089,41 @@ public final class EntityDefinitions {
|
|||
.build();
|
||||
}
|
||||
|
||||
// Water creatures (AgeableWaterCreature)
|
||||
{
|
||||
DOLPHIN = EntityDefinition.inherited(DolphinEntity::new, ageableEntityBase)
|
||||
.type(EntityType.DOLPHIN)
|
||||
.height(0.6f).width(0.9f)
|
||||
//TODO check
|
||||
.addTranslator(null) // treasure position
|
||||
.addTranslator(null) // "got fish"
|
||||
.addTranslator(null) // "moistness level"
|
||||
.build();
|
||||
SQUID = EntityDefinition.inherited(SquidEntity::new, ageableEntityBase)
|
||||
.type(EntityType.SQUID)
|
||||
.heightAndWidth(0.8f)
|
||||
.build();
|
||||
GLOW_SQUID = EntityDefinition.inherited(GlowSquidEntity::new, SQUID)
|
||||
.type(EntityType.GLOW_SQUID)
|
||||
.addTranslator(null) // Set dark ticks remaining, possible TODO
|
||||
.build();
|
||||
}
|
||||
|
||||
// Horses
|
||||
{
|
||||
EntityDefinition<AbstractHorseEntity> abstractHorseEntityBase = EntityDefinition.inherited(AbstractHorseEntity::new, ageableEntityBase)
|
||||
.addTranslator(MetadataType.BYTE, AbstractHorseEntity::setHorseFlags)
|
||||
.addTranslator(MetadataTypes.BYTE, AbstractHorseEntity::setHorseFlags)
|
||||
.build();
|
||||
CAMEL = EntityDefinition.inherited(CamelEntity::new, abstractHorseEntityBase)
|
||||
.type(EntityType.CAMEL)
|
||||
.height(2.375f).width(1.7f)
|
||||
.addTranslator(MetadataType.BOOLEAN, CamelEntity::setDashing)
|
||||
.addTranslator(MetadataType.LONG, CamelEntity::setLastPoseTick)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, CamelEntity::setDashing)
|
||||
.addTranslator(MetadataTypes.LONG, CamelEntity::setLastPoseTick)
|
||||
.build();
|
||||
HORSE = EntityDefinition.inherited(HorseEntity::new, abstractHorseEntityBase)
|
||||
.type(EntityType.HORSE)
|
||||
.height(1.6f).width(1.3965f)
|
||||
.addTranslator(MetadataType.INT, HorseEntity::setHorseVariant)
|
||||
.addTranslator(MetadataTypes.INT, HorseEntity::setHorseVariant)
|
||||
.build();
|
||||
SKELETON_HORSE = EntityDefinition.inherited(SkeletonHorseEntity::new, abstractHorseEntityBase)
|
||||
.type(EntityType.SKELETON_HORSE)
|
||||
|
@ -1068,7 +1134,7 @@ public final class EntityDefinitions {
|
|||
.height(1.6f).width(1.3965f)
|
||||
.build();
|
||||
EntityDefinition<ChestedHorseEntity> chestedHorseEntityBase = EntityDefinition.inherited(ChestedHorseEntity::new, abstractHorseEntityBase)
|
||||
.addTranslator(MetadataType.BOOLEAN, (horseEntity, entityMetadata) -> horseEntity.setFlag(EntityFlag.CHESTED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
|
||||
.addTranslator(MetadataTypes.BOOLEAN, (horseEntity, entityMetadata) -> horseEntity.setFlag(EntityFlag.CHESTED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
|
||||
.build();
|
||||
DONKEY = EntityDefinition.inherited(chestedHorseEntityBase.factory(), chestedHorseEntityBase)
|
||||
.type(EntityType.DONKEY)
|
||||
|
@ -1081,8 +1147,8 @@ public final class EntityDefinitions {
|
|||
LLAMA = EntityDefinition.inherited(LlamaEntity::new, chestedHorseEntityBase)
|
||||
.type(EntityType.LLAMA)
|
||||
.height(1.87f).width(0.9f)
|
||||
.addTranslator(MetadataType.INT, LlamaEntity::setStrength)
|
||||
.addTranslator(MetadataType.INT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityDataTypes.VARIANT, entityMetadata.getValue()))
|
||||
.addTranslator(MetadataTypes.INT, LlamaEntity::setStrength)
|
||||
.addTranslator(MetadataTypes.INT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityDataTypes.VARIANT, entityMetadata.getValue()))
|
||||
.build();
|
||||
TRADER_LLAMA = EntityDefinition.inherited(TraderLlamaEntity::new, LLAMA)
|
||||
.type(EntityType.TRADER_LLAMA)
|
||||
|
@ -1091,30 +1157,30 @@ public final class EntityDefinitions {
|
|||
}
|
||||
|
||||
EntityDefinition<TameableEntity> tameableEntityBase = EntityDefinition.<TameableEntity>inherited(null, ageableEntityBase) // No factory, is abstract
|
||||
.addTranslator(MetadataType.BYTE, TameableEntity::setTameableFlags)
|
||||
.addTranslator(MetadataType.OPTIONAL_UUID, TameableEntity::setOwner)
|
||||
.addTranslator(MetadataTypes.BYTE, TameableEntity::setTameableFlags)
|
||||
.addTranslator(MetadataTypes.OPTIONAL_UUID, TameableEntity::setOwner)
|
||||
.build();
|
||||
CAT = EntityDefinition.inherited(CatEntity::new, tameableEntityBase)
|
||||
.type(EntityType.CAT)
|
||||
.height(0.35f).width(0.3f)
|
||||
.addTranslator(MetadataType.CAT_VARIANT, CatEntity::setCatVariant)
|
||||
.addTranslator(MetadataType.BOOLEAN, CatEntity::setResting)
|
||||
.addTranslator(MetadataTypes.CAT_VARIANT, CatEntity::setCatVariant)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, CatEntity::setResting)
|
||||
.addTranslator(null) // "resting state one" //TODO
|
||||
.addTranslator(MetadataType.INT, CatEntity::setCollarColor)
|
||||
.addTranslator(MetadataTypes.INT, CatEntity::setCollarColor)
|
||||
.build();
|
||||
PARROT = EntityDefinition.inherited(ParrotEntity::new, tameableEntityBase)
|
||||
.type(EntityType.PARROT)
|
||||
.height(0.9f).width(0.5f)
|
||||
.addTranslator(MetadataType.INT, (parrotEntity, entityMetadata) -> parrotEntity.getDirtyMetadata().put(EntityDataTypes.VARIANT, entityMetadata.getValue())) // Parrot color
|
||||
.addTranslator(MetadataTypes.INT, (parrotEntity, entityMetadata) -> parrotEntity.getDirtyMetadata().put(EntityDataTypes.VARIANT, entityMetadata.getValue())) // Parrot color
|
||||
.build();
|
||||
WOLF = EntityDefinition.inherited(WolfEntity::new, tameableEntityBase)
|
||||
.type(EntityType.WOLF)
|
||||
.height(0.85f).width(0.6f)
|
||||
// "Begging" on wiki.vg, "Interested" in Nukkit - the tilt of the head
|
||||
.addTranslator(MetadataType.BOOLEAN, (wolfEntity, entityMetadata) -> wolfEntity.setFlag(EntityFlag.INTERESTED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
|
||||
.addTranslator(MetadataType.INT, WolfEntity::setCollarColor)
|
||||
.addTranslator(MetadataType.INT, WolfEntity::setWolfAngerTime)
|
||||
.addTranslator(MetadataType.WOLF_VARIANT, WolfEntity::setWolfVariant)
|
||||
.addTranslator(MetadataTypes.BOOLEAN, (wolfEntity, entityMetadata) -> wolfEntity.setFlag(EntityFlag.INTERESTED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
|
||||
.addTranslator(MetadataTypes.INT, WolfEntity::setCollarColor)
|
||||
.addTranslator(MetadataTypes.INT, WolfEntity::setWolfAngerTime)
|
||||
.addTranslator(MetadataTypes.WOLF_VARIANT, WolfEntity::setWolfVariant)
|
||||
.build();
|
||||
|
||||
// As of 1.18 these don't track entity data at all
|
||||
|
@ -1122,10 +1188,23 @@ public final class EntityDefinitions {
|
|||
.identifier("minecraft:armor_stand") // Emulated
|
||||
.build(false); // Never sent over the network
|
||||
|
||||
// causes the registries to load
|
||||
if (!EnvironmentUtils.isUnitTesting) {
|
||||
Registries.JAVA_ENTITY_IDENTIFIERS.get().put("minecraft:marker", null); // We don't need an entity definition for this as it is never sent over the network
|
||||
}
|
||||
Registries.JAVA_ENTITY_IDENTIFIERS.get().put("minecraft:marker", null); // We don't need an entity definition for this as it is never sent over the network
|
||||
}
|
||||
|
||||
private static EntityDefinition<BoatEntity> buildBoat(EntityDefinition<BoatEntity> base, EntityType entityType, BoatEntity.BoatVariant variant) {
|
||||
return EntityDefinition.inherited((session, javaId, bedrockId, uuid, definition, position, motion, yaw, pitch, headYaw) ->
|
||||
new BoatEntity(session, javaId, bedrockId, uuid, definition, position, motion, yaw, variant), base)
|
||||
.type(entityType)
|
||||
.identifier("minecraft:boat")
|
||||
.build();
|
||||
}
|
||||
|
||||
private static EntityDefinition<ChestBoatEntity> buildChestBoat(EntityDefinition<ChestBoatEntity> base, EntityType entityType, BoatEntity.BoatVariant variant) {
|
||||
return EntityDefinition.inherited((session, javaId, bedrockId, uuid, definition, position, motion, yaw, pitch, headYaw) ->
|
||||
new ChestBoatEntity(session, javaId, bedrockId, uuid, definition, position, motion, yaw, variant), base)
|
||||
.type(entityType)
|
||||
.identifier("minecraft:chest_boat")
|
||||
.build();
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
|
|
|
@ -32,7 +32,7 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataType;
|
|||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A write-only wrapper for temporarily storing entity metadata that will be sent to Bedrock.
|
||||
* A wrapper for temporarily storing entity metadata that will be sent to Bedrock.
|
||||
*/
|
||||
public final class GeyserDirtyMetadata {
|
||||
private final Map<EntityDataType<?>, Object> metadata = new Object2ObjectLinkedOpenHashMap<>();
|
||||
|
@ -53,6 +53,14 @@ public final class GeyserDirtyMetadata {
|
|||
return !metadata.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Intended for testing purposes only
|
||||
*/
|
||||
public <T> T get(EntityDataType<T> entityData) {
|
||||
//noinspection unchecked
|
||||
return (T) metadata.get(entityData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return metadata.toString();
|
||||
|
|
|
@ -35,22 +35,25 @@ import lombok.Getter;
|
|||
public enum GeyserAttributeType {
|
||||
|
||||
// Universal Attributes
|
||||
FOLLOW_RANGE("minecraft:generic.follow_range", "minecraft:follow_range", 0f, 2048f, 32f),
|
||||
KNOCKBACK_RESISTANCE("minecraft:generic.knockback_resistance", "minecraft:knockback_resistance", 0f, 1f, 0f),
|
||||
MOVEMENT_SPEED("minecraft:generic.movement_speed", "minecraft:movement", 0f, 1024f, 0.1f),
|
||||
FLYING_SPEED("minecraft:generic.flying_speed", "minecraft:movement", 0.0f, 1024.0f, 0.4000000059604645f),
|
||||
ATTACK_DAMAGE("minecraft:generic.attack_damage", "minecraft:attack_damage", 0f, 2048f, 1f),
|
||||
HORSE_JUMP_STRENGTH("minecraft:horse.jump_strength", "minecraft:horse.jump_strength", 0.0f, 2.0f, 0.7f),
|
||||
LUCK("minecraft:generic.luck", "minecraft:luck", -1024f, 1024f, 0f),
|
||||
FOLLOW_RANGE("minecraft:follow_range", "minecraft:follow_range", 0f, 2048f, 32f),
|
||||
KNOCKBACK_RESISTANCE("minecraft:knockback_resistance", "minecraft:knockback_resistance", 0f, 1f, 0f),
|
||||
MOVEMENT_SPEED("minecraft:movement_speed", "minecraft:movement", 0f, 1024f, 0.1f),
|
||||
FLYING_SPEED("minecraft:flying_speed", "minecraft:movement", 0.0f, 1024.0f, 0.4000000059604645f),
|
||||
ATTACK_DAMAGE("minecraft:attack_damage", "minecraft:attack_damage", 0f, 2048f, 1f),
|
||||
HORSE_JUMP_STRENGTH("minecraft:jump_strength", "minecraft:horse.jump_strength", 0.0f, 2.0f, 0.7f),
|
||||
LUCK("minecraft:luck", "minecraft:luck", -1024f, 1024f, 0f),
|
||||
|
||||
// Java Attributes
|
||||
ARMOR("minecraft:generic.armor", null, 0f, 30f, 0f),
|
||||
ARMOR_TOUGHNESS("minecraft:generic.armor_toughness", null, 0F, 20f, 0f),
|
||||
ATTACK_KNOCKBACK("minecraft:generic.attack_knockback", null, 1.5f, Float.MAX_VALUE, 0f),
|
||||
ATTACK_SPEED("minecraft:generic.attack_speed", null, 0f, 1024f, 4f),
|
||||
MAX_HEALTH("minecraft:generic.max_health", null, 0f, 1024f, 20f),
|
||||
SCALE("minecraft:generic.scale", null, 0.0625f, 16f, 1f),
|
||||
BLOCK_INTERACTION_RANGE("minecraft:player.block_interaction_range", null, 0.0f, 64f, 4.5f),
|
||||
ARMOR("minecraft:armor", null, 0f, 30f, 0f),
|
||||
ARMOR_TOUGHNESS("minecraft:armor_toughness", null, 0F, 20f, 0f),
|
||||
ATTACK_KNOCKBACK("minecraft:attack_knockback", null, 1.5f, Float.MAX_VALUE, 0f),
|
||||
ATTACK_SPEED("minecraft:attack_speed", null, 0f, 1024f, 4f),
|
||||
MAX_HEALTH("minecraft:max_health", null, 0f, 1024f, 20f),
|
||||
SCALE("minecraft:scale", null, 0.0625f, 16f, 1f),
|
||||
BLOCK_INTERACTION_RANGE("minecraft:block_interaction_range", null, 0.0f, 64f, 4.5f),
|
||||
MINING_EFFICIENCY("minecraft:mining_efficiency", null, 0f, 1024f, 0f),
|
||||
BLOCK_BREAK_SPEED("minecraft:block_break_speed", null, 0f, 1024f, 1f),
|
||||
SUBMERGED_MINING_SPEED("minecraft:submerged_mining_speed", null, 0f, 20f, 0.2f),
|
||||
|
||||
// Bedrock Attributes
|
||||
ABSORPTION(null, "minecraft:absorption", 0f, 1024f, 0f),
|
||||
|
|
|
@ -32,12 +32,13 @@ import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket;
|
|||
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundPaddleBoatPacket;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -63,16 +64,24 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
|
|||
* Saved for using the "pick" functionality on a boat.
|
||||
*/
|
||||
@Getter
|
||||
private int variant;
|
||||
protected final BoatVariant variant;
|
||||
|
||||
private long leashHolderBedrockId = -1;
|
||||
|
||||
// Looks too fast and too choppy with 0.1f, which is how I believe the Microsoftian client handles it
|
||||
private final float ROWING_SPEED = 0.1f;
|
||||
|
||||
public BoatEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
||||
public BoatEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, BoatVariant variant) {
|
||||
// Initial rotation is incorrect
|
||||
super(session, entityId, geyserId, uuid, definition, position.add(0d, definition.offset(), 0d), motion, yaw + 90, 0, yaw + 90);
|
||||
this.variant = variant;
|
||||
|
||||
// TODO remove once 1.21.40 is dropped
|
||||
if (variant == BoatVariant.PALE_OAK && GameProtocol.isPreWinterDrop(session)) {
|
||||
variant = BoatVariant.BIRCH;
|
||||
}
|
||||
|
||||
dirtyMetadata.put(EntityDataTypes.VARIANT, variant.ordinal());
|
||||
|
||||
// Required to be able to move on land 1.16.200+ or apply gravity not in the water 1.16.100+
|
||||
dirtyMetadata.put(EntityDataTypes.IS_BUOYANT, true);
|
||||
|
@ -124,15 +133,6 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
|
|||
moveRelative(0, 0, 0, yaw + 90, 0, 0, isOnGround);
|
||||
}
|
||||
|
||||
public void setVariant(IntEntityMetadata entityMetadata) {
|
||||
variant = entityMetadata.getPrimitiveValue();
|
||||
dirtyMetadata.put(EntityDataTypes.VARIANT, switch (variant) {
|
||||
case 6, 7, 8 -> variant - 1; // dark_oak, mangrove, bamboo
|
||||
case 5 -> 8; // cherry
|
||||
default -> variant;
|
||||
});
|
||||
}
|
||||
|
||||
public void setPaddlingLeft(BooleanEntityMetadata entityMetadata) {
|
||||
isPaddlingLeft = entityMetadata.getPrimitiveValue();
|
||||
if (!isPaddlingLeft) {
|
||||
|
@ -187,7 +187,13 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
|
|||
@Override
|
||||
public void tick() {
|
||||
// Java sends simply "true" and "false" (is_paddling_left), Bedrock keeps sending packets as you're rowing
|
||||
doTick = !doTick; // Run every 100 ms
|
||||
if (session.getPlayerEntity().getVehicle() == this) {
|
||||
// For packet timing accuracy, we'll send the packets here, as that's what Java Edition 1.21.3 does.
|
||||
ServerboundPaddleBoatPacket steerPacket = new ServerboundPaddleBoatPacket(session.isSteeringLeft(), session.isSteeringRight());
|
||||
session.sendDownstreamGamePacket(steerPacket);
|
||||
return;
|
||||
}
|
||||
doTick = !doTick; // Run every other tick
|
||||
if (!doTick || passengers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -219,4 +225,22 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
|
|||
packet.setRowingTime(rowTime);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ordered by Bedrock ordinal
|
||||
*/
|
||||
public enum BoatVariant {
|
||||
OAK,
|
||||
SPRUCE,
|
||||
BIRCH,
|
||||
JUNGLE,
|
||||
ACACIA,
|
||||
DARK_OAK,
|
||||
MANGROVE,
|
||||
BAMBOO,
|
||||
CHERRY,
|
||||
PALE_OAK;
|
||||
|
||||
BoatVariant() {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,8 +35,8 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
|||
import java.util.UUID;
|
||||
|
||||
public class ChestBoatEntity extends BoatEntity {
|
||||
public ChestBoatEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
||||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
public ChestBoatEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, BoatVariant variant) {
|
||||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, variant);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.entity.type;
|
||||
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class EnderEyeEntity extends Entity {
|
||||
public EnderEyeEntity(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();
|
||||
// Correct sizing
|
||||
dirtyMetadata.put(EntityDataTypes.SCALE, 0.5f);
|
||||
}
|
||||
}
|
|
@ -25,12 +25,6 @@
|
|||
|
||||
package org.geysermc.geyser.entity.type;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
@ -67,6 +61,13 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEnt
|
|||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class Entity implements GeyserEntity {
|
||||
|
@ -89,6 +90,7 @@ public class Entity implements GeyserEntity {
|
|||
|
||||
/**
|
||||
* x = Yaw, y = Pitch, z = HeadYaw
|
||||
* Java: Y = Yaw, X = Pitch
|
||||
*/
|
||||
protected float yaw;
|
||||
protected float pitch;
|
||||
|
@ -174,6 +176,7 @@ public class Entity implements GeyserEntity {
|
|||
setFlag(EntityFlag.HAS_COLLISION, true);
|
||||
setFlag(EntityFlag.CAN_SHOW_NAME, true);
|
||||
setFlag(EntityFlag.CAN_CLIMB, true);
|
||||
setFlag(EntityFlag.HIDDEN_WHEN_INVISIBLE, true);
|
||||
// Let the Java server (or us) supply all sounds for an entity
|
||||
setClientSideSilent();
|
||||
}
|
||||
|
@ -699,9 +702,4 @@ public class Entity implements GeyserEntity {
|
|||
packet.setData(data);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <I extends Entity> @Nullable I as(Class<I> entityClass) {
|
||||
return entityClass.isInstance(this) ? (I) this : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ public class FireworkEntity extends Entity {
|
|||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
DataComponents components = item.getDataComponents();
|
||||
DataComponents components = item.getDataComponentsPatch();
|
||||
if (components == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -113,14 +113,23 @@ public class ItemFrameEntity extends Entity {
|
|||
if (entityMetadata.getValue() != null) {
|
||||
this.heldItem = entityMetadata.getValue();
|
||||
ItemData itemData = ItemTranslator.translateToBedrock(session, heldItem);
|
||||
|
||||
String customIdentifier = session.getItemMappings().getCustomIdMappings().get(itemData.getDefinition().getRuntimeId());
|
||||
|
||||
NbtMapBuilder builder = NbtMap.builder();
|
||||
|
||||
builder.putByte("Count", (byte) itemData.getCount());
|
||||
if (itemData.getTag() != null) {
|
||||
builder.put("tag", itemData.getTag());
|
||||
NbtMap itemDataTag = itemData.getTag();
|
||||
if (itemDataTag != null) {
|
||||
// Remove custom name that Geyser sets for items due to translating non-"custom_name" components
|
||||
String customName = ItemTranslator.getCustomName(session, heldItem.getDataComponentsPatch(),
|
||||
session.getItemMappings().getMapping(heldItem), 'f', true, false);
|
||||
if (customName == null) {
|
||||
// No custom name found, must modify tag if custom name exists
|
||||
NbtMapBuilder copy = itemDataTag.toBuilder();
|
||||
copy.remove("display"); // Also removes lore, but, should not matter
|
||||
itemDataTag = copy.build();
|
||||
}
|
||||
|
||||
builder.put("tag", itemDataTag);
|
||||
}
|
||||
builder.putShort("Damage", (short) itemData.getDamage());
|
||||
builder.putString("Name", customIdentifier != null ? customIdentifier : session.getItemMappings().getMapping(entityMetadata.getValue()).getBedrockIdentifier());
|
||||
|
|
|
@ -25,11 +25,6 @@
|
|||
|
||||
package org.geysermc.geyser.entity.type;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
@ -66,11 +61,17 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEnt
|
|||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.particle.EntityEffectParticleData;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.particle.Particle;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.particle.ParticleType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class LivingEntity extends Entity {
|
||||
|
@ -343,7 +344,7 @@ public class LivingEntity extends Entity {
|
|||
*/
|
||||
// Implementation note for 1.20.5: this code was moved to the NameTag item.
|
||||
protected final InteractionResult checkInteractWithNameTag(GeyserItemStack itemStack) {
|
||||
if (itemStack.getComponent(DataComponentType.CUSTOM_NAME) != null) {
|
||||
if (itemStack.getComponent(DataComponentTypes.CUSTOM_NAME) != null) {
|
||||
// The mob shall be named
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
@ -445,35 +446,35 @@ public class LivingEntity extends Entity {
|
|||
protected void updateAttribute(Attribute javaAttribute, List<AttributeData> newAttributes) {
|
||||
if (javaAttribute.getType() instanceof AttributeType.Builtin type) {
|
||||
switch (type) {
|
||||
case GENERIC_MAX_HEALTH -> {
|
||||
case MAX_HEALTH -> {
|
||||
// Since 1.18.0, setting the max health to 0 or below causes the entity to die on Bedrock but not on Java
|
||||
// See https://github.com/GeyserMC/Geyser/issues/2971
|
||||
this.maxHealth = Math.max((float) AttributeUtils.calculateValue(javaAttribute), 1f);
|
||||
newAttributes.add(createHealthAttribute());
|
||||
}
|
||||
case GENERIC_MOVEMENT_SPEED -> {
|
||||
case MOVEMENT_SPEED -> {
|
||||
AttributeData attributeData = calculateAttribute(javaAttribute, GeyserAttributeType.MOVEMENT_SPEED);
|
||||
newAttributes.add(attributeData);
|
||||
if (this instanceof ClientVehicle clientVehicle) {
|
||||
clientVehicle.getVehicleComponent().setMoveSpeed(attributeData.getValue());
|
||||
}
|
||||
}
|
||||
case GENERIC_STEP_HEIGHT -> {
|
||||
case STEP_HEIGHT -> {
|
||||
if (this instanceof ClientVehicle clientVehicle) {
|
||||
clientVehicle.getVehicleComponent().setStepHeight((float) AttributeUtils.calculateValue(javaAttribute));
|
||||
}
|
||||
}
|
||||
case GENERIC_GRAVITY -> {
|
||||
case GRAVITY -> {
|
||||
if (this instanceof ClientVehicle clientVehicle) {
|
||||
clientVehicle.getVehicleComponent().setGravity(AttributeUtils.calculateValue(javaAttribute));
|
||||
}
|
||||
}
|
||||
case GENERIC_ATTACK_DAMAGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.ATTACK_DAMAGE));
|
||||
case GENERIC_FLYING_SPEED -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED));
|
||||
case GENERIC_FOLLOW_RANGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FOLLOW_RANGE));
|
||||
case GENERIC_KNOCKBACK_RESISTANCE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.KNOCKBACK_RESISTANCE));
|
||||
case GENERIC_JUMP_STRENGTH -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.HORSE_JUMP_STRENGTH));
|
||||
case GENERIC_SCALE -> {
|
||||
case ATTACK_DAMAGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.ATTACK_DAMAGE));
|
||||
case FLYING_SPEED -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED));
|
||||
case FOLLOW_RANGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FOLLOW_RANGE));
|
||||
case KNOCKBACK_RESISTANCE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.KNOCKBACK_RESISTANCE));
|
||||
case JUMP_STRENGTH -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.HORSE_JUMP_STRENGTH));
|
||||
case SCALE -> {
|
||||
// Attribute on Java, entity data on Bedrock
|
||||
setAttributeScale((float) AttributeUtils.calculateValue(javaAttribute));
|
||||
updateBedrockMetadata();
|
||||
|
|
|
@ -25,20 +25,40 @@
|
|||
|
||||
package org.geysermc.geyser.entity.type;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3d;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityDeltaPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityMotionPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
import org.geysermc.geyser.util.MathUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.MinecartStep;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundMoveMinecartPacket;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MinecartEntity extends Entity {
|
||||
public class MinecartEntity extends Entity implements Tickable {
|
||||
private static final int POS_ROT_LERP_TICKS = 3;
|
||||
|
||||
private final List<MinecartStep> lerpSteps = new LinkedList<>();
|
||||
private final List<MinecartStep> currentLerpSteps = new LinkedList<>();
|
||||
|
||||
private MinecartStep lastCompletedStep = new MinecartStep(Vector3d.ZERO, Vector3d. ZERO, 0.0F, 0.0F, 0.0F);
|
||||
private float currentStepsTotalWeight = 0.0F;
|
||||
private int lerpDelay = 0;
|
||||
|
||||
private PartialStep cachedPartialStep;
|
||||
private int cachedStepDelay;
|
||||
private float cachedDelta;
|
||||
|
||||
public MinecartEntity(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.add(0d, definition.offset(), 0d), motion, yaw, pitch, headYaw);
|
||||
|
@ -58,6 +78,131 @@ public class MinecartEntity extends Entity {
|
|||
dirtyMetadata.put(EntityDataTypes.CUSTOM_DISPLAY, (byte) (entityMetadata.getPrimitiveValue() ? 1 : 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (!session.isUsingExperimentalMinecartLogic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// All minecart lerp code here and in the methods below has been based off of the code in the Java NewMinecartBehavior class
|
||||
lerpDelay--;
|
||||
if (lerpDelay <= 0) {
|
||||
updateCompletedStep();
|
||||
currentLerpSteps.clear();
|
||||
if (!lerpSteps.isEmpty()) {
|
||||
currentLerpSteps.addAll(lerpSteps);
|
||||
lerpSteps.clear();
|
||||
currentStepsTotalWeight = 0.0F;
|
||||
|
||||
for (MinecartStep step : currentLerpSteps) {
|
||||
currentStepsTotalWeight += step.weight();
|
||||
}
|
||||
|
||||
lerpDelay = currentStepsTotalWeight == 0.0F ? 0 : POS_ROT_LERP_TICKS;
|
||||
}
|
||||
}
|
||||
|
||||
if (isLerping()) {
|
||||
float delta = 1.0F; // This is always 1, maybe it should be removed
|
||||
|
||||
Vector3f position = getCurrentLerpPosition(delta).toFloat();
|
||||
Vector3f movement = getCurrentLerpMovement(delta).toFloat();
|
||||
setPosition(position);
|
||||
setMotion(movement);
|
||||
|
||||
setYaw(180.0F - getCurrentLerpYaw(delta));
|
||||
setPitch(getCurrentLerpPitch(delta));
|
||||
|
||||
MoveEntityDeltaPacket moveEntityPacket = new MoveEntityDeltaPacket();
|
||||
moveEntityPacket.setRuntimeEntityId(geyserId);
|
||||
|
||||
moveEntityPacket.setX(position.getX());
|
||||
moveEntityPacket.setY(position.getY() + definition.offset());
|
||||
moveEntityPacket.setZ(position.getZ());
|
||||
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_X);
|
||||
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Y);
|
||||
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Z);
|
||||
|
||||
moveEntityPacket.setYaw(getYaw());
|
||||
moveEntityPacket.setPitch(getPitch());
|
||||
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_YAW);
|
||||
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH);
|
||||
|
||||
SetEntityMotionPacket entityMotionPacket = new SetEntityMotionPacket();
|
||||
entityMotionPacket.setRuntimeEntityId(geyserId);
|
||||
entityMotionPacket.setMotion(movement);
|
||||
|
||||
session.sendUpstreamPacket(moveEntityPacket);
|
||||
session.sendUpstreamPacket(entityMotionPacket);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleMinecartMovePacket(ClientboundMoveMinecartPacket packet) {
|
||||
lerpSteps.addAll(packet.getLerpSteps());
|
||||
}
|
||||
|
||||
private boolean isLerping() {
|
||||
return !currentLerpSteps.isEmpty();
|
||||
}
|
||||
|
||||
private float getCurrentLerpPitch(float delta) {
|
||||
PartialStep partialStep = getCurrentLerpStep(delta);
|
||||
return lerpRotation(partialStep.delta, partialStep.previousStep.xRot(), partialStep.currentStep.xRot());
|
||||
}
|
||||
|
||||
private float getCurrentLerpYaw(float delta) {
|
||||
PartialStep partialStep = getCurrentLerpStep(delta);
|
||||
return lerpRotation(partialStep.delta, partialStep.previousStep.yRot(), partialStep.currentStep.yRot());
|
||||
}
|
||||
|
||||
private Vector3d getCurrentLerpPosition(float delta) {
|
||||
PartialStep partialStep = getCurrentLerpStep(delta);
|
||||
return lerp(partialStep.delta, partialStep.previousStep.position(), partialStep.currentStep.position());
|
||||
}
|
||||
|
||||
private Vector3d getCurrentLerpMovement(float delta) {
|
||||
PartialStep partialStep = getCurrentLerpStep(delta);
|
||||
return lerp(partialStep.delta, partialStep.previousStep.movement(), partialStep.currentStep.movement());
|
||||
}
|
||||
|
||||
private PartialStep getCurrentLerpStep(float delta) {
|
||||
if (cachedDelta != delta || lerpDelay != cachedStepDelay || cachedPartialStep == null) {
|
||||
float g = ((POS_ROT_LERP_TICKS - lerpDelay) + delta) / POS_ROT_LERP_TICKS;
|
||||
float totalWeight = 0.0F;
|
||||
float stepDelta = 1.0F;
|
||||
boolean foundStep = false;
|
||||
|
||||
int step;
|
||||
for (step = 0; step < currentLerpSteps.size(); step++) {
|
||||
float currentWeight = currentLerpSteps.get(step).weight();
|
||||
if (!(currentWeight <= 0.0F)) {
|
||||
totalWeight += currentWeight;
|
||||
if ((double) totalWeight >= currentStepsTotalWeight * (double) g) {
|
||||
float h = totalWeight - currentWeight;
|
||||
stepDelta = (g * currentStepsTotalWeight - h) / currentWeight;
|
||||
foundStep = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundStep) {
|
||||
step = currentLerpSteps.size() - 1;
|
||||
}
|
||||
|
||||
MinecartStep currentStep = currentLerpSteps.get(step);
|
||||
MinecartStep previousStep = step > 0 ? currentLerpSteps.get(step - 1) : lastCompletedStep;
|
||||
cachedPartialStep = new PartialStep(stepDelta, currentStep, previousStep);
|
||||
cachedStepDelay = lerpDelay;
|
||||
cachedDelta = delta;
|
||||
}
|
||||
return cachedPartialStep;
|
||||
}
|
||||
|
||||
private void updateCompletedStep() {
|
||||
lastCompletedStep = new MinecartStep(position.toDouble(), motion.toDouble(), yaw, pitch, 0.0F);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
|
||||
super.moveAbsolute(position.add(0d, this.definition.offset(), 0d), yaw, pitch, headYaw, isOnGround, teleported);
|
||||
|
@ -103,4 +248,19 @@ public class MinecartEntity extends Entity {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Vector3d lerp(double delta, Vector3d start, Vector3d end) {
|
||||
return Vector3d.from(lerp(delta, start.getX(), end.getX()), lerp(delta, start.getY(), end.getY()), lerp(delta, start.getZ(), end.getZ()));
|
||||
}
|
||||
|
||||
public static double lerp(double delta, double start, double end) {
|
||||
return start + delta * (end - start);
|
||||
}
|
||||
|
||||
private static float lerpRotation(float delta, float start, float end) {
|
||||
return start + delta * MathUtils.wrapDegrees(end - start);
|
||||
}
|
||||
|
||||
private record PartialStep(float delta, MinecartStep currentStep, MinecartStep previousStep) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,18 +25,25 @@
|
|||
|
||||
package org.geysermc.geyser.entity.type;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
// Note: 1.19.4 requires that the billboard is set to something in order to show, on Java Edition
|
||||
@Getter
|
||||
public class TextDisplayEntity extends DisplayBaseEntity {
|
||||
|
||||
private int lineCount;
|
||||
|
||||
public TextDisplayEntity(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.add(0, definition.offset(), 0), motion, yaw, pitch, headYaw);
|
||||
}
|
||||
|
@ -60,6 +67,15 @@ public class TextDisplayEntity extends DisplayBaseEntity {
|
|||
}
|
||||
|
||||
public void setText(EntityMetadata<Component, ?> entityMetadata) {
|
||||
this.dirtyMetadata.put(EntityDataTypes.NAME, MessageTranslator.convertMessage(entityMetadata.getValue()));
|
||||
this.dirtyMetadata.put(EntityDataTypes.NAME, MessageTranslator.convertMessage(entityMetadata.getValue(), session.locale()));
|
||||
calculateLineCount(entityMetadata.getValue());
|
||||
}
|
||||
|
||||
private void calculateLineCount(@Nullable Component text) {
|
||||
if (text == null) {
|
||||
lineCount = 0;
|
||||
return;
|
||||
}
|
||||
lineCount = PlainTextComponentSerializer.plainText().serialize(text).split("\n").length;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,12 +25,14 @@
|
|||
|
||||
package org.geysermc.geyser.entity.type;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -39,7 +41,7 @@ import java.util.UUID;
|
|||
*/
|
||||
public class ThrowableItemEntity extends ThrowableEntity {
|
||||
/**
|
||||
* Number of ticks since the entity was spawned by the Java server
|
||||
* Number of draw ticks since the entity was spawned by the Java server
|
||||
*/
|
||||
private int age;
|
||||
private boolean invisible;
|
||||
|
@ -48,29 +50,38 @@ public class ThrowableItemEntity extends ThrowableEntity {
|
|||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
setFlag(EntityFlag.INVISIBLE, true);
|
||||
invisible = false;
|
||||
}
|
||||
|
||||
private void checkVisibility() {
|
||||
if (invisible != getFlag(EntityFlag.INVISIBLE)) {
|
||||
if (!invisible) {
|
||||
Vector3f playerPos = session.getPlayerEntity().getPosition();
|
||||
// Prevent projectiles from blocking the player's screen
|
||||
if (age >= 4 || position.distanceSquared(playerPos) > 16) {
|
||||
setFlag(EntityFlag.INVISIBLE, false);
|
||||
updateBedrockMetadata();
|
||||
}
|
||||
} else {
|
||||
setFlag(EntityFlag.INVISIBLE, true);
|
||||
updateBedrockMetadata();
|
||||
}
|
||||
}
|
||||
age++;
|
||||
age = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
protected void initializeMetadata() {
|
||||
super.initializeMetadata();
|
||||
// Correct sizing
|
||||
dirtyMetadata.put(EntityDataTypes.SCALE, 0.5f);
|
||||
}
|
||||
|
||||
private void checkVisibility() {
|
||||
age++;
|
||||
|
||||
// Prevent projectiles from blocking the player's screen
|
||||
if (session.isTickingFrozen()) {
|
||||
// This may seem odd, but it matches java edition
|
||||
Vector3f playerPos = session.getPlayerEntity().getPosition().down(EntityDefinitions.PLAYER.offset());
|
||||
setInvisible(playerPos.distanceSquared(position.add(0, definition.offset(), 0)) < 12.25);
|
||||
} else {
|
||||
setInvisible(age < 2);
|
||||
}
|
||||
|
||||
if (invisible != getFlag(EntityFlag.INVISIBLE)) {
|
||||
setFlag(EntityFlag.INVISIBLE, invisible);
|
||||
updateBedrockMetadata();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTick() {
|
||||
checkVisibility();
|
||||
super.tick();
|
||||
super.drawTick();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -36,7 +36,7 @@ import org.geysermc.geyser.registry.Registries;
|
|||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.PotionContents;
|
||||
|
||||
|
@ -59,9 +59,9 @@ public class ThrownPotionEntity extends ThrowableItemEntity {
|
|||
setFlag(EntityFlag.LINGERING, false);
|
||||
} else {
|
||||
// As of Java 1.19.3, the server/client doesn't seem to care of the item is actually a potion?
|
||||
DataComponents components = itemStack.getDataComponents();
|
||||
DataComponents components = itemStack.getDataComponentsPatch();
|
||||
if (components != null) {
|
||||
PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS);
|
||||
PotionContents potionContents = components.get(DataComponentTypes.POTION_CONTENTS);
|
||||
if (potionContents != null) {
|
||||
Potion potion = Potion.getByJavaId(potionContents.getPotionId());
|
||||
if (potion != null) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -26,8 +26,21 @@
|
|||
package org.geysermc.geyser.entity.type;
|
||||
|
||||
/**
|
||||
* Implemented onto anything that should have code ran every Minecraft tick - 50 milliseconds.
|
||||
* Implemented onto anything that should have code ran every Minecraft tick.
|
||||
* By default, the Java server runs at 20 TPS, 50 milliseconds for each tick.
|
||||
*/
|
||||
public interface Tickable {
|
||||
/**
|
||||
* This function gets called every tick at all times, even when the server requests that
|
||||
* the game should be frozen. This should be used for updating things that are always
|
||||
* client side updated on Java, regardless of if the server is frozen or not.
|
||||
*/
|
||||
default void drawTick() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This function gets called every game tick as long as the
|
||||
* game tick loop isn't frozen.
|
||||
*/
|
||||
void tick();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -23,27 +23,21 @@
|
|||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.registry.type;
|
||||
package org.geysermc.geyser.entity.type.living;
|
||||
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
/**
|
||||
* Implements ItemDefinition while also providing a reference to our item mappings.
|
||||
*/
|
||||
public record GeyserItemDefinition(Item javaItem, String identifier, boolean componentBased, int runtimeId) implements ItemDefinition {
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return identifier;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class AgeableWaterEntity extends AgeableEntity {
|
||||
public AgeableWaterEntity(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 boolean isComponentBased() {
|
||||
return componentBased;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRuntimeId() {
|
||||
return runtimeId;
|
||||
public boolean canBeLeashed() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type.living;
|
|||
|
||||
import lombok.Getter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
|
@ -36,6 +37,7 @@ import org.geysermc.geyser.entity.EntityDefinition;
|
|||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.LivingEntity;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.scoreboard.Team;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.MathUtils;
|
||||
|
@ -123,6 +125,12 @@ public class ArmorStandEntity extends LivingEntity {
|
|||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateNametag(@Nullable Team team) {
|
||||
// unlike all other LivingEntities, armor stands are not affected by team nametag visibility
|
||||
super.updateNametag(team, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplayName(EntityMetadata<Optional<Component>, ?> entityMetadata) {
|
||||
super.setDisplayName(entityMetadata);
|
||||
|
|
|
@ -37,7 +37,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
|||
|
||||
import java.util.UUID;
|
||||
|
||||
public class DolphinEntity extends WaterEntity {
|
||||
public class DolphinEntity extends AgeableWaterEntity {
|
||||
public DolphinEntity(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);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class SquidEntity extends WaterEntity implements Tickable {
|
||||
public class SquidEntity extends AgeableWaterEntity implements Tickable {
|
||||
private float targetPitch;
|
||||
private float targetYaw;
|
||||
|
||||
|
|
|
@ -33,8 +33,9 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
|||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.type.living.AgeableEntity;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
|
@ -48,7 +49,7 @@ public abstract class AnimalEntity extends AgeableEntity {
|
|||
}
|
||||
|
||||
protected final boolean canEat(GeyserItemStack itemStack) {
|
||||
ItemTag tag = getFoodTag();
|
||||
Tag<Item> tag = getFoodTag();
|
||||
if (tag == null) {
|
||||
return false;
|
||||
}
|
||||
|
@ -58,7 +59,7 @@ public abstract class AnimalEntity extends AgeableEntity {
|
|||
/**
|
||||
* @return the tag associated with this animal for eating food. Null for nothing or different behavior.
|
||||
*/
|
||||
protected abstract @Nullable ItemTag getFoodTag();
|
||||
protected abstract @Nullable Tag<Item> getFoodTag();
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
|
|
|
@ -28,8 +28,10 @@ package org.geysermc.geyser.entity.type.living.animal;
|
|||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.ArmadilloState;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
|
||||
|
||||
|
@ -75,7 +77,7 @@ public class ArmadilloEntity extends AnimalEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.ARMADILLO_FOOD;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,8 +32,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
|||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.EntityUtils;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
|
@ -62,7 +64,7 @@ public class AxolotlEntity extends AnimalEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.AXOLOTL_FOOD;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,8 +32,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
|
|||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
|
||||
|
@ -69,7 +71,7 @@ public class BeeEntity extends AnimalEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.BEE_FOOD;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,10 @@ package org.geysermc.geyser.entity.type.living.animal;
|
|||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -41,7 +43,7 @@ public class ChickenEntity extends AnimalEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.CHICKEN_FOOD;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,8 +33,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
|||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
|
@ -69,7 +71,7 @@ public class CowEntity extends AnimalEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.COW_FOOD;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,10 @@ import org.cloudburstmc.math.vector.Vector3f;
|
|||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
|
||||
|
@ -57,7 +59,7 @@ public class FoxEntity extends AnimalEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.FOX_FOOD;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,8 +31,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
|||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
|
||||
|
@ -77,7 +79,7 @@ public class FrogEntity extends AnimalEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.FROG_FOOD;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,8 +34,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
|||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
|
@ -99,7 +101,7 @@ public class GoatEntity extends AnimalEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.GOAT_FOOD;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,10 @@ import org.cloudburstmc.math.vector.Vector3f;
|
|||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -58,7 +60,7 @@ public class HoglinEntity extends AnimalEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.HOGLIN_FOOD;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,8 +31,10 @@ import org.cloudburstmc.math.vector.Vector3f;
|
|||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
|
@ -47,7 +49,7 @@ public class OcelotEntity extends AnimalEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.OCELOT_FOOD;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,8 +34,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
|||
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
|
@ -90,7 +92,7 @@ public class PandaEntity extends AnimalEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.PANDA_FOOD;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,9 +38,11 @@ import org.geysermc.geyser.entity.vehicle.BoostableVehicleComponent;
|
|||
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
|
||||
import org.geysermc.geyser.entity.vehicle.VehicleComponent;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.EntityUtils;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
@ -58,7 +60,7 @@ public class PigEntity extends AnimalEntity implements Tickable, ClientVehicle {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.PIG_FOOD;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,8 +28,9 @@ package org.geysermc.geyser.entity.type.living.animal;
|
|||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -41,7 +42,7 @@ public class PolarBearEntity extends AnimalEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,8 +31,10 @@ import org.cloudburstmc.math.vector.Vector3f;
|
|||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.EntityUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
|
||||
|
@ -79,7 +81,7 @@ public class RabbitEntity extends AnimalEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.RABBIT_FOOD;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,8 +34,10 @@ import org.geysermc.geyser.entity.EntityDefinition;
|
|||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.type.DyeItem;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
|
@ -59,7 +61,7 @@ public class SheepEntity extends AnimalEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.SHEEP_FOOD;
|
||||
}
|
||||
|
||||
|
@ -103,4 +105,4 @@ public class SheepEntity extends AnimalEntity {
|
|||
private boolean canDye(GeyserItemStack item) {
|
||||
return item.asItem() instanceof DyeItem dyeItem && dyeItem.dyeColor() != this.color && !getFlag(EntityFlag.SHEARED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,8 +35,10 @@ 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 org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.SnifferState;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
|
||||
|
@ -73,7 +75,7 @@ public class SnifferEntity extends AnimalEntity implements Tickable {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.SNIFFER_FOOD;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,9 +39,11 @@ import org.geysermc.geyser.entity.vehicle.BoostableVehicleComponent;
|
|||
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
|
||||
import org.geysermc.geyser.entity.vehicle.VehicleComponent;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.EntityUtils;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
@ -105,7 +107,7 @@ public class StriderEntity extends AnimalEntity implements Tickable, ClientVehic
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.STRIDER_FOOD;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,8 +29,10 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -51,7 +53,7 @@ public class TurtleEntity extends AnimalEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.TURTLE_FOOD;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,8 +39,10 @@ import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
|||
import org.geysermc.geyser.entity.type.living.animal.AnimalEntity;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
|
@ -119,7 +121,7 @@ public class AbstractHorseEntity extends AnimalEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.HORSE_FOOD;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,12 +35,14 @@ 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.item.type.Item;
|
||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.geyser.entity.vehicle.CamelVehicleComponent;
|
||||
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
|
||||
import org.geysermc.geyser.entity.vehicle.VehicleComponent;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.Attribute;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
|
||||
|
@ -100,7 +102,7 @@ public class CamelEntity extends AbstractHorseEntity implements ClientVehicle {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ItemTag getFoodTag() {
|
||||
protected @Nullable Tag<Item> getFoodTag() {
|
||||
return ItemTag.CAMEL_FOOD;
|
||||
}
|
||||
|
||||
|
@ -141,7 +143,7 @@ public class CamelEntity extends AbstractHorseEntity implements ClientVehicle {
|
|||
@Override
|
||||
protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttributeType type) {
|
||||
AttributeData attributeData = super.calculateAttribute(javaAttribute, type);
|
||||
if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_JUMP_STRENGTH) {
|
||||
if (javaAttribute.getType() == AttributeType.Builtin.JUMP_STRENGTH) {
|
||||
vehicleComponent.setHorseJumpStrength(attributeData.getValue());
|
||||
}
|
||||
return attributeData;
|
||||
|
|
|
@ -30,8 +30,10 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.MathUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
|
||||
|
@ -56,7 +58,7 @@ public class LlamaEntity extends ChestedHorseEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ItemTag getFoodTag() {
|
||||
protected @Nullable Tag<Item> getFoodTag() {
|
||||
return ItemTag.LLAMA_FOOD;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,8 +32,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
|||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
|
@ -109,7 +111,7 @@ public class CatEntity extends TameableEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ItemTag getFoodTag() {
|
||||
protected @Nullable Tag<Item> getFoodTag() {
|
||||
return ItemTag.CAT_FOOD;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.geysermc.geyser.inventory.GeyserItemStack;
|
|||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
|
@ -47,7 +48,7 @@ public class ParrotEntity extends TameableEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,8 +36,10 @@ import org.geysermc.geyser.inventory.GeyserItemStack;
|
|||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.enchantment.EnchantmentComponent;
|
||||
import org.geysermc.geyser.item.type.DyeItem;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
import org.geysermc.geyser.util.ItemUtils;
|
||||
|
@ -49,6 +51,8 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.Object
|
|||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
|
@ -56,7 +60,7 @@ import java.util.UUID;
|
|||
|
||||
public class WolfEntity extends TameableEntity {
|
||||
private byte collarColor = 14; // Red - default
|
||||
|
||||
private HolderSet repairableItems = null;
|
||||
private boolean isCurseOfBinding = false;
|
||||
|
||||
public WolfEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
||||
|
@ -116,14 +120,16 @@ public class WolfEntity extends TameableEntity {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
protected ItemTag getFoodTag() {
|
||||
protected Tag<Item> getFoodTag() {
|
||||
return ItemTag.WOLF_FOOD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChestplate(ItemStack stack) {
|
||||
super.setChestplate(stack);
|
||||
isCurseOfBinding = ItemUtils.hasEffect(session, stack, EnchantmentComponent.PREVENT_ARMOR_CHANGE); // TODO test
|
||||
public void setBody(ItemStack stack) {
|
||||
super.setBody(stack);
|
||||
isCurseOfBinding = ItemUtils.hasEffect(session, stack, EnchantmentComponent.PREVENT_ARMOR_CHANGE);
|
||||
// Not using ItemStack#getDataComponents as that wouldn't include default item components
|
||||
repairableItems = GeyserItemStack.from(stack).getComponent(DataComponentTypes.REPAIRABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -150,16 +156,17 @@ public class WolfEntity extends TameableEntity {
|
|||
return super.testMobInteraction(hand, itemInHand);
|
||||
}
|
||||
}
|
||||
if (itemInHand.asItem() == Items.WOLF_ARMOR && !this.chestplate.isValid() && !getFlag(EntityFlag.BABY)) {
|
||||
if (itemInHand.asItem() == Items.WOLF_ARMOR && !this.body.isValid() && !getFlag(EntityFlag.BABY)) {
|
||||
return InteractiveTag.EQUIP_WOLF_ARMOR;
|
||||
}
|
||||
if (itemInHand.asItem() == Items.SHEARS && this.chestplate.isValid()
|
||||
if (itemInHand.asItem() == Items.SHEARS && this.body.isValid()
|
||||
&& (!isCurseOfBinding || session.getGameMode().equals(GameMode.CREATIVE))) {
|
||||
return InteractiveTag.REMOVE_WOLF_ARMOR;
|
||||
}
|
||||
if (Items.WOLF_ARMOR.isValidRepairItem(itemInHand.asItem()) && getFlag(EntityFlag.SITTING) &&
|
||||
this.chestplate.isValid() && this.chestplate.getTag() != null &&
|
||||
this.chestplate.getTag().getInt("Damage") > 0) {
|
||||
if (getFlag(EntityFlag.SITTING) &&
|
||||
session.getTagCache().isItem(repairableItems, itemInHand.asItem()) &&
|
||||
this.body.isValid() && this.body.getTag() != null &&
|
||||
this.body.getTag().getInt("Damage") > 0) {
|
||||
return InteractiveTag.REPAIR_WOLF_ARMOR;
|
||||
}
|
||||
// Tamed and owned by player - can sit/stand
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.entity.type.living.monster;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelEventGenericPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class CreakingEntity extends MonsterEntity {
|
||||
public static final String CREAKING_STATE = "minecraft:creaking_state";
|
||||
public static final String CREAKING_SWAYING_TICKS = "minecraft:creaking_swaying_ticks";
|
||||
|
||||
private Vector3i homePosition;
|
||||
|
||||
public CreakingEntity(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();
|
||||
setFlag(EntityFlag.FIRE_IMMUNE, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalSpawnData(AddEntityPacket addEntityPacket) {
|
||||
propertyManager.add(CREAKING_STATE, "neutral");
|
||||
// also, the creaking seems to have this minecraft:creaking_swaying_ticks thingy
|
||||
// which i guess is responsible for some animation?
|
||||
// it's sent over the network, all 6 "stages" 50ms in between of each other.
|
||||
// no clue what it's used for tbh, so i'm not gonna bother implementing it
|
||||
// - chris
|
||||
propertyManager.add(CREAKING_SWAYING_TICKS, 0);
|
||||
propertyManager.applyIntProperties(addEntityPacket.getProperties().getIntProperties());
|
||||
}
|
||||
|
||||
public void setCanMove(EntityMetadata<Boolean,? extends MetadataType<Boolean>> booleanEntityMetadata) {
|
||||
setFlag(EntityFlag.BODY_ROTATION_BLOCKED, !booleanEntityMetadata.getValue());
|
||||
propertyManager.add(CREAKING_STATE, booleanEntityMetadata.getValue() ? "hostile_unobserved" : "hostile_observed");
|
||||
updateBedrockEntityProperties();
|
||||
}
|
||||
|
||||
public void setActive(EntityMetadata<Boolean,? extends MetadataType<Boolean>> booleanEntityMetadata) {
|
||||
if (!booleanEntityMetadata.getValue()) {
|
||||
propertyManager.add(CREAKING_STATE, "neutral");
|
||||
}
|
||||
}
|
||||
|
||||
public void setIsTearingDown(EntityMetadata<Boolean,? extends MetadataType<Boolean>> booleanEntityMetadata) {
|
||||
if (booleanEntityMetadata.getValue()) {
|
||||
propertyManager.add(CREAKING_STATE, "crumbling");
|
||||
updateBedrockEntityProperties();
|
||||
}
|
||||
}
|
||||
|
||||
public void setHomePos(EntityMetadata<Optional<Vector3i>,? extends MetadataType<Optional<Vector3i>>> optionalEntityMetadata) {
|
||||
if (optionalEntityMetadata.getValue().isPresent()) {
|
||||
this.homePosition = optionalEntityMetadata.getValue().get();
|
||||
} else {
|
||||
this.homePosition = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void createParticleBeam() {
|
||||
if (this.homePosition != null) {
|
||||
LevelEventGenericPacket levelEventGenericPacket = new LevelEventGenericPacket();
|
||||
levelEventGenericPacket.setType(LevelEvent.PARTICLE_CREAKING_HEART_TRIAL);
|
||||
levelEventGenericPacket.setTag(
|
||||
NbtMap.builder()
|
||||
.putInt("CreakingAmount", 20)
|
||||
.putFloat("CreakingX", position.getX())
|
||||
.putFloat("CreakingY", position.getY())
|
||||
.putFloat("CreakingZ", position.getZ())
|
||||
.putInt("HeartAmount", 20)
|
||||
.putFloat("HeartX", homePosition.getX())
|
||||
.putFloat("HeartY", homePosition.getY())
|
||||
.putFloat("HeartZ", homePosition.getZ())
|
||||
.build()
|
||||
);
|
||||
|
||||
session.sendUpstreamPacket(levelEventGenericPacket);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,12 +25,6 @@
|
|||
|
||||
package org.geysermc.geyser.entity.type.player;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
@ -43,7 +37,6 @@ import org.cloudburstmc.protocol.bedrock.data.AbilityLayer;
|
|||
import org.cloudburstmc.protocol.bedrock.data.GameType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.PlayerPermission;
|
||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataMap;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData;
|
||||
|
@ -66,6 +59,13 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.Boolea
|
|||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Getter @Setter
|
||||
public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
||||
public static final float SNEAKING_POSE_HEIGHT = 1.5f;
|
||||
|
@ -97,11 +97,11 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
|||
/**
|
||||
* Saves the parrot currently on the player's left shoulder; otherwise null
|
||||
*/
|
||||
private ParrotEntity leftParrot;
|
||||
private @Nullable ParrotEntity leftParrot;
|
||||
/**
|
||||
* Saves the parrot currently on the player's right shoulder; otherwise null
|
||||
*/
|
||||
private ParrotEntity rightParrot;
|
||||
private @Nullable ParrotEntity rightParrot;
|
||||
|
||||
public PlayerEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, Vector3f position,
|
||||
Vector3f motion, float yaw, float pitch, float headYaw, String username, @Nullable String texturesProperty) {
|
||||
|
@ -112,20 +112,6 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
|||
this.texturesProperty = texturesProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use! For testing purposes only
|
||||
*/
|
||||
public PlayerEntity(GeyserSession session, long geyserId, UUID uuid, String username) {
|
||||
super(session, -1, geyserId, uuid, EntityDefinitions.PLAYER, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0);
|
||||
this.username = username;
|
||||
this.nametag = username;
|
||||
this.texturesProperty = null;
|
||||
|
||||
// clear initial metadata
|
||||
dirtyMetadata.apply(new EntityDataMap());
|
||||
setFlagsDirty(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeMetadata() {
|
||||
super.initializeMetadata();
|
||||
|
@ -193,11 +179,7 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
|||
if (session.getEntityCache().getPlayerEntity(uuid) == null)
|
||||
return;
|
||||
|
||||
if (session.getEntityCache().getEntityByGeyserId(geyserId) == null) {
|
||||
session.getEntityCache().spawnEntity(this);
|
||||
} else {
|
||||
spawnEntity();
|
||||
}
|
||||
session.getEntityCache().spawnEntity(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -269,10 +251,6 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
|||
}
|
||||
}
|
||||
|
||||
public void updateRotation(float yaw, float pitch, float headYaw, boolean isOnGround) {
|
||||
moveRelative(0, 0, 0, yaw, pitch, headYaw, isOnGround);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPosition(Vector3f position) {
|
||||
if (this.bedPosition != null) {
|
||||
|
@ -357,7 +335,7 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
|||
parrot.updateBedrockMetadata();
|
||||
SetEntityLinkPacket linkPacket = new SetEntityLinkPacket();
|
||||
EntityLinkData.Type type = isLeft ? EntityLinkData.Type.RIDER : EntityLinkData.Type.PASSENGER;
|
||||
linkPacket.setEntityLink(new EntityLinkData(geyserId, parrot.getGeyserId(), type, false, false));
|
||||
linkPacket.setEntityLink(new EntityLinkData(geyserId, parrot.getGeyserId(), type, false, false, 0f));
|
||||
// Delay, or else spawned-in players won't get the link
|
||||
// TODO: Find a better solution.
|
||||
session.scheduleInEventLoop(() -> session.sendUpstreamPacket(linkPacket), 500, TimeUnit.MILLISECONDS);
|
||||
|
@ -472,6 +450,6 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
|||
|
||||
@Override
|
||||
public Vector3f position() {
|
||||
return this.position.clone();
|
||||
return this.position.down(definition.offset());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
|||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.level.BedrockDimension;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.AttributeUtils;
|
||||
|
@ -143,9 +142,32 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
this.position = position.add(0, definition.offset(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special method used only when updating the session player's rotation.
|
||||
* For some reason, Mode#NORMAL ignored rotation. Yay.
|
||||
* @param yaw the new yaw
|
||||
* @param pitch the new pitch
|
||||
* @param headYaw the head yaw
|
||||
*/
|
||||
public void updateOwnRotation(float yaw, float pitch, float headYaw) {
|
||||
setYaw(yaw);
|
||||
setPitch(pitch);
|
||||
setHeadYaw(headYaw);
|
||||
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
movePlayerPacket.setRuntimeEntityId(geyserId);
|
||||
movePlayerPacket.setPosition(position);
|
||||
movePlayerPacket.setRotation(getBedrockRotation());
|
||||
movePlayerPacket.setOnGround(isOnGround());
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
|
||||
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR);
|
||||
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the player's position without applying an offset or moving the bounding box
|
||||
* This is used in BedrockMovePlayerTranslator which receives the player's position
|
||||
* This is used in BedrockMovePlayer which receives the player's position
|
||||
* with the offset pre-applied
|
||||
*
|
||||
* @param position the new position of the Bedrock player
|
||||
|
@ -212,12 +234,7 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
// the bubbles visually pop
|
||||
setFlag(EntityFlag.BREATHING, amount >= this.lastAirSupply);
|
||||
this.lastAirSupply = amount;
|
||||
|
||||
if (amount == getMaxAir() && GameProtocol.isPre1_21_0(session)) {
|
||||
super.setAirSupply(0); // Hide the bubble counter from the UI for the player
|
||||
} else {
|
||||
super.setAirSupply(amount);
|
||||
}
|
||||
super.setAirSupply(amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -247,9 +264,9 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
|
||||
@Override
|
||||
protected void updateAttribute(Attribute javaAttribute, List<AttributeData> newAttributes) {
|
||||
if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_ATTACK_SPEED) {
|
||||
if (javaAttribute.getType() == AttributeType.Builtin.ATTACK_SPEED) {
|
||||
session.setAttackSpeed(AttributeUtils.calculateValue(javaAttribute));
|
||||
} else if (javaAttribute.getType() == AttributeType.Builtin.PLAYER_BLOCK_INTERACTION_RANGE) {
|
||||
} else if (javaAttribute.getType() == AttributeType.Builtin.BLOCK_INTERACTION_RANGE) {
|
||||
this.blockInteractionRange = AttributeUtils.calculateValue(javaAttribute);
|
||||
} else {
|
||||
super.updateAttribute(javaAttribute, newAttributes);
|
||||
|
@ -263,6 +280,15 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
return attributeData;
|
||||
}
|
||||
|
||||
public float attributeOrDefault(GeyserAttributeType type) {
|
||||
var attribute = this.attributes.get(type);
|
||||
if (attribute == null) {
|
||||
return type.getDefaultValue();
|
||||
}
|
||||
|
||||
return attribute.getValue();
|
||||
}
|
||||
|
||||
public void setLastDeathPosition(@Nullable GlobalPos pos) {
|
||||
if (pos != null) {
|
||||
dirtyMetadata.put(EntityDataTypes.PLAYER_LAST_DEATH_POS, pos.getPosition());
|
||||
|
|
|
@ -102,17 +102,6 @@ public class SkullPlayerEntity extends PlayerEntity {
|
|||
session.sendUpstreamPacket(addPlayerPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the player entity so that it can be reused for a different skull.
|
||||
*/
|
||||
public void free() {
|
||||
setFlag(EntityFlag.INVISIBLE, true);
|
||||
updateBedrockMetadata();
|
||||
|
||||
// Move skull entity out of the way
|
||||
moveAbsolute(session.getPlayerEntity().getPosition().up(128), 0, 0, 0, false, true);
|
||||
}
|
||||
|
||||
public void updateSkull(SkullCache.Skull skull) {
|
||||
skullPosition = skull.getPosition();
|
||||
|
||||
|
|
|
@ -76,8 +76,8 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
|||
public VehicleComponent(T vehicle, float stepHeight) {
|
||||
this.vehicle = vehicle;
|
||||
this.stepHeight = stepHeight;
|
||||
this.moveSpeed = (float) AttributeType.Builtin.GENERIC_MOVEMENT_SPEED.getDef();
|
||||
this.gravity = AttributeType.Builtin.GENERIC_GRAVITY.getDef();
|
||||
this.moveSpeed = (float) AttributeType.Builtin.MOVEMENT_SPEED.getDef();
|
||||
this.gravity = AttributeType.Builtin.GRAVITY.getDef();
|
||||
|
||||
double width = vehicle.getBoundingBoxWidth();
|
||||
double height = vehicle.getBoundingBoxHeight();
|
||||
|
@ -105,6 +105,10 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
|||
boundingBox.setMiddleZ(z);
|
||||
}
|
||||
|
||||
public void moveAbsolute(Vector3d vec) {
|
||||
moveAbsolute(vec.getX(), vec.getY(), vec.getZ());
|
||||
}
|
||||
|
||||
public void moveRelative(double x, double y, double z) {
|
||||
boundingBox.translate(x, y, z);
|
||||
}
|
||||
|
@ -756,9 +760,8 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
|||
vehicle.getSession().sendUpstreamPacket(moveEntityDeltaPacket);
|
||||
}
|
||||
|
||||
ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(javaPos.getX(), javaPos.getY(), javaPos.getZ(), rotation.getX(), rotation.getY());
|
||||
ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(javaPos, rotation.getX(), rotation.getY(), vehicle.isOnGround());
|
||||
vehicle.getSession().sendDownstreamPacket(moveVehiclePacket);
|
||||
vehicle.getSession().setLastVehicleMoveTimestamp(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
protected double getGravity() {
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2025 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 lombok.Getter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.event.bedrock.SessionDisconnectEvent;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
|
||||
/**
|
||||
* A wrapper around the {@link SessionDisconnectEvent} that allows
|
||||
* Geyser to access the underlying component when replacing disconnect messages.
|
||||
*/
|
||||
@Getter
|
||||
public class SessionDisconnectEventImpl extends SessionDisconnectEvent {
|
||||
|
||||
private final Component reasonComponent;
|
||||
|
||||
public SessionDisconnectEventImpl(@NonNull GeyserSession session, Component reason) {
|
||||
super(session, MessageTranslator.convertMessageRaw(reason, session.locale()));
|
||||
this.reasonComponent = reason;
|
||||
}
|
||||
}
|
|
@ -36,14 +36,14 @@ import java.util.UUID;
|
|||
|
||||
public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksEvent {
|
||||
|
||||
private final Map<String, ResourcePack> packs;
|
||||
private final Map<UUID, ResourcePack> packs;
|
||||
|
||||
public SessionLoadResourcePacksEventImpl(GeyserSession session, Map<String, ResourcePack> packMap) {
|
||||
public SessionLoadResourcePacksEventImpl(GeyserSession session, Map<UUID, ResourcePack> packMap) {
|
||||
super(session);
|
||||
this.packs = packMap;
|
||||
}
|
||||
|
||||
public @NonNull Map<String, ResourcePack> getPacks() {
|
||||
public @NonNull Map<UUID, ResourcePack> getPacks() {
|
||||
return packs;
|
||||
}
|
||||
|
||||
|
@ -54,16 +54,16 @@ public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksE
|
|||
|
||||
@Override
|
||||
public boolean register(@NonNull ResourcePack resourcePack) {
|
||||
String packID = resourcePack.manifest().header().uuid().toString();
|
||||
UUID packID = resourcePack.manifest().header().uuid();
|
||||
if (packs.containsValue(resourcePack) || packs.containsKey(packID)) {
|
||||
return false;
|
||||
}
|
||||
packs.put(resourcePack.manifest().header().uuid().toString(), resourcePack);
|
||||
packs.put(resourcePack.manifest().header().uuid(), resourcePack);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregister(@NonNull UUID uuid) {
|
||||
return packs.remove(uuid.toString()) != null;
|
||||
return packs.remove(uuid) != null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import lombok.RequiredArgsConstructor;
|
|||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.api.util.ApiVersion;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
import org.geysermc.geyser.api.event.ExtensionEventBus;
|
||||
import org.geysermc.geyser.api.extension.Extension;
|
||||
|
@ -42,6 +43,7 @@ import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException;
|
|||
import org.geysermc.geyser.api.extension.exception.InvalidExtensionException;
|
||||
import org.geysermc.geyser.extension.event.GeyserExtensionEventBus;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.ThrowingBiConsumer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
@ -51,10 +53,12 @@ import java.nio.file.Files;
|
|||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
|
@ -155,6 +159,7 @@ public class GeyserExtensionLoader extends ExtensionLoader {
|
|||
|
||||
@Override
|
||||
protected void loadAllExtensions(@NonNull ExtensionManager extensionManager) {
|
||||
GeyserLogger logger = GeyserImpl.getInstance().getLogger();
|
||||
try {
|
||||
if (Files.notExists(extensionsDirectory)) {
|
||||
Files.createDirectory(extensionsDirectory);
|
||||
|
@ -163,55 +168,68 @@ public class GeyserExtensionLoader extends ExtensionLoader {
|
|||
Map<String, Path> extensions = new LinkedHashMap<>();
|
||||
Map<String, GeyserExtensionContainer> loadedExtensions = new LinkedHashMap<>();
|
||||
|
||||
Pattern[] extensionFilters = this.extensionFilters();
|
||||
List<Path> extensionPaths = Files.walk(extensionsDirectory).toList();
|
||||
extensionPaths.forEach(path -> {
|
||||
if (Files.isDirectory(path)) {
|
||||
return;
|
||||
}
|
||||
Path updateDirectory = extensionsDirectory.resolve("update");
|
||||
if (Files.isDirectory(updateDirectory)) {
|
||||
// Step 1: Collect the extension files that currently exist so they can be replaced
|
||||
Map<String, List<Path>> extensionFiles = new HashMap<>();
|
||||
this.processExtensionsFolder(extensionsDirectory, (path, description) -> {
|
||||
extensionFiles.computeIfAbsent(description.id(), k -> new ArrayList<>()).add(path);
|
||||
}, (path, e) -> {
|
||||
// this file will throw again when we actually try to load extensions, and it will be handled there
|
||||
});
|
||||
|
||||
for (Pattern filter : extensionFilters) {
|
||||
if (!filter.matcher(path.getFileName().toString()).matches()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
GeyserExtensionDescription description = this.extensionDescription(path);
|
||||
|
||||
String name = description.name();
|
||||
String id = description.id();
|
||||
if (extensions.containsKey(id) || extensionManager.extension(id) != null) {
|
||||
GeyserImpl.getInstance().getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.extensions.load.duplicate", name, path.toString()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether an extensions' requested api version is compatible
|
||||
ApiVersion.Compatibility compatibility = GeyserApi.api().geyserApiVersion().supportsRequestedVersion(
|
||||
description.humanApiVersion(),
|
||||
description.majorApiVersion(),
|
||||
description.minorApiVersion()
|
||||
);
|
||||
|
||||
if (compatibility != ApiVersion.Compatibility.COMPATIBLE) {
|
||||
// Workaround for the switch to the Geyser API version instead of the Base API version in extensions
|
||||
if (compatibility == ApiVersion.Compatibility.HUMAN_DIFFER && description.humanApiVersion() == 1) {
|
||||
GeyserImpl.getInstance().getLogger().warning("The extension %s requested the Base API version %s, which is deprecated in favor of specifying the Geyser API version. Please update the extension, or contact its developer."
|
||||
.formatted(name, description.apiVersion()));
|
||||
} else {
|
||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, description.apiVersion()));
|
||||
return;
|
||||
// Step 2: Move the updated/new extensions
|
||||
this.processExtensionsFolder(updateDirectory, (path, description) -> {
|
||||
// Remove the old extension files with the same ID if it exists
|
||||
List<Path> oldExtensionFiles = extensionFiles.get(description.id());
|
||||
if (oldExtensionFiles != null) {
|
||||
for (Path oldExtensionFile : oldExtensionFiles) {
|
||||
Files.delete(oldExtensionFile);
|
||||
}
|
||||
}
|
||||
|
||||
GeyserExtensionContainer container = this.loadExtension(path, description);
|
||||
extensions.put(id, path);
|
||||
loadedExtensions.put(id, container);
|
||||
} catch (Throwable e) {
|
||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_with_name", path.getFileName(), path.toAbsolutePath()), e);
|
||||
// Overwrite the extension with the new jar
|
||||
Files.move(path, extensionsDirectory.resolve(path.getFileName()), StandardCopyOption.REPLACE_EXISTING);
|
||||
}, (path, e) -> {
|
||||
logger.error(GeyserLocale.getLocaleStringLog("geyser.extensions.update.failed", path.getFileName()), e);
|
||||
});
|
||||
}
|
||||
|
||||
// Step 3: Load the extensions
|
||||
this.processExtensionsFolder(extensionsDirectory, (path, description) -> {
|
||||
String name = description.name();
|
||||
String id = description.id();
|
||||
if (extensions.containsKey(id) || extensionManager.extension(id) != null) {
|
||||
logger.warning(GeyserLocale.getLocaleStringLog("geyser.extensions.load.duplicate", name, path.toString()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether an extensions' requested api version is compatible
|
||||
ApiVersion.Compatibility compatibility = GeyserApi.api().geyserApiVersion().supportsRequestedVersion(
|
||||
description.humanApiVersion(),
|
||||
description.majorApiVersion(),
|
||||
description.minorApiVersion()
|
||||
);
|
||||
|
||||
if (compatibility != ApiVersion.Compatibility.COMPATIBLE) {
|
||||
// Workaround for the switch to the Geyser API version instead of the Base API version in extensions
|
||||
if (compatibility == ApiVersion.Compatibility.HUMAN_DIFFER && description.humanApiVersion() == 1) {
|
||||
logger.warning("The extension %s requested the Base API version %s, which is deprecated in favor of specifying the Geyser API version. Please update the extension, or contact its developer."
|
||||
.formatted(name, description.apiVersion()));
|
||||
} else {
|
||||
logger.error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, description.apiVersion()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GeyserExtensionContainer container = this.loadExtension(path, description);
|
||||
extensions.put(id, path);
|
||||
loadedExtensions.put(id, container);
|
||||
}, (path, e) -> {
|
||||
logger.error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_with_name", path.getFileName(), path.toAbsolutePath()), e);
|
||||
});
|
||||
|
||||
// Step 4: Register the extensions
|
||||
for (GeyserExtensionContainer container : loadedExtensions.values()) {
|
||||
this.extensionContainers.put(container.extension(), container);
|
||||
this.register(container.extension(), extensionManager);
|
||||
|
@ -221,6 +239,40 @@ public class GeyserExtensionLoader extends ExtensionLoader {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process extension jars in a folder and call the accept or reject consumer based on the result
|
||||
*
|
||||
* @param directory the directory to process
|
||||
* @param accept the consumer to call when an extension is accepted
|
||||
* @param reject the consumer to call when an extension is rejected
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
private void processExtensionsFolder(Path directory, ThrowingBiConsumer<Path, GeyserExtensionDescription> accept, BiConsumer<Path, Throwable> reject) throws IOException {
|
||||
List<Path> extensionPaths = Files.list(directory).toList();
|
||||
Pattern[] extensionFilters = this.extensionFilters();
|
||||
extensionPaths.forEach(path -> {
|
||||
if (Files.isDirectory(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only look at files that meet the extension filter
|
||||
for (Pattern filter : extensionFilters) {
|
||||
if (!filter.matcher(path.getFileName().toString()).matches()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Try load the description, so we know it's a valid extension
|
||||
GeyserExtensionDescription description = this.extensionDescription(path);
|
||||
|
||||
accept.acceptThrows(path, description);
|
||||
} catch (Throwable e) {
|
||||
reject.accept(path, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabled(@NonNull Extension extension) {
|
||||
return this.extensionContainers.get(extension).enabled;
|
||||
|
|
|
@ -43,13 +43,14 @@ public class CameraDefinitions {
|
|||
|
||||
static {
|
||||
CAMERA_PRESETS = List.of(
|
||||
new CameraPreset(CameraPerspective.FIRST_PERSON.id(), "", null, null, null, null, null, null, OptionalBoolean.empty(), null, OptionalBoolean.empty(), null),
|
||||
new CameraPreset(CameraPerspective.FREE.id(), "", null, null, null, null, null, null, OptionalBoolean.empty(), null, OptionalBoolean.empty(), null),
|
||||
new CameraPreset(CameraPerspective.THIRD_PERSON.id(), "", null, null, null, null, null, null, OptionalBoolean.empty(), null, OptionalBoolean.empty(), null),
|
||||
new CameraPreset(CameraPerspective.THIRD_PERSON_FRONT.id(), "", null, null, null, null, null, null, OptionalBoolean.empty(), null, OptionalBoolean.empty(), null),
|
||||
new CameraPreset("geyser:free_audio", "minecraft:free", null, null, null, null, null, CameraAudioListener.PLAYER, OptionalBoolean.empty(), null, OptionalBoolean.of(false), null),
|
||||
new CameraPreset("geyser:free_effects", "minecraft:free", null, null, null, null, null, CameraAudioListener.CAMERA, OptionalBoolean.empty(), null, OptionalBoolean.of(true), null),
|
||||
new CameraPreset("geyser:free_audio_effects", "minecraft:free", null, null, null, null, null, CameraAudioListener.PLAYER, OptionalBoolean.empty(), null, OptionalBoolean.of(true), null));
|
||||
CameraPreset.builder().identifier(CameraPerspective.FIRST_PERSON.id()).build(),
|
||||
CameraPreset.builder().identifier(CameraPerspective.FREE.id()).build(),
|
||||
CameraPreset.builder().identifier(CameraPerspective.THIRD_PERSON.id()).build(),
|
||||
CameraPreset.builder().identifier(CameraPerspective.THIRD_PERSON_FRONT.id()).build(),
|
||||
CameraPreset.builder().identifier("geyser:free_audio").parentPreset(CameraPerspective.FREE.id()).listener(CameraAudioListener.PLAYER).playEffect(OptionalBoolean.of(false)).build(),
|
||||
CameraPreset.builder().identifier("geyser:free_effects").parentPreset(CameraPerspective.FREE.id()).listener(CameraAudioListener.CAMERA).playEffect(OptionalBoolean.of(true)).build(),
|
||||
CameraPreset.builder().identifier("geyser:free_audio_effects").parentPreset(CameraPerspective.FREE.id()).listener(CameraAudioListener.PLAYER).playEffect(OptionalBoolean.of(true)).build()
|
||||
);
|
||||
|
||||
SimpleDefinitionRegistry.Builder<NamedDefinition> builder = SimpleDefinitionRegistry.builder();
|
||||
for (int i = 0; i < CAMERA_PRESETS.size(); i++) {
|
||||
|
|
|
@ -25,15 +25,15 @@
|
|||
|
||||
package org.geysermc.geyser.inventory;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundRenameItemPacket;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.geyser.util.ItemUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundRenameItemPacket;
|
||||
|
||||
/**
|
||||
* Used to determine if rename packets should be sent and stores
|
||||
|
@ -73,7 +73,7 @@ public class AnvilContainer extends Container {
|
|||
String correctRename;
|
||||
newName = rename;
|
||||
|
||||
Component originalName = ItemUtils.getCustomName(getInput().getComponents());
|
||||
Component originalName = getInput().getComponent(DataComponentTypes.CUSTOM_NAME);
|
||||
|
||||
String plainOriginalName = MessageTranslator.convertToPlainText(originalName, session.locale());
|
||||
String plainNewName = MessageTranslator.convertToPlainText(rename);
|
||||
|
|
|
@ -25,12 +25,12 @@
|
|||
|
||||
package org.geysermc.geyser.inventory;
|
||||
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,21 +25,33 @@
|
|||
|
||||
package org.geysermc.geyser.inventory;
|
||||
|
||||
import lombok.*;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.BundleCache;
|
||||
import org.geysermc.geyser.translator.item.ItemTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.EmptySlotDisplay;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemSlotDisplay;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemStackSlotDisplay;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Data
|
||||
public class GeyserItemStack {
|
||||
|
@ -50,19 +62,23 @@ public class GeyserItemStack {
|
|||
private DataComponents components;
|
||||
private int netId;
|
||||
|
||||
@EqualsAndHashCode.Exclude
|
||||
private BundleCache.BundleData bundleData;
|
||||
|
||||
@Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE)
|
||||
@EqualsAndHashCode.Exclude
|
||||
private Item item;
|
||||
|
||||
private GeyserItemStack(int javaId, int amount, DataComponents components) {
|
||||
this(javaId, amount, components, 1);
|
||||
this(javaId, amount, components, 1, null);
|
||||
}
|
||||
|
||||
private GeyserItemStack(int javaId, int amount, DataComponents components, int netId) {
|
||||
private GeyserItemStack(int javaId, int amount, DataComponents components, int netId, BundleCache.BundleData bundleData) {
|
||||
this.javaId = javaId;
|
||||
this.amount = amount;
|
||||
this.components = components;
|
||||
this.netId = netId;
|
||||
this.bundleData = bundleData;
|
||||
}
|
||||
|
||||
public static @NonNull GeyserItemStack of(int javaId, int amount) {
|
||||
|
@ -74,7 +90,21 @@ public class GeyserItemStack {
|
|||
}
|
||||
|
||||
public static @NonNull GeyserItemStack from(@Nullable ItemStack itemStack) {
|
||||
return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getDataComponents());
|
||||
return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getDataComponentsPatch());
|
||||
}
|
||||
|
||||
public static @NonNull GeyserItemStack from(@NonNull SlotDisplay slotDisplay) {
|
||||
if (slotDisplay instanceof EmptySlotDisplay) {
|
||||
return GeyserItemStack.EMPTY;
|
||||
}
|
||||
if (slotDisplay instanceof ItemSlotDisplay itemSlotDisplay) {
|
||||
return GeyserItemStack.of(itemSlotDisplay.item(), 1);
|
||||
}
|
||||
if (slotDisplay instanceof ItemStackSlotDisplay itemStackSlotDisplay) {
|
||||
return GeyserItemStack.from(itemStackSlotDisplay.itemStack());
|
||||
}
|
||||
GeyserImpl.getInstance().getLogger().warning("Unsure how to convert to ItemStack: " + slotDisplay);
|
||||
return GeyserItemStack.EMPTY;
|
||||
}
|
||||
|
||||
public int getJavaId() {
|
||||
|
@ -85,10 +115,31 @@ public class GeyserItemStack {
|
|||
return isEmpty() ? 0 : amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all components of this item - base and additional components sent over the network.
|
||||
* These are NOT modifiable! To add components, use {@link #getOrCreateComponents()}.
|
||||
*
|
||||
* @return the item's base data components and the "additional" ones that may exist.
|
||||
*/
|
||||
public @Nullable DataComponents getAllComponents() {
|
||||
return isEmpty() ? null : asItem().gatherComponents(components);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link DataComponents} that aren't the base/default components.
|
||||
*/
|
||||
public @Nullable DataComponents getComponents() {
|
||||
return isEmpty() ? null : components;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether this GeyserItemStack has any additional components on top of
|
||||
* the base item components.
|
||||
*/
|
||||
public boolean hasNonBaseComponents() {
|
||||
return components != null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public DataComponents getOrCreateComponents() {
|
||||
if (components == null) {
|
||||
|
@ -97,42 +148,56 @@ public class GeyserItemStack {
|
|||
return components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stored data component for a given {@link DataComponentType}, or null.
|
||||
* <p>
|
||||
* This method will first check the additional components that may exist,
|
||||
* and fallback to the item's default (or, "base") components if need be.
|
||||
* @param type the {@link DataComponentType} to query
|
||||
* @return the value for said type, or null.
|
||||
* @param <T> the value's type
|
||||
*/
|
||||
@Nullable
|
||||
public <T> T getComponent(@NonNull DataComponentType<T> type) {
|
||||
if (components == null) {
|
||||
return null;
|
||||
return asItem().getComponent(type);
|
||||
}
|
||||
return components.get(type);
|
||||
|
||||
T value = components.get(type);
|
||||
if (value == null) {
|
||||
return asItem().getComponent(type);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public <T extends Boolean> boolean getComponent(@NonNull DataComponentType<T> type, boolean def) {
|
||||
if (components == null) {
|
||||
return def;
|
||||
}
|
||||
|
||||
Boolean result = components.get(type);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
public <T extends Integer> int getComponent(@NonNull DataComponentType<T> type, int def) {
|
||||
if (components == null) {
|
||||
return def;
|
||||
}
|
||||
|
||||
Integer result = components.get(type);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
return def;
|
||||
public <T> T getComponentElseGet(@NonNull DataComponentType<T> type, Supplier<T> supplier) {
|
||||
T value = getComponent(type);
|
||||
return value == null ? supplier.get() : value;
|
||||
}
|
||||
|
||||
public int getNetId() {
|
||||
return isEmpty() ? 0 : netId;
|
||||
}
|
||||
|
||||
public int getBundleId() {
|
||||
if (isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return bundleData == null ? -1 : bundleData.bundleId();
|
||||
}
|
||||
|
||||
public void mergeBundleData(GeyserSession session, BundleCache.BundleData oldBundleData) {
|
||||
if (oldBundleData != null && this.bundleData != null) {
|
||||
// Old bundle; re-use old IDs
|
||||
this.bundleData.updateNetIds(session, oldBundleData);
|
||||
} else if (this.bundleData != null) {
|
||||
// New bundle; allocate new ID
|
||||
session.getBundleCache().markNewBundle(this.bundleData);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(int add) {
|
||||
amount += add;
|
||||
}
|
||||
|
@ -146,6 +211,21 @@ public class GeyserItemStack {
|
|||
}
|
||||
|
||||
public @Nullable ItemStack getItemStack(int newAmount) {
|
||||
if (isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
// Sync our updated bundle data to server, if applicable
|
||||
// Not fresh from server? Then we have changes to apply!~
|
||||
if (bundleData != null && !bundleData.freshFromServer()) {
|
||||
if (!bundleData.contents().isEmpty()) {
|
||||
getOrCreateComponents().put(DataComponentTypes.BUNDLE_CONTENTS, bundleData.toComponent());
|
||||
} else {
|
||||
if (components != null) {
|
||||
// Empty list = no component = should delete
|
||||
components.getDataComponents().remove(DataComponentTypes.BUNDLE_CONTENTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
return isEmpty() ? null : new ItemStack(javaId, newAmount, components);
|
||||
}
|
||||
|
||||
|
@ -156,14 +236,25 @@ public class GeyserItemStack {
|
|||
ItemData.Builder itemData = ItemTranslator.translateToBedrock(session, javaId, amount, components);
|
||||
itemData.netId(getNetId());
|
||||
itemData.usingNetId(true);
|
||||
return itemData.build();
|
||||
|
||||
return session.getBundleCache().checkForBundle(this, itemData);
|
||||
}
|
||||
|
||||
public ItemMapping getMapping(GeyserSession session) {
|
||||
return session.getItemMappings().getMapping(this.javaId);
|
||||
}
|
||||
|
||||
public SlotDisplay asSlotDisplay() {
|
||||
if (isEmpty()) {
|
||||
return EmptySlotDisplay.INSTANCE;
|
||||
}
|
||||
return new ItemStackSlotDisplay(this.getItemStack());
|
||||
}
|
||||
|
||||
public Item asItem() {
|
||||
if (isEmpty()) {
|
||||
return Items.AIR;
|
||||
}
|
||||
if (item == null) {
|
||||
return (item = Registries.JAVA_ITEMS.get().get(javaId));
|
||||
}
|
||||
|
@ -179,6 +270,6 @@ public class GeyserItemStack {
|
|||
}
|
||||
|
||||
public GeyserItemStack copy(int newAmount) {
|
||||
return isEmpty() ? EMPTY : new GeyserItemStack(javaId, newAmount, components == null ? null : components.clone(), netId);
|
||||
return isEmpty() ? EMPTY : new GeyserItemStack(javaId, newAmount, components == null ? null : components.clone(), netId, bundleData == null ? null : bundleData.copy());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ import org.geysermc.geyser.item.Items;
|
|||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.item.ItemTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
@ -135,22 +135,28 @@ public abstract class Inventory {
|
|||
|
||||
// Lodestone caching
|
||||
if (newItem.asItem() == Items.COMPASS) {
|
||||
var tracker = newItem.getComponent(DataComponentType.LODESTONE_TRACKER);
|
||||
var tracker = newItem.getComponent(DataComponentTypes.LODESTONE_TRACKER);
|
||||
if (tracker != null) {
|
||||
session.getLodestoneCache().cacheInventoryItem(newItem, tracker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateItemNetId(GeyserItemStack oldItem, GeyserItemStack newItem, GeyserSession session) {
|
||||
public static void updateItemNetId(GeyserItemStack oldItem, GeyserItemStack newItem, GeyserSession session) {
|
||||
if (!newItem.isEmpty()) {
|
||||
ItemDefinition oldMapping = ItemTranslator.getBedrockItemDefinition(session, oldItem);
|
||||
ItemDefinition newMapping = ItemTranslator.getBedrockItemDefinition(session, newItem);
|
||||
if (oldMapping.equals(newMapping)) {
|
||||
newItem.setNetId(oldItem.getNetId());
|
||||
newItem.mergeBundleData(session, oldItem.getBundleData());
|
||||
} else {
|
||||
newItem.setNetId(session.getNextItemNetId());
|
||||
session.getBundleCache().markNewBundle(newItem.getBundleData());
|
||||
session.getBundleCache().onOldItemDelete(oldItem);
|
||||
}
|
||||
} else {
|
||||
// Empty item means no more bundle if one existed.
|
||||
session.getBundleCache().onOldItemDelete(oldItem);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,4 +171,12 @@ public abstract class Inventory {
|
|||
public void resetNextStateId() {
|
||||
nextStateId = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether we should be sending a {@link org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClosePacket}
|
||||
* when closing the inventory.
|
||||
*/
|
||||
public boolean shouldConfirmContainerClose() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,30 +34,28 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.translator.protocol.java.inventory.JavaOpenBookTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
|
||||
@Getter
|
||||
public class LecternContainer extends Container {
|
||||
@Getter @Setter
|
||||
@Setter
|
||||
private int currentBedrockPage = 0;
|
||||
@Getter @Setter
|
||||
@Setter
|
||||
private NbtMap blockEntityTag;
|
||||
@Getter @Setter
|
||||
@Setter
|
||||
private Vector3i position;
|
||||
|
||||
// Sigh. When the lectern container is created, we don't know (yet) if it's fake or not.
|
||||
// So... time for a manual check :/
|
||||
@Getter
|
||||
private boolean isFakeLectern = false;
|
||||
private boolean isBookInPlayerInventory = false;
|
||||
|
||||
public LecternContainer(String title, int id, int size, ContainerType containerType, PlayerInventory playerInventory) {
|
||||
super(title, id, size, containerType, playerInventory);
|
||||
}
|
||||
|
||||
/**
|
||||
* When we are using a fake lectern, the Java server expects us to still be in a player inventory.
|
||||
* We can't use {@link #isUsingRealBlock()} as that may not be determined yet.
|
||||
* When the Java server asks the client to open a book in their hotbar, we create a fake lectern to show it to the client.
|
||||
* We can't use the {@link #isUsingRealBlock()} check as we may also be dealing with a real virtual lectern (with its own inventory).
|
||||
*/
|
||||
@Override
|
||||
public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) {
|
||||
if (isFakeLectern) {
|
||||
if (isBookInPlayerInventory) {
|
||||
session.getPlayerInventory().setItem(slot, newItem, session);
|
||||
} else {
|
||||
super.setItem(slot, newItem, session);
|
||||
|
@ -69,7 +67,12 @@ public class LecternContainer extends Container {
|
|||
* See {@link LecternContainer#setItem(int, GeyserItemStack, GeyserSession)} as for why this is separate.
|
||||
*/
|
||||
public void setFakeLecternBook(GeyserItemStack book, GeyserSession session) {
|
||||
this.isFakeLectern = true;
|
||||
this.isBookInPlayerInventory = true;
|
||||
super.setItem(0, book, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldConfirmContainerClose() {
|
||||
return !isBookInPlayerInventory;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,11 +25,11 @@
|
|||
|
||||
package org.geysermc.geyser.inventory;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
|
||||
public class StonecutterContainer extends Container {
|
||||
/**
|
||||
|
|
|
@ -31,7 +31,10 @@ import lombok.AllArgsConstructor;
|
|||
@AllArgsConstructor
|
||||
public enum Click {
|
||||
LEFT(ContainerActionType.CLICK_ITEM, ClickItemAction.LEFT_CLICK),
|
||||
LEFT_BUNDLE(ContainerActionType.CLICK_ITEM, ClickItemAction.LEFT_CLICK),
|
||||
LEFT_BUNDLE_FROM_CURSOR(ContainerActionType.CLICK_ITEM, ClickItemAction.LEFT_CLICK),
|
||||
RIGHT(ContainerActionType.CLICK_ITEM, ClickItemAction.RIGHT_CLICK),
|
||||
RIGHT_BUNDLE(ContainerActionType.CLICK_ITEM, ClickItemAction.RIGHT_CLICK),
|
||||
LEFT_SHIFT(ContainerActionType.SHIFT_CLICK_ITEM, ShiftClickItemAction.LEFT_CLICK),
|
||||
DROP_ONE(ContainerActionType.DROP_ITEM, DropItemAction.DROP_FROM_SELECTED),
|
||||
DROP_ALL(ContainerActionType.DROP_ITEM, DropItemAction.DROP_SELECTED_STACK),
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue