The Great Refactor Part 1 - connector -> core

This commit is contained in:
RednedEpic 2021-11-20 13:56:40 -06:00
parent a4b2e05132
commit 0b5009b415
573 changed files with 135 additions and 135 deletions

362
core/pom.xml Normal file
View file

@ -0,0 +1,362 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.geysermc</groupId>
<artifactId>geyser-parent</artifactId>
<version>1.4.3-SNAPSHOT</version>
</parent>
<artifactId>core</artifactId>
<properties>
<adventure.version>4.9.3</adventure.version>
<fastutil.version>8.5.2</fastutil.version>
<jackson.version>2.12.4</jackson.version>
<netty.version>4.1.66.Final</netty.version>
</properties>
<dependencies>
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>ap</artifactId>
<version>1.4.3-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>common</artifactId>
<version>1.4.3-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<!-- Jackson JSON and YAML serialization -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>${jackson.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
<scope>compile</scope>
</dependency>
<!-- fastutil maps -->
<dependency>
<groupId>com.nukkitx</groupId>
<artifactId>nbt</artifactId>
<!-- Used for key/value interning -->
<version>2.1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.nukkitx.fastutil</groupId>
<artifactId>fastutil-int-int-maps</artifactId>
<version>${fastutil.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.nukkitx.fastutil</groupId>
<artifactId>fastutil-long-long-maps</artifactId>
<version>${fastutil.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.nukkitx.fastutil</groupId>
<artifactId>fastutil-int-byte-maps</artifactId>
<version>${fastutil.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.nukkitx.fastutil</groupId>
<artifactId>fastutil-int-boolean-maps</artifactId>
<version>${fastutil.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.nukkitx.fastutil</groupId>
<artifactId>fastutil-object-int-maps</artifactId>
<version>${fastutil.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.nukkitx.fastutil</groupId>
<artifactId>fastutil-object-object-maps</artifactId>
<version>${fastutil.version}</version>
<scope>compile</scope>
</dependency>
<!-- Network libraries -->
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.5.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.CloudburstMC.Protocol</groupId>
<artifactId>bedrock-v475</artifactId>
<version>c22aa595</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>com.nukkitx.network</groupId>
<artifactId>raknet</artifactId>
</exclusion>
<exclusion>
<groupId>com.nukkitx</groupId>
<artifactId>nbt</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.nukkitx.network</groupId>
<artifactId>raknet</artifactId>
<version>1.6.27-20210506.111625-1</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.RednedEpic</groupId>
<artifactId>MCAuthLib</artifactId>
<version>6c99331</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.steveice10</groupId>
<artifactId>mcprotocollib</artifactId>
<version>1.18-pre-SNAPSHOT</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>com.github.steveice10</groupId>
<artifactId>packetlib</artifactId>
</exclusion>
<exclusion>
<groupId>com.github.steveice10</groupId>
<artifactId>mcauthlib</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.steveice10</groupId>
<artifactId>packetlib</artifactId>
<version>2.1-SNAPSHOT</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</exclusion>
<exclusion>
<!-- This is still experimental - additionally, it could only really benefit standalone -->
<groupId>io.netty.incubator</groupId>
<artifactId>netty-incubator-transport-native-io_uring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-resolver-dns</artifactId>
<version>${netty.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-resolver-dns-native-macos</artifactId>
<version>${netty.version}</version>
<scope>compile</scope>
<classifier>osx-x86_64</classifier>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-haproxy</artifactId>
<version>${netty.version}</version>
<scope>compile</scope>
</dependency>
<!-- Network dependencies we are updating ourselves -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
<version>${netty.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<version>${netty.version}</version>
<scope>compile</scope>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<version>${netty.version}</version>
<scope>compile</scope>
<classifier>linux-aarch_64</classifier>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-kqueue</artifactId>
<version>${netty.version}</version>
<scope>compile</scope>
<classifier>osx-x86_64</classifier>
</dependency>
<!-- Adventure text serialization -->
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-legacy</artifactId>
<version>${adventure.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-plain</artifactId>
<version>${adventure.version}</version>
<scope>compile</scope>
</dependency>
<!-- Other -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<excludes>
<exclude>**/services/javax.annotation.processing.Processor</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<version>4.0.0</version>
<executions>
<execution>
<id>get-the-git-infos</id>
<goals>
<goal>revision</goal>
</goals>
</execution>
</executions>
<configuration>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
<generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
<format>properties</format>
<failOnNoGitDirectory>false</failOnNoGitDirectory>
<failOnUnableToExtractRepoInfo>false</failOnUnableToExtractRepoInfo>
<runOnlyOnce>false</runOnlyOnce>
<verbose>true</verbose>
<skipPoms>false</skipPoms>
<excludeProperties>
<excludeProperty>git.user.*</excludeProperty>
<excludeProperty>git.*.user.*</excludeProperty>
<excludeProperty>git.closest.*</excludeProperty>
<excludeProperty>git.commit.id.describe</excludeProperty>
<excludeProperty>git.commit.id.describe-short</excludeProperty>
<excludeProperty>git.commit.message.short</excludeProperty>
</excludeProperties>
<commitIdGenerationMode>flat</commitIdGenerationMode>
<gitDescribe>
<always>true</always>
</gitDescribe>
</configuration>
</plugin>
<plugin>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>replacer</artifactId>
<version>1.5.3</version>
<executions>
<execution>
<id>add-version</id>
<phase>process-sources</phase>
<goals>
<goal>replace</goal>
</goals>
<configuration>
<includes>
<include>${project.basedir}/src/main/java/org/geysermc/connector/GeyserConnector.java</include>
</includes>
<replacements>
<replacement>
<token>String VERSION = ".*"</token>
<value>String VERSION = "${project.version} (" + GIT_VERSION + ")"</value>
</replacement>
<replacement>
<token>String GIT_VERSION = ".*"</token>
<!--suppress UnresolvedMavenProperty -->
<value>String GIT_VERSION = "git-${git.branch}-${git.commit.id.abbrev}"</value>
</replacement>
</replacements>
</configuration>
</execution>
<execution>
<id>remove-version</id>
<phase>process-classes</phase>
<goals>
<goal>replace</goal>
</goals>
<configuration>
<includes>
<include>${project.basedir}/src/main/java/org/geysermc/connector/GeyserConnector.java</include>
</includes>
<replacements>
<replacement>
<token>String VERSION = ".*"</token>
<value>String VERSION = "DEV"</value>
</replacement>
<replacement>
<token>String GIT_VERSION = ".*"</token>
<value>String GIT_VERSION = "DEV"</value>
</replacement>
</replacements>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<!-- Force the right file encoding during unit testing -->
<argLine>-Dfile.encoding=${project.build.sourceEncoding}</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector;
import org.geysermc.connector.common.AuthType;
import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
import org.geysermc.connector.utils.LanguageUtils;
import java.nio.file.Files;
import java.nio.file.Path;
public class FloodgateKeyLoader {
public static Path getKeyPath(GeyserJacksonConfiguration config, Path floodgateDataFolder, Path geyserDataFolder, GeyserLogger logger) {
if (config.getRemote().getAuthType() != AuthType.FLOODGATE) {
return geyserDataFolder.resolve(config.getFloodgateKeyFile());
}
// Always prioritize Floodgate's key, if it is installed.
// This mostly prevents people from trying to copy the key and corrupting it in the process
if (floodgateDataFolder != null) {
Path autoKey = floodgateDataFolder.resolve("key.pem");
if (Files.exists(autoKey)) {
logger.info(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.auto_loaded"));
return autoKey;
} else {
logger.error(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.missing_key"));
}
}
Path floodgateKey;
if (config.getFloodgateKeyFile().equals("public-key.pem")) {
logger.info("Floodgate 2.0 doesn't use a public/private key system anymore. We'll search for key.pem instead");
floodgateKey = geyserDataFolder.resolve("key.pem");
} else {
floodgateKey = geyserDataFolder.resolve(config.getFloodgateKeyFile());
}
if (!Files.exists(floodgateKey)) {
logger.error(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed"));
}
return floodgateKey;
}
}

View file

@ -0,0 +1,511 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.steveice10.packetlib.tcp.TcpSession;
import com.nukkitx.network.raknet.RakNetConstants;
import com.nukkitx.network.util.EventLoops;
import com.nukkitx.protocol.bedrock.BedrockServer;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.kqueue.KQueue;
import io.netty.util.NettyRuntime;
import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.internal.SystemPropertyUtil;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.common.AuthType;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.entity.EntityDefinitions;
import org.geysermc.connector.metrics.Metrics;
import org.geysermc.connector.network.ConnectorServerEventHandler;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.network.translators.world.WorldManager;
import org.geysermc.connector.registry.BlockRegistries;
import org.geysermc.connector.registry.Registries;
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
import org.geysermc.connector.skin.FloodgateSkinUploader;
import org.geysermc.connector.utils.*;
import org.geysermc.floodgate.crypto.AesCipher;
import org.geysermc.floodgate.crypto.AesKeyProducer;
import org.geysermc.floodgate.crypto.Base64Topping;
import org.geysermc.floodgate.crypto.FloodgateCipher;
import org.geysermc.floodgate.news.NewsItemAction;
import org.geysermc.floodgate.time.TimeSyncer;
import org.jetbrains.annotations.Contract;
import javax.naming.directory.Attribute;
import javax.naming.directory.InitialDirContext;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.security.Key;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Getter
public class GeyserConnector {
public static final ObjectMapper JSON_MAPPER = new ObjectMapper()
.enable(JsonParser.Feature.IGNORE_UNDEFINED)
.enable(JsonParser.Feature.ALLOW_COMMENTS)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES)
.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
public static final String NAME = "Geyser";
public static final String GIT_VERSION = "DEV"; // A fallback for running in IDEs
public static final String VERSION = "DEV"; // A fallback for running in IDEs
/**
* Oauth client ID for Microsoft authentication
*/
public static final String OAUTH_CLIENT_ID = "204cefd1-4818-4de1-b98d-513fae875d88";
private static final String IP_REGEX = "\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b";
private final SessionManager sessionManager = new SessionManager();
private static GeyserConnector instance;
/**
* This is used in GeyserConnect to stop the bedrock server binding to a port
*/
@Setter
private static boolean shouldStartListener = true;
private final TimeSyncer timeSyncer;
private FloodgateCipher cipher;
private FloodgateSkinUploader skinUploader;
private final NewsHandler newsHandler;
private volatile boolean shuttingDown = false;
private final ScheduledExecutorService scheduledThread;
private final BedrockServer bedrockServer;
private final PlatformType platformType;
private final GeyserBootstrap bootstrap;
private final Metrics metrics;
private GeyserConnector(PlatformType platformType, GeyserBootstrap bootstrap) {
long startupTime = System.currentTimeMillis();
instance = this;
this.bootstrap = bootstrap;
GeyserLogger logger = bootstrap.getGeyserLogger();
GeyserConfiguration config = bootstrap.getGeyserConfig();
this.platformType = platformType;
logger.info("******************************************");
logger.info("");
logger.info(LanguageUtils.getLocaleStringLog("geyser.core.load", NAME, VERSION));
logger.info("");
logger.info("******************************************");
this.scheduledThread = Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory("Geyser Scheduled Thread"));
logger.setDebug(config.isDebugMode());
PacketTranslatorRegistry.init();
/* Initialize translators and registries */
BlockRegistries.init();
Registries.init();
EntityDefinitions.init();
ItemTranslator.init();
MessageTranslator.init();
LocaleUtils.init();
ScoreboardUpdater.init();
ResourcePack.loadPacks();
if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) {
// Set the remote address to localhost since that is where we are always connecting
try {
config.getRemote().setAddress(InetAddress.getLocalHost().getHostAddress());
} catch (UnknownHostException ex) {
logger.debug("Unknown host when trying to find localhost.");
if (config.isDebugMode()) {
ex.printStackTrace();
}
config.getRemote().setAddress(InetAddress.getLoopbackAddress().getHostAddress());
}
}
String remoteAddress = config.getRemote().getAddress();
// Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry.
if (!remoteAddress.matches(IP_REGEX) && !remoteAddress.equalsIgnoreCase("localhost")) {
int remotePort;
try {
// Searches for a server address and a port from a SRV record of the specified host name
InitialDirContext ctx = new InitialDirContext();
Attribute attr = ctx.getAttributes("dns:///_minecraft._tcp." + remoteAddress, new String[]{"SRV"}).get("SRV");
// size > 0 = SRV entry found
if (attr != null && attr.size() > 0) {
String[] record = ((String) attr.get(0)).split(" ");
// Overwrites the existing address and port with that from the SRV record.
config.getRemote().setAddress(remoteAddress = record[3]);
config.getRemote().setPort(remotePort = Integer.parseInt(record[2]));
logger.debug("Found SRV record \"" + remoteAddress + ":" + remotePort + "\"");
}
} catch (Exception | NoClassDefFoundError ex) { // Check for a NoClassDefFoundError to prevent Android crashes
logger.debug("Exception while trying to find an SRV record for the remote host.");
if (config.isDebugMode())
ex.printStackTrace(); // Otherwise we can get a stack trace for any domain that doesn't have an SRV record
}
}
// Ensure that PacketLib does not create an event loop for handling packets; we'll do that ourselves
TcpSession.USE_EVENT_LOOP_FOR_PACKETS = false;
TimeSyncer timeSyncer = null;
if (config.getRemote().getAuthType() == AuthType.FLOODGATE) {
timeSyncer = new TimeSyncer(Constants.NTP_SERVER);
try {
Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath());
cipher = new AesCipher(new Base64Topping());
cipher.init(key);
logger.info(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.loaded_key"));
skinUploader = new FloodgateSkinUploader(this).start();
} catch (Exception exception) {
logger.severe(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.bad_key"), exception);
}
}
this.timeSyncer = timeSyncer;
String branch = "unknown";
int buildNumber = -1;
if (this.isProductionEnvironment()) {
try {
Properties gitProperties = new Properties();
gitProperties.load(FileUtils.getResource("git.properties"));
branch = gitProperties.getProperty("git.branch");
String build = gitProperties.getProperty("git.build.number");
if (build != null) {
buildNumber = Integer.parseInt(build);
}
} catch (Throwable e) {
logger.error("Failed to read git.properties", e);
}
} else {
logger.debug("Not getting git properties for the news handler as we are in a development environment.");
}
newsHandler = new NewsHandler(branch, buildNumber);
CooldownUtils.setDefaultShowCooldown(config.getShowCooldown());
DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether
// https://github.com/GeyserMC/Geyser/issues/957
RakNetConstants.MAXIMUM_MTU_SIZE = (short) config.getMtu();
logger.debug("Setting MTU to " + config.getMtu());
Integer bedrockThreadCount = Integer.getInteger("Geyser.BedrockNetworkThreads");
if (bedrockThreadCount == null) {
// Copy the code from Netty's default thread count fallback
bedrockThreadCount = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
}
boolean enableProxyProtocol = config.getBedrock().isEnableProxyProtocol();
bedrockServer = new BedrockServer(
new InetSocketAddress(config.getBedrock().getAddress(), config.getBedrock().getPort()),
bedrockThreadCount,
EventLoops.commonGroup(),
enableProxyProtocol
);
if (config.isDebugMode()) {
logger.debug("EventLoop type: " + EventLoops.getChannelType());
if (EventLoops.getChannelType() == EventLoops.ChannelType.NIO) {
if (System.getProperties().contains("disableNativeEventLoop")) {
logger.debug("EventLoop type is NIO because native event loops are disabled.");
} else {
logger.debug("Reason for no Epoll: " + Epoll.unavailabilityCause().toString());
logger.debug("Reason for no KQueue: " + KQueue.unavailabilityCause().toString());
}
}
}
bedrockServer.setHandler(new ConnectorServerEventHandler(this));
if (shouldStartListener) {
bedrockServer.bind().whenComplete((avoid, throwable) -> {
if (throwable == null) {
logger.info(LanguageUtils.getLocaleStringLog("geyser.core.start", config.getBedrock().getAddress(), String.valueOf(config.getBedrock().getPort())));
} else {
logger.severe(LanguageUtils.getLocaleStringLog("geyser.core.fail", config.getBedrock().getAddress(), String.valueOf(config.getBedrock().getPort())));
throwable.printStackTrace();
}
}).join();
}
if (config.getMetrics().isEnabled()) {
metrics = new Metrics(this, "GeyserMC", config.getMetrics().getUniqueId(), false, java.util.logging.Logger.getLogger(""));
metrics.addCustomChart(new Metrics.SingleLineChart("players", sessionManager::size));
// Prevent unwanted words best we can
metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().getAuthType().toString().toLowerCase()));
metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::getPlatformName));
metrics.addCustomChart(new Metrics.SimplePie("defaultLocale", LanguageUtils::getDefaultLocale));
metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserConnector.VERSION));
metrics.addCustomChart(new Metrics.AdvancedPie("playerPlatform", () -> {
Map<String, Integer> valueMap = new HashMap<>();
for (GeyserSession session : sessionManager.getAllSessions()) {
if (session == null) continue;
if (session.getClientData() == null) continue;
String os = session.getClientData().getDeviceOs().toString();
if (!valueMap.containsKey(os)) {
valueMap.put(os, 1);
} else {
valueMap.put(os, valueMap.get(os) + 1);
}
}
return valueMap;
}));
metrics.addCustomChart(new Metrics.AdvancedPie("playerVersion", () -> {
Map<String, Integer> valueMap = new HashMap<>();
for (GeyserSession session : sessionManager.getAllSessions()) {
if (session == null) continue;
if (session.getClientData() == null) continue;
String version = session.getClientData().getGameVersion();
if (!valueMap.containsKey(version)) {
valueMap.put(version, 1);
} else {
valueMap.put(version, valueMap.get(version) + 1);
}
}
return valueMap;
}));
String minecraftVersion = bootstrap.getMinecraftServerVersion();
if (minecraftVersion != null) {
Map<String, Map<String, Integer>> versionMap = new HashMap<>();
Map<String, Integer> platformMap = new HashMap<>();
platformMap.put(platformType.getPlatformName(), 1);
versionMap.put(minecraftVersion, platformMap);
metrics.addCustomChart(new Metrics.DrilldownPie("minecraftServerVersion", () -> {
// By the end, we should return, for example:
// 1.16.5 => (Spigot, 1)
return versionMap;
}));
}
// The following code can be attributed to the PaperMC project
// https://github.com/PaperMC/Paper/blob/master/Spigot-Server-Patches/0005-Paper-Metrics.patch#L614
metrics.addCustomChart(new Metrics.DrilldownPie("javaVersion", () -> {
Map<String, Map<String, Integer>> map = new HashMap<>();
String javaVersion = System.getProperty("java.version");
Map<String, Integer> entry = new HashMap<>();
entry.put(javaVersion, 1);
// http://openjdk.java.net/jeps/223
// Java decided to change their versioning scheme and in doing so modified the
// java.version system property to return $major[.$minor][.$security][-ea], as opposed to
// 1.$major.0_$identifier we can handle pre-9 by checking if the "major" is equal to "1",
// otherwise, 9+
String majorVersion = javaVersion.split("\\.")[0];
String release;
int indexOf = javaVersion.lastIndexOf('.');
if (majorVersion.equals("1")) {
release = "Java " + javaVersion.substring(0, indexOf);
} else {
// of course, it really wouldn't be all that simple if they didn't add a quirk, now
// would it valid strings for the major may potentially include values such as -ea to
// denote a pre release
Matcher versionMatcher = Pattern.compile("\\d+").matcher(majorVersion);
if (versionMatcher.find()) {
majorVersion = versionMatcher.group(0);
}
release = "Java " + majorVersion;
}
map.put(release, entry);
return map;
}));
} else {
metrics = null;
}
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.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 = LanguageUtils.getLocaleStringLog("geyser.core.finish.done", new DecimalFormat("#.###").format(completeTime)) + " ";
if (isGui) {
message += LanguageUtils.getLocaleStringLog("geyser.core.finish.gui");
} else {
message += LanguageUtils.getLocaleStringLog("geyser.core.finish.console");
}
logger.info(message);
if (platformType == PlatformType.STANDALONE) {
logger.warning(LanguageUtils.getLocaleStringLog("geyser.core.movement_warn"));
}
newsHandler.handleNews(null, NewsItemAction.ON_SERVER_STARTED);
}
public void shutdown() {
bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown"));
shuttingDown = true;
if (sessionManager.size() >= 1) {
bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown.kick.log", sessionManager.size()));
sessionManager.disconnectAll("geyser.core.shutdown.kick.message");
bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown.kick.done"));
}
scheduledThread.shutdown();
bedrockServer.close();
if (timeSyncer != null) {
timeSyncer.shutdown();
}
if (skinUploader != null) {
skinUploader.close();
}
newsHandler.shutdown();
this.getCommandManager().getCommands().clear();
bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown.done"));
}
/**
* Gets a player by their current UUID
*
* @param uuid the uuid
* @return the player or <code>null</code> if there is no player online with this UUID
*/
@Contract("null -> null")
public GeyserSession getPlayerByUuid(UUID uuid) {
if (uuid == null) {
return null;
}
return sessionManager.getSessions().get(uuid);
}
/**
* Gets a player by their Xbox user identifier
*
* @param xuid the Xbox user identifier
* @return the player or <code>null</code> if there is no player online with this xuid
*/
@SuppressWarnings("unused") // API usage
public GeyserSession getPlayerByXuid(String xuid) {
for (GeyserSession session : sessionManager.getPendingSessions()) {
if (session.getAuthData().getXboxUUID().equals(xuid)) {
return session;
}
}
for (GeyserSession session : sessionManager.getSessions().values()) {
if (session.getAuthData().getXboxUUID().equals(xuid)) {
return session;
}
}
return null;
}
public static GeyserConnector start(PlatformType platformType, GeyserBootstrap bootstrap) {
return new GeyserConnector(platformType, bootstrap);
}
public void reload() {
shutdown();
bootstrap.onEnable();
}
public GeyserLogger getLogger() {
return bootstrap.getGeyserLogger();
}
public GeyserConfiguration getConfig() {
return bootstrap.getGeyserConfig();
}
public CommandManager getCommandManager() {
return bootstrap.getGeyserCommandManager();
}
public WorldManager getWorldManager() {
return bootstrap.getWorldManager();
}
public TimeSyncer getTimeSyncer() {
return timeSyncer;
}
/**
* Returns false if this Geyser instance is running in an IDE. This only needs to be used in cases where files
* expected to be in a jarfile are not present.
*
* @return true if the version number is not 'DEV'.
*/
public boolean isProductionEnvironment() {
//noinspection ConstantConditions - changes in production
return !"DEV".equals(GeyserConnector.VERSION);
}
/**
* Deprecated. Get the AuthType from the GeyserConfiguration through {@link GeyserConnector#getConfig()}
* @return The
*/
@Deprecated
public AuthType getDefaultAuthType() {
return getConfig().getRemote().getAuthType();
}
public static GeyserConnector getInstance() {
return instance;
}
}

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector;
public interface GeyserLogger {
/**
* Logs a severe message to console
*
* @param message the message to log
*/
void severe(String message);
/**
* Logs a severe message and an exception to console
*
* @param message the message to log
* @param error the error to throw
*/
void severe(String message, Throwable error);
/**
* Logs an error message to console
*
* @param message the message to log
*/
void error(String message);
/**
* Logs an error message and an exception to console
*
* @param message the message to log
* @param error the error to throw
*/
void error(String message, Throwable error);
/**
* Logs a warning message to console
*
* @param message the message to log
*/
void warning(String message);
/**
* Logs an info message to console
*
* @param message the message to log
*/
void info(String message);
/**
* Logs a debug message to console
*
* @param message the message to log
*/
void debug(String message);
/**
* Sets if the logger should print debug messages
*
* @param debug if the logger should print debug messages
*/
void setDebug(boolean debug);
/**
* If debug is enabled for this logger
*/
boolean isDebug();
}

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector;
import com.google.common.collect.ImmutableList;
import lombok.AccessLevel;
import lombok.Getter;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.LanguageUtils;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public final class SessionManager {
/**
* A list of all players who don't currently have a permanent UUID attached yet.
*/
@Getter(AccessLevel.PACKAGE)
private final Set<GeyserSession> pendingSessions = ConcurrentHashMap.newKeySet();
/**
* A list of all players who are currently in-game.
*/
@Getter
private final Map<UUID, GeyserSession> sessions = new ConcurrentHashMap<>();
/**
* Called once the player has successfully authenticated to the Geyser server.
*/
public void addPendingSession(GeyserSession session) {
pendingSessions.add(session);
}
/**
* Called once a player has successfully logged into their Java server.
*/
public void addSession(UUID uuid, GeyserSession session) {
pendingSessions.remove(session);
sessions.put(uuid, session);
}
public void removeSession(GeyserSession session) {
if (sessions.remove(session.getPlayerEntity().getUuid()) == null) {
// Session was likely pending
pendingSessions.remove(session);
}
}
/**
* Creates a new, immutable list containing all pending and active sessions.
*/
public Collection<GeyserSession> getAllSessions() {
return ImmutableList.<GeyserSession>builder() // builderWithExpectedSize is probably not a good idea yet as older Spigot builds probably won't have it.
.addAll(pendingSessions)
.addAll(sessions.values())
.build();
}
public void disconnectAll(String message) {
Collection<GeyserSession> sessions = getAllSessions();
for (GeyserSession session : sessions) {
session.disconnect(LanguageUtils.getPlayerLocaleString(message, session.getLocale()));
}
}
/**
* @return the total amount of sessions, including those pending.
*/
public int size() {
return pendingSessions.size() + sessions.size();
}
}

View file

@ -0,0 +1,128 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.bootstrap;
import org.geysermc.connector.GeyserLogger;
import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.dump.BootstrapDumpInfo;
import org.geysermc.connector.network.translators.world.GeyserWorldManager;
import org.geysermc.connector.network.translators.world.WorldManager;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import javax.annotation.Nullable;
import java.net.SocketAddress;
import java.nio.file.Path;
import java.nio.file.Paths;
public interface GeyserBootstrap {
GeyserWorldManager DEFAULT_CHUNK_MANAGER = new GeyserWorldManager();
/**
* Called when the GeyserBootstrap is enabled
*/
void onEnable();
/**
* Called when the GeyserBootstrap is disabled
*/
void onDisable();
/**
* Returns the current GeyserConfiguration
*
* @return The current GeyserConfiguration
*/
GeyserConfiguration getGeyserConfig();
/**
* Returns the current GeyserLogger
*
* @return The current GeyserLogger
*/
GeyserLogger getGeyserLogger();
/**
* Returns the current CommandManager
*
* @return The current CommandManager
*/
CommandManager getGeyserCommandManager();
/**
* Returns the current PingPassthrough manager
*
* @return The current PingPassthrough manager
*/
IGeyserPingPassthrough getGeyserPingPassthrough();
/**
* Returns the current WorldManager
*
* @return the current WorldManager
*/
default WorldManager getWorldManager() {
return DEFAULT_CHUNK_MANAGER;
}
/**
* Return the data folder where files get stored
*
* @return Path location of data folder
*/
Path getConfigFolder();
/**
* Information used for the bootstrap section of the debug dump
*
* @return The info about the bootstrap
*/
BootstrapDumpInfo getDumpInfo();
/**
* Returns the Minecraft version currently being used on the server. This should be only be implemented on platforms
* that have direct server access - platforms such as proxies always have to be on their latest version to support
* the newest Minecraft version, but older servers can use ViaVersion to enable newer versions to join.
* <br>
* If used, this should not be null before {@link org.geysermc.connector.GeyserConnector} initialization.
*
* @return the Minecraft version being used on the server, or <code>null</code> if not applicable
*/
@Nullable
default String getMinecraftServerVersion() {
return null;
}
@Nullable
default SocketAddress getSocketAddress() {
return null;
}
default Path getLogsPath() {
return Paths.get("logs/latest.log");
}
}

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.command;
import lombok.AllArgsConstructor;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Represents helper functions for listening to {@code /geyser} commands.
*/
@AllArgsConstructor
public class CommandExecutor {
protected final GeyserConnector connector;
public GeyserCommand getCommand(String label) {
return connector.getCommandManager().getCommands().get(label);
}
@Nullable
public GeyserSession getGeyserSession(CommandSender sender) {
if (sender.isConsole()) {
return null;
}
for (GeyserSession session : connector.getSessionManager().getSessions().values()) {
if (sender.getName().equals(session.getPlayerEntity().getUsername())) {
return session;
}
}
return null;
}
/**
* Determine which subcommands to suggest in the tab complete for the main /geyser command by a given command sender.
*
* @param sender The command sender to receive the tab complete suggestions.
* If the command sender is a bedrock player, an empty list will be returned as bedrock players do not get command argument suggestions.
* If the command sender is not a bedrock player, bedrock commands will not be shown.
* If the command sender does not have the permission for a given command, the command will not be shown.
* @return A list of command names to include in the tab complete
*/
public List<String> tabComplete(CommandSender sender) {
if (getGeyserSession(sender) != null) {
// Bedrock doesn't get tab completions or argument suggestions
return Collections.emptyList();
}
List<String> availableCommands = new ArrayList<>();
Map<String, GeyserCommand> commands = connector.getCommandManager().getCommands();
// Only show commands they have permission to use
for (String name : commands.keySet()) {
GeyserCommand geyserCommand = commands.get(name);
if (sender.hasPermission(geyserCommand.getPermission())) {
if (geyserCommand.isBedrockOnly()) {
// Don't show commands the JE player can't run
continue;
}
availableCommands.add(name);
}
}
return availableCommands;
}
}

View file

@ -0,0 +1,121 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.command;
import lombok.Getter;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.defaults.*;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.LanguageUtils;
import java.util.*;
public abstract class CommandManager {
@Getter
private final Map<String, GeyserCommand> commands = Collections.synchronizedMap(new HashMap<>());
private final GeyserConnector connector;
public CommandManager(GeyserConnector connector) {
this.connector = connector;
registerCommand(new HelpCommand(connector, "help", "geyser.commands.help.desc", "geyser.command.help"));
registerCommand(new ListCommand(connector, "list", "geyser.commands.list.desc", "geyser.command.list"));
registerCommand(new ReloadCommand(connector, "reload", "geyser.commands.reload.desc", "geyser.command.reload"));
registerCommand(new OffhandCommand(connector, "offhand", "geyser.commands.offhand.desc", "geyser.command.offhand"));
registerCommand(new DumpCommand(connector, "dump", "geyser.commands.dump.desc", "geyser.command.dump"));
registerCommand(new VersionCommand(connector, "version", "geyser.commands.version.desc", "geyser.command.version"));
registerCommand(new SettingsCommand(connector, "settings", "geyser.commands.settings.desc", "geyser.command.settings"));
registerCommand(new StatisticsCommand(connector, "statistics", "geyser.commands.statistics.desc", "geyser.command.statistics"));
registerCommand(new AdvancementsCommand("advancements", "geyser.commands.advancements.desc", "geyser.command.advancements"));
if (GeyserConnector.getInstance().getPlatformType() == PlatformType.STANDALONE) {
registerCommand(new StopCommand(connector, "stop", "geyser.commands.stop.desc", "geyser.command.stop"));
}
}
public void registerCommand(GeyserCommand command) {
commands.put(command.getName(), command);
connector.getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.commands.registered", command.getName()));
if (command.getAliases().isEmpty())
return;
for (String alias : command.getAliases())
commands.put(alias, command);
}
public void runCommand(CommandSender sender, String command) {
if (!command.startsWith("geyser "))
return;
command = command.trim().replace("geyser ", "");
String label;
String[] args;
if (!command.contains(" ")) {
label = command.toLowerCase();
args = new String[0];
} else {
label = command.substring(0, command.indexOf(" ")).toLowerCase();
String argLine = command.substring(command.indexOf(" ") + 1);
args = argLine.contains(" ") ? argLine.split(" ") : new String[] { argLine };
}
GeyserCommand cmd = commands.get(label);
if (cmd == null) {
connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.invalid"));
return;
}
if (sender instanceof GeyserSession) {
cmd.execute((GeyserSession) sender, sender, args);
} else {
if (!cmd.isBedrockOnly()) {
cmd.execute(null, sender, args);
} else {
connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.bootstrap.command.bedrock_only"));
}
}
}
/**
* @return a list of all subcommands under {@code /geyser}.
*/
public List<String> getCommandNames() {
return Arrays.asList(connector.getCommandManager().getCommands().keySet().toArray(new String[0]));
}
/**
* Returns the description of the given command
*
* @param command Command to get the description for
* @return Command description
*/
public abstract String getDescription(String command);
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.command;
import org.geysermc.connector.utils.LanguageUtils;
/**
* Implemented on top of any class that can send a command.
* For example, it wraps around Spigot's CommandSender class.
*/
public interface CommandSender {
String getName();
default void sendMessage(String[] messages) {
for (String message : messages) {
sendMessage(message);
}
}
void sendMessage(String message);
/**
* @return true if the specified sender is from the console.
*/
boolean isConsole();
/**
* Returns the locale of the command sender. Defaults to the default locale at {@link LanguageUtils#getDefaultLocale()}.
*
* @return the locale of the command sender.
*/
default String getLocale() {
return LanguageUtils.getDefaultLocale();
}
/**
* Checks if the CommandSender has a permission
*
* @param permission The permission node to check
* @return true if the CommandSender has the requested permission, false if not
*/
boolean hasPermission(String permission);
}

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.command;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.geysermc.connector.network.session.GeyserSession;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Getter
@RequiredArgsConstructor
public abstract class GeyserCommand {
protected final String name;
/**
* The description of the command - will attempt to be translated.
*/
protected final String description;
protected final String permission;
@Setter
private List<String> aliases = new ArrayList<>();
public abstract void execute(@Nullable GeyserSession session, CommandSender sender, String[] args);
/**
* If false, hides the command from being shown on the Geyser Standalone GUI.
*
* @return true if the command can be run on the server console
*/
public boolean isExecutableOnConsole() {
return true;
}
/**
* Used in the GUI to know what subcommands can be run
*
* @return a list of all possible subcommands, or empty if none.
*/
public List<String> getSubCommands() {
return Collections.emptyList();
}
/**
* Shortcut to {@link #getSubCommands()}{@code .isEmpty()}.
*
* @return true if there are subcommand present for this command.
*/
public boolean hasSubCommands() {
return !getSubCommands().isEmpty();
}
/**
* Used to send a deny message to Java players if this command can only be used by Bedrock players.
*
* @return true if this command can only be used by Bedrock players.
*/
public boolean isBedrockOnly() {
return false;
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.command.defaults;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.network.session.GeyserSession;
public class AdvancementsCommand extends GeyserCommand {
public AdvancementsCommand(String name, String description, String permission) {
super(name, description, permission);
}
@Override
public void execute(GeyserSession session, CommandSender sender, String[] args) {
if (session != null) {
session.getAdvancementsCache().buildAndShowMenuForm();
}
}
@Override
public boolean isExecutableOnConsole() {
return false;
}
@Override
public boolean isBedrockOnly() {
return true;
}
}

View file

@ -0,0 +1,148 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.command.defaults;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.common.ChatColor;
import org.geysermc.connector.common.serializer.AsteriskSerializer;
import org.geysermc.connector.dump.DumpInfo;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.LanguageUtils;
import org.geysermc.connector.utils.WebUtils;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class DumpCommand extends GeyserCommand {
private final GeyserConnector connector;
private static final ObjectMapper MAPPER = new ObjectMapper();
private static final String DUMP_URL = "https://dump.geysermc.org/";
public DumpCommand(GeyserConnector connector, String name, String description, String permission) {
super(name, description, permission);
this.connector = connector;
}
@Override
public void execute(GeyserSession session, CommandSender sender, String[] args) {
// Only allow the console to create dumps on Geyser Standalone
if (!sender.isConsole() && connector.getPlatformType() == PlatformType.STANDALONE) {
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.getLocale()));
return;
}
boolean showSensitive = false;
boolean offlineDump = false;
boolean addLog = false;
if (args.length >= 1) {
for (String arg : args) {
switch (arg) {
case "full" -> showSensitive = true;
case "offline" -> offlineDump = true;
case "logs" -> addLog = true;
}
}
}
AsteriskSerializer.showSensitive = showSensitive;
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.commands.dump.collecting", sender.getLocale()));
String dumpData;
try {
if (offlineDump) {
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
// Make arrays easier to read
prettyPrinter.indentArraysWith(new DefaultIndenter(" ", "\n"));
dumpData = MAPPER.writer(prettyPrinter).writeValueAsString(new DumpInfo(addLog));
} else {
dumpData = MAPPER.writeValueAsString(new DumpInfo(addLog));
}
} catch (IOException e) {
sender.sendMessage(ChatColor.RED + LanguageUtils.getPlayerLocaleString("geyser.commands.dump.collect_error", sender.getLocale()));
connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.dump.collect_error_short"), e);
return;
}
String uploadedDumpUrl = "";
if (offlineDump) {
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.commands.dump.writing", sender.getLocale()));
try {
FileOutputStream outputStream = new FileOutputStream(GeyserConnector.getInstance().getBootstrap().getConfigFolder().resolve("dump.json").toFile());
outputStream.write(dumpData.getBytes());
outputStream.close();
} catch (IOException e) {
sender.sendMessage(ChatColor.RED + LanguageUtils.getPlayerLocaleString("geyser.commands.dump.write_error", sender.getLocale()));
connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.dump.write_error_short"), e);
return;
}
uploadedDumpUrl = "dump.json";
} else {
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.commands.dump.uploading", sender.getLocale()));
String response;
JsonNode responseNode;
try {
response = WebUtils.post(DUMP_URL + "documents", dumpData);
responseNode = MAPPER.readTree(response);
} catch (IOException e) {
sender.sendMessage(ChatColor.RED + LanguageUtils.getPlayerLocaleString("geyser.commands.dump.upload_error", sender.getLocale()));
connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error_short"), e);
return;
}
if (!responseNode.has("key")) {
sender.sendMessage(ChatColor.RED + LanguageUtils.getPlayerLocaleString("geyser.commands.dump.upload_error_short", sender.getLocale()) + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response));
return;
}
uploadedDumpUrl = DUMP_URL + responseNode.get("key").asText();
}
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.commands.dump.message", sender.getLocale()) + " " + ChatColor.DARK_AQUA + uploadedDumpUrl);
if (!sender.isConsole()) {
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.commands.dump.created", sender.getName(), uploadedDumpUrl));
}
}
@Override
public List<String> getSubCommands() {
return Arrays.asList("offline", "full", "logs");
}
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.command.defaults;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.common.ChatColor;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.LanguageUtils;
import java.util.Collections;
import java.util.Map;
public class HelpCommand extends GeyserCommand {
private final GeyserConnector connector;
public HelpCommand(GeyserConnector connector, String name, String description, String permission) {
super(name, description, permission);
this.connector = connector;
this.setAliases(Collections.singletonList("?"));
}
/**
* Sends the help menu to a command sender. Will not show certain commands depending on the command sender and session.
*
* @param session The Geyser session of the command sender, if it is a bedrock player. If null, bedrock-only commands will be hidden.
* @param sender The CommandSender to send the help message to.
* @param args Not used.
*/
@Override
public void execute(GeyserSession session, CommandSender sender, String[] args) {
int page = 1;
int maxPage = 1;
String header = LanguageUtils.getPlayerLocaleString("geyser.commands.help.header", sender.getLocale(), page, maxPage);
sender.sendMessage(header);
Map<String, GeyserCommand> cmds = connector.getCommandManager().getCommands();
for (String cmdName : cmds.keySet()) {
GeyserCommand cmd = cmds.get(cmdName);
if (sender.hasPermission(cmd.getPermission())) {
// Only list commands the player can actually run
if (cmd.isBedrockOnly() && session == null) {
continue;
}
sender.sendMessage(ChatColor.YELLOW + "/geyser " + cmdName + ChatColor.WHITE + ": " +
LanguageUtils.getPlayerLocaleString(cmd.getDescription(), sender.getLocale()));
}
}
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.command.defaults;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.LanguageUtils;
import java.util.stream.Collectors;
public class ListCommand extends GeyserCommand {
private final GeyserConnector connector;
public ListCommand(GeyserConnector connector, String name, String description, String permission) {
super(name, description, permission);
this.connector = connector;
}
@Override
public void execute(GeyserSession session, CommandSender sender, String[] args) {
String message = LanguageUtils.getPlayerLocaleString("geyser.commands.list.message", sender.getLocale(),
connector.getSessionManager().size(),
connector.getSessionManager().getAllSessions().stream().map(GeyserSession::getName).collect(Collectors.joining(" ")));
sender.sendMessage(message);
}
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.command.defaults;
import com.github.steveice10.mc.protocol.data.game.entity.object.Direction;
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.BlockUtils;
public class OffhandCommand extends GeyserCommand {
public OffhandCommand(GeyserConnector connector, String name, String description, String permission) {
super(name, description, permission);
}
@Override
public void execute(GeyserSession session, CommandSender sender, String[] args) {
if (session == null) {
return;
}
ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.SWAP_HANDS, BlockUtils.POSITION_ZERO,
Direction.DOWN);
session.sendDownstreamPacket(releaseItemPacket);
}
@Override
public boolean isExecutableOnConsole() {
return false;
}
@Override
public boolean isBedrockOnly() {
return true;
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.command.defaults;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.LanguageUtils;
public class ReloadCommand extends GeyserCommand {
private final GeyserConnector connector;
public ReloadCommand(GeyserConnector connector, String name, String description, String permission) {
super(name, description, permission);
this.connector = connector;
}
@Override
public void execute(GeyserSession session, CommandSender sender, String[] args) {
if (!sender.isConsole() && connector.getPlatformType() == PlatformType.STANDALONE) {
return;
}
String message = LanguageUtils.getPlayerLocaleString("geyser.commands.reload.message", sender.getLocale());
sender.sendMessage(message);
connector.getSessionManager().disconnectAll("geyser.commands.reload.kick");
connector.reload();
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.command.defaults;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.SettingsUtils;
public class SettingsCommand extends GeyserCommand {
public SettingsCommand(GeyserConnector connector, String name, String description, String permission) {
super(name, description, permission);
}
@Override
public void execute(GeyserSession session, CommandSender sender, String[] args) {
if (session != null) {
session.sendForm(SettingsUtils.buildForm(session));
}
}
@Override
public boolean isExecutableOnConsole() {
return false;
}
@Override
public boolean isBedrockOnly() {
return true;
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.command.defaults;
import com.github.steveice10.mc.protocol.data.game.ClientCommand;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundClientCommandPacket;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.network.session.GeyserSession;
public class StatisticsCommand extends GeyserCommand {
public StatisticsCommand(GeyserConnector connector, String name, String description, String permission) {
super(name, description, permission);
}
@Override
public void execute(GeyserSession session, CommandSender sender, String[] args) {
if (session == null) return;
session.setWaitingForStatistics(true);
ServerboundClientCommandPacket ServerboundClientCommandPacket = new ServerboundClientCommandPacket(ClientCommand.STATS);
session.sendDownstreamPacket(ServerboundClientCommandPacket);
}
@Override
public boolean isExecutableOnConsole() {
return false;
}
@Override
public boolean isBedrockOnly() {
return true;
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.command.defaults;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.LanguageUtils;
import java.util.Collections;
public class StopCommand extends GeyserCommand {
private final GeyserConnector connector;
public StopCommand(GeyserConnector connector, String name, String description, String permission) {
super(name, description, permission);
this.connector = connector;
this.setAliases(Collections.singletonList("shutdown"));
}
@Override
public void execute(GeyserSession session, CommandSender sender, String[] args) {
if (!sender.isConsole() && connector.getPlatformType() == PlatformType.STANDALONE) {
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.getLocale()));
return;
}
connector.getBootstrap().onDisable();
}
}

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.command.defaults;
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.common.ChatColor;
import org.geysermc.connector.network.MinecraftProtocol;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.FileUtils;
import org.geysermc.connector.utils.LanguageUtils;
import org.geysermc.connector.utils.WebUtils;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Properties;
public class VersionCommand extends GeyserCommand {
private final GeyserConnector connector;
public VersionCommand(GeyserConnector connector, String name, String description, String permission) {
super(name, description, permission);
this.connector = connector;
}
@Override
public void execute(GeyserSession session, CommandSender sender, String[] args) {
String bedrockVersions;
List<BedrockPacketCodec> supportedCodecs = MinecraftProtocol.SUPPORTED_BEDROCK_CODECS;
if (supportedCodecs.size() > 1) {
bedrockVersions = supportedCodecs.get(0).getMinecraftVersion() + " - " + supportedCodecs.get(supportedCodecs.size() - 1).getMinecraftVersion();
} else {
bedrockVersions = MinecraftProtocol.SUPPORTED_BEDROCK_CODECS.get(0).getMinecraftVersion();
}
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.commands.version.version", sender.getLocale(),
GeyserConnector.NAME, GeyserConnector.VERSION, MinecraftProtocol.getJavaVersion(), bedrockVersions));
// Disable update checking in dev mode and for players in Geyser Standalone
if (GeyserConnector.getInstance().isProductionEnvironment() && !(!sender.isConsole() && connector.getPlatformType() == PlatformType.STANDALONE)) {
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.commands.version.checking", sender.getLocale()));
try {
Properties gitProp = new Properties();
gitProp.load(FileUtils.getResource("git.properties"));
String buildXML = WebUtils.getBody("https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/" +
URLEncoder.encode(gitProp.getProperty("git.branch"), StandardCharsets.UTF_8.toString()) + "/lastSuccessfulBuild/api/xml?xpath=//buildNumber");
if (buildXML.startsWith("<buildNumber>")) {
int latestBuildNum = Integer.parseInt(buildXML.replaceAll("<(\\\\)?(/)?buildNumber>", "").trim());
int buildNum = Integer.parseInt(gitProp.getProperty("git.build.number"));
if (latestBuildNum == buildNum) {
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.commands.version.no_updates", sender.getLocale()));
} else {
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.commands.version.outdated",
sender.getLocale(), (latestBuildNum - buildNum), "https://ci.geysermc.org/"));
}
} else {
throw new AssertionError("buildNumber missing");
}
} catch (IOException | AssertionError | NumberFormatException e) {
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.version.failed"), e);
sender.sendMessage(ChatColor.RED + LanguageUtils.getPlayerLocaleString("geyser.commands.version.failed", sender.getLocale()));
}
}
}
}

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.common;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import lombok.Getter;
import java.io.IOException;
@Getter
public enum AuthType {
OFFLINE,
ONLINE,
FLOODGATE;
public static final AuthType[] VALUES = values();
public static AuthType getById(int id) {
return id < VALUES.length ? VALUES[id] : OFFLINE;
}
/**
* Convert the AuthType string (from config) to the enum, ONLINE on fail
*
* @param name AuthType string
*
* @return The converted AuthType
*/
public static AuthType getByName(String name) {
String upperCase = name.toUpperCase();
for (AuthType type : VALUES) {
if (type.name().equals(upperCase)) {
return type;
}
}
return ONLINE;
}
public static class Deserializer extends JsonDeserializer<AuthType> {
@Override
public AuthType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return getByName(p.getValueAsString());
}
}
}

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.common;
public class ChatColor {
public static final char ESCAPE = '§';
public static final String BLACK = ESCAPE + "0";
public static final String DARK_BLUE = ESCAPE + "1";
public static final String DARK_GREEN = ESCAPE + "2";
public static final String DARK_AQUA = ESCAPE + "3";
public static final String DARK_RED = ESCAPE + "4";
public static final String DARK_PURPLE = ESCAPE + "5";
public static final String GOLD = ESCAPE + "6";
public static final String GRAY = ESCAPE + "7";
public static final String DARK_GRAY = ESCAPE + "8";
public static final String BLUE = ESCAPE + "9";
public static final String GREEN = ESCAPE + "a";
public static final String AQUA = ESCAPE + "b";
public static final String RED = ESCAPE + "c";
public static final String LIGHT_PURPLE = ESCAPE + "d";
public static final String YELLOW = ESCAPE + "e";
public static final String WHITE = ESCAPE + "f";
public static final String OBFUSCATED = ESCAPE + "k";
public static final String BOLD = ESCAPE + "l";
public static final String STRIKETHROUGH = ESCAPE + "m";
public static final String UNDERLINE = ESCAPE + "n";
public static final String ITALIC = ESCAPE + "o";
public static final String RESET = ESCAPE + "r";
/**
* Convert chat colour codes to terminal colours
*
* @param string The text to replace colours for
*
* @return A string ready for terminal printing
*/
public static String toANSI(String string) {
string = string.replace(BOLD, (char) 0x1b + "[1m");
string = string.replace(OBFUSCATED, (char) 0x1b + "[5m");
string = string.replace(ITALIC, (char) 0x1b + "[3m");
string = string.replace(UNDERLINE, (char) 0x1b + "[4m");
string = string.replace(STRIKETHROUGH, (char) 0x1b + "[9m");
string = string.replace(RESET, (char) 0x1b + "[0m");
string = string.replace(BLACK, (char) 0x1b + "[0;30m");
string = string.replace(DARK_BLUE, (char) 0x1b + "[0;34m");
string = string.replace(DARK_GREEN, (char) 0x1b + "[0;32m");
string = string.replace(DARK_AQUA, (char) 0x1b + "[0;36m");
string = string.replace(DARK_RED, (char) 0x1b + "[0;31m");
string = string.replace(DARK_PURPLE, (char) 0x1b + "[0;35m");
string = string.replace(GOLD, (char) 0x1b + "[0;33m");
string = string.replace(GRAY, (char) 0x1b + "[0;37m");
string = string.replace(DARK_GRAY, (char) 0x1b + "[30;1m");
string = string.replace(BLUE, (char) 0x1b + "[34;1m");
string = string.replace(GREEN, (char) 0x1b + "[32;1m");
string = string.replace(AQUA, (char) 0x1b + "[36;1m");
string = string.replace(RED, (char) 0x1b + "[31;1m");
string = string.replace(LIGHT_PURPLE, (char) 0x1b + "[35;1m");
string = string.replace(YELLOW, (char) 0x1b + "[33;1m");
string = string.replace(WHITE, (char) 0x1b + "[37;1m");
return string;
}
public String translateAlternateColorCodes(char color, String message) {
return message.replace(color, ESCAPE);
}
/**
* Remove all colour formatting tags from a message
*
* @param message Message to remove colour tags from
*
* @return The sanitised message
*/
public static String stripColors(String message) {
return message = message.replaceAll("(&([a-fk-or0-9]))","").replaceAll("(§([a-fk-or0-9]))","").replaceAll("s/\\x1b\\[[0-9;]*[a-zA-Z]//g","");
}
}

View file

@ -0,0 +1,265 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.common.connection;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.*;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import java.net.SocketAddress;
public class ChannelWrapper implements Channel {
protected final Channel source;
private volatile SocketAddress remoteAddress;
public ChannelWrapper(Channel channel) {
this.source = channel;
}
@Override
public SocketAddress localAddress() {
return source.localAddress();
}
@Override
public SocketAddress remoteAddress() {
if (remoteAddress == null) {
return source.remoteAddress();
}
return remoteAddress;
}
public void remoteAddress(SocketAddress socketAddress) {
remoteAddress = socketAddress;
}
@Override
public ChannelId id() {
return source.id();
}
@Override
public EventLoop eventLoop() {
return source.eventLoop();
}
@Override
public Channel parent() {
return source.parent();
}
@Override
public ChannelConfig config() {
return source.config();
}
@Override
public boolean isOpen() {
return source.isOpen();
}
@Override
public boolean isRegistered() {
return source.isRegistered();
}
@Override
public boolean isActive() {
return source.isActive();
}
@Override
public ChannelMetadata metadata() {
return source.metadata();
}
@Override
public ChannelFuture closeFuture() {
return source.closeFuture();
}
@Override
public boolean isWritable() {
return source.isWritable();
}
@Override
public long bytesBeforeUnwritable() {
return source.bytesBeforeUnwritable();
}
@Override
public long bytesBeforeWritable() {
return source.bytesBeforeWritable();
}
@Override
public Unsafe unsafe() {
return source.unsafe();
}
@Override
public ChannelPipeline pipeline() {
return source.pipeline();
}
@Override
public ByteBufAllocator alloc() {
return source.alloc();
}
@Override
public ChannelFuture bind(SocketAddress socketAddress) {
return source.bind(socketAddress);
}
@Override
public ChannelFuture connect(SocketAddress socketAddress) {
return source.connect(socketAddress);
}
@Override
public ChannelFuture connect(SocketAddress socketAddress, SocketAddress socketAddress1) {
return source.connect(socketAddress, socketAddress1);
}
@Override
public ChannelFuture disconnect() {
return source.disconnect();
}
@Override
public ChannelFuture close() {
return source.disconnect();
}
@Override
public ChannelFuture deregister() {
return source.deregister();
}
@Override
public ChannelFuture bind(SocketAddress socketAddress, ChannelPromise channelPromise) {
return source.bind(socketAddress, channelPromise);
}
@Override
public ChannelFuture connect(SocketAddress socketAddress, ChannelPromise channelPromise) {
return source.connect(socketAddress, channelPromise);
}
@Override
public ChannelFuture connect(SocketAddress socketAddress, SocketAddress socketAddress1, ChannelPromise channelPromise) {
return source.connect(socketAddress, socketAddress1, channelPromise);
}
@Override
public ChannelFuture disconnect(ChannelPromise channelPromise) {
return source.disconnect(channelPromise);
}
@Override
public ChannelFuture close(ChannelPromise channelPromise) {
return source.close(channelPromise);
}
@Override
public ChannelFuture deregister(ChannelPromise channelPromise) {
return source.deregister(channelPromise);
}
@Override
public Channel read() {
source.read();
return this;
}
@Override
public ChannelFuture write(Object o) {
return source.write(o);
}
@Override
public ChannelFuture write(Object o, ChannelPromise channelPromise) {
return source.write(o, channelPromise);
}
@Override
public Channel flush() {
return source.flush();
}
@Override
public ChannelFuture writeAndFlush(Object o, ChannelPromise channelPromise) {
return source.writeAndFlush(o, channelPromise);
}
@Override
public ChannelFuture writeAndFlush(Object o) {
return source.writeAndFlush(o);
}
@Override
public ChannelPromise newPromise() {
return source.newPromise();
}
@Override
public ChannelProgressivePromise newProgressivePromise() {
return source.newProgressivePromise();
}
@Override
public ChannelFuture newSucceededFuture() {
return source.newSucceededFuture();
}
@Override
public ChannelFuture newFailedFuture(Throwable throwable) {
return source.newFailedFuture(throwable);
}
@Override
public ChannelPromise voidPromise() {
return source.voidPromise();
}
@Override
public <T> Attribute<T> attr(AttributeKey<T> attributeKey) {
return source.attr(attributeKey);
}
@Override
public <T> boolean hasAttr(AttributeKey<T> attributeKey) {
return source.hasAttr(attributeKey);
}
@Override
public int compareTo(Channel o) {
return source.compareTo(o);
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.common.connection;
import io.netty.channel.Channel;
import io.netty.channel.DefaultChannelPipeline;
/**
* Exists solely to make DefaultChannelPipeline's protected constructor public
*/
public class DefaultChannelPipelinePublic extends DefaultChannelPipeline {
public DefaultChannelPipelinePublic(Channel channel) {
super(channel);
}
}

View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.common.connection;
import io.netty.channel.ChannelFuture;
import lombok.Getter;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
import java.net.SocketAddress;
/**
* Used to inject Geyser clients directly into the server, bypassing the need to implement a complete TCP connection,
* by creating a local channel.
*/
public abstract class GeyserInjector {
/**
* The local channel we can use to inject ourselves into the server without creating a TCP connection.
*/
protected ChannelFuture localChannel;
/**
* The LocalAddress to use to connect to the server without connecting over TCP.
*/
@Getter
protected SocketAddress serverSocketAddress;
/**
* @param bootstrap the bootstrap of the Geyser instance.
*/
public void initializeLocalChannel(GeyserBootstrap bootstrap) {
if (!bootstrap.getGeyserConfig().isUseDirectConnection()) {
bootstrap.getGeyserLogger().debug("Disabling direct injection!");
return;
}
if (this.localChannel != null) {
bootstrap.getGeyserLogger().warning("Geyser attempted to inject into the server connection handler twice! Please ensure you aren't using /reload or any plugin that (re)loads Geyser after the server has started.");
return;
}
try {
initializeLocalChannel0(bootstrap);
bootstrap.getGeyserLogger().debug("Local injection succeeded!");
} catch (Exception e) {
e.printStackTrace();
// If the injector partially worked, undo it
shutdown();
}
}
/**
* The method to implement that is called by {@link #initializeLocalChannel(GeyserBootstrap)} wrapped around a try/catch.
*/
protected abstract void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Exception;
public void shutdown() {
if (localChannel != null && localChannel.channel().isOpen()) {
try {
localChannel.channel().close().sync();
localChannel = null;
} catch (Exception e) {
e.printStackTrace();
}
} else if (localChannel != null) {
localChannel = null;
}
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.common.connection;
import io.netty.channel.local.LocalChannel;
import java.net.InetSocketAddress;
/**
* Client -> server storing the spoofed remote address.
*/
public class LocalChannelWithRemoteAddress extends LocalChannel {
private InetSocketAddress spoofedAddress;
public InetSocketAddress spoofedRemoteAddress() {
return spoofedAddress;
}
public void spoofedRemoteAddress(InetSocketAddress socketAddress) {
this.spoofedAddress = socketAddress;
}
}

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.common.connection;
import io.netty.channel.DefaultChannelPipeline;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalServerChannel;
import java.net.InetSocketAddress;
public class LocalChannelWrapper extends LocalChannel {
private final ChannelWrapper wrapper;
/**
* {@link #newChannelPipeline()} is called during super, so this exists until the wrapper can be initialized.
*/
private volatile ChannelWrapper tempWrapper;
public LocalChannelWrapper() {
wrapper = new ChannelWrapper(this);
}
public LocalChannelWrapper(LocalServerChannel parent, LocalChannel peer) {
super(parent, peer);
if (tempWrapper == null) {
this.wrapper = new ChannelWrapper(this);
} else {
this.wrapper = tempWrapper;
}
wrapper.remoteAddress(new InetSocketAddress(0));
}
public ChannelWrapper wrapper() {
return wrapper;
}
@Override
protected DefaultChannelPipeline newChannelPipeline() {
if (wrapper != null) {
return new DefaultChannelPipelinePublic(wrapper);
} else if (tempWrapper != null) {
return new DefaultChannelPipelinePublic(tempWrapper);
} else {
tempWrapper = new ChannelWrapper(this);
return new DefaultChannelPipelinePublic(tempWrapper);
}
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.common.connection;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalServerChannel;
/**
* If the incoming channel if an instance of LocalChannelWithRemoteAddress, this server creates a LocalChannelWrapper
* for the other end and attaches the spoofed remote address
*/
public class LocalServerChannelWrapper extends LocalServerChannel {
@Override
protected LocalChannel newLocalChannel(LocalChannel peer) {
// LocalChannel here should be an instance of LocalChannelWithRemoteAddress, which we can use to set the "remote address" on the other end
if (peer instanceof LocalChannelWithRemoteAddress) {
LocalChannelWrapper channel = new LocalChannelWrapper(this, peer);
channel.wrapper().remoteAddress(((LocalChannelWithRemoteAddress) peer).spoofedRemoteAddress());
return channel;
}
return super.newLocalChannel(peer);
}
}

View file

@ -0,0 +1,142 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.common.connection;
import com.github.steveice10.packetlib.BuiltinFlags;
import com.github.steveice10.packetlib.packet.PacketProtocol;
import com.github.steveice10.packetlib.tcp.*;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.unix.PreferredDirectByteBufAllocator;
import io.netty.handler.codec.haproxy.*;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
/**
* Manages a Minecraft Java session over our LocalChannel implementations.
*/
public final class LocalSession extends TcpSession {
private static DefaultEventLoopGroup DEFAULT_EVENT_LOOP_GROUP;
private static PreferredDirectByteBufAllocator PREFERRED_DIRECT_BYTE_BUF_ALLOCATOR = null;
private final SocketAddress targetAddress;
private final String clientIp;
public LocalSession(String host, int port, SocketAddress targetAddress, String clientIp, PacketProtocol protocol) {
super(host, port, protocol);
this.targetAddress = targetAddress;
this.clientIp = clientIp;
}
@Override
public void connect() {
if (this.disconnected) {
throw new IllegalStateException("Session has already been disconnected.");
}
if (DEFAULT_EVENT_LOOP_GROUP == null) {
DEFAULT_EVENT_LOOP_GROUP = new DefaultEventLoopGroup();
}
try {
final Bootstrap bootstrap = new Bootstrap();
bootstrap.channel(LocalChannelWithRemoteAddress.class);
bootstrap.handler(new ChannelInitializer<LocalChannelWithRemoteAddress>() {
@Override
public void initChannel(LocalChannelWithRemoteAddress channel) {
channel.spoofedRemoteAddress(new InetSocketAddress(clientIp, 0));
getPacketProtocol().newClientSession(LocalSession.this);
refreshReadTimeoutHandler(channel);
refreshWriteTimeoutHandler(channel);
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("encryption", new TcpPacketEncryptor(LocalSession.this));
pipeline.addLast("sizer", new TcpPacketSizer(LocalSession.this));
pipeline.addLast("codec", new TcpPacketCodec(LocalSession.this));
pipeline.addLast("manager", LocalSession.this);
addHAProxySupport(pipeline);
}
}).group(DEFAULT_EVENT_LOOP_GROUP).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getConnectTimeout() * 1000);
if (PREFERRED_DIRECT_BYTE_BUF_ALLOCATOR != null) {
bootstrap.option(ChannelOption.ALLOCATOR, PREFERRED_DIRECT_BYTE_BUF_ALLOCATOR);
}
bootstrap.remoteAddress(targetAddress);
bootstrap.connect().addListener((future) -> {
if (!future.isSuccess()) {
exceptionCaught(null, future.cause());
}
});
} catch (Throwable t) {
exceptionCaught(null, t);
}
}
// TODO duplicate code
private void addHAProxySupport(ChannelPipeline pipeline) {
InetSocketAddress clientAddress = getFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS);
if (getFlag(BuiltinFlags.ENABLE_CLIENT_PROXY_PROTOCOL, false) && clientAddress != null) {
pipeline.addFirst("proxy-protocol-packet-sender", new ChannelInboundHandlerAdapter() {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
HAProxyProxiedProtocol proxiedProtocol = clientAddress.getAddress() instanceof Inet4Address ? HAProxyProxiedProtocol.TCP4 : HAProxyProxiedProtocol.TCP6;
InetSocketAddress remoteAddress;
if (ctx.channel().remoteAddress() instanceof InetSocketAddress) {
remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();
} else {
remoteAddress = new InetSocketAddress(host, port);
}
ctx.channel().writeAndFlush(new HAProxyMessage(
HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, proxiedProtocol,
clientAddress.getAddress().getHostAddress(), remoteAddress.getAddress().getHostAddress(),
clientAddress.getPort(), remoteAddress.getPort()
));
ctx.pipeline().remove(this);
ctx.pipeline().remove("proxy-protocol-encoder");
super.channelActive(ctx);
}
});
pipeline.addFirst("proxy-protocol-encoder", HAProxyMessageEncoder.INSTANCE);
}
}
/**
* Should only be called when direct ByteBufs should be preferred. At this moment, this should only be called on BungeeCord.
*/
public static void createDirectByteBufAllocator() {
if (PREFERRED_DIRECT_BYTE_BUF_ALLOCATOR == null) {
PREFERRED_DIRECT_BYTE_BUF_ALLOCATOR = new PreferredDirectByteBufAllocator();
PREFERRED_DIRECT_BYTE_BUF_ALLOCATOR.updateAllocator(ByteBufAllocator.DEFAULT);
}
}
}

View file

@ -0,0 +1,119 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.common.main;
import javax.swing.*;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Locale;
import java.util.Scanner;
public class IGeyserMain {
/**
* Displays the run help message in the console and a message box if running with a gui
*/
public void displayMessage() {
String message = createMessage();
if (System.console() == null && !isHeadless()) {
JOptionPane.showMessageDialog(null, message, "GeyserMC Plugin: " + this.getPluginType(), JOptionPane.ERROR_MESSAGE);
}
printMessage(message);
}
/**
* Load and format the run help text
*
* @return The formatted message
*/
private String createMessage() {
StringBuilder message = new StringBuilder();
InputStream helpStream = IGeyserMain.class.getClassLoader().getResourceAsStream("languages/run-help/" + Locale.getDefault().toString() + ".txt");
if (helpStream == null) {
helpStream = IGeyserMain.class.getClassLoader().getResourceAsStream("languages/run-help/en_US.txt");
}
Scanner help = new Scanner(helpStream).useDelimiter("\\Z");
String line = "";
while (help.hasNext()) {
line = help.next();
line = line.replace("${plugin_type}", this.getPluginType());
line = line.replace("${plugin_folder}", this.getPluginFolder());
message.append(line).append("\n");
}
return message.toString();
}
/**
* Check if we are in a headless environment
*
* @return Are we in a headless environment?
*/
private boolean isHeadless() {
try {
Class<?> graphicsEnvironment = Class.forName("java.awt.GraphicsEnvironment");
Method isHeadless = graphicsEnvironment.getDeclaredMethod("isHeadless");
return (Boolean)isHeadless.invoke(null);
} catch (Exception ignored) {
}
return true;
}
/**
* Simply print a message to console
*
* @param message The message to print
*/
private void printMessage(String message) {
System.out.print(message);
}
/**
* Get the platform the plugin is for
*
* @return The string representation of the plugin platforms name
*/
public String getPluginType() {
return "unknown";
}
/**
* Get the folder name the plugin should go into
*
* @return The string representation of the folder
*/
public String getPluginFolder() {
return "unknown";
}
}

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.common.ping;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import java.util.ArrayList;
import java.util.Collection;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class GeyserPingInfo {
private String description;
private Players players;
private Version version;
@JsonIgnore
private Collection<String> playerList = new ArrayList<>();
public GeyserPingInfo() {
}
public GeyserPingInfo(String description, Players players, Version version) {
this.description = description;
this.players = players;
this.version = version;
}
@JsonSetter("description")
void setDescription(JsonNode description) {
this.description = description.toString();
}
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Players {
private int max;
private int online;
public Players() {
}
public Players(int max, int online) {
this.max = max;
this.online = online;
}
}
@Data
public static class Version {
private String name;
private int protocol;
public Version() {
}
public Version(String name, int protocol) {
this.name = name;
this.protocol = protocol;
}
}
}

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.common.serializer;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Optional;
public class AsteriskSerializer extends StdSerializer<Object> implements ContextualSerializer {
public static boolean showSensitive = false;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = AsteriskSerializer.class)
public @interface Asterisk {
String value() default "***";
/**
* If true, this value will be shown if {@link #showSensitive} is true, or if the IP is determined to not be a public IP
*
* @return true if this should be analyzed and treated as an IP
*/
boolean isIp() default false;
}
String asterisk;
boolean isIp;
public AsteriskSerializer() {
super(Object.class);
}
public AsteriskSerializer(String asterisk, boolean isIp) {
super(Object.class);
this.asterisk = asterisk;
this.isIp = isIp;
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty property) {
Optional<Asterisk> anno = Optional.ofNullable(property)
.map(prop -> prop.getAnnotation(Asterisk.class));
return new AsteriskSerializer(anno.map(Asterisk::value).orElse(null), anno.map(Asterisk::isIp).orElse(null));
}
@Override
public void serialize(Object obj, JsonGenerator gen, SerializerProvider prov) throws IOException {
if (isIp && (showSensitive || !isSensitiveIp((String) obj))) {
gen.writeObject(obj);
return;
}
gen.writeString(asterisk);
}
private boolean isSensitiveIp(String ip) {
if (ip.equalsIgnoreCase("localhost") || ip.equalsIgnoreCase("auto")) {
// `auto` should not be shown unless there is an obscure issue with setting the localhost address
return false;
}
return !ip.isEmpty() && !ip.equals("0.0.0.0") && !ip.equals("127.0.0.1");
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.configuration;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
public enum EmoteOffhandWorkaroundOption {
NO_EMOTES,
EMOTES_AND_OFFHAND,
DISABLED;
public static class Deserializer extends JsonDeserializer<EmoteOffhandWorkaroundOption> {
@Override
public EmoteOffhandWorkaroundOption deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String value = p.getValueAsString();
return switch (value) {
case "no-emotes" -> NO_EMOTES;
case "emotes-and-offhand" -> EMOTES_AND_OFFHAND;
default -> DISABLED;
};
}
}
}

View file

@ -0,0 +1,181 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.configuration;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.geysermc.connector.GeyserLogger;
import org.geysermc.connector.common.AuthType;
import org.geysermc.connector.network.CIDRMatcher;
import org.geysermc.connector.utils.LanguageUtils;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
public interface GeyserConfiguration {
// Modify this when you introduce breaking changes into the config
int CURRENT_CONFIG_VERSION = 4;
IBedrockConfiguration getBedrock();
IRemoteConfiguration getRemote();
Map<String, ? extends IUserAuthenticationInfo> getUserAuths();
boolean isCommandSuggestions();
@JsonIgnore
boolean isPassthroughMotd();
@JsonIgnore
boolean isPassthroughProtocolName();
@JsonIgnore
boolean isPassthroughPlayerCounts();
@JsonIgnore
boolean isLegacyPingPassthrough();
int getPingPassthroughInterval();
boolean isForwardPlayerPing();
int getMaxPlayers();
boolean isDebugMode();
boolean isAllowThirdPartyCapes();
boolean isAllowThirdPartyEars();
String getShowCooldown();
boolean isShowCoordinates();
EmoteOffhandWorkaroundOption getEmoteOffhandWorkaround();
String getDefaultLocale();
Path getFloodgateKeyPath();
boolean isAddNonBedrockItems();
boolean isAboveBedrockNetherBuilding();
boolean isForceResourcePacks();
boolean isXboxAchievementsEnabled();
int getCacheImages();
boolean isAllowCustomSkulls();
IMetricsInfo getMetrics();
interface IBedrockConfiguration {
String getAddress();
int getPort();
boolean isCloneRemotePort();
String getMotd1();
String getMotd2();
String getServerName();
int getCompressionLevel();
boolean isEnableProxyProtocol();
List<String> getProxyProtocolWhitelistedIPs();
/**
* @return Unmodifiable list of {@link CIDRMatcher}s from {@link #getProxyProtocolWhitelistedIPs()}
*/
List<CIDRMatcher> getWhitelistedIPsMatchers();
}
interface IRemoteConfiguration {
String getAddress();
int getPort();
void setAddress(String address);
void setPort(int port);
AuthType getAuthType();
boolean isPasswordAuthentication();
boolean isUseProxyProtocol();
boolean isForwardHost();
}
interface IUserAuthenticationInfo {
String getEmail();
String getPassword();
/**
* Will be removed after Microsoft accounts are fully migrated
*/
@Deprecated
boolean isMicrosoftAccount();
}
interface IMetricsInfo {
boolean isEnabled();
String getUniqueId();
}
int getScoreboardPacketThreshold();
// if u have offline mode enabled pls be safe
boolean isEnableProxyConnections();
int getMtu();
boolean isUseDirectConnection();
int getConfigVersion();
static void checkGeyserConfiguration(GeyserConfiguration geyserConfig, GeyserLogger geyserLogger) {
if (geyserConfig.getConfigVersion() < CURRENT_CONFIG_VERSION) {
geyserLogger.warning(LanguageUtils.getLocaleStringLog("geyser.bootstrap.config.outdated"));
} else if (geyserConfig.getConfigVersion() > CURRENT_CONFIG_VERSION) {
geyserLogger.warning(LanguageUtils.getLocaleStringLog("geyser.bootstrap.config.too_new"));
}
}
}

View file

@ -0,0 +1,265 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.configuration;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.common.AuthType;
import org.geysermc.connector.common.serializer.AsteriskSerializer;
import org.geysermc.connector.network.CIDRMatcher;
import org.geysermc.connector.utils.LanguageUtils;
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;
@Getter
@JsonIgnoreProperties(ignoreUnknown = true)
@SuppressWarnings("FieldMayBeFinal") // Jackson requires that the fields are not final
public abstract class GeyserJacksonConfiguration implements GeyserConfiguration {
/**
* If the config was originally 'auto' before the values changed
*/
@Setter
private boolean autoconfiguredRemote = false;
private BedrockConfiguration bedrock = new BedrockConfiguration();
private RemoteConfiguration remote = new RemoteConfiguration();
@JsonProperty("floodgate-key-file")
private String floodgateKeyFile = "key.pem";
public abstract Path getFloodgateKeyPath();
private Map<String, UserAuthenticationInfo> userAuths;
@JsonProperty("command-suggestions")
private boolean commandSuggestions = true;
@JsonProperty("passthrough-motd")
private boolean isPassthroughMotd = false;
@JsonProperty("passthrough-player-counts")
private boolean isPassthroughPlayerCounts = false;
@JsonProperty("passthrough-protocol-name")
private boolean isPassthroughProtocolName = false;
@JsonProperty("legacy-ping-passthrough")
private boolean isLegacyPingPassthrough = false;
@JsonProperty("ping-passthrough-interval")
private int pingPassthroughInterval = 3;
@JsonProperty("forward-player-ping")
private boolean forwardPlayerPing = false;
@JsonProperty("max-players")
private int maxPlayers = 100;
@JsonProperty("debug-mode")
private boolean debugMode = false;
@JsonProperty("allow-third-party-capes")
private boolean allowThirdPartyCapes = true;
@JsonProperty("show-cooldown")
private String showCooldown = "title";
@JsonProperty("show-coordinates")
private boolean showCoordinates = true;
@JsonDeserialize(using = EmoteOffhandWorkaroundOption.Deserializer.class)
@JsonProperty("emote-offhand-workaround")
private EmoteOffhandWorkaroundOption emoteOffhandWorkaround = EmoteOffhandWorkaroundOption.DISABLED;
@JsonProperty("allow-third-party-ears")
private boolean allowThirdPartyEars = false;
@JsonProperty("default-locale")
private String defaultLocale = null; // is null by default so system language takes priority
@JsonProperty("cache-images")
private int cacheImages = 0;
@JsonProperty("allow-custom-skulls")
private boolean allowCustomSkulls = true;
@JsonProperty("add-non-bedrock-items")
private boolean addNonBedrockItems = true;
@JsonProperty("above-bedrock-nether-building")
private boolean aboveBedrockNetherBuilding = false;
@JsonProperty("force-resource-packs")
private boolean forceResourcePacks = true;
@JsonProperty("xbox-achievements-enabled")
private boolean xboxAchievementsEnabled = false;
private MetricsInfo metrics = new MetricsInfo();
@Getter
@JsonIgnoreProperties(ignoreUnknown = true)
public static class BedrockConfiguration implements IBedrockConfiguration {
@AsteriskSerializer.Asterisk(isIp = true)
private String address = "0.0.0.0";
@Setter
private int port = 19132;
@JsonProperty("clone-remote-port")
private boolean cloneRemotePort = false;
private String motd1 = "GeyserMC";
private String motd2 = "Geyser";
@JsonProperty("server-name")
private String serverName = GeyserConnector.NAME;
@JsonProperty("compression-level")
private int compressionLevel = 6;
public int getCompressionLevel() {
return Math.max(-1, Math.min(compressionLevel, 9));
}
@JsonProperty("enable-proxy-protocol")
private boolean enableProxyProtocol = false;
@JsonProperty("proxy-protocol-whitelisted-ips")
private List<String> proxyProtocolWhitelistedIPs = Collections.emptyList();
@JsonIgnore
private List<CIDRMatcher> whitelistedIPsMatchers = null;
@Override
public List<CIDRMatcher> getWhitelistedIPsMatchers() {
// Effective Java, Third Edition; Item 83: Use lazy initialization judiciously
List<CIDRMatcher> matchers = this.whitelistedIPsMatchers;
if (matchers == null) {
synchronized (this) {
this.whitelistedIPsMatchers = matchers = proxyProtocolWhitelistedIPs.stream()
.map(CIDRMatcher::new)
.collect(Collectors.toList());
}
}
return Collections.unmodifiableList(matchers);
}
}
@Getter
@JsonIgnoreProperties(ignoreUnknown = true)
public static class RemoteConfiguration implements IRemoteConfiguration {
@Setter
@AsteriskSerializer.Asterisk(isIp = true)
private String address = "auto";
@JsonDeserialize(using = PortDeserializer.class)
@Setter
private int port = 25565;
@Setter
@JsonDeserialize(using = AuthType.Deserializer.class)
@JsonProperty("auth-type")
private AuthType authType = AuthType.ONLINE;
@JsonProperty("allow-password-authentication")
private boolean passwordAuthentication = true;
@JsonProperty("use-proxy-protocol")
private boolean useProxyProtocol = false;
@JsonProperty("forward-hostname")
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 {
private boolean enabled = true;
@JsonProperty("uuid")
private String uniqueId = UUID.randomUUID().toString();
}
@JsonProperty("scoreboard-packet-threshold")
private int scoreboardPacketThreshold = 10;
@JsonProperty("enable-proxy-connections")
private boolean enableProxyConnections = false;
@JsonProperty("mtu")
private int mtu = 1400;
@JsonProperty("use-direct-connection")
private boolean useDirectConnection = true;
@JsonProperty("config-version")
private int configVersion = 0;
/**
* Ensure that the port deserializes in the config as a number no matter what.
*/
protected static class PortDeserializer extends JsonDeserializer<Integer> {
@Override
public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String value = p.getValueAsString();
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
System.err.println(LanguageUtils.getLocaleStringLog("geyser.bootstrap.config.invalid_port"));
return 25565;
}
}
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.dump;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import java.util.List;
@Getter
public class BootstrapDumpInfo {
private final PlatformType platform;
public BootstrapDumpInfo() {
this.platform = GeyserConnector.getInstance().getPlatformType();
}
@Getter
@AllArgsConstructor
public static class PluginInfo {
public boolean enabled;
public String name;
public String version;
public String main;
public List<String> authors;
}
@Getter
@AllArgsConstructor
public static class ListenerInfo {
public String ip;
public int port;
}
}

View file

@ -0,0 +1,259 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.dump;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.hash.Hashing;
import com.google.common.io.ByteSource;
import com.google.common.io.Files;
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.common.serializer.AsteriskSerializer;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.network.MinecraftProtocol;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.DockerCheck;
import org.geysermc.connector.utils.FileUtils;
import org.geysermc.connector.utils.WebUtils;
import org.geysermc.floodgate.util.DeviceOs;
import org.geysermc.floodgate.util.FloodgateInfoHolder;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
@Getter
public class DumpInfo {
@JsonIgnore
private static final long MEGABYTE = 1024L * 1024L;
private final DumpInfo.VersionInfo versionInfo;
private Properties gitInfo;
private final GeyserConfiguration config;
private final Floodgate floodgate;
private final Object2IntMap<DeviceOs> userPlatforms;
private final HashInfo hashInfo;
private final RamInfo ramInfo;
private LogsInfo logsInfo;
private final BootstrapDumpInfo bootstrapInfo;
private final FlagsInfo flagsInfo;
public DumpInfo(boolean addLog) {
this.versionInfo = new VersionInfo();
try {
this.gitInfo = new Properties();
this.gitInfo.load(FileUtils.getResource("git.properties"));
} catch (IOException ignored) {
}
this.config = GeyserConnector.getInstance().getConfig();
this.floodgate = new Floodgate();
String md5Hash = "unknown";
String sha256Hash = "unknown";
try {
// https://stackoverflow.com/questions/320542/how-to-get-the-path-of-a-running-jar-file
// 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
md5Hash = byteSource.hash(Hashing.md5()).toString();
//noinspection UnstableApiUsage
sha256Hash = byteSource.hash(Hashing.sha256()).toString();
} catch (Exception e) {
if (GeyserConnector.getInstance().getConfig().isDebugMode()) {
e.printStackTrace();
}
}
this.hashInfo = new HashInfo(md5Hash, sha256Hash);
this.ramInfo = new DumpInfo.RamInfo();
if (addLog) {
this.logsInfo = new LogsInfo();
}
this.userPlatforms = new Object2IntOpenHashMap<>();
for (GeyserSession session : GeyserConnector.getInstance().getSessionManager().getAllSessions()) {
DeviceOs device = session.getClientData().getDeviceOs();
userPlatforms.put(device, userPlatforms.getOrDefault(device, 0) + 1);
}
this.bootstrapInfo = GeyserConnector.getInstance().getBootstrap().getDumpInfo();
this.flagsInfo = new FlagsInfo();
}
@Getter
public static class VersionInfo {
private final String name;
private final String version;
private final String javaName;
private final String javaVendor;
private final String javaVersion;
private final String architecture;
private final String operatingSystem;
private final String operatingSystemVersion;
private final NetworkInfo network;
private final MCInfo mcInfo;
VersionInfo() {
this.name = GeyserConnector.NAME;
this.version = GeyserConnector.VERSION;
this.javaName = System.getProperty("java.vm.name");
this.javaVendor = System.getProperty("java.vendor");
this.javaVersion = ManagementFactory.getRuntimeMXBean().getVmVersion(); // Gives a little more to the version we can use over the system property
// Usually gives Java architecture but still may be helpful.
this.architecture = System.getProperty("os.arch");
this.operatingSystem = System.getProperty("os.name");
this.operatingSystemVersion = System.getProperty("os.version");
this.network = new NetworkInfo();
this.mcInfo = new MCInfo();
}
}
@Getter
public static class NetworkInfo {
private final boolean dockerCheck;
private String internalIP;
NetworkInfo() {
if (AsteriskSerializer.showSensitive) {
try {
// This is the most reliable for getting the main local IP
Socket socket = new Socket();
socket.connect(new InetSocketAddress("geysermc.org", 80));
this.internalIP = socket.getLocalAddress().getHostAddress();
} catch (IOException e1) {
try {
// Fallback to the normal way of getting the local IP
this.internalIP = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException ignored) {
}
}
} else {
// Sometimes the internal IP is the external IP...
this.internalIP = "***";
}
this.dockerCheck = DockerCheck.checkBasic();
}
}
@Getter
public static class MCInfo {
private final List<String> bedrockVersions;
private final List<Integer> bedrockProtocols;
private final int defaultBedrockProtocol;
private final String javaVersion;
private final int javaProtocol;
MCInfo() {
this.bedrockVersions = MinecraftProtocol.SUPPORTED_BEDROCK_CODECS.stream().map(BedrockPacketCodec::getMinecraftVersion).toList();
this.bedrockProtocols = MinecraftProtocol.SUPPORTED_BEDROCK_CODECS.stream().map(BedrockPacketCodec::getProtocolVersion).toList();
this.defaultBedrockProtocol = MinecraftProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion();
this.javaVersion = MinecraftProtocol.getJavaVersion();
this.javaProtocol = MinecraftProtocol.getJavaProtocolVersion();
}
}
@Getter
public static class Floodgate {
private final Properties gitInfo;
private final Object config;
Floodgate() {
this.gitInfo = FloodgateInfoHolder.getGitProperties();
this.config = FloodgateInfoHolder.getConfig();
}
}
@Getter
public static class LogsInfo {
private String link;
public LogsInfo() {
try {
Map<String, String> fields = new HashMap<>();
fields.put("content", FileUtils.readAllLines(GeyserConnector.getInstance().getBootstrap().getLogsPath()).collect(Collectors.joining("\n")));
JsonNode logData = GeyserConnector.JSON_MAPPER.readTree(WebUtils.postForm("https://api.mclo.gs/1/log", fields));
this.link = logData.get("url").textValue();
} catch (IOException ignored) { }
}
}
@AllArgsConstructor
@Getter
public static class HashInfo {
private final String md5Hash;
private final 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;
}
}
/**
* 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();
}
}
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class AbstractArrowEntity extends Entity {
public AbstractArrowEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
// Set the correct texture if using the resource pack
setFlag(EntityFlag.BRIBED, definition.entityType() == EntityType.SPECTRAL_ARROW);
setMotion(motion);
}
public void setArrowFlags(ByteEntityMetadata entityMetadata) {
byte data = entityMetadata.getPrimitiveValue();
setFlag(EntityFlag.CRITICAL, (data & 0x01) == 0x01);
}
// Ignore the rotation sent by the Java server since the
// Java client calculates the rotation from the motion
@Override
public void setYaw(float yaw) {
}
@Override
public void setPitch(float pitch) {
}
@Override
public void setHeadYaw(float headYaw) {
}
@Override
public void setMotion(Vector3f motion) {
super.setMotion(motion);
double horizontalSpeed = Math.sqrt(motion.getX() * motion.getX() + motion.getZ() * motion.getZ());
this.yaw = (float) Math.toDegrees(Math.atan2(motion.getX(), motion.getZ()));
this.pitch = (float) Math.toDegrees(Math.atan2(motion.getY(), horizontalSpeed));
this.headYaw = yaw;
}
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.level.particle.Particle;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.EffectUtils;
import java.util.UUID;
public class AreaEffectCloudEntity extends Entity {
public AreaEffectCloudEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
protected void initializeMetadata() {
super.initializeMetadata();
// Without this the cloud doesn't appear,
dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_DURATION, 600);
// This disabled client side shrink of the cloud
dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS, 0.0f);
dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_CHANGE_RATE, -0.005f);
dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_CHANGE_ON_PICKUP, -0.5f);
setFlag(EntityFlag.FIRE_IMMUNE, true);
}
public void setRadius(FloatEntityMetadata entityMetadata) {
float value = entityMetadata.getPrimitiveValue();
dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS, value);
dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, 2.0f * value);
}
public void setParticle(EntityMetadata<Particle, ?> entityMetadata) {
Particle particle = entityMetadata.getValue();
int particleId = EffectUtils.getParticleId(session, particle.getType());
if (particleId != -1) {
dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_PARTICLE_ID, particleId);
}
}
}

View file

@ -0,0 +1,188 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
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.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.packet.AnimatePacket;
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import lombok.Getter;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class BoatEntity extends Entity {
/**
* Required when IS_BUOYANT is sent in order for boats to work in the water. <br>
*
* Taken from BDS 1.16.200, with the modification of <code>simulate_waves</code> since Java doesn't bob the boat up and down
* like Bedrock.
*/
private static final String BUOYANCY_DATA = "{\"apply_gravity\":true,\"base_buoyancy\":1.0,\"big_wave_probability\":0.02999999932944775," +
"\"big_wave_speed\":10.0,\"drag_down_on_buoyancy_removed\":0.0,\"liquid_blocks\":[\"minecraft:water\"," +
"\"minecraft:flowing_water\"],\"simulate_waves\":false}";
private boolean isPaddlingLeft;
private float paddleTimeLeft;
private boolean isPaddlingRight;
private float paddleTimeRight;
/**
* Saved for using the "pick" functionality on a boat.
*/
@Getter
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;
public BoatEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
// Initial rotation is incorrect
super(session, entityId, geyserId, uuid, definition, position.add(0d, definition.offset(), 0d), motion, yaw + 90, 0, yaw + 90);
// Required to be able to move on land 1.16.200+ or apply gravity not in the water 1.16.100+
dirtyMetadata.put(EntityData.IS_BUOYANT, (byte) 1);
dirtyMetadata.put(EntityData.BUOYANCY_DATA, BUOYANCY_DATA);
}
@Override
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
// We don't include the rotation (y) as it causes the boat to appear sideways
setPosition(position.add(0d, this.definition.offset(), 0d));
this.yaw = yaw + 90;
this.headYaw = yaw + 90;
setOnGround(isOnGround);
MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket();
moveEntityPacket.setRuntimeEntityId(geyserId);
// Minimal glitching when ClientboundMoveVehiclePacket is sent
moveEntityPacket.setPosition(session.getRidingVehicleEntity() == this ? position.up(EntityDefinitions.PLAYER.offset() - this.definition.offset()) : this.position);
moveEntityPacket.setRotation(getBedrockRotation());
moveEntityPacket.setOnGround(isOnGround);
moveEntityPacket.setTeleported(teleported);
session.sendUpstreamPacket(moveEntityPacket);
}
/**
* Move the boat without making the adjustments needed to translate from Java
*/
public void moveAbsoluteWithoutAdjustments(Vector3f position, float yaw, boolean isOnGround, boolean teleported) {
super.moveAbsolute(position, yaw, 0, yaw, isOnGround, teleported);
}
@Override
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
super.moveRelative(relX, relY, relZ, yaw, 0, yaw, isOnGround);
}
@Override
public void updatePositionAndRotation(double moveX, double moveY, double moveZ, float yaw, float pitch, boolean isOnGround) {
moveRelative(moveX, moveY, moveZ, yaw + 90, pitch, isOnGround);
}
@Override
public void updateRotation(float yaw, float pitch, boolean isOnGround) {
moveRelative(0, 0, 0, yaw + 90, 0, 0, isOnGround);
}
public void setVariant(IntEntityMetadata entityMetadata) {
variant = entityMetadata.getPrimitiveValue();
dirtyMetadata.put(EntityData.VARIANT, variant);
}
public void setPaddlingLeft(BooleanEntityMetadata entityMetadata) {
isPaddlingLeft = entityMetadata.getPrimitiveValue();
if (isPaddlingLeft) {
// Java sends simply "true" and "false" (is_paddling_left), Bedrock keeps sending packets as you're rowing
// This is an asynchronous method that emulates Bedrock rowing until "false" is sent.
paddleTimeLeft = 0f;
if (!this.passengers.isEmpty()) {
// Get the entity by the first stored passenger and convey motion in this manner
Entity entity = session.getEntityCache().getEntityByJavaId(this.passengers.iterator().nextLong());
if (entity != null) {
updateLeftPaddle(session, entity);
}
}
} else {
// Indicate that the row position should be reset
dirtyMetadata.put(EntityData.ROW_TIME_LEFT, 0.0f);
}
}
public void setPaddlingRight(BooleanEntityMetadata entityMetadata) {
isPaddlingRight = entityMetadata.getPrimitiveValue();
if (isPaddlingRight) {
paddleTimeRight = 0f;
if (!this.passengers.isEmpty()) {
Entity entity = session.getEntityCache().getEntityByJavaId(this.passengers.iterator().nextLong());
if (entity != null) {
updateRightPaddle(session, entity);
}
}
} else {
dirtyMetadata.put(EntityData.ROW_TIME_RIGHT, 0.0f);
}
}
private void updateLeftPaddle(GeyserSession session, Entity rower) {
if (isPaddlingLeft) {
paddleTimeLeft += ROWING_SPEED;
sendAnimationPacket(session, rower, AnimatePacket.Action.ROW_LEFT, paddleTimeLeft);
session.scheduleInEventLoop(() ->
updateLeftPaddle(session, rower),
100,
TimeUnit.MILLISECONDS
);
}
}
private void updateRightPaddle(GeyserSession session, Entity rower) {
if (isPaddlingRight) {
paddleTimeRight += ROWING_SPEED;
sendAnimationPacket(session, rower, AnimatePacket.Action.ROW_RIGHT, paddleTimeRight);
session.scheduleInEventLoop(() ->
updateRightPaddle(session, rower),
100,
TimeUnit.MILLISECONDS
);
}
}
private void sendAnimationPacket(GeyserSession session, Entity rower, AnimatePacket.Action action, float rowTime) {
AnimatePacket packet = new AnimatePacket();
packet.setRuntimeEntityId(rower.getGeyserId());
packet.setAction(action);
packet.setRowingTime(rowTime);
session.sendUpstreamPacket(packet);
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity {
public CommandBlockMinecartEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
protected void initializeMetadata() {
// Required, or else the GUI will not open
dirtyMetadata.put(EntityData.CONTAINER_TYPE, (byte) 16);
dirtyMetadata.put(EntityData.CONTAINER_BASE_SIZE, 1);
// Required, or else the client does not bother to send a packet back with the new information
dirtyMetadata.put(EntityData.COMMAND_BLOCK_ENABLED, (byte) 1);
}
/**
* By default, the command block shown is purple on Bedrock, which does not match Java Edition's orange.
*/
@Override
public void updateDefaultBlockMetadata() {
dirtyMetadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getCommandBlockRuntimeId());
dirtyMetadata.put(EntityData.DISPLAY_OFFSET, 6);
}
}

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
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.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
/**
* This class is used as a base for minecarts with a default block to display like furnaces and spawners
*/
public class DefaultBlockMinecartEntity extends MinecartEntity {
public int customBlock = 0;
public int customBlockOffset = 0;
public boolean showCustomBlock = false;
public DefaultBlockMinecartEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
dirtyMetadata.put(EntityData.CUSTOM_DISPLAY, (byte) 1);
}
@Override
public void spawnEntity() {
updateDefaultBlockMetadata();
super.spawnEntity();
}
@Override
public void setCustomBlock(IntEntityMetadata entityMetadata) {
customBlock = ((IntEntityMetadata) entityMetadata).getPrimitiveValue();
if (showCustomBlock) {
dirtyMetadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(customBlock));
}
}
@Override
public void setCustomBlockOffset(IntEntityMetadata entityMetadata) {
customBlockOffset = entityMetadata.getPrimitiveValue();
if (showCustomBlock) {
dirtyMetadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset);
}
}
@Override
public void setShowCustomBlock(BooleanEntityMetadata entityMetadata) {
if (entityMetadata.getPrimitiveValue()) {
showCustomBlock = true;
dirtyMetadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(customBlock));
dirtyMetadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset);
} else {
showCustomBlock = false;
updateDefaultBlockMetadata();
}
}
public void updateDefaultBlockMetadata() {
}
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.Optional;
import java.util.UUID;
public class EnderCrystalEntity extends Entity {
public EnderCrystalEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
protected void initializeMetadata() {
super.initializeMetadata();
// Bedrock 1.16.100+ - prevents the entity from appearing on fire itself when fire is underneath it
setFlag(EntityFlag.FIRE_IMMUNE, true);
}
public void setBlockTarget(EntityMetadata<Optional<Position>, ?> entityMetadata) {
// Show beam
// Usually performed client-side on Bedrock except for Ender Dragon respawn event
Optional<Position> optionalPos = entityMetadata.getValue();
if (optionalPos.isPresent()) {
Position pos = optionalPos.get();
dirtyMetadata.put(EntityData.BLOCK_TARGET, Vector3i.from(pos.getX(), pos.getY(), pos.getZ()));
} else {
dirtyMetadata.put(EntityData.BLOCK_TARGET, Vector3i.ZERO);
}
}
}

View file

@ -0,0 +1,439 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlags;
import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import com.nukkitx.protocol.bedrock.packet.RemoveEntityPacket;
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import net.kyori.adventure.text.Component;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import org.geysermc.connector.utils.MathUtils;
import java.util.Optional;
import java.util.UUID;
@Getter
@Setter
public class Entity {
protected final GeyserSession session;
protected long entityId;
protected final long geyserId;
protected UUID uuid;
protected Vector3f position;
protected Vector3f motion;
/**
* x = Yaw, y = Pitch, z = HeadYaw
*/
protected float yaw;
protected float pitch;
protected float headYaw;
/**
* Saves if the entity should be on the ground. Otherwise entities like parrots are flapping when rotating
*/
protected boolean onGround;
protected EntityDefinition<?> definition;
protected boolean valid;
/* Metadata about this specific entity */
@Setter(AccessLevel.NONE)
protected float boundingBoxHeight;
@Setter(AccessLevel.NONE)
protected float boundingBoxWidth;
@Setter(AccessLevel.NONE)
protected String nametag = "";
/* Metadata end */
protected LongOpenHashSet passengers = new LongOpenHashSet();
/**
* A container to store temporary metadata before it's sent to Bedrock.
*/
protected final GeyserDirtyMetadata dirtyMetadata = new GeyserDirtyMetadata();
/**
* The entity flags for the Bedrock entity.
* These must always be saved - if flags are updated and the other values aren't present, the Bedrock client will
* think they are set to false.
*/
@Getter(AccessLevel.NONE)
protected final EntityFlags flags = new EntityFlags();
/**
* Indicates if flags have been updated and need to be sent to the client.
*/
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.PROTECTED) // For players
private boolean flagsDirty = false;
public Entity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
this.session = session;
this.entityId = entityId;
this.geyserId = geyserId;
this.uuid = uuid;
this.definition = definition;
this.motion = motion;
this.yaw = yaw;
this.pitch = pitch;
this.headYaw = headYaw;
this.valid = false;
setPosition(position);
setAirSupply(getMaxAir());
initializeMetadata();
}
/**
* Called on entity spawn. Used to populate the entity metadata and flags with default values.
*/
protected void initializeMetadata() {
dirtyMetadata.put(EntityData.SCALE, 1f);
dirtyMetadata.put(EntityData.COLOR, 0);
dirtyMetadata.put(EntityData.MAX_AIR_SUPPLY, getMaxAir());
setDimensions(Pose.STANDING);
setFlag(EntityFlag.HAS_GRAVITY, true);
setFlag(EntityFlag.HAS_COLLISION, true);
setFlag(EntityFlag.CAN_SHOW_NAME, true);
setFlag(EntityFlag.CAN_CLIMB, true);
}
public void spawnEntity() {
AddEntityPacket addEntityPacket = new AddEntityPacket();
addEntityPacket.setIdentifier(definition.identifier());
addEntityPacket.setRuntimeEntityId(geyserId);
addEntityPacket.setUniqueEntityId(geyserId);
addEntityPacket.setPosition(position);
addEntityPacket.setMotion(motion);
addEntityPacket.setRotation(getBedrockRotation());
addEntityPacket.getMetadata().putFlags(flags);
dirtyMetadata.apply(addEntityPacket.getMetadata());
addAdditionalSpawnData(addEntityPacket);
valid = true;
session.sendUpstreamPacket(addEntityPacket);
flagsDirty = false;
session.getConnector().getLogger().debug("Spawned entity " + getClass().getName() + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
}
/**
* To be overridden in other entity classes, if additional things need to be done to the spawn entity packet.
*/
public void addAdditionalSpawnData(AddEntityPacket addEntityPacket) {
}
/**
* Despawns the entity
*
* @return can be deleted
*/
public boolean despawnEntity() {
if (!valid) return true;
for (long passenger : passengers) { // Make sure all passengers on the despawned entity are updated
Entity entity = session.getEntityCache().getEntityByJavaId(passenger);
if (entity == null) continue;
entity.setFlag(EntityFlag.RIDING, false);
entity.updateBedrockMetadata();
}
RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket();
removeEntityPacket.setUniqueEntityId(geyserId);
session.sendUpstreamPacket(removeEntityPacket);
valid = false;
return true;
}
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, boolean isOnGround) {
moveRelative(relX, relY, relZ, yaw, pitch, this.headYaw, isOnGround);
}
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
setYaw(yaw);
setPitch(pitch);
setHeadYaw(headYaw);
setOnGround(isOnGround);
this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ);
MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket();
moveEntityPacket.setRuntimeEntityId(geyserId);
moveEntityPacket.setPosition(position);
moveEntityPacket.setRotation(getBedrockRotation());
moveEntityPacket.setOnGround(isOnGround);
moveEntityPacket.setTeleported(false);
session.sendUpstreamPacket(moveEntityPacket);
}
public void moveAbsolute(Vector3f position, float yaw, float pitch, boolean isOnGround, boolean teleported) {
moveAbsolute(position, yaw, pitch, this.headYaw, isOnGround, teleported);
}
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
setPosition(position);
// Setters are intentional so it can be overridden in places like AbstractArrowEntity
setYaw(yaw);
setPitch(pitch);
setHeadYaw(headYaw);
setOnGround(isOnGround);
MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket();
moveEntityPacket.setRuntimeEntityId(geyserId);
moveEntityPacket.setPosition(position);
moveEntityPacket.setRotation(getBedrockRotation());
moveEntityPacket.setOnGround(isOnGround);
moveEntityPacket.setTeleported(teleported);
session.sendUpstreamPacket(moveEntityPacket);
}
/**
* Teleports an entity to a new location. Used in JavaTeleportEntityTranslator.
* @param position The new position of the entity.
* @param yaw The new yaw of the entity.
* @param pitch The new pitch of the entity.
* @param isOnGround Whether the entity is currently on the ground.
*/
public void teleport(Vector3f position, float yaw, float pitch, boolean isOnGround) {
moveAbsolute(position, yaw, pitch, isOnGround, false);
}
/**
* Updates an entity's head position. Used in JavaRotateHeadTranslator.
* @param headYaw The new head rotation of the entity.
*/
public void updateHeadLookRotation(float headYaw) {
moveRelative(0, 0, 0, headYaw, pitch, this.headYaw, onGround);
}
/**
* Updates an entity's position and rotation. Used in JavaMoveEntityPosRotTranslator.
* @param moveX The new X offset of the current position.
* @param moveY The new Y offset of the current position.
* @param moveZ The new Z offset of the current position.
* @param yaw The new yaw of the entity.
* @param pitch The new pitch of the entity.
* @param isOnGround Whether the entity is currently on the ground.
*/
public void updatePositionAndRotation(double moveX, double moveY, double moveZ, float yaw, float pitch, boolean isOnGround) {
moveRelative(moveX, moveY, moveZ, this.yaw, pitch, yaw, isOnGround);
}
/**
* Updates an entity's rotation. Used in JavaMoveEntityRotTranslator.
* @param yaw The new yaw of the entity.
* @param pitch The new pitch of the entity.
* @param isOnGround Whether the entity is currently on the ground.
*/
public void updateRotation(float yaw, float pitch, boolean isOnGround) {
updatePositionAndRotation(0, 0, 0, yaw, pitch, isOnGround);
}
public final boolean getFlag(EntityFlag flag) {
return flags.getFlag(flag);
}
/**
* Updates a flag value and determines if the flags would need synced with the Bedrock client.
*/
public final void setFlag(EntityFlag flag, boolean value) {
flagsDirty |= flags.setFlag(flag, value);
}
/**
* Sends the Bedrock metadata to the client
*/
public void updateBedrockMetadata() {
if (!valid) {
return;
}
if (dirtyMetadata.hasEntries() || flagsDirty) {
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
entityDataPacket.setRuntimeEntityId(geyserId);
if (flagsDirty) {
entityDataPacket.getMetadata().putFlags(flags);
flagsDirty = false;
}
dirtyMetadata.apply(entityDataPacket.getMetadata());
session.sendUpstreamPacket(entityDataPacket);
}
}
public void setFlags(ByteEntityMetadata entityMetadata) {
byte xd = entityMetadata.getPrimitiveValue();
setFlag(EntityFlag.ON_FIRE, ((xd & 0x01) == 0x01) && !getFlag(EntityFlag.FIRE_IMMUNE)); // Otherwise immune entities sometimes flicker onfire
setFlag(EntityFlag.SNEAKING, (xd & 0x02) == 0x02);
setFlag(EntityFlag.SPRINTING, (xd & 0x08) == 0x08);
// Swimming is ignored here and instead we rely on the pose
setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80);
setInvisible((xd & 0x20) == 0x20);
}
/**
* Set a boolean - whether the entity is invisible or visible
*
* @param value true if the entity is invisible
*/
protected void setInvisible(boolean value) {
setFlag(EntityFlag.INVISIBLE, value);
}
/**
* Set an int from 0 - this entity's maximum air - (air / maxAir) represents the percentage of bubbles left
*/
public final void setAir(IntEntityMetadata entityMetadata) {
setAirSupply(entityMetadata.getPrimitiveValue());
}
protected void setAirSupply(int amount) {
dirtyMetadata.put(EntityData.AIR_SUPPLY, (short) MathUtils.constrain(amount, 0, getMaxAir()));
}
protected int getMaxAir() {
return 300;
}
public void setDisplayName(EntityMetadata<Optional<Component>, ?> entityMetadata) {
Optional<Component> name = entityMetadata.getValue();
if (name.isPresent()) {
nametag = MessageTranslator.convertMessage(name.get(), session.getLocale());
dirtyMetadata.put(EntityData.NAMETAG, nametag);
} else if (!nametag.isEmpty()) {
// Clear nametag
dirtyMetadata.put(EntityData.NAMETAG, "");
}
}
public void setDisplayNameVisible(BooleanEntityMetadata entityMetadata) {
dirtyMetadata.put(EntityData.NAMETAG_ALWAYS_SHOW, (byte) (entityMetadata.getPrimitiveValue() ? 1 : 0));
}
public void setGravity(BooleanEntityMetadata entityMetadata) {
setFlag(EntityFlag.HAS_GRAVITY, !entityMetadata.getPrimitiveValue());
}
/**
* Usually used for bounding box and not animation.
*/
public void setPose(EntityMetadata<Pose, ?> entityMetadata) {
Pose pose = entityMetadata.getValue();
setFlag(EntityFlag.SLEEPING, pose.equals(Pose.SLEEPING));
// Triggered when crawling
setFlag(EntityFlag.SWIMMING, pose.equals(Pose.SWIMMING));
setDimensions(pose);
}
/**
* Set the height and width of the entity's bounding box
*/
protected void setDimensions(Pose pose) {
// No flexibility options for basic entities
setBoundingBoxHeight(definition.height());
setBoundingBoxWidth(definition.width());
}
public void setBoundingBoxHeight(float height) {
if (height != boundingBoxHeight) {
boundingBoxHeight = height;
dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, boundingBoxHeight);
}
}
public void setBoundingBoxWidth(float width) {
if (width != boundingBoxWidth) {
boundingBoxWidth = width;
dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, boundingBoxWidth);
}
}
/**
* Set a float from 0-1 - how strong the "frozen" overlay should be on screen.
*/
public float setFreezing(IntEntityMetadata entityMetadata) {
// The value that Java edition gives us is in ticks, but Bedrock uses a float percentage of the strength 0.0 -> 1.0
// The Java client caps its freezing tick percentage at 140
int freezingTicks = Math.min(((IntEntityMetadata) entityMetadata).getPrimitiveValue(), 140);
float freezingPercentage = freezingTicks / 140f;
dirtyMetadata.put(EntityData.FREEZING_EFFECT_STRENGTH, freezingPercentage);
return freezingPercentage;
}
public void setRiderSeatPosition(Vector3f position) {
dirtyMetadata.put(EntityData.RIDER_SEAT_POSITION, position);
}
/**
* If true, the entity should be shaking on the client's end.
*
* @return whether {@link EntityFlag#SHAKING} should be set to true.
*/
protected boolean isShaking() {
return false;
}
/**
* x = Pitch, y = HeadYaw, z = Yaw
*
* @return the bedrock rotation
*/
public Vector3f getBedrockRotation() {
return Vector3f.from(pitch, headYaw, yaw);
}
@SuppressWarnings("unchecked")
public <I extends Entity> I as(Class<I> entityClass) {
return entityClass.isInstance(this) ? (I) this : null;
}
public <I extends Entity> boolean is(Class<I> entityClass) {
return entityClass.isInstance(this);
}
}

View file

@ -0,0 +1,158 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.entity.factory.BaseEntityFactory;
import org.geysermc.connector.entity.factory.EntityFactory;
import org.geysermc.connector.registry.Registries;
import java.util.List;
import java.util.Locale;
import java.util.function.BiConsumer;
/**
* Represents data for an entity. This includes properties such as height and width, as well as the list of entity
* metadata translators needed to translate the properties sent from the server. The translators are structured in such
* a way that inserting a new one (for example in version updates) is convenient.
*
* @param <T> the entity type this definition represents
*/
public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, EntityType entityType, String identifier,
float width, float height, float offset, List<EntityMetadataTranslator<? super T, ?, ?>> translators) {
public static <T extends Entity> Builder<T> inherited(BaseEntityFactory<T> factory, EntityDefinition<? super T> parent) {
return inherited((EntityFactory<T>) factory, parent);
}
public static <T extends Entity> Builder<T> inherited(EntityFactory<T> factory, EntityDefinition<? super T> parent) {
return new Builder<>(factory, parent.entityType, parent.identifier, parent.width, parent.height, parent.offset, new ObjectArrayList<>(parent.translators));
}
public static <T extends Entity> Builder<T> builder(EntityFactory<T> factory) {
return new Builder<>(factory);
}
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) {
// This can safely happen; it means we don't translate this entity metadata
return;
}
if (translator.acceptedType() != metadata.getType()) {
GeyserConnector.getInstance().getLogger().warning("Metadata ID " + metadata.getId() + " was received with type " + metadata.getType() + " but we expected " + translator.acceptedType() + " for " + entity.getDefinition().entityType());
if (GeyserConnector.getInstance().getConfig().isDebugMode()) {
GeyserConnector.getInstance().getLogger().debug(metadata.toString());
}
return;
}
translator.translateFunction().accept(entity, metadata);
}
@Setter
@Accessors(fluent = true, chain = true)
public static class Builder<T extends Entity> {
private final EntityFactory<T> factory;
private EntityType type;
private String identifier;
private float width;
private float height;
private float offset;
private final List<EntityMetadataTranslator<? super T, ?, ?>> translators;
private Builder(EntityFactory<T> factory) {
this.factory = factory;
translators = new ObjectArrayList<>();
}
public Builder(EntityFactory<T> factory, EntityType type, String identifier, float width, float height, float offset, List<EntityMetadataTranslator<? super T, ?, ?>> translators) {
this.factory = factory;
this.type = type;
this.identifier = identifier;
this.width = width;
this.height = height;
this.offset = offset;
this.translators = translators;
}
/**
* Sets the height and width as one value
*/
public Builder<T> heightAndWidth(float value) {
height = value;
width = value;
return this;
}
/**
* Resets the identifier as well
*/
public Builder<T> type(EntityType type) {
this.type = type;
identifier = null;
return this;
}
public <U, EM extends EntityMetadata<U, ? extends MetadataType<U>>> Builder<T> addTranslator(MetadataType<U> type, BiConsumer<T, EM> translateFunction) {
translators.add(new EntityMetadataTranslator<>(type, translateFunction));
return this;
}
public Builder<T> addTranslator(EntityMetadataTranslator<T, ?, ?> translator) {
translators.add(translator);
return this;
}
public EntityDefinition<T> build() {
return build(true);
}
/**
* @param register whether to register this entity in the Registries for entity types. Generally this should be
* set to false if we're not expecting this entity to spawn from the network.
*/
public EntityDefinition<T> build(boolean register) {
if (identifier == null && type != null) {
identifier = "minecraft:" + type.name().toLowerCase(Locale.ROOT);
}
EntityDefinition<T> definition = new EntityDefinition<>(factory, type, identifier, width, height, offset, translators);
if (register && definition.entityType() != null) {
Registries.ENTITY_DEFINITIONS.get().putIfAbsent(definition.entityType(), definition);
Registries.JAVA_ENTITY_IDENTIFIERS.get().putIfAbsent("minecraft:" + type.name().toLowerCase(Locale.ROOT), definition);
}
return definition;
}
}
}

View file

@ -0,0 +1,904 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.factory.BaseEntityFactory;
import org.geysermc.connector.entity.factory.ExperienceOrbEntityFactory;
import org.geysermc.connector.entity.factory.PaintingEntityFactory;
import org.geysermc.connector.entity.living.*;
import org.geysermc.connector.entity.living.animal.*;
import org.geysermc.connector.entity.living.animal.horse.*;
import org.geysermc.connector.entity.living.animal.tameable.CatEntity;
import org.geysermc.connector.entity.living.animal.tameable.ParrotEntity;
import org.geysermc.connector.entity.living.animal.tameable.TameableEntity;
import org.geysermc.connector.entity.living.animal.tameable.WolfEntity;
import org.geysermc.connector.entity.living.merchant.AbstractMerchantEntity;
import org.geysermc.connector.entity.living.merchant.VillagerEntity;
import org.geysermc.connector.entity.living.monster.*;
import org.geysermc.connector.entity.living.monster.raid.PillagerEntity;
import org.geysermc.connector.entity.living.monster.raid.RaidParticipantEntity;
import org.geysermc.connector.entity.living.monster.raid.SpellcasterIllagerEntity;
import org.geysermc.connector.entity.living.monster.raid.VindicatorEntity;
import org.geysermc.connector.entity.player.PlayerEntity;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import org.geysermc.connector.registry.Registries;
public final class EntityDefinitions {
public static final EntityDefinition<AreaEffectCloudEntity> AREA_EFFECT_CLOUD;
public static final EntityDefinition<ArmorStandEntity> ARMOR_STAND;
public static final EntityDefinition<TippedArrowEntity> ARROW;
public static final EntityDefinition<AxolotlEntity> AXOLOTL;
public static final EntityDefinition<BatEntity> BAT;
public static final EntityDefinition<BeeEntity> BEE;
public static final EntityDefinition<BlazeEntity> BLAZE;
public static final EntityDefinition<BoatEntity> BOAT;
public static final EntityDefinition<CatEntity> CAT;
public static final EntityDefinition<MonsterEntity> CAVE_SPIDER;
public static final EntityDefinition<ChickenEntity> CHICKEN;
public static final EntityDefinition<CreeperEntity> CREEPER;
public static final EntityDefinition<AbstractFishEntity> COD;
public static final EntityDefinition<AnimalEntity> COW;
public static final EntityDefinition<ChestedHorseEntity> DONKEY;
public static final EntityDefinition<WaterEntity> DOLPHIN;
public static final EntityDefinition<ItemedFireballEntity> DRAGON_FIREBALL;
public static final EntityDefinition<ZombieEntity> DROWNED;
public static final EntityDefinition<ElderGuardianEntity> ELDER_GUARDIAN;
public static final EntityDefinition<EndermanEntity> ENDERMAN;
public static final EntityDefinition<MonsterEntity> ENDERMITE;
public static final EntityDefinition<EnderDragonEntity> ENDER_DRAGON;
public static final EntityDefinition<EnderCrystalEntity> END_CRYSTAL;
public static final EntityDefinition<SpellcasterIllagerEntity> EVOKER;
public static final EntityDefinition<Entity> EVOKER_FANGS;
public static final EntityDefinition<ExpOrbEntity> EXPERIENCE_ORB;
public static final EntityDefinition<Entity> EYE_OF_ENDER;
public static final EntityDefinition<FallingBlockEntity> FALLING_BLOCK;
public static final EntityDefinition<ItemedFireballEntity> FIREBALL;
public static final EntityDefinition<FishingHookEntity> FISHING_BOBBER;
public static final EntityDefinition<FireworkEntity> FIREWORK_ROCKET;
public static final EntityDefinition<FoxEntity> FOX;
public static final EntityDefinition<GiantEntity> GIANT;
public static final EntityDefinition<GhastEntity> GHAST;
public static final EntityDefinition<ItemFrameEntity> GLOW_ITEM_FRAME;
public static final EntityDefinition<GlowSquidEntity> GLOW_SQUID;
public static final EntityDefinition<GoatEntity> GOAT;
public static final EntityDefinition<GuardianEntity> GUARDIAN;
public static final EntityDefinition<HoglinEntity> HOGLIN;
public static final EntityDefinition<HorseEntity> HORSE;
public static final EntityDefinition<ZombieEntity> HUSK;
public static final EntityDefinition<SpellcasterIllagerEntity> ILLUSIONER; // Not present on Bedrock
public static final EntityDefinition<IronGolemEntity> IRON_GOLEM;
public static final EntityDefinition<ItemEntity> ITEM;
public static final EntityDefinition<ItemFrameEntity> ITEM_FRAME;
public static final EntityDefinition<LeashKnotEntity> LEASH_KNOT;
public static final EntityDefinition<LightningEntity> LIGHTNING_BOLT;
public static final EntityDefinition<LlamaEntity> LLAMA;
public static final EntityDefinition<ThrowableEntity> LLAMA_SPIT;
public static final EntityDefinition<MagmaCubeEntity> MAGMA_CUBE;
public static final EntityDefinition<MinecartEntity> MINECART;
public static final EntityDefinition<MinecartEntity> MINECART_CHEST;
public static final EntityDefinition<CommandBlockMinecartEntity> MINECART_COMMAND_BLOCK;
public static final EntityDefinition<MinecartEntity> MINECART_HOPPER;
public static final EntityDefinition<FurnaceMinecartEntity> MINECART_FURNACE; // Not present on Bedrock
public static final EntityDefinition<SpawnerMinecartEntity> MINECART_SPAWNER; // Not present on Bedrock
public static final EntityDefinition<MinecartEntity> MINECART_TNT;
public static final EntityDefinition<MooshroomEntity> MOOSHROOM;
public static final EntityDefinition<ChestedHorseEntity> MULE;
public static final EntityDefinition<OcelotEntity> OCELOT;
public static final EntityDefinition<PaintingEntity> PAINTING;
public static final EntityDefinition<PandaEntity> PANDA;
public static final EntityDefinition<ParrotEntity> PARROT;
public static final EntityDefinition<PhantomEntity> PHANTOM;
public static final EntityDefinition<PigEntity> PIG;
public static final EntityDefinition<PiglinEntity> PIGLIN;
public static final EntityDefinition<BasePiglinEntity> PIGLIN_BRUTE;
public static final EntityDefinition<PillagerEntity> PILLAGER;
public static final EntityDefinition<PlayerEntity> PLAYER;
public static final EntityDefinition<PolarBearEntity> POLAR_BEAR;
public static final EntityDefinition<TNTEntity> PRIMED_TNT;
public static final EntityDefinition<PufferFishEntity> PUFFERFISH;
public static final EntityDefinition<RabbitEntity> RABBIT;
public static final EntityDefinition<RaidParticipantEntity> RAVAGER;
public static final EntityDefinition<AbstractFishEntity> SALMON;
public static final EntityDefinition<SheepEntity> SHEEP;
public static final EntityDefinition<ShulkerEntity> SHULKER;
public static final EntityDefinition<ThrowableEntity> SHULKER_BULLET;
public static final EntityDefinition<MonsterEntity> SILVERFISH;
public static final EntityDefinition<SkeletonEntity> SKELETON;
public static final EntityDefinition<AbstractHorseEntity> SKELETON_HORSE;
public static final EntityDefinition<SlimeEntity> SLIME;
public static final EntityDefinition<ItemedFireballEntity> SMALL_FIREBALL;
public static final EntityDefinition<ThrowableItemEntity> SNOWBALL;
public static final EntityDefinition<SnowGolemEntity> SNOW_GOLEM;
public static final EntityDefinition<AbstractArrowEntity> SPECTRAL_ARROW;
public static final EntityDefinition<SpiderEntity> SPIDER;
public static final EntityDefinition<SquidEntity> SQUID;
public static final EntityDefinition<AbstractSkeletonEntity> STRAY;
public static final EntityDefinition<StriderEntity> STRIDER;
public static final EntityDefinition<ThrowableItemEntity> THROWN_EGG;
public static final EntityDefinition<ThrowableItemEntity> THROWN_ENDERPEARL;
public static final EntityDefinition<ThrowableItemEntity> THROWN_EXP_BOTTLE;
public static final EntityDefinition<ThrownPotionEntity> THROWN_POTION;
public static final EntityDefinition<TropicalFishEntity> TROPICAL_FISH;
public static final EntityDefinition<TurtleEntity> TURTLE;
public static final EntityDefinition<TraderLlamaEntity> TRADER_LLAMA;
public static final EntityDefinition<TridentEntity> TRIDENT;
public static final EntityDefinition<AbstractMerchantEntity> WANDERING_TRADER;
public static final EntityDefinition<RaidParticipantEntity> WITCH;
public static final EntityDefinition<WitherEntity> WITHER;
public static final EntityDefinition<AbstractSkeletonEntity> WITHER_SKELETON;
public static final EntityDefinition<WitherSkullEntity> WITHER_SKULL;
public static final EntityDefinition<WolfEntity> WOLF;
public static final EntityDefinition<VillagerEntity> VILLAGER;
public static final EntityDefinition<VindicatorEntity> VINDICATOR;
public static final EntityDefinition<VexEntity> VEX;
public static final EntityDefinition<ZoglinEntity> ZOGLIN;
public static final EntityDefinition<ZombieEntity> ZOMBIE;
public static final EntityDefinition<AbstractHorseEntity> ZOMBIE_HORSE;
public static final EntityDefinition<ZombieVillagerEntity> ZOMBIE_VILLAGER;
public static final EntityDefinition<ZombifiedPiglinEntity> ZOMBIFIED_PIGLIN;
/**
* Is not sent over the network
*/
public static final EntityDefinition<EnderDragonPartEntity> ENDER_DRAGON_PART;
/**
* Special Bedrock type
*/
public static final EntityDefinition<WitherSkullEntity> WITHER_SKULL_DANGEROUS;
static {
EntityDefinition<Entity> entityBase = EntityDefinition.builder((BaseEntityFactory<Entity>) Entity::new)
.addTranslator(MetadataType.BYTE, Entity::setFlags)
.addTranslator(MetadataType.INT, Entity::setAir) // Air/bubbles
.addTranslator(MetadataType.OPTIONAL_CHAT, Entity::setDisplayName)
.addTranslator(MetadataType.BOOLEAN, Entity::setDisplayNameVisible)
.addTranslator(MetadataType.BOOLEAN, (entity, entityMetadata) -> entity.setFlag(EntityFlag.SILENT, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
.addTranslator(MetadataType.BOOLEAN, Entity::setGravity)
.addTranslator(MetadataType.POSE, Entity::setPose)
.addTranslator(MetadataType.INT, Entity::setFreezing)
.build();
// Extends entity
{
AREA_EFFECT_CLOUD = EntityDefinition.inherited(AreaEffectCloudEntity::new, entityBase)
.type(EntityType.AREA_EFFECT_CLOUD)
.height(0.5f).width(1.0f)
.addTranslator(MetadataType.FLOAT, AreaEffectCloudEntity::setRadius)
.addTranslator(MetadataType.INT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityData.EFFECT_COLOR, entityMetadata.getValue()))
.addTranslator(null) // Waiting
.addTranslator(MetadataType.PARTICLE, AreaEffectCloudEntity::setParticle)
.build();
BOAT = EntityDefinition.inherited(BoatEntity::new, entityBase)
.type(EntityType.BOAT)
.height(0.6f).width(1.6f)
.offset(0.35f)
.addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityData.HURT_TIME, entityMetadata.getValue())) // Time since last hit
.addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityData.HURT_DIRECTION, entityMetadata.getValue())) // Rocking direction
.addTranslator(MetadataType.FLOAT, (boatEntity, entityMetadata) ->
// 'Health' in Bedrock, damage taken in Java - it makes motion in Bedrock
boatEntity.getDirtyMetadata().put(EntityData.HEALTH, 40 - ((int) ((FloatEntityMetadata) entityMetadata).getPrimitiveValue())))
.addTranslator(MetadataType.INT, BoatEntity::setVariant)
.addTranslator(MetadataType.BOOLEAN, BoatEntity::setPaddlingLeft)
.addTranslator(MetadataType.BOOLEAN, BoatEntity::setPaddlingRight)
.addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityData.BOAT_BUBBLE_TIME, entityMetadata.getValue())) // May not actually do anything
.build();
DRAGON_FIREBALL = EntityDefinition.inherited(ItemedFireballEntity::new, entityBase)
.type(EntityType.DRAGON_FIREBALL)
.heightAndWidth(1.0f)
.build();
END_CRYSTAL = EntityDefinition.inherited(EnderCrystalEntity::new, entityBase)
.type(EntityType.END_CRYSTAL)
.heightAndWidth(2.0f)
.addTranslator(MetadataType.OPTIONAL_POSITION, EnderCrystalEntity::setBlockTarget)
.addTranslator(MetadataType.BOOLEAN,
(enderCrystalEntity, entityMetadata) -> enderCrystalEntity.setFlag(EntityFlag.SHOW_BOTTOM, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue())) // There is a base located on the ender crystal
.build();
EXPERIENCE_ORB = EntityDefinition.inherited((ExperienceOrbEntityFactory) ExpOrbEntity::new, entityBase)
.type(EntityType.EXPERIENCE_ORB)
.identifier("minecraft:xp_orb")
.build();
EVOKER_FANGS = EntityDefinition.inherited(entityBase.factory(), entityBase)
.type(EntityType.EVOKER_FANGS)
.height(0.8f).width(0.5f)
.identifier("minecraft:evocation_fang")
.build();
EYE_OF_ENDER = EntityDefinition.inherited(Entity::new, entityBase)
.type(EntityType.EYE_OF_ENDER)
.heightAndWidth(0.25f)
.identifier("minecraft:eye_of_ender_signal")
.build();
FALLING_BLOCK = EntityDefinition.<FallingBlockEntity>inherited(null, entityBase)
.type(EntityType.FALLING_BLOCK)
.heightAndWidth(0.98f)
.addTranslator(null) // "start block position"
.build();
FIREBALL = EntityDefinition.inherited(ItemedFireballEntity::new, entityBase)
.type(EntityType.FIREBALL)
.heightAndWidth(1.0f)
.build();
FIREWORK_ROCKET = EntityDefinition.inherited(FireworkEntity::new, entityBase)
.type(EntityType.FIREWORK_ROCKET)
.heightAndWidth(0.25f)
.identifier("minecraft:fireworks_rocket")
.addTranslator(MetadataType.ITEM, FireworkEntity::setFireworkItem)
.addTranslator(MetadataType.OPTIONAL_VARINT, FireworkEntity::setPlayerGliding)
.addTranslator(null) // Shot at angle
.build();
FISHING_BOBBER = EntityDefinition.<FishingHookEntity>inherited(null, entityBase)
.type(EntityType.FISHING_BOBBER)
.identifier("minecraft:fishing_book")
.addTranslator(MetadataType.INT, FishingHookEntity::setHookedEntity)
.build();
ITEM = EntityDefinition.inherited(ItemEntity::new, entityBase)
.type(EntityType.ITEM)
.heightAndWidth(0.25f)
.offset(0.125f)
.addTranslator(MetadataType.ITEM, ItemEntity::setItem)
.build();
LEASH_KNOT = EntityDefinition.inherited(LeashKnotEntity::new, entityBase)
.type(EntityType.LEASH_KNOT)
.height(0.5f).width(0.375f)
.build();
LIGHTNING_BOLT = EntityDefinition.inherited(LightningEntity::new, entityBase)
.type(EntityType.LIGHTNING_BOLT)
.build();
LLAMA_SPIT = EntityDefinition.inherited(ThrowableEntity::new, entityBase)
.type(EntityType.LLAMA_SPIT)
.heightAndWidth(0.25f)
.build();
PAINTING = EntityDefinition.inherited((PaintingEntityFactory) PaintingEntity::new, entityBase)
.type(EntityType.PAINTING)
.build();
PRIMED_TNT = EntityDefinition.inherited(TNTEntity::new, entityBase)
.type(EntityType.PRIMED_TNT)
.heightAndWidth(0.98f)
.identifier("minecraft:tnt")
.addTranslator(MetadataType.INT, TNTEntity::setFuseLength)
.build();
SHULKER_BULLET = EntityDefinition.inherited(ThrowableEntity::new, entityBase)
.type(EntityType.SHULKER_BULLET)
.heightAndWidth(0.3125f)
.build();
SMALL_FIREBALL = EntityDefinition.inherited(ItemedFireballEntity::new, entityBase)
.type(EntityType.SMALL_FIREBALL)
.heightAndWidth(0.3125f)
.build();
SNOWBALL = EntityDefinition.inherited(ThrowableItemEntity::new, entityBase)
.type(EntityType.SNOWBALL)
.heightAndWidth(0.25f)
.build();
THROWN_ENDERPEARL = EntityDefinition.inherited(ThrowableItemEntity::new, entityBase)
.type(EntityType.THROWN_ENDERPEARL)
.heightAndWidth(0.25f)
.identifier("minecraft:ender_pearl")
.build();
THROWN_EGG = EntityDefinition.inherited(ThrowableItemEntity::new, entityBase)
.type(EntityType.THROWN_EGG)
.heightAndWidth(0.25f)
.identifier("minecraft:egg")
.build();
THROWN_EXP_BOTTLE = EntityDefinition.inherited(ThrowableItemEntity::new, entityBase)
.type(EntityType.THROWN_EXP_BOTTLE)
.heightAndWidth(0.25f)
.identifier("minecraft:xp_bottle")
.build();
THROWN_POTION = EntityDefinition.inherited(ThrownPotionEntity::new, entityBase)
.type(EntityType.THROWN_POTION)
.heightAndWidth(0.25f)
.identifier("minecraft:splash_potion")
.addTranslator(MetadataType.ITEM, ThrownPotionEntity::setPotion)
.build();
EntityDefinition<AbstractArrowEntity> abstractArrowBase = EntityDefinition.inherited(AbstractArrowEntity::new, entityBase)
.addTranslator(MetadataType.BYTE, AbstractArrowEntity::setArrowFlags)
.addTranslator(null) // "Piercing level"
.build();
ARROW = EntityDefinition.inherited(TippedArrowEntity::new, abstractArrowBase)
.type(EntityType.ARROW)
.heightAndWidth(0.25f)
.addTranslator(MetadataType.INT, TippedArrowEntity::setPotionEffectColor)
.build();
SPECTRAL_ARROW = EntityDefinition.inherited(abstractArrowBase.factory(), abstractArrowBase)
.type(EntityType.SPECTRAL_ARROW)
.heightAndWidth(0.25f)
.identifier("minecraft:arrow")
.build();
TRIDENT = EntityDefinition.inherited(TridentEntity::new, abstractArrowBase) // TODO remove class
.type(EntityType.TRIDENT)
.identifier("minecraft:thrown_trident")
.addTranslator(null) // Loyalty
.addTranslator(MetadataType.BOOLEAN, (tridentEntity, entityMetadata) -> tridentEntity.setFlag(EntityFlag.ENCHANTED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
.build();
// Item frames are handled differently as they are blocks, not items, in Bedrock
ITEM_FRAME = EntityDefinition.<ItemFrameEntity>inherited(null, entityBase)
.type(EntityType.ITEM_FRAME)
.addTranslator(MetadataType.ITEM, ItemFrameEntity::setItemInFrame)
.addTranslator(MetadataType.INT, ItemFrameEntity::setItemRotation)
.build();
GLOW_ITEM_FRAME = EntityDefinition.inherited(ITEM_FRAME.factory(), ITEM_FRAME)
.type(EntityType.GLOW_ITEM_FRAME)
.build();
MINECART = EntityDefinition.inherited(MinecartEntity::new, entityBase)
.type(EntityType.MINECART)
.height(0.7f).width(0.98f)
.offset(0.35f)
.addTranslator(MetadataType.INT, (minecartEntity, entityMetadata) -> minecartEntity.getDirtyMetadata().put(EntityData.HEALTH, entityMetadata.getValue()))
.addTranslator(MetadataType.INT, (minecartEntity, entityMetadata) -> minecartEntity.getDirtyMetadata().put(EntityData.HURT_DIRECTION, entityMetadata.getValue())) // Direction in which the minecart is shaking
.addTranslator(MetadataType.FLOAT, (minecartEntity, entityMetadata) ->
// Power in Java, time in Bedrock
minecartEntity.getDirtyMetadata().put(EntityData.HURT_TIME, Math.min((int) ((FloatEntityMetadata) entityMetadata).getPrimitiveValue(), 15)))
.addTranslator(MetadataType.INT, MinecartEntity::setCustomBlock)
.addTranslator(MetadataType.INT, MinecartEntity::setCustomBlockOffset)
.addTranslator(MetadataType.BOOLEAN, MinecartEntity::setShowCustomBlock)
.build();
MINECART_CHEST = EntityDefinition.inherited(MINECART.factory(), MINECART)
.type(EntityType.MINECART_CHEST)
.identifier("minecraft:chest_minecart")
.build();
MINECART_COMMAND_BLOCK = EntityDefinition.inherited(CommandBlockMinecartEntity::new, MINECART)
.type(EntityType.MINECART_COMMAND_BLOCK)
.identifier("minecraft:command_block_minecart")
.addTranslator(MetadataType.STRING, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityData.COMMAND_BLOCK_COMMAND, entityMetadata.getValue()))
.addTranslator(MetadataType.CHAT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityData.COMMAND_BLOCK_LAST_OUTPUT, MessageTranslator.convertMessage(entityMetadata.getValue())))
.build();
MINECART_FURNACE = EntityDefinition.inherited(FurnaceMinecartEntity::new, MINECART)
.type(EntityType.MINECART_FURNACE)
.identifier("minecraft:minecart")
.addTranslator(MetadataType.BOOLEAN, FurnaceMinecartEntity::setHasFuel)
.build();
MINECART_HOPPER = EntityDefinition.inherited(MINECART.factory(), MINECART)
.type(EntityType.MINECART_HOPPER)
.identifier("minecraft:hopper_minecart")
.build();
MINECART_SPAWNER = EntityDefinition.inherited(SpawnerMinecartEntity::new, MINECART)
.type(EntityType.MINECART_SPAWNER)
.identifier("minecraft:minecart")
.build();
MINECART_TNT = EntityDefinition.inherited(MINECART.factory(), MINECART)
.type(EntityType.MINECART_TNT)
.identifier("minecraft:tnt_minecart")
.build();
WITHER_SKULL = EntityDefinition.inherited(WitherSkullEntity::new, entityBase)
.type(EntityType.WITHER_SKULL)
.heightAndWidth(0.3125f)
.addTranslator(MetadataType.BOOLEAN, WitherSkullEntity::setDangerous)
.build();
WITHER_SKULL_DANGEROUS = EntityDefinition.inherited(WITHER_SKULL.factory(), WITHER_SKULL)
.build(false);
}
EntityDefinition<LivingEntity> livingEntityBase = EntityDefinition.inherited(LivingEntity::new, entityBase)
.addTranslator(MetadataType.BYTE, LivingEntity::setLivingEntityFlags)
.addTranslator(MetadataType.FLOAT, LivingEntity::setHealth)
.addTranslator(MetadataType.INT,
(livingEntity, entityMetadata) -> livingEntity.getDirtyMetadata().put(EntityData.EFFECT_COLOR, entityMetadata.getValue()))
.addTranslator(MetadataType.BOOLEAN,
(livingEntity, entityMetadata) -> livingEntity.getDirtyMetadata().put(EntityData.EFFECT_AMBIENT, (byte) (((BooleanEntityMetadata) entityMetadata).getPrimitiveValue() ? 1 : 0)))
.addTranslator(null) // Arrow count
.addTranslator(null) // Stinger count
.addTranslator(MetadataType.OPTIONAL_POSITION, LivingEntity::setBedPosition)
.build();
ARMOR_STAND = EntityDefinition.inherited(ArmorStandEntity::new, livingEntityBase)
.type(EntityType.ARMOR_STAND)
.height(1.975f).width(0.5f)
.addTranslator(MetadataType.BYTE, ArmorStandEntity::setArmorStandFlags)
.addTranslator(MetadataType.ROTATION, ArmorStandEntity::setHeadRotation)
.addTranslator(MetadataType.ROTATION, ArmorStandEntity::setBodyRotation)
.addTranslator(MetadataType.ROTATION, ArmorStandEntity::setLeftArmRotation)
.addTranslator(MetadataType.ROTATION, ArmorStandEntity::setRightArmRotation)
.addTranslator(MetadataType.ROTATION, ArmorStandEntity::setLeftLegRotation)
.addTranslator(MetadataType.ROTATION, ArmorStandEntity::setRightLegRotation)
.build();
PLAYER = EntityDefinition.<PlayerEntity>inherited(null, livingEntityBase)
.type(EntityType.PLAYER)
.height(1.8f).width(0.6f)
.offset(1.62f)
.addTranslator(MetadataType.FLOAT, PlayerEntity::setAbsorptionHearts)
.addTranslator(null) // Player score
.addTranslator(MetadataType.BYTE, PlayerEntity::setSkinVisibility)
.addTranslator(null) // Player main hand
.addTranslator(MetadataType.NBT_TAG, PlayerEntity::setLeftParrot)
.addTranslator(MetadataType.NBT_TAG, PlayerEntity::setRightParrot)
.build();
EntityDefinition<MobEntity> mobEntityBase = EntityDefinition.inherited(MobEntity::new, livingEntityBase)
.addTranslator(MetadataType.BYTE, MobEntity::setMobFlags)
.build();
// Extends mob
{
BAT = EntityDefinition.inherited(BatEntity::new, mobEntityBase)
.type(EntityType.BAT)
.height(0.9f).width(0.5f)
.addTranslator(MetadataType.BYTE, BatEntity::setBatFlags)
.build();
BLAZE = EntityDefinition.inherited(BlazeEntity::new, mobEntityBase)
.type(EntityType.BLAZE)
.height(1.8f).width(0.6f)
.addTranslator(MetadataType.BYTE, BlazeEntity::setBlazeFlags)
.build();
CAVE_SPIDER = EntityDefinition.inherited(MonsterEntity::new, mobEntityBase)
.type(EntityType.CAVE_SPIDER)
.height(0.5f).width(0.7f)
.build();
CREEPER = EntityDefinition.inherited(CreeperEntity::new, mobEntityBase)
.type(EntityType.CREEPER)
.height(1.7f).width(0.6f)
.offset(1.62f)
.addTranslator(MetadataType.INT, CreeperEntity::setSwelling)
.addTranslator(MetadataType.BOOLEAN, (entity, entityMetadata) -> entity.setFlag(EntityFlag.POWERED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
.addTranslator(MetadataType.BOOLEAN, CreeperEntity::setIgnited)
.build();
DOLPHIN = EntityDefinition.inherited(WaterEntity::new, mobEntityBase)
.type(EntityType.DOLPHIN)
.height(0.6f).width(0.9f)
//TODO check
.addTranslator(null) // treasure position
.addTranslator(null) // "got fish"
.addTranslator(null) // "moistness level"
.build();
ENDERMAN = EntityDefinition.inherited(EndermanEntity::new, mobEntityBase)
.type(EntityType.ENDERMAN)
.height(2.9f).width(0.6f)
.addTranslator(MetadataType.BLOCK_STATE, EndermanEntity::setCarriedBlock)
.addTranslator(MetadataType.BOOLEAN, EndermanEntity::setScreaming)
.addTranslator(MetadataType.BOOLEAN, EndermanEntity::setAngry)
.build();
ENDERMITE = EntityDefinition.inherited(MonsterEntity::new, mobEntityBase)
.type(EntityType.ENDERMITE)
.height(0.3f).width(0.4f)
.build();
ENDER_DRAGON = EntityDefinition.inherited(EnderDragonEntity::new, mobEntityBase)
.type(EntityType.ENDER_DRAGON)
.addTranslator(MetadataType.INT, EnderDragonEntity::setPhase)
.build();
GHAST = EntityDefinition.inherited(GhastEntity::new, mobEntityBase)
.type(EntityType.GHAST)
.heightAndWidth(4.0f)
.addTranslator(MetadataType.BOOLEAN, GhastEntity::setGhastAttacking)
.build();
GIANT = EntityDefinition.inherited(GiantEntity::new, mobEntityBase)
.type(EntityType.GIANT)
.height(1.8f).width(1.6f)
.offset(1.62f)
.identifier("minecraft:zombie")
.build();
IRON_GOLEM = EntityDefinition.inherited(IronGolemEntity::new, mobEntityBase)
.type(EntityType.IRON_GOLEM)
.height(2.7f).width(1.4f)
.addTranslator(null) // "is player created", which doesn't seem to do anything clientside
.build();
PHANTOM = EntityDefinition.inherited(PhantomEntity::new, mobEntityBase)
.type(EntityType.PHANTOM)
.height(0.5f).width(0.9f)
.offset(0.6f)
.addTranslator(MetadataType.INT, PhantomEntity::setPhantomScale)
.build();
SILVERFISH = EntityDefinition.inherited(MonsterEntity::new, mobEntityBase)
.type(EntityType.SILVERFISH)
.height(0.3f).width(0.4f)
.build();
SHULKER = EntityDefinition.inherited(ShulkerEntity::new, mobEntityBase)
.type(EntityType.SHULKER)
.heightAndWidth(1f)
.addTranslator(MetadataType.DIRECTION, ShulkerEntity::setAttachedFace)
.addTranslator(MetadataType.BYTE, ShulkerEntity::setShulkerHeight)
.addTranslator(MetadataType.BYTE, ShulkerEntity::setShulkerColor)
.build();
SKELETON = EntityDefinition.inherited(SkeletonEntity::new, mobEntityBase)
.type(EntityType.SKELETON)
.height(1.8f).width(0.6f)
.offset(1.62f)
.addTranslator(MetadataType.BOOLEAN, SkeletonEntity::setConvertingToStray)
.build();
SNOW_GOLEM = EntityDefinition.inherited(SnowGolemEntity::new, mobEntityBase)
.type(EntityType.SNOW_GOLEM)
.height(1.9f).width(0.7f)
.addTranslator(MetadataType.BYTE, SnowGolemEntity::setSnowGolemFlags)
.build();
SPIDER = EntityDefinition.inherited(SpiderEntity::new, mobEntityBase)
.type(EntityType.SPIDER)
.height(0.9f).width(1.4f)
.offset(1f)
.addTranslator(MetadataType.BYTE, SpiderEntity::setSpiderFlags)
.build();
SQUID = EntityDefinition.inherited(SquidEntity::new, mobEntityBase)
.type(EntityType.SQUID)
.heightAndWidth(0.8f)
.build();
STRAY = EntityDefinition.inherited(AbstractSkeletonEntity::new, mobEntityBase)
.type(EntityType.STRAY)
.height(1.8f).width(0.6f)
.offset(1.62f)
.build();
VEX = EntityDefinition.inherited(VexEntity::new, mobEntityBase)
.type(EntityType.VEX)
.height(0.8f).width(0.4f)
.addTranslator(MetadataType.BYTE, VexEntity::setVexFlags)
.build();
WITHER = EntityDefinition.inherited(WitherEntity::new, mobEntityBase)
.type(EntityType.WITHER)
.height(3.5f).width(0.9f)
.addTranslator(MetadataType.INT, WitherEntity::setTarget1)
.addTranslator(MetadataType.INT, WitherEntity::setTarget2)
.addTranslator(MetadataType.INT, WitherEntity::setTarget3)
.addTranslator(MetadataType.INT, WitherEntity::setInvulnerableTicks)
.build();
WITHER_SKELETON = EntityDefinition.inherited(AbstractSkeletonEntity::new, mobEntityBase)
.type(EntityType.WITHER_SKELETON)
.height(2.4f).width(0.7f)
.build();
ZOGLIN = EntityDefinition.inherited(ZoglinEntity::new, mobEntityBase)
.type(EntityType.ZOGLIN)
.height(1.4f).width(1.3965f)
.addTranslator(MetadataType.BOOLEAN, ZoglinEntity::setBaby)
.build();
ZOMBIE = EntityDefinition.inherited(ZombieEntity::new, mobEntityBase)
.type(EntityType.ZOMBIE)
.height(1.8f).width(0.6f)
.offset(1.62f)
.addTranslator(MetadataType.BOOLEAN, ZombieEntity::setZombieBaby)
.addTranslator(null) // "set special type", doesn't do anything
.addTranslator(MetadataType.BOOLEAN, ZombieEntity::setConvertingToDrowned)
.build();
ZOMBIE_VILLAGER = EntityDefinition.inherited(ZombieVillagerEntity::new, ZOMBIE)
.type(EntityType.ZOMBIE_VILLAGER)
.height(1.8f).width(0.6f)
.offset(1.62f)
.identifier("minecraft:zombie_villager_v2")
.addTranslator(MetadataType.BOOLEAN, ZombieVillagerEntity::setTransforming)
.addTranslator(MetadataType.VILLAGER_DATA, ZombieVillagerEntity::setZombieVillagerData)
.build();
ZOMBIFIED_PIGLIN = EntityDefinition.inherited(ZombifiedPiglinEntity::new, ZOMBIE) //TODO test how zombie entity metadata is handled?
.type(EntityType.ZOMBIFIED_PIGLIN)
.height(1.95f).width(0.6f)
.offset(1.62f)
.identifier("minecraft:zombie_pigman")
.build();
DROWNED = EntityDefinition.inherited(ZOMBIE.factory(), ZOMBIE)
.type(EntityType.DROWNED)
.height(1.95f).width(0.6f)
.build();
HUSK = EntityDefinition.inherited(ZOMBIE.factory(), ZOMBIE)
.type(EntityType.HUSK)
.build();
GUARDIAN = EntityDefinition.inherited(GuardianEntity::new, mobEntityBase)
.type(EntityType.GUARDIAN)
.heightAndWidth(0.85f)
.addTranslator(null) // Moving //TODO
.addTranslator(MetadataType.INT, GuardianEntity::setGuardianTarget)
.build();
ELDER_GUARDIAN = EntityDefinition.inherited(ElderGuardianEntity::new, GUARDIAN)
.type(EntityType.ELDER_GUARDIAN)
.heightAndWidth(1.9975f)
.build();
SLIME = EntityDefinition.inherited(SlimeEntity::new, mobEntityBase)
.type(EntityType.SLIME)
.heightAndWidth(0.51f)
.addTranslator(MetadataType.INT, SlimeEntity::setScale)
.build();
MAGMA_CUBE = EntityDefinition.inherited(MagmaCubeEntity::new, SLIME)
.type(EntityType.MAGMA_CUBE)
.build();
EntityDefinition<AbstractFishEntity> abstractFishEntityBase = EntityDefinition.inherited(AbstractFishEntity::new, mobEntityBase)
.addTranslator(null) // From bucket
.build();
COD = EntityDefinition.inherited(abstractFishEntityBase.factory(), abstractFishEntityBase)
.type(EntityType.COD)
.height(0.25f).width(0.5f)
.build();
PUFFERFISH = EntityDefinition.inherited(PufferFishEntity::new, abstractFishEntityBase)
.type(EntityType.PUFFERFISH)
.heightAndWidth(0.7f)
.addTranslator(MetadataType.INT, PufferFishEntity::setPufferfishSize)
.build();
SALMON = EntityDefinition.inherited(abstractFishEntityBase.factory(), abstractFishEntityBase)
.type(EntityType.SALMON)
.height(0.5f).width(0.7f)
.build();
TROPICAL_FISH = EntityDefinition.inherited(TropicalFishEntity::new, abstractFishEntityBase)
.type(EntityType.TROPICAL_FISH)
.heightAndWidth(0.6f)
.identifier("minecraft:tropicalfish")
.addTranslator(MetadataType.INT, TropicalFishEntity::setFishVariant)
.build();
EntityDefinition<BasePiglinEntity> abstractPiglinEntityBase = EntityDefinition.inherited(BasePiglinEntity::new, mobEntityBase)
.addTranslator(MetadataType.BOOLEAN, BasePiglinEntity::setImmuneToZombification)
.build();
PIGLIN = EntityDefinition.inherited(PiglinEntity::new, abstractPiglinEntityBase)
.type(EntityType.PIGLIN)
.height(1.95f).width(0.6f)
.addTranslator(MetadataType.BOOLEAN, PiglinEntity::setBaby)
.addTranslator(MetadataType.BOOLEAN, PiglinEntity::setChargingCrossbow)
.addTranslator(MetadataType.BOOLEAN, PiglinEntity::setDancing)
.build();
PIGLIN_BRUTE = EntityDefinition.inherited(abstractPiglinEntityBase.factory(), abstractPiglinEntityBase)
.type(EntityType.PIGLIN_BRUTE)
.height(1.95f).width(0.6f)
.build();
GLOW_SQUID = EntityDefinition.inherited(GlowSquidEntity::new, SQUID)
.type(EntityType.GLOW_SQUID)
.addTranslator(null) // Set dark ticks remaining, possible TODO
.build();
EntityDefinition<RaidParticipantEntity> raidParticipantEntityBase = EntityDefinition.inherited(RaidParticipantEntity::new, mobEntityBase)
.addTranslator(null) // Celebrating //TODO
.build();
EntityDefinition<SpellcasterIllagerEntity> spellcasterEntityBase = EntityDefinition.inherited(SpellcasterIllagerEntity::new, raidParticipantEntityBase)
.addTranslator(MetadataType.BYTE, SpellcasterIllagerEntity::setSpellType)
.build();
EVOKER = EntityDefinition.inherited(spellcasterEntityBase.factory(), spellcasterEntityBase)
.type(EntityType.EVOKER)
.height(1.95f).width(0.6f)
.identifier("minecraft:evocation_illager")
.build();
ILLUSIONER = EntityDefinition.inherited(spellcasterEntityBase.factory(), spellcasterEntityBase)
.type(EntityType.ILLUSIONER)
.height(1.95f).width(0.6f)
.identifier("minecraft:evocation_illager")
.build();
PILLAGER = EntityDefinition.inherited(PillagerEntity::new, raidParticipantEntityBase)
.type(EntityType.PILLAGER)
.height(1.8f).width(0.6f)
.offset(1.62f)
.addTranslator(null) // Charging; doesn't have an equivalent on Bedrock //TODO check
.build();
RAVAGER = EntityDefinition.inherited(raidParticipantEntityBase.factory(), raidParticipantEntityBase)
.type(EntityType.RAVAGER)
.height(1.9f).width(1.2f)
.build();
VINDICATOR = EntityDefinition.inherited(VindicatorEntity::new, raidParticipantEntityBase)
.type(EntityType.VINDICATOR)
.height(1.8f).width(0.6f)
.offset(1.62f)
.build();
WITCH = EntityDefinition.inherited(raidParticipantEntityBase.factory(), raidParticipantEntityBase)
.type(EntityType.WITCH)
.height(1.8f).width(0.6f)
.offset(1.62f)
.addTranslator(null) // Using item
.build();
}
EntityDefinition<AgeableEntity> ageableEntityBase = EntityDefinition.inherited(AgeableEntity::new, mobEntityBase)
.addTranslator(MetadataType.BOOLEAN, AgeableEntity::setBaby)
.build();
// Extends ageable
{
AXOLOTL = EntityDefinition.inherited(AxolotlEntity::new, ageableEntityBase)
.type(EntityType.AXOLOTL)
.height(0.42f).width(0.7f)
.addTranslator(MetadataType.INT, AxolotlEntity::setVariant)
.addTranslator(MetadataType.BOOLEAN, AxolotlEntity::setPlayingDead)
.addTranslator(null) // From bucket
.build();
BEE = EntityDefinition.inherited(BeeEntity::new, ageableEntityBase)
.type(EntityType.BEE)
.heightAndWidth(0.6f)
.addTranslator(MetadataType.BYTE, BeeEntity::setBeeFlags)
.addTranslator(MetadataType.INT, BeeEntity::setAngerTime)
.build();
CHICKEN = EntityDefinition.inherited(ChickenEntity::new, ageableEntityBase)
.type(EntityType.CHICKEN)
.height(0.7f).width(0.4f)
.build();
COW = EntityDefinition.inherited(AnimalEntity::new, ageableEntityBase)
.type(EntityType.COW)
.height(1.4f).width(0.9f)
.build();
FOX = EntityDefinition.inherited(FoxEntity::new, ageableEntityBase)
.type(EntityType.FOX)
.height(0.5f).width(1.25f)
.addTranslator(MetadataType.INT, FoxEntity::setFoxVariant)
.addTranslator(MetadataType.BYTE, FoxEntity::setFoxFlags)
.build();
HOGLIN = EntityDefinition.inherited(HoglinEntity::new, ageableEntityBase)
.type(EntityType.HOGLIN)
.height(1.4f).width(1.3965f)
.addTranslator(MetadataType.BOOLEAN, HoglinEntity::setImmuneToZombification)
.build();
GOAT = EntityDefinition.inherited(GoatEntity::new, ageableEntityBase)
.type(EntityType.GOAT)
.height(1.3f).width(0.9f)
.addTranslator(MetadataType.BOOLEAN, GoatEntity::setScreamer)
.build();
MOOSHROOM = EntityDefinition.inherited(MooshroomEntity::new, ageableEntityBase) // TODO remove class
.type(EntityType.MOOSHROOM)
.height(1.4f).width(0.9f)
.addTranslator(MetadataType.STRING, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityData.VARIANT, entityMetadata.getValue().equals("brown") ? 1 : 0))
.build();
OCELOT = EntityDefinition.inherited(OcelotEntity::new, ageableEntityBase)
.type(EntityType.OCELOT)
.height(0.35f).width(0.3f)
.addTranslator(MetadataType.BOOLEAN, (ocelotEntity, entityMetadata) -> ocelotEntity.setFlag(EntityFlag.TRUSTING, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
.build();
PANDA = EntityDefinition.inherited(PandaEntity::new, ageableEntityBase)
.type(EntityType.PANDA)
.height(1.25f).width(1.125f)
.addTranslator(null) // Unhappy counter
.addTranslator(null) // Sneeze counter
.addTranslator(MetadataType.INT, PandaEntity::setEatingCounter)
.addTranslator(MetadataType.BYTE, PandaEntity::setMainGene)
.addTranslator(MetadataType.BYTE, PandaEntity::setHiddenGene)
.addTranslator(MetadataType.BYTE, PandaEntity::setPandaFlags)
.build();
PIG = EntityDefinition.inherited(PigEntity::new, ageableEntityBase)
.type(EntityType.PIG)
.heightAndWidth(0.9f)
.addTranslator(MetadataType.BOOLEAN, (pigEntity, entityMetadata) -> pigEntity.setFlag(EntityFlag.SADDLED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
.addTranslator(null) // Boost time
.build();
POLAR_BEAR = EntityDefinition.inherited(PolarBearEntity::new, ageableEntityBase)
.type(EntityType.POLAR_BEAR)
.height(1.4f).width(1.3f)
.addTranslator(MetadataType.BOOLEAN, (entity, entityMetadata) -> entity.setFlag(EntityFlag.STANDING, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
.build();
RABBIT = EntityDefinition.inherited(RabbitEntity::new, ageableEntityBase)
.type(EntityType.RABBIT)
.height(0.5f).width(0.4f)
.addTranslator(MetadataType.INT, RabbitEntity::setRabbitVariant)
.build();
SHEEP = EntityDefinition.inherited(SheepEntity::new, ageableEntityBase)
.type(EntityType.SHEEP)
.heightAndWidth(0.9f)
.addTranslator(MetadataType.BYTE, SheepEntity::setSheepFlags)
.build();
STRIDER = EntityDefinition.inherited(StriderEntity::new, ageableEntityBase)
.type(EntityType.STRIDER)
.height(1.7f).width(0.9f)
.addTranslator(null) // Boost time
.addTranslator(MetadataType.BOOLEAN, StriderEntity::setCold)
.addTranslator(MetadataType.BOOLEAN, StriderEntity::setSaddled)
.build();
TURTLE = EntityDefinition.inherited(TurtleEntity::new, ageableEntityBase)
.type(EntityType.TURTLE)
.height(0.4f).width(1.2f)
.addTranslator(null) // Home position
.addTranslator(MetadataType.BOOLEAN, TurtleEntity::setPregnant)
.addTranslator(MetadataType.BOOLEAN, TurtleEntity::setLayingEgg)
.addTranslator(null) // Travel position
.addTranslator(null) // Going home
.addTranslator(null) // Travelling
.build();
EntityDefinition<AbstractMerchantEntity> abstractVillagerEntityBase = EntityDefinition.inherited(AbstractMerchantEntity::new, ageableEntityBase)
.addTranslator(null) // Unhappy ticks
.build();
VILLAGER = EntityDefinition.inherited(VillagerEntity::new, abstractVillagerEntityBase)
.type(EntityType.VILLAGER)
.height(1.8f).width(0.6f)
.offset(1.62f)
.identifier("minecraft:villager_v2")
.addTranslator(MetadataType.VILLAGER_DATA, VillagerEntity::setVillagerData)
.build();
WANDERING_TRADER = EntityDefinition.inherited(abstractVillagerEntityBase.factory(), abstractVillagerEntityBase)
.type(EntityType.WANDERING_TRADER)
.height(1.8f).width(0.6f)
.offset(1.62f)
.build();
}
// Horses
{
EntityDefinition<AbstractHorseEntity> abstractHorseEntityBase = EntityDefinition.inherited(AbstractHorseEntity::new, ageableEntityBase)
.addTranslator(MetadataType.BYTE, AbstractHorseEntity::setHorseFlags)
.addTranslator(null) // UUID of owner
.build();
HORSE = EntityDefinition.inherited(HorseEntity::new, abstractHorseEntityBase)
.type(EntityType.HORSE)
.height(1.6f).width(1.3965f)
.addTranslator(MetadataType.INT, HorseEntity::setHorseVariant)
.build();
SKELETON_HORSE = EntityDefinition.inherited(abstractHorseEntityBase.factory(), abstractHorseEntityBase)
.type(EntityType.SKELETON_HORSE)
.height(1.6f).width(1.3965f)
.build();
ZOMBIE_HORSE = EntityDefinition.inherited(abstractHorseEntityBase.factory(), abstractHorseEntityBase)
.type(EntityType.ZOMBIE_HORSE)
.height(1.6f).width(1.3965f)
.build();
EntityDefinition<ChestedHorseEntity> chestedHorseEntityBase = EntityDefinition.inherited(ChestedHorseEntity::new, abstractHorseEntityBase)
.addTranslator(MetadataType.BOOLEAN, (horseEntity, entityMetadata) -> horseEntity.setFlag(EntityFlag.CHESTED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
.build();
DONKEY = EntityDefinition.inherited(chestedHorseEntityBase.factory(), chestedHorseEntityBase)
.type(EntityType.DONKEY)
.height(1.6f).width(1.3965f)
.build();
MULE = EntityDefinition.inherited(chestedHorseEntityBase.factory(), chestedHorseEntityBase)
.type(EntityType.MULE)
.height(1.6f).width(1.3965f)
.build();
LLAMA = EntityDefinition.inherited(LlamaEntity::new, chestedHorseEntityBase)
.type(EntityType.LLAMA)
.height(1.87f).width(0.9f)
.addTranslator(MetadataType.INT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityData.STRENGTH, entityMetadata.getValue()))
.addTranslator(MetadataType.INT, LlamaEntity::setCarpetedColor)
.addTranslator(MetadataType.INT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityData.VARIANT, entityMetadata.getValue()))
.build();
TRADER_LLAMA = EntityDefinition.inherited(TraderLlamaEntity::new, LLAMA)
.type(EntityType.TRADER_LLAMA)
.identifier("minecraft:llama")
.build();
}
EntityDefinition<TameableEntity> tameableEntityBase = EntityDefinition.inherited(TameableEntity::new, ageableEntityBase)
.addTranslator(MetadataType.BYTE, TameableEntity::setTameableFlags)
.addTranslator(MetadataType.OPTIONAL_UUID, TameableEntity::setOwner)
.build();
CAT = EntityDefinition.inherited(CatEntity::new, tameableEntityBase)
.type(EntityType.CAT)
.height(0.35f).width(0.3f)
.addTranslator(MetadataType.INT, CatEntity::setCatVariant)
.addTranslator(MetadataType.BOOLEAN, CatEntity::setResting)
.addTranslator(null) // "resting state one" //TODO
.addTranslator(MetadataType.INT, CatEntity::setCollarColor)
.build();
PARROT = EntityDefinition.inherited(ParrotEntity::new, tameableEntityBase)
.type(EntityType.PARROT)
.height(0.9f).width(0.5f)
.addTranslator(MetadataType.INT, (parrotEntity, entityMetadata) -> parrotEntity.getDirtyMetadata().put(EntityData.VARIANT, entityMetadata.getValue())) // Parrot color
.build();
WOLF = EntityDefinition.inherited(WolfEntity::new, tameableEntityBase)
.type(EntityType.WOLF)
.height(0.85f).width(0.6f)
// "Begging" on wiki.vg, "Interested" in Nukkit - the tilt of the head
.addTranslator(MetadataType.BOOLEAN, (wolfEntity, entityMetadata) -> wolfEntity.setFlag(EntityFlag.INTERESTED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
.addTranslator(MetadataType.INT, WolfEntity::setCollarColor)
.addTranslator(MetadataType.INT, WolfEntity::setWolfAngerTime)
.build();
// As of 1.18 these don't track entity data at all
ENDER_DRAGON_PART = EntityDefinition.<EnderDragonPartEntity>builder(null)
.identifier("minecraft:armor_stand") // Emulated
.build();
Registries.JAVA_ENTITY_IDENTIFIERS.get().put("minecraft:marker", null); // We don't need an entity definition for this as it is never sent over the network
}
public static void init() {
// no-op
}
private EntityDefinitions() {
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
import java.util.function.BiConsumer;
/**
* Translates a given Java {@link EntityMetadata} into a similar/same construct for Bedrock
*/
public record EntityMetadataTranslator<E extends Entity, T, EM extends EntityMetadata<T, ? extends MetadataType<T>>>(
MetadataType<T> acceptedType,
BiConsumer<E, EM> translateFunction) {
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.network.session.GeyserSession;
public class ExpOrbEntity extends Entity {
private final int amount;
public ExpOrbEntity(GeyserSession session, int amount, long entityId, long geyserId, Vector3f position) {
super(session, entityId, geyserId, null, EntityDefinitions.EXPERIENCE_ORB, position, Vector3f.ZERO, 0, 0, 0);
this.amount = amount;
}
@Override
protected void initializeMetadata() {
super.initializeMetadata();
this.dirtyMetadata.put(EntityData.EXPERIENCE_VALUE, amount);
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class FallingBlockEntity extends Entity {
private final int javaId;
public FallingBlockEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, Vector3f position, Vector3f motion, float yaw, float pitch, int javaId) {
super(session, entityId, geyserId, uuid, EntityDefinitions.FALLING_BLOCK, position, motion, yaw, pitch, 0f);
this.javaId = javaId;
}
@Override
protected void initializeMetadata() {
super.initializeMetadata();
this.dirtyMetadata.put(EntityData.VARIANT, session.getBlockMappings().getBedrockBlockId(javaId));
}
@Override
public void setGravity(BooleanEntityMetadata entityMetadata) {
super.setGravity(entityMetadata);
// Set the NO_AI flag based on the no gravity flag to prevent movement
setFlag(EntityFlag.NO_AI, entityMetadata.getPrimitiveValue());
}
}

View file

@ -0,0 +1,160 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.nbt.NbtType;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket;
import org.geysermc.connector.entity.player.PlayerEntity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.FireworkColor;
import org.geysermc.connector.utils.MathUtils;
import org.geysermc.floodgate.util.DeviceOs;
import java.util.ArrayList;
import java.util.List;
import java.util.OptionalInt;
import java.util.UUID;
public class FireworkEntity extends Entity {
public FireworkEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setFireworkItem(EntityMetadata<ItemStack, ?> entityMetadata) {
ItemStack item = entityMetadata.getValue();
if (item == null) {
return;
}
CompoundTag tag = item.getNbt();
if (tag == null) {
return;
}
// TODO: Remove once Mojang fixes bugs with fireworks crashing clients on these specific devices.
// https://bugs.mojang.com/browse/MCPE-89115
if (session.getClientData().getDeviceOs() == DeviceOs.XBOX
|| session.getClientData().getDeviceOs() == DeviceOs.PS4) {
return;
}
CompoundTag fireworks = tag.get("Fireworks");
if (fireworks == null) {
// Thank you Mineplex very cool
return;
}
NbtMapBuilder fireworksBuilder = NbtMap.builder();
if (fireworks.get("Flight") != null) {
fireworksBuilder.putByte("Flight", MathUtils.getNbtByte(fireworks.get("Flight").getValue()));
}
List<NbtMap> explosions = new ArrayList<>();
if (fireworks.get("Explosions") != null) {
for (Tag effect : ((ListTag) fireworks.get("Explosions")).getValue()) {
CompoundTag effectData = (CompoundTag) effect;
NbtMapBuilder effectBuilder = NbtMap.builder();
if (effectData.get("Type") != null) {
effectBuilder.putByte("FireworkType", MathUtils.getNbtByte(effectData.get("Type").getValue()));
}
if (effectData.get("Colors") != null) {
int[] oldColors = (int[]) effectData.get("Colors").getValue();
byte[] colors = new byte[oldColors.length];
int i = 0;
for (int color : oldColors) {
colors[i++] = FireworkColor.fromJavaRGB(color);
}
effectBuilder.putByteArray("FireworkColor", colors);
}
if (effectData.get("FadeColors") != null) {
int[] oldColors = (int[]) effectData.get("FadeColors").getValue();
byte[] colors = new byte[oldColors.length];
int i = 0;
for (int color : oldColors) {
colors[i++] = FireworkColor.fromJavaRGB(color);
}
effectBuilder.putByteArray("FireworkFade", colors);
}
if (effectData.get("Trail") != null) {
effectBuilder.putByte("FireworkTrail", MathUtils.getNbtByte(effectData.get("Trail").getValue()));
}
if (effectData.get("Flicker") != null) {
effectBuilder.putByte("FireworkFlicker", MathUtils.getNbtByte(effectData.get("Flicker").getValue()));
}
explosions.add(effectBuilder.build());
}
}
fireworksBuilder.putList("Explosions", NbtType.COMPOUND, explosions);
NbtMapBuilder builder = NbtMap.builder();
builder.put("Fireworks", fireworksBuilder.build());
dirtyMetadata.put(EntityData.DISPLAY_ITEM, builder.build());
}
public void setPlayerGliding(EntityMetadata<OptionalInt, ?> entityMetadata) {
OptionalInt optional = entityMetadata.getValue();
// Checks if the firework has an entity ID (used when a player is gliding)
// and checks to make sure the player that is gliding is the one getting sent the packet
// or else every player near the gliding player will boost too.
if (optional.isPresent() && optional.getAsInt() == session.getPlayerEntity().getEntityId()) {
PlayerEntity entity = session.getPlayerEntity();
float yaw = entity.getYaw();
float pitch = entity.getPitch();
// Uses math from NukkitX
entity.setMotion(Vector3f.from(
-Math.sin(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2,
-Math.sin(Math.toRadians(pitch)) * 2,
Math.cos(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2));
// Need to update the EntityMotionPacket or else the player won't boost
SetEntityMotionPacket entityMotionPacket = new SetEntityMotionPacket();
entityMotionPacket.setRuntimeEntityId(entity.getGeyserId());
entityMotionPacket.setMotion(entity.getMotion());
session.sendUpstreamPacket(entityMotionPacket);
}
}
}

View file

@ -0,0 +1,192 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.PlaySoundPacket;
import lombok.Getter;
import org.geysermc.connector.entity.player.PlayerEntity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.collision.BoundingBox;
import org.geysermc.connector.network.translators.collision.translators.BlockCollision;
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import org.geysermc.connector.registry.BlockRegistries;
import org.geysermc.connector.utils.BlockPositionIterator;
import org.geysermc.connector.utils.BlockUtils;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
public class FishingHookEntity extends ThrowableEntity {
private boolean hooked = false;
private boolean inWater = false;
@Getter
private final boolean isOwnerSessionPlayer;
@Getter
private long bedrockTargetId;
private final BoundingBox boundingBox;
public FishingHookEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, Vector3f position, Vector3f motion, float yaw, float pitch, PlayerEntity owner) {
super(session, entityId, geyserId, uuid, EntityDefinitions.FISHING_BOBBER, position, motion, yaw, pitch, 0f);
this.boundingBox = new BoundingBox(0.125, 0.125, 0.125, 0.25, 0.25, 0.25);
// In Java, the splash sound depends on the entity's velocity, but in Bedrock the volume doesn't change.
// This splash can be confused with the sound from catching a fish. This silences the splash from Bedrock,
// so that it can be handled by moveAbsoluteImmediate.
setBoundingBoxHeight(128);
isOwnerSessionPlayer = owner.getGeyserId() == session.getPlayerEntity().getGeyserId();
this.dirtyMetadata.put(EntityData.OWNER_EID, owner.getGeyserId());
}
@Override
public void spawnEntity() {
super.spawnEntity();
}
public void setHookedEntity(IntEntityMetadata entityMetadata) {
int hookedEntityId = entityMetadata.getPrimitiveValue() - 1;
Entity entity;
if (session.getPlayerEntity().getEntityId() == hookedEntityId) {
entity = session.getPlayerEntity();
} else {
entity = session.getEntityCache().getEntityByJavaId(hookedEntityId);
}
if (entity != null) {
bedrockTargetId = entity.getGeyserId();
dirtyMetadata.put(EntityData.TARGET_EID, bedrockTargetId);
hooked = true;
} else {
hooked = false;
}
}
@Override
protected void moveAbsoluteImmediate(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
boundingBox.setMiddleX(position.getX());
boundingBox.setMiddleY(position.getY() + boundingBox.getSizeY() / 2);
boundingBox.setMiddleZ(position.getZ());
boolean touchingWater = false;
boolean collided = false;
for (BlockPositionIterator iter = session.getCollisionManager().collidableBlocksIterator(boundingBox); iter.hasNext(); iter.next()) {
int blockID = session.getConnector().getWorldManager().getBlockAt(session, iter.getX(), iter.getY(), iter.getZ());
BlockCollision blockCollision = BlockUtils.getCollision(blockID);
if (blockCollision != null) {
if (blockCollision.checkIntersection(iter.getX(), iter.getY(), iter.getZ(), boundingBox)) {
// TODO Push bounding box out of collision to improve movement
collided = true;
}
}
int waterLevel = BlockStateValues.getWaterLevel(blockID);
if (BlockRegistries.WATERLOGGED.get().contains(blockID)) {
waterLevel = 0;
}
if (waterLevel >= 0) {
double waterMaxY = iter.getY() + 1 - (waterLevel + 1) / 9.0;
// Falling water is a full block
if (waterLevel >= 8) {
waterMaxY = iter.getY() + 1;
}
if (position.getY() <= waterMaxY) {
touchingWater = true;
}
}
}
if (!inWater && touchingWater) {
sendSplashSound(session);
}
inWater = touchingWater;
if (!collided) {
super.moveAbsoluteImmediate(position, yaw, pitch, headYaw, isOnGround, teleported);
} else {
super.moveAbsoluteImmediate(this.position, yaw, pitch, headYaw, true, true);
}
}
private void sendSplashSound(GeyserSession session) {
if (!getFlag(EntityFlag.SILENT)) {
float volume = (float) (0.2f * Math.sqrt(0.2 * (motion.getX() * motion.getX() + motion.getZ() * motion.getZ()) + motion.getY() * motion.getY()));
if (volume > 1) {
volume = 1;
}
PlaySoundPacket playSoundPacket = new PlaySoundPacket();
playSoundPacket.setSound("random.splash");
playSoundPacket.setPosition(position);
playSoundPacket.setVolume(volume);
playSoundPacket.setPitch(1f + ThreadLocalRandom.current().nextFloat() * 0.3f);
session.sendUpstreamPacket(playSoundPacket);
}
}
@Override
public void tick() {
if (hooked || !isInAir() && !isInWater() || isOnGround()) {
motion = Vector3f.ZERO;
return;
}
float gravity = getGravity();
motion = motion.down(gravity);
moveAbsoluteImmediate(position.add(motion), yaw, pitch, headYaw, onGround, false);
float drag = getDrag();
motion = motion.mul(drag);
}
@Override
protected float getGravity() {
if (!isInWater() && !onGround) {
return 0.03f;
}
return 0;
}
/**
* @return true if this entity is currently in air.
*/
protected boolean isInAir() {
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
return block == BlockStateValues.JAVA_AIR_ID;
}
@Override
protected float getDrag() {
return 0.92f;
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.UUID;
public class FurnaceMinecartEntity extends DefaultBlockMinecartEntity {
private boolean hasFuel = false;
public FurnaceMinecartEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setHasFuel(BooleanEntityMetadata entityMetadata) {
hasFuel = entityMetadata.getPrimitiveValue();
updateDefaultBlockMetadata();
}
@Override
public void updateDefaultBlockMetadata() {
dirtyMetadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(hasFuel ? BlockStateValues.JAVA_FURNACE_LIT_ID : BlockStateValues.JAVA_FURNACE_ID));
dirtyMetadata.put(EntityData.DISPLAY_OFFSET, 6);
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import java.util.Map;
/**
* A write-only wrapper for temporarily storing entity metadata that will be sent to Bedrock.
*/
public class GeyserDirtyMetadata {
private final Map<EntityData, Object> metadata = new Object2ObjectLinkedOpenHashMap<>();
public void put(EntityData entityData, Object value) {
metadata.put(entityData, value);
}
/**
* Applies the contents of the dirty metadata into the input and clears the contents of our map.
*/
public void apply(EntityDataMap map) {
map.putAll(metadata);
metadata.clear();
}
public boolean hasEntries() {
return !metadata.isEmpty();
}
}

View file

@ -0,0 +1,148 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import com.nukkitx.protocol.bedrock.packet.AddItemEntityPacket;
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.UUID;
public class ItemEntity extends ThrowableEntity {
protected ItemData item;
private int waterLevel = -1;
public ItemEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
public void spawnEntity() {
if (item == null) {
return;
}
valid = true;
AddItemEntityPacket itemPacket = new AddItemEntityPacket();
itemPacket.setRuntimeEntityId(geyserId);
itemPacket.setUniqueEntityId(geyserId);
itemPacket.setPosition(position.add(0d, this.definition.offset(), 0d));
itemPacket.setMotion(motion);
itemPacket.setFromFishing(false);
itemPacket.setItemInHand(item);
itemPacket.getMetadata().putFlags(this.flags);
dirtyMetadata.apply(itemPacket.getMetadata());
setFlagsDirty(false);
session.sendUpstreamPacket(itemPacket);
}
@Override
public void tick() {
if (isInWater()) {
return;
}
if (!onGround || (motion.getX() * motion.getX() + motion.getZ() * motion.getZ()) > 0.00001) {
float gravity = getGravity();
motion = motion.down(gravity);
moveAbsoluteImmediate(position.add(motion), yaw, pitch, headYaw, onGround, false);
float drag = getDrag();
motion = motion.mul(drag, 0.98f, drag);
}
}
public void setItem(EntityMetadata<ItemStack, ?> entityMetadata) {
ItemData item = ItemTranslator.translateToBedrock(session, entityMetadata.getValue());
if (this.item == null) {
this.item = item;
spawnEntity();
} else if (item.equals(this.item, false, true, true)) {
// Don't bother respawning the entity if items are equal
if (this.item.getCount() != item.getCount()) {
// Just item count updated; let's make this easy
this.item = item;
EntityEventPacket packet = new EntityEventPacket();
packet.setRuntimeEntityId(geyserId);
packet.setType(EntityEventType.UPDATE_ITEM_STACK_SIZE);
packet.setData(this.item.getCount());
session.sendUpstreamPacket(packet);
}
} else {
this.item = item;
despawnEntity();
spawnEntity();
}
}
@Override
protected void moveAbsoluteImmediate(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
float offset = definition.offset();
if (waterLevel == 0) { // Item is in a full block of water
// Move the item entity down so it doesn't float above the water
offset = -definition.offset();
}
super.moveAbsoluteImmediate(position.add(0, offset, 0), 0, 0, 0, isOnGround, teleported);
this.position = position;
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
waterLevel = BlockStateValues.getWaterLevel(block);
}
@Override
protected float getGravity() {
if (getFlag(EntityFlag.HAS_GRAVITY) && !onGround && !isInWater()) {
// Gravity can change if the item is in water/lava, but
// the server calculates the motion & position for us
return 0.04f;
}
return 0;
}
@Override
protected float getDrag() {
if (onGround) {
Vector3i groundBlockPos = position.toInt().down(1);
int blockState = session.getConnector().getWorldManager().getBlockAt(session, groundBlockPos);
return BlockStateValues.getSlipperiness(blockState) * 0.98f;
}
return 0.98f;
}
@Override
protected boolean isInWater() {
return waterLevel != -1;
}
}

View file

@ -0,0 +1,219 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.object.Direction;
import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
import com.nukkitx.protocol.bedrock.v465.Bedrock_v465;
import lombok.Getter;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.registry.type.ItemMapping;
import java.util.UUID;
/**
* Item frames are an entity in Java but a block entity in Bedrock.
*/
public class ItemFrameEntity extends Entity {
/**
* Used for getting the Bedrock block position.
* Blocks deal with integers whereas entities deal with floats.
*/
private final Vector3i bedrockPosition;
/**
* Specific block 'state' we are emulating in Bedrock.
*/
private final int bedrockRuntimeId;
/**
* Rotation of item in frame.
*/
private float rotation = 0.0f;
/**
* Cached item frame's Bedrock compound tag.
*/
private NbtMap cachedTag;
/**
* The item currently in the item frame. Used for block picking.
*/
@Getter
private ItemStack heldItem = null;
/**
* Determines if this entity needs updated on the client end/
*/
private boolean changed = true;
public ItemFrameEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, Direction direction) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, 0f);
NbtMapBuilder blockBuilder = NbtMap.builder()
.putString("name", this.definition.entityType() == EntityType.GLOW_ITEM_FRAME ? "minecraft:glow_frame" : "minecraft:frame")
.putInt("version", session.getBlockMappings().getBlockStateVersion());
NbtMapBuilder statesBuilder = NbtMap.builder()
.putInt("facing_direction", direction.ordinal())
.putByte("item_frame_map_bit", (byte) 0);
if (session.getUpstream().getProtocolVersion() >= Bedrock_v465.V465_CODEC.getProtocolVersion()) {
statesBuilder.putByte("item_frame_photo_bit", (byte) 0);
}
blockBuilder.put("states", statesBuilder.build());
bedrockRuntimeId = session.getBlockMappings().getItemFrame(blockBuilder.build());
bedrockPosition = Vector3i.from(position.getFloorX(), position.getFloorY(), position.getFloorZ());
session.getItemFrameCache().put(bedrockPosition, this);
}
@Override
protected void initializeMetadata() {
// lol nah don't do anything
// This isn't a real entity for Bedrock so it isn't going to do anything
}
@Override
public void spawnEntity() {
updateBlock(true);
session.getConnector().getLogger().debug("Spawned item frame at location " + bedrockPosition + " with java id " + entityId);
valid = true;
}
public void setItemInFrame(EntityMetadata<ItemStack, ?> entityMetadata) {
if (entityMetadata.getValue() != null) {
this.heldItem = entityMetadata.getValue();
ItemData itemData = ItemTranslator.translateToBedrock(session, heldItem);
ItemMapping mapping = session.getItemMappings().getMapping(entityMetadata.getValue());
NbtMapBuilder builder = NbtMap.builder();
builder.putByte("Count", (byte) itemData.getCount());
if (itemData.getTag() != null) {
builder.put("tag", itemData.getTag());
}
builder.putShort("Damage", (short) itemData.getDamage());
builder.putString("Name", mapping.getBedrockIdentifier());
NbtMapBuilder tag = getDefaultTag().toBuilder();
tag.put("Item", builder.build());
tag.putFloat("ItemDropChance", 1.0f);
tag.putFloat("ItemRotation", rotation);
cachedTag = tag.build();
changed = true;
} else if (cachedTag != null) {
cachedTag = getDefaultTag();
changed = true;
}
}
public void setItemRotation(IntEntityMetadata entityMetadata) {
rotation = entityMetadata.getPrimitiveValue() * 45;
if (cachedTag == null) {
return;
}
NbtMapBuilder builder = cachedTag.toBuilder();
builder.putFloat("ItemRotation", rotation);
cachedTag = builder.build();
changed = true;
}
@Override
public boolean despawnEntity() {
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0);
updateBlockPacket.setBlockPosition(bedrockPosition);
updateBlockPacket.setRuntimeId(session.getBlockMappings().getBedrockAirId()); //TODO maybe set this to the world block or another item frame?
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
session.sendUpstreamPacket(updateBlockPacket);
session.getItemFrameCache().remove(bedrockPosition, this);
valid = false;
return true;
}
private NbtMap getDefaultTag() {
NbtMapBuilder builder = NbtMap.builder();
builder.putInt("x", bedrockPosition.getX());
builder.putInt("y", bedrockPosition.getY());
builder.putInt("z", bedrockPosition.getZ());
builder.putByte("isMovable", (byte) 1);
builder.putString("id", this.definition.entityType() == EntityType.GLOW_ITEM_FRAME ? "GlowItemFrame" : "ItemFrame");
return builder.build();
}
@Override
public void updateBedrockMetadata() {
updateBlock(false);
}
/**
* Updates the item frame as a block
*/
public void updateBlock(boolean force) {
if (!changed && !force) {
// Don't send a block update packet - nothing changed
return;
}
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0);
updateBlockPacket.setBlockPosition(bedrockPosition);
updateBlockPacket.setRuntimeId(bedrockRuntimeId);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
session.sendUpstreamPacket(updateBlockPacket);
BlockEntityDataPacket blockEntityDataPacket = new BlockEntityDataPacket();
blockEntityDataPacket.setBlockPosition(bedrockPosition);
if (cachedTag != null) {
blockEntityDataPacket.setData(cachedTag);
} else {
blockEntityDataPacket.setData(getDefaultTag());
}
session.sendUpstreamPacket(blockEntityDataPacket);
changed = false;
}
/**
* Finds the Java entity ID of an item frame from its Bedrock position.
* @param position position of item frame in Bedrock.
* @param session GeyserSession.
* @return Java entity ID or -1 if not found.
*/
public static ItemFrameEntity getItemFrameEntity(GeyserSession session, Vector3i position) {
return session.getItemFrameCache().get(position);
}
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class ItemedFireballEntity extends ThrowableEntity {
private final Vector3f acceleration;
/**
* The number of ticks to advance movement before sending to Bedrock
*/
protected int futureTicks = 3;
public ItemedFireballEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, Vector3f.ZERO, yaw, pitch, headYaw);
float magnitude = motion.length();
if (magnitude != 0) {
acceleration = motion.div(magnitude).mul(0.1f);
} else {
acceleration = Vector3f.ZERO;
}
}
private Vector3f tickMovement(Vector3f position) {
position = position.add(motion);
float drag = getDrag();
motion = motion.add(acceleration).mul(drag);
return position;
}
@Override
protected void moveAbsoluteImmediate(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
// Advance the position by a few ticks before sending it to Bedrock
Vector3f lastMotion = motion;
Vector3f newPosition = position;
for (int i = 0; i < futureTicks; i++) {
newPosition = tickMovement(newPosition);
}
super.moveAbsoluteImmediate(newPosition, yaw, pitch, headYaw, isOnGround, teleported);
this.position = position;
this.motion = lastMotion;
}
@Override
public void tick() {
moveAbsoluteImmediate(tickMovement(position), yaw, pitch, headYaw, false, false);
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class LeashKnotEntity extends Entity {
public LeashKnotEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
// Position is incorrect by default
super(session, entityId, geyserId, uuid, definition, position.add(0.5f, 0.25f, 0.5f), motion, yaw, pitch, headYaw);
}
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.packet.PlaySoundPacket;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
public class LightningEntity extends Entity {
public LightningEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
public void spawnEntity() {
super.spawnEntity();
// Add these two sound effects - they're done completely clientside on Java Edition as of 1.17.1
ThreadLocalRandom random = ThreadLocalRandom.current();
PlaySoundPacket thunderPacket = new PlaySoundPacket();
thunderPacket.setPosition(this.position);
thunderPacket.setSound("ambient.weather.thunder");
thunderPacket.setPitch(0.8f + random.nextFloat() * 0.2f);
thunderPacket.setVolume(10000f); // Really.
session.sendUpstreamPacket(thunderPacket);
PlaySoundPacket impactPacket = new PlaySoundPacket();
impactPacket.setPosition(this.position);
impactPacket.setSound("ambient.weather.lightning.impact");
impactPacket.setPitch(0.5f + random.nextFloat() * 0.2f);
impactPacket.setVolume(2.0f);
session.sendUpstreamPacket(impactPacket);
}
}

View file

@ -0,0 +1,273 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.AttributeData;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import com.nukkitx.protocol.bedrock.packet.MobArmorEquipmentPacket;
import com.nukkitx.protocol.bedrock.packet.MobEquipmentPacket;
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.connector.entity.attribute.GeyserAttributeType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.registry.type.ItemMapping;
import org.geysermc.connector.utils.AttributeUtils;
import org.geysermc.connector.utils.ChunkUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@Getter
@Setter
public class LivingEntity extends Entity {
protected ItemData helmet = ItemData.AIR;
protected ItemData chestplate = ItemData.AIR;
protected ItemData leggings = ItemData.AIR;
protected ItemData boots = ItemData.AIR;
protected ItemData hand = ItemData.AIR;
protected ItemData offHand = ItemData.AIR;
@Getter(value = AccessLevel.NONE)
protected float health = 1f; // The default value in Java Edition before any entity metadata is sent
@Getter(value = AccessLevel.NONE)
protected float maxHealth = 20f; // The value Java Edition defaults to if no attribute is given
/**
* A convenience variable for if the entity has reached the maximum frozen ticks and should be shaking
*/
private boolean isMaxFrozenState = false;
public LivingEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
protected void initializeMetadata() {
super.initializeMetadata();
// Matches Bedrock behavior; is always set to this
dirtyMetadata.put(EntityData.HEALTH, 1);
}
public void setLivingEntityFlags(ByteEntityMetadata entityMetadata) {
byte xd = entityMetadata.getPrimitiveValue();
// Blocking gets triggered when using a bow, but if we set USING_ITEM for all items, it may look like
// you're "mining" with ex. a shield.
ItemMapping shield = session.getItemMappings().getStoredItems().shield();
boolean isUsingShield = (getHand().getId() == shield.getBedrockId() ||
getHand().equals(ItemData.AIR) && getOffHand().getId() == shield.getBedrockId());
setFlag(EntityFlag.USING_ITEM, (xd & 0x01) == 0x01 && !isUsingShield);
setFlag(EntityFlag.BLOCKING, (xd & 0x01) == 0x01);
// Riptide spin attack
setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, (xd & 0x04) == 0x04);
}
public void setHealth(FloatEntityMetadata entityMetadata) {
this.health = entityMetadata.getPrimitiveValue();
AttributeData healthData = createHealthAttribute();
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
attributesPacket.setRuntimeEntityId(geyserId);
attributesPacket.setAttributes(Collections.singletonList(healthData));
session.sendUpstreamPacket(attributesPacket);
}
public Vector3i setBedPosition(EntityMetadata<Optional<Position>, ?> entityMetadata) {
Optional<Position> optionalPos = entityMetadata.getValue();
if (optionalPos.isPresent()) {
Position bedPosition = optionalPos.get();
Vector3i vector = Vector3i.from(bedPosition.getX(), bedPosition.getY(), bedPosition.getZ());
dirtyMetadata.put(EntityData.BED_POSITION, vector);
int bed = session.getConnector().getWorldManager().getBlockAt(session, bedPosition);
// Bed has to be updated, or else player is floating in the air
ChunkUtils.updateBlock(session, bed, bedPosition);
// Indicate that the player should enter the sleep cycle
// Has to be a byte or it does not work
// (Bed position is what actually triggers sleep - "pose" is only optional)
dirtyMetadata.put(EntityData.PLAYER_FLAGS, (byte) 2);
return vector;
} else {
// Player is no longer sleeping
dirtyMetadata.put(EntityData.PLAYER_FLAGS, (byte) 0);
return null;
}
}
@Override
protected boolean isShaking() {
return isMaxFrozenState;
}
@Override
protected void setDimensions(Pose pose) {
if (pose == Pose.SLEEPING) {
setBoundingBoxWidth(0.2f);
setBoundingBoxHeight(0.2f);
} else {
super.setDimensions(pose);
}
}
@Override
public float setFreezing(IntEntityMetadata entityMetadata) {
float freezingPercentage = super.setFreezing(entityMetadata);
this.isMaxFrozenState = freezingPercentage >= 1.0f;
setFlag(EntityFlag.SHAKING, isShaking());
return freezingPercentage;
}
/**
* @return a Bedrock health attribute constructed from the data sent from the server
*/
protected AttributeData createHealthAttribute() {
// Default health needs to be specified as the max health in order for maximum hearts to show correctly on mounted entities
// Round health value up, so that Bedrock doesn't consider the entity to be dead when health is between 0 and 1
return new AttributeData(GeyserAttributeType.HEALTH.getBedrockIdentifier(), 0f, this.maxHealth, (float) Math.ceil(this.health), this.maxHealth);
}
public void updateArmor(GeyserSession session) {
if (!valid) return;
ItemData helmet = this.helmet;
ItemData chestplate = this.chestplate;
// 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 (chestplate.getId() == ItemData.AIR.getId() && helmet.getId() == banner.getBedrockId()) {
chestplate = this.helmet;
helmet = ItemData.AIR;
} else if (chestplate.getId() == banner.getBedrockId()) {
// Prevent chestplate banners from showing erroneously
chestplate = ItemData.AIR;
}
MobArmorEquipmentPacket armorEquipmentPacket = new MobArmorEquipmentPacket();
armorEquipmentPacket.setRuntimeEntityId(geyserId);
armorEquipmentPacket.setHelmet(helmet);
armorEquipmentPacket.setChestplate(chestplate);
armorEquipmentPacket.setLeggings(leggings);
armorEquipmentPacket.setBoots(boots);
session.sendUpstreamPacket(armorEquipmentPacket);
}
public void updateMainHand(GeyserSession session) {
if (!valid) return;
MobEquipmentPacket handPacket = new MobEquipmentPacket();
handPacket.setRuntimeEntityId(geyserId);
handPacket.setItem(hand);
handPacket.setHotbarSlot(-1);
handPacket.setInventorySlot(0);
handPacket.setContainerId(ContainerId.INVENTORY);
session.sendUpstreamPacket(handPacket);
}
public void updateOffHand(GeyserSession session) {
if (!valid) return;
MobEquipmentPacket offHandPacket = new MobEquipmentPacket();
offHandPacket.setRuntimeEntityId(geyserId);
offHandPacket.setItem(offHand);
offHandPacket.setHotbarSlot(-1);
offHandPacket.setInventorySlot(0);
offHandPacket.setContainerId(ContainerId.OFFHAND);
session.sendUpstreamPacket(offHandPacket);
}
/**
* Attributes are properties of an entity that are generally more runtime-based instead of permanent properties.
* Movement speed, current attack damage with a weapon, current knockback resistance.
*
* @param attributes the Java list of attributes sent from the server
*/
public void updateBedrockAttributes(GeyserSession session, List<Attribute> attributes) {
if (!valid) return;
List<AttributeData> newAttributes = new ArrayList<>();
for (Attribute attribute : attributes) {
// Convert the attribute to a Bedrock version, if relevant
updateAttribute(attribute, newAttributes);
}
if (newAttributes.isEmpty()) {
// If there are Java-only attributes or only attributes that are not translated by us
return;
}
UpdateAttributesPacket updateAttributesPacket = new UpdateAttributesPacket();
updateAttributesPacket.setRuntimeEntityId(geyserId);
updateAttributesPacket.setAttributes(newAttributes);
session.sendUpstreamPacket(updateAttributesPacket);
}
/**
* Takes the Java attribute and adds it to newAttributes as a Bedrock-formatted attribute
*/
protected void updateAttribute(Attribute javaAttribute, List<AttributeData> newAttributes) {
switch (javaAttribute.getType()) {
case GENERIC_MAX_HEALTH -> {
this.maxHealth = (float) AttributeUtils.calculateValue(javaAttribute);
newAttributes.add(createHealthAttribute());
}
case GENERIC_ATTACK_DAMAGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.ATTACK_DAMAGE));
case GENERIC_FLYING_SPEED -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED));
case GENERIC_MOVEMENT_SPEED -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.MOVEMENT_SPEED));
case GENERIC_FOLLOW_RANGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FOLLOW_RANGE));
case GENERIC_KNOCKBACK_RESISTANCE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.KNOCKBACK_RESISTANCE));
case HORSE_JUMP_STRENGTH -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.HORSE_JUMP_STRENGTH));
}
}
/**
* Calculates the complete attribute value to send to Bedrock. Will be overriden if attributes need to be cached.
*/
protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttributeType type) {
return type.getAttribute((float) AttributeUtils.calculateValue(javaAttribute));
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
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.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class MinecartEntity extends Entity {
public MinecartEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position.add(0d, definition.offset(), 0d), motion, yaw, pitch, headYaw);
}
public void setCustomBlock(IntEntityMetadata entityMetadata) {
dirtyMetadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(entityMetadata.getPrimitiveValue()));
}
public void setCustomBlockOffset(IntEntityMetadata entityMetadata) {
dirtyMetadata.put(EntityData.DISPLAY_OFFSET, entityMetadata.getPrimitiveValue());
}
public void setShowCustomBlock(BooleanEntityMetadata entityMetadata) {
// If the custom block should be enabled
// Needs a byte based off of Java's boolean
dirtyMetadata.put(EntityData.CUSTOM_DISPLAY, (byte) (entityMetadata.getPrimitiveValue() ? 1 : 0));
}
@Override
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
super.moveAbsolute(position.add(0d, this.definition.offset(), 0d), yaw, pitch, headYaw, isOnGround, teleported);
}
@Override
public Vector3f getBedrockRotation() {
// Note: minecart rotation on rails does not care about the actual rotation value
return Vector3f.from(0, yaw, 0);
}
}

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.packet.AddPaintingPacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.PaintingType;
import java.util.UUID;
public class PaintingEntity extends Entity {
private static final double OFFSET = -0.46875;
private final PaintingType paintingName;
private final int direction;
public PaintingEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, Vector3f position, PaintingType paintingName, int direction) {
super(session, entityId, geyserId, uuid, EntityDefinitions.PAINTING, position, Vector3f.ZERO, 0f, 0f, 0f);
this.paintingName = paintingName;
this.direction = direction;
}
@Override
public void spawnEntity() {
AddPaintingPacket addPaintingPacket = new AddPaintingPacket();
addPaintingPacket.setUniqueEntityId(geyserId);
addPaintingPacket.setRuntimeEntityId(geyserId);
addPaintingPacket.setMotive(paintingName.getBedrockName());
addPaintingPacket.setPosition(fixOffset());
addPaintingPacket.setDirection(direction);
session.sendUpstreamPacket(addPaintingPacket);
valid = true;
session.getConnector().getLogger().debug("Spawned painting on " + position);
}
@Override
public void updateHeadLookRotation(float headYaw) {
// Do nothing, as head look messes up paintings
}
private Vector3f fixOffset() {
Vector3f position = super.position;
position = position.add(0.5, 0.5, 0.5);
double widthOffset = paintingName.getWidth() > 1 ? 0.5 : 0;
double heightOffset = paintingName.getHeight() > 1 && paintingName.getHeight() != 3 ? 0.5 : 0;
return switch (direction) {
case 0 -> position.add(widthOffset, heightOffset, OFFSET);
case 1 -> position.add(-OFFSET, heightOffset, widthOffset);
case 2 -> position.add(-widthOffset, heightOffset, -OFFSET);
case 3 -> position.add(OFFSET, heightOffset, -widthOffset);
default -> position;
};
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.UUID;
public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity {
public SpawnerMinecartEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
public void updateDefaultBlockMetadata() {
dirtyMetadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(BlockStateValues.JAVA_SPAWNER_ID));
dirtyMetadata.put(EntityData.DISPLAY_OFFSET, 6);
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class TNTEntity extends Entity implements Tickable {
private int currentTick;
public TNTEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setFuseLength(IntEntityMetadata entityMetadata) {
currentTick = ((IntEntityMetadata) entityMetadata).getPrimitiveValue();
setFlag(EntityFlag.IGNITED, true);
dirtyMetadata.put(EntityData.FUSE_LENGTH, currentTick);
}
@Override
public void tick() {
if (currentTick == 0) {
// No need to update the fuse when there is none
return;
}
if (currentTick % 5 == 0) {
dirtyMetadata.put(EntityData.FUSE_LENGTH, currentTick);
SetEntityDataPacket packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(geyserId);
packet.getMetadata().put(EntityData.FUSE_LENGTH, currentTick);
session.sendUpstreamPacket(packet);
}
currentTick--;
}
}

View file

@ -0,0 +1,194 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
import com.nukkitx.protocol.bedrock.packet.MoveEntityDeltaPacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.UUID;
/**
* Used as a class for any object-like entity that moves as a projectile
*/
public class ThrowableEntity extends Entity implements Tickable {
protected Vector3f lastJavaPosition;
public ThrowableEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
this.lastJavaPosition = position;
}
/**
* Updates the position for the Bedrock client.
*
* Java clients assume the next positions of moving items. Bedrock needs to be explicitly told positions
*/
@Override
public void tick() {
moveAbsoluteImmediate(position.add(motion), yaw, pitch, headYaw, onGround, false);
float drag = getDrag();
float gravity = getGravity();
motion = motion.mul(drag).down(gravity);
}
protected void moveAbsoluteImmediate(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
MoveEntityDeltaPacket moveEntityDeltaPacket = new MoveEntityDeltaPacket();
moveEntityDeltaPacket.setRuntimeEntityId(geyserId);
if (isOnGround) {
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.ON_GROUND);
}
setOnGround(isOnGround);
if (teleported) {
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.TELEPORTING);
}
if (this.position.getX() != position.getX()) {
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_X);
moveEntityDeltaPacket.setX(position.getX());
}
if (this.position.getY() != position.getY()) {
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Y);
moveEntityDeltaPacket.setY(position.getY());
}
if (this.position.getZ() != position.getZ()) {
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Z);
moveEntityDeltaPacket.setZ(position.getZ());
}
setPosition(position);
if (this.yaw != yaw) {
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_YAW);
moveEntityDeltaPacket.setYaw(yaw);
this.yaw = yaw;
}
if (this.pitch != pitch) {
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH);
moveEntityDeltaPacket.setPitch(pitch);
this.pitch = pitch;
}
if (this.headYaw != headYaw) {
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_HEAD_YAW);
moveEntityDeltaPacket.setHeadYaw(headYaw);
this.headYaw = headYaw;
}
if (!moveEntityDeltaPacket.getFlags().isEmpty()) {
session.sendUpstreamPacket(moveEntityDeltaPacket);
}
}
/**
* Get the gravity of this entity type. Used for applying gravity while the entity is in motion.
*
* @return the amount of gravity to apply to this entity while in motion.
*/
protected float getGravity() {
if (getFlag(EntityFlag.HAS_GRAVITY)) {
switch (definition.entityType()) {
case THROWN_POTION:
return 0.05f;
case THROWN_EXP_BOTTLE:
return 0.07f;
case FIREBALL:
case SHULKER_BULLET:
return 0;
case SNOWBALL:
case THROWN_EGG:
case THROWN_ENDERPEARL:
return 0.03f;
case LLAMA_SPIT:
return 0.06f;
}
}
return 0;
}
/**
* @return the drag that should be multiplied to the entity's motion
*/
protected float getDrag() {
if (isInWater()) {
return 0.8f;
} else {
switch (definition.entityType()) {
case THROWN_POTION:
case THROWN_EXP_BOTTLE:
case SNOWBALL:
case THROWN_EGG:
case THROWN_ENDERPEARL:
case LLAMA_SPIT:
return 0.99f;
case FIREBALL:
case SMALL_FIREBALL:
case DRAGON_FIREBALL:
return 0.95f;
case SHULKER_BULLET:
return 1;
}
}
return 1;
}
/**
* @return true if this entity is currently in water.
*/
protected boolean isInWater() {
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
return BlockStateValues.getWaterLevel(block) != -1;
}
@Override
public boolean despawnEntity() {
if (definition.entityType() == EntityType.THROWN_ENDERPEARL) {
LevelEventPacket particlePacket = new LevelEventPacket();
particlePacket.setType(LevelEventType.PARTICLE_TELEPORT);
particlePacket.setPosition(position);
session.sendUpstreamPacket(particlePacket);
}
return super.despawnEntity();
}
@Override
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
moveAbsoluteImmediate(lastJavaPosition.add(relX, relY, relZ), yaw, pitch, headYaw, isOnGround, false);
lastJavaPosition = position;
}
@Override
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
moveAbsoluteImmediate(position, yaw, pitch, headYaw, isOnGround, teleported);
lastJavaPosition = position;
}
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
/**
* Used as a class for any projectile entity that looks like an item
*/
public class ThrowableItemEntity extends ThrowableEntity {
/**
* Number of ticks since the entity was spawned by the Java server
*/
private int age;
private boolean invisible;
public ThrowableItemEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
setFlag(EntityFlag.INVISIBLE, true);
invisible = false;
}
private void checkVisibility() {
if (invisible != getFlag(EntityFlag.INVISIBLE)) {
if (!invisible) {
Vector3f playerPos = session.getPlayerEntity().getPosition();
// Prevent projectiles from blocking the player's screen
if (age >= 4 || position.distanceSquared(playerPos) > 16) {
setFlag(EntityFlag.INVISIBLE, false);
updateBedrockMetadata();
}
} else {
setFlag(EntityFlag.INVISIBLE, true);
updateBedrockMetadata();
}
}
age++;
}
@Override
public void tick() {
checkVisibility();
super.tick();
}
@Override
protected void setInvisible(boolean value) {
invisible = value;
}
}

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.item.Potion;
import org.geysermc.connector.registry.type.ItemMapping;
import java.util.EnumSet;
import java.util.UUID;
public class ThrownPotionEntity extends ThrowableItemEntity {
private static final EnumSet<Potion> NON_ENCHANTED_POTIONS = EnumSet.of(Potion.WATER, Potion.MUNDANE, Potion.THICK, Potion.AWKWARD);
public ThrownPotionEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setPotion(EntityMetadata<ItemStack, ?> entityMetadata) {
ItemStack itemStack = entityMetadata.getValue();
ItemMapping mapping = session.getItemMappings().getMapping(itemStack);
if (mapping.getJavaIdentifier().endsWith("potion") && itemStack.getNbt() != null) {
Tag potionTag = itemStack.getNbt().get("Potion");
if (potionTag instanceof StringTag) {
Potion potion = Potion.getByJavaIdentifier(((StringTag) potionTag).getValue());
if (potion != null) {
dirtyMetadata.put(EntityData.POTION_AUX_VALUE, potion.getBedrockId());
setFlag(EntityFlag.ENCHANTED, !NON_ENCHANTED_POTIONS.contains(potion));
} else {
dirtyMetadata.put(EntityData.POTION_AUX_VALUE, 0);
GeyserConnector.getInstance().getLogger().debug("Unknown java potion: " + potionTag.getValue());
}
}
boolean isLingering = mapping.getJavaIdentifier().equals("minecraft:lingering_potion");
setFlag(EntityFlag.LINGERING, isLingering);
}
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
/**
* Implemented onto anything that should have code ran every Minecraft tick - 50 milliseconds.
*/
public interface Tickable {
void tick();
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.item.TippedArrowPotion;
import java.util.UUID;
/**
* Internally this is known as TippedArrowEntity but is used with tipped arrows and normal arrows
*/
public class TippedArrowEntity extends AbstractArrowEntity {
public TippedArrowEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setPotionEffectColor(IntEntityMetadata entityMetadata) {
int potionColor = entityMetadata.getPrimitiveValue();
// -1 means no color
if (potionColor == -1) {
dirtyMetadata.put(EntityData.CUSTOM_DISPLAY, 0);
} else {
TippedArrowPotion potion = TippedArrowPotion.getByJavaColor(potionColor);
if (potion != null && potion.getJavaColor() != -1) {
dirtyMetadata.put(EntityData.CUSTOM_DISPLAY, (byte) potion.getBedrockId());
} else {
dirtyMetadata.put(EntityData.CUSTOM_DISPLAY, 0);
}
}
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class TridentEntity extends AbstractArrowEntity {
public TridentEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class WitherSkullEntity extends ItemedFireballEntity {
private boolean isCharged;
public WitherSkullEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
this.futureTicks = 1;
}
public void setDangerous(BooleanEntityMetadata entityMetadata) {
boolean newDangerous = entityMetadata.getPrimitiveValue();
if (newDangerous != isCharged) {
isCharged = newDangerous;
// Is an entirely new entity in Bedrock but just a metadata type in Java
definition = isCharged ? EntityDefinitions.WITHER_SKULL_DANGEROUS : EntityDefinitions.WITHER_SKULL;
despawnEntity();
spawnEntity();
}
}
@Override
protected float getDrag() {
return isCharged ? 0.73f : super.getDrag();
}
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.attribute;
import com.nukkitx.protocol.bedrock.data.AttributeData;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum GeyserAttributeType {
// Universal Attributes
FOLLOW_RANGE("minecraft:generic.follow_range", "minecraft:follow_range", 0f, 2048f, 32f),
KNOCKBACK_RESISTANCE("minecraft:generic.knockback_resistance", "minecraft:knockback_resistance", 0f, 1f, 0f),
MOVEMENT_SPEED("minecraft:generic.movement_speed", "minecraft:movement", 0f, 1024f, 0.1f),
FLYING_SPEED("minecraft:generic.flying_speed", "minecraft:movement", 0.0f, 1024.0f, 0.4000000059604645f),
ATTACK_DAMAGE("minecraft:generic.attack_damage", "minecraft:attack_damage", 0f, 2048f, 1f),
HORSE_JUMP_STRENGTH("minecraft:horse.jump_strength", "minecraft:horse.jump_strength", 0.0f, 2.0f, 0.7f),
LUCK("minecraft:generic.luck", "minecraft:luck", -1024f, 1024f, 0f),
// Java Attributes
ARMOR("minecraft:generic.armor", null, 0f, 30f, 0f),
ARMOR_TOUGHNESS("minecraft:generic.armor_toughness", null, 0F, 20f, 0f),
ATTACK_KNOCKBACK("minecraft:generic.attack_knockback", null, 1.5f, Float.MAX_VALUE, 0f),
ATTACK_SPEED("minecraft:generic.attack_speed", null, 0f, 1024f, 4f),
MAX_HEALTH("minecraft:generic.max_health", null, 0f, 1024f, 20f),
// Bedrock Attributes
ABSORPTION(null, "minecraft:absorption", 0f, Float.MAX_VALUE, 0f),
EXHAUSTION(null, "minecraft:player.exhaustion", 0f, 5f, 0f),
EXPERIENCE(null, "minecraft:player.experience", 0f, 1f, 0f),
EXPERIENCE_LEVEL(null, "minecraft:player.level", 0f, 24791.00f, 0f),
HEALTH(null, "minecraft:health", 0f, 1024f, 20f),
HUNGER(null, "minecraft:player.hunger", 0f, 20f, 20f),
SATURATION(null, "minecraft:player.saturation", 0f, 20f, 20f);
private final String javaIdentifier;
private final String bedrockIdentifier;
private final float minimum;
private final float maximum;
private final float defaultValue;
public AttributeData getAttribute(float value) {
return getAttribute(value, maximum);
}
public AttributeData getAttribute(float value, float maximum) {
if (bedrockIdentifier == null) {
return null;
}
// Minimum, maximum, and default values are hardcoded on Java Edition, whereas controlled by the server on Bedrock
return new AttributeData(bedrockIdentifier, minimum, maximum, value, defaultValue);
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.factory;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
@FunctionalInterface
public interface BaseEntityFactory<T extends Entity> extends EntityFactory<T> {
T create(GeyserSession session, long javaId, long bedrockId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw);
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.factory;
import org.geysermc.connector.entity.Entity;
public interface EntityFactory<T extends Entity> {
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.factory;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.ExpOrbEntity;
import org.geysermc.connector.network.session.GeyserSession;
@FunctionalInterface
public interface ExperienceOrbEntityFactory extends EntityFactory<ExpOrbEntity> {
ExpOrbEntity create(GeyserSession session, int amount, long entityId, long geyserId, Vector3f position);
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.factory;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.PaintingEntity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.PaintingType;
import java.util.UUID;
public interface PaintingEntityFactory extends EntityFactory<PaintingEntity> {
PaintingEntity create(GeyserSession session, long entityId, long geyserId, UUID uuid, Vector3f position, PaintingType paintingName, int direction);
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class AbstractFishEntity extends WaterEntity {
public AbstractFishEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
setFlag(EntityFlag.CAN_SWIM, true);
setFlag(EntityFlag.BREATHING, true);
setFlag(EntityFlag.CAN_CLIMB, false);
setFlag(EntityFlag.HAS_GRAVITY, false);
}
}

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class AgeableEntity extends CreatureEntity {
public AgeableEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setBaby(BooleanEntityMetadata entityMetadata) {
boolean isBaby = entityMetadata.getPrimitiveValue();
dirtyMetadata.put(EntityData.SCALE, isBaby ? getBabySize() : getAdultSize());
setFlag(EntityFlag.BABY, isBaby);
// TODO save this?
dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, definition.height() * (isBaby ? getBabySize() : getAdultSize()));
dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, definition.width() * (isBaby ? getBabySize() : getAdultSize()));
}
/**
* The scale that should be used when this entity is not a baby.
*/
protected float getAdultSize() {
return 1f;
}
/**
* The scale that should be used when this entity is a baby.
*/
protected float getBabySize() {
return 0.55f;
}
public boolean isBaby() {
return getFlag(EntityFlag.BABY);
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class AmbientEntity extends MobEntity {
public AmbientEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
}

View file

@ -0,0 +1,433 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Rotation;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.entity.EntityDefinitions;
import org.geysermc.connector.entity.LivingEntity;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.Optional;
import java.util.UUID;
public class ArmorStandEntity extends LivingEntity {
// These are used to store the state of the armour stand for use when handling invisibility
@Getter
private boolean isMarker = false;
private boolean isInvisible = false;
private boolean isSmall = false;
/**
* On Java Edition, armor stands always show their name. Invisibility hides the name on Bedrock.
* By having a second entity, we can allow an invisible entity with the name tag.
* (This lets armor on armor stands still show)
*/
private ArmorStandEntity secondEntity = null;
/**
* Whether this is the primary armor stand that holds the armor and not the name tag.
*/
private boolean primaryEntity = true;
/**
* Whether the entity's position must be updated to included the offset.
*
* This should be true when the Java server marks the armor stand as invisible, but we shrink the entity
* to allow the nametag to appear. Basically:
* - Is visible: this is irrelevant (false)
* - Has armor, no name: false
* - Has armor, has name: false, with a second entity
* - No armor, no name: false
* - No armor, yes name: true
*/
private boolean positionRequiresOffset = false;
/**
* Whether we should update the position of this armor stand after metadata updates.
*/
private boolean positionUpdateRequired = false;
public ArmorStandEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
public void spawnEntity() {
this.pitch = yaw;
this.headYaw = yaw;
super.spawnEntity();
}
@Override
public boolean despawnEntity() {
if (secondEntity != null) {
secondEntity.despawnEntity();
}
return super.despawnEntity();
}
@Override
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
if (secondEntity != null) {
secondEntity.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
}
super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
}
@Override
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
if (secondEntity != null) {
secondEntity.moveAbsolute(applyOffsetToPosition(position), yaw, pitch, headYaw, isOnGround, teleported);
} else if (positionRequiresOffset) {
// Fake the height to be above where it is so the nametag appears in the right location for invisible non-marker armour stands
position = applyOffsetToPosition(position);
}
super.moveAbsolute(position, yaw, yaw, yaw, isOnGround, teleported);
}
@Override
public void setDisplayName(EntityMetadata<Optional<Component>, ?> entityMetadata) {
super.setDisplayName(entityMetadata);
updateSecondEntityStatus(false);
}
public void setArmorStandFlags(ByteEntityMetadata entityMetadata) {
byte xd = entityMetadata.getPrimitiveValue();
// isSmall
boolean newIsSmall = (xd & 0x01) == 0x01;
if (newIsSmall != isSmall) {
if (positionRequiresOffset) {
// Fix new inconsistency with offset
this.position = fixOffsetForSize(position, newIsSmall);
positionUpdateRequired = true;
}
isSmall = newIsSmall;
if (!isMarker) {
toggleSmallStatus();
}
}
// setMarker
boolean oldIsMarker = isMarker;
isMarker = (xd & 0x10) == 0x10;
if (oldIsMarker != isMarker) {
if (isMarker) {
dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.0f);
dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f);
dirtyMetadata.put(EntityData.SCALE, 0f);
} else {
toggleSmallStatus();
}
updateSecondEntityStatus(false);
}
// The following values don't do anything on normal Bedrock.
// But if given a resource pack, then we can use these values to control armor stand visual properties
setFlag(EntityFlag.ANGRY, (xd & 0x04) != 0x04); // Has arms
setFlag(EntityFlag.ADMIRING, (xd & 0x08) == 0x08); // Has no baseplate
}
public void setHeadRotation(EntityMetadata<Rotation, ?> entityMetadata) {
onRotationUpdate(EntityData.MARK_VARIANT, EntityFlag.INTERESTED, EntityFlag.CHARGED, EntityFlag.POWERED, entityMetadata.getValue());
}
public void setBodyRotation(EntityMetadata<Rotation, ?> entityMetadata) {
onRotationUpdate(EntityData.VARIANT, EntityFlag.IN_LOVE, EntityFlag.CELEBRATING, EntityFlag.CELEBRATING_SPECIAL, entityMetadata.getValue());
}
public void setLeftArmRotation(EntityMetadata<Rotation, ?> entityMetadata) {
onRotationUpdate(EntityData.TRADE_TIER, EntityFlag.CHARGING, EntityFlag.CRITICAL, EntityFlag.DANCING, entityMetadata.getValue());
}
public void setRightArmRotation(EntityMetadata<Rotation, ?> entityMetadata) {
onRotationUpdate(EntityData.MAX_TRADE_TIER, EntityFlag.ELDER, EntityFlag.EMOTING, EntityFlag.IDLING, entityMetadata.getValue());
}
public void setLeftLegRotation(EntityMetadata<Rotation, ?> entityMetadata) {
onRotationUpdate(EntityData.SKIN_ID, EntityFlag.IS_ILLAGER_CAPTAIN, EntityFlag.IS_IN_UI, EntityFlag.LINGERING, entityMetadata.getValue());
}
public void setRightLegRotation(EntityMetadata<Rotation, ?> entityMetadata) {
onRotationUpdate(EntityData.HURT_DIRECTION, EntityFlag.IS_PREGNANT, EntityFlag.SHEARED, EntityFlag.STALKING, entityMetadata.getValue());
}
/**
* Updates rotation on the armor stand by hijacking other unused Bedrock entity data/flags.
* Do note: as of recent Bedrock versions there is a custom entity data system that can be replaced with this,
* but at this time there is no need to implement this.
*
* @param dataLeech the entity data to "leech" off of that stores a compressed version of the rotation
* @param negativeXToggle the flag to set true if the X value of rotation is negative
* @param negativeYToggle the flag to set true if the Y value of rotation is negative
* @param negativeZToggle the flag to set true if the Z value of rotation is negative
* @param rotation the Java rotation value
*/
private void onRotationUpdate(EntityData dataLeech, EntityFlag negativeXToggle, EntityFlag negativeYToggle, EntityFlag negativeZToggle, Rotation rotation) {
// Indicate that rotation should be checked
setFlag(EntityFlag.BRIBED, true);
int rotationX = getRotation(rotation.getPitch());
int rotationY = getRotation(rotation.getYaw());
int rotationZ = getRotation(rotation.getRoll());
// The top bit acts like binary and determines if each rotation goes above 100
// We don't do this for the negative values out of concerns of the number being too big
int topBit = (Math.abs(rotationX) >= 100 ? 4 : 0) + (Math.abs(rotationY) >= 100 ? 2 : 0) + (Math.abs(rotationZ) >= 100 ? 1 : 0);
int value = (topBit * 1000000) + ((Math.abs(rotationX) % 100) * 10000) + ((Math.abs(rotationY) % 100) * 100) + (Math.abs(rotationZ) % 100);
dirtyMetadata.put(dataLeech, value);
// Set the entity flags if a value is negative
setFlag(negativeXToggle, rotationX < 0);
setFlag(negativeYToggle, rotationY < 0);
setFlag(negativeZToggle, rotationZ < 0);
}
@Override
public void updateBedrockMetadata() {
if (secondEntity != null) {
secondEntity.updateBedrockMetadata();
}
super.updateBedrockMetadata();
if (positionUpdateRequired) {
positionUpdateRequired = false;
updatePosition();
}
}
@Override
protected void setInvisible(boolean value) {
// Check if the armour stand is invisible and store accordingly
if (primaryEntity) {
isInvisible = value;
updateSecondEntityStatus(false);
}
}
@Override
public void setHelmet(ItemData helmet) {
super.setHelmet(helmet);
updateSecondEntityStatus(true);
}
@Override
public void setChestplate(ItemData chestplate) {
super.setChestplate(chestplate);
updateSecondEntityStatus(true);
}
@Override
public void setLeggings(ItemData leggings) {
super.setLeggings(leggings);
updateSecondEntityStatus(true);
}
@Override
public void setBoots(ItemData boots) {
super.setBoots(boots);
updateSecondEntityStatus(true);
}
@Override
public void setHand(ItemData hand) {
super.setHand(hand);
updateSecondEntityStatus(true);
}
@Override
public void setOffHand(ItemData offHand) {
super.setOffHand(offHand);
updateSecondEntityStatus(true);
}
/**
* Determine if we need to load or unload the second entity.
*
* @param sendMetadata whether to send a metadata update after a change.
*/
private void updateSecondEntityStatus(boolean sendMetadata) {
// A secondary entity always has to have the offset applied, so it remains invisible and the nametag shows.
if (!primaryEntity) return;
if (!isInvisible || isMarker) {
// It is either impossible to show armor, or the armor stand isn't invisible. We good.
setFlag(EntityFlag.INVISIBLE, false);
updateOffsetRequirement(false);
if (positionUpdateRequired) {
positionUpdateRequired = false;
updatePosition();
}
if (secondEntity != null) {
secondEntity.despawnEntity();
secondEntity = null;
}
return;
}
boolean isNametagEmpty = nametag.isEmpty();
if (!isNametagEmpty && (!helmet.equals(ItemData.AIR) || !chestplate.equals(ItemData.AIR) || !leggings.equals(ItemData.AIR)
|| !boots.equals(ItemData.AIR) || !hand.equals(ItemData.AIR) || !offHand.equals(ItemData.AIR))) {
// If the second entity exists, no need to recreate it.
// We can't stuff this check above or else it'll fall into another else case and delete the second entity
if (secondEntity != null) return;
// Create the second entity. It doesn't need to worry about the items, but it does need to worry about
// the metadata as it will hold the name tag.
secondEntity = new ArmorStandEntity(session, 0, session.getEntityCache().getNextEntityId().incrementAndGet(), null,
EntityDefinitions.ARMOR_STAND, position, motion, yaw, pitch, headYaw);
secondEntity.primaryEntity = false;
if (!this.positionRequiresOffset) {
// Ensure the offset is applied for the 0 scale
secondEntity.position = secondEntity.applyOffsetToPosition(secondEntity.position);
}
// Copy metadata
secondEntity.isSmall = isSmall;
//secondEntity.getDirtyMetadata().putAll(dirtyMetadata); //TODO check
secondEntity.flags.merge(this.flags);
// Guarantee this copy is NOT invisible
secondEntity.setFlag(EntityFlag.INVISIBLE, false);
// Scale to 0 to show nametag
secondEntity.getDirtyMetadata().put(EntityData.SCALE, 0.0f);
// No bounding box as we don't want to interact with this entity
secondEntity.getDirtyMetadata().put(EntityData.BOUNDING_BOX_WIDTH, 0.0f);
secondEntity.getDirtyMetadata().put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f);
secondEntity.spawnEntity();
// Reset scale of the proper armor stand
this.dirtyMetadata.put(EntityData.SCALE, isSmall ? 0.55f : 1f);
// Set the proper armor stand to invisible to show armor
setFlag(EntityFlag.INVISIBLE, true);
// Update the position of the armor stand
updateOffsetRequirement(false);
} else if (isNametagEmpty) {
// We can just make an invisible entity
// Reset scale of the proper armor stand
dirtyMetadata.put(EntityData.SCALE, isSmall ? 0.55f : 1f);
// Set the proper armor stand to invisible to show armor
setFlag(EntityFlag.INVISIBLE, true);
// Update offset
updateOffsetRequirement(false);
if (secondEntity != null) {
secondEntity.despawnEntity();
secondEntity = null;
}
} else {
// Nametag is not empty and there is no armor
// We don't need to make a new entity
setFlag(EntityFlag.INVISIBLE, false);
dirtyMetadata.put(EntityData.SCALE, 0.0f);
// As the above is applied, we need an offset
updateOffsetRequirement(true);
if (secondEntity != null) {
secondEntity.despawnEntity();
secondEntity = null;
}
}
if (sendMetadata) {
this.updateBedrockMetadata();
}
}
private int getRotation(float rotation) {
rotation = rotation % 360f;
if (rotation < -180f) {
rotation += 360f;
} else if (rotation >= 180f) {
// 181 -> -179
rotation = -(180 - (rotation - 180));
}
return (int) rotation;
}
/**
* If this armor stand is not a marker, set its bounding box size and scale.
*/
private void toggleSmallStatus() {
dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, isSmall ? 0.25f : definition.width());
dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, isSmall ? 0.9875f : definition.height());
dirtyMetadata.put(EntityData.SCALE, isSmall ? 0.55f : 1f);
}
/**
* @return the selected position with the position offset applied.
*/
private Vector3f applyOffsetToPosition(Vector3f position) {
return position.add(0d, definition.height() * (isSmall ? 0.55d : 1d), 0d);
}
/**
* @return an adjusted offset for the new small status.
*/
private Vector3f fixOffsetForSize(Vector3f position, boolean isNowSmall) {
position = removeOffsetFromPosition(position);
return position.add(0d, definition.height() * (isNowSmall ? 0.55d : 1d), 0d);
}
/**
* @return the selected position with the position offset removed.
*/
private Vector3f removeOffsetFromPosition(Vector3f position) {
return position.sub(0d, definition.height() * (isSmall ? 0.55d : 1d), 0d);
}
/**
* Set the offset to a new value; if it changed, update the position, too.
*/
private void updateOffsetRequirement(boolean newValue) {
if (newValue != positionRequiresOffset) {
this.positionRequiresOffset = newValue;
if (positionRequiresOffset) {
this.position = applyOffsetToPosition(position);
} else {
this.position = removeOffsetFromPosition(position);
}
positionUpdateRequired = true;
}
}
/**
* Updates position without calling movement code.
*/
private void updatePosition() {
MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket();
moveEntityPacket.setRuntimeEntityId(geyserId);
moveEntityPacket.setPosition(position);
moveEntityPacket.setRotation(Vector3f.from(yaw, yaw, yaw));
moveEntityPacket.setOnGround(onGround);
moveEntityPacket.setTeleported(false);
session.sendUpstreamPacket(moveEntityPacket);
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class BatEntity extends AmbientEntity {
public BatEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setBatFlags(ByteEntityMetadata entityMetadata) {
byte xd = entityMetadata.getPrimitiveValue();
setFlag(EntityFlag.RESTING, (xd & 0x01) == 0x01);
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class CreatureEntity extends MobEntity {
public CreatureEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class FlyingEntity extends MobEntity {
public FlyingEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class GlowSquidEntity extends SquidEntity {
public GlowSquidEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class GolemEntity extends CreatureEntity {
public GolemEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class IronGolemEntity extends GolemEntity {
public IronGolemEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
// Indicate that we should show cracks through a resource pack
setFlag(EntityFlag.BRIBED, true);
// Required, or else the overlay is black
dirtyMetadata.put(EntityData.COLOR_2, (byte) 0);
}
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class MagmaCubeEntity extends SlimeEntity {
public MagmaCubeEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
updateJump(isOnGround);
super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
}
@Override
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
updateJump(isOnGround);
super.moveAbsolute(position, yaw, pitch, headYaw, isOnGround, teleported);
}
public void updateJump(boolean newOnGround) {
if (newOnGround != onGround) {
// Add the jumping effect to the magma cube
dirtyMetadata.put(EntityData.CLIENT_EVENT, (byte) (newOnGround ? 1 : 2));
updateBedrockMetadata();
}
}
}

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import lombok.Getter;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.entity.LivingEntity;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class MobEntity extends LivingEntity {
/**
* If another mob is holding this mob by a leash, this variable tracks their Bedrock entity ID.
*/
@Getter
private long leashHolderBedrockId;
public MobEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
protected void initializeMetadata() {
super.initializeMetadata();
setLeashHolderBedrockId(-1);
}
public void setMobFlags(ByteEntityMetadata entityMetadata) {
byte xd = entityMetadata.getPrimitiveValue();
setFlag(EntityFlag.NO_AI, (xd & 0x01) == 0x01);
}
public void setLeashHolderBedrockId(long bedrockId) {
this.leashHolderBedrockId = bedrockId;
dirtyMetadata.put(EntityData.LEASH_HOLDER_EID, bedrockId);
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class SlimeEntity extends MobEntity {
public SlimeEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setScale(IntEntityMetadata entityMetadata) {
dirtyMetadata.put(EntityData.SCALE, 0.10f + entityMetadata.getPrimitiveValue());
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class SnowGolemEntity extends GolemEntity {
public SnowGolemEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setSnowGolemFlags(ByteEntityMetadata entityMetadata) {
byte xd = entityMetadata.getPrimitiveValue();
// Handle the visibility of the pumpkin
setFlag(EntityFlag.SHEARED, (xd & 0x10) != 0x10);
}
}

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.MoveEntityDeltaPacket;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.entity.Tickable;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.UUID;
public class SquidEntity extends WaterEntity implements Tickable {
private float targetPitch;
private float targetYaw;
private boolean inWater;
public SquidEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
public void tick() {
boolean pitchChanged;
boolean yawChanged;
float oldPitch = pitch;
if (inWater) {
float oldYaw = yaw;
pitch += (targetPitch - pitch) * 0.1f;
yaw += (targetYaw - yaw) * 0.1f;
yawChanged = oldYaw != yaw;
} else {
pitch += (-90 - pitch) * 0.02f;
yawChanged = false;
}
pitchChanged = oldPitch != pitch;
if (pitchChanged || yawChanged) {
MoveEntityDeltaPacket packet = new MoveEntityDeltaPacket();
packet.setRuntimeEntityId(geyserId);
if (pitchChanged) {
packet.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH);
packet.setPitch(pitch);
}
if (yawChanged) {
packet.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_YAW);
packet.setYaw(yaw);
}
session.sendUpstreamPacket(packet);
}
}
@Override
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
checkInWater();
}
@Override
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
super.moveAbsolute(position, yaw, pitch, headYaw, isOnGround, teleported);
checkInWater();
}
@Override
public void setYaw(float yaw) {
// Let the Java server control yaw when the squid is out of water
if (!inWater) {
this.yaw = yaw;
}
}
@Override
public void setPitch(float pitch) {
}
@Override
public void setHeadYaw(float headYaw) {
}
@Override
public void setMotion(Vector3f motion) {
super.setMotion(motion);
double horizontalSpeed = Math.sqrt(motion.getX() * motion.getX() + motion.getZ() * motion.getZ());
targetPitch = (float) Math.toDegrees(-Math.atan2(horizontalSpeed, motion.getY()));
targetYaw = (float) Math.toDegrees(-Math.atan2(motion.getX(), motion.getZ()));
}
@Override
public Vector3f getBedrockRotation() {
return Vector3f.from(pitch, yaw, yaw);
}
private void checkInWater() {
if (getFlag(EntityFlag.RIDING)) {
inWater = false;
} else {
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
inWater = BlockStateValues.getWaterLevel(block) != -1;
}
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class WaterEntity extends CreatureEntity {
public WaterEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living.animal;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.entity.living.AgeableEntity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.registry.type.ItemMapping;
import java.util.UUID;
public class AnimalEntity extends AgeableEntity {
public AnimalEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
/**
* @param javaIdentifierStripped the stripped Java identifier of the item that is potential breeding food. For example,
* <code>wheat</code>.
* @return true if this is a valid item to breed with for this animal.
*/
public boolean canEat(String javaIdentifierStripped, ItemMapping mapping) {
// This is what it defaults to. OK.
return javaIdentifierStripped.equals("wheat");
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.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.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.registry.type.ItemMapping;
import java.util.UUID;
public class AxolotlEntity extends AnimalEntity {
public AxolotlEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setVariant(IntEntityMetadata entityMetadata) {
int variant = entityMetadata.getPrimitiveValue();
switch (variant) {
case 1 -> variant = 3; // Java - "Wild" (brown)
case 3 -> variant = 1; // Java - cyan
}
dirtyMetadata.put(EntityData.VARIANT, variant);
}
public void setPlayingDead(BooleanEntityMetadata entityMetadata) {
setFlag(EntityFlag.PLAYING_DEAD, entityMetadata.getPrimitiveValue());
}
@Override
public boolean canEat(String javaIdentifierStripped, ItemMapping mapping) {
return javaIdentifierStripped.equals("tropical_fish_bucket");
}
@Override
protected int getMaxAir() {
return 6000;
}
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.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.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.registry.type.ItemMapping;
import java.util.UUID;
public class BeeEntity extends AnimalEntity {
public BeeEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setBeeFlags(ByteEntityMetadata entityMetadata) {
byte xd = entityMetadata.getPrimitiveValue();
// Bee is performing sting attack; trigger animation
if ((xd & 0x02) == 0x02) {
EntityEventPacket packet = new EntityEventPacket();
packet.setRuntimeEntityId(geyserId);
packet.setType(EntityEventType.ATTACK_START);
packet.setData(0);
session.sendUpstreamPacket(packet);
}
// If the bee has stung
dirtyMetadata.put(EntityData.MARK_VARIANT, (xd & 0x04) == 0x04 ? 1 : 0);
// If the bee has nectar or not
setFlag(EntityFlag.POWERED, (xd & 0x08) == 0x08);
}
public void setAngerTime(IntEntityMetadata entityMetadata) {
// Converting "anger time" to a boolean
setFlag(EntityFlag.ANGRY, entityMetadata.getPrimitiveValue() > 0);
}
@Override
public boolean canEat(String javaIdentifierStripped, ItemMapping mapping) {
return session.getTagCache().isFlower(mapping);
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living.animal;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.registry.type.ItemMapping;
import java.util.UUID;
public class ChickenEntity extends AnimalEntity {
public ChickenEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
public boolean canEat(String javaIdentifierStripped, ItemMapping mapping) {
return javaIdentifierStripped.contains("seeds");
}
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.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.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.registry.type.ItemMapping;
import java.util.UUID;
public class FoxEntity extends AnimalEntity {
public FoxEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setFoxVariant(IntEntityMetadata entityMetadata) {
dirtyMetadata.put(EntityData.VARIANT, entityMetadata.getPrimitiveValue());
}
public void setFoxFlags(ByteEntityMetadata entityMetadata) {
byte xd = entityMetadata.getPrimitiveValue();
setFlag(EntityFlag.SITTING, (xd & 0x01) == 0x01);
setFlag(EntityFlag.SNEAKING, (xd & 0x04) == 0x04);
setFlag(EntityFlag.INTERESTED, (xd & 0x08) == 0x08);
setFlag(EntityFlag.SLEEPING, (xd & 0x20) == 0x20);
}
@Override
public boolean canEat(String javaIdentifierStripped, ItemMapping mapping) {
return session.getTagCache().isFoxFood(mapping);
}
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.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.nukkitx.math.vector.Vector3f;
import lombok.Getter;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class GoatEntity extends AnimalEntity {
private static final float LONG_JUMPING_HEIGHT = 1.3f * 0.7f;
private static final float LONG_JUMPING_WIDTH = 0.9f * 0.7f;
@Getter
private boolean isScreamer;
public GoatEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setScreamer(BooleanEntityMetadata entityMetadata) {
// Metadata not used in Bedrock Edition
isScreamer = entityMetadata.getPrimitiveValue();
}
@Override
protected void setDimensions(Pose pose) {
if (pose == Pose.LONG_JUMPING) {
setBoundingBoxWidth(LONG_JUMPING_WIDTH);
setBoundingBoxHeight(LONG_JUMPING_HEIGHT);
} else {
super.setDimensions(pose);
}
}
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living.animal;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.registry.type.ItemMapping;
import org.geysermc.connector.utils.DimensionUtils;
import java.util.UUID;
public class HoglinEntity extends AnimalEntity {
private boolean isImmuneToZombification;
public HoglinEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
public void setImmuneToZombification(BooleanEntityMetadata entityMetadata) {
// Apply shaking effect if not in the nether and zombification is possible
this.isImmuneToZombification = entityMetadata.getPrimitiveValue();
setFlag(EntityFlag.SHAKING, isShaking());
}
@Override
protected boolean isShaking() {
return (!isImmuneToZombification && !session.getDimension().equals(DimensionUtils.NETHER)) || super.isShaking();
}
@Override
public boolean canEat(String javaIdentifierStripped, ItemMapping mapping) {
return javaIdentifierStripped.equals("crimson_fungus");
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living.animal;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class MooshroomEntity extends AnimalEntity {
public MooshroomEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living.animal;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.EntityDefinition;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.registry.type.ItemMapping;
import java.util.UUID;
public class OcelotEntity extends AnimalEntity {
public OcelotEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
public boolean canEat(String javaIdentifierStripped, ItemMapping mapping) {
return javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon");
}
}

Some files were not shown because too many files have changed in this diff Show more