Merge remote-tracking branch 'origin/master' into floodgate-2.0

# Conflicts:
#	connector/src/main/java/org/geysermc/connector/GeyserConnector.java
#	connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java
This commit is contained in:
Tim203 2020-11-18 19:45:25 +01:00
commit b8f398aa3c
No known key found for this signature in database
GPG Key ID: 064EE9F5BF7C3EE8
149 changed files with 7853 additions and 6870 deletions

2
.github/FUNDING.yml vendored
View File

@ -1,7 +1,7 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: GeyserMC
patreon: #GeyserMC # Disabled currently
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel

1
Jenkinsfile vendored
View File

@ -24,6 +24,7 @@ pipeline {
when {
branch "master"
}
steps {
sh 'mvn javadoc:jar source:jar deploy -DskipTests'
}

View File

@ -18,7 +18,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have now joined us here!
### Currently supporting Minecraft Bedrock v1.16.x and Minecraft Java v1.16.3.
### Currently supporting Minecraft Bedrock v1.16.100 and Minecraft Java v1.16.4.
## Setting Up
Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set up Geyser.

View File

@ -6,15 +6,15 @@
<parent>
<groupId>org.geysermc</groupId>
<artifactId>bootstrap-parent</artifactId>
<version>1.1.0</version>
<relativePath>../</relativePath>
<version>1.2.0-SNAPSHOT</version>
</parent>
<artifactId>bootstrap-bungeecord</artifactId>
<dependencies>
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>connector</artifactId>
<version>1.1.0</version>
<version>1.2.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>

View File

@ -27,10 +27,10 @@ package org.geysermc.platform.bungeecord;
import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.plugin.Plugin;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.common.PlatformType;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.dump.BootstrapDumpInfo;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;

View File

@ -6,12 +6,11 @@
<parent>
<groupId>org.geysermc</groupId>
<artifactId>geyser-parent</artifactId>
<version>parent</version>
<relativePath>../</relativePath>
<version>1.2.0-SNAPSHOT</version>
</parent>
<artifactId>bootstrap-parent</artifactId>
<version>1.1.0</version>
<packaging>pom</packaging>
<repositories>
<repository>
<id>spigot-public</id>

View File

@ -6,15 +6,15 @@
<parent>
<groupId>org.geysermc</groupId>
<artifactId>bootstrap-parent</artifactId>
<version>1.1.0</version>
<relativePath>../</relativePath>
<version>1.2.0-SNAPSHOT</version>
</parent>
<artifactId>bootstrap-spigot</artifactId>
<dependencies>
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>connector</artifactId>
<version>1.1.0</version>
<version>1.2.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
@ -26,7 +26,7 @@
<dependency>
<groupId>us.myles</groupId>
<artifactId>viaversion</artifactId>
<version>3.1.1</version>
<version>3.2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>

View File

@ -27,10 +27,10 @@ package org.geysermc.platform.spigot;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.common.PlatformType;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.dump.BootstrapDumpInfo;
import org.geysermc.connector.network.translators.world.WorldManager;
@ -43,6 +43,7 @@ import org.geysermc.platform.spigot.command.GeyserSpigotCommandManager;
import org.geysermc.platform.spigot.command.SpigotCommandSender;
import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener;
import org.geysermc.platform.spigot.world.GeyserSpigotWorldManager;
import us.myles.ViaVersion.api.Via;
import java.io.File;
import java.io.IOException;
@ -121,6 +122,13 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
this.geyserCommandManager = new GeyserSpigotCommandManager(this, connector);
boolean isViaVersion = (Bukkit.getPluginManager().getPlugin("ViaVersion") != null);
if (isViaVersion) {
if (!isCompatible(Via.getAPI().getVersion().replace("-SNAPSHOT", ""), "3.2.0")) {
geyserLogger.warning(LanguageUtils.getLocaleStringLog("geyser.bootstrap.viaversion.too_old",
"https://ci.viaversion.com/job/ViaVersion/"));
isViaVersion = false;
}
}
// Used to determine if Block.getBlockData() is present.
boolean isLegacy = !isCompatible(Bukkit.getServer().getVersion(), "1.13.0");
if (isLegacy)

View File

@ -26,6 +26,7 @@
package org.geysermc.platform.spigot.world;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.steveice10.mc.protocol.MinecraftConstants;
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
@ -41,14 +42,32 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.FileUtils;
import org.geysermc.connector.utils.GameRule;
import org.geysermc.connector.utils.LanguageUtils;
import us.myles.ViaVersion.protocols.protocol1_13_1to1_13.Protocol1_13_1To1_13;
import us.myles.ViaVersion.protocols.protocol1_16_2to1_16_1.data.MappingData;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.MappingData;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
import java.io.InputStream;
import java.util.List;
public class GeyserSpigotWorldManager extends GeyserWorldManager {
/**
* The current client protocol version for ViaVersion usage.
*/
private static final int CLIENT_PROTOCOL_VERSION = MinecraftConstants.PROTOCOL_VERSION;
/**
* Whether the server is pre-1.13.
*/
private final boolean isLegacy;
/**
* Whether the server is pre-1.16 and therefore does not support 3D biomes on an API level guaranteed.
*/
private final boolean use3dBiomes;
/**
* You need ViaVersion to connect to an older server with Geyser.
@ -83,8 +102,9 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
}
// Only load in the biomes that are present in this version of Minecraft
for (Biome enumBiome : Biome.values()) {
if (biomes.has(enumBiome.toString())) {
biomeToIdMap.put(enumBiome.ordinal(), biomes.get(enumBiome.toString()).intValue());
JsonNode biome = biomes.get(enumBiome.toString());
if (biome != null) {
biomeToIdMap.put(enumBiome.ordinal(), biome.intValue());
} else {
GeyserConnector.getInstance().getLogger().debug("No biome mapping found for " + enumBiome.toString() +
", defaulting to 0");
@ -99,7 +119,7 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
if ((this.isLegacy && !this.isViaVersion)
|| session.getPlayerEntity() == null
|| (bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
return BlockTranslator.AIR;
return BlockTranslator.JAVA_AIR_ID;
}
World world = bukkitPlayer.getWorld();
if (isLegacy) {
@ -111,28 +131,38 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
public static int getLegacyBlock(GeyserSession session, int x, int y, int z, boolean isViaVersion) {
if (isViaVersion) {
return getLegacyBlock(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld(), x, y, z, true);
Player bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
// Get block entity storage
BlockStorage storage = Via.getManager().getConnection(bukkitPlayer.getUniqueId()).get(BlockStorage.class);
return getLegacyBlock(storage, bukkitPlayer.getWorld(), x, y, z);
} else {
return BlockTranslator.AIR;
return BlockTranslator.JAVA_AIR_ID;
}
}
@SuppressWarnings("deprecation")
public static int getLegacyBlock(World world, int x, int y, int z, boolean isViaVersion) {
if (isViaVersion) {
Block block = world.getBlockAt(x, y, z);
// Black magic that gets the old block state ID
int oldBlockId = (block.getType().getId() << 4) | (block.getData() & 0xF);
// Convert block state from old version -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2
int thirteenBlockId = us.myles.ViaVersion.protocols.protocol1_13to1_12_2.data.MappingData.blockMappings.getNewId(oldBlockId);
int thirteenPointOneBlockId = Protocol1_13_1To1_13.getNewBlockStateId(thirteenBlockId);
int fourteenBlockId = us.myles.ViaVersion.protocols.protocol1_14to1_13_2.data.MappingData.blockStateMappings.getNewId(thirteenPointOneBlockId);
int fifteenBlockId = us.myles.ViaVersion.protocols.protocol1_15to1_14_4.data.MappingData.blockStateMappings.getNewId(fourteenBlockId);
int sixteenBlockId = us.myles.ViaVersion.protocols.protocol1_16to1_15_2.data.MappingData.blockStateMappings.getNewId(fifteenBlockId);
return MappingData.blockStateMappings.getNewId(sixteenBlockId);
} else {
return BlockTranslator.AIR;
public static int getLegacyBlock(BlockStorage storage, World world, int x, int y, int z) {
Block block = world.getBlockAt(x, y, z);
// Black magic that gets the old block state ID
int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF);
// Convert block state from old version (1.12.2) -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2
blockId = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData().getNewBlockId(blockId);
List<Pair<Integer, Protocol>> protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION,
ProtocolVersion.v1_13.getId());
// Translate block entity differences - some information was stored in block tags and not block states
if (storage.isWelcome(blockId)) { // No getOrDefault method
BlockStorage.ReplacementData data = storage.get(new Position(x, (short) y, z));
if (data != null && data.getReplacement() != -1) {
blockId = data.getReplacement();
}
}
for (int i = protocolList.size() - 1; i >= 0; i--) {
MappingData mappingData = protocolList.get(i).getValue().getMappingData();
if (mappingData != null) {
blockId = mappingData.getNewBlockStateId(blockId);
}
}
return blockId;
}
@Override
@ -144,11 +174,13 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
return;
}
World world = bukkitPlayer.getWorld();
if (this.isLegacy) {
if (this.isLegacy) {
// Get block entity storage
BlockStorage storage = Via.getManager().getConnection(bukkitPlayer.getUniqueId()).get(BlockStorage.class);
for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
for (int blockZ = 0; blockZ < 16; blockZ++) {
for (int blockX = 0; blockX < 16; blockX++) {
chunk.set(blockX, blockY, blockZ, getLegacyBlock(world, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ, true));
chunk.set(blockX, blockY, blockZ, getLegacyBlock(storage, world, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ));
}
}
}
@ -158,7 +190,7 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
for (int blockZ = 0; blockZ < 16; blockZ++) {
for (int blockX = 0; blockX < 16; blockX++) {
Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ);
int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), 0);
int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID);
chunk.set(blockX, blockY, blockZ, id);
}
}

View File

@ -6,15 +6,15 @@
<parent>
<groupId>org.geysermc</groupId>
<artifactId>bootstrap-parent</artifactId>
<version>1.1.0</version>
<relativePath>../</relativePath>
<version>1.2.0-SNAPSHOT</version>
</parent>
<artifactId>bootstrap-sponge</artifactId>
<dependencies>
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>connector</artifactId>
<version>1.1.0</version>
<version>1.2.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>

View File

@ -26,10 +26,10 @@
package org.geysermc.platform.sponge;
import com.google.inject.Inject;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.common.PlatformType;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.dump.BootstrapDumpInfo;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;

View File

@ -6,15 +6,15 @@
<parent>
<groupId>org.geysermc</groupId>
<artifactId>bootstrap-parent</artifactId>
<version>1.1.0</version>
<relativePath>../</relativePath>
<version>1.2.0-SNAPSHOT</version>
</parent>
<artifactId>bootstrap-standalone</artifactId>
<dependencies>
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>connector</artifactId>
<version>1.1.0</version>
<version>1.2.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>

View File

@ -25,17 +25,23 @@
package org.geysermc.platform.standalone;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import lombok.Getter;
import net.minecrell.terminalconsole.TerminalConsoleAppender;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.common.PlatformType;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
import org.geysermc.connector.dump.BootstrapDumpInfo;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
@ -50,7 +56,8 @@ import java.lang.reflect.Method;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;
public class GeyserStandaloneBootstrap implements GeyserBootstrap {
@ -67,6 +74,9 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
private GeyserConnector connector;
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final Map<String, String> argsConfigKeys = new HashMap<>();
public static void main(String[] args) {
GeyserStandaloneBootstrap bootstrap = new GeyserStandaloneBootstrap();
@ -74,6 +84,8 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
boolean useGuiOpts = bootstrap.useGui;
String configFilenameOpt = bootstrap.configFilename;
List<BeanPropertyDefinition> availableProperties = getPOJOForClass(GeyserJacksonConfiguration.class);
for (int i = 0; i < args.length; i++) {
// By default, standalone Geyser will check if it should open the GUI based on if the GUI is null
// Optionally, you can force the use of a GUI or no GUI by specifying args
@ -91,11 +103,11 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
case "--config":
case "-c":
if (i >= args.length - 1) {
System.err.println(MessageFormat.format(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.confignotspecified"), "-c"));
System.err.println(MessageFormat.format(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.config_not_specified"), "-c"));
return;
}
configFilenameOpt = args[i+1]; i++;
System.out.println(MessageFormat.format(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.configspecified"), configFilenameOpt));
System.out.println(MessageFormat.format(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.config_specified"), configFilenameOpt));
break;
case "--help":
case "-h":
@ -106,8 +118,43 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
System.out.println(" --gui, --nogui " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.gui"));
return;
default:
String badArgMsg = LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.unrecognised");
System.err.println(MessageFormat.format(badArgMsg, arg));
// We have likely added a config option argument
if (arg.startsWith("--")) {
// Split the argument by an =
String[] argParts = arg.substring(2).split("=");
if (argParts.length == 2) {
// Split the config key by . to allow for nested options
String[] configKeyParts = argParts[0].split("\\.");
// Loop the possible config options to check the passed key is valid
boolean found = false;
for (BeanPropertyDefinition property : availableProperties) {
if (configKeyParts[0].equals(property.getName())) {
if (configKeyParts.length > 1) {
// Loop sub-section options to check the passed key is valid
for (BeanPropertyDefinition subProperty : getPOJOForClass(property.getRawPrimaryType())) {
if (configKeyParts[1].equals(subProperty.getName())) {
found = true;
break;
}
}
} else {
found = true;
}
break;
}
}
// Add the found key to the stored list for later usage
if (found) {
argsConfigKeys.put(argParts[0], argParts[1]);
break;
}
}
}
System.err.println(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.unrecognised", arg));
return;
}
}
@ -148,6 +195,9 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
try {
File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()));
geyserConfig = FileUtils.loadConfig(configFile, GeyserStandaloneConfiguration.class);
handleArgsConfigOptions();
if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) {
geyserConfig.setAutoconfiguredRemote(true); // Doesn't really need to be set but /shrug
geyserConfig.getRemote().setAddress("127.0.0.1");
@ -223,4 +273,99 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
public BootstrapDumpInfo getDumpInfo() {
return new GeyserStandaloneDumpInfo(this);
}
/**
* Get the {@link BeanPropertyDefinition}s for the given class
*
* @param clazz The class to get the definitions for
* @return A list of {@link BeanPropertyDefinition} for the given class
*/
public static List<BeanPropertyDefinition> getPOJOForClass(Class<?> clazz) {
JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructType(clazz);
// Introspect the given type
BeanDescription beanDescription = OBJECT_MAPPER.getSerializationConfig().introspect(javaType);
// Find properties
List<BeanPropertyDefinition> properties = beanDescription.findProperties();
// Get the ignored properties
Set<String> ignoredProperties = OBJECT_MAPPER.getSerializationConfig().getAnnotationIntrospector()
.findPropertyIgnorals(beanDescription.getClassInfo()).getIgnored();
// Filter properties removing the ignored ones
return properties.stream()
.filter(property -> !ignoredProperties.contains(property.getName()))
.collect(Collectors.toList());
}
/**
* Set a POJO property value on an object
*
* @param property The {@link BeanPropertyDefinition} to set
* @param parentObject The object to alter
* @param value The new value of the property
*/
private static void setConfigOption(BeanPropertyDefinition property, Object parentObject, Object value) {
Object parsedValue = value;
// Change the values type if needed
if (int.class.equals(property.getRawPrimaryType())) {
parsedValue = Integer.valueOf((String) parsedValue);
} else if (boolean.class.equals(property.getRawPrimaryType())) {
parsedValue = Boolean.valueOf((String) parsedValue);
}
// Force the value to be set
AnnotatedField field = property.getField();
field.fixAccess(true);
field.setValue(parentObject, parsedValue);
}
/**
* Update the loaded {@link GeyserStandaloneConfiguration} with any values passed in the command line arguments
*/
private void handleArgsConfigOptions() {
// Get the available properties from the class
List<BeanPropertyDefinition> availableProperties = getPOJOForClass(GeyserJacksonConfiguration.class);
for (Map.Entry<String, String> configKey : argsConfigKeys.entrySet()) {
String[] configKeyParts = configKey.getKey().split("\\.");
// Loop over the properties looking for any matches against the stored one from the argument
for (BeanPropertyDefinition property : availableProperties) {
if (configKeyParts[0].equals(property.getName())) {
if (configKeyParts.length > 1) {
// Loop through the sub property if the first part matches
for (BeanPropertyDefinition subProperty : getPOJOForClass(property.getRawPrimaryType())) {
if (configKeyParts[1].equals(subProperty.getName())) {
geyserLogger.info(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.set_config_option", configKey.getKey(), configKey.getValue()));
// Set the sub property value on the config
try {
Object subConfig = property.getGetter().callOn(geyserConfig);
setConfigOption(subProperty, subConfig, configKey.getValue());
} catch (Exception e) {
geyserLogger.error("Failed to set config option: " + property.getFullName());
}
break;
}
}
} else {
geyserLogger.info(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.set_config_option", configKey.getKey(), configKey.getValue()));
// Set the property value on the config
try {
setConfigOption(property, geyserConfig, configKey.getValue());
} catch (Exception e) {
geyserLogger.error("Failed to set config option: " + property.getFullName());
}
}
break;
}
}
}
}
}

View File

@ -6,21 +6,21 @@
<parent>
<groupId>org.geysermc</groupId>
<artifactId>bootstrap-parent</artifactId>
<version>1.1.0</version>
<relativePath>../</relativePath>
<version>1.2.0-SNAPSHOT</version>
</parent>
<artifactId>bootstrap-velocity</artifactId>
<dependencies>
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>connector</artifactId>
<version>1.1.0</version>
<version>1.2.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>1.0.0-SNAPSHOT</version>
<version>1.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>

View File

@ -33,9 +33,9 @@ import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.proxy.ProxyServer;
import lombok.Getter;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.common.PlatformType;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.dump.BootstrapDumpInfo;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
@ -121,7 +121,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
this.connector = GeyserConnector.start(PlatformType.VELOCITY, this);
this.geyserCommandManager = new GeyserVelocityCommandManager(connector);
this.commandManager.register(new GeyserVelocityCommandExecutor(connector), "geyser");
this.commandManager.register("geyser", new GeyserVelocityCommandExecutor(connector));
if (geyserConfig.isLegacyPingPassthrough()) {
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(connector);
} else {

View File

@ -6,11 +6,10 @@
<parent>
<groupId>org.geysermc</groupId>
<artifactId>geyser-parent</artifactId>
<version>parent</version>
<relativePath>../</relativePath>
<version>1.2.0-SNAPSHOT</version>
</parent>
<artifactId>common</artifactId>
<version>1.1.0</version>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>

View File

@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.common;
package org.geysermc.common;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@ -6,16 +6,15 @@
<parent>
<groupId>org.geysermc</groupId>
<artifactId>geyser-parent</artifactId>
<version>parent</version>
<relativePath>../</relativePath>
<version>1.2.0-SNAPSHOT</version>
</parent>
<artifactId>connector</artifactId>
<version>1.1.0</version>
<dependencies>
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>common</artifactId>
<version>1.1.0</version>
<version>1.2.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
@ -32,8 +31,8 @@
</dependency>
<dependency>
<groupId>com.github.CloudburstMC.Protocol</groupId>
<artifactId>bedrock-v408</artifactId>
<version>02f46a8700</version>
<artifactId>bedrock-v419</artifactId>
<version>ce59d39118</version>
<scope>compile</scope>
<exclusions>
<exclusion>
@ -111,7 +110,7 @@
<dependency>
<groupId>com.github.steveice10</groupId>
<artifactId>mcprotocollib</artifactId>
<version>1b01b1ffef</version>
<version>86e1901be5</version>
<scope>compile</scope>
<exclusions>
<exclusion>
@ -143,17 +142,23 @@
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<groupId>com.github.kyoripowered.adventure</groupId>
<artifactId>adventure-text-serializer-gson</artifactId>
<version>4.1.1</version>
<version>4d8a67d798</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<groupId>com.github.kyoripowered.adventure</groupId>
<artifactId>adventure-text-serializer-legacy</artifactId>
<version>4.1.1</version>
<version>0599048</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@ -283,6 +288,15 @@
</dependency>
</dependencies>
</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

@ -32,10 +32,10 @@ import com.nukkitx.network.raknet.RakNetConstants;
import com.nukkitx.protocol.bedrock.BedrockServer;
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.common.PlatformType;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.metrics.Metrics;
import org.geysermc.connector.network.ConnectorServerEventHandler;
@ -197,8 +197,7 @@ public class GeyserConnector {
}
}
if (config.isAboveBedrockNetherBuilding())
DimensionUtils.changeBedrockNetherId(); // Apply End dimension ID workaround to Nether
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();

View File

@ -25,7 +25,7 @@
package org.geysermc.connector.command.defaults;
import org.geysermc.connector.common.PlatformType;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;

View File

@ -25,7 +25,7 @@
package org.geysermc.connector.command.defaults;
import org.geysermc.connector.common.PlatformType;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;

View File

@ -81,6 +81,8 @@ public interface GeyserConfiguration {
boolean isForceResourcePacks();
boolean isXboxAchievementsEnabled();
int getCacheImages();
IMetricsInfo getMetrics();

View File

@ -107,6 +107,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
@JsonProperty("force-resource-packs")
private boolean forceResourcePacks = true;
@JsonProperty("xbox-achievements-enabled")
private boolean xboxAchievementsEnabled = false;
private MetricsInfo metrics = new MetricsInfo();
@Getter

View File

@ -27,7 +27,7 @@ package org.geysermc.connector.dump;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.geysermc.connector.common.PlatformType;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import java.util.List;

View File

@ -26,13 +26,12 @@
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.message.Message;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity {
@ -51,7 +50,7 @@ public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity {
metadata.put(EntityData.COMMAND_BLOCK_COMMAND, entityMetadata.getValue());
}
if (entityMetadata.getId() == 14) {
metadata.put(EntityData.COMMAND_BLOCK_LAST_OUTPUT, MessageUtils.getBedrockMessage((Message) entityMetadata.getValue()));
metadata.put(EntityData.COMMAND_BLOCK_LAST_OUTPUT, MessageTranslator.convertMessage(entityMetadata.getValue().toString()));
}
super.updateBedrockMetadata(entityMetadata, session);
}

View File

@ -54,7 +54,7 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.utils.AttributeUtils;
import org.geysermc.connector.utils.ChunkUtils;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import java.util.ArrayList;
import java.util.HashMap;
@ -67,8 +67,6 @@ public class Entity {
protected long entityId;
protected long geyserId;
protected String dimension;
protected Vector3f position;
protected Vector3f motion;
@ -100,7 +98,6 @@ public class Entity {
this.rotation = rotation;
this.valid = false;
this.dimension = "minecraft:overworld";
setPosition(position);
@ -321,7 +318,7 @@ public class Entity {
Message message = (Message) entityMetadata.getValue();
if (message != null)
// Always translate even if it's a TextMessage since there could be translatable parameters
metadata.put(EntityData.NAMETAG, MessageUtils.getTranslatedBedrockMessage(message, session.getLocale(), true));
metadata.put(EntityData.NAMETAG, MessageTranslator.convertMessage(message.toString(), session.getLocale()));
}
break;
case 3: // is custom name visible
@ -339,18 +336,6 @@ public class Entity {
metadata.getFlags().setFlag(EntityFlag.SLEEPING, true);
// Has to be a byte or it does not work
metadata.put(EntityData.PLAYER_FLAGS, (byte) 2);
if (entityId == session.getPlayerEntity().getEntityId()) {
Vector3i lastInteractionPos = session.getLastInteractionPosition();
metadata.put(EntityData.BED_POSITION, lastInteractionPos);
if (session.getConnector().getConfig().isCacheChunks()) {
int bed = session.getConnector().getWorldManager().getBlockAt(session, lastInteractionPos.getX(),
lastInteractionPos.getY(), lastInteractionPos.getZ());
// Bed has to be updated, or else player is floating in the air
ChunkUtils.updateBlock(session, bed, lastInteractionPos);
}
} else {
metadata.put(EntityData.BED_POSITION, Vector3i.from(position.getFloorX(), position.getFloorY() - 2, position.getFloorZ()));
}
metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.2f);
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.2f);
} else if (metadata.getFlags().getFlag(EntityFlag.SLEEPING)) {

View File

@ -69,7 +69,6 @@ public class ItemFrameEntity extends Entity {
public ItemFrameEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, HangingDirection direction) {
super(entityId, geyserId, entityType, position, motion, rotation);
NbtMapBuilder builder = NbtMap.builder();
NbtMapBuilder blockBuilder = NbtMap.builder()
.putString("name", "minecraft:frame")
.putInt("version", BlockTranslator.getBlockStateVersion());
@ -77,9 +76,7 @@ public class ItemFrameEntity extends Entity {
.putInt("facing_direction", direction.ordinal())
.putByte("item_frame_map_bit", (byte) 0)
.build());
builder.put("block", blockBuilder.build());
builder.putShort("id", (short) 199);
bedrockRuntimeId = BlockTranslator.getItemFrame(builder.build());
bedrockRuntimeId = BlockTranslator.getItemFrame(blockBuilder.build());
bedrockPosition = Vector3i.from(position.getFloorX(), position.getFloorY(), position.getFloorZ());
}
@ -101,14 +98,12 @@ public class ItemFrameEntity extends Entity {
ItemEntry itemEntry = ItemRegistry.getItem((ItemStack) entityMetadata.getValue());
NbtMapBuilder builder = NbtMap.builder();
String blockName = ItemRegistry.getBedrockIdentifer(itemEntry);
builder.putByte("Count", (byte) itemData.getCount());
if (itemData.getTag() != null) {
builder.put("tag", itemData.getTag().toBuilder().build());
}
builder.putShort("Damage", itemData.getDamage());
builder.putString("Name", blockName);
builder.putString("Name", itemEntry.getBedrockIdentifier());
NbtMapBuilder tag = getDefaultTag().toBuilder();
tag.put("Item", builder.build());
tag.putFloat("ItemDropChance", 1.0f);
@ -141,7 +136,7 @@ public class ItemFrameEntity extends Entity {
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0);
updateBlockPacket.setBlockPosition(bedrockPosition);
updateBlockPacket.setRuntimeId(0);
updateBlockPacket.setRuntimeId(BlockTranslator.BEDROCK_AIR_ID);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
@ -196,18 +191,6 @@ public class ItemFrameEntity extends Entity {
return session.getItemFrameCache().getOrDefault(position, -1);
}
/**
* Determines if the position contains an item frame.
* Does largely the same thing as getItemFrameEntityId, but for speed purposes is implemented separately,
* since every block destroy packet has to check for an item frame.
* @param position position of block.
* @param session GeyserSession.
* @return true if position contains item frame, false if not.
*/
public static boolean positionContainsItemFrame(GeyserSession session, Vector3i position) {
return session.getItemFrameCache().containsKey(position);
}
/**
* Force-remove from the position-to-ID map so it doesn't cause conflicts.
* @param session GeyserSession.

View File

@ -27,10 +27,24 @@ package org.geysermc.connector.entity;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
public class ItemedFireballEntity extends Entity {
public class ItemedFireballEntity extends ThrowableEntity {
private final Vector3f acceleration;
public ItemedFireballEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
super(entityId, geyserId, entityType, position, Vector3f.ZERO, rotation);
acceleration = motion;
}
@Override
protected void updatePosition(GeyserSession session) {
position = position.add(motion);
// TODO: While this reduces latency in position updating (needed for better fireball reflecting),
// TODO: movement is incredibly stiff. See if the MoveEntityDeltaPacket in 1.16.100 fixes this, and if not,
// TODO: only use this laggy movement for fireballs that be reflected
moveAbsoluteImmediate(session, position, rotation, false, true);
float drag = getDrag(session);
motion = motion.add(acceleration).mul(drag);
}
}

View File

@ -26,7 +26,9 @@
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.AttributeData;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
@ -42,6 +44,7 @@ import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.utils.AttributeUtils;
import org.geysermc.connector.utils.ChunkUtils;
import java.util.ArrayList;
import java.util.List;
@ -84,6 +87,17 @@ public class LivingEntity extends Entity {
case 10:
metadata.put(EntityData.EFFECT_AMBIENT, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0));
break;
case 13: // Bed Position
Position bedPosition = (Position) entityMetadata.getValue();
if (bedPosition != null) {
metadata.put(EntityData.BED_POSITION, Vector3i.from(bedPosition.getX(), bedPosition.getY(), bedPosition.getZ()));
if (session.getConnector().getConfig().isCacheChunks()) {
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);
}
}
break;
}
super.updateBedrockMetadata(entityMetadata, session);

View File

@ -27,14 +27,15 @@ package org.geysermc.connector.entity;
import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.scoreboard.NameTagVisibility;
import com.github.steveice10.mc.protocol.data.message.TextMessage;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
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.PlayerPermission;
import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData;
import com.nukkitx.protocol.bedrock.packet.AddPlayerPacket;
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
@ -50,7 +51,7 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.session.cache.EntityEffectCache;
import org.geysermc.connector.scoreboard.Team;
import org.geysermc.connector.utils.AttributeUtils;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import java.util.ArrayList;
import java.util.List;
@ -168,6 +169,17 @@ public class PlayerEntity extends LivingEntity {
movePlayerPacket.setRotation(getBedrockRotation());
movePlayerPacket.setOnGround(isOnGround);
movePlayerPacket.setMode(MovePlayerPacket.Mode.NORMAL);
// If the player is moved while sleeping, we have to adjust their y, so it appears
// correctly on Bedrock. This fixes GSit's lay.
if (metadata.getFlags().getFlag(EntityFlag.SLEEPING)) {
Vector3i bedPosition = metadata.getPos(EntityData.BED_POSITION);
if (bedPosition != null && (bedPosition.getY() == 0 || bedPosition.distanceSquared(position.toInt()) > 4)) {
// Force the player movement by using a teleport
movePlayerPacket.setPosition(Vector3f.from(position.getX(), position.getY() - entityType.getOffset() + 0.2f, position.getZ()));
movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.UNKNOWN);
}
}
session.sendUpstreamPacket(movePlayerPacket);
if (leftParrot != null) {
leftParrot.moveRelative(session, relX, relY, relZ, rotation, true);
@ -231,22 +243,16 @@ public class PlayerEntity extends LivingEntity {
String username = this.username;
TextMessage name = (TextMessage) entityMetadata.getValue();
if (name != null) {
username = MessageUtils.getBedrockMessage(name);
username = MessageTranslator.convertMessage(name.toString());
}
Team team = session.getWorldCache().getScoreboard().getTeamFor(username);
if (team != null) {
// Cover different visibility settings
if (team.getNameTagVisibility() == NameTagVisibility.NEVER) {
metadata.put(EntityData.NAMETAG, "");
} else if (team.getNameTagVisibility() == NameTagVisibility.HIDE_FOR_OTHER_TEAMS &&
!team.getEntities().contains(session.getPlayerEntity().getUsername())) {
metadata.put(EntityData.NAMETAG, "");
} else if (team.getNameTagVisibility() == NameTagVisibility.HIDE_FOR_OWN_TEAM &&
team.getEntities().contains(session.getPlayerEntity().getUsername())) {
metadata.put(EntityData.NAMETAG, "");
} else {
metadata.put(EntityData.NAMETAG, team.getPrefix() + MessageUtils.toChatColor(team.getColor()) + username + team.getSuffix());
String displayName = "";
if (team.isVisibleFor(session.getPlayerEntity().getUsername())) {
displayName = MessageTranslator.toChatColor(team.getColor()) + username;
displayName = team.getCurrentData().getDisplayName(displayName);
}
metadata.put(EntityData.NAMETAG, displayName);
}
}

View File

@ -31,14 +31,23 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* Used as a class for any object-like entity that moves as a projectile
*/
public class ThrowableEntity extends Entity {
private Vector3f lastPosition;
private ScheduledFuture<?> positionUpdater;
/**
* Updates the position for the Bedrock client.
*
* Java clients assume the next positions of moving items. Bedrock needs to be explicitly told positions
*/
protected ScheduledFuture<?> positionUpdater;
public ThrowableEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
@ -49,20 +58,86 @@ public class ThrowableEntity extends Entity {
public void spawnEntity(GeyserSession session) {
super.spawnEntity(session);
positionUpdater = session.getConnector().getGeneralThreadPool().scheduleAtFixedRate(() -> {
super.moveRelative(session, motion.getX(), motion.getY(), motion.getZ(), rotation, onGround);
if (metadata.getFlags().getFlag(EntityFlag.HAS_GRAVITY)) {
float gravity = 0.03f; // Snowball, Egg, and Ender Pearl
if (entityType == EntityType.THROWN_POTION || entityType == EntityType.LINGERING_POTION) {
gravity = 0.05f;
} else if (entityType == EntityType.THROWN_EXP_BOTTLE) {
gravity = 0.07f;
}
motion = motion.down(gravity);
if (session.isClosed()) {
positionUpdater.cancel(true);
return;
}
updatePosition(session);
}, 0, 50, TimeUnit.MILLISECONDS);
}
protected void moveAbsoluteImmediate(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
super.moveAbsolute(session, position, rotation, isOnGround, teleported);
}
protected void updatePosition(GeyserSession session) {
super.moveRelative(session, motion.getX(), motion.getY(), motion.getZ(), rotation, onGround);
float drag = getDrag(session);
float gravity = getGravity();
motion = motion.mul(drag).down(gravity);
}
/**
* 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 (metadata.getFlags().getFlag(EntityFlag.HAS_GRAVITY)) {
switch (entityType) {
case THROWN_POTION:
case LINGERING_POTION:
return 0.05f;
case THROWN_EXP_BOTTLE:
return 0.07f;
case FIREBALL:
return 0;
case SNOWBALL:
case THROWN_EGG:
case THROWN_ENDERPEARL:
return 0.03f;
}
}
return 0;
}
/**
* @param session the session of the Bedrock client.
* @return the drag that should be multiplied to the entity's motion
*/
protected float getDrag(GeyserSession session) {
if (isInWater(session)) {
return 0.8f;
} else {
switch (entityType) {
case THROWN_POTION:
case LINGERING_POTION:
case THROWN_EXP_BOTTLE:
case SNOWBALL:
case THROWN_EGG:
case THROWN_ENDERPEARL:
return 0.99f;
case FIREBALL:
case SMALL_FIREBALL:
case DRAGON_FIREBALL:
return 0.95f;
}
}
return 1;
}
/**
* @param session the session of the Bedrock client.
* @return true if this entity is currently in water.
*/
protected boolean isInWater(GeyserSession session) {
if (session.getConnector().getConfig().isCacheChunks()) {
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
return block == BlockTranslator.BEDROCK_WATER_ID;
}
return false;
}
@Override
public boolean despawnEntity(GeyserSession session) {
positionUpdater.cancel(true);

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2019-2020 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.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
public class WitherSkullEntity extends ItemedFireballEntity {
private boolean isCharged;
public WitherSkullEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@Override
protected float getDrag(GeyserSession session) {
return isCharged ? 0.73f : super.getDrag(session);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7) {
boolean newIsCharged = (boolean) entityMetadata.getValue();
if (newIsCharged != isCharged) {
isCharged = newIsCharged;
entityType = isCharged ? EntityType.WITHER_SKULL_DANGEROUS : EntityType.WITHER_SKULL;
despawnEntity(session);
spawnEntity(session);
}
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View File

@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadat
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import lombok.Getter;
import org.geysermc.connector.entity.LivingEntity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
@ -36,6 +37,7 @@ import org.geysermc.connector.network.session.GeyserSession;
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;
@ -47,7 +49,7 @@ public class ArmorStandEntity extends LivingEntity {
@Override
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
// Fake the height to be above where it is so the nametag appears in the right location for invisible non-marker armour stands
if (!isMarker && isInvisible) {
if (!isMarker && isInvisible && passengers.isEmpty()) {
position = position.add(0d, entityType.getHeight() * (isSmall ? 0.55d : 1d), 0d);
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2019-2020 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.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
public class BatEntity extends AmbientEntity {
public BatEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 15) {
byte xd = (byte) entityMetadata.getValue();
metadata.getFlags().setFlag(EntityFlag.RESTING, (xd & 0x01) == 0x01);
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2019-2020 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.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
public class SnowGolemEntity extends GolemEntity {
public SnowGolemEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 15) {
byte xd = (byte) entityMetadata.getValue();
// Handle the visibility of the pumpkin
metadata.getFlags().setFlag(EntityFlag.SHEARED, (xd & 0x10) != 0x10);
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View File

@ -27,7 +27,10 @@ package org.geysermc.connector.entity.living.animal;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
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.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
@ -41,10 +44,23 @@ public class BeeEntity extends AnimalEntity {
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 16) {
byte xd = (byte) entityMetadata.getValue();
metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 0x02) == 0x02);
// 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
metadata.put(EntityData.MARK_VARIANT, (xd & 0x04) == 0x04 ? 1 : 0);
// If the bee has nectar or not
metadata.getFlags().setFlag(EntityFlag.POWERED, (xd & 0x08) == 0x08);
}
if (entityMetadata.getId() == 17) {
// Converting "anger time" to a boolean
metadata.getFlags().setFlag(EntityFlag.ANGRY, (int) entityMetadata.getValue() > 0);
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View File

@ -41,12 +41,13 @@ public class FoxEntity extends AnimalEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 16) {
metadata.put(EntityData.VARIANT, (int) entityMetadata.getValue());
metadata.put(EntityData.VARIANT, entityMetadata.getValue());
}
if (entityMetadata.getId() == 17) {
byte xd = (byte) entityMetadata.getValue();
metadata.getFlags().setFlag(EntityFlag.SITTING, (xd & 0x01) == 0x01);
metadata.getFlags().setFlag(EntityFlag.SNEAKING, (xd & 0x04) == 0x04);
metadata.getFlags().setFlag(EntityFlag.INTERESTED, (xd & 0x08) == 0x08);
metadata.getFlags().setFlag(EntityFlag.SLEEPING, (xd & 0x20) == 0x20);
}
super.updateBedrockMetadata(entityMetadata, session);

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2019-2020 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.EntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.DimensionUtils;
public class HoglinEntity extends AnimalEntity {
public HoglinEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 16) {
// Immune to zombification?
// Apply shaking effect if not in the nether and zombification is possible
metadata.getFlags().setFlag(EntityFlag.SHAKING, !((boolean) entityMetadata.getValue()) && !session.getDimension().equals(DimensionUtils.NETHER));
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2019-2020 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.EntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
public class MooshroomEntity extends AnimalEntity {
public MooshroomEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 16) {
metadata.put(EntityData.VARIANT, entityMetadata.getValue().equals("brown") ? 1 : 0);
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View File

@ -27,23 +27,75 @@ package org.geysermc.connector.entity.living.animal;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
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.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.item.ItemRegistry;
public class PandaEntity extends AnimalEntity {
private int mainGene;
private int hiddenGene;
public PandaEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 18) {
metadata.getFlags().setFlag(EntityFlag.EATING, (int) entityMetadata.getValue() > 0);
metadata.put(EntityData.EATING_COUNTER, entityMetadata.getValue());
if ((int) entityMetadata.getValue() != 0) {
// Particles and sound
EntityEventPacket packet = new EntityEventPacket();
packet.setRuntimeEntityId(geyserId);
packet.setType(EntityEventType.EATING_ITEM);
packet.setData(ItemRegistry.BAMBOO.getBedrockId() << 16);
session.sendUpstreamPacket(packet);
}
}
if (entityMetadata.getId() == 19) {
mainGene = (int) (byte) entityMetadata.getValue();
updateAppearance();
}
if (entityMetadata.getId() == 20) {
hiddenGene = (int) (byte) entityMetadata.getValue();
updateAppearance();
}
if (entityMetadata.getId() == 21) {
byte xd = (byte) entityMetadata.getValue();
metadata.getFlags().setFlag(EntityFlag.SNEEZING, (xd & 0x02) == 0x02);
metadata.getFlags().setFlag(EntityFlag.EATING, (xd & 0x04) == 0x04);
metadata.getFlags().setFlag(EntityFlag.ROLLING, (xd & 0x04) == 0x04);
metadata.getFlags().setFlag(EntityFlag.SITTING, (xd & 0x08) == 0x08);
// Required to put these both for sitting to actually show
metadata.put(EntityData.SITTING_AMOUNT, (xd & 0x08) == 0x08 ? 1f : 0f);
metadata.put(EntityData.SITTING_AMOUNT_PREVIOUS, (xd & 0x08) == 0x08 ? 1f : 0f);
metadata.getFlags().setFlag(EntityFlag.LAYING_DOWN, (xd & 0x10) == 0x10);
}
super.updateBedrockMetadata(entityMetadata, session);
}
/**
* Update the panda's appearance, and take into consideration the recessive brown and weak traits that only show up
* when both main and hidden genes match
*/
private void updateAppearance() {
if (mainGene == 4 || mainGene == 5) {
// Main gene is a recessive trait
if (mainGene == hiddenGene) {
// Main and hidden genes match; this is what the panda looks like.
metadata.put(EntityData.VARIANT, mainGene);
} else {
// Genes have no effect on appearance
metadata.put(EntityData.VARIANT, 0);
}
} else {
// No need to worry about hidden gene
metadata.put(EntityData.VARIANT, mainGene);
}
}
}

View File

@ -48,6 +48,15 @@ public class RabbitEntity extends AnimalEntity {
metadata.put(EntityData.SCALE, .35f);
metadata.getFlags().setFlag(EntityFlag.BABY, true);
}
} else if (entityMetadata.getId() == 16) {
int variant = (int) entityMetadata.getValue();
// Change the killer bunny to display as white since it only exists on Java Edition
if (variant == 99) {
variant = 1;
}
metadata.put(EntityData.VARIANT, variant);
}
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2019-2020 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.EntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
public class TurtleEntity extends AnimalEntity {
public TurtleEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 17) {
metadata.getFlags().setFlag(EntityFlag.IS_PREGNANT, (boolean) entityMetadata.getValue());
} else if (entityMetadata.getId() == 18) {
metadata.getFlags().setFlag(EntityFlag.LAYING_EGG, (boolean) entityMetadata.getValue());
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View File

@ -27,10 +27,14 @@ package org.geysermc.connector.entity.living.animal.horse;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
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.living.animal.AnimalEntity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.item.ItemRegistry;
public class AbstractHorseEntity extends AnimalEntity {
@ -47,6 +51,30 @@ public class AbstractHorseEntity extends AnimalEntity {
metadata.getFlags().setFlag(EntityFlag.SADDLED, (xd & 0x04) == 0x04);
metadata.getFlags().setFlag(EntityFlag.EATING, (xd & 0x10) == 0x10);
metadata.getFlags().setFlag(EntityFlag.STANDING, (xd & 0x20) == 0x20);
// HorseFlags
// Bred 0x10
// Eating 0x20
// Open mouth 0x80
int horseFlags = 0x0;
horseFlags = (xd & 0x40) == 0x40 ? horseFlags | 0x80 : horseFlags;
// Only set eating when we don't have mouth open so a player interaction doesn't trigger the eating animation
horseFlags = (xd & 0x10) == 0x10 && (xd & 0x40) != 0x40 ? horseFlags | 0x20 : horseFlags;
// Set the flags into the display item
metadata.put(EntityData.DISPLAY_ITEM, horseFlags);
// Send the eating particles
// We use the wheat metadata as static particles since Java
// doesn't send over what item was used to feed the horse
if ((xd & 0x40) == 0x40) {
EntityEventPacket entityEventPacket = new EntityEventPacket();
entityEventPacket.setRuntimeEntityId(geyserId);
entityEventPacket.setType(EntityEventType.EATING_ITEM);
entityEventPacket.setData(ItemRegistry.WHEAT.getBedrockId() << 16);
session.sendUpstreamPacket(entityEventPacket);
}
}
// Needed to control horses

View File

@ -28,7 +28,6 @@ package org.geysermc.connector.entity.living.animal.horse;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
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.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
@ -41,7 +40,7 @@ public class HorseEntity extends AbstractHorseEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 18) {
metadata.put(EntityData.VARIANT, (int) entityMetadata.getValue());
metadata.put(EntityData.VARIANT, entityMetadata.getValue());
metadata.put(EntityData.MARK_VARIANT, (((int) entityMetadata.getValue()) >> 8) % 5);
}
super.updateBedrockMetadata(entityMetadata, session);

View File

@ -34,6 +34,8 @@ import org.geysermc.connector.network.session.GeyserSession;
public class CatEntity extends TameableEntity {
private byte collarColor;
public CatEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@ -45,6 +47,13 @@ public class CatEntity extends TameableEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
super.updateBedrockMetadata(entityMetadata, session);
if (entityMetadata.getId() == 16) {
// Update collar color if tamed
if (metadata.getFlags().getFlag(EntityFlag.TAMED)) {
metadata.put(EntityData.COLOR, collarColor);
}
}
if (entityMetadata.getId() == 18) {
// Different colors in Java and Bedrock for some reason
int variantColor;
@ -67,11 +76,11 @@ public class CatEntity extends TameableEntity {
metadata.put(EntityData.VARIANT, variantColor);
}
if (entityMetadata.getId() == 21) {
collarColor = (byte) (int) entityMetadata.getValue();
// Needed or else wild cats are a red color
if (metadata.getFlags().getFlag(EntityFlag.TAMED)) {
metadata.put(EntityData.COLOR, (byte) (int) entityMetadata.getValue());
metadata.put(EntityData.COLOR, collarColor);
}
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View File

@ -29,10 +29,13 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadat
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.Entity;
import org.geysermc.connector.entity.living.animal.AnimalEntity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
public class TameableEntity extends AnimalEntity {
public TameableEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
@ -46,11 +49,22 @@ public class TameableEntity extends AnimalEntity {
metadata.getFlags().setFlag(EntityFlag.SITTING, (xd & 0x01) == 0x01);
metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 0x02) == 0x02);
metadata.getFlags().setFlag(EntityFlag.TAMED, (xd & 0x04) == 0x04);
// Must be set for wolf collar color to work
// Extending it to all entities to prevent future bugs
if (metadata.getFlags().getFlag(EntityFlag.TAMED)) {
metadata.put(EntityData.OWNER_EID, session.getPlayerEntity().getGeyserId());
} // Can't de-tame an entity so no resetting the owner ID
}
// Note: Must be set for wolf collar color to work
if (entityMetadata.getId() == 17) {
if (entityMetadata.getValue() != null) {
// Owner UUID of entity
Entity entity = session.getEntityCache().getPlayerEntity((UUID) entityMetadata.getValue());
// Used as both a check since the player isn't in the entity cache and a normal fallback
if (entity == null) {
entity = session.getPlayerEntity();
}
// Translate to entity ID
metadata.put(EntityData.OWNER_EID, entity.getGeyserId());
} else {
metadata.put(EntityData.OWNER_EID, 0L); // Reset
}
}
super.updateBedrockMetadata(entityMetadata, session);
}

View File

@ -26,26 +26,32 @@
package org.geysermc.connector.entity.living.merchant;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData;
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 com.nukkitx.protocol.bedrock.data.entity.EntityFlags;
import com.nukkitx.protocol.bedrock.packet.*;
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.WorldManager;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class VillagerEntity extends AbstractMerchantEntity {
/**
* A map of Java profession IDs to Bedrock IDs
*/
private static final Int2IntMap VILLAGER_VARIANTS = new Int2IntOpenHashMap();
private static final Int2IntMap VILLAGER_REGIONS = new Int2IntOpenHashMap();
/**
* A map of all Java region IDs (plains, savanna...) to Bedrock
*/
public static final Int2IntMap VILLAGER_REGIONS = new Int2IntOpenHashMap();
static {
// Java villager profession IDs -> Bedrock
@ -99,9 +105,9 @@ public class VillagerEntity extends AbstractMerchantEntity {
int bedId = 0;
float bedPositionSubtractorW = 0;
float bedPositionSubtractorN = 0;
if (session.getConnector().getConfig().isCacheChunks()) {
Position bedLocation = new Position((int) position.getFloorX(), (int) position.getFloorY(), (int) position.getFloorZ());
bedId = session.getConnector().getWorldManager().getBlockAt(session, bedLocation);
Vector3i bedPosition = metadata.getPos(EntityData.BED_POSITION);
if (session.getConnector().getConfig().isCacheChunks() && bedPosition != null) {
bedId = session.getConnector().getWorldManager().getBlockAt(session, bedPosition);
}
String bedRotationZ = BlockTranslator.getJavaIdBlockMap().inverse().get(bedId);
setRotation(rotation);

View File

@ -1,11 +1,25 @@
package org.geysermc.connector.entity.living.monster;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.DimensionUtils;
public class BasePiglinEntity extends MonsterEntity {
public BasePiglinEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 15) {
// Immune to zombification?
// Apply shaking effect if not in the nether and zombification is possible
metadata.getFlags().setFlag(EntityFlag.SHAKING, !((boolean) entityMetadata.getValue()) && !session.getDimension().equals(DimensionUtils.NETHER));
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View File

@ -33,6 +33,12 @@ import org.geysermc.connector.network.session.GeyserSession;
public class CreeperEntity extends MonsterEntity {
/**
* Whether the creeper has been ignited and is using ID 17.
* In this instance we ignore ID 15 since it's sending us -1 which confuses poor Bedrock.
*/
private boolean ignitedByFlintAndSteel = false;
public CreeperEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@ -40,13 +46,16 @@ public class CreeperEntity extends MonsterEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 15) {
metadata.getFlags().setFlag(EntityFlag.IGNITED, (int) entityMetadata.getValue() == 1);
if (!ignitedByFlintAndSteel) {
metadata.getFlags().setFlag(EntityFlag.IGNITED, (int) entityMetadata.getValue() == 1);
}
}
if (entityMetadata.getId() == 16) {
metadata.getFlags().setFlag(EntityFlag.POWERED, (boolean) entityMetadata.getValue());
}
if (entityMetadata.getId() == 17) {
metadata.getFlags().setFlag(EntityFlag.IGNITED, (boolean) entityMetadata.getValue());
ignitedByFlintAndSteel = (boolean) entityMetadata.getValue();
metadata.getFlags().setFlag(EntityFlag.IGNITED, ignitedByFlintAndSteel);
}
super.updateBedrockMetadata(entityMetadata, session);

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2019-2020 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.monster;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.entity.living.FlyingEntity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
public class GhastEntity extends FlyingEntity {
public GhastEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 15) {
// If the ghast is attacking
metadata.put(EntityData.CHARGE_AMOUNT, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0));
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View File

@ -41,7 +41,7 @@ public class PiglinEntity extends BasePiglinEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 15) {
if (entityMetadata.getId() == 16) {
boolean isBaby = (boolean) entityMetadata.getValue();
if (isBaby) {
metadata.put(EntityData.SCALE, .55f);

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2019-2020 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.monster;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
public class VexEntity extends MonsterEntity {
public VexEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 15) {
byte xd = (byte) entityMetadata.getValue();
// Set the target to the player to force the attack animation
// even if the player isn't the target as we dont get the target on Java
metadata.put(EntityData.TARGET_EID, (xd & 0x01) == 0x01 ? session.getPlayerEntity().getGeyserId() : 0);
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2019-2020 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.monster;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData;
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.living.merchant.VillagerEntity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
public class ZombieVillagerEntity extends ZombieEntity {
public ZombieVillagerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 18) {
metadata.getFlags().setFlag(EntityFlag.IS_TRANSFORMING, (boolean) entityMetadata.getValue());
metadata.getFlags().setFlag(EntityFlag.SHAKING, (boolean) entityMetadata.getValue());
}
if (entityMetadata.getId() == 19) {
VillagerData villagerData = (VillagerData) entityMetadata.getValue();
// Region - only one used on Bedrock
metadata.put(EntityData.MARK_VARIANT, VillagerEntity.VILLAGER_REGIONS.get(villagerData.getType()));
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View File

@ -47,12 +47,12 @@ public enum EntityType {
SHEEP(SheepEntity.class, 13, 1.3f, 0.9f),
WOLF(WolfEntity.class, 14, 0.85f, 0.6f),
VILLAGER(VillagerEntity.class, 15, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:villager_v2"),
MOOSHROOM(AnimalEntity.class, 16, 1.4f, 0.9f),
MOOSHROOM(MooshroomEntity.class, 16, 1.4f, 0.9f),
SQUID(SquidEntity.class, 17, 0.8f),
RABBIT(RabbitEntity.class, 18, 0.5f, 0.4f),
BAT(AmbientEntity.class, 19, 0.9f, 0.5f),
BAT(BatEntity.class, 19, 0.9f, 0.5f),
IRON_GOLEM(GolemEntity.class, 20, 2.7f, 1.4f),
SNOW_GOLEM(GolemEntity.class, 21, 1.9f, 0.7f),
SNOW_GOLEM(SnowGolemEntity.class, 21, 1.9f, 0.7f),
OCELOT(OcelotEntity.class, 22, 0.35f, 0.3f),
HORSE(HorseEntity.class, 23, 1.6f, 1.3965f),
DONKEY(ChestedHorseEntity.class, 24, 1.6f, 1.3965f),
@ -74,10 +74,10 @@ public enum EntityType {
ENDERMAN(EndermanEntity.class, 38, 2.9f, 0.6f),
SILVERFISH(MonsterEntity.class, 39, 0.3f, 0.4f),
CAVE_SPIDER(MonsterEntity.class, 40, 0.5f, 0.7f),
GHAST(FlyingEntity.class, 41, 4.0f),
GHAST(GhastEntity.class, 41, 4.0f),
MAGMA_CUBE(MagmaCubeEntity.class, 42, 0.51f),
BLAZE(BlazeEntity.class, 43, 1.8f, 0.6f),
ZOMBIE_VILLAGER(ZombieEntity.class, 44, 1.8f, 0.6f, 0.6f, 1.62f),
ZOMBIE_VILLAGER(ZombieVillagerEntity.class, 44, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:zombie_villager_v2"),
WITCH(RaidParticipantEntity.class, 45, 1.8f, 0.6f, 0.6f, 1.62f),
STRAY(AbstractSkeletonEntity.class, 46, 1.8f, 0.6f, 0.6f, 1.62f),
HUSK(ZombieEntity.class, 47, 1.8f, 0.6f, 0.6f, 1.62f),
@ -109,7 +109,7 @@ public enum EntityType {
END_CRYSTAL(EnderCrystalEntity.class, 71, 2.0f, 2.0f, 2.0f, 0f, "minecraft:ender_crystal"),
FIREWORK_ROCKET(FireworkEntity.class, 72, 0.25f, 0.25f, 0.25f, 0f, "minecraft:fireworks_rocket"),
TRIDENT(TridentEntity.class, 73, 0f, 0f, 0f, 0f, "minecraft:thrown_trident"),
TURTLE(AnimalEntity.class, 74, 0.4f, 1.2f),
TURTLE(TurtleEntity.class, 74, 0.4f, 1.2f),
CAT(CatEntity.class, 75, 0.35f, 0.3f),
SHULKER_BULLET(Entity.class, 76, 0.3125f),
FISHING_BOBBER(FishingHookEntity.class, 77, 0f, 0f, 0f, 0f, "minecraft:fishing_hook"),
@ -125,9 +125,9 @@ public enum EntityType {
THROWN_POTION(ThrowableEntity.class, 86, 0.25f, 0.25f, 0.25f, 0f, "minecraft:splash_potion"),
THROWN_ENDERPEARL(ThrowableEntity.class, 87, 0.25f, 0.25f, 0.25f, 0f, "minecraft:ender_pearl"),
LEASH_KNOT(LeashKnotEntity.class, 88, 0.5f, 0.375f),
WITHER_SKULL(Entity.class, 89, 0.3125f),
WITHER_SKULL(WitherSkullEntity.class, 89, 0.3125f),
BOAT(BoatEntity.class, 90, 0.7f, 1.6f, 1.6f, 0.35f),
WITHER_SKULL_DANGEROUS(Entity.class, 91, 0f),
WITHER_SKULL_DANGEROUS(WitherSkullEntity.class, 91, 0f),
LIGHTNING_BOLT(Entity.class, 93, 0f),
SMALL_FIREBALL(ItemedFireballEntity.class, 94, 0.3125f),
AREA_EFFECT_CLOUD(AreaEffectCloudEntity.class, 95, 0.5f, 1.0f),
@ -141,7 +141,7 @@ public enum EntityType {
LLAMA_SPIT(Entity.class, 102, 0.25f),
EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f, 0.5f, 0f, "minecraft:evocation_fang"),
EVOKER(SpellcasterIllagerEntity.class, 104, 1.95f, 0.6f, 0.6f, 0f, "minecraft:evocation_illager"),
VEX(MonsterEntity.class, 105, 0.8f, 0.4f),
VEX(VexEntity.class, 105, 0.8f, 0.4f),
ICE_BOMB(Entity.class, 106, 0f),
BALLOON(Entity.class, 107, 0f), //TODO
PUFFERFISH(PufferFishEntity.class, 108, 0.7f, 0.7f),
@ -153,7 +153,7 @@ public enum EntityType {
FOX(FoxEntity.class, 121, 0.5f, 1.25f),
BEE(BeeEntity.class, 122, 0.6f, 0.6f),
STRIDER(StriderEntity.class, 125, 1.7f, 0.9f, 0f, 0f, "minecraft:strider"),
HOGLIN(AnimalEntity.class, 124, 1.4f, 1.3965f, 1.3965f, 0f, "minecraft:hoglin"),
HOGLIN(HoglinEntity.class, 124, 1.4f, 1.3965f, 1.3965f, 0f, "minecraft:hoglin"),
ZOGLIN(ZoglinEntity.class, 126, 1.4f, 1.3965f, 1.3965f, 0f, "minecraft:zoglin"),
PIGLIN(PiglinEntity.class, 123, 1.95f, 0.6f, 0.6f, 0f, "minecraft:piglin"),
PIGLIN_BRUTE(BasePiglinEntity.class, 127, 1.95f, 0.6f, 0.6f, 0f, "minecraft:piglin_brute"),

View File

@ -26,8 +26,7 @@
package org.geysermc.connector.network;
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
import com.nukkitx.protocol.bedrock.v407.Bedrock_v407;
import com.nukkitx.protocol.bedrock.v408.Bedrock_v408;
import com.nukkitx.protocol.bedrock.v419.Bedrock_v419;
import java.util.ArrayList;
import java.util.List;
@ -40,14 +39,13 @@ public class BedrockProtocol {
* Default Bedrock codec that should act as a fallback. Should represent the latest available
* release of the game that Geyser supports.
*/
public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v408.V408_CODEC;
public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v419.V419_CODEC;
/**
* A list of all supported Bedrock versions that can join Geyser
*/
public static final List<BedrockPacketCodec> SUPPORTED_BEDROCK_CODECS = new ArrayList<>();
static {
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v407.V407_CODEC);
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC);
}

View File

@ -25,7 +25,6 @@
package org.geysermc.connector.network;
import com.github.steveice10.mc.protocol.data.message.MessageSerializer;
import com.nukkitx.protocol.bedrock.BedrockPong;
import com.nukkitx.protocol.bedrock.BedrockServerEventHandler;
import com.nukkitx.protocol.bedrock.BedrockServerSession;
@ -36,7 +35,7 @@ import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import org.geysermc.connector.utils.LanguageUtils;
import java.net.InetSocketAddress;
@ -76,7 +75,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
pong.setIpv4Port(config.getBedrock().getPort());
if (config.isPassthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) {
String[] motd = MessageUtils.getBedrockMessage(MessageSerializer.fromString(pingInfo.getDescription())).split("\n");
String[] motd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n");
String mainMotd = motd[0]; // First line of the motd.
String subMotd = (motd.length != 1) ? motd[1] : ""; // Second line of the motd if present, otherwise blank.

View File

@ -25,12 +25,11 @@
package org.geysermc.connector.network;
import com.github.steveice10.mc.protocol.data.message.MessageSerializer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import org.geysermc.connector.common.ping.GeyserPingInfo;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@ -148,7 +147,7 @@ public class QueryPacketHandler {
}
if (connector.getConfig().isPassthroughMotd() && pingInfo != null) {
String[] javaMotd = MessageUtils.getBedrockMessage(MessageSerializer.fromString(pingInfo.getDescription())).split("\n");
String[] javaMotd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n");
motd = javaMotd[0].trim(); // First line of the motd.
} else {
motd = connector.getConfig().getBedrock().getMotd1();

View File

@ -59,9 +59,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
if (packetCodec == null) {
if (loginPacket.getProtocolVersion() > BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) {
// Too early to determine session locale
session.getConnector().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.outdated.server", BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()));
session.disconnect(LanguageUtils.getLocaleStringLog("geyser.network.outdated.server", BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()));
return true;
} else if (loginPacket.getProtocolVersion() < BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) {
session.getConnector().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.outdated.client", BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()));
session.disconnect(LanguageUtils.getLocaleStringLog("geyser.network.outdated.client", BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()));
return true;
}

View File

@ -34,7 +34,6 @@ import com.github.steveice10.mc.protocol.data.SubProtocol;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.statistic.Statistic;
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
import com.github.steveice10.mc.protocol.data.message.MessageSerializer;
import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket;
@ -65,6 +64,7 @@ import org.geysermc.connector.common.AuthType;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.PlayerEntity;
import org.geysermc.connector.inventory.PlayerInventory;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import org.geysermc.connector.network.remote.RemoteServer;
import org.geysermc.connector.network.session.auth.AuthData;
import org.geysermc.connector.network.session.auth.BedrockClientData;
@ -74,7 +74,6 @@ import org.geysermc.connector.network.translators.EntityIdentifierRegistry;
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
import org.geysermc.connector.network.translators.inventory.EnchantmentInventoryTranslator;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.*;
import org.geysermc.floodgate.crypto.FloodgateCipher;
import org.geysermc.floodgate.util.BedrockData;
@ -142,6 +141,13 @@ public class GeyserSession implements CommandSender {
@Setter
private boolean jumping;
/**
* The dimension of the player.
* As all entities are in the same world, this can be safely applied to all other entities.
*/
@Setter
private String dimension = DimensionUtils.OVERWORLD;
@Setter
private int breakingBlock;
@ -217,6 +223,12 @@ public class GeyserSession implements CommandSender {
@Setter
private ScheduledFuture<?> bucketScheduledFuture;
/**
* Sends a movement packet every three seconds if the player hasn't moved. Prevents timeouts when AFK in certain instances.
*/
@Setter
private ScheduledFuture<?> movementSendIfIdle;
private boolean reducedDebugInfo = false;
/**
@ -471,7 +483,7 @@ public class GeyserSession implements CommandSender {
event.getCause().printStackTrace();
}
upstream.disconnect(MessageUtils.getBedrockMessage(MessageSerializer.fromString(event.getReason())));
upstream.disconnect(MessageTranslator.convertMessageLenient(event.getReason()));
}
@Override
@ -611,12 +623,12 @@ public class GeyserSession implements CommandSender {
startGamePacket.setRotation(Vector2f.from(1, 1));
startGamePacket.setSeed(-1);
startGamePacket.setDimensionId(DimensionUtils.javaToBedrock(playerEntity.getDimension()));
startGamePacket.setDimensionId(DimensionUtils.javaToBedrock(dimension));
startGamePacket.setGeneratorId(1);
startGamePacket.setLevelGameType(GameType.SURVIVAL);
startGamePacket.setDifficulty(1);
startGamePacket.setDefaultSpawn(Vector3i.ZERO);
startGamePacket.setAchievementsDisabled(true);
startGamePacket.setAchievementsDisabled(!connector.getConfig().isXboxAchievementsEnabled());
startGamePacket.setCurrentTick(-1);
startGamePacket.setEduEditionOffers(0);
startGamePacket.setEduFeaturesEnabled(false);
@ -627,7 +639,7 @@ public class GeyserSession implements CommandSender {
startGamePacket.getGamerules().add(new GameRuleData<>("showcoordinates", true));
startGamePacket.setPlatformBroadcastMode(GamePublishSetting.PUBLIC);
startGamePacket.setXblBroadcastMode(GamePublishSetting.PUBLIC);
startGamePacket.setCommandsEnabled(true);
startGamePacket.setCommandsEnabled(!connector.getConfig().isXboxAchievementsEnabled());
startGamePacket.setTexturePacksRequired(false);
startGamePacket.setBonusChestEnabled(false);
startGamePacket.setStartingWithMap(false);
@ -649,7 +661,6 @@ public class GeyserSession implements CommandSender {
// startGamePacket.setCurrentTick(0);
startGamePacket.setEnchantmentSeed(0);
startGamePacket.setMultiplayerCorrelationId("");
startGamePacket.setBlockPalette(BlockTranslator.BLOCKS);
startGamePacket.setItemEntries(ItemRegistry.ITEMS);
startGamePacket.setVanillaVersion("*");
startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.CLIENT);

View File

@ -33,7 +33,7 @@ import com.nukkitx.protocol.bedrock.packet.BossEventPacket;
import com.nukkitx.protocol.bedrock.packet.RemoveEntityPacket;
import lombok.AllArgsConstructor;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
@AllArgsConstructor
public class BossBar {
@ -58,7 +58,7 @@ public class BossBar {
BossEventPacket bossEventPacket = new BossEventPacket();
bossEventPacket.setBossUniqueEntityId(entityId);
bossEventPacket.setAction(BossEventPacket.Action.CREATE);
bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(title, session.getLocale()));
bossEventPacket.setTitle(MessageTranslator.convertMessage(title.toString(), session.getLocale()));
bossEventPacket.setHealthPercentage(health);
bossEventPacket.setColor(color); //ignored by client
bossEventPacket.setOverlay(overlay);
@ -72,7 +72,7 @@ public class BossBar {
BossEventPacket bossEventPacket = new BossEventPacket();
bossEventPacket.setBossUniqueEntityId(entityId);
bossEventPacket.setAction(BossEventPacket.Action.UPDATE_NAME);
bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(title, session.getLocale()));
bossEventPacket.setTitle(MessageTranslator.convertMessage(title.toString(), session.getLocale()));
session.sendUpstreamPacket(bossEventPacket);
}

View File

@ -94,12 +94,12 @@ public class ChunkCache {
public int getBlockAt(int x, int y, int z) {
if (!cache) {
return BlockTranslator.AIR;
return BlockTranslator.JAVA_AIR_ID;
}
Column column = this.getChunk(x >> 4, z >> 4);
if (column == null) {
return BlockTranslator.AIR;
return BlockTranslator.JAVA_AIR_ID;
}
Chunk chunk = column.getChunks()[y >> 4];
@ -107,7 +107,7 @@ public class ChunkCache {
return chunk.get(x & 0xF, y & 0xF, z & 0xF);
}
return BlockTranslator.AIR;
return BlockTranslator.JAVA_AIR_ID;
}
public void removeChunk(int chunkX, int chunkZ) {

View File

@ -42,7 +42,7 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator<BlockPic
int blockToPick = session.getConnector().getWorldManager().getBlockAt(session, vector.getX(), vector.getY(), vector.getZ());
// Block is air - chunk caching is probably off
if (blockToPick == 0) {
if (blockToPick == BlockTranslator.JAVA_AIR_ID) {
return;
}

View File

@ -25,7 +25,7 @@
package org.geysermc.connector.network.translators.bedrock;
import org.geysermc.connector.common.PlatformType;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.network.session.GeyserSession;
@ -34,7 +34,7 @@ import org.geysermc.connector.network.translators.Translator;
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientChatPacket;
import com.nukkitx.protocol.bedrock.packet.CommandRequestPacket;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
@Translator(packet = CommandRequestPacket.class)
public class BedrockCommandRequestTranslator extends PacketTranslator<CommandRequestPacket> {
@ -48,7 +48,7 @@ public class BedrockCommandRequestTranslator extends PacketTranslator<CommandReq
} else {
String message = packet.getCommand().trim();
if (MessageUtils.isTooLong(message, session)) {
if (MessageTranslator.isTooLong(message, session)) {
return;
}

View File

@ -117,13 +117,13 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
false);
session.sendDownstreamPacket(blockPacket);
// Otherwise boats will not be able to be placed in survival and buckets wont work on mobile
if (packet.getItemInHand() != null && packet.getItemInHand().getId() == ItemRegistry.BOAT.getBedrockId()) {
// Otherwise boats will not be able to be placed in survival and buckets won't work on mobile
if (packet.getItemInHand() != null && ItemRegistry.BOATS.contains(packet.getItemInHand().getId())) {
ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
session.sendDownstreamPacket(itemPacket);
}
// Check actions, otherwise buckets may be activated when block inventories are accessed
else if (packet.getItemInHand() != null && packet.getItemInHand().getId() == ItemRegistry.BUCKET.getBedrockId()) {
else if (packet.getItemInHand() != null && ItemRegistry.BUCKETS.contains(packet.getItemInHand().getId())) {
// Let the server decide if the bucket item should change, not the client, and revert the changes the client made
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.INVENTORY);
@ -172,8 +172,8 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
}
// Handled in ITEM_USE if the item is not milk
if (packet.getItemInHand() != null && packet.getItemInHand().getId() == ItemRegistry.BUCKET.getBedrockId() &&
packet.getItemInHand().getDamage() != 1) {
if (packet.getItemInHand() != null && ItemRegistry.BUCKETS.contains(packet.getItemInHand().getId()) &&
packet.getItemInHand().getId() != ItemRegistry.MILK_BUCKET.getBedrockId()) {
break;
}
@ -194,10 +194,9 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
session.sendUpstreamPacket(blockBreakPacket);
}
if (ItemFrameEntity.positionContainsItemFrame(session, packet.getBlockPosition()) &&
session.getEntityCache().getEntityByJavaId(ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition())) != null) {
ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition()),
InteractAction.ATTACK, session.isSneaking());
long frameEntityId = ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition());
if (frameEntityId != -1 && session.getEntityCache().getEntityByJavaId(frameEntityId) != null) {
ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) frameEntityId, InteractAction.ATTACK, session.isSneaking());
session.sendDownstreamPacket(attackPacket);
break;
}

View File

@ -31,7 +31,7 @@ import org.geysermc.connector.network.translators.Translator;
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientChatPacket;
import com.nukkitx.protocol.bedrock.packet.TextPacket;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
@Translator(packet = TextPacket.class)
public class BedrockTextTranslator extends PacketTranslator<TextPacket> {
@ -40,7 +40,7 @@ public class BedrockTextTranslator extends PacketTranslator<TextPacket> {
public void translate(TextPacket packet, GeyserSession session) {
String message = packet.getMessage().replaceAll("^\\.", "/").trim();
if (MessageUtils.isTooLong(message, session)) {
if (MessageTranslator.isTooLong(message, session)) {
return;
}

View File

@ -25,27 +25,65 @@
package org.geysermc.connector.network.translators.bedrock.entity.player;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction;
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerState;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerStatePacket;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
import com.nukkitx.protocol.bedrock.packet.InteractPacket;
import lombok.Getter;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import java.util.Arrays;
import java.util.List;
@Translator(packet = InteractPacket.class)
public class BedrockInteractTranslator extends PacketTranslator<InteractPacket> {
/**
* A list of all foods a horse/donkey can eat on Java Edition.
* Used to display interactive tag if needed.
*/
private static final List<String> DONKEY_AND_HORSE_FOODS = Arrays.asList("golden_apple", "enchanted_golden_apple",
"golden_carrot", "sugar", "apple", "wheat", "hay_block");
/**
* A list of all flowers. Used for feeding bees.
*/
private static final List<String> FLOWERS = Arrays.asList("dandelion", "poppy", "blue_orchid", "allium", "azure_bluet",
"red_tulip", "pink_tulip", "white_tulip", "orange_tulip", "cornflower", "lily_of_the_valley", "wither_rose",
"sunflower", "lilac", "rose_bush", "peony");
/**
* All entity types that can be leashed on Java Edition
*/
private static final List<EntityType> LEASHABLE_MOB_TYPES = Arrays.asList(EntityType.BEE, EntityType.CAT, EntityType.CHICKEN,
EntityType.COW, EntityType.DOLPHIN, EntityType.DONKEY, EntityType.FOX, EntityType.HOGLIN, EntityType.HORSE, EntityType.SKELETON_HORSE,
EntityType.ZOMBIE_HORSE, EntityType.IRON_GOLEM, EntityType.LLAMA, EntityType.TRADER_LLAMA, EntityType.MOOSHROOM,
EntityType.MULE, EntityType.OCELOT, EntityType.PARROT, EntityType.PIG, EntityType.POLAR_BEAR, EntityType.RABBIT,
EntityType.SHEEP, EntityType.SNOW_GOLEM, EntityType.STRIDER, EntityType.WOLF, EntityType.ZOGLIN);
private static final List<EntityType> SADDLEABLE_WHEN_TAMED_MOB_TYPES = Arrays.asList(EntityType.DONKEY, EntityType.HORSE,
EntityType.ZOMBIE_HORSE, EntityType.MULE);
/**
* A list of all foods a wolf can eat on Java Edition.
* Used to display interactive tag if needed.
*/
private static final List<String> WOLF_FOODS = Arrays.asList("pufferfish", "tropical_fish", "chicken", "cooked_chicken",
"porkchop", "beef", "rabbit", "cooked_porkchop", "cooked_beef", "rotten_flesh", "mutton", "cooked_mutton",
"cooked_rabbit");
@Override
public void translate(InteractPacket packet, GeyserSession session) {
Entity entity;
@ -84,50 +122,232 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
if (interactEntity == null)
return;
EntityDataMap entityMetadata = interactEntity.getMetadata();
ItemEntry itemEntry = session.getInventory().getItemInHand() == null ? ItemEntry.AIR : ItemRegistry.getItem(session.getInventory().getItemInHand());
String javaIdentifierStripped = itemEntry.getJavaIdentifier().replace("minecraft:", "");
String interactiveTag;
switch (interactEntity.getEntityType()) {
case BOAT:
interactiveTag = "action.interact.ride.boat";
break;
case DONKEY:
case HORSE:
case LLAMA:
case MULE:
case SKELETON_HORSE:
case TRADER_LLAMA:
case ZOMBIE_HORSE:
if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) {
interactiveTag = "action.interact.ride.horse";
} else {
interactiveTag = "action.interact.mount";
}
break;
case MINECART:
interactiveTag = "action.interact.ride.minecart";
break;
case PIG:
if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) {
interactiveTag = "action.interact.mount";
} else interactiveTag = "";
break;
case VILLAGER:
if (entityMetadata.getInt(EntityData.VARIANT) != 14 && entityMetadata.getInt(EntityData.VARIANT) != 0
&& entityMetadata.getFloat(EntityData.SCALE) >= 0.75f) { // Not a nitwit, has a profession and is not a baby
interactiveTag = "action.interact.trade";
} else interactiveTag = "";
break;
case WANDERING_TRADER:
interactiveTag = "action.interact.trade"; // Since you can always trade with a wandering villager, presumably.
break;
default:
return; // No need to process any further since there is no interactive tag
// TODO - in the future, update these in the metadata? So the client doesn't have to wiggle their cursor around for it to happen
// TODO - also, might be good to abstract out the eating thing. I know there will need to be food tracked for https://github.com/GeyserMC/Geyser/issues/1005 but not all food is breeding food
InteractiveTag interactiveTag = InteractiveTag.NONE;
if (entityMetadata.getLong(EntityData.LEASH_HOLDER_EID) == session.getPlayerEntity().getGeyserId()) {
// Unleash the entity
interactiveTag = InteractiveTag.REMOVE_LEASH;
} else if (javaIdentifierStripped.equals("saddle") && !entityMetadata.getFlags().getFlag(EntityFlag.SADDLED) &&
((SADDLEABLE_WHEN_TAMED_MOB_TYPES.contains(interactEntity.getEntityType()) && entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) ||
interactEntity.getEntityType() == EntityType.PIG || interactEntity.getEntityType() == EntityType.STRIDER)) {
// Entity can be saddled and the conditions meet (entity can be saddled and, if needed, is tamed)
interactiveTag = InteractiveTag.SADDLE;
} else if (javaIdentifierStripped.equals("name_tag") && session.getInventory().getItemInHand().getNbt() != null &&
session.getInventory().getItemInHand().getNbt().contains("display")) {
// Holding a named name tag
interactiveTag = InteractiveTag.NAME;
} else if (javaIdentifierStripped.equals("lead") && LEASHABLE_MOB_TYPES.contains(interactEntity.getEntityType()) &&
entityMetadata.getLong(EntityData.LEASH_HOLDER_EID) == -1L) {
// Holding a leash and the mob is leashable for sure
// (Plugins can change this behavior so that's something to look into in the far far future)
interactiveTag = InteractiveTag.LEASH;
} else {
switch (interactEntity.getEntityType()) {
case BEE:
if (FLOWERS.contains(javaIdentifierStripped)) {
interactiveTag = InteractiveTag.FEED;
}
break;
case BOAT:
interactiveTag = InteractiveTag.BOARD_BOAT;
break;
case CAT:
if (javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon")) {
interactiveTag = InteractiveTag.FEED;
} else if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) &&
entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) {
// Tamed and owned by player - can sit/stand
interactiveTag = entityMetadata.getFlags().getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT;
break;
}
break;
case CHICKEN:
if (javaIdentifierStripped.contains("seeds")) {
interactiveTag = InteractiveTag.FEED;
}
break;
case MOOSHROOM:
// Shear the mooshroom
if (javaIdentifierStripped.equals("shears")) {
interactiveTag = InteractiveTag.MOOSHROOM_SHEAR;
break;
}
// Bowls are acceptable here
else if (javaIdentifierStripped.equals("bowl")) {
interactiveTag = InteractiveTag.MOOSHROOM_MILK_STEW;
break;
}
// Fall down to COW as this works on mooshrooms
case COW:
if (javaIdentifierStripped.equals("wheat")) {
interactiveTag = InteractiveTag.FEED;
} else if (javaIdentifierStripped.equals("bucket")) {
// Milk the cow
interactiveTag = InteractiveTag.MILK;
}
break;
case CREEPER:
if (javaIdentifierStripped.equals("flint_and_steel")) {
// Today I learned that you can ignite a creeper with flint and steel! Huh.
interactiveTag = InteractiveTag.IGNITE_CREEPER;
}
break;
case DONKEY:
case LLAMA:
case MULE:
if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && !entityMetadata.getFlags().getFlag(EntityFlag.CHESTED)
&& javaIdentifierStripped.equals("chest")) {
// Can attach a chest
interactiveTag = InteractiveTag.ATTACH_CHEST;
break;
}
// Intentional fall-through
case HORSE:
case SKELETON_HORSE:
case TRADER_LLAMA:
case ZOMBIE_HORSE:
// have another switch statement as, while these share mount attributes they don't share food
switch (interactEntity.getEntityType()) {
case LLAMA:
case TRADER_LLAMA:
if (javaIdentifierStripped.equals("wheat") || javaIdentifierStripped.equals("hay_block")) {
interactiveTag = InteractiveTag.FEED;
break;
}
case DONKEY:
case HORSE:
// Undead can't eat
if (DONKEY_AND_HORSE_FOODS.contains(javaIdentifierStripped)) {
interactiveTag = InteractiveTag.FEED;
break;
}
}
if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY)) {
// Can't ride a baby
if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) {
interactiveTag = InteractiveTag.RIDE_HORSE;
} else if (!entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && itemEntry.equals(ItemEntry.AIR)) {
// Can't hide an untamed entity without having your hand empty
interactiveTag = InteractiveTag.MOUNT;
}
}
break;
case FOX:
if (javaIdentifierStripped.equals("sweet_berries")) {
interactiveTag = InteractiveTag.FEED;
}
break;
case HOGLIN:
if (javaIdentifierStripped.equals("crimson_fungus")) {
interactiveTag = InteractiveTag.FEED;
}
break;
case MINECART:
interactiveTag = InteractiveTag.RIDE_MINECART;
break;
case MINECART_CHEST:
case MINECART_COMMAND_BLOCK:
case MINECART_HOPPER:
interactiveTag = InteractiveTag.OPEN_CONTAINER;
break;
case OCELOT:
if (javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon")) {
interactiveTag = InteractiveTag.FEED;
}
break;
case PANDA:
if (javaIdentifierStripped.equals("bamboo")) {
interactiveTag = InteractiveTag.FEED;
}
break;
case PARROT:
if (javaIdentifierStripped.contains("seeds") || javaIdentifierStripped.equals("cookie")) {
interactiveTag = InteractiveTag.FEED;
}
break;
case PIG:
if (javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("potato") || javaIdentifierStripped.equals("beetroot")) {
interactiveTag = InteractiveTag.FEED;
} else if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) {
interactiveTag = InteractiveTag.MOUNT;
}
break;
case PIGLIN:
if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY) && javaIdentifierStripped.equals("gold_ingot")) {
interactiveTag = InteractiveTag.BARTER;
}
break;
case RABBIT:
if (javaIdentifierStripped.equals("dandelion") || javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("golden_carrot")) {
interactiveTag = InteractiveTag.FEED;
}
break;
case SHEEP:
if (javaIdentifierStripped.equals("wheat")) {
interactiveTag = InteractiveTag.FEED;
} else if (!entityMetadata.getFlags().getFlag(EntityFlag.SHEARED)) {
if (javaIdentifierStripped.equals("shears")) {
// Shear the sheep
interactiveTag = InteractiveTag.SHEAR;
} else if (javaIdentifierStripped.contains("_dye")) {
// Dye the sheep
interactiveTag = InteractiveTag.DYE;
}
}
break;
case STRIDER:
if (javaIdentifierStripped.equals("warped_fungus")) {
interactiveTag = InteractiveTag.FEED;
} else if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) {
interactiveTag = InteractiveTag.RIDE_STRIDER;
}
break;
case TURTLE:
if (javaIdentifierStripped.equals("seagrass")) {
interactiveTag = InteractiveTag.FEED;
}
break;
case VILLAGER:
if (entityMetadata.getInt(EntityData.VARIANT) != 14 && entityMetadata.getInt(EntityData.VARIANT) != 0
&& entityMetadata.getFloat(EntityData.SCALE) >= 0.75f) { // Not a nitwit, has a profession and is not a baby
interactiveTag = InteractiveTag.TRADE;
}
break;
case WANDERING_TRADER:
interactiveTag = InteractiveTag.TRADE; // Since you can always trade with a wandering villager, presumably.
break;
case WOLF:
if (javaIdentifierStripped.equals("bone") && !entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) {
// Bone and untamed - can tame
interactiveTag = InteractiveTag.TAME;
} else if (WOLF_FOODS.contains(javaIdentifierStripped)) {
// Compatible food in hand - feed
// Sometimes just sits/stands when the wolf isn't hungry - there doesn't appear to be a way to fix this
interactiveTag = InteractiveTag.FEED;
} else if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) &&
entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) {
// Tamed and owned by player - can sit/stand
interactiveTag = entityMetadata.getFlags().getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT;
}
break;
case ZOMBIE_VILLAGER:
// We can't guarantee the existence of the weakness effect so we just always show it.
if (javaIdentifierStripped.equals("golden_apple")) {
interactiveTag = InteractiveTag.CURE;
}
break;
default:
break;
}
}
session.getPlayerEntity().getMetadata().put(EntityData.INTERACTIVE_TAG, interactiveTag);
session.getPlayerEntity().getMetadata().put(EntityData.INTERACTIVE_TAG, interactiveTag.getValue());
session.getPlayerEntity().updateBedrockMetadata(session);
} else {
if (!(session.getPlayerEntity().getMetadata().get(EntityData.INTERACTIVE_TAG) == null) ||
!(session.getPlayerEntity().getMetadata().get(EntityData.INTERACTIVE_TAG) == "")) {
if (!session.getPlayerEntity().getMetadata().getString(EntityData.INTERACTIVE_TAG).isEmpty()) {
// No interactive tag should be sent
session.getPlayerEntity().getMetadata().remove(EntityData.INTERACTIVE_TAG);
session.getPlayerEntity().updateBedrockMetadata(session);
@ -147,4 +367,65 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
break;
}
}
/**
* All interactive tags in enum form. For potential API usage.
*/
public enum InteractiveTag {
NONE(true),
IGNITE_CREEPER("creeper"),
EDIT,
LEAVE_BOAT("exit.boat"),
FEED,
FISH("fishing"),
MILK,
MOOSHROOM_SHEAR("mooshear"),
MOOSHROOM_MILK_STEW("moostew"),
BOARD_BOAT("ride.boat"),
RIDE_MINECART("ride.minecart"),
RIDE_HORSE("ride.horse"),
RIDE_STRIDER("ride.strider"),
SHEAR,
SIT,
STAND,
TALK,
TAME,
DYE,
CURE,
OPEN_CONTAINER("opencontainer"),
CREATE_MAP("createMap"),
TAKE_PICTURE("takepicture"),
SADDLE,
MOUNT,
BOOST,
WRITE,
LEASH,
REMOVE_LEASH("unleash"),
NAME,
ATTACH_CHEST("attachchest"),
TRADE,
POSE_ARMOR_STAND("armorstand.pose"),
EQUIP_ARMOR_STAND("armorstand.equip"),
READ,
WAKE_VILLAGER("wakevillager"),
BARTER;
/**
* The full string that should be passed on to the client.
*/
@Getter
private final String value;
InteractiveTag(boolean isNone) {
this.value = "";
}
InteractiveTag(String value) {
this.value = "action.interact." + value;
}
InteractiveTag() {
this.value = "action.interact." + name().toLowerCase();
}
}
}

View File

@ -25,7 +25,13 @@
package org.geysermc.connector.network.translators.bedrock.entity.player;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket;
import com.nukkitx.math.vector.Vector3d;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
import org.geysermc.connector.common.ChatColor;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.PlayerEntity;
@ -34,11 +40,7 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
import java.util.concurrent.TimeUnit;
@Translator(packet = MovePlayerPacket.class)
public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPacket> {
@ -59,13 +61,11 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
return;
}
// We need to parse the float as a string since casting a float to a double causes us to
// lose precision and thus, causes players to get stuck when walking near walls
double javaY = packet.getPosition().getY() - EntityType.PLAYER.getOffset();
if (packet.isOnGround()) javaY = Math.ceil(javaY * 2) / 2;
if (session.getMovementSendIfIdle() != null) {
session.getMovementSendIfIdle().cancel(true);
}
Vector3d position = Vector3d.from(Double.parseDouble(Float.toString(packet.getPosition().getX())), javaY,
Double.parseDouble(Float.toString(packet.getPosition().getZ())));
Vector3d position = adjustBedrockPosition(packet.getPosition(), packet.isOnGround());
if(!session.confirmTeleport(position)){
return;
@ -106,6 +106,28 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
if (!colliding)
*/
session.sendDownstreamPacket(playerPositionRotationPacket);
// Schedule a position send loop if the player is idle
session.setMovementSendIfIdle(session.getConnector().getGeneralThreadPool().schedule(() -> sendPositionIfIdle(session),
3, TimeUnit.SECONDS));
}
/**
* Adjust the Bedrock position before sending to the Java server to account for inaccuracies in movement between
* the two versions.
*
* @param position the current Bedrock position of the client
* @param onGround whether the Bedrock player is on the ground
* @return the position to send to the Java server.
*/
private Vector3d adjustBedrockPosition(Vector3f position, boolean onGround) {
// We need to parse the float as a string since casting a float to a double causes us to
// lose precision and thus, causes players to get stuck when walking near walls
double javaY = position.getY() - EntityType.PLAYER.getOffset();
if (onGround) javaY = Math.ceil(javaY * 2) / 2;
return Vector3d.from(Double.parseDouble(Float.toString(position.getX())), javaY,
Double.parseDouble(Float.toString(position.getZ())));
}
public boolean isValidMove(GeyserSession session, MovePlayerPacket.Mode mode, Vector3f currentPosition, Vector3f newPosition) {
@ -147,4 +169,16 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
movePlayerPacket.setMode(MovePlayerPacket.Mode.RESPAWN);
session.sendUpstreamPacket(movePlayerPacket);
}
private void sendPositionIfIdle(GeyserSession session) {
if (session.isClosed()) return;
PlayerEntity entity = session.getPlayerEntity();
// Recalculate in case something else changed position
Vector3d position = adjustBedrockPosition(entity.getPosition(), entity.isOnGround());
ClientPlayerPositionPacket packet = new ClientPlayerPositionPacket(session.getPlayerEntity().isOnGround(),
position.getX(), position.getY(), position.getZ());
session.sendDownstreamPacket(packet);
session.setMovementSendIfIdle(session.getConnector().getGeneralThreadPool().schedule(() -> sendPositionIfIdle(session),
3, TimeUnit.SECONDS));
}
}

View File

@ -0,0 +1,278 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.network.translators.chat;
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
import com.github.steveice10.mc.protocol.data.message.style.ChatColor;
import com.github.steveice10.mc.protocol.data.message.style.ChatFormat;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.renderer.TranslatableComponentRenderer;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.translation.TranslationRegistry;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.LanguageUtils;
import java.util.*;
public class MessageTranslator {
// These are used for handling the translations of the messages
private static final TranslationRegistry REGISTRY = new MinecraftTranslationRegistry();
private static final TranslatableComponentRenderer<Locale> RENDERER = TranslatableComponentRenderer.usingTranslationSource(REGISTRY);
// Store team colors for player names
private static final Map<TeamColor, String> TEAM_COLORS = new HashMap<>();
static {
TEAM_COLORS.put(TeamColor.BLACK, getColor(ChatColor.BLACK));
TEAM_COLORS.put(TeamColor.DARK_BLUE, getColor(ChatColor.DARK_BLUE));
TEAM_COLORS.put(TeamColor.DARK_GREEN, getColor(ChatColor.DARK_GREEN));
TEAM_COLORS.put(TeamColor.DARK_AQUA, getColor(ChatColor.DARK_AQUA));
TEAM_COLORS.put(TeamColor.DARK_RED, getColor(ChatColor.DARK_RED));
TEAM_COLORS.put(TeamColor.DARK_PURPLE, getColor(ChatColor.DARK_PURPLE));
TEAM_COLORS.put(TeamColor.GOLD, getColor(ChatColor.GOLD));
TEAM_COLORS.put(TeamColor.GRAY, getColor(ChatColor.GRAY));
TEAM_COLORS.put(TeamColor.DARK_GRAY, getColor(ChatColor.DARK_GRAY));
TEAM_COLORS.put(TeamColor.BLUE, getColor(ChatColor.BLUE));
TEAM_COLORS.put(TeamColor.GREEN, getColor(ChatColor.GREEN));
TEAM_COLORS.put(TeamColor.AQUA, getColor(ChatColor.AQUA));
TEAM_COLORS.put(TeamColor.RED, getColor(ChatColor.RED));
TEAM_COLORS.put(TeamColor.LIGHT_PURPLE, getColor(ChatColor.LIGHT_PURPLE));
TEAM_COLORS.put(TeamColor.YELLOW, getColor(ChatColor.YELLOW));
TEAM_COLORS.put(TeamColor.WHITE, getColor(ChatColor.WHITE));
TEAM_COLORS.put(TeamColor.OBFUSCATED, getFormat(ChatFormat.OBFUSCATED));
TEAM_COLORS.put(TeamColor.BOLD, getFormat(ChatFormat.BOLD));
TEAM_COLORS.put(TeamColor.STRIKETHROUGH, getFormat(ChatFormat.STRIKETHROUGH));
TEAM_COLORS.put(TeamColor.ITALIC, getFormat(ChatFormat.ITALIC));
}
/**
* Convert a Java message to the legacy format ready for bedrock
*
* @param message Java message
* @param locale Locale to use for translation strings
* @return Parsed and formatted message for bedrock
*/
public static String convertMessage(String message, String locale) {
Component component = GsonComponentSerializer.gson().deserialize(message);
// Get a Locale from the given locale string
Locale localeCode = Locale.forLanguageTag(locale.replace('_', '-'));
component = RENDERER.render(component, localeCode);
return LegacyComponentSerializer.legacySection().serialize(component);
}
public static String convertMessage(String message) {
return convertMessage(message, LanguageUtils.getDefaultLocale());
}
/**
* Verifies the message is valid JSON in case it's plaintext. Works around GsonComponentSeraializer not using lenient mode.
* See https://wiki.vg/Chat for messages sent in lenient mode, and for a description on leniency.
*
* @param message Potentially lenient JSON message
* @param locale Locale to use for translation strings
* @return Bedrock formatted message
*/
public static String convertMessageLenient(String message, String locale) {
if (isMessage(message)) {
return convertMessage(message, locale);
} else {
String convertedMessage = convertMessage(convertToJavaMessage(message), locale);
// We have to do this since Adventure strips the starting reset character
if (message.startsWith(getColor(ChatColor.RESET))) {
convertedMessage = getColor(ChatColor.RESET) + convertedMessage;
}
return convertedMessage;
}
}
public static String convertMessageLenient(String message) {
return convertMessageLenient(message, LanguageUtils.getDefaultLocale());
}
/**
* Convert a Bedrock message string back to a format Java can understand
*
* @param message Message to convert
* @return The formatted JSON string
*/
public static String convertToJavaMessage(String message) {
Component component = LegacyComponentSerializer.legacySection().deserialize(message);
return GsonComponentSerializer.gson().serialize(component);
}
/**
* Checks if the given text string is a JSON message
*
* @param text String to test
* @return True if its a valid message JSON string, false if not
*/
public static boolean isMessage(String text) {
if (text.trim().isEmpty()) {
return false;
}
try {
GsonComponentSerializer.gson().deserialize(text);
} catch (Exception ex) {
return false;
}
return true;
}
/**
* Convert a {@link ChatColor} into a string for inserting into messages
*
* @param color {@link ChatColor} to convert
* @return The converted color string
*/
private static String getColor(String color) {
String base = "\u00a7";
switch (color) {
case ChatColor.BLACK:
base += "0";
break;
case ChatColor.DARK_BLUE:
base += "1";
break;
case ChatColor.DARK_GREEN:
base += "2";
break;
case ChatColor.DARK_AQUA:
base += "3";
break;
case ChatColor.DARK_RED:
base += "4";
break;
case ChatColor.DARK_PURPLE:
base += "5";
break;
case ChatColor.GOLD:
base += "6";
break;
case ChatColor.GRAY:
base += "7";
break;
case ChatColor.DARK_GRAY:
base += "8";
break;
case ChatColor.BLUE:
base += "9";
break;
case ChatColor.GREEN:
base += "a";
break;
case ChatColor.AQUA:
base += "b";
break;
case ChatColor.RED:
base += "c";
break;
case ChatColor.LIGHT_PURPLE:
base += "d";
break;
case ChatColor.YELLOW:
base += "e";
break;
case ChatColor.WHITE:
base += "f";
break;
case ChatColor.RESET:
base += "r";
break;
default:
return "";
}
return base;
}
/**
* Convert a {@link ChatFormat} into a string for inserting into messages
*
* @param format {@link ChatFormat} to convert
* @return The converted chat formatting string
*/
private static String getFormat(ChatFormat format) {
StringBuilder str = new StringBuilder();
String base = "\u00a7";
switch (format) {
case OBFUSCATED:
base += "k";
break;
case BOLD:
base += "l";
break;
case STRIKETHROUGH:
base += "m";
break;
case UNDERLINED:
base += "n";
break;
case ITALIC:
base += "o";
break;
default:
return "";
}
str.append(base);
return str.toString();
}
/**
* Convert a team color to a chat color
*
* @param teamColor
* @return The chat color character
*/
public static String toChatColor(TeamColor teamColor) {
return TEAM_COLORS.getOrDefault(teamColor, "");
}
/**
* Checks if the given message is over 256 characters (Java edition server chat limit) and sends a message to the user if it is
*
* @param message Message to check
* @param session {@link GeyserSession} for the user
* @return True if the message is too long, false if not
*/
public static boolean isTooLong(String message, GeyserSession session) {
if (message.length() > 256) {
session.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.chat.too_long", session.getLocale(), message.length()));
return true;
}
return false;
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.network.translators.chat;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.translation.TranslationRegistry;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.connector.utils.LocaleUtils;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This class is used for mapping a translation key with the already loaded Java locale data
* Used in MessageTranslator.java as part of the KyoriPowered/Adventure library
*/
public class MinecraftTranslationRegistry implements TranslationRegistry {
@Override
public @NonNull Key name() {
return Key.key("", "");
}
@Override
public @Nullable MessageFormat translate(@NonNull String key, @NonNull Locale locale) {
// Get the locale string
String localeString = LocaleUtils.getLocaleString(key, locale.toString());
// Replace the `%s` with numbered inserts `{0}`
Pattern p = Pattern.compile("%s");
Matcher m = p.matcher(localeString);
StringBuffer sb = new StringBuffer();
int i = 0;
while (m.find()) {
m.appendReplacement(sb, "{" + (i++) + "}");
}
m.appendTail(sb);
return new MessageFormat(sb.toString(), locale);
}
@Override
public void defaultLocale(@NonNull Locale locale) {
}
@Override
public void register(@NonNull String key, @NonNull Locale locale, @NonNull MessageFormat format) {
}
@Override
public void unregister(@NonNull String key) {
}
}

View File

@ -42,6 +42,7 @@ import org.geysermc.connector.common.ChatColor;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.utils.InventoryUtils;
import org.geysermc.connector.utils.LocaleUtils;
@ -57,9 +58,8 @@ import java.util.List;
*/
public class EnchantmentInventoryTranslator extends BlockInventoryTranslator {
private static final int DYE_ID = 351;
private static final short LAPIS_DAMAGE = 4;
private static final int ENCHANTED_BOOK_ID = 403;
private static final int DYE_ID = ItemRegistry.getItemEntry("minecraft:lapis_lazuli").getBedrockId();
private static final int ENCHANTED_BOOK_ID = ItemRegistry.getItemEntry("minecraft:enchanted_book").getBedrockId();
public EnchantmentInventoryTranslator(InventoryUpdater updater) {
super(2, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, updater);
@ -73,8 +73,7 @@ public class EnchantmentInventoryTranslator extends BlockInventoryTranslator {
switch (action.getSlot()) {
case 1:
// Don't allow the slot to be put through if the item isn't lapis
if ((action.getToItem().getId() != DYE_ID
&& action.getToItem().getDamage() != LAPIS_DAMAGE) && action.getToItem() != ItemData.AIR) {
if ((action.getToItem().getId() != DYE_ID) && action.getToItem() != ItemData.AIR) {
updateInventory(session, inventory);
InventoryUtils.updateCursor(session);
return;

View File

@ -53,9 +53,9 @@ public abstract class InventoryTranslator {
put(WindowType.BREWING_STAND, new BrewingInventoryTranslator());
put(WindowType.ANVIL, new AnvilInventoryTranslator());
put(WindowType.CRAFTING, new CraftingInventoryTranslator());
put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator());
//put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator()); //FIXME
put(WindowType.MERCHANT, new MerchantInventoryTranslator());
put(WindowType.SMITHING, new SmithingInventoryTranslator());
//put(WindowType.SMITHING, new SmithingInventoryTranslator()); //TODO for server authoritative inventories
InventoryTranslator furnace = new FurnaceInventoryTranslator();
put(WindowType.FURNACE, furnace);

View File

@ -34,9 +34,10 @@ import lombok.ToString;
@ToString
public class ItemEntry {
public static ItemEntry AIR = new ItemEntry("minecraft:air", 0, 0, 0, false);
public static ItemEntry AIR = new ItemEntry("minecraft:air", "minecraft:air", 0, 0, 0, false);
private final String javaIdentifier;
private final String bedrockIdentifier;
private final int javaId;
private final int bedrockId;
private final int bedrockData;

View File

@ -34,6 +34,8 @@ import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.utils.FileUtils;
import org.geysermc.connector.utils.LanguageUtils;
@ -56,13 +58,21 @@ public class ItemRegistry {
public static final Int2ObjectMap<ItemEntry> ITEM_ENTRIES = new Int2ObjectOpenHashMap<>();
/**
* Boat item entry, used in BedrockInventoryTransactionTranslator.java
* Bamboo item entry, used in PandaEntity.java
*/
public static ItemEntry BOAT;
public static ItemEntry BAMBOO;
/**
* Bucket item entry, used in BedrockInventoryTransactionTranslator.java
* Boat item entries, used in BedrockInventoryTransactionTranslator.java
*/
public static ItemEntry BUCKET;
public static IntList BOATS = new IntArrayList();
/**
* Bucket item entries (excluding the milk bucket), used in BedrockInventoryTransactionTranslator.java
*/
public static IntList BUCKETS = new IntArrayList();
/**
* Empty item bucket, used in BedrockInventoryTransactionTranslator.java
*/
public static ItemEntry MILK_BUCKET;
/**
* Egg item entry, used in JavaEntityStatusTranslator.java
*/
@ -75,6 +85,10 @@ public class ItemRegistry {
* Shield item entry, used in Entity.java and LivingEntity.java
*/
public static ItemEntry SHIELD;
/**
* Wheat item entry, used in AbstractHorseEntity.java
*/
public static ItemEntry WHEAT;
public static int BARRIER_INDEX = 0;
@ -84,11 +98,14 @@ public class ItemRegistry {
static {
/* Load item palette */
InputStream stream = FileUtils.getResource("bedrock/items.json");
InputStream stream = FileUtils.getResource("bedrock/runtime_item_states.json");
TypeReference<List<JsonNode>> itemEntriesType = new TypeReference<List<JsonNode>>() {
};
// Used to get the Bedrock namespaced ID (in instances where there are small differences)
Int2ObjectMap<String> bedrockIdToIdentifier = new Int2ObjectOpenHashMap<>();
List<JsonNode> itemEntries;
try {
itemEntries = GeyserConnector.JSON_MAPPER.readValue(stream, itemEntriesType);
@ -96,8 +113,14 @@ public class ItemRegistry {
throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.runtime_bedrock"), e);
}
int lodestoneCompassId = 0;
for (JsonNode entry : itemEntries) {
ITEMS.add(new StartGamePacket.ItemEntry(entry.get("name").textValue(), (short) entry.get("id").intValue()));
bedrockIdToIdentifier.put(entry.get("id").intValue(), entry.get("name").textValue());
if (entry.get("name").textValue().equals("minecraft:lodestone_compass")) {
lodestoneCompassId = entry.get("id").intValue();
}
}
stream = FileUtils.getResource("mappings/items.json");
@ -113,28 +136,29 @@ public class ItemRegistry {
Iterator<Map.Entry<String, JsonNode>> iterator = items.fields();
while (iterator.hasNext()) {
Map.Entry<String, JsonNode> entry = iterator.next();
int bedrockId = entry.getValue().get("bedrock_id").intValue();
String bedrockIdentifier = bedrockIdToIdentifier.get(bedrockId);
if (bedrockIdentifier == null) {
throw new RuntimeException("Missing Bedrock ID in mappings!: " + bedrockId);
}
if (entry.getValue().has("tool_type")) {
if (entry.getValue().has("tool_tier")) {
ITEM_ENTRIES.put(itemIndex, new ToolItemEntry(
entry.getKey(), itemIndex,
entry.getValue().get("bedrock_id").intValue(),
entry.getKey(), bedrockIdentifier, itemIndex, bedrockId,
entry.getValue().get("bedrock_data").intValue(),
entry.getValue().get("tool_type").textValue(),
entry.getValue().get("tool_tier").textValue(),
entry.getValue().get("is_block") != null && entry.getValue().get("is_block").booleanValue()));
} else {
ITEM_ENTRIES.put(itemIndex, new ToolItemEntry(
entry.getKey(), itemIndex,
entry.getValue().get("bedrock_id").intValue(),
entry.getKey(), bedrockIdentifier, itemIndex, bedrockId,
entry.getValue().get("bedrock_data").intValue(),
entry.getValue().get("tool_type").textValue(),
"",
entry.getValue().get("is_block").booleanValue()));
"", entry.getValue().get("is_block").booleanValue()));
}
} else {
ITEM_ENTRIES.put(itemIndex, new ItemEntry(
entry.getKey(), itemIndex,
entry.getValue().get("bedrock_id").intValue(),
entry.getKey(), bedrockIdentifier, itemIndex, bedrockId,
entry.getValue().get("bedrock_data").intValue(),
entry.getValue().get("is_block") != null && entry.getValue().get("is_block").booleanValue()));
}
@ -142,8 +166,8 @@ public class ItemRegistry {
case "minecraft:barrier":
BARRIER_INDEX = itemIndex;
break;
case "minecraft:oak_boat":
BOAT = ITEM_ENTRIES.get(itemIndex);
case "minecraft:bamboo":
BAMBOO = ITEM_ENTRIES.get(itemIndex);
break;
case "minecraft:egg":
EGG = ITEM_ENTRIES.get(itemIndex);
@ -154,18 +178,32 @@ public class ItemRegistry {
case "minecraft:shield":
SHIELD = ITEM_ENTRIES.get(itemIndex);
break;
case "minecraft:bucket":
BUCKET = ITEM_ENTRIES.get(itemIndex);
case "minecraft:milk_bucket":
MILK_BUCKET = ITEM_ENTRIES.get(itemIndex);
break;
case "minecraft:wheat":
WHEAT = ITEM_ENTRIES.get(itemIndex);
break;
default:
break;
}
if (entry.getKey().contains("boat")) {
BOATS.add(entry.getValue().get("bedrock_id").intValue());
} else if (entry.getKey().contains("bucket") && !entry.getKey().contains("milk")) {
BUCKETS.add(entry.getValue().get("bedrock_id").intValue());
}
itemIndex++;
}
// Add the loadstonecompass since it doesn't exist on java but we need it for item conversion
ITEM_ENTRIES.put(itemIndex, new ItemEntry("minecraft:lodestonecompass", itemIndex, 741, 0, false));
if (lodestoneCompassId == 0) {
throw new RuntimeException("Lodestone compass not found in item palette!");
}
// Add the loadstone compass since it doesn't exist on java but we need it for item conversion
ITEM_ENTRIES.put(itemIndex, new ItemEntry("minecraft:lodestone_compass", "minecraft:lodestone_compass", itemIndex,
lodestoneCompassId, 0, false));
/* Load creative items */
stream = FileUtils.getResource("bedrock/creative_items.json");
@ -226,25 +264,14 @@ public class ItemRegistry {
* @return an item entry from the given java edition identifier
*/
public static ItemEntry getItemEntry(String javaIdentifier) {
return JAVA_IDENTIFIER_MAP.computeIfAbsent(javaIdentifier, key -> ITEM_ENTRIES.values()
.stream().filter(itemEntry -> itemEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null));
}
/**
* Finds the Bedrock string identifier of an ItemEntry
*
* @param entry the ItemEntry to search for
* @return the Bedrock identifier
*/
public static String getBedrockIdentifer(ItemEntry entry) {
String blockName = "";
for (StartGamePacket.ItemEntry startGamePacketItemEntry : ItemRegistry.ITEMS) {
if (startGamePacketItemEntry.getId() == (short) entry.getBedrockId()) {
blockName = startGamePacketItemEntry.getIdentifier(); // Find the Bedrock string name
break;
return JAVA_IDENTIFIER_MAP.computeIfAbsent(javaIdentifier, key -> {
for (ItemEntry entry : ITEM_ENTRIES.values()) {
if (entry.getJavaIdentifier().equals(key)) {
return entry;
}
}
}
return blockName;
return null;
});
}
/**

View File

@ -26,7 +26,6 @@
package org.geysermc.connector.network.translators.item;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.message.MessageSerializer;
import com.github.steveice10.opennbt.tag.builtin.*;
import com.nukkitx.nbt.NbtList;
import com.nukkitx.nbt.NbtMap;
@ -35,23 +34,19 @@ import com.nukkitx.nbt.NbtType;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.ItemRemapper;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.FileUtils;
import org.geysermc.connector.utils.LanguageUtils;
import org.geysermc.connector.utils.MessageUtils;
import org.reflections.Reflections;
import java.util.*;
import java.util.stream.Collectors;
public abstract class ItemTranslator {
private static final Int2ObjectMap<ItemTranslator> ITEM_STACK_TRANSLATORS = new Int2ObjectOpenHashMap<>();
private static final List<NbtItemStackTranslator> NBT_TRANSLATORS;
@ -220,7 +215,7 @@ public abstract class ItemTranslator {
public abstract List<ItemEntry> getAppliedItems();
public NbtMap translateNbtToBedrock(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag) {
Map<String, Object> javaValue = new HashMap<>();
NbtMapBuilder builder = NbtMap.builder();
if (tag.getValue() != null && !tag.getValue().isEmpty()) {
for (String str : tag.getValue().keySet()) {
com.github.steveice10.opennbt.tag.builtin.Tag javaTag = tag.get(str);
@ -228,11 +223,9 @@ public abstract class ItemTranslator {
if (translatedTag == null)
continue;
javaValue.put(javaTag.getName(), translatedTag);
builder.put(javaTag.getName(), translatedTag);
}
}
NbtMapBuilder builder = NbtMap.builder();
javaValue.forEach(builder::put);
return builder.build();
}
@ -388,26 +381,17 @@ public abstract class ItemTranslator {
public static void translateDisplayProperties(GeyserSession session, CompoundTag tag) {
if (tag != null) {
CompoundTag display = tag.get("display");
if (display != null && !display.isEmpty() && display.contains("Name")) {
if (display != null && display.contains("Name")) {
String name = ((StringTag) display.get("Name")).getValue();
// If its not a message convert it
if (!MessageUtils.isMessage(name)) {
Component component = LegacyComponentSerializer.legacySection().deserialize(name);
name = GsonComponentSerializer.gson().serialize(component);
}
// Get the translated name and prefix it with a reset char
name = MessageTranslator.convertMessageLenient(name, session.getLocale());
// Check if its a message to translate
if (MessageUtils.isMessage(name)) {
// Get the translated name
name = MessageUtils.getTranslatedBedrockMessage(MessageSerializer.fromString(name), session.getLocale());
// Add the new name tag
display.put(new StringTag("Name", name));
// Add the new name tag
display.put(new StringTag("Name", name));
// Add to the new root tag
tag.put(display);
}
// Add to the new root tag
tag.put(display);
}
}
}

View File

@ -32,8 +32,8 @@ public class ToolItemEntry extends ItemEntry {
private final String toolType;
private final String toolTier;
public ToolItemEntry(String javaIdentifier, int javaId, int bedrockId, int bedrockData, String toolType, String toolTier, boolean isBlock) {
super(javaIdentifier, javaId, bedrockId, bedrockData, isBlock);
public ToolItemEntry(String javaIdentifier, String bedrockIdentifier, int javaId, int bedrockId, int bedrockData, String toolType, String toolTier, boolean isBlock) {
super(javaIdentifier, bedrockIdentifier, javaId, bedrockId, bedrockData, isBlock);
this.toolType = toolType;
this.toolTier = toolTier;
}

View File

@ -26,20 +26,16 @@
package org.geysermc.connector.network.translators.item.translators;
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.IntTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.github.steveice10.opennbt.tag.builtin.*;
import com.nukkitx.nbt.NbtList;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.nbt.NbtType;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.connector.network.translators.ItemRemapper;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.network.translators.item.ItemEntry;
import java.util.ArrayList;
import java.util.HashMap;
@ -49,54 +45,13 @@ import java.util.stream.Collectors;
@ItemRemapper
public class BannerTranslator extends ItemTranslator {
private final List<ItemEntry> appliedItems;
public BannerTranslator() {
appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("banner")).collect(Collectors.toList());
}
@Override
public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) {
if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, itemEntry);
ItemData itemData = super.translateToBedrock(itemStack, itemEntry);
CompoundTag blockEntityTag = itemStack.getNbt().get("BlockEntityTag");
if (blockEntityTag.contains("Patterns")) {
ListTag patterns = blockEntityTag.get("Patterns");
NbtMapBuilder builder = itemData.getTag().toBuilder();
builder.put("Patterns", convertBannerPattern(patterns));
itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.build());
}
return itemData;
}
@Override
public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) {
if (itemData.getTag() == null) return super.translateToJava(itemData, itemEntry);
ItemStack itemStack = super.translateToJava(itemData, itemEntry);
NbtMap nbtTag = itemData.getTag();
if (nbtTag.containsKey("Patterns", NbtType.COMPOUND)) {
List<NbtMap> patterns = nbtTag.getList("Patterns", NbtType.COMPOUND);
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
blockEntityTag.put(convertBannerPattern(patterns));
itemStack.getNbt().put(blockEntityTag);
}
return itemStack;
}
@Override
public List<ItemEntry> getAppliedItems() {
return appliedItems;
appliedItems = ItemRegistry.ITEM_ENTRIES.values()
.stream()
.filter(entry -> entry.getJavaIdentifier().endsWith("banner"))
.collect(Collectors.toList());
}
/**
@ -133,7 +88,6 @@ public class BannerTranslator extends ItemTranslator {
return NbtMap.builder()
.putInt("Color", 15 - (int) pattern.get("Color").getValue())
.putString("Pattern", (String) pattern.get("Pattern").getValue())
.putString("Pattern", patternName)
.build();
}
@ -147,8 +101,7 @@ public class BannerTranslator extends ItemTranslator {
public static ListTag convertBannerPattern(List<NbtMap> patterns) {
List<Tag> tagsList = new ArrayList<>();
for (Object patternTag : patterns) {
CompoundTag newPatternTag = getJavaBannerPattern((NbtMap) patternTag);
tagsList.add(newPatternTag);
tagsList.add(getJavaBannerPattern((NbtMap) patternTag));
}
return new ListTag("Patterns", tagsList);
@ -167,4 +120,51 @@ public class BannerTranslator extends ItemTranslator {
return new CompoundTag("", tags);
}
@Override
public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) {
if (itemStack.getNbt() == null) {
return super.translateToBedrock(itemStack, itemEntry);
}
ItemData itemData = super.translateToBedrock(itemStack, itemEntry);
CompoundTag blockEntityTag = itemStack.getNbt().get("BlockEntityTag");
if (blockEntityTag != null && blockEntityTag.contains("Patterns")) {
ListTag patterns = blockEntityTag.get("Patterns");
NbtMapBuilder builder = itemData.getTag().toBuilder();
builder.put("Patterns", convertBannerPattern(patterns));
itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.build());
}
return itemData;
}
@Override
public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) {
if (itemData.getTag() == null) {
return super.translateToJava(itemData, itemEntry);
}
ItemStack itemStack = super.translateToJava(itemData, itemEntry);
NbtMap nbtTag = itemData.getTag();
if (nbtTag.containsKey("Patterns", NbtType.COMPOUND)) {
List<NbtMap> patterns = nbtTag.getList("Patterns", NbtType.COMPOUND);
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
blockEntityTag.put(convertBannerPattern(patterns));
itemStack.getNbt().put(blockEntityTag);
}
return itemStack;
}
@Override
public List<ItemEntry> getAppliedItems() {
return appliedItems;
}
}

View File

@ -53,7 +53,7 @@ public class CompassTranslator extends ItemTranslator {
Tag lodestoneTag = itemStack.getNbt().get("LodestoneTracked");
if (lodestoneTag instanceof ByteTag) {
// Get the fake lodestonecompass entry
itemEntry = ItemRegistry.getItemEntry("minecraft:lodestonecompass");
itemEntry = ItemRegistry.getItemEntry("minecraft:lodestone_compass");
// Get the loadstone pos
CompoundTag loadstonePos = itemStack.getNbt().get("LodestonePos");
@ -83,7 +83,7 @@ public class CompassTranslator extends ItemTranslator {
@Override
public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) {
boolean isLoadstone = false;
if (itemEntry.getJavaIdentifier().equals("minecraft:lodestonecompass")) {
if (itemEntry.getBedrockIdentifier().equals("minecraft:lodestone_compass")) {
// Revert the entry back to the compass
itemEntry = ItemRegistry.getItemEntry("minecraft:compass");

View File

@ -37,7 +37,6 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.ItemRemapper;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
import org.geysermc.connector.utils.MessageUtils;
import java.util.ArrayList;
import java.util.List;
@ -108,7 +107,7 @@ public class BasicItemTranslator extends NbtItemStackTranslator {
private String toBedrockMessage(StringTag tag) {
String message = tag.getValue();
if (message == null) return null;
TextComponent component = (TextComponent) MessageUtils.phraseJavaMessage(message);
TextComponent component = (TextComponent) GsonComponentSerializer.gson().deserialize(message);
String legacy = LegacyComponentSerializer.legacySection().serialize(component);
if (hasFormatting(LegacyComponentSerializer.legacySection().deserialize(legacy))) {
return "§r" + legacy;

View File

@ -33,7 +33,7 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.ItemRemapper;
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import java.util.ArrayList;
import java.util.List;
@ -56,7 +56,7 @@ public class BookPagesTranslator extends NbtItemStackTranslator {
CompoundTag pageTag = new CompoundTag("");
pageTag.put(new StringTag("photoname", ""));
pageTag.put(new StringTag("text", MessageUtils.getBedrockMessageLenient(textTag.getValue())));
pageTag.put(new StringTag("text", MessageTranslator.convertMessageLenient(textTag.getValue())));
pages.add(pageTag);
}
@ -78,7 +78,7 @@ public class BookPagesTranslator extends NbtItemStackTranslator {
CompoundTag pageTag = (CompoundTag) tag;
StringTag textTag = pageTag.get("text");
pages.add(new StringTag(MessageUtils.getJavaMessage(textTag.getValue())));
pages.add(new StringTag(MessageTranslator.convertToJavaMessage(textTag.getValue())));
}
itemTag.remove("pages");

View File

@ -45,15 +45,15 @@ public class CrossbowTranslator extends NbtItemStackTranslator {
if (!chargedProjectiles.getValue().isEmpty()) {
CompoundTag projectile = (CompoundTag) chargedProjectiles.getValue().get(0);
ItemEntry entry = ItemRegistry.getItemEntry((String) projectile.get("id").getValue());
if (entry == null) return;
ItemEntry projectileEntry = ItemRegistry.getItemEntry((String) projectile.get("id").getValue());
if (projectileEntry == null) return;
CompoundTag tag = projectile.get("tag");
ItemStack itemStack = new ItemStack(itemEntry.getJavaId(), (byte) projectile.get("Count").getValue(), tag);
ItemData itemData = ItemTranslator.translateToBedrock(session, itemStack);
CompoundTag newProjectile = new CompoundTag("chargedItem");
newProjectile.put(new ByteTag("Count", (byte) itemData.getCount()));
newProjectile.put(new StringTag("Name", ItemRegistry.getBedrockIdentifer(entry)));
newProjectile.put(new StringTag("Name", projectileEntry.getBedrockIdentifier()));
newProjectile.put(new ShortTag("Damage", itemData.getDamage()));

View File

@ -25,10 +25,7 @@
package org.geysermc.connector.network.translators.item.translators.nbt;
import com.github.steveice10.opennbt.tag.builtin.ByteTag;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
import com.github.steveice10.opennbt.tag.builtin.LongTag;
import com.github.steveice10.opennbt.tag.builtin.*;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.ItemRemapper;
import org.geysermc.connector.network.translators.item.ItemEntry;
@ -39,14 +36,22 @@ public class MapItemTranslator extends NbtItemStackTranslator {
@Override
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) {
IntTag mapId = itemTag.get("map");
// Can be either an IntTag or ShortTag
Tag mapId = itemTag.get("map");
if (mapId == null) return;
if (mapId != null) {
itemTag.put(new LongTag("map_uuid", mapId.getValue()));
itemTag.put(new IntTag("map_name_index", mapId.getValue()));
itemTag.put(new ByteTag("map_display_players", (byte) 1));
itemTag.remove("map");
int mapValue;
if (mapId.getValue() instanceof Short) {
// Convert to int if necessary
mapValue = (int) (short) mapId.getValue();
} else {
mapValue = (int) mapId.getValue();
}
itemTag.put(new LongTag("map_uuid", mapValue));
itemTag.put(new IntTag("map_name_index", mapValue));
itemTag.put(new ByteTag("map_display_players", (byte) 1));
itemTag.remove("map");
}
@Override

View File

@ -50,9 +50,8 @@ public class ShulkerBoxItemTranslator extends NbtItemStackTranslator {
boxItemTag.put(new ByteTag("WasPickedUp", (byte) 0)); // ???
ItemEntry boxItemEntry = ItemRegistry.getItemEntry(((StringTag) itemData.get("id")).getValue());
String blockName = ItemRegistry.getBedrockIdentifer(boxItemEntry);
boxItemTag.put(new StringTag("Name", blockName));
boxItemTag.put(new StringTag("Name", boxItemEntry.getBedrockIdentifier()));
boxItemTag.put(new ShortTag("Damage", (short) boxItemEntry.getBedrockData()));
boxItemTag.put(new ByteTag("Count", ((ByteTag) itemData.get("Count")).getValue()));
if (itemData.contains("tag")) {

View File

@ -25,15 +25,12 @@
package org.geysermc.connector.network.translators.java;
import com.github.steveice10.mc.protocol.data.message.TranslationMessage;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerChatPacket;
import com.nukkitx.protocol.bedrock.packet.TextPacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.utils.MessageUtils;
import java.util.List;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
@Translator(packet = ServerChatPacket.class)
public class JavaChatTranslator extends PacketTranslator<ServerChatPacket> {
@ -59,21 +56,8 @@ public class JavaChatTranslator extends PacketTranslator<ServerChatPacket> {
break;
}
String locale = session.getLocale();
if (packet.getMessage() instanceof TranslationMessage) {
textPacket.setType(TextPacket.Type.TRANSLATION);
textPacket.setNeedsTranslation(true);
List<String> paramsTranslated = MessageUtils.getTranslationParams(((TranslationMessage) packet.getMessage()).getWith(), locale, packet.getMessage());
textPacket.setParameters(paramsTranslated);
textPacket.setMessage(MessageUtils.insertParams(MessageUtils.getTranslatedBedrockMessage(packet.getMessage(), locale, true, packet.getMessage()), paramsTranslated));
} else {
textPacket.setNeedsTranslation(false);
textPacket.setMessage(MessageUtils.getTranslatedBedrockMessage(packet.getMessage(), locale, false, packet.getMessage()));
}
textPacket.setNeedsTranslation(false);
textPacket.setMessage(MessageTranslator.convertMessage(packet.getMessage().toString(), session.getLocale()));
session.sendUpstreamPacket(textPacket);
}

View File

@ -29,13 +29,13 @@ import com.github.steveice10.mc.protocol.packet.ingame.server.ServerDisconnectPa
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
@Translator(packet = ServerDisconnectPacket.class)
public class JavaDisconnectPacket extends PacketTranslator<ServerDisconnectPacket> {
@Override
public void translate(ServerDisconnectPacket packet, GeyserSession session) {
session.disconnect(MessageUtils.getTranslatedBedrockMessage(packet.getReason(), session.getLocale(), true));
session.disconnect(MessageTranslator.convertMessage(packet.getReason().toString(), session.getLocale()));
}
}

View File

@ -55,7 +55,7 @@ public class JavaJoinGameTranslator extends PacketTranslator<ServerJoinGamePacke
// are swapping servers
String newDimension = DimensionUtils.getNewDimension(packet.getDimension());
if (session.isSpawned()) {
String fakeDim = entity.getDimension().equals(DimensionUtils.OVERWORLD) ? DimensionUtils.NETHER : DimensionUtils.OVERWORLD;
String fakeDim = session.getDimension().equals(DimensionUtils.OVERWORLD) ? DimensionUtils.NETHER : DimensionUtils.OVERWORLD;
DimensionUtils.switchDimension(session, fakeDim);
DimensionUtils.switchDimension(session, newDimension);
@ -101,7 +101,7 @@ public class JavaJoinGameTranslator extends PacketTranslator<ServerJoinGamePacke
session.sendDownstreamPacket(new ClientPluginMessagePacket("minecraft:register", PluginMessageUtils.getFloodgateRegisterData()));
}
if (!newDimension.equals(entity.getDimension())) {
if (!newDimension.equals(session.getDimension())) {
DimensionUtils.switchDimension(session, newDimension);
}
}

View File

@ -29,7 +29,7 @@ import com.github.steveice10.mc.protocol.packet.login.server.LoginDisconnectPack
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
@Translator(packet = LoginDisconnectPacket.class)
public class JavaLoginDisconnectTranslator extends PacketTranslator<LoginDisconnectPacket> {
@ -37,6 +37,6 @@ public class JavaLoginDisconnectTranslator extends PacketTranslator<LoginDisconn
@Override
public void translate(LoginDisconnectPacket packet, GeyserSession session) {
// The client doesn't manually get disconnected so we have to do it ourselves
session.disconnect(MessageUtils.getTranslatedBedrockMessage(packet.getReason(), session.getLocale()));
session.disconnect(MessageTranslator.convertMessage(packet.getReason().toString(), session.getLocale()));
}
}

View File

@ -67,11 +67,11 @@ public class JavaRespawnTranslator extends PacketTranslator<ServerRespawnPacket>
}
String newDimension = DimensionUtils.getNewDimension(packet.getDimension());
if (!entity.getDimension().equals(newDimension)) {
if (!session.getDimension().equals(newDimension)) {
DimensionUtils.switchDimension(session, newDimension);
} else {
if (session.isManyDimPackets()) { //reloading world
String fakeDim = entity.getDimension().equals(DimensionUtils.OVERWORLD) ? DimensionUtils.NETHER : DimensionUtils.OVERWORLD;
String fakeDim = session.getDimension().equals(DimensionUtils.OVERWORLD) ? DimensionUtils.NETHER : DimensionUtils.OVERWORLD;
DimensionUtils.switchDimension(session, fakeDim);
DimensionUtils.switchDimension(session, newDimension);
} else {

View File

@ -28,7 +28,7 @@ package org.geysermc.connector.network.translators.java;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerTitlePacket;
import com.nukkitx.protocol.bedrock.packet.SetTitlePacket;
@ -41,14 +41,21 @@ public class JavaTitleTranslator extends PacketTranslator<ServerTitlePacket> {
SetTitlePacket titlePacket = new SetTitlePacket();
String locale = session.getLocale();
String text;
if (packet.getTitle() == null) {
text = " ";
} else {
text = MessageTranslator.convertMessage(packet.getTitle().toString(), locale);
}
switch (packet.getAction()) {
case TITLE:
titlePacket.setType(SetTitlePacket.Type.TITLE);
titlePacket.setText(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), locale));
titlePacket.setText(text);
break;
case SUBTITLE:
titlePacket.setType(SetTitlePacket.Type.SUBTITLE);
titlePacket.setText(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), locale));
titlePacket.setText(text);
break;
case CLEAR:
case RESET:
@ -57,9 +64,10 @@ public class JavaTitleTranslator extends PacketTranslator<ServerTitlePacket> {
break;
case ACTION_BAR:
titlePacket.setType(SetTitlePacket.Type.ACTIONBAR);
titlePacket.setText(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), locale));
titlePacket.setText(text);
break;
case TIMES:
titlePacket.setType(SetTitlePacket.Type.TIMES);
titlePacket.setFadeInTime(packet.getFadeIn());
titlePacket.setFadeOutTime(packet.getFadeOut());
titlePacket.setStayTime(packet.getStay());

View File

@ -62,7 +62,7 @@ public class JavaEntityAttachTranslator extends PacketTranslator<ServerEntityAtt
if ((attachedToId == null || packet.getAttachedToId() == 0)) {
// Is not being leashed
holderId.getMetadata().getFlags().setFlag(EntityFlag.LEASHED, false);
holderId.getMetadata().put(EntityData.LEASH_HOLDER_EID, 0);
holderId.getMetadata().put(EntityData.LEASH_HOLDER_EID, -1L);
holderId.updateBedrockMetadata(session);
EntityEventPacket eventPacket = new EntityEventPacket();
eventPacket.setRuntimeEntityId(holderId.getGeyserId());

View File

@ -33,6 +33,8 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData;
import com.nukkitx.protocol.bedrock.packet.SetEntityLinkPacket;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.living.ArmorStandEntity;
import org.geysermc.connector.entity.living.animal.AnimalEntity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
@ -46,6 +48,10 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator<ServerEn
@Override
public void translate(ServerEntitySetPassengersPacket packet, GeyserSession session) {
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
entity = session.getPlayerEntity();
}
if (entity == null) return;
LongOpenHashSet passengers = entity.getPassengers().clone();
@ -123,19 +129,19 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator<ServerEn
}
private float getMountedHeightOffset(Entity mount) {
final EntityType mountType = mount.getEntityType();
float mountedHeightOffset = mountType.getHeight() * 0.75f;
switch (mountType) {
float height = mount.getMetadata().getFloat(EntityData.BOUNDING_BOX_HEIGHT);
float mountedHeightOffset = height * 0.75f;
switch (mount.getEntityType()) {
case CHICKEN:
case SPIDER:
mountedHeightOffset = mountType.getHeight() * 0.5f;
mountedHeightOffset = height * 0.5f;
break;
case DONKEY:
case MULE:
mountedHeightOffset -= 0.25f;
break;
case LLAMA:
mountedHeightOffset = mountType.getHeight() * 0.67f;
mountedHeightOffset = height * 0.67f;
break;
case MINECART:
case MINECART_HOPPER:
@ -152,10 +158,10 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator<ServerEn
case HOGLIN:
case ZOGLIN:
boolean isBaby = mount.getMetadata().getFlags().getFlag(EntityFlag.BABY);
mountedHeightOffset = mountType.getHeight() - (isBaby ? 0.2f : 0.15f);
mountedHeightOffset = height - (isBaby ? 0.2f : 0.15f);
break;
case PIGLIN:
mountedHeightOffset = mountType.getHeight() * 0.92f;
mountedHeightOffset = height * 0.92f;
break;
case RAVAGER:
mountedHeightOffset = 2.1f;
@ -164,7 +170,7 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator<ServerEn
mountedHeightOffset -= 0.1875f;
break;
case STRIDER:
mountedHeightOffset = mountType.getHeight() - 0.19f;
mountedHeightOffset = height - 0.19f;
break;
}
return mountedHeightOffset;
@ -178,11 +184,10 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator<ServerEn
case WITHER_SKELETON:
return -0.6f;
case ARMOR_STAND:
// Armor stand isn't a marker
if (passenger.getMetadata().getFloat(EntityData.BOUNDING_BOX_HEIGHT) != 0.0f) {
return 0.1f;
} else {
if (((ArmorStandEntity) passenger).isMarker()) {
return 0.0f;
} else {
return 0.1f;
}
case ENDERMITE:
case SILVERFISH:
@ -205,6 +210,9 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator<ServerEn
case PLAYER:
return -0.35f;
}
if (passenger instanceof AnimalEntity) {
return 0.14f;
}
return 0f;
}
@ -240,7 +248,7 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator<ServerEn
* Horses are tinier
* Players, Minecarts, and Boats have different origins
*/
if (passenger.getEntityType() == EntityType.PLAYER) {
if (passenger.getEntityType() == EntityType.PLAYER && mount.getEntityType() != EntityType.PLAYER) {
yOffset += EntityType.PLAYER.getOffset();
}
switch (mount.getEntityType()) {

View File

@ -25,15 +25,13 @@
package org.geysermc.connector.network.translators.java.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.world.particle.ItemParticleData;
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityStatusPacket;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.SoundEvent;
import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
import com.nukkitx.protocol.bedrock.packet.LevelSoundEvent2Packet;
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket;
@ -42,9 +40,7 @@ import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.item.ItemTranslator;
@Translator(packet = ServerEntityStatusPacket.class)
public class JavaEntityStatusTranslator extends PacketTranslator<ServerEntityStatusPacket> {
@ -141,9 +137,15 @@ public class JavaEntityStatusTranslator extends PacketTranslator<ServerEntitySta
case TAMEABLE_TAMING_SUCCEEDED:
entityEventPacket.setType(EntityEventType.TAME_SUCCEEDED);
break;
case ZOMBIE_VILLAGER_CURE:
entityEventPacket.setType(EntityEventType.ZOMBIE_VILLAGER_CURE);
break;
case ZOMBIE_VILLAGER_CURE: // Played when a zombie bites the golden apple
LevelSoundEvent2Packet soundPacket = new LevelSoundEvent2Packet();
soundPacket.setSound(SoundEvent.REMEDY);
soundPacket.setPosition(entity.getPosition());
soundPacket.setExtraData(-1);
soundPacket.setIdentifier("");
soundPacket.setRelativeVolumeDisabled(false);
session.sendUpstreamPacket(soundPacket);
return;
case ANIMAL_EMIT_HEARTS:
entityEventPacket.setType(EntityEventType.LOVE_PARTICLES);
break;

View File

@ -43,15 +43,21 @@ public class JavaSpawnPlayerTranslator extends PacketTranslator<ServerSpawnPlaye
Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
Vector3f rotation = Vector3f.from(packet.getYaw(), packet.getPitch(), packet.getYaw());
PlayerEntity entity = session.getEntityCache().getPlayerEntity(packet.getUuid());
if (entity == null) {
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.entity.player.failed_list", packet.getUuid()));
return;
}
PlayerEntity entity;
if (packet.getUuid().equals(session.getPlayerEntity().getUuid())) {
// Server is sending a fake version of the current player
entity = new PlayerEntity(session.getPlayerEntity().getProfile(), packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), position, Vector3f.ZERO, rotation);
} else {
entity = session.getEntityCache().getPlayerEntity(packet.getUuid());
if (entity == null) {
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.entity.player.failed_list", packet.getUuid()));
return;
}
entity.setEntityId(packet.getEntityId());
entity.setPosition(position);
entity.setRotation(rotation);
entity.setEntityId(packet.getEntityId());
entity.setPosition(position);
entity.setRotation(rotation);
}
session.getEntityCache().cacheEntity(entity);
if (session.getUpstream().isInitialized()) {

View File

@ -36,8 +36,7 @@ public class JavaDisplayScoreboardTranslator extends PacketTranslator<ServerDisp
@Override
public void translate(ServerDisplayScoreboardPacket packet, GeyserSession session) {
session.getWorldCache().getScoreboard().registerNewObjective(
packet.getName(), packet.getPosition()
);
session.getWorldCache().getScoreboard()
.displayObjective(packet.getName(), packet.getPosition());
}
}

View File

@ -31,7 +31,8 @@ import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.scoreboard.Objective;
import org.geysermc.connector.scoreboard.Scoreboard;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import com.github.steveice10.mc.protocol.data.game.scoreboard.ObjectiveAction;
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerScoreboardObjectivePacket;
@ -41,8 +42,10 @@ public class JavaScoreboardObjectiveTranslator extends PacketTranslator<ServerSc
@Override
public void translate(ServerScoreboardObjectivePacket packet, GeyserSession session) {
Scoreboard scoreboard = session.getWorldCache().getScoreboard();
WorldCache worldCache = session.getWorldCache();
Scoreboard scoreboard = worldCache.getScoreboard();
Objective objective = scoreboard.getObjective(packet.getName());
int pps = worldCache.increaseAndGetScoreboardPacketsPerSecond();
if (objective == null && packet.getAction() != ObjectiveAction.REMOVE) {
objective = scoreboard.registerNewObjective(packet.getName(), false);
@ -51,15 +54,21 @@ public class JavaScoreboardObjectiveTranslator extends PacketTranslator<ServerSc
switch (packet.getAction()) {
case ADD:
case UPDATE:
objective.setDisplayName(MessageUtils.getBedrockMessage(packet.getDisplayName()));
objective.setType(packet.getType().ordinal());
objective.setDisplayName(MessageTranslator.convertMessage(packet.getDisplayName().toString()))
.setType(packet.getType().ordinal());
break;
case REMOVE:
scoreboard.unregisterObjective(packet.getName());
break;
}
if (objective != null && objective.isActive()) {
if (objective == null || !objective.isActive()) {
return;
}
// ScoreboardUpdater will handle it for us if the packets per second
// (for score and team packets) is higher then the first threshold
if (pps < ScoreboardUpdater.FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD) {
scoreboard.onUpdate();
}
}

View File

@ -37,7 +37,7 @@ import org.geysermc.connector.scoreboard.ScoreboardUpdater;
import org.geysermc.connector.scoreboard.Team;
import org.geysermc.connector.scoreboard.UpdateType;
import org.geysermc.connector.utils.LanguageUtils;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
import java.util.Arrays;
import java.util.Set;
@ -59,11 +59,11 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
switch (packet.getAction()) {
case CREATE:
scoreboard.registerNewTeam(packet.getTeamName(), toPlayerSet(packet.getPlayers()))
.setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
.setName(MessageTranslator.convertMessage(packet.getDisplayName().toString()))
.setColor(packet.getColor())
.setNameTagVisibility(packet.getNameTagVisibility())
.setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getLocale()))
.setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getLocale()));
.setPrefix(MessageTranslator.convertMessage(packet.getPrefix().toString(), session.getLocale()))
.setSuffix(MessageTranslator.convertMessage(packet.getSuffix().toString(), session.getLocale()));
break;
case UPDATE:
if (team == null) {
@ -74,11 +74,11 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
return;
}
team.setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
team.setName(MessageTranslator.convertMessage(packet.getDisplayName().toString()))
.setColor(packet.getColor())
.setNameTagVisibility(packet.getNameTagVisibility())
.setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getLocale()))
.setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getLocale()))
.setPrefix(MessageTranslator.convertMessage(packet.getPrefix().toString(), session.getLocale()))
.setSuffix(MessageTranslator.convertMessage(packet.getSuffix().toString(), session.getLocale()))
.setUpdateType(UpdateType.UPDATE);
break;
case ADD_PLAYER:

View File

@ -25,7 +25,6 @@
package org.geysermc.connector.network.translators.java.window;
import com.github.steveice10.mc.protocol.data.message.MessageSerializer;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
import org.geysermc.connector.inventory.Inventory;
@ -35,7 +34,7 @@ import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.utils.InventoryUtils;
import org.geysermc.connector.utils.LocaleUtils;
import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.network.translators.chat.MessageTranslator;
@Translator(packet = ServerOpenWindowPacket.class)
public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowPacket> {
@ -57,8 +56,7 @@ public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowP
return;
}
String name = MessageUtils.getTranslatedBedrockMessage(MessageSerializer.fromString(packet.getName()),
session.getLocale());
String name = MessageTranslator.convertMessageLenient(packet.getName(), session.getLocale());
name = LocaleUtils.getLocaleString(name, session.getLocale());

View File

@ -29,7 +29,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.SoundEvent;
import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
import org.geysermc.connector.common.PlatformType;
import org.geysermc.common.PlatformType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;

View File

@ -25,6 +25,7 @@
package org.geysermc.connector.network.translators.java.world;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.world.block.value.*;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerBlockValuePacket;
import com.nukkitx.math.vector.Vector3i;
@ -53,16 +54,12 @@ public class JavaBlockValueTranslator extends PacketTranslator<ServerBlockValueP
blockEventPacket.setEventType(1);
blockEventPacket.setEventData(value.getViewers() > 0 ? 1 : 0);
session.sendUpstreamPacket(blockEventPacket);
}
if (packet.getValue() instanceof EndGatewayValue) {
} else if (packet.getValue() instanceof EndGatewayValue) {
blockEventPacket.setEventType(1);
session.sendUpstreamPacket(blockEventPacket);
}
if (packet.getValue() instanceof NoteBlockValue) {
} else if (packet.getValue() instanceof NoteBlockValue) {
NoteblockBlockEntityTranslator.translate(session, packet.getPosition());
return;
}
if (packet.getValue() instanceof PistonValue) {
} else if (packet.getValue() instanceof PistonValue) {
PistonValueType type = (PistonValueType) packet.getType();
// Unlike everything else, pistons need a block entity packet to convey motion
@ -73,14 +70,46 @@ public class JavaBlockValueTranslator extends PacketTranslator<ServerBlockValueP
} else {
retractPiston(session, position, 1.0f, 1.0f);
}
}
if (packet.getValue() instanceof MobSpawnerValue) {
} else if (packet.getValue() instanceof MobSpawnerValue) {
blockEventPacket.setEventType(1);
session.sendUpstreamPacket(blockEventPacket);
}
if (packet.getValue() instanceof EndGatewayValue) {
} else if (packet.getValue() instanceof EndGatewayValue) {
blockEventPacket.setEventType(1);
session.sendUpstreamPacket(blockEventPacket);
} else if (packet.getValue() instanceof GenericBlockValue && packet.getBlockId() == 677) {
// TODO: Remove hardcode? Remove hardcodes for all of these? (EndGatewayValue for example still hardcodes the block)
// Bells - needed to show ring from other players
GenericBlockValue bellValue = (GenericBlockValue) packet.getValue();
Position position = packet.getPosition();
BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket();
blockEntityPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ()));
NbtMapBuilder builder = NbtMap.builder();
builder.putInt("x", position.getX());
builder.putInt("y", position.getY());
builder.putInt("z", position.getZ());
builder.putString("id", "Bell");
int bedrockRingDirection;
switch (bellValue.getValue()) {
case 3: // north
bedrockRingDirection = 0;
break;
case 4: // east
bedrockRingDirection = 1;
break;
case 5: // west
bedrockRingDirection = 3;
break;
default: // south (2) is identical
bedrockRingDirection = bellValue.getValue();
}
builder.putInt("Direction", bedrockRingDirection);
builder.putByte("Ringing", (byte) 1);
builder.putInt("Ticks", 0);
blockEntityPacket.setData(builder.build());
session.sendUpstreamPacket(blockEntityPacket);
}
}

View File

@ -45,14 +45,13 @@ import org.geysermc.connector.utils.ChunkUtils;
@Translator(packet = ServerChunkDataPacket.class)
public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPacket> {
/**
* Determines if we should process non-full chunks
*/
private final boolean isCacheChunks;
private final boolean cacheChunks;
public JavaChunkDataTranslator() {
isCacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks();
cacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks();
}
@Override
@ -61,7 +60,7 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
ChunkUtils.updateChunkPosition(session, session.getPlayerEntity().getPosition().toInt());
}
if (packet.getColumn().getBiomeData() == null && !isCacheChunks) {
if (packet.getColumn().getBiomeData() == null && !cacheChunks) {
// Non-full chunk without chunk caching
session.getConnector().getLogger().debug("Not sending non-full chunk because chunk caching is off.");
return;

View File

@ -46,7 +46,7 @@ public class JavaExplosionTranslator extends PacketTranslator<ServerExplosionPac
public void translate(ServerExplosionPacket packet, GeyserSession session) {
for (ExplodedBlockRecord record : packet.getExploded()) {
Vector3f pos = Vector3f.from(packet.getX() + record.getX(), packet.getY() + record.getY(), packet.getZ() + record.getZ());
ChunkUtils.updateBlock(session, BlockTranslator.AIR, pos.toInt());
ChunkUtils.updateBlock(session, BlockTranslator.JAVA_AIR_ID, pos.toInt());
}
Vector3f pos = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());

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