mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Merge branch 'master' into feature/new-custom-entity-api
This commit is contained in:
commit
086431573b
632 changed files with 62218 additions and 53249 deletions
|
@ -1,8 +1,7 @@
|
|||
import net.kyori.blossom.BlossomExtension
|
||||
|
||||
plugins {
|
||||
id("net.kyori.blossom")
|
||||
id("net.kyori.indra.git")
|
||||
alias(libs.plugins.blossom)
|
||||
id("geyser.publish-conventions")
|
||||
}
|
||||
|
||||
|
@ -21,6 +20,7 @@ dependencies {
|
|||
implementation(libs.websocket)
|
||||
|
||||
api(libs.bundles.protocol)
|
||||
implementation(libs.blockstateupdater)
|
||||
|
||||
api(libs.mcauthlib)
|
||||
api(libs.mcprotocollib) {
|
||||
|
@ -30,7 +30,7 @@ dependencies {
|
|||
}
|
||||
|
||||
implementation(libs.raknet) {
|
||||
exclude("io.netty", "*");
|
||||
exclude("io.netty", "*")
|
||||
}
|
||||
|
||||
implementation(libs.netty.resolver.dns)
|
||||
|
@ -97,7 +97,7 @@ configure<BlossomExtension> {
|
|||
}
|
||||
|
||||
fun Project.buildNumber(): Int =
|
||||
System.getenv("BUILD_NUMBER")?.let { Integer.parseInt(it) } ?: -1
|
||||
(System.getenv("GITHUB_RUN_NUMBER") ?: jenkinsBuildNumber())?.let { Integer.parseInt(it) } ?: -1
|
||||
|
||||
inner class GitInfo {
|
||||
val branch: String
|
||||
|
@ -129,3 +129,22 @@ inner class GitInfo {
|
|||
repository = git?.repository?.config?.getString("remote", "origin", "url") ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
// todo remove this when we're not using Jenkins anymore
|
||||
fun jenkinsBuildNumber(): String? = System.getenv("BUILD_NUMBER")
|
||||
|
||||
// Manual task to download the bedrock data files from the CloudburstMC/Data repository
|
||||
// Invoke with ./gradlew :core:downloadBedrockData --suffix=1_20_70
|
||||
// Set suffix to the current Bedrock version
|
||||
tasks.register<DownloadFilesTask>("downloadBedrockData") {
|
||||
urls = listOf(
|
||||
"https://raw.githubusercontent.com/CloudburstMC/Data/master/entity_identifiers.dat",
|
||||
"https://raw.githubusercontent.com/CloudburstMC/Data/master/biome_definitions.dat",
|
||||
"https://raw.githubusercontent.com/CloudburstMC/Data/master/block_palette.nbt",
|
||||
"https://raw.githubusercontent.com/CloudburstMC/Data/master/creative_items.json",
|
||||
"https://raw.githubusercontent.com/CloudburstMC/Data/master/runtime_item_states.json"
|
||||
)
|
||||
suffixedFiles = listOf("block_palette.nbt", "creative_items.json", "runtime_item_states.json")
|
||||
|
||||
destinationDir = "$projectDir/src/main/resources/bedrock"
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector;
|
||||
|
||||
import org.geysermc.api.Geyser;
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Deprecated, please use {@link Geyser} or {@link GeyserImpl}.
|
||||
*
|
||||
* @deprecated legacy code
|
||||
*/
|
||||
@Deprecated
|
||||
public class GeyserConnector {
|
||||
public static final String NAME = GeyserImpl.NAME;
|
||||
public static final String GIT_VERSION = GeyserImpl.GIT_VERSION; // A fallback for running in IDEs
|
||||
public static final String VERSION = GeyserImpl.VERSION; // A fallback for running in IDEs
|
||||
|
||||
public static final String OAUTH_CLIENT_ID = GeyserImpl.OAUTH_CLIENT_ID;
|
||||
|
||||
private static final GeyserConnector INSTANCE = new GeyserConnector();
|
||||
|
||||
public static GeyserConnector getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public boolean isShuttingDown() {
|
||||
return GeyserImpl.getInstance().isShuttingDown();
|
||||
}
|
||||
|
||||
public PlatformType getPlatformType() {
|
||||
return GeyserImpl.getInstance().getPlatformType();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
GeyserImpl.getInstance().shutdown();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
GeyserImpl.getInstance().reload();
|
||||
}
|
||||
|
||||
public GeyserSession getPlayerByXuid(String xuid) {
|
||||
org.geysermc.geyser.session.GeyserSession session = GeyserImpl.getInstance().connectionByXuid(xuid);
|
||||
if (session != null) {
|
||||
return new GeyserSession(session);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public GeyserSession getPlayerByUuid(UUID uuid) {
|
||||
org.geysermc.geyser.session.GeyserSession session = GeyserImpl.getInstance().connectionByUuid(uuid);
|
||||
if (session != null) {
|
||||
return new GeyserSession(session);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isProductionEnvironment() {
|
||||
return GeyserImpl.getInstance().isProductionEnvironment();
|
||||
}
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.session;
|
||||
|
||||
import com.github.steveice10.packetlib.packet.Packet;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
|
||||
import org.geysermc.connector.network.session.auth.AuthData;
|
||||
|
||||
/**
|
||||
* Deprecated, legacy code. Serves as a wrapper around
|
||||
* the class used now.
|
||||
*
|
||||
* @deprecated legacy code
|
||||
*/
|
||||
@Deprecated
|
||||
public class GeyserSession {
|
||||
private final org.geysermc.geyser.session.GeyserSession handle;
|
||||
|
||||
public GeyserSession(org.geysermc.geyser.session.GeyserSession handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public AuthData getAuthData() {
|
||||
return new AuthData(this.handle.getAuthData());
|
||||
}
|
||||
|
||||
public boolean isMicrosoftAccount() {
|
||||
return this.handle.isMicrosoftAccount();
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return this.handle.isClosed();
|
||||
}
|
||||
|
||||
public String getRemoteAddress() {
|
||||
return this.handle.remoteServer().address();
|
||||
}
|
||||
|
||||
public int getRemotePort() {
|
||||
return this.handle.remoteServer().port();
|
||||
}
|
||||
|
||||
public int getRenderDistance() {
|
||||
return this.handle.getServerRenderDistance();
|
||||
}
|
||||
|
||||
public boolean isSentSpawnPacket() {
|
||||
return this.handle.isSentSpawnPacket();
|
||||
}
|
||||
|
||||
public boolean isLoggedIn() {
|
||||
return this.handle.isLoggedIn();
|
||||
}
|
||||
|
||||
public boolean isLoggingIn() {
|
||||
return this.handle.isLoggingIn();
|
||||
}
|
||||
|
||||
public boolean isSpawned() {
|
||||
return this.handle.isSpawned();
|
||||
}
|
||||
|
||||
public boolean isInteracting() {
|
||||
return this.handle.isInteracting();
|
||||
}
|
||||
|
||||
public boolean isCanFly() {
|
||||
return this.handle.isCanFly();
|
||||
}
|
||||
|
||||
public boolean isFlying() {
|
||||
return this.handle.isFlying();
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
this.handle.connect();
|
||||
}
|
||||
|
||||
public void login() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void authenticate(String username) {
|
||||
this.handle.authenticate(username);
|
||||
}
|
||||
|
||||
public void authenticate(String username, String password) {
|
||||
this.handle.authenticate(username, password);
|
||||
}
|
||||
|
||||
public void authenticateWithMicrosoftCode() {
|
||||
this.handle.authenticateWithMicrosoftCode();
|
||||
}
|
||||
|
||||
public void disconnect(String reason) {
|
||||
this.handle.disconnect(reason);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void executeInEventLoop(Runnable runnable) {
|
||||
this.handle.executeInEventLoop(runnable);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.handle.bedrockUsername();
|
||||
}
|
||||
|
||||
public boolean isConsole() {
|
||||
return this.handle.isConsole();
|
||||
}
|
||||
|
||||
public String getLocale() {
|
||||
return this.handle.locale();
|
||||
}
|
||||
|
||||
public void sendUpstreamPacket(BedrockPacket packet) {
|
||||
this.handle.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
public void sendUpstreamPacketImmediately(BedrockPacket packet) {
|
||||
this.handle.sendUpstreamPacketImmediately(packet);
|
||||
}
|
||||
|
||||
public void sendDownstreamPacket(Packet packet) {
|
||||
this.handle.sendDownstreamPacket(packet);
|
||||
}
|
||||
|
||||
public boolean hasPermission(String permission) {
|
||||
return this.handle.hasPermission(permission);
|
||||
}
|
||||
|
||||
public void sendAdventureSettings() {
|
||||
this.handle.sendAdventureSettings();
|
||||
}
|
||||
}
|
|
@ -36,11 +36,15 @@ public final class Constants {
|
|||
|
||||
public static final String FLOODGATE_DOWNLOAD_LOCATION = "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/";
|
||||
|
||||
public static final String GEYSER_DOWNLOAD_LOCATION = "https://ci.geysermc.org";
|
||||
public static final String GEYSER_DOWNLOAD_LOCATION = "https://geysermc.org/download";
|
||||
public static final String UPDATE_PERMISSION = "geyser.update";
|
||||
|
||||
static final String SAVED_REFRESH_TOKEN_FILE = "saved-refresh-tokens.json";
|
||||
|
||||
public static final String GEYSER_CUSTOM_NAMESPACE = "geyser_custom";
|
||||
|
||||
public static final String MINECRAFT_SKIN_SERVER_URL = "https://textures.minecraft.net/texture/";
|
||||
|
||||
static {
|
||||
URI wsUri = null;
|
||||
try {
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
package org.geysermc.geyser;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
|
@ -32,8 +34,6 @@ import org.geysermc.geyser.level.GeyserWorldManager;
|
|||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.InputStream;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.file.Path;
|
||||
|
@ -44,14 +44,28 @@ public interface GeyserBootstrap {
|
|||
GeyserWorldManager DEFAULT_CHUNK_MANAGER = new GeyserWorldManager();
|
||||
|
||||
/**
|
||||
* Called when the GeyserBootstrap is enabled
|
||||
* Called when the GeyserBootstrap is initialized.
|
||||
* This will only be called once, when Geyser is loading. Calling this must
|
||||
* happen before {@link #onGeyserEnable()}, since this "sets up" Geyser.
|
||||
*/
|
||||
void onEnable();
|
||||
void onGeyserInitialize();
|
||||
|
||||
/**
|
||||
* Called when the GeyserBootstrap is disabled
|
||||
* Called when the GeyserBootstrap is enabled/reloaded.
|
||||
* This starts Geyser, after which, Geyser is in a player-accepting state.
|
||||
*/
|
||||
void onDisable();
|
||||
void onGeyserEnable();
|
||||
|
||||
/**
|
||||
* Called when the GeyserBootstrap is disabled - either before a reload,
|
||||
* of before fully shutting down.
|
||||
*/
|
||||
void onGeyserDisable();
|
||||
|
||||
/**
|
||||
* Called when the GeyserBootstrap is shutting down.
|
||||
*/
|
||||
void onGeyserShutdown();
|
||||
|
||||
/**
|
||||
* Returns the current GeyserConfiguration
|
||||
|
@ -79,6 +93,7 @@ public interface GeyserBootstrap {
|
|||
*
|
||||
* @return The current PingPassthrough manager
|
||||
*/
|
||||
@Nullable
|
||||
IGeyserPingPassthrough getGeyserPingPassthrough();
|
||||
|
||||
/**
|
||||
|
@ -151,7 +166,7 @@ public interface GeyserBootstrap {
|
|||
* @param resource Resource to get
|
||||
* @return InputStream of the given resource
|
||||
*/
|
||||
default @Nonnull InputStream getResource(String resource) {
|
||||
default @NonNull InputStream getResourceOrThrow(@NonNull String resource) {
|
||||
InputStream stream = getResourceOrNull(resource);
|
||||
if (stream == null) {
|
||||
throw new AssertionError("Unable to find resource: " + resource);
|
||||
|
@ -162,7 +177,7 @@ public interface GeyserBootstrap {
|
|||
/**
|
||||
* @return the bind address being used by the Java server.
|
||||
*/
|
||||
@Nonnull
|
||||
@NonNull
|
||||
String getServerBindAddress();
|
||||
|
||||
/**
|
||||
|
|
|
@ -42,8 +42,8 @@ import net.kyori.adventure.text.format.NamedTextColor;
|
|||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
||||
import org.geysermc.api.Geyser;
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.cumulus.form.Form;
|
||||
import org.geysermc.cumulus.form.util.FormBuilder;
|
||||
import org.geysermc.erosion.packet.Packets;
|
||||
|
@ -53,25 +53,28 @@ import org.geysermc.floodgate.crypto.Base64Topping;
|
|||
import org.geysermc.floodgate.crypto.FloodgateCipher;
|
||||
import org.geysermc.floodgate.news.NewsItemAction;
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
import org.geysermc.geyser.api.command.CommandSource;
|
||||
import org.geysermc.geyser.api.event.EventBus;
|
||||
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserShutdownEvent;
|
||||
import org.geysermc.geyser.api.event.lifecycle.*;
|
||||
import org.geysermc.geyser.api.network.AuthType;
|
||||
import org.geysermc.geyser.api.network.BedrockListener;
|
||||
import org.geysermc.geyser.api.network.RemoteServer;
|
||||
import org.geysermc.geyser.api.util.MinecraftVersion;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
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.extension.GeyserExtensionManager;
|
||||
import org.geysermc.geyser.impl.MinecraftVersionImpl;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.network.netty.GeyserServer;
|
||||
import org.geysermc.geyser.pack.ResourcePack;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
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;
|
||||
|
@ -90,6 +93,7 @@ import java.io.IOException;
|
|||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.Key;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.*;
|
||||
|
@ -110,8 +114,8 @@ public class GeyserImpl implements GeyserApi {
|
|||
.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
|
||||
|
||||
public static final String NAME = "Geyser";
|
||||
public static final String GIT_VERSION = "${gitVersion}"; // A fallback for running in IDEs
|
||||
public static final String VERSION = "${version}"; // A fallback for running in IDEs
|
||||
public static final String GIT_VERSION = "${gitVersion}";
|
||||
public static final String VERSION = "${version}";
|
||||
|
||||
public static final String BUILD_NUMBER = "${buildNumber}";
|
||||
public static final String BRANCH = "${branch}";
|
||||
|
@ -139,6 +143,7 @@ public class GeyserImpl implements GeyserApi {
|
|||
|
||||
private UnixSocketClientListener erosionUnixListener;
|
||||
|
||||
@Setter
|
||||
private volatile boolean shuttingDown = false;
|
||||
|
||||
private ScheduledExecutorService scheduledThread;
|
||||
|
@ -156,8 +161,20 @@ public class GeyserImpl implements GeyserApi {
|
|||
@Getter(AccessLevel.NONE)
|
||||
private Map<String, String> savedRefreshTokens;
|
||||
|
||||
@Getter
|
||||
private static GeyserImpl instance;
|
||||
|
||||
/**
|
||||
* Determines if we're currently reloading. Replaces per-bootstrap reload checks
|
||||
*/
|
||||
private volatile boolean isReloading;
|
||||
|
||||
/**
|
||||
* Determines if Geyser is currently enabled. This is used to determine if {@link #disable()} should be called during {@link #shutdown()}.
|
||||
*/
|
||||
@Setter
|
||||
private boolean isEnabled;
|
||||
|
||||
private GeyserImpl(PlatformType platformType, GeyserBootstrap bootstrap) {
|
||||
instance = this;
|
||||
|
||||
|
@ -166,13 +183,16 @@ public class GeyserImpl implements GeyserApi {
|
|||
this.platformType = platformType;
|
||||
this.bootstrap = bootstrap;
|
||||
|
||||
GeyserLocale.finalizeDefaultLocale(this);
|
||||
|
||||
/* Initialize event bus */
|
||||
this.eventBus = new GeyserEventBus();
|
||||
|
||||
/* Load Extensions */
|
||||
/* Create Extension Manager */
|
||||
this.extensionManager = new GeyserExtensionManager();
|
||||
|
||||
/* Finalize locale loading now that we know the default locale from the config */
|
||||
GeyserLocale.finalizeDefaultLocale(this);
|
||||
|
||||
/* Load Extensions */
|
||||
this.extensionManager.init();
|
||||
this.eventBus.fire(new GeyserPreInitializeEvent(this.extensionManager, this.eventBus));
|
||||
}
|
||||
|
@ -201,6 +221,7 @@ public class GeyserImpl implements GeyserApi {
|
|||
if (ex != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
MinecraftLocale.ensureEN_US();
|
||||
String locale = GeyserLocale.getDefaultLocale();
|
||||
if (!"en_us".equals(locale)) {
|
||||
|
@ -220,25 +241,9 @@ public class GeyserImpl implements GeyserApi {
|
|||
|
||||
GeyserConfiguration config = bootstrap.getGeyserConfig();
|
||||
|
||||
boolean isGui = false;
|
||||
// This will check if we are in standalone and get the 'useGui' variable from there
|
||||
if (platformType == PlatformType.STANDALONE) {
|
||||
try {
|
||||
Class<?> cls = Class.forName("org.geysermc.geyser.platform.standalone.GeyserStandaloneBootstrap");
|
||||
isGui = (boolean) cls.getMethod("isUseGui").invoke(cls.cast(bootstrap));
|
||||
} catch (Exception e) {
|
||||
logger.debug("Failed detecting if standalone is using a GUI; if this is a GeyserConnect instance this can be safely ignored.");
|
||||
}
|
||||
}
|
||||
|
||||
double completeTime = (System.currentTimeMillis() - startupTime) / 1000D;
|
||||
String message = GeyserLocale.getLocaleStringLog("geyser.core.finish.done", new DecimalFormat("#.###").format(completeTime)) + " ";
|
||||
if (isGui) {
|
||||
message += GeyserLocale.getLocaleStringLog("geyser.core.finish.gui");
|
||||
} else {
|
||||
message += GeyserLocale.getLocaleStringLog("geyser.core.finish.console");
|
||||
}
|
||||
|
||||
String message = GeyserLocale.getLocaleStringLog("geyser.core.finish.done", new DecimalFormat("#.###").format(completeTime));
|
||||
message += " " + GeyserLocale.getLocaleStringLog("geyser.core.finish.console");
|
||||
logger.info(message);
|
||||
|
||||
if (platformType == PlatformType.STANDALONE) {
|
||||
|
@ -249,11 +254,17 @@ public class GeyserImpl implements GeyserApi {
|
|||
} else if (config.getRemote().authType() == AuthType.FLOODGATE) {
|
||||
VersionCheckUtils.checkForOutdatedFloodgate(logger);
|
||||
}
|
||||
|
||||
VersionCheckUtils.checkForOutdatedJava(logger);
|
||||
}
|
||||
|
||||
private void startInstance() {
|
||||
this.scheduledThread = Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory("Geyser Scheduled Thread"));
|
||||
|
||||
if (isReloading) {
|
||||
// If we're reloading, the default locale in the config might have changed.
|
||||
GeyserLocale.finalizeDefaultLocale(this);
|
||||
}
|
||||
GeyserLogger logger = bootstrap.getGeyserLogger();
|
||||
GeyserConfiguration config = bootstrap.getGeyserConfig();
|
||||
|
||||
|
@ -261,7 +272,7 @@ public class GeyserImpl implements GeyserApi {
|
|||
|
||||
SkinProvider.registerCacheImageTask(this);
|
||||
|
||||
ResourcePack.loadPacks();
|
||||
Registries.RESOURCE_PACKS.load();
|
||||
|
||||
String geyserUdpPort = System.getProperty("geyserUdpPort", "");
|
||||
String pluginUdpPort = geyserUdpPort.isEmpty() ? System.getProperty("pluginUdpPort", "") : geyserUdpPort;
|
||||
|
@ -327,15 +338,33 @@ public class GeyserImpl implements GeyserApi {
|
|||
}
|
||||
}
|
||||
|
||||
boolean floodgatePresent = bootstrap.testFloodgatePluginPresent();
|
||||
if (config.getRemote().authType() == AuthType.FLOODGATE && !floodgatePresent) {
|
||||
logger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " "
|
||||
+ GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
|
||||
return;
|
||||
} else if (config.isAutoconfiguredRemote() && floodgatePresent) {
|
||||
// Floodgate installed means that the user wants Floodgate authentication
|
||||
logger.debug("Auto-setting to Floodgate authentication.");
|
||||
config.getRemote().setAuthType(AuthType.FLOODGATE);
|
||||
String broadcastPort = System.getProperty("geyserBroadcastPort", "");
|
||||
if (!broadcastPort.isEmpty()) {
|
||||
int parsedPort;
|
||||
try {
|
||||
parsedPort = Integer.parseInt(broadcastPort);
|
||||
if (parsedPort < 1 || parsedPort > 65535) {
|
||||
throw new NumberFormatException("The broadcast port must be between 1 and 65535 inclusive!");
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.error(String.format("Invalid broadcast port: %s! Defaulting to configured port.", broadcastPort + " (" + e.getMessage() + ")"));
|
||||
parsedPort = config.getBedrock().port();
|
||||
}
|
||||
config.getBedrock().setBroadcastPort(parsedPort);
|
||||
logger.info("Broadcast port set from system property: " + parsedPort);
|
||||
}
|
||||
|
||||
if (platformType != PlatformType.VIAPROXY) {
|
||||
boolean floodgatePresent = bootstrap.testFloodgatePluginPresent();
|
||||
if (config.getRemote().authType() == AuthType.FLOODGATE && !floodgatePresent) {
|
||||
logger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " "
|
||||
+ GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
|
||||
return;
|
||||
} else if (config.isAutoconfiguredRemote() && floodgatePresent) {
|
||||
// Floodgate installed means that the user wants Floodgate authentication
|
||||
logger.debug("Auto-setting to Floodgate authentication.");
|
||||
config.getRemote().setAuthType(AuthType.FLOODGATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -413,7 +442,7 @@ public class GeyserImpl implements GeyserApi {
|
|||
metrics.addCustomChart(new Metrics.SingleLineChart("players", sessionManager::size));
|
||||
// Prevent unwanted words best we can
|
||||
metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().authType().toString().toLowerCase(Locale.ROOT)));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::getPlatformName));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::platformName));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("defaultLocale", GeyserLocale::getDefaultLocale));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserImpl.VERSION));
|
||||
metrics.addCustomChart(new Metrics.AdvancedPie("playerPlatform", () -> {
|
||||
|
@ -449,7 +478,7 @@ public class GeyserImpl implements GeyserApi {
|
|||
if (minecraftVersion != null) {
|
||||
Map<String, Map<String, Integer>> versionMap = new HashMap<>();
|
||||
Map<String, Integer> platformMap = new HashMap<>();
|
||||
platformMap.put(platformType.getPlatformName(), 1);
|
||||
platformMap.put(platformType.platformName(), 1);
|
||||
versionMap.put(minecraftVersion, platformMap);
|
||||
|
||||
metrics.addCustomChart(new Metrics.DrilldownPie("minecraftServerVersion", () -> {
|
||||
|
@ -497,12 +526,6 @@ public class GeyserImpl implements GeyserApi {
|
|||
}
|
||||
|
||||
if (config.getRemote().authType() == AuthType.ONLINE) {
|
||||
if (config.getUserAuths() != null && !config.getUserAuths().isEmpty()) {
|
||||
getLogger().warning("The 'userAuths' config section is now deprecated, and will be removed in the near future! " +
|
||||
"Please migrate to the new 'saved-user-logins' config option: " +
|
||||
"https://wiki.geysermc.org/geyser/understanding-the-config/");
|
||||
}
|
||||
|
||||
// May be written/read to on multiple threads from each GeyserSession as well as writing the config
|
||||
savedRefreshTokens = new ConcurrentHashMap<>();
|
||||
|
||||
|
@ -539,12 +562,15 @@ public class GeyserImpl implements GeyserApi {
|
|||
|
||||
newsHandler.handleNews(null, NewsItemAction.ON_SERVER_STARTED);
|
||||
|
||||
this.eventBus.fire(new GeyserPostInitializeEvent(this.extensionManager, this.eventBus));
|
||||
if (isReloading) {
|
||||
this.eventBus.fire(new GeyserPostReloadEvent(this.extensionManager, this.eventBus));
|
||||
} else {
|
||||
this.eventBus.fire(new GeyserPostInitializeEvent(this.extensionManager, this.eventBus));
|
||||
}
|
||||
|
||||
if (config.isNotifyOnNewBedrockUpdate()) {
|
||||
VersionCheckUtils.checkForGeyserUpdate(this::getLogger);
|
||||
}
|
||||
|
||||
VersionCheckUtils.checkForOutdatedJava(logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -603,9 +629,8 @@ public class GeyserImpl implements GeyserApi {
|
|||
return session.transfer(address, port);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
public void disable() {
|
||||
bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown"));
|
||||
shuttingDown = true;
|
||||
|
||||
if (sessionManager.size() >= 1) {
|
||||
bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.kick.log", sessionManager.size()));
|
||||
|
@ -619,24 +644,38 @@ public class GeyserImpl implements GeyserApi {
|
|||
skinUploader.close();
|
||||
}
|
||||
newsHandler.shutdown();
|
||||
this.commandManager().getCommands().clear();
|
||||
|
||||
if (this.erosionUnixListener != null) {
|
||||
this.erosionUnixListener.close();
|
||||
}
|
||||
|
||||
ResourcePack.PACKS.clear();
|
||||
Registries.RESOURCE_PACKS.get().clear();
|
||||
|
||||
this.setEnabled(false);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
shuttingDown = true;
|
||||
if (isEnabled) {
|
||||
this.disable();
|
||||
}
|
||||
this.commandManager().getCommands().clear();
|
||||
|
||||
// Disable extensions, fire the shutdown event
|
||||
this.eventBus.fire(new GeyserShutdownEvent(this.extensionManager, this.eventBus));
|
||||
this.extensionManager.disableExtensions();
|
||||
|
||||
bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done"));
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
shutdown();
|
||||
this.extensionManager.enableExtensions();
|
||||
bootstrap.onEnable();
|
||||
public void reloadGeyser() {
|
||||
isReloading = true;
|
||||
this.eventBus.fire(new GeyserPreReloadEvent(this.extensionManager, this.eventBus));
|
||||
|
||||
bootstrap.onGeyserDisable();
|
||||
bootstrap.onGeyserEnable();
|
||||
|
||||
isReloading = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -647,7 +686,7 @@ public class GeyserImpl implements GeyserApi {
|
|||
*/
|
||||
public boolean isProductionEnvironment() {
|
||||
// First is if Blossom runs, second is if Blossom doesn't run
|
||||
// noinspection ConstantConditions - changes in production
|
||||
//noinspection ConstantConditions,MismatchedStringCase - changes in production
|
||||
return !("git-local/dev-0000000".equals(GeyserImpl.GIT_VERSION) || "${gitVersion}".equals(GeyserImpl.GIT_VERSION));
|
||||
}
|
||||
|
||||
|
@ -663,8 +702,13 @@ public class GeyserImpl implements GeyserApi {
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <R extends T, T> @NonNull R provider(@NonNull Class<T> apiClass, @Nullable Object... args) {
|
||||
return (R) Registries.PROVIDERS.get(apiClass).create(args);
|
||||
ProviderSupplier provider = Registries.PROVIDERS.get(apiClass);
|
||||
if (provider == null) {
|
||||
throw new IllegalArgumentException("No provider found for " + apiClass);
|
||||
}
|
||||
return (R) provider.create(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -684,6 +728,43 @@ public class GeyserImpl implements GeyserApi {
|
|||
return getConfig().getBedrock();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Path configDirectory() {
|
||||
return bootstrap.getConfigFolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Path packDirectory() {
|
||||
return bootstrap.getConfigFolder().resolve("packs");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public PlatformType platformType() {
|
||||
return platformType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull MinecraftVersion supportedJavaVersion() {
|
||||
return new MinecraftVersionImpl(GameProtocol.getJavaMinecraftVersion(), GameProtocol.getJavaProtocolVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull List<MinecraftVersion> supportedBedrockVersions() {
|
||||
ArrayList<MinecraftVersion> versions = new ArrayList<>();
|
||||
for (BedrockCodec codec : GameProtocol.SUPPORTED_BEDROCK_CODECS) {
|
||||
versions.add(new MinecraftVersionImpl(codec.getMinecraftVersion(), codec.getProtocolVersion()));
|
||||
}
|
||||
return Collections.unmodifiableList(versions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CommandSource consoleCommandSource() {
|
||||
return getLogger();
|
||||
}
|
||||
|
||||
public int buildNumber() {
|
||||
if (!this.isProductionEnvironment()) {
|
||||
return 0;
|
||||
|
@ -705,13 +786,12 @@ public class GeyserImpl implements GeyserApi {
|
|||
throw new RuntimeException("Geyser has not been loaded yet!");
|
||||
}
|
||||
|
||||
// We've been reloaded
|
||||
if (instance.isShuttingDown()) {
|
||||
instance.shuttingDown = false;
|
||||
if (getInstance().isReloading()) {
|
||||
instance.startInstance();
|
||||
} else {
|
||||
instance.initialize();
|
||||
}
|
||||
instance.setEnabled(true);
|
||||
}
|
||||
|
||||
public GeyserLogger getLogger() {
|
||||
|
@ -758,8 +838,4 @@ public class GeyserImpl implements GeyserApi {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static GeyserImpl getInstance() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,9 +26,10 @@
|
|||
package org.geysermc.geyser;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface GeyserLogger extends GeyserCommandSource {
|
||||
|
||||
|
@ -119,7 +120,7 @@ public interface GeyserLogger extends GeyserCommandSource {
|
|||
}
|
||||
|
||||
@Override
|
||||
default void sendMessage(String message) {
|
||||
default void sendMessage(@NonNull String message) {
|
||||
info(message);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,9 @@ package org.geysermc.geyser;
|
|||
import javax.swing.*;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Scanner;
|
||||
|
||||
public class GeyserMain {
|
||||
|
@ -60,8 +62,8 @@ public class GeyserMain {
|
|||
helpStream = GeyserMain.class.getClassLoader().getResourceAsStream("languages/run-help/en_US.txt");
|
||||
}
|
||||
|
||||
Scanner help = new Scanner(helpStream).useDelimiter("\\Z");
|
||||
String line = "";
|
||||
Scanner help = new Scanner(Objects.requireNonNull(helpStream), StandardCharsets.UTF_8).useDelimiter("\\Z");
|
||||
String line;
|
||||
while (help.hasNext()) {
|
||||
line = help.next();
|
||||
|
||||
|
|
|
@ -29,10 +29,10 @@ import lombok.Getter;
|
|||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -73,15 +73,6 @@ public abstract class GeyserCommand implements Command {
|
|||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to {@link #subCommands()} ()}{@code .isEmpty()}.
|
||||
*
|
||||
* @return true if there are subcommand present for this command.
|
||||
*/
|
||||
public boolean hasSubCommands() {
|
||||
return !this.subCommands().isEmpty();
|
||||
}
|
||||
|
||||
public void setAliases(List<String> aliases) {
|
||||
this.aliases = aliases;
|
||||
}
|
||||
|
|
|
@ -26,11 +26,11 @@
|
|||
package org.geysermc.geyser.command;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
|
|
@ -29,7 +29,8 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
import org.geysermc.geyser.api.command.CommandExecutor;
|
||||
|
@ -53,8 +54,6 @@ 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.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -87,7 +86,7 @@ public class GeyserCommandManager {
|
|||
registerBuiltInCommand(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop"));
|
||||
}
|
||||
|
||||
if (this.geyser.extensionManager().extensions().size() > 0) {
|
||||
if (!this.geyser.extensionManager().extensions().isEmpty()) {
|
||||
registerBuiltInCommand(new ExtensionsCommand(this.geyser, "extensions", "geyser.commands.extensions.desc", "geyser.command.extensions"));
|
||||
}
|
||||
|
||||
|
@ -107,7 +106,8 @@ public class GeyserCommandManager {
|
|||
|
||||
// Register help commands for all extensions with commands
|
||||
for (Map.Entry<Extension, Map<String, Command>> entry : this.extensionCommands.entrySet()) {
|
||||
registerExtensionCommand(entry.getKey(), new HelpCommand(this.geyser, "help", "geyser.commands.exthelp.desc", "geyser.command.exthelp", entry.getKey().description().id(), entry.getValue()));
|
||||
String id = entry.getKey().description().id();
|
||||
registerExtensionCommand(entry.getKey(), new HelpCommand(this.geyser, "help", "geyser.commands.exthelp.desc", "geyser.command.exthelp." + id, id, entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,12 +135,12 @@ public class GeyserCommandManager {
|
|||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@NonNull
|
||||
public Map<String, Command> commands() {
|
||||
return Collections.unmodifiableMap(this.commands);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@NonNull
|
||||
public Map<Extension, Map<String, Command>> extensionCommands() {
|
||||
return Collections.unmodifiableMap(this.extensionCommands);
|
||||
}
|
||||
|
|
|
@ -26,21 +26,33 @@
|
|||
package org.geysermc.geyser.command.defaults;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.LoopbackUtil;
|
||||
import org.geysermc.geyser.util.WebUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class ConnectionTestCommand extends GeyserCommand {
|
||||
/*
|
||||
* The MOTD is temporarily changed during the connection test.
|
||||
* This allows us to check if we are pinging the correct Geyser instance
|
||||
*/
|
||||
public static String CONNECTION_TEST_MOTD = null;
|
||||
|
||||
private final GeyserImpl geyser;
|
||||
|
||||
private final Random random = new Random();
|
||||
|
||||
public ConnectionTestCommand(GeyserImpl geyser, String name, String description, String permission) {
|
||||
super(name, description, permission);
|
||||
this.geyser = geyser;
|
||||
|
@ -55,32 +67,87 @@ public class ConnectionTestCommand extends GeyserCommand {
|
|||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
sender.sendMessage("Provide the Bedrock server IP you are trying to connect with. Example: `test.geysermc.org:19132`");
|
||||
sender.sendMessage("Provide the server IP and port you are trying to test Bedrock connections for. Example: `test.geysermc.org:19132`");
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace "<" and ">" symbols if they are present to avoid the common issue of people including them
|
||||
String[] fullAddress = args[0].replace("<", "").replace(">", "").split(":", 2);
|
||||
|
||||
// Still allow people to not supply a port and fallback to 19132
|
||||
String[] fullAddress = args[0].split(":", 2);
|
||||
int port;
|
||||
if (fullAddress.length == 2) {
|
||||
port = Integer.parseInt(fullAddress[1]);
|
||||
try {
|
||||
port = Integer.parseInt(fullAddress[1]);
|
||||
} catch (NumberFormatException e) {
|
||||
// can occur if e.g. "/geyser connectiontest <ip>:<port> is ran
|
||||
sender.sendMessage("Not a valid port! Specify a valid numeric port.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
port = 19132;
|
||||
port = geyser.getConfig().getBedrock().broadcastPort();
|
||||
}
|
||||
String ip = fullAddress[0];
|
||||
|
||||
// Issue: people commonly checking placeholders
|
||||
if (ip.equals("ip")) {
|
||||
sender.sendMessage(ip + " is not a valid IP, and instead a placeholder. Please specify the IP to check.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Issue: do the ports not line up?
|
||||
if (port != geyser.getConfig().getBedrock().port()) {
|
||||
sender.sendMessage("The port you supplied (" + port + ") does not match the port supplied in Geyser's configuration ("
|
||||
+ geyser.getConfig().getBedrock().port() + "). You can change it under `bedrock` `port`.");
|
||||
// Issue: checking 0.0.0.0 won't work
|
||||
if (ip.equals("0.0.0.0")) {
|
||||
sender.sendMessage("Please specify the IP that you would connect with. 0.0.0.0 in the config tells Geyser to the listen on the server's IPv4.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Issue: people testing local ip
|
||||
if (ip.equals("localhost") || ip.startsWith("127.") || ip.startsWith("10.") || ip.startsWith("192.168.")) {
|
||||
sender.sendMessage("This tool checks if connections from other networks are possible, so you cannot check a local IP.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Issue: port out of bounds
|
||||
if (port <= 0 || port >= 65535) {
|
||||
sender.sendMessage("The port you specified is invalid! Please specify a valid port.");
|
||||
return;
|
||||
}
|
||||
|
||||
GeyserConfiguration config = geyser.getConfig();
|
||||
|
||||
// Issue: do the ports not line up? We only check this if players don't override the broadcast port - if they do, they (hopefully) know what they're doing
|
||||
if (config.getBedrock().broadcastPort() == config.getBedrock().port()) {
|
||||
if (port != config.getBedrock().port()) {
|
||||
if (fullAddress.length == 2) {
|
||||
sender.sendMessage("The port you are testing with (" + port + ") is not the same as you set in your Geyser configuration ("
|
||||
+ config.getBedrock().port() + ")");
|
||||
sender.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `port` in the config.");
|
||||
if (config.getBedrock().isCloneRemotePort()) {
|
||||
sender.sendMessage("You have `clone-remote-port` enabled. This option ignores the `bedrock` `port` in the config, and uses the Java server port instead.");
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage("You did not specify the port to check (add it with \":<port>\"), " +
|
||||
"and the default port 19132 does not match the port in your Geyser configuration ("
|
||||
+ config.getBedrock().port() + ")!");
|
||||
sender.sendMessage("Re-run the command with that port, or change the port in the config under `bedrock` `port`.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (config.getBedrock().broadcastPort() != port) {
|
||||
sender.sendMessage("The port you are testing with (" + port + ") is not the same as the broadcast port set in your Geyser configuration ("
|
||||
+ config.getBedrock().broadcastPort() + "). ");
|
||||
sender.sendMessage("You ONLY need to change the broadcast port if clients connects with a port different from the port Geyser is running on.");
|
||||
sender.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `broadcast-port` in the config.");
|
||||
}
|
||||
}
|
||||
|
||||
// Issue: is the `bedrock` `address` in the config different?
|
||||
if (!geyser.getConfig().getBedrock().address().equals("0.0.0.0")) {
|
||||
if (!config.getBedrock().address().equals("0.0.0.0")) {
|
||||
sender.sendMessage("The address specified in `bedrock` `address` is not \"0.0.0.0\" - this may cause issues unless this is deliberate and intentional.");
|
||||
}
|
||||
|
||||
// Issue: did someone turn on enable-proxy-protocol and they didn't mean it?
|
||||
if (geyser.getConfig().getBedrock().isEnableProxyProtocol()) {
|
||||
// Issue: did someone turn on enable-proxy-protocol, and they didn't mean it?
|
||||
if (config.getBedrock().isEnableProxyProtocol()) {
|
||||
sender.sendMessage("You have the `enable-proxy-protocol` setting enabled. " +
|
||||
"Unless you're deliberately using additional software that REQUIRES this setting, you may not need it enabled.");
|
||||
}
|
||||
|
@ -88,7 +155,6 @@ public class ConnectionTestCommand extends GeyserCommand {
|
|||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
// Issue: SRV record?
|
||||
String ip = fullAddress[0];
|
||||
String[] record = WebUtils.findSrvRecord(geyser, ip);
|
||||
if (record != null && !ip.equals(record[3]) && !record[2].equals(String.valueOf(port))) {
|
||||
sender.sendMessage("Bedrock Edition does not support SRV records. Try connecting to your server using the address " + record[3] + " and the port " + record[2]
|
||||
|
@ -102,37 +168,72 @@ public class ConnectionTestCommand extends GeyserCommand {
|
|||
"See here for steps on how to resolve: " + "https://wiki.geysermc.org/geyser/fixing-unable-to-connect-to-world/#using-geyser-on-the-same-computer");
|
||||
}
|
||||
|
||||
// mcsrvstatus will likely be replaced in the future with our own service where we can also test
|
||||
// around the OVH workaround without worrying about caching
|
||||
JsonNode output = WebUtils.getJson("https://api.mcsrvstat.us/bedrock/2/" + args[0]);
|
||||
// Generate some random, unique bits that another server wouldn't provide
|
||||
byte[] randomBytes = new byte[2];
|
||||
this.random.nextBytes(randomBytes);
|
||||
StringBuilder randomStr = new StringBuilder();
|
||||
for (byte b : randomBytes) {
|
||||
randomStr.append(Integer.toHexString(b));
|
||||
}
|
||||
String connectionTestMotd = "Geyser Connection Test " + randomStr;
|
||||
CONNECTION_TEST_MOTD = connectionTestMotd;
|
||||
|
||||
long cacheTime = output.get("debug").get("cachetime").asLong();
|
||||
String when;
|
||||
if (cacheTime == 0) {
|
||||
when = "now";
|
||||
} else {
|
||||
when = ((System.currentTimeMillis() / 1000L) - cacheTime) + " seconds ago";
|
||||
sender.sendMessage("Testing server connection to " + ip + " with port: " + port + " now. Please wait...");
|
||||
JsonNode output;
|
||||
try {
|
||||
String hostname = URLEncoder.encode(ip, StandardCharsets.UTF_8);
|
||||
output = WebUtils.getJson("https://checker.geysermc.org/ping?hostname=" + hostname + "&port=" + port);
|
||||
} finally {
|
||||
CONNECTION_TEST_MOTD = null;
|
||||
}
|
||||
|
||||
if (output.get("online").asBoolean()) {
|
||||
sender.sendMessage("Your server is likely online as of " + when + "!");
|
||||
if (output.get("success").asBoolean()) {
|
||||
JsonNode cache = output.get("cache");
|
||||
String when;
|
||||
if (cache.get("fromCache").asBoolean()) {
|
||||
when = cache.get("secondsSince").asInt() + " seconds ago";
|
||||
} else {
|
||||
when = "now";
|
||||
}
|
||||
|
||||
JsonNode ping = output.get("ping");
|
||||
JsonNode pong = ping.get("pong");
|
||||
String remoteMotd = pong.get("motd").asText();
|
||||
if (!connectionTestMotd.equals(remoteMotd)) {
|
||||
sender.sendMessage("The MOTD did not match when we pinged the server (we got '" + remoteMotd + "'). " +
|
||||
"Did you supply the correct IP and port of your server?");
|
||||
sendLinks(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ping.get("tcpFirst").asBoolean()) {
|
||||
sender.sendMessage("Your server hardware likely has some sort of firewall preventing people from joining easily. See https://geysermc.link/ovh-firewall for more information.");
|
||||
sendLinks(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage("Your server is likely online and working as of " + when + "!");
|
||||
sendLinks(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage("Your server is likely unreachable from outside the network as of " + when + ".");
|
||||
sender.sendMessage("Your server is likely unreachable from outside the network!");
|
||||
JsonNode message = output.get("message");
|
||||
if (message != null && !message.asText().isEmpty()) {
|
||||
sender.sendMessage("Got the error message: " + message.asText());
|
||||
}
|
||||
sendLinks(sender);
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage("Error while trying to check your connection!");
|
||||
sender.sendMessage("An error occurred while trying to check your connection! Check the console for more information.");
|
||||
geyser.getLogger().error("Error while trying to check your connection!", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendLinks(GeyserCommandSource sender) {
|
||||
sender.sendMessage("If you still have issues, check to see if your hosting provider has a specific setup: " +
|
||||
"https://wiki.geysermc.org/geyser/supported-hosting-providers/" + ", see this page: "
|
||||
+ "https://wiki.geysermc.org/geyser/fixing-unable-to-connect-to-world/" + ", or contact us on our Discord: " + "https://discord.gg/geysermc");
|
||||
sender.sendMessage("If you still face issues, check the setup guide for instructions: " +
|
||||
"https://wiki.geysermc.org/geyser/setup/");
|
||||
sender.sendMessage("If that does not work, see " + "https://wiki.geysermc.org/geyser/fixing-unable-to-connect-to-world/" + ", or contact us on Discord: " + "https://discord.gg/geysermc");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -30,7 +30,7 @@ import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
|||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
|
@ -98,7 +98,7 @@ public class DumpCommand extends GeyserCommand {
|
|||
return;
|
||||
}
|
||||
|
||||
String uploadedDumpUrl = "";
|
||||
String uploadedDumpUrl;
|
||||
|
||||
if (offlineDump) {
|
||||
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.writing", sender.locale()));
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
package org.geysermc.geyser.command.defaults;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.extension.Extension;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
|
@ -32,7 +33,6 @@ import org.geysermc.geyser.command.GeyserCommandSource;
|
|||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
package org.geysermc.geyser.command.defaults;
|
||||
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
|
@ -63,7 +63,8 @@ public class HelpCommand extends GeyserCommand {
|
|||
public void execute(GeyserSession session, GeyserCommandSource sender, String[] args) {
|
||||
int page = 1;
|
||||
int maxPage = 1;
|
||||
String header = GeyserLocale.getPlayerLocaleString("geyser.commands.help.header", sender.locale(), page, maxPage);
|
||||
String translationKey = this.baseCommand.equals("geyser") ? "geyser.commands.help.header" : "geyser.commands.extensions.header";
|
||||
String header = GeyserLocale.getPlayerLocaleString(translationKey, sender.locale(), page, maxPage);
|
||||
sender.sendMessage(header);
|
||||
|
||||
this.commands.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry -> {
|
||||
|
|
|
@ -25,13 +25,15 @@
|
|||
|
||||
package org.geysermc.geyser.command.defaults;
|
||||
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ReloadCommand extends GeyserCommand {
|
||||
|
||||
private final GeyserImpl geyser;
|
||||
|
@ -52,7 +54,8 @@ public class ReloadCommand extends GeyserCommand {
|
|||
sender.sendMessage(message);
|
||||
|
||||
geyser.getSessionManager().disconnectAll("geyser.commands.reload.kick");
|
||||
geyser.reload();
|
||||
//FIXME Without the tiny wait, players do not get kicked - same happens when Geyser tries to disconnect all sessions on shutdown
|
||||
geyser.getScheduledThread().schedule(geyser::reloadGeyser, 10, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -44,7 +44,7 @@ public class StatisticsCommand extends GeyserCommand {
|
|||
|
||||
session.setWaitingForStatistics(true);
|
||||
ServerboundClientCommandPacket ServerboundClientCommandPacket = new ServerboundClientCommandPacket(ClientCommand.STATS);
|
||||
session.sendDownstreamPacket(ServerboundClientCommandPacket);
|
||||
session.sendDownstreamGamePacket(ServerboundClientCommandPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
package org.geysermc.geyser.command.defaults;
|
||||
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
|
@ -52,7 +52,7 @@ public class StopCommand extends GeyserCommand {
|
|||
return;
|
||||
}
|
||||
|
||||
geyser.getBootstrap().onDisable();
|
||||
geyser.getBootstrap().onGeyserShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,8 +26,9 @@
|
|||
package org.geysermc.geyser.command.defaults;
|
||||
|
||||
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
|
@ -36,7 +37,6 @@ import org.geysermc.geyser.text.ChatColor;
|
|||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.WebUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
@ -76,7 +76,7 @@ public class VersionCommand extends GeyserCommand {
|
|||
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", sender.locale()));
|
||||
try {
|
||||
String buildXML = WebUtils.getBody("https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/" +
|
||||
URLEncoder.encode(GeyserImpl.BRANCH, StandardCharsets.UTF_8.toString()) + "/lastSuccessfulBuild/api/xml?xpath=//buildNumber");
|
||||
URLEncoder.encode(GeyserImpl.BRANCH, StandardCharsets.UTF_8) + "/lastSuccessfulBuild/api/xml?xpath=//buildNumber");
|
||||
if (buildXML.startsWith("<buildNumber>")) {
|
||||
int latestBuildNum = Integer.parseInt(buildXML.replaceAll("<(\\\\)?(/)?buildNumber>", "").trim());
|
||||
int buildNum = this.geyser.buildNumber();
|
||||
|
@ -84,12 +84,12 @@ public class VersionCommand extends GeyserCommand {
|
|||
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", sender.locale()));
|
||||
} else {
|
||||
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.outdated",
|
||||
sender.locale(), (latestBuildNum - buildNum), "https://ci.geysermc.org/"));
|
||||
sender.locale(), (latestBuildNum - buildNum), Constants.GEYSER_DOWNLOAD_LOCATION));
|
||||
}
|
||||
} else {
|
||||
throw new AssertionError("buildNumber missing");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.version.failed"), e);
|
||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.version.failed", sender.locale()));
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ import org.geysermc.geyser.text.GeyserLocale;
|
|||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface GeyserConfiguration {
|
||||
/**
|
||||
|
@ -53,17 +52,12 @@ public interface GeyserConfiguration {
|
|||
|
||||
List<String> getSavedUserLogins();
|
||||
|
||||
@Deprecated
|
||||
Map<String, ? extends IUserAuthenticationInfo> getUserAuths();
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
boolean isCommandSuggestions();
|
||||
|
||||
@JsonIgnore
|
||||
boolean isPassthroughMotd();
|
||||
|
||||
@JsonIgnore
|
||||
boolean isPassthroughProtocolName();
|
||||
|
||||
@JsonIgnore
|
||||
boolean isPassthroughPlayerCounts();
|
||||
|
||||
|
@ -100,6 +94,7 @@ public interface GeyserConfiguration {
|
|||
|
||||
boolean isForceResourcePacks();
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
boolean isXboxAchievementsEnabled();
|
||||
|
||||
int getCacheImages();
|
||||
|
@ -127,6 +122,8 @@ public interface GeyserConfiguration {
|
|||
|
||||
void setPort(int port);
|
||||
|
||||
void setBroadcastPort(int broadcastPort);
|
||||
|
||||
boolean isCloneRemotePort();
|
||||
|
||||
int getCompressionLevel();
|
||||
|
@ -147,8 +144,6 @@ public interface GeyserConfiguration {
|
|||
|
||||
void setPort(int port);
|
||||
|
||||
boolean isPasswordAuthentication();
|
||||
|
||||
boolean isUseProxyProtocol();
|
||||
|
||||
boolean isForwardHost();
|
||||
|
@ -164,18 +159,6 @@ public interface GeyserConfiguration {
|
|||
void setAuthType(AuthType authType);
|
||||
}
|
||||
|
||||
interface IUserAuthenticationInfo {
|
||||
String getEmail();
|
||||
|
||||
String getPassword();
|
||||
|
||||
/**
|
||||
* Will be removed after Microsoft accounts are fully migrated
|
||||
*/
|
||||
@Deprecated
|
||||
boolean isMicrosoftAccount();
|
||||
}
|
||||
|
||||
interface IMetricsInfo {
|
||||
|
||||
boolean isEnabled();
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@SuppressWarnings("FieldMayBeFinal") // Jackson requires that the fields are not final
|
||||
public class GeyserCustomSkullConfiguration {
|
||||
@JsonProperty("player-usernames")
|
||||
private List<String> playerUsernames;
|
||||
|
||||
@JsonProperty("player-uuids")
|
||||
private List<String> playerUUIDs;
|
||||
|
||||
@JsonProperty("player-profiles")
|
||||
private List<String> playerProfiles;
|
||||
|
||||
@JsonProperty("skin-hashes")
|
||||
private List<String> skinHashes;
|
||||
|
||||
public List<String> getPlayerUsernames() {
|
||||
return Objects.requireNonNullElse(playerUsernames, Collections.emptyList());
|
||||
}
|
||||
|
||||
public List<String> getPlayerUUIDs() {
|
||||
return Objects.requireNonNullElse(playerUUIDs, Collections.emptyList());
|
||||
}
|
||||
|
||||
public List<String> getPlayerProfiles() {
|
||||
return Objects.requireNonNullElse(playerProfiles, Collections.emptyList());
|
||||
}
|
||||
|
||||
public List<String> getPlayerSkinHashes() {
|
||||
return Objects.requireNonNullElse(skinHashes, Collections.emptyList());
|
||||
}
|
||||
}
|
|
@ -34,6 +34,7 @@ import com.fasterxml.jackson.databind.JsonDeserializer;
|
|||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.network.AuthType;
|
||||
import org.geysermc.geyser.network.CIDRMatcher;
|
||||
|
@ -44,7 +45,6 @@ import java.io.IOException;
|
|||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -67,8 +67,6 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||
|
||||
public abstract Path getFloodgateKeyPath();
|
||||
|
||||
private Map<String, UserAuthenticationInfo> userAuths;
|
||||
|
||||
@JsonProperty("command-suggestions")
|
||||
private boolean commandSuggestions = true;
|
||||
|
||||
|
@ -78,9 +76,6 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||
@JsonProperty("passthrough-player-counts")
|
||||
private boolean isPassthroughPlayerCounts = false;
|
||||
|
||||
@JsonProperty("passthrough-protocol-name")
|
||||
private boolean isPassthroughProtocolName = false;
|
||||
|
||||
@JsonProperty("legacy-ping-passthrough")
|
||||
private boolean isLegacyPingPassthrough = false;
|
||||
|
||||
|
@ -164,7 +159,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||
private String address = "0.0.0.0";
|
||||
|
||||
@Override
|
||||
public String address() {
|
||||
public @NonNull String address() {
|
||||
return address;
|
||||
}
|
||||
|
||||
|
@ -177,6 +172,15 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||
return port;
|
||||
}
|
||||
|
||||
@Setter
|
||||
@JsonProperty("broadcast-port")
|
||||
private int broadcastPort = 0;
|
||||
|
||||
@Override
|
||||
public int broadcastPort() {
|
||||
return broadcastPort;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@JsonProperty("clone-remote-port")
|
||||
private boolean cloneRemotePort = false;
|
||||
|
@ -266,13 +270,14 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||
private AuthType authType = AuthType.ONLINE;
|
||||
|
||||
@Override
|
||||
public AuthType authType() {
|
||||
public @NonNull AuthType authType() {
|
||||
return authType;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@JsonProperty("allow-password-authentication")
|
||||
private boolean passwordAuthentication = true;
|
||||
@Override
|
||||
public boolean resolveSrv() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@JsonProperty("use-proxy-protocol")
|
||||
|
@ -283,19 +288,6 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||
private boolean forwardHost = false;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@JsonIgnoreProperties(ignoreUnknown = true) // DO NOT REMOVE THIS! Otherwise, after we remove microsoft-account configs will not load
|
||||
public static class UserAuthenticationInfo implements IUserAuthenticationInfo {
|
||||
@AsteriskSerializer.Asterisk()
|
||||
private String email;
|
||||
|
||||
@AsteriskSerializer.Asterisk()
|
||||
private String password;
|
||||
|
||||
@JsonProperty("microsoft-account")
|
||||
private boolean microsoftAccount = false;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class MetricsInfo implements IMetricsInfo {
|
||||
|
|
|
@ -27,7 +27,7 @@ package org.geysermc.geyser.dump;
|
|||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.text.AsteriskSerializer;
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@ import com.google.common.io.ByteSource;
|
|||
import com.google.common.io.Files;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
||||
import org.geysermc.floodgate.util.DeviceOs;
|
||||
|
@ -106,8 +105,8 @@ public class DumpInfo {
|
|||
// https://stackoverflow.com/questions/304268/getting-a-files-md5-checksum-in-java
|
||||
File file = new File(DumpInfo.class.getProtectionDomain().getCodeSource().getLocation().toURI());
|
||||
ByteSource byteSource = Files.asByteSource(file);
|
||||
// Jenkins uses MD5 for its hash
|
||||
//noinspection UnstableApiUsage
|
||||
// Jenkins uses MD5 for its hash - TODO remove
|
||||
//noinspection UnstableApiUsage,deprecation
|
||||
md5Hash = byteSource.hash(Hashing.md5()).toString();
|
||||
//noinspection UnstableApiUsage
|
||||
sha256Hash = byteSource.hash(Hashing.sha256()).toString();
|
||||
|
@ -118,7 +117,7 @@ public class DumpInfo {
|
|||
}
|
||||
this.hashInfo = new HashInfo(md5Hash, sha256Hash);
|
||||
|
||||
this.ramInfo = new DumpInfo.RamInfo();
|
||||
this.ramInfo = new RamInfo();
|
||||
|
||||
if (addLog) {
|
||||
this.logsInfo = new LogsInfo();
|
||||
|
@ -202,7 +201,7 @@ public class DumpInfo {
|
|||
private boolean checkDockerBasic() {
|
||||
try {
|
||||
String OS = System.getProperty("os.name").toLowerCase();
|
||||
if (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0 || OS.indexOf("aix") > 0) {
|
||||
if (OS.contains("nix") || OS.contains("nux") || OS.indexOf("aix") > 0) {
|
||||
String output = new String(java.nio.file.Files.readAllBytes(Paths.get("/proc/1/cgroup")));
|
||||
|
||||
if (output.contains("docker")) {
|
||||
|
@ -259,60 +258,30 @@ public class DumpInfo {
|
|||
}
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public static class HashInfo {
|
||||
private final String md5Hash;
|
||||
private final String sha256Hash;
|
||||
public record HashInfo(String md5Hash, String sha256Hash) {
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class RamInfo {
|
||||
private final long free;
|
||||
private final long total;
|
||||
private final long max;
|
||||
|
||||
RamInfo() {
|
||||
this.free = Runtime.getRuntime().freeMemory() / MEGABYTE;
|
||||
this.total = Runtime.getRuntime().totalMemory() / MEGABYTE;
|
||||
this.max = Runtime.getRuntime().maxMemory() / MEGABYTE;
|
||||
public record RamInfo(long free, long total, long max) {
|
||||
public RamInfo() {
|
||||
this(Runtime.getRuntime().freeMemory() / MEGABYTE,
|
||||
Runtime.getRuntime().totalMemory() / MEGABYTE,
|
||||
Runtime.getRuntime().maxMemory() / MEGABYTE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* E.G. `-Xmx1024M` - all runtime JVM flags on this machine
|
||||
*/
|
||||
@Getter
|
||||
public static class FlagsInfo {
|
||||
private final List<String> flags;
|
||||
|
||||
FlagsInfo() {
|
||||
this.flags = ManagementFactory.getRuntimeMXBean().getInputArguments();
|
||||
public record FlagsInfo(List<String> flags) {
|
||||
public FlagsInfo() {
|
||||
this(ManagementFactory.getRuntimeMXBean().getInputArguments());
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class ExtensionInfo {
|
||||
public boolean enabled;
|
||||
public String name;
|
||||
public String version;
|
||||
public String apiVersion;
|
||||
public String main;
|
||||
public List<String> authors;
|
||||
public record ExtensionInfo(boolean enabled, String name, String version, String apiVersion, String main, List<String> authors) {
|
||||
}
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class GitInfo {
|
||||
private final String buildNumber;
|
||||
@JsonProperty("git.commit.id.abbrev")
|
||||
private final String commitHashAbbrev;
|
||||
@JsonProperty("git.commit.id")
|
||||
private final String commitHash;
|
||||
@JsonProperty("git.branch")
|
||||
private final String branchName;
|
||||
@JsonProperty("git.remote.origin.url")
|
||||
private final String originUrl;
|
||||
public record GitInfo(String buildNumber, @JsonProperty("git.commit.id.abbrev") String commitHashAbbrev, @JsonProperty("git.commit.id") String commitHash,
|
||||
@JsonProperty("git.branch") String branchName, @JsonProperty("git.remote.origin.url") String originUrl) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -104,6 +104,7 @@ public final class EntityDefinitions {
|
|||
public static final GeyserEntityDefinition<HorseEntity> HORSE;
|
||||
public static final GeyserEntityDefinition<ZombieEntity> HUSK;
|
||||
public static final GeyserEntityDefinition<SpellcasterIllagerEntity> ILLUSIONER; // Not present on Bedrock
|
||||
public static final GeyserEntityDefinition<InteractionEntity> INTERACTION;
|
||||
public static final GeyserEntityDefinition<IronGolemEntity> IRON_GOLEM;
|
||||
public static final GeyserEntityDefinition<ItemEntity> ITEM;
|
||||
public static final GeyserEntityDefinition<ItemFrameEntity> ITEM_FRAME;
|
||||
|
@ -133,6 +134,7 @@ public final class EntityDefinitions {
|
|||
public static final GeyserEntityDefinition<AbstractFishEntity> SALMON;
|
||||
public static final GeyserEntityDefinition<SheepEntity> SHEEP;
|
||||
public static final GeyserEntityDefinition<ShulkerEntity> SHULKER;
|
||||
public static final GeyserEntityDefinition<SnifferEntity> SNIFFER;
|
||||
public static final GeyserEntityDefinition<ThrowableEntity> SHULKER_BULLET;
|
||||
public static final GeyserEntityDefinition<MonsterEntity> SILVERFISH;
|
||||
public static final GeyserEntityDefinition<SkeletonEntity> SKELETON;
|
||||
|
@ -235,7 +237,7 @@ public final class EntityDefinitions {
|
|||
.type(EntityType.EXPERIENCE_ORB)
|
||||
.identifier("minecraft:xp_orb")
|
||||
.build();
|
||||
EVOKER_FANGS = GeyserEntityDefinition.builder(EvokerFangsEntity::new) // No entity metadata to listen to as of 1.18.1
|
||||
EVOKER_FANGS = GeyserEntityDefinition.inherited(EvokerFangsEntity::new, entityBase)
|
||||
.type(EntityType.EVOKER_FANGS)
|
||||
.height(0.8f).width(0.5f)
|
||||
.identifier("minecraft:evocation_fang")
|
||||
|
@ -297,8 +299,9 @@ public final class EntityDefinitions {
|
|||
.build();
|
||||
|
||||
GeyserEntityDefinition<Entity> displayBase = GeyserEntityDefinition.inherited(entityBase.factory(), entityBase)
|
||||
.addTranslator(null) // Interpolation start ticks
|
||||
.addTranslator(null) // Interpolation duration ID
|
||||
.addTranslator(null) // Interpolation delay
|
||||
.addTranslator(null) // Transformation interpolation duration
|
||||
.addTranslator(null) // Position/Rotation interpolation duration
|
||||
.addTranslator(null) // Translation
|
||||
.addTranslator(null) // Scale
|
||||
.addTranslator(null) // Left rotation
|
||||
|
@ -316,6 +319,19 @@ public final class EntityDefinitions {
|
|||
.type(EntityType.TEXT_DISPLAY)
|
||||
.identifier("minecraft:armor_stand")
|
||||
.addTranslator(MetadataType.CHAT, TextDisplayEntity::setText)
|
||||
.addTranslator(null) // Line width
|
||||
.addTranslator(null) // Background color
|
||||
.addTranslator(null) // Text opacity
|
||||
.addTranslator(null) // Bit mask
|
||||
.build();
|
||||
|
||||
INTERACTION = GeyserEntityDefinition.inherited(InteractionEntity::new, entityBase)
|
||||
.type(EntityType.INTERACTION)
|
||||
.heightAndWidth(1.0f) // default size until server specifies otherwise
|
||||
.identifier("minecraft:armor_stand")
|
||||
.addTranslator(MetadataType.FLOAT, InteractionEntity::setWidth)
|
||||
.addTranslator(MetadataType.FLOAT, InteractionEntity::setHeight)
|
||||
.addTranslator(MetadataType.BOOLEAN, InteractionEntity::setResponse)
|
||||
.build();
|
||||
|
||||
GeyserEntityDefinition<FireballEntity> fireballBase = GeyserEntityDefinition.inherited(FireballEntity::new, entityBase)
|
||||
|
@ -777,7 +793,7 @@ public final class EntityDefinitions {
|
|||
.build();
|
||||
FOX = GeyserEntityDefinition.inherited(FoxEntity::new, ageableEntityBase)
|
||||
.type(EntityType.FOX)
|
||||
.height(0.5f).width(1.25f)
|
||||
.height(0.7f).width(0.6f)
|
||||
.addTranslator(MetadataType.INT, FoxEntity::setFoxVariant)
|
||||
.addTranslator(MetadataType.BYTE, FoxEntity::setFoxFlags)
|
||||
.addTranslator(null) // Trusted player 1
|
||||
|
@ -842,6 +858,12 @@ public final class EntityDefinitions {
|
|||
.height(1.3f).width(0.9f)
|
||||
.addTranslator(MetadataType.BYTE, SheepEntity::setSheepFlags)
|
||||
.build();
|
||||
SNIFFER = GeyserEntityDefinition.inherited(SnifferEntity::new, ageableEntityBase)
|
||||
.type(EntityType.SNIFFER)
|
||||
.height(1.75f).width(1.9f)
|
||||
.addTranslator(MetadataType.SNIFFER_STATE, SnifferEntity::setSnifferState)
|
||||
.addTranslator(null) // Integer, drop seed at tick
|
||||
.build();
|
||||
STRIDER = GeyserEntityDefinition.inherited(StriderEntity::new, ageableEntityBase)
|
||||
.type(EntityType.STRIDER)
|
||||
.height(1.7f).width(0.9f)
|
||||
|
@ -884,7 +906,6 @@ public final class EntityDefinitions {
|
|||
.build();
|
||||
CAMEL = GeyserEntityDefinition.inherited(CamelEntity::new, abstractHorseEntityBase)
|
||||
.type(EntityType.CAMEL)
|
||||
.identifier("minecraft:llama") // todo 1.20
|
||||
.height(2.375f).width(1.7f)
|
||||
.addTranslator(MetadataType.BOOLEAN, CamelEntity::setDashing)
|
||||
.addTranslator(null) // Last pose change tick
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.entity;
|
||||
|
||||
import org.checkerframework.checker.index.qual.NonNegative;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.EmotePacket;
|
||||
import org.geysermc.geyser.api.entity.EntityData;
|
||||
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class GeyserEntityData implements EntityData {
|
||||
|
||||
private final GeyserSession session;
|
||||
|
||||
private final Set<UUID> movementLockOwners = new HashSet<>();
|
||||
|
||||
public GeyserEntityData(GeyserSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId) {
|
||||
CompletableFuture<GeyserEntity> future = new CompletableFuture<>();
|
||||
session.ensureInEventLoop(() -> future.complete(session.getEntityCache().getEntityByJavaId(javaId)));
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId) {
|
||||
Objects.requireNonNull(emoter, "emoter must not be null!");
|
||||
Entity entity = (Entity) emoter;
|
||||
if (entity.getSession() != session) {
|
||||
throw new IllegalStateException("Given entity must be from this session!");
|
||||
}
|
||||
|
||||
EmotePacket packet = new EmotePacket();
|
||||
packet.setRuntimeEntityId(entity.getGeyserId());
|
||||
packet.setXuid("");
|
||||
packet.setPlatformId(""); // BDS sends empty
|
||||
packet.setEmoteId(emoteId);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull GeyserPlayerEntity playerEntity() {
|
||||
return session.getPlayerEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean lockMovement(boolean lock, @NonNull UUID owner) {
|
||||
Objects.requireNonNull(owner, "owner must not be null!");
|
||||
if (lock) {
|
||||
movementLockOwners.add(owner);
|
||||
} else {
|
||||
movementLockOwners.remove(owner);
|
||||
}
|
||||
|
||||
session.lockInputs(session.camera().isCameraLocked(), isMovementLocked());
|
||||
return isMovementLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMovementLocked() {
|
||||
return !movementLockOwners.isEmpty();
|
||||
}
|
||||
}
|
|
@ -66,6 +66,7 @@ public record GeyserEntityDefinition<T extends Entity>(EntityFactory<T> factory,
|
|||
return new EntityDefinitionBuilder<>(factory);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <M> void translateMetadata(T entity, EntityMetadata<M, ? extends MetadataType<M>> metadata) {
|
||||
EntityMetadataTranslator<? super T, M, EntityMetadata<M, ? extends MetadataType<M>>> translator = (EntityMetadataTranslator<? super T, M, EntityMetadata<M, ? extends MetadataType<M>>>) this.translators.get(metadata.getId());
|
||||
if (translator == null) {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
package org.geysermc.geyser.entity.attribute;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
@ -69,7 +70,7 @@ public enum GeyserAttributeType {
|
|||
return getAttribute(value, maximum);
|
||||
}
|
||||
|
||||
public AttributeData getAttribute(float value, float maximum) {
|
||||
public @Nullable AttributeData getAttribute(float value, float maximum) {
|
||||
if (bedrockIdentifier == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ public class BoatEntity extends Entity {
|
|||
private int variant;
|
||||
|
||||
// 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.05f;
|
||||
private final float ROWING_SPEED = 0.1f;
|
||||
|
||||
public BoatEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, GeyserEntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
||||
// Initial rotation is incorrect
|
||||
|
@ -125,8 +125,8 @@ public class BoatEntity extends Entity {
|
|||
public void setVariant(IntEntityMetadata entityMetadata) {
|
||||
variant = entityMetadata.getPrimitiveValue();
|
||||
dirtyMetadata.put(EntityDataTypes.VARIANT, switch (variant) {
|
||||
case 6, 7 -> variant - 1; // Dark oak and mangrove
|
||||
case 5, 8 -> 0; // TODO temp until 1.20. Cherry and bamboo
|
||||
case 6, 7, 8 -> variant - 1; // dark_oak, mangrove, bamboo
|
||||
case 5 -> 8; // cherry
|
||||
default -> variant;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ import lombok.AccessLevel;
|
|||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector2f;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
|
||||
|
@ -171,7 +173,9 @@ public class Entity implements GeyserEntity {
|
|||
addEntityPacket.setUniqueEntityId(geyserId);
|
||||
addEntityPacket.setPosition(position);
|
||||
addEntityPacket.setMotion(motion);
|
||||
addEntityPacket.setRotation(getBedrockRotation().toVector2(false)); // TODO: Check this
|
||||
addEntityPacket.setRotation(Vector2f.from(pitch, yaw));
|
||||
addEntityPacket.setHeadRotation(headYaw);
|
||||
addEntityPacket.setBodyRotation(yaw); // TODO: This should be bodyYaw
|
||||
addEntityPacket.getMetadata().putFlags(flags);
|
||||
dirtyMetadata.apply(addEntityPacket.getMetadata());
|
||||
addAdditionalSpawnData(addEntityPacket);
|
||||
|
@ -490,9 +494,10 @@ public class Entity implements GeyserEntity {
|
|||
* Update the mount offsets of each passenger on this vehicle
|
||||
*/
|
||||
protected void updatePassengerOffsets() {
|
||||
for (Entity passenger : passengers) {
|
||||
for (int i = 0; i < passengers.size(); i++) {
|
||||
Entity passenger = passengers.get(i);
|
||||
if (passenger != null) {
|
||||
boolean rider = passengers.get(0) == this;
|
||||
boolean rider = i == 0;
|
||||
EntityUtils.updateMountOffset(passenger, this, rider, true, passengers.size() > 1);
|
||||
passenger.updateBedrockMetadata();
|
||||
}
|
||||
|
@ -576,7 +581,7 @@ public class Entity implements GeyserEntity {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <I extends Entity> I as(Class<I> entityClass) {
|
||||
public <I extends Entity> @Nullable I as(Class<I> entityClass) {
|
||||
return entityClass.isInstance(this) ? (I) this : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.entity.type;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class InteractionEntity extends Entity {
|
||||
|
||||
/**
|
||||
* true - java client hears swing sound when attacking, and arm swings when right-clicking
|
||||
* false - java client hears no swing sound when attacking, and arm does not swing when right-clicking
|
||||
*/
|
||||
private boolean response = false;
|
||||
|
||||
public InteractionEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, GeyserEntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
||||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeMetadata() {
|
||||
super.initializeMetadata();
|
||||
|
||||
// hide the armor stand but keep the hitbox active
|
||||
setFlag(EntityFlag.INVISIBLE, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult interact(Hand hand) {
|
||||
// these InteractionResults do mirror the java client
|
||||
// but the bedrock client won't arm swing itself because of our armor stand workaround
|
||||
if (response) {
|
||||
AnimatePacket animatePacket = new AnimatePacket();
|
||||
animatePacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
|
||||
animatePacket.setAction(AnimatePacket.Action.SWING_ARM);
|
||||
session.sendUpstreamPacket(animatePacket);
|
||||
|
||||
session.sendDownstreamGamePacket(new ServerboundSwingPacket(hand));
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
return InteractionResult.CONSUME;
|
||||
}
|
||||
|
||||
public void setWidth(FloatEntityMetadata width) {
|
||||
setBoundingBoxWidth(width.getPrimitiveValue());
|
||||
}
|
||||
|
||||
public void setHeight(FloatEntityMetadata height) {
|
||||
setBoundingBoxHeight(height.getPrimitiveValue());
|
||||
}
|
||||
|
||||
public void setResponse(BooleanEntityMetadata response) {
|
||||
this.response = response.getPrimitiveValue();
|
||||
}
|
||||
}
|
|
@ -35,7 +35,7 @@ import org.cloudburstmc.math.vector.Vector3f;
|
|||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.protocol.bedrock.data.defintions.BlockDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
||||
|
@ -83,8 +83,7 @@ public class ItemFrameEntity extends Entity {
|
|||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
|
||||
NbtMapBuilder blockBuilder = NbtMap.builder()
|
||||
.putString("name", this.definition.entityType() == EntityType.GLOW_ITEM_FRAME ? "minecraft:glow_frame" : "minecraft:frame")
|
||||
.putInt("version", session.getBlockMappings().getBlockStateVersion());
|
||||
.putString("name", this.definition.entityType() == EntityType.GLOW_ITEM_FRAME ? "minecraft:glow_frame" : "minecraft:frame");
|
||||
NbtMapBuilder statesBuilder = NbtMap.builder()
|
||||
.putInt("facing_direction", direction.ordinal())
|
||||
.putByte("item_frame_map_bit", (byte) 0)
|
||||
|
|
|
@ -38,10 +38,10 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
|||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.defintions.ItemDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId;
|
||||
|
@ -121,7 +121,7 @@ public class LivingEntity extends Entity {
|
|||
session.sendUpstreamPacket(attributesPacket);
|
||||
}
|
||||
|
||||
public Vector3i setBedPosition(EntityMetadata<Optional<Vector3i>, ?> entityMetadata) {
|
||||
public @Nullable Vector3i setBedPosition(EntityMetadata<Optional<Vector3i>, ?> entityMetadata) {
|
||||
Optional<Vector3i> optionalPos = entityMetadata.getValue();
|
||||
if (optionalPos.isPresent()) {
|
||||
Vector3i bedPosition = optionalPos.get();
|
||||
|
@ -211,10 +211,10 @@ public class LivingEntity extends Entity {
|
|||
// If an entity has a banner on them, it will be in the helmet slot in Java but the chestplate spot in Bedrock
|
||||
// But don't overwrite the chestplate if it isn't empty
|
||||
ItemMapping banner = session.getItemMappings().getStoredItems().banner();
|
||||
if (ItemDefinition.AIR.equals(chestplate.getDefinition()) && helmet.getDefinition().equals(banner)) {
|
||||
if (ItemData.AIR.equals(chestplate) && helmet.getDefinition().equals(banner.getBedrockDefinition())) {
|
||||
chestplate = this.helmet;
|
||||
helmet = ItemData.AIR;
|
||||
} else if (chestplate.getDefinition().equals(banner)) {
|
||||
} else if (chestplate.getDefinition().equals(banner.getBedrockDefinition())) {
|
||||
// Prevent chestplate banners from showing erroneously
|
||||
chestplate = ItemData.AIR;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.geyser.entity.type;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
|
@ -33,6 +34,7 @@ import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
|||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
// Note: 1.19.4 requires that the billboard is set to something in order to show, on Java Edition
|
||||
|
@ -49,6 +51,18 @@ public class TextDisplayEntity extends Entity {
|
|||
this.dirtyMetadata.put(EntityDataTypes.NAMETAG_ALWAYS_SHOW, (byte) 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplayNameVisible(BooleanEntityMetadata entityMetadata) {
|
||||
// Don't allow the display name to be hidden - messes with our armor stand.
|
||||
// On JE: Hiding the display name still shows the display entity text.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplayName(EntityMetadata<Optional<Component>, ?> entityMetadata) {
|
||||
// This would usually set EntityDataTypes.NAME, but we are instead using NAME for the text display.
|
||||
// On JE: custom name does not override text display.
|
||||
}
|
||||
|
||||
public void setText(EntityMetadata<Component, ?> entityMetadata) {
|
||||
this.dirtyMetadata.put(EntityDataTypes.NAME, MessageTranslator.convertMessage(entityMetadata.getValue()));
|
||||
}
|
||||
|
|
|
@ -51,7 +51,6 @@ public class ThrowableEntity extends Entity implements Tickable {
|
|||
|
||||
/**
|
||||
* Updates the position for the Bedrock client.
|
||||
*
|
||||
* Java clients assume the next positions of moving items. Bedrock needs to be explicitly told positions
|
||||
*/
|
||||
@Override
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.geyser.entity.type.living;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
|
@ -34,7 +35,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.EntityUtils;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class AbstractFishEntity extends WaterEntity {
|
||||
|
@ -48,9 +48,9 @@ public class AbstractFishEntity extends WaterEntity {
|
|||
setFlag(EntityFlag.HAS_GRAVITY, false);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (EntityUtils.attemptToBucket(itemInHand)) {
|
||||
return InteractionResult.SUCCESS;
|
||||
} else {
|
||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type.living;
|
|||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
|
@ -36,7 +37,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class AllayEntity extends MobEntity {
|
||||
|
@ -54,9 +54,9 @@ public class AllayEntity extends MobEntity {
|
|||
this.canDuplicate = entityMetadata.getPrimitiveValue();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (this.canDuplicate && getFlag(EntityFlag.DANCING) && isDuplicationItem(itemInHand)) {
|
||||
// Maybe better as another tag?
|
||||
return InteractiveTag.GIVE_ITEM_TO_ALLAY;
|
||||
|
@ -70,9 +70,9 @@ public class AllayEntity extends MobEntity {
|
|||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (this.canDuplicate && getFlag(EntityFlag.DANCING) && isDuplicationItem(itemInHand)) {
|
||||
//TOCHECK sound
|
||||
return InteractionResult.SUCCESS;
|
||||
|
|
|
@ -70,7 +70,7 @@ public class ArmorStandEntity extends LivingEntity {
|
|||
private boolean primaryEntity = true;
|
||||
/**
|
||||
* Whether the entity's position must be updated to included the offset.
|
||||
*
|
||||
* <p>
|
||||
* This should be true when the Java server marks the armor stand as invisible, but we shrink the entity
|
||||
* to allow the nametag to appear. Basically:
|
||||
* - Is visible: this is irrelevant (false)
|
||||
|
@ -207,7 +207,7 @@ public class ArmorStandEntity extends LivingEntity {
|
|||
* @param negativeZToggle the flag to set true if the Z value of rotation is negative
|
||||
* @param rotation the Java rotation value
|
||||
*/
|
||||
private void onRotationUpdate(EntityDataType dataLeech, EntityFlag negativeXToggle, EntityFlag negativeYToggle, EntityFlag negativeZToggle, Vector3f rotation) {
|
||||
private void onRotationUpdate(EntityDataType<Integer> dataLeech, EntityFlag negativeXToggle, EntityFlag negativeYToggle, EntityFlag negativeZToggle, Vector3f rotation) {
|
||||
// Indicate that rotation should be checked
|
||||
setFlag(EntityFlag.BRIBED, true);
|
||||
|
||||
|
@ -249,7 +249,7 @@ public class ArmorStandEntity extends LivingEntity {
|
|||
@Override
|
||||
public InteractionResult interactAt(Hand hand) {
|
||||
if (!isMarker && session.getPlayerInventory().getItemInHand(hand).asItem() != Items.NAME_TAG) {
|
||||
// Java Edition returns SUCCESS if in spectator mode, but this is overrided with an earlier check on the client
|
||||
// Java Edition returns SUCCESS if in spectator mode, but this is overridden with an earlier check on the client
|
||||
return InteractionResult.CONSUME;
|
||||
} else {
|
||||
return InteractionResult.PASS;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.geyser.entity.type.living;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
|
@ -33,7 +34,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DolphinEntity extends WaterEntity {
|
||||
|
@ -46,18 +46,18 @@ public class DolphinEntity extends WaterEntity {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (!itemInHand.isEmpty() && session.getTagCache().isFish(itemInHand)) {
|
||||
return InteractiveTag.FEED;
|
||||
}
|
||||
return super.testMobInteraction(hand, itemInHand);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (!itemInHand.isEmpty() && session.getTagCache().isFish(itemInHand)) {
|
||||
// Feed
|
||||
return InteractionResult.SUCCESS;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.geyser.entity.type.living;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
|
@ -35,7 +36,6 @@ import org.geysermc.geyser.item.Items;
|
|||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class IronGolemEntity extends GolemEntity {
|
||||
|
@ -51,9 +51,9 @@ public class IronGolemEntity extends GolemEntity {
|
|||
maxHealth = 100f;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (itemInHand.asItem() == Items.IRON_INGOT) {
|
||||
if (health < maxHealth) {
|
||||
// Healing the iron golem
|
||||
|
|
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.entity.type.living;
|
|||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
|
@ -40,7 +41,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MobEntity extends LivingEntity {
|
||||
|
@ -133,13 +133,13 @@ public class MobEntity extends LivingEntity {
|
|||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
@NonNull
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
return InteractiveTag.NONE;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
@NonNull
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type.living;
|
|||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
|
@ -36,7 +37,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SnowGolemEntity extends GolemEntity {
|
||||
|
@ -51,9 +51,9 @@ public class SnowGolemEntity extends GolemEntity {
|
|||
setFlag(EntityFlag.SHEARED, (xd & 0x10) != 0x10);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (Items.SHEARS == itemInHand.asItem() && isAlive() && !getFlag(EntityFlag.SHEARED)) {
|
||||
// Shearing the snow golem
|
||||
return InteractiveTag.SHEAR;
|
||||
|
@ -61,9 +61,9 @@ public class SnowGolemEntity extends GolemEntity {
|
|||
return InteractiveTag.NONE;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (Items.SHEARS == itemInHand.asItem() && isAlive() && !getFlag(EntityFlag.SHEARED)) {
|
||||
// Shearing the snow golem
|
||||
return InteractionResult.SUCCESS;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.geyser.entity.type.living;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
|
@ -34,7 +35,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class TadpoleEntity extends AbstractFishEntity {
|
||||
|
@ -42,18 +42,18 @@ public class TadpoleEntity extends AbstractFishEntity {
|
|||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (isFood(itemInHand)) {
|
||||
return InteractiveTag.FEED;
|
||||
}
|
||||
return super.testMobInteraction(hand, itemInHand);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (isFood(itemInHand)) {
|
||||
//TODO particles
|
||||
return InteractionResult.SUCCESS;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.geyser.entity.type.living.animal;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
|
@ -38,7 +39,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class AnimalEntity extends AgeableEntity {
|
||||
|
@ -59,18 +59,18 @@ public class AnimalEntity extends AgeableEntity {
|
|||
return item == Items.WHEAT;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (canEat(itemInHand)) {
|
||||
return InteractiveTag.FEED;
|
||||
}
|
||||
return super.testMobInteraction(hand, itemInHand);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (canEat(itemInHand)) {
|
||||
// FEED
|
||||
if (getFlag(EntityFlag.BABY)) {
|
||||
|
|
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.entity.type.living.animal;
|
|||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
|
@ -38,7 +39,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.EntityUtils;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class AxolotlEntity extends AnimalEntity {
|
||||
|
@ -74,9 +74,9 @@ public class AxolotlEntity extends AnimalEntity {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (EntityUtils.attemptToBucket(itemInHand)) {
|
||||
return InteractionResult.SUCCESS;
|
||||
} else {
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.geyser.entity.type.living.animal;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
|
@ -36,7 +37,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class CowEntity extends AnimalEntity {
|
||||
|
@ -44,9 +44,9 @@ public class CowEntity extends AnimalEntity {
|
|||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (getFlag(EntityFlag.BABY) || itemInHand.asItem() != Items.BUCKET) {
|
||||
return super.testMobInteraction(hand, itemInHand);
|
||||
}
|
||||
|
@ -54,9 +54,9 @@ public class CowEntity extends AnimalEntity {
|
|||
return InteractiveTag.MILK;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (getFlag(EntityFlag.BABY) || itemInHand.asItem() != Items.BUCKET) {
|
||||
return super.mobInteract(hand, itemInHand);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.entity.type.living.animal;
|
|||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
|
@ -38,7 +39,6 @@ import org.geysermc.geyser.item.Items;
|
|||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class GoatEntity extends AnimalEntity {
|
||||
|
@ -70,9 +70,9 @@ public class GoatEntity extends AnimalEntity {
|
|||
|
||||
// TODO testMobInteraction?
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (!getFlag(EntityFlag.BABY) && itemInHand.asItem() == Items.BUCKET) {
|
||||
session.playSoundEvent(isScreamer ? SoundEvent.MILK_SCREAMER : SoundEvent.MILK, position);
|
||||
return InteractionResult.SUCCESS;
|
||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type.living.animal;
|
|||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
|
@ -37,7 +38,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MooshroomEntity extends AnimalEntity {
|
||||
|
@ -52,9 +52,9 @@ public class MooshroomEntity extends AnimalEntity {
|
|||
dirtyMetadata.put(EntityDataTypes.VARIANT, isBrown ? 1 : 0);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (!isBaby()) {
|
||||
if (itemInHand.asItem() == Items.BOWL) {
|
||||
// Stew
|
||||
|
@ -67,9 +67,9 @@ public class MooshroomEntity extends AnimalEntity {
|
|||
return super.testMobInteraction(hand, itemInHand);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
boolean isBaby = isBaby();
|
||||
if (!isBaby && itemInHand.asItem() == Items.BOWL) {
|
||||
// Stew
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.geyser.entity.type.living.animal;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
|
@ -36,7 +37,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class OcelotEntity extends AnimalEntity {
|
||||
|
@ -50,9 +50,9 @@ public class OcelotEntity extends AnimalEntity {
|
|||
return item == Items.COD || item == Items.SALMON;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (!getFlag(EntityFlag.TRUSTING) && canEat(itemInHand) && session.getPlayerEntity().getPosition().distanceSquared(position) < 9f) {
|
||||
// Attempt to feed
|
||||
return InteractiveTag.FEED;
|
||||
|
@ -61,9 +61,9 @@ public class OcelotEntity extends AnimalEntity {
|
|||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (!getFlag(EntityFlag.TRUSTING) && canEat(itemInHand) && session.getPlayerEntity().getPosition().distanceSquared(position) < 9f) {
|
||||
// Attempt to feed
|
||||
return InteractionResult.SUCCESS;
|
||||
|
|
|
@ -28,6 +28,8 @@ package org.geysermc.geyser.entity.type.living.animal;
|
|||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
|
||||
|
@ -41,8 +43,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PandaEntity extends AnimalEntity {
|
||||
|
@ -93,18 +93,18 @@ public class PandaEntity extends AnimalEntity {
|
|||
return item == Items.BAMBOO;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (mainGene == Gene.WORRIED && session.isThunder()) {
|
||||
return InteractiveTag.NONE;
|
||||
}
|
||||
return super.testMobInteraction(hand, itemInHand);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (mainGene == Gene.WORRIED && session.isThunder()) {
|
||||
// Huh!
|
||||
return InteractionResult.PASS;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.geyser.entity.type.living.animal;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
|
@ -37,7 +38,6 @@ import org.geysermc.geyser.util.EntityUtils;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PigEntity extends AnimalEntity {
|
||||
|
@ -51,9 +51,9 @@ public class PigEntity extends AnimalEntity {
|
|||
return item == Items.CARROT || item == Items.POTATO || item == Items.BEETROOT;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (!canEat(itemInHand) && getFlag(EntityFlag.SADDLED) && passengers.isEmpty() && !session.isSneaking()) {
|
||||
// Mount
|
||||
return InteractiveTag.MOUNT;
|
||||
|
@ -68,9 +68,9 @@ public class PigEntity extends AnimalEntity {
|
|||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (!canEat(itemInHand) && getFlag(EntityFlag.SADDLED) && passengers.isEmpty() && !session.isSneaking()) {
|
||||
// Mount
|
||||
return InteractionResult.SUCCESS;
|
||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type.living.animal;
|
|||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
|
@ -38,7 +39,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SheepEntity extends AnimalEntity {
|
||||
|
@ -55,9 +55,9 @@ public class SheepEntity extends AnimalEntity {
|
|||
dirtyMetadata.put(EntityDataTypes.COLOR, (byte) color);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (itemInHand.asItem() == Items.SHEARS) {
|
||||
return InteractiveTag.SHEAR;
|
||||
} else {
|
||||
|
@ -73,9 +73,9 @@ public class SheepEntity extends AnimalEntity {
|
|||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (itemInHand.asItem() == Items.SHEARS) {
|
||||
return InteractionResult.CONSUME;
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.entity.type.living.animal;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.SnifferState;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
import org.geysermc.geyser.entity.type.Tickable;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class SnifferEntity extends AnimalEntity implements Tickable {
|
||||
private static final float DIGGING_HEIGHT = EntityDefinitions.SNIFFER.height() - 0.4f;
|
||||
private static final int DIG_END = 120;
|
||||
private static final int DIG_START = DIG_END - 34;
|
||||
|
||||
private Pose pose = Pose.STANDING; // Needed to call setDimensions for DIGGING state
|
||||
private int digTicks;
|
||||
|
||||
public SnifferEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, GeyserEntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
||||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPose(Pose pose) {
|
||||
this.pose = pose;
|
||||
super.setPose(pose);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDimensions(Pose pose) {
|
||||
if (getFlag(EntityFlag.DIGGING)) {
|
||||
setBoundingBoxHeight(DIGGING_HEIGHT);
|
||||
setBoundingBoxWidth(definition.width());
|
||||
} else {
|
||||
super.setDimensions(pose);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEat(Item item) {
|
||||
return session.getTagCache().isSnifferFood(item);
|
||||
}
|
||||
|
||||
public void setSnifferState(ObjectEntityMetadata<SnifferState> entityMetadata) {
|
||||
SnifferState snifferState = entityMetadata.getValue();
|
||||
|
||||
// SnifferState.SCENTING and SnifferState.IDLING not used in bedrock
|
||||
// The bedrock client does the scenting animation and sound on its own
|
||||
setFlag(EntityFlag.FEELING_HAPPY, snifferState == SnifferState.FEELING_HAPPY);
|
||||
setFlag(EntityFlag.SCENTING, snifferState == SnifferState.SNIFFING); // SnifferState.SNIFFING -> EntityFlag.SCENTING
|
||||
setFlag(EntityFlag.SEARCHING, snifferState == SnifferState.SEARCHING);
|
||||
setFlag(EntityFlag.DIGGING, snifferState == SnifferState.DIGGING);
|
||||
setFlag(EntityFlag.RISING, snifferState == SnifferState.RISING);
|
||||
|
||||
setDimensions(pose);
|
||||
|
||||
if (getFlag(EntityFlag.DIGGING)) {
|
||||
digTicks = DIG_END;
|
||||
} else {
|
||||
// Handles situations where the DIGGING state is exited earlier than expected,
|
||||
// such as hitting the sniffer or joining the game while it is digging
|
||||
digTicks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
// The java client renders digging particles on its own, but bedrock does not
|
||||
if (digTicks > 0 && --digTicks < DIG_START && digTicks % 5 == 0) {
|
||||
Vector3f rot = Vector3f.createDirectionDeg(0, -getYaw()).mul(2.25f);
|
||||
Vector3f pos = getPosition().add(rot).up(0.2f).floor(); // Handle non-full blocks
|
||||
int blockId = session.getBlockMappings().getBedrockBlockId(session.getGeyser().getWorldManager().getBlockAt(session, pos.toInt().down()));
|
||||
|
||||
LevelEventPacket levelEventPacket = new LevelEventPacket();
|
||||
levelEventPacket.setType(LevelEvent.PARTICLE_DESTROY_BLOCK_NO_SOUND);
|
||||
levelEventPacket.setPosition(pos);
|
||||
levelEventPacket.setData(blockId);
|
||||
session.sendUpstreamPacket(levelEventPacket);
|
||||
|
||||
if (digTicks % 10 == 0) {
|
||||
LevelSoundEventPacket levelSoundEventPacket = new LevelSoundEventPacket();
|
||||
levelSoundEventPacket.setSound(SoundEvent.HIT);
|
||||
levelSoundEventPacket.setPosition(pos);
|
||||
levelSoundEventPacket.setExtraData(blockId);
|
||||
levelSoundEventPacket.setIdentifier(":");
|
||||
session.sendUpstreamPacket(levelSoundEventPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type.living.animal;
|
|||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
|
@ -39,7 +40,6 @@ import org.geysermc.geyser.util.EntityUtils;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class StriderEntity extends AnimalEntity {
|
||||
|
@ -98,9 +98,9 @@ public class StriderEntity extends AnimalEntity {
|
|||
return item == Items.WARPED_FUNGUS;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (!canEat(itemInHand) && getFlag(EntityFlag.SADDLED) && passengers.isEmpty() && !session.isSneaking()) {
|
||||
// Mount Strider
|
||||
return InteractiveTag.RIDE_STRIDER;
|
||||
|
@ -115,9 +115,9 @@ public class StriderEntity extends AnimalEntity {
|
|||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (!canEat(itemInHand) && getFlag(EntityFlag.SADDLED) && passengers.isEmpty() && !session.isSneaking()) {
|
||||
// Mount Strider
|
||||
return InteractionResult.SUCCESS;
|
||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type.living.animal.horse;
|
|||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
|
||||
|
@ -44,7 +45,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -128,14 +128,14 @@ public class AbstractHorseEntity extends AnimalEntity {
|
|||
return DONKEY_AND_HORSE_FOODS.contains(item);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
return testHorseInteraction(hand, itemInHand);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
protected final InteractiveTag testHorseInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
@NonNull
|
||||
protected final InteractiveTag testHorseInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
boolean isBaby = isBaby();
|
||||
if (!isBaby) {
|
||||
if (getFlag(EntityFlag.TAMED) && session.isSneaking()) {
|
||||
|
@ -178,14 +178,14 @@ public class AbstractHorseEntity extends AnimalEntity {
|
|||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
return mobHorseInteract(hand, itemInHand);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
protected final InteractionResult mobHorseInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
@NonNull
|
||||
protected final InteractionResult mobHorseInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
boolean isBaby = isBaby();
|
||||
if (!isBaby) {
|
||||
if (getFlag(EntityFlag.TAMED) && session.isSneaking()) {
|
||||
|
@ -236,21 +236,21 @@ public class AbstractHorseEntity extends AnimalEntity {
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean testSaddle(@Nonnull GeyserItemStack itemInHand) {
|
||||
protected boolean testSaddle(@NonNull GeyserItemStack itemInHand) {
|
||||
return isAlive() && !getFlag(EntityFlag.BABY) && getFlag(EntityFlag.TAMED);
|
||||
}
|
||||
|
||||
protected boolean testForChest(@Nonnull GeyserItemStack itemInHand) {
|
||||
protected boolean testForChest(@NonNull GeyserItemStack itemInHand) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean additionalTestForInventoryOpen(@Nonnull GeyserItemStack itemInHand) {
|
||||
protected boolean additionalTestForInventoryOpen(@NonNull GeyserItemStack itemInHand) {
|
||||
return itemInHand.asItem().javaIdentifier().endsWith("_horse_armor");
|
||||
}
|
||||
|
||||
/* Just a place to stuff common code for the undead variants without having duplicate code */
|
||||
|
||||
protected final InteractiveTag testUndeadHorseInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected final InteractiveTag testUndeadHorseInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (!getFlag(EntityFlag.TAMED)) {
|
||||
return InteractiveTag.NONE;
|
||||
} else if (isBaby()) {
|
||||
|
@ -272,7 +272,7 @@ public class AbstractHorseEntity extends AnimalEntity {
|
|||
}
|
||||
}
|
||||
|
||||
protected final InteractionResult undeadHorseInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected final InteractionResult undeadHorseInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (!getFlag(EntityFlag.TAMED)) {
|
||||
return InteractionResult.PASS;
|
||||
} else if (isBaby()) {
|
||||
|
|
|
@ -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
|
||||
|
@ -27,8 +27,13 @@ package org.geysermc.geyser.entity.type.living.animal.horse;
|
|||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
|
@ -38,16 +43,50 @@ import java.util.UUID;
|
|||
|
||||
public class CamelEntity extends AbstractHorseEntity {
|
||||
|
||||
private static final float SITTING_HEIGHT_DIFFERENCE = 1.43F;
|
||||
public static final float SITTING_HEIGHT_DIFFERENCE = 1.43F;
|
||||
|
||||
public CamelEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, GeyserEntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
||||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
|
||||
dirtyMetadata.put(EntityDataTypes.CONTAINER_TYPE, (byte) ContainerType.HORSE.getId());
|
||||
|
||||
// Always tamed, but not indicated in horse flags
|
||||
setFlag(EntityFlag.TAMED, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeMetadata() {
|
||||
super.initializeMetadata();
|
||||
this.dirtyMetadata.put(EntityDataTypes.VARIANT, 2); // Closest llama colour to camel
|
||||
public void setHorseFlags(ByteEntityMetadata entityMetadata) {
|
||||
byte xd = entityMetadata.getPrimitiveValue();
|
||||
boolean saddled = (xd & 0x04) == 0x04;
|
||||
setFlag(EntityFlag.SADDLED, saddled);
|
||||
setFlag(EntityFlag.EATING, (xd & 0x10) == 0x10);
|
||||
setFlag(EntityFlag.STANDING, (xd & 0x20) == 0x20);
|
||||
|
||||
// HorseFlags
|
||||
// Bred 0x10
|
||||
// Eating 0x20
|
||||
// Open mouth 0x80
|
||||
int horseFlags = 0x0;
|
||||
horseFlags = (xd & 0x40) == 0x40 ? horseFlags | 0x80 : horseFlags;
|
||||
|
||||
// Only set eating when we don't have mouth open so a player interaction doesn't trigger the eating animation
|
||||
horseFlags = (xd & 0x10) == 0x10 && (xd & 0x40) != 0x40 ? horseFlags | 0x20 : horseFlags;
|
||||
|
||||
// Set the flags into the horse flags
|
||||
dirtyMetadata.put(EntityDataTypes.HORSE_FLAGS, horseFlags);
|
||||
|
||||
// Send the eating particles
|
||||
// We use the wheat metadata as static particles since Java
|
||||
// doesn't send over what item was used to feed the horse
|
||||
if ((xd & 0x40) == 0x40) {
|
||||
EntityEventPacket entityEventPacket = new EntityEventPacket();
|
||||
entityEventPacket.setRuntimeEntityId(geyserId);
|
||||
entityEventPacket.setType(EntityEventType.EATING_ITEM);
|
||||
entityEventPacket.setData(session.getItemMappings().getStoredItems().wheat().getBedrockDefinition().getRuntimeId() << 16);
|
||||
session.sendUpstreamPacket(entityEventPacket);
|
||||
}
|
||||
|
||||
// Shows the dash meter
|
||||
setFlag(EntityFlag.CAN_DASH, saddled);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -55,10 +94,16 @@ public class CamelEntity extends AbstractHorseEntity {
|
|||
return item == Items.CACTUS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPose(Pose pose) {
|
||||
setFlag(EntityFlag.SITTING, pose == Pose.SITTING);
|
||||
super.setPose(pose);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDimensions(Pose pose) {
|
||||
if (pose == Pose.SITTING) {
|
||||
setBoundingBoxWidth(definition.height() - SITTING_HEIGHT_DIFFERENCE);
|
||||
setBoundingBoxHeight(definition.height() - SITTING_HEIGHT_DIFFERENCE);
|
||||
setBoundingBoxWidth(definition.width());
|
||||
} else {
|
||||
super.setDimensions(pose);
|
||||
|
@ -66,6 +111,5 @@ public class CamelEntity extends AbstractHorseEntity {
|
|||
}
|
||||
|
||||
public void setDashing(BooleanEntityMetadata entityMetadata) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
package org.geysermc.geyser.entity.type.living.animal.horse;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
|
@ -32,7 +33,6 @@ import org.geysermc.geyser.inventory.GeyserItemStack;
|
|||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ChestedHorseEntity extends AbstractHorseEntity {
|
||||
|
@ -47,18 +47,18 @@ public class ChestedHorseEntity extends AbstractHorseEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean testSaddle(@Nonnull GeyserItemStack itemInHand) {
|
||||
protected boolean testSaddle(@NonNull GeyserItemStack itemInHand) {
|
||||
// Not checked here
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean testForChest(@Nonnull GeyserItemStack itemInHand) {
|
||||
protected boolean testForChest(@NonNull GeyserItemStack itemInHand) {
|
||||
return itemInHand.asItem() == Items.CHEST && !getFlag(EntityFlag.CHESTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean additionalTestForInventoryOpen(@Nonnull GeyserItemStack itemInHand) {
|
||||
protected boolean additionalTestForInventoryOpen(@NonNull GeyserItemStack itemInHand) {
|
||||
// Armor won't work on these
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.geyser.entity.type.living.animal.horse;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
|
@ -33,7 +34,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SkeletonHorseEntity extends AbstractHorseEntity {
|
||||
|
@ -41,15 +41,15 @@ public class SkeletonHorseEntity extends AbstractHorseEntity {
|
|||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
return testUndeadHorseInteraction(hand, itemInHand);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
return undeadHorseInteract(hand, itemInHand);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.geyser.entity.type.living.animal.horse;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
|
@ -33,7 +34,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ZombieHorseEntity extends AbstractHorseEntity {
|
||||
|
@ -41,15 +41,15 @@ public class ZombieHorseEntity extends AbstractHorseEntity {
|
|||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
return testUndeadHorseInteraction(hand, itemInHand);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
return undeadHorseInteract(hand, itemInHand);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanE
|
|||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
|
@ -40,7 +41,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class CatEntity extends TameableEntity {
|
||||
|
@ -112,9 +112,9 @@ public class CatEntity extends TameableEntity {
|
|||
return item == Items.COD || item == Items.SALMON;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
boolean tamed = getFlag(EntityFlag.TAMED);
|
||||
if (tamed && ownerBedrockId == session.getPlayerEntity().getGeyserId()) {
|
||||
// Toggle sitting
|
||||
|
@ -124,9 +124,9 @@ public class CatEntity extends TameableEntity {
|
|||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
boolean tamed = getFlag(EntityFlag.TAMED);
|
||||
if (tamed && ownerBedrockId == session.getPlayerEntity().getGeyserId()) {
|
||||
return InteractionResult.SUCCESS;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.geyser.entity.type.living.animal.tameable;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
|
@ -36,7 +37,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -61,9 +61,9 @@ public class ParrotEntity extends TameableEntity {
|
|||
return item == Items.COOKIE;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
boolean tame = getFlag(EntityFlag.TAMED);
|
||||
if (!tame && isTameFood(itemInHand.asItem())) {
|
||||
return InteractiveTag.FEED;
|
||||
|
@ -76,9 +76,9 @@ public class ParrotEntity extends TameableEntity {
|
|||
return super.testMobInteraction(hand, itemInHand);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
boolean tame = getFlag(EntityFlag.TAMED);
|
||||
if (!tame && isTameFood(itemInHand.asItem())) {
|
||||
return InteractionResult.SUCCESS;
|
||||
|
|
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.entity.type.living.animal.tameable;
|
|||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
|
@ -40,7 +41,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -103,9 +103,9 @@ public class WolfEntity extends TameableEntity {
|
|||
return !getFlag(EntityFlag.ANGRY) && super.canBeLeashed();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (getFlag(EntityFlag.ANGRY)) {
|
||||
return InteractiveTag.NONE;
|
||||
}
|
||||
|
@ -126,9 +126,9 @@ public class WolfEntity extends TameableEntity {
|
|||
return super.testMobInteraction(hand, itemInHand);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (ownerBedrockId == session.getPlayerEntity().getGeyserId() || getFlag(EntityFlag.TAMED)
|
||||
|| itemInHand.asItem() == Items.BONE && !getFlag(EntityFlag.ANGRY)) {
|
||||
// Sitting toggle or feeding; not angry
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.geyser.entity.type.living.merchant;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.GeyserEntityDefinition;
|
||||
|
@ -37,7 +38,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class AbstractMerchantEntity extends AgeableEntity {
|
||||
|
@ -51,9 +51,9 @@ public class AbstractMerchantEntity extends AgeableEntity {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (itemInHand.asItem() != Items.VILLAGER_SPAWN_EGG
|
||||
&& (definition != EntityDefinitions.VILLAGER || !getFlag(EntityFlag.SLEEPING) && ((VillagerEntity) this).isCanTradeWith())) {
|
||||
// An additional check we know cannot work
|
||||
|
@ -64,9 +64,9 @@ public class AbstractMerchantEntity extends AgeableEntity {
|
|||
return super.testMobInteraction(hand, itemInHand);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (itemInHand.asItem() != Items.VILLAGER_SPAWN_EGG
|
||||
&& (definition != EntityDefinitions.VILLAGER || !getFlag(EntityFlag.SLEEPING))
|
||||
&& (definition != EntityDefinitions.WANDERING_TRADER || !getFlag(EntityFlag.BABY))) {
|
||||
|
|
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.entity.type.living.merchant;
|
|||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData;
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
|
@ -78,6 +79,7 @@ public class VillagerEntity extends AbstractMerchantEntity {
|
|||
VILLAGER_REGIONS[6] = 6;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Vector3i bedPosition;
|
||||
/**
|
||||
* Used in the interactive tag manager
|
||||
|
@ -103,7 +105,7 @@ public class VillagerEntity extends AbstractMerchantEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Vector3i setBedPosition(EntityMetadata<Optional<Vector3i>, ?> entityMetadata) {
|
||||
public @Nullable Vector3i setBedPosition(EntityMetadata<Optional<Vector3i>, ?> entityMetadata) {
|
||||
return bedPosition = super.setBedPosition(entityMetadata);
|
||||
}
|
||||
|
||||
|
@ -118,7 +120,7 @@ public class VillagerEntity extends AbstractMerchantEntity {
|
|||
|
||||
// The bed block
|
||||
int blockId = session.getGeyser().getWorldManager().getBlockAt(session, bedPosition);
|
||||
String fullIdentifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(blockId, BlockMapping.AIR).getJavaIdentifier();
|
||||
String fullIdentifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(blockId, BlockMapping.DEFAULT).getJavaIdentifier();
|
||||
|
||||
// Set the correct position offset and rotation when sleeping
|
||||
int bedRotation = 0;
|
||||
|
|
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.entity.type.living.monster;
|
|||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
|
@ -37,7 +38,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class CreeperEntity extends MonsterEntity {
|
||||
|
@ -62,9 +62,9 @@ public class CreeperEntity extends MonsterEntity {
|
|||
setFlag(EntityFlag.IGNITED, ignitedByFlintAndSteel);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (session.getTagCache().isCreeperIgniter(itemInHand.asItem())) {
|
||||
return InteractiveTag.IGNITE_CREEPER;
|
||||
} else {
|
||||
|
@ -72,9 +72,9 @@ public class CreeperEntity extends MonsterEntity {
|
|||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (session.getTagCache().isCreeperIgniter(itemInHand.asItem())) {
|
||||
// Ignite creeper - as of 1.19.3
|
||||
session.playSoundEvent(SoundEvent.IGNITE, position);
|
||||
|
|
|
@ -29,7 +29,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanE
|
|||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.data.defintions.BlockDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEvent2Packet;
|
||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type.living.monster;
|
|||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
|
@ -37,7 +38,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PiglinEntity extends BasePiglinEntity {
|
||||
|
@ -71,9 +71,9 @@ public class PiglinEntity extends BasePiglinEntity {
|
|||
super.updateOffHand(session);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
InteractiveTag tag = super.testMobInteraction(hand, itemInHand);
|
||||
if (tag != InteractiveTag.NONE) {
|
||||
return tag;
|
||||
|
@ -82,9 +82,9 @@ public class PiglinEntity extends BasePiglinEntity {
|
|||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
InteractionResult superResult = super.mobInteract(hand, itemInHand);
|
||||
if (superResult.consumesAction()) {
|
||||
return superResult;
|
||||
|
@ -93,7 +93,7 @@ public class PiglinEntity extends BasePiglinEntity {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean canGiveGoldTo(@Nonnull GeyserItemStack itemInHand) {
|
||||
private boolean canGiveGoldTo(@NonNull GeyserItemStack itemInHand) {
|
||||
return !getFlag(EntityFlag.BABY) && itemInHand.asItem() == Items.GOLD_INGOT && !getFlag(EntityFlag.ADMIRING);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadat
|
|||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
|
@ -40,7 +41,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ZombieVillagerEntity extends ZombieEntity {
|
||||
|
@ -67,9 +67,9 @@ public class ZombieVillagerEntity extends ZombieEntity {
|
|||
return getFlag(EntityFlag.IS_TRANSFORMING) || super.isShaking();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (itemInHand.asItem() == Items.GOLDEN_APPLE) {
|
||||
return InteractiveTag.CURE;
|
||||
} else {
|
||||
|
@ -77,9 +77,9 @@ public class ZombieVillagerEntity extends ZombieEntity {
|
|||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@Nonnull Hand hand, @Nonnull GeyserItemStack itemInHand) {
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (itemInHand.asItem() == Items.GOLDEN_APPLE) {
|
||||
// The client doesn't know if the entity has weakness as that's not usually sent over the network
|
||||
return InteractionResult.CONSUME;
|
||||
|
|
|
@ -36,14 +36,23 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
|||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.*;
|
||||
import org.cloudburstmc.protocol.bedrock.data.Ability;
|
||||
import org.cloudburstmc.protocol.bedrock.data.AbilityLayer;
|
||||
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||
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.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.*;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
|
@ -57,7 +66,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.geyser.util.ChunkUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
@ -86,6 +94,7 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
|||
@Nullable
|
||||
private String texturesProperty;
|
||||
|
||||
@Nullable
|
||||
private Vector3i bedPosition;
|
||||
|
||||
/**
|
||||
|
@ -142,6 +151,10 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
|||
addPlayerPacket.setGameType(GameType.SURVIVAL); //TODO
|
||||
addPlayerPacket.setAbilityLayers(BASE_ABILITY_LAYER); // Recommended to be added since 1.19.10, but only needed here for permissions viewing
|
||||
addPlayerPacket.getMetadata().putFlags(flags);
|
||||
|
||||
// Since 1.20.60, the nametag does not show properly if this is not set :/
|
||||
// The nametag does disappear properly when the player is invisible though.
|
||||
dirtyMetadata.put(EntityDataTypes.NAMETAG_ALWAYS_SHOW, (byte) 1);
|
||||
dirtyMetadata.apply(addPlayerPacket.getMetadata());
|
||||
|
||||
setFlagsDirty(false);
|
||||
|
@ -240,7 +253,7 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Vector3i setBedPosition(EntityMetadata<Optional<Vector3i>, ?> entityMetadata) {
|
||||
public @Nullable Vector3i setBedPosition(EntityMetadata<Optional<Vector3i>, ?> entityMetadata) {
|
||||
bedPosition = super.setBedPosition(entityMetadata);
|
||||
if (bedPosition != null) {
|
||||
// Required to sync position of entity to bed
|
||||
|
@ -432,4 +445,9 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
|||
public UUID getTabListUuid() {
|
||||
return getUuid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3f position() {
|
||||
return this.position.clone();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,10 @@ import com.github.steveice10.mc.protocol.data.game.entity.attribute.AttributeTyp
|
|||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.GlobalPos;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
|
@ -43,7 +45,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.util.AttributeUtils;
|
||||
import org.geysermc.geyser.util.DimensionUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -67,10 +68,6 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
*/
|
||||
@Getter
|
||||
private boolean isRidingInFront;
|
||||
/**
|
||||
* Used for villager inventory emulation.
|
||||
*/
|
||||
private int fakeTradeXp;
|
||||
|
||||
public SessionPlayerEntity(GeyserSession session) {
|
||||
super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null);
|
||||
|
@ -113,16 +110,23 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
this.position = position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sending any updated flags (sprinting, onFire, etc.) to the client while in spectator is not needed
|
||||
* Also "fixes" <a href="https://github.com/GeyserMC/Geyser/issues/3318">issue 3318</a>
|
||||
*/
|
||||
@Override
|
||||
public void setFlags(ByteEntityMetadata entityMetadata) {
|
||||
super.setFlags(entityMetadata);
|
||||
session.setSwimmingInWater((entityMetadata.getPrimitiveValue() & 0x10) == 0x10 && getFlag(EntityFlag.SPRINTING));
|
||||
// TODO: proper fix, BDS somehow does it? https://paste.gg/p/anonymous/3adfb7612f1540be80fa03a2281f93dc (BDS 1.20.13)
|
||||
if (!this.session.getGameMode().equals(GameMode.SPECTATOR)) {
|
||||
super.setFlags(entityMetadata);
|
||||
session.setSwimmingInWater((entityMetadata.getPrimitiveValue() & 0x10) == 0x10 && getFlag(EntityFlag.SPRINTING));
|
||||
}
|
||||
refreshSpeed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since 1.19.40, the client must be re-informed of its bounding box on respawn
|
||||
* See https://github.com/GeyserMC/Geyser/issues/3370
|
||||
* See <a href="https://github.com/GeyserMC/Geyser/issues/3370">issue 3370</a>
|
||||
*/
|
||||
public void updateBoundingBox() {
|
||||
dirtyMetadata.put(EntityDataTypes.HEIGHT, getBoundingBoxHeight());
|
||||
|
@ -175,11 +179,6 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
this.isRidingInFront = position != null && position.getX() > 0;
|
||||
}
|
||||
|
||||
public void addFakeTradeExperience(int tradeXp) {
|
||||
fakeTradeXp += tradeXp;
|
||||
dirtyMetadata.put(EntityDataTypes.TRADE_EXPERIENCE, fakeTradeXp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeData createHealthAttribute() {
|
||||
// Max health must be divisible by two in bedrock
|
||||
|
@ -255,4 +254,17 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
public UUID getTabListUuid() {
|
||||
return session.getAuthData().uuid();
|
||||
}
|
||||
|
||||
public void resetMetadata() {
|
||||
// Reset all metadata to their default values
|
||||
// This is used when a player respawns
|
||||
this.initializeMetadata();
|
||||
|
||||
// Reset air
|
||||
this.resetAir();
|
||||
}
|
||||
|
||||
public void resetAir() {
|
||||
this.setAirSupply(getMaxAir());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,9 +25,9 @@
|
|||
|
||||
package org.geysermc.geyser.erosion;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.erosion.packet.geyserbound.*;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class AbstractGeyserboundPacketHandler implements GeyserboundPacketHandler {
|
||||
protected final GeyserSession session;
|
||||
|
|
|
@ -26,11 +26,11 @@
|
|||
package org.geysermc.geyser.erosion;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.erosion.netty.NettyPacketSender;
|
||||
import org.geysermc.erosion.packet.ErosionPacketHandler;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundHandshakePacket;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class GeyserboundHandshakePacketHandler extends AbstractGeyserboundPacketHandler {
|
||||
|
||||
|
@ -66,7 +66,7 @@ public final class GeyserboundHandshakePacketHandler extends AbstractGeyserbound
|
|||
}
|
||||
|
||||
@Override
|
||||
public ErosionPacketHandler setChannel(Channel channel) {
|
||||
public @Nullable ErosionPacketHandler setChannel(Channel channel) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import io.netty.channel.ChannelInitializer;
|
|||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.epoll.EpollDomainSocketChannel;
|
||||
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.erosion.netty.impl.AbstractUnixSocketListener;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundPacketHandler;
|
||||
|
||||
|
@ -49,9 +50,9 @@ public final class UnixSocketClientListener extends AbstractUnixSocketListener {
|
|||
initializeEventLoopGroup();
|
||||
(new Bootstrap()
|
||||
.channel(EpollDomainSocketChannel.class)
|
||||
.handler(new ChannelInitializer<Channel>() {
|
||||
.handler(new ChannelInitializer<>() {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) {
|
||||
protected void initChannel(@NonNull Channel ch) {
|
||||
initPipeline(ch, handler);
|
||||
}
|
||||
})
|
||||
|
|
|
@ -34,7 +34,6 @@ import org.geysermc.event.subscribe.Subscribe;
|
|||
import org.geysermc.geyser.api.event.EventBus;
|
||||
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||
import org.geysermc.geyser.api.event.EventSubscriber;
|
||||
import org.geysermc.geyser.api.extension.Extension;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
|
|
@ -31,7 +31,6 @@ import org.geysermc.event.PostOrder;
|
|||
import org.geysermc.event.subscribe.impl.OwnedSubscriberImpl;
|
||||
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||
import org.geysermc.geyser.api.event.ExtensionEventSubscriber;
|
||||
import org.geysermc.geyser.api.extension.Extension;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.event.type;
|
||||
|
||||
import org.checkerframework.checker.index.qual.NonNegative;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.protocol.bedrock.BedrockPong;
|
||||
import org.geysermc.geyser.api.event.connection.GeyserBedrockPingEvent;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Objects;
|
||||
|
||||
public class GeyserBedrockPingEventImpl implements GeyserBedrockPingEvent {
|
||||
private final InetSocketAddress address;
|
||||
private final BedrockPong pong;
|
||||
|
||||
public GeyserBedrockPingEventImpl(BedrockPong pong, InetSocketAddress address) {
|
||||
this.address = address;
|
||||
this.pong = pong;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void primaryMotd(@NonNull String primary) {
|
||||
pong.motd(Objects.requireNonNull(primary, "Primary MOTD cannot be null"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void secondaryMotd(@NonNull String secondary) {
|
||||
pong.subMotd(Objects.requireNonNull(secondary, "Secondary MOTD cannot be null"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerCount(int count) {
|
||||
if (count < 0) throw new IllegalArgumentException("Player count cannot be below 0");
|
||||
pong.playerCount(count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maxPlayerCount(int max) {
|
||||
if (max < 1) throw new IllegalArgumentException("Max player count cannot be below 1");
|
||||
pong.maximumPlayerCount(max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String primaryMotd() {
|
||||
return pong.motd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String secondaryMotd() {
|
||||
return pong.subMotd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNegative int playerCount() {
|
||||
return pong.playerCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int maxPlayerCount() {
|
||||
return pong.maximumPlayerCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull InetSocketAddress address() {
|
||||
return address;
|
||||
}
|
||||
}
|
|
@ -51,7 +51,7 @@ public abstract class GeyserDefineCustomItemsEventImpl implements GeyserDefineCu
|
|||
* @return a multimap of all the already registered custom items
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Collection<CustomItemData>> getExistingCustomItems() {
|
||||
public @NonNull Map<String, Collection<CustomItemData>> getExistingCustomItems() {
|
||||
return Collections.unmodifiableMap(this.customItems.asMap());
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ public abstract class GeyserDefineCustomItemsEventImpl implements GeyserDefineCu
|
|||
* @return the list of the already registered non-vanilla custom items
|
||||
*/
|
||||
@Override
|
||||
public List<NonVanillaCustomItemData> getExistingNonVanillaCustomItems() {
|
||||
public @NonNull List<NonVanillaCustomItemData> getExistingNonVanillaCustomItems() {
|
||||
return Collections.unmodifiableList(this.nonVanillaCustomItems);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.event.type;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.event.bedrock.SessionLoadResourcePacksEvent;
|
||||
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksEvent {
|
||||
|
||||
private final Map<String, ResourcePack> packs;
|
||||
|
||||
public SessionLoadResourcePacksEventImpl(GeyserSession session, Map<String, ResourcePack> packMap) {
|
||||
super(session);
|
||||
this.packs = packMap;
|
||||
}
|
||||
|
||||
public @NonNull Map<String, ResourcePack> getPacks() {
|
||||
return packs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull List<ResourcePack> resourcePacks() {
|
||||
return List.copyOf(packs.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean register(@NonNull ResourcePack resourcePack) {
|
||||
String packID = resourcePack.manifest().header().uuid().toString();
|
||||
if (packs.containsValue(resourcePack) || packs.containsKey(packID)) {
|
||||
return false;
|
||||
}
|
||||
packs.put(resourcePack.manifest().header().uuid().toString(), resourcePack);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregister(@NonNull UUID uuid) {
|
||||
return packs.remove(uuid.toString()) != null;
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.extension;
|
|||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.extension.Extension;
|
||||
import org.geysermc.geyser.api.extension.ExtensionDescription;
|
||||
import org.geysermc.geyser.api.extension.exception.InvalidExtensionException;
|
||||
|
@ -39,14 +40,17 @@ import java.nio.file.Path;
|
|||
|
||||
public class GeyserExtensionClassLoader extends URLClassLoader {
|
||||
private final GeyserExtensionLoader loader;
|
||||
private final ExtensionDescription description;
|
||||
private final Object2ObjectMap<String, Class<?>> classes = new Object2ObjectOpenHashMap<>();
|
||||
private boolean warnedForExternalClassAccess;
|
||||
|
||||
public GeyserExtensionClassLoader(GeyserExtensionLoader loader, ClassLoader parent, Path path) throws MalformedURLException {
|
||||
public GeyserExtensionClassLoader(GeyserExtensionLoader loader, ClassLoader parent, Path path, ExtensionDescription description) throws MalformedURLException {
|
||||
super(new URL[] { path.toUri().toURL() }, parent);
|
||||
this.loader = loader;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Extension load(ExtensionDescription description) throws InvalidExtensionException {
|
||||
public Extension load() throws InvalidExtensionException {
|
||||
try {
|
||||
Class<?> jarClass;
|
||||
try {
|
||||
|
@ -76,22 +80,32 @@ public class GeyserExtensionClassLoader extends URLClassLoader {
|
|||
}
|
||||
|
||||
protected Class<?> findClass(String name, boolean checkGlobal) throws ClassNotFoundException {
|
||||
if (name.startsWith("org.geysermc.geyser.") || name.startsWith("net.minecraft.")) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
|
||||
Class<?> result = this.classes.get(name);
|
||||
if (result == null) {
|
||||
result = super.findClass(name);
|
||||
if (result == null && checkGlobal) {
|
||||
result = this.loader.classByName(name);
|
||||
// Try to find class in current extension
|
||||
try {
|
||||
result = super.findClass(name);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
// If class is not found in current extension, check in the global class loader
|
||||
// This is used for classes that are not in the extension, but are in other extensions
|
||||
if (checkGlobal) {
|
||||
if (!warnedForExternalClassAccess) {
|
||||
GeyserImpl.getInstance().getLogger().warning("Extension " + this.description.name() + " loads class " + name + " from an external source. " +
|
||||
"This can change at any time and break the extension, additionally to potentially causing unexpected behaviour!");
|
||||
warnedForExternalClassAccess = true;
|
||||
}
|
||||
result = this.loader.classByName(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (result != null) {
|
||||
// If class is found, cache it
|
||||
this.loader.setClass(name, result);
|
||||
this.classes.put(name, result);
|
||||
} else {
|
||||
// If class is not found, throw exception
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
|
||||
this.classes.put(name, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||
import org.geysermc.geyser.api.extension.ExtensionDescription;
|
||||
import org.geysermc.geyser.api.extension.exception.InvalidDescriptionException;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.CustomClassLoaderConstructor;
|
||||
|
||||
|
@ -48,7 +49,7 @@ public record GeyserExtensionDescription(@NonNull String id,
|
|||
@NonNull String version,
|
||||
@NonNull List<String> authors) implements ExtensionDescription {
|
||||
|
||||
private static final Yaml YAML = new Yaml(new CustomClassLoaderConstructor(Source.class.getClassLoader()));
|
||||
private static final Yaml YAML = new Yaml(new CustomClassLoaderConstructor(Source.class.getClassLoader(), new LoaderOptions()));
|
||||
|
||||
public static final Pattern ID_PATTERN = Pattern.compile("[a-z][a-z0-9-_]{0,63}");
|
||||
public static final Pattern NAME_PATTERN = Pattern.compile("^[A-Za-z_.-]+$");
|
||||
|
|
|
@ -43,9 +43,9 @@ import java.io.Reader;
|
|||
import java.nio.file.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class GeyserExtensionLoader extends ExtensionLoader {
|
||||
|
@ -56,7 +56,7 @@ public class GeyserExtensionLoader extends ExtensionLoader {
|
|||
private final Map<Extension, GeyserExtensionContainer> extensionContainers = new HashMap<>();
|
||||
private final Path extensionsDirectory = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("extensions");
|
||||
|
||||
public GeyserExtensionContainer loadExtension(Path path, GeyserExtensionDescription description) throws InvalidExtensionException {
|
||||
public GeyserExtensionContainer loadExtension(Path path, GeyserExtensionDescription description) throws Throwable {
|
||||
if (path == null) {
|
||||
throw new InvalidExtensionException("Path is null");
|
||||
}
|
||||
|
@ -66,26 +66,44 @@ public class GeyserExtensionLoader extends ExtensionLoader {
|
|||
}
|
||||
|
||||
Path parentFile = path.getParent();
|
||||
Path dataFolder = parentFile.resolve(description.name());
|
||||
|
||||
// Extension folders used to be created by name; this changes them to the ID
|
||||
Path oldDataFolder = parentFile.resolve(description.name());
|
||||
Path dataFolder = parentFile.resolve(description.id());
|
||||
|
||||
if (Files.exists(oldDataFolder) && Files.isDirectory(oldDataFolder) && !oldDataFolder.equals(dataFolder)) {
|
||||
try {
|
||||
Files.move(oldDataFolder, dataFolder, StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
throw new InvalidExtensionException("Failed to move data folder for extension " + description.name(), e);
|
||||
}
|
||||
}
|
||||
|
||||
if (Files.exists(dataFolder) && !Files.isDirectory(dataFolder)) {
|
||||
throw new InvalidExtensionException("The folder " + dataFolder + " is not a directory and is the data folder for the extension " + description.name() + "!");
|
||||
}
|
||||
|
||||
final GeyserExtensionClassLoader loader;
|
||||
try {
|
||||
loader = new GeyserExtensionClassLoader(this, getClass().getClassLoader(), path);
|
||||
loader = new GeyserExtensionClassLoader(this, getClass().getClassLoader(), path, description);
|
||||
} catch (Throwable e) {
|
||||
throw new InvalidExtensionException(e);
|
||||
}
|
||||
|
||||
this.classLoaders.put(description.name(), loader);
|
||||
this.classLoaders.put(description.id(), loader);
|
||||
|
||||
final Extension extension = loader.load(description);
|
||||
return this.setup(extension, description, dataFolder, new GeyserExtensionEventBus(GeyserImpl.getInstance().eventBus(), extension));
|
||||
try {
|
||||
final Extension extension = loader.load();
|
||||
return this.setup(extension, description, dataFolder, new GeyserExtensionEventBus(GeyserImpl.getInstance().eventBus(), extension));
|
||||
} catch (Throwable e) {
|
||||
// if the extension failed to load, remove its classloader and close it.
|
||||
this.classLoaders.remove(description.id()).close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private GeyserExtensionContainer setup(Extension extension, GeyserExtensionDescription description, Path dataFolder, ExtensionEventBus eventBus) {
|
||||
GeyserExtensionLogger logger = new GeyserExtensionLogger(GeyserImpl.getInstance().getLogger(), description.name());
|
||||
GeyserExtensionLogger logger = new GeyserExtensionLogger(GeyserImpl.getInstance().getLogger(), description.id());
|
||||
return new GeyserExtensionContainer(extension, dataFolder, description, this, logger, eventBus);
|
||||
}
|
||||
|
||||
|
@ -136,46 +154,47 @@ public class GeyserExtensionLoader extends ExtensionLoader {
|
|||
Map<String, GeyserExtensionContainer> loadedExtensions = new LinkedHashMap<>();
|
||||
|
||||
Pattern[] extensionFilters = this.extensionFilters();
|
||||
try (Stream<Path> entries = Files.walk(extensionsDirectory)) {
|
||||
entries.forEach(path -> {
|
||||
if (Files.isDirectory(path)) {
|
||||
List<Path> extensionPaths = Files.walk(extensionsDirectory).toList();
|
||||
extensionPaths.forEach(path -> {
|
||||
if (Files.isDirectory(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
for (Pattern filter : extensionFilters) {
|
||||
if (!filter.matcher(path.getFileName().toString()).matches()) {
|
||||
return;
|
||||
}
|
||||
// Completely different API version
|
||||
if (description.majorApiVersion() != Geyser.api().majorApiVersion()) {
|
||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, description.apiVersion()));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
GeyserExtensionDescription description = this.extensionDescription(path);
|
||||
|
||||
String name = description.name();
|
||||
if (extensions.containsKey(name) || extensionManager.extension(name) != null) {
|
||||
GeyserImpl.getInstance().getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.extensions.load.duplicate", name, path.toString()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Completely different API version
|
||||
if (description.majorApiVersion() != Geyser.api().majorApiVersion()) {
|
||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, description.apiVersion()));
|
||||
return;
|
||||
}
|
||||
|
||||
// If the extension requires new API features, being backwards compatible
|
||||
if (description.minorApiVersion() > Geyser.api().minorApiVersion()) {
|
||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_api_version", name, description.apiVersion()));
|
||||
return;
|
||||
}
|
||||
|
||||
extensions.put(name, path);
|
||||
loadedExtensions.put(name, this.loadExtension(path, description));
|
||||
} catch (Exception e) {
|
||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_with_name", path.getFileName(), path.toAbsolutePath()), e);
|
||||
// If the extension requires new API features, being backwards compatible
|
||||
if (description.minorApiVersion() > Geyser.api().minorApiVersion()) {
|
||||
GeyserImpl.getInstance().getLogger().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);
|
||||
} catch (Throwable e) {
|
||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.extensions.load.failed_with_name", path.getFileName(), path.toAbsolutePath()), e);
|
||||
}
|
||||
});
|
||||
|
||||
for (GeyserExtensionContainer container : loadedExtensions.values()) {
|
||||
this.extensionContainers.put(container.extension(), container);
|
||||
|
|
|
@ -52,8 +52,8 @@ public class GeyserExtensionManager extends ExtensionManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Extension extension(@NonNull String name) {
|
||||
return this.extensions.get(name);
|
||||
public Extension extension(@NonNull String id) {
|
||||
return this.extensions.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -83,7 +83,7 @@ public class GeyserExtensionManager extends ExtensionManager {
|
|||
if (!extension.isEnabled()) {
|
||||
extension.setEnabled(true);
|
||||
GeyserImpl.getInstance().eventBus().register(extension, extension);
|
||||
GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.enable.success", extension.description().name()));
|
||||
GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.enable.success", extension.name()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@ public class GeyserExtensionManager extends ExtensionManager {
|
|||
GeyserImpl.getInstance().eventBus().unregisterAll(extension);
|
||||
|
||||
extension.setEnabled(false);
|
||||
GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.disable.success", extension.description().name()));
|
||||
GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.disable.success", extension.name()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,6 +121,6 @@ public class GeyserExtensionManager extends ExtensionManager {
|
|||
|
||||
@Override
|
||||
public void register(@NonNull Extension extension) {
|
||||
this.extensions.put(extension.name(), extension);
|
||||
this.extensions.put(extension.description().id(), extension);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.extension.event;
|
|||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.event.Event;
|
||||
import org.geysermc.event.FireResult;
|
||||
import org.geysermc.event.PostOrder;
|
||||
import org.geysermc.event.subscribe.Subscriber;
|
||||
import org.geysermc.geyser.api.event.EventBus;
|
||||
|
@ -47,10 +48,15 @@ public record GeyserExtensionEventBus(EventBus<EventRegistrar> eventBus, Extensi
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean fire(@NonNull Event event) {
|
||||
public FireResult fire(@NonNull Event event) {
|
||||
return eventBus.fire(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FireResult fireSilently(@NonNull Event event) {
|
||||
return eventBus.fireSilently(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull <T extends Event> Set<? extends EventSubscriber<EventRegistrar, T>> subscribers(@NonNull Class<T> eventClass) {
|
||||
return eventBus.subscribers(eventClass);
|
||||
|
|
|
@ -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
|
||||
|
@ -23,16 +23,9 @@
|
|||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.translator.collision;
|
||||
package org.geysermc.geyser.impl;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.geysermc.geyser.api.util.MinecraftVersion;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@CollisionRemapper(regex = "^spawner$")
|
||||
public class SpawnerCollision extends SolidCollision {
|
||||
public SpawnerCollision(String params) {
|
||||
super(params);
|
||||
// Increase pushAwayTolerance to work around https://bugs.mojang.com/browse/MCPE-41996
|
||||
pushAwayTolerance = 0.0002;
|
||||
}
|
||||
public record MinecraftVersionImpl(String versionString, int protocolVersion) implements MinecraftVersion {
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.impl.camera;
|
||||
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraAudioListener;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraPreset;
|
||||
import org.cloudburstmc.protocol.common.DefinitionRegistry;
|
||||
import org.cloudburstmc.protocol.common.NamedDefinition;
|
||||
import org.cloudburstmc.protocol.common.SimpleDefinitionRegistry;
|
||||
import org.cloudburstmc.protocol.common.util.OptionalBoolean;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraPerspective;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CameraDefinitions {
|
||||
|
||||
public static final DefinitionRegistry<NamedDefinition> CAMERA_DEFINITIONS;
|
||||
|
||||
public static final List<CameraPreset> CAMERA_PRESETS;
|
||||
|
||||
static {
|
||||
CAMERA_PRESETS = List.of(
|
||||
new CameraPreset(CameraPerspective.FIRST_PERSON.id(), "", null, null, null, null, OptionalBoolean.empty()),
|
||||
new CameraPreset(CameraPerspective.FREE.id(), "", null, null, null, null, OptionalBoolean.empty()),
|
||||
new CameraPreset(CameraPerspective.THIRD_PERSON.id(), "", null, null, null, null, OptionalBoolean.empty()),
|
||||
new CameraPreset(CameraPerspective.THIRD_PERSON_FRONT.id(), "", null, null, null, null, OptionalBoolean.empty()),
|
||||
new CameraPreset("geyser:free_audio", "minecraft:free", null, null, null, CameraAudioListener.PLAYER, OptionalBoolean.of(false)),
|
||||
new CameraPreset("geyser:free_effects", "minecraft:free", null, null, null, CameraAudioListener.CAMERA, OptionalBoolean.of(true)),
|
||||
new CameraPreset("geyser:free_audio_effects", "minecraft:free", null, null, null, CameraAudioListener.PLAYER, OptionalBoolean.of(true)));
|
||||
|
||||
SimpleDefinitionRegistry.Builder<NamedDefinition> builder = SimpleDefinitionRegistry.builder();
|
||||
for (int i = 0; i < CAMERA_PRESETS.size(); i++) {
|
||||
builder.add(CameraDefinition.of(CAMERA_PRESETS.get(i).getIdentifier(), i));
|
||||
}
|
||||
CAMERA_DEFINITIONS = builder.build();
|
||||
}
|
||||
|
||||
public static NamedDefinition getById(int id) {
|
||||
return CAMERA_DEFINITIONS.getDefinition(id);
|
||||
}
|
||||
|
||||
public static NamedDefinition getByFunctionality(boolean audio, boolean effects) {
|
||||
if (!audio && !effects) {
|
||||
return getById(1); // FREE
|
||||
}
|
||||
if (audio) {
|
||||
if (effects) {
|
||||
return getById(6); // FREE_AUDIO_EFFECTS
|
||||
} else {
|
||||
return getById(4); // FREE_AUDIO
|
||||
}
|
||||
} else {
|
||||
return getById(5); // FREE_EFFECTS
|
||||
}
|
||||
}
|
||||
|
||||
public record CameraDefinition(String identifier, int runtimeId) implements NamedDefinition {
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRuntimeId() {
|
||||
return runtimeId;
|
||||
}
|
||||
|
||||
public static CameraDefinition of(String identifier, int runtimeId) {
|
||||
return new CameraDefinition(identifier, runtimeId);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.impl.camera;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector2f;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.CameraShakeAction;
|
||||
import org.cloudburstmc.protocol.bedrock.data.CameraShakeType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraEase;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraFadeInstruction;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraSetInstruction;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.CameraInstructionPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.CameraShakePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayerFogPacket;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraEaseType;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraData;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraFade;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraPerspective;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraPosition;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class GeyserCameraData implements CameraData {
|
||||
|
||||
private final GeyserSession session;
|
||||
|
||||
@Getter
|
||||
private CameraPerspective cameraPerspective;
|
||||
|
||||
/**
|
||||
* All fog effects that are currently applied to the client.
|
||||
*/
|
||||
private final Set<String> appliedFog = new HashSet<>();
|
||||
|
||||
private final Set<UUID> cameraLockOwners = new HashSet<>();
|
||||
|
||||
public GeyserCameraData(GeyserSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCameraInstructions() {
|
||||
this.cameraPerspective = null;
|
||||
CameraInstructionPacket packet = new CameraInstructionPacket();
|
||||
packet.setClear(true);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forceCameraPerspective(@NonNull CameraPerspective perspective) {
|
||||
Objects.requireNonNull(perspective, "perspective cannot be null!");
|
||||
|
||||
if (perspective == cameraPerspective) {
|
||||
return; // nothing to do
|
||||
}
|
||||
|
||||
this.cameraPerspective = perspective;
|
||||
CameraInstructionPacket packet = new CameraInstructionPacket();
|
||||
CameraSetInstruction setInstruction = new CameraSetInstruction();
|
||||
|
||||
if (perspective == CameraPerspective.FREE) {
|
||||
throw new IllegalArgumentException("Cannot force a stationary camera (CameraPerspective#FREE) on the player!" +
|
||||
"Send a CameraPosition with an exact position instead");
|
||||
}
|
||||
|
||||
setInstruction.setPreset(CameraDefinitions.getById(perspective.ordinal()));
|
||||
packet.setSetInstruction(setInstruction);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable CameraPerspective forcedCameraPerspective() {
|
||||
return this.cameraPerspective;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendCameraFade(@NonNull CameraFade fade) {
|
||||
Objects.requireNonNull(fade, "fade cannot be null!");
|
||||
CameraFadeInstruction fadeInstruction = new CameraFadeInstruction();
|
||||
fadeInstruction.setColor(fade.color());
|
||||
fadeInstruction.setTimeData(
|
||||
new CameraFadeInstruction.TimeData(
|
||||
fade.fadeInSeconds(),
|
||||
fade.fadeHoldSeconds(),
|
||||
fade.fadeOutSeconds()
|
||||
)
|
||||
);
|
||||
|
||||
CameraInstructionPacket packet = new CameraInstructionPacket();
|
||||
packet.setFadeInstruction(fadeInstruction);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendCameraPosition(@NonNull CameraPosition movement) {
|
||||
Objects.requireNonNull(movement, "movement cannot be null!");
|
||||
this.cameraPerspective = CameraPerspective.FREE; // Movements only work with the free preset
|
||||
CameraSetInstruction setInstruction = new CameraSetInstruction();
|
||||
|
||||
CameraEaseType easeType = movement.easeType();
|
||||
if (easeType != null) {
|
||||
setInstruction.setEase(new CameraSetInstruction.EaseData(
|
||||
CameraEase.fromName(easeType.id()),
|
||||
movement.easeSeconds()
|
||||
));
|
||||
}
|
||||
|
||||
Vector3f facingPosition = movement.facingPosition();
|
||||
if (facingPosition != null) {
|
||||
setInstruction.setFacing(facingPosition);
|
||||
}
|
||||
|
||||
setInstruction.setPos(movement.position());
|
||||
setInstruction.setRot(Vector2f.from(movement.rotationX(), movement.rotationY()));
|
||||
setInstruction.setPreset(CameraDefinitions.getByFunctionality(movement.playerPositionForAudio(), movement.renderPlayerEffects()));
|
||||
|
||||
CameraInstructionPacket packet = new CameraInstructionPacket();
|
||||
packet.setSetInstruction(setInstruction);
|
||||
|
||||
// If present, also send the fade
|
||||
CameraFade fade = movement.cameraFade();
|
||||
if (fade != null) {
|
||||
CameraFadeInstruction fadeInstruction = new CameraFadeInstruction();
|
||||
fadeInstruction.setColor(fade.color());
|
||||
fadeInstruction.setTimeData(
|
||||
new CameraFadeInstruction.TimeData(
|
||||
fade.fadeInSeconds(),
|
||||
fade.fadeHoldSeconds(),
|
||||
fade.fadeOutSeconds()
|
||||
)
|
||||
);
|
||||
packet.setFadeInstruction(fadeInstruction);
|
||||
}
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shakeCamera(float intensity, float duration, @NonNull CameraShake type) {
|
||||
Objects.requireNonNull(type, "camera shake type must be non null!");
|
||||
CameraShakePacket packet = new CameraShakePacket();
|
||||
packet.setIntensity(intensity);
|
||||
packet.setDuration(duration);
|
||||
packet.setShakeType(type == CameraShake.POSITIONAL ? CameraShakeType.POSITIONAL : CameraShakeType.ROTATIONAL);
|
||||
packet.setShakeAction(CameraShakeAction.ADD);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopCameraShake() {
|
||||
CameraShakePacket packet = new CameraShakePacket();
|
||||
// CameraShakeAction.STOP removes all types regardless of the given type, but regardless it can't be null
|
||||
packet.setShakeType(CameraShakeType.POSITIONAL);
|
||||
packet.setShakeAction(CameraShakeAction.STOP);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFog(String... fogNameSpaces) {
|
||||
Collections.addAll(this.appliedFog, fogNameSpaces);
|
||||
|
||||
PlayerFogPacket packet = new PlayerFogPacket();
|
||||
packet.getFogStack().addAll(this.appliedFog);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFog(String... fogNameSpaces) {
|
||||
if (fogNameSpaces.length == 0) {
|
||||
this.appliedFog.clear();
|
||||
} else {
|
||||
for (String id : fogNameSpaces) {
|
||||
this.appliedFog.remove(id);
|
||||
}
|
||||
}
|
||||
PlayerFogPacket packet = new PlayerFogPacket();
|
||||
packet.getFogStack().addAll(this.appliedFog);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Set<String> fogEffects() {
|
||||
// Use a copy so that sendFog/removeFog can be called while iterating the returned set (avoid CME)
|
||||
return Set.copyOf(this.appliedFog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean lockCamera(boolean lock, @NonNull UUID owner) {
|
||||
Objects.requireNonNull(owner, "owner cannot be null!");
|
||||
if (lock) {
|
||||
this.cameraLockOwners.add(owner);
|
||||
} else {
|
||||
this.cameraLockOwners.remove(owner);
|
||||
}
|
||||
|
||||
session.lockInputs(isCameraLocked(), session.entities().isMovementLocked());
|
||||
return isCameraLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCameraLocked() {
|
||||
return !this.cameraLockOwners.isEmpty();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.impl.camera;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.common.value.qual.IntRange;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraFade;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.Objects;
|
||||
|
||||
public record GeyserCameraFade(
|
||||
Color color,
|
||||
float fadeInSeconds,
|
||||
float fadeHoldSeconds,
|
||||
float fadeOutSeconds
|
||||
|
||||
) implements CameraFade {
|
||||
public static class Builder implements CameraFade.Builder {
|
||||
private Color color;
|
||||
private float fadeInSeconds;
|
||||
private float fadeHoldSeconds;
|
||||
private float fadeOutSeconds;
|
||||
|
||||
@Override
|
||||
public CameraFade.Builder color(@NonNull Color color) {
|
||||
Objects.requireNonNull(color, "color cannot be null!");
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraFade.Builder fadeInSeconds(@IntRange(from = 0, to = 10) float fadeInSeconds) {
|
||||
if (fadeInSeconds < 0f) {
|
||||
throw new IllegalArgumentException("Fade in seconds must be at least 0 seconds");
|
||||
}
|
||||
|
||||
if (fadeInSeconds > 10f) {
|
||||
throw new IllegalArgumentException("Fade in seconds must be at most 10 seconds");
|
||||
}
|
||||
this.fadeInSeconds = fadeInSeconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraFade.Builder fadeHoldSeconds(@IntRange(from = 0, to = 10) float fadeHoldSeconds) {
|
||||
if (fadeHoldSeconds < 0f) {
|
||||
throw new IllegalArgumentException("Fade hold seconds must be at least 0 seconds");
|
||||
}
|
||||
|
||||
if (fadeHoldSeconds > 10f) {
|
||||
throw new IllegalArgumentException("Fade hold seconds must be at most 10 seconds");
|
||||
}
|
||||
this.fadeHoldSeconds = fadeHoldSeconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraFade.Builder fadeOutSeconds(@IntRange(from = 0, to = 10) float fadeOutSeconds) {
|
||||
if (fadeOutSeconds < 0f) {
|
||||
throw new IllegalArgumentException("Fade out seconds must be at least 0 seconds");
|
||||
}
|
||||
|
||||
if (fadeOutSeconds > 10f) {
|
||||
throw new IllegalArgumentException("Fade out seconds must be at most 10 seconds");
|
||||
}
|
||||
this.fadeOutSeconds = fadeOutSeconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraFade build() {
|
||||
Objects.requireNonNull(color, "color must be non null!");
|
||||
if (fadeInSeconds + fadeHoldSeconds + fadeOutSeconds < 0.5f) {
|
||||
throw new IllegalArgumentException("Total fade time (in, hold, out) must be at least 0.5 seconds");
|
||||
}
|
||||
|
||||
return new GeyserCameraFade(color, fadeInSeconds, fadeHoldSeconds, fadeOutSeconds);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.impl.camera;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.checkerframework.common.value.qual.IntRange;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraEaseType;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraFade;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraPosition;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record GeyserCameraPosition(CameraFade cameraFade,
|
||||
boolean renderPlayerEffects,
|
||||
boolean playerPositionForAudio,
|
||||
CameraEaseType easeType,
|
||||
float easeSeconds,
|
||||
Vector3f position,
|
||||
@IntRange(from = -90, to = 90) int rotationX,
|
||||
int rotationY,
|
||||
Vector3f facingPosition
|
||||
) implements CameraPosition {
|
||||
|
||||
public static class Builder implements CameraPosition.Builder {
|
||||
private CameraFade cameraFade;
|
||||
private boolean renderPlayerEffects;
|
||||
private boolean playerPositionForAudio;
|
||||
private CameraEaseType easeType;
|
||||
private float easeSeconds;
|
||||
private Vector3f position;
|
||||
private @IntRange(from = -90, to = 90) int rotationX;
|
||||
private int rotationY;
|
||||
private Vector3f facingPosition;
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder cameraFade(@Nullable CameraFade cameraFade) {
|
||||
this.cameraFade = cameraFade;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder renderPlayerEffects(boolean renderPlayerEffects) {
|
||||
this.renderPlayerEffects = renderPlayerEffects;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder playerPositionForAudio(boolean playerPositionForAudio) {
|
||||
this.playerPositionForAudio = playerPositionForAudio;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder easeType(@Nullable CameraEaseType easeType) {
|
||||
this.easeType = easeType;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder easeSeconds(float easeSeconds) {
|
||||
if (easeSeconds < 0) {
|
||||
throw new IllegalArgumentException("Camera ease duration cannot be negative!");
|
||||
}
|
||||
this.easeSeconds = easeSeconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder position(@NonNull Vector3f position) {
|
||||
Objects.requireNonNull(position, "camera position cannot be null!");
|
||||
this.position = position;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder rotationX(int rotationX) {
|
||||
if (rotationX < -90 || rotationX > 90) {
|
||||
throw new IllegalArgumentException("x-axis rotation needs to be between -90 and 90 degrees.");
|
||||
}
|
||||
this.rotationX = rotationX;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder rotationY(int rotationY) {
|
||||
this.rotationY = rotationY;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder facingPosition(@Nullable Vector3f facingPosition) {
|
||||
this.facingPosition = facingPosition;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition build() {
|
||||
if (easeSeconds > 0 && easeType == null) {
|
||||
throw new IllegalArgumentException("Camera ease type cannot be null if ease duration is greater than 0");
|
||||
}
|
||||
|
||||
Objects.requireNonNull(position, "camera position must be non null!");
|
||||
return new GeyserCameraPosition(cameraFade, renderPlayerEffects, playerPositionForAudio, easeType, easeSeconds, position, rotationX, rotationY, facingPosition);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,12 +29,11 @@ import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType;
|
|||
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundRenameItemPacket;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
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 javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Used to determine if rename packets should be sent and stores
|
||||
* the expected level cost for AnvilInventoryUpdater
|
||||
|
@ -75,21 +74,21 @@ public class AnvilContainer extends Container {
|
|||
|
||||
String originalName = ItemUtils.getCustomName(getInput().getNbt());
|
||||
|
||||
String plainOriginalName = MessageTranslator.convertToPlainText(originalName, session.locale());
|
||||
String plainOriginalName = MessageTranslator.convertToPlainTextLenient(originalName, session.locale());
|
||||
String plainNewName = MessageTranslator.convertToPlainText(rename);
|
||||
if (!plainOriginalName.equals(plainNewName)) {
|
||||
// Strip out formatting since Java Edition does not allow it
|
||||
correctRename = plainNewName;
|
||||
// Java Edition sends a packet every time an item is renamed even slightly in GUI. Fortunately, this works out for us now
|
||||
ServerboundRenameItemPacket renameItemPacket = new ServerboundRenameItemPacket(plainNewName);
|
||||
session.sendDownstreamPacket(renameItemPacket);
|
||||
session.sendDownstreamGamePacket(renameItemPacket);
|
||||
} else {
|
||||
// Restore formatting for item since we're not renaming
|
||||
correctRename = MessageTranslator.convertMessageLenient(originalName);
|
||||
// Java Edition sends the original custom name when not renaming,
|
||||
// if there isn't a custom name an empty string is sent
|
||||
ServerboundRenameItemPacket renameItemPacket = new ServerboundRenameItemPacket(plainOriginalName);
|
||||
session.sendDownstreamPacket(renameItemPacket);
|
||||
session.sendDownstreamGamePacket(renameItemPacket);
|
||||
}
|
||||
|
||||
useJavaLevelCost = false;
|
||||
|
|
|
@ -27,12 +27,11 @@ package org.geysermc.geyser.inventory;
|
|||
|
||||
import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType;
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Combination of {@link Inventory} and {@link PlayerInventory}
|
||||
*/
|
||||
|
@ -67,7 +66,7 @@ public class Container extends Inventory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int slot, @Nonnull GeyserItemStack newItem, GeyserSession session) {
|
||||
public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) {
|
||||
if (slot < this.size) {
|
||||
super.setItem(slot, newItem, session);
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.inventory;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
|
||||
@Getter
|
||||
public class CrafterContainer extends Container {
|
||||
|
||||
@Setter
|
||||
private boolean triggered = false;
|
||||
|
||||
/**
|
||||
* Bedrock Edition bitmask of the *disabled* slots.
|
||||
* Disabled slots are 1, enabled slots are 0 - same as Java Edition
|
||||
*/
|
||||
private short disabledSlotsMask = 0;
|
||||
|
||||
public CrafterContainer(String title, int id, int size, ContainerType containerType, PlayerInventory playerInventory) {
|
||||
super(title, id, size, containerType, playerInventory);
|
||||
}
|
||||
|
||||
public void setSlot(int slot, boolean enabled) {
|
||||
if (slot < 0 || slot > 8) {
|
||||
GeyserImpl.getInstance().getLogger().warning("Crafter slot out of bounds: " + slot);
|
||||
return;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
disabledSlotsMask = (short) (disabledSlotsMask & ~(1 << slot));
|
||||
} else {
|
||||
disabledSlotsMask = (short) (disabledSlotsMask | (1 << slot));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,7 +33,7 @@ import org.geysermc.geyser.translator.inventory.Generic3X3InventoryTranslator;
|
|||
public class Generic3X3Container extends Container {
|
||||
/**
|
||||
* Whether we need to set the container type as {@link org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType#DROPPER}.
|
||||
*
|
||||
* <p>
|
||||
* Used at {@link Generic3X3InventoryTranslator#openInventory(GeyserSession, Inventory)}
|
||||
*/
|
||||
@Getter
|
||||
|
|
|
@ -40,7 +40,7 @@ import java.util.List;
|
|||
public class GeyserEnchantOption {
|
||||
private static final List<EnchantData> EMPTY = Collections.emptyList();
|
||||
/**
|
||||
* This: https://cdn.discordapp.com/attachments/613168850925649981/791030657169227816/unknown.png
|
||||
* This <a href="https://cdn.discordapp.com/attachments/613168850925649981/791030657169227816/unknown.png">text</a>
|
||||
* is controlled by the server.
|
||||
* So, of course, we have to throw in some easter eggs. ;)
|
||||
*/
|
||||
|
|
|
@ -28,21 +28,22 @@ package org.geysermc.geyser.inventory;
|
|||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||
import lombok.Data;
|
||||
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.translator.inventory.item.ItemTranslator;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
@Data
|
||||
public class GeyserItemStack {
|
||||
public static final GeyserItemStack EMPTY = new GeyserItemStack(0, 0, null);
|
||||
public static final GeyserItemStack EMPTY = new GeyserItemStack(Items.AIR_ID, 0, null);
|
||||
|
||||
private final int javaId;
|
||||
private int amount;
|
||||
|
@ -64,7 +65,7 @@ public class GeyserItemStack {
|
|||
this.netId = netId;
|
||||
}
|
||||
|
||||
public static @Nonnull GeyserItemStack from(ItemStack itemStack) {
|
||||
public static @NonNull GeyserItemStack from(@Nullable ItemStack itemStack) {
|
||||
return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getNbt());
|
||||
}
|
||||
|
||||
|
@ -76,7 +77,7 @@ public class GeyserItemStack {
|
|||
return isEmpty() ? 0 : amount;
|
||||
}
|
||||
|
||||
public CompoundTag getNbt() {
|
||||
public @Nullable CompoundTag getNbt() {
|
||||
return isEmpty() ? null : nbt;
|
||||
}
|
||||
|
||||
|
@ -96,7 +97,7 @@ public class GeyserItemStack {
|
|||
return getItemStack(amount);
|
||||
}
|
||||
|
||||
public ItemStack getItemStack(int newAmount) {
|
||||
public @Nullable ItemStack getItemStack(int newAmount) {
|
||||
return isEmpty() ? null : new ItemStack(javaId, newAmount, nbt);
|
||||
}
|
||||
|
||||
|
@ -122,7 +123,7 @@ public class GeyserItemStack {
|
|||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return amount <= 0 || javaId == 0;
|
||||
return amount <= 0 || javaId == Items.AIR_ID;
|
||||
}
|
||||
|
||||
public GeyserItemStack copy() {
|
||||
|
|
|
@ -29,18 +29,18 @@ import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType;
|
|||
import com.github.steveice10.opennbt.tag.builtin.ByteTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.cloudburstmc.protocol.bedrock.data.defintions.ItemDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.item.ItemTranslator;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Arrays;
|
||||
|
||||
@ToString
|
||||
|
@ -126,7 +126,7 @@ public abstract class Inventory {
|
|||
|
||||
public abstract int getOffsetForHotbar(@Range(from = 0, to = 8) int slot);
|
||||
|
||||
public void setItem(int slot, @Nonnull GeyserItemStack newItem, GeyserSession session) {
|
||||
public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) {
|
||||
if (slot > this.size) {
|
||||
session.getGeyser().getLogger().debug("Tried to set an item out of bounds! " + this);
|
||||
return;
|
||||
|
|
|
@ -26,10 +26,13 @@
|
|||
package org.geysermc.geyser.inventory;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.java.inventory.JavaOpenBookTranslator;
|
||||
|
||||
public class LecternContainer extends Container {
|
||||
@Getter @Setter
|
||||
|
@ -39,7 +42,34 @@ public class LecternContainer extends Container {
|
|||
@Getter @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;
|
||||
|
||||
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.
|
||||
*/
|
||||
@Override
|
||||
public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession session) {
|
||||
if (isFakeLectern) {
|
||||
session.getPlayerInventory().setItem(slot, newItem, session);
|
||||
} else {
|
||||
super.setItem(slot, newItem, session);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used ONLY once to set the book of a fake lectern in {@link JavaOpenBookTranslator}.
|
||||
* See {@link LecternContainer#setItem(int, GeyserItemStack, GeyserSession)} as for why this is separate.
|
||||
*/
|
||||
public void setFakeLecternBook(GeyserItemStack book, GeyserSession session) {
|
||||
this.isFakeLectern = true;
|
||||
super.setItem(0, book, session);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import com.github.steveice10.mc.protocol.data.game.inventory.VillagerTrade;
|
|||
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.inventory.ClientboundMerchantOffersPacket;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
|
@ -40,6 +41,8 @@ public class MerchantContainer extends Container {
|
|||
private VillagerTrade[] villagerTrades;
|
||||
@Getter @Setter
|
||||
private ClientboundMerchantOffersPacket pendingOffersPacket;
|
||||
@Getter @Setter
|
||||
private int tradeExperience;
|
||||
|
||||
public MerchantContainer(String title, int id, int size, ContainerType containerType, PlayerInventory playerInventory) {
|
||||
super(title, id, size, containerType, playerInventory);
|
||||
|
@ -49,9 +52,10 @@ public class MerchantContainer extends Container {
|
|||
if (villagerTrades != null && slot >= 0 && slot < villagerTrades.length) {
|
||||
VillagerTrade trade = villagerTrades[slot];
|
||||
setItem(2, GeyserItemStack.from(trade.getOutput()), session);
|
||||
// TODO this logic doesn't add up
|
||||
session.getPlayerEntity().addFakeTradeExperience(trade.getXp());
|
||||
session.getPlayerEntity().updateBedrockMetadata();
|
||||
|
||||
tradeExperience += trade.getXp();
|
||||
villager.getDirtyMetadata().put(EntityDataTypes.TRADE_EXPERIENCE, tradeExperience);
|
||||
villager.updateBedrockMetadata();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,12 +28,11 @@ package org.geysermc.geyser.inventory;
|
|||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class PlayerInventory extends Inventory {
|
||||
/**
|
||||
* Stores the held item slot, starting at index 0.
|
||||
|
@ -44,7 +43,7 @@ public class PlayerInventory extends Inventory {
|
|||
private int heldItemSlot;
|
||||
|
||||
@Getter
|
||||
@Nonnull
|
||||
@NonNull
|
||||
private GeyserItemStack cursor = GeyserItemStack.EMPTY;
|
||||
|
||||
public PlayerInventory() {
|
||||
|
@ -57,12 +56,12 @@ public class PlayerInventory extends Inventory {
|
|||
return slot + 36;
|
||||
}
|
||||
|
||||
public void setCursor(@Nonnull GeyserItemStack newCursor, GeyserSession session) {
|
||||
public void setCursor(@NonNull GeyserItemStack newCursor, GeyserSession session) {
|
||||
updateItemNetId(cursor, newCursor, session);
|
||||
cursor = newCursor;
|
||||
}
|
||||
|
||||
public GeyserItemStack getItemInHand(@Nonnull Hand hand) {
|
||||
public GeyserItemStack getItemInHand(@NonNull Hand hand) {
|
||||
return hand == Hand.OFF_HAND ? getOffhand() : getItemInHand();
|
||||
}
|
||||
|
||||
|
@ -74,7 +73,7 @@ public class PlayerInventory extends Inventory {
|
|||
return items[36 + heldItemSlot];
|
||||
}
|
||||
|
||||
public void setItemInHand(@Nonnull GeyserItemStack item) {
|
||||
public void setItemInHand(@NonNull GeyserItemStack item) {
|
||||
if (36 + heldItemSlot > this.size) {
|
||||
GeyserImpl.getInstance().getLogger().debug("Held item slot was larger than expected!");
|
||||
return;
|
||||
|
|
|
@ -27,8 +27,8 @@ package org.geysermc.geyser.inventory;
|
|||
|
||||
import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
public class StonecutterContainer extends Container {
|
||||
|
|
|
@ -152,7 +152,7 @@ public final class ClickPlan {
|
|||
changedItems
|
||||
);
|
||||
|
||||
session.sendDownstreamPacket(clickPacket);
|
||||
session.sendDownstreamGamePacket(clickPacket);
|
||||
}
|
||||
|
||||
session.getPlayerInventory().setCursor(simulatedCursor, session);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue