Merge pull request #1 from Camotoy/server-inventory

Progress
This commit is contained in:
AJ Ferguson 2020-12-26 14:07:50 -09:00 committed by GitHub
commit 6df89ed679
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
296 changed files with 15346 additions and 7750 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

3
.gitignore vendored
View File

@ -242,4 +242,5 @@ logs/
public-key.pem
locales/
/cache/
/packs/
/packs/
/dump.json

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/v1.16.101/v1.16.200 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.
@ -34,16 +34,26 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set
- Test Server: `test.geysermc.org` port `25565` for Java and `19132` for Bedrock
## What's Left to be Added/Fixed
- The Following Inventories
- [ ] Enchantment Table (as a proper GUI)
- [ ] Beacon
- [ ] Cartography Table
- [ ] Stonecutter
- [ ] Structure Block
- [ ] Horse Inventory
- [ ] Loom
- [ ] Smithing Table
- Lecterns
- Near-perfect movement (to the point where anticheat on large servers is unlikely to ban you)
- Resource pack conversion/CustomModelData
- Some Entity Flags
- The Following Inventories
- Enchantment Table (as a proper GUI)
- Beacon
- Cartography Table
- Stonecutter
- Structure Block
- Horse Inventory
- Loom
- Smithing Table
## What can't be fixed
The following things can't be fixed because of Bedrock limitations. They might be fixable in the future, but not as of now.
- Custom heads in inventories
- Clickable links in chat
- Glowing effect
## Compiling
1. Clone the repo to your computer

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

@ -47,14 +47,12 @@ import java.util.concurrent.CompletableFuture;
@AllArgsConstructor
public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, Listener {
private static final GeyserPendingConnection PENDING_CONNECTION = new GeyserPendingConnection();
private final ProxyServer proxyServer;
@Override
public GeyserPingInfo getPingInformation() {
public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
CompletableFuture<ProxyPingEvent> future = new CompletableFuture<>();
proxyServer.getPluginManager().callEvent(new ProxyPingEvent(PENDING_CONNECTION, getPingInfo(), (event, throwable) -> {
proxyServer.getPluginManager().callEvent(new ProxyPingEvent(new GeyserPendingConnection(inetSocketAddress), getPingInfo(), (event, throwable) -> {
if (throwable != null) future.completeExceptionally(throwable);
else future.complete(event);
}));
@ -89,7 +87,12 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
private static class GeyserPendingConnection implements PendingConnection {
private static final UUID FAKE_UUID = UUID.nameUUIDFromBytes("geyser!internal".getBytes());
private static final InetSocketAddress FAKE_REMOTE = new InetSocketAddress(Inet4Address.getLoopbackAddress(), 69);
private final InetSocketAddress remote;
public GeyserPendingConnection(InetSocketAddress remote) {
this.remote = remote;
}
@Override
public String getName() {
@ -143,7 +146,7 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
@Override
public InetSocketAddress getAddress() {
return FAKE_REMOTE;
return remote;
}
@Override

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

@ -25,17 +25,20 @@
package org.geysermc.platform.bungeecord.command;
import lombok.AllArgsConstructor;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.utils.LanguageUtils;
@AllArgsConstructor
public class BungeeCommandSender implements CommandSender {
private net.md_5.bungee.api.CommandSender handle;
private final net.md_5.bungee.api.CommandSender handle;
public BungeeCommandSender(net.md_5.bungee.api.CommandSender handle) {
this.handle = handle;
// Ensure even Java players' languages are loaded
LanguageUtils.loadGeyserLocale(getLocale());
}
@Override
public String getName() {
@ -51,4 +54,14 @@ public class BungeeCommandSender implements CommandSender {
public boolean isConsole() {
return !(handle instanceof ProxiedPlayer);
}
@Override
public String getLocale() {
if (handle instanceof ProxiedPlayer) {
ProxiedPlayer player = (ProxiedPlayer) handle;
String locale = player.getLocale().getLanguage() + "_" + player.getLocale().getCountry();
return LanguageUtils.formatLocale(locale);
}
return LanguageUtils.getDefaultLocale();
}
}

View File

@ -27,13 +27,10 @@ package org.geysermc.platform.bungeecord.command;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.LanguageUtils;
import java.util.ArrayList;
@ -41,7 +38,7 @@ import java.util.Arrays;
public class GeyserBungeeCommandExecutor extends Command implements TabExecutor {
private GeyserConnector connector;
private final GeyserConnector connector;
public GeyserBungeeCommandExecutor(GeyserConnector connector) {
super("geyser");
@ -54,14 +51,10 @@ public class GeyserBungeeCommandExecutor extends Command implements TabExecutor
if (args.length > 0) {
if (getCommand(args[0]) != null) {
if (!sender.hasPermission(getCommand(args[0]).getPermission())) {
String message = "";
if (sender instanceof GeyserSession) {
message = LanguageUtils.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", ((GeyserSession) sender).getClientData().getLanguageCode());
} else {
message = LanguageUtils.getLocaleStringLog("geyser.bootstrap.command.permission_fail");
}
BungeeCommandSender commandSender = new BungeeCommandSender(sender);
String message = LanguageUtils.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", commandSender.getLocale());
sender.sendMessage(TextComponent.fromLegacyText(ChatColor.RED + message));
commandSender.sendMessage(ChatColor.RED + message);
return;
}
getCommand(args[0]).execute(new BungeeCommandSender(sender), args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]);
@ -74,7 +67,7 @@ public class GeyserBungeeCommandExecutor extends Command implements TabExecutor
@Override
public Iterable<String> onTabComplete(CommandSender sender, String[] args) {
if (args.length == 1) {
return Arrays.asList("?", "help", "reload", "shutdown", "stop");
return connector.getCommandManager().getCommandNames();
}
return new ArrayList<>();
}

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,9 +26,14 @@
<dependency>
<groupId>us.myles</groupId>
<artifactId>viaversion</artifactId>
<version>3.1.1</version>
<version>3.2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.geysermc.adapters</groupId>
<artifactId>spigot-all</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<finalName>${outputName}-Spigot</finalName>

View File

@ -35,6 +35,7 @@ import org.geysermc.connector.common.ping.GeyserPingInfo;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.Iterator;
@ -44,9 +45,9 @@ public class GeyserSpigotPingPassthrough implements IGeyserPingPassthrough {
private final GeyserSpigotLogger logger;
@Override
public GeyserPingInfo getPingInformation() {
public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
try {
ServerListPingEvent event = new GeyserPingEvent(InetAddress.getLocalHost(), Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(), Bukkit.getMaxPlayers());
ServerListPingEvent event = new GeyserPingEvent(inetSocketAddress.getAddress(), Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(), Bukkit.getMaxPlayers());
Bukkit.getPluginManager().callEvent(event);
GeyserPingInfo geyserPingInfo = new GeyserPingInfo(event.getMotd(),
new GeyserPingInfo.Players(event.getMaxPlayers(), event.getNumPlayers()),

View File

@ -25,12 +25,14 @@
package org.geysermc.platform.spigot;
import com.github.steveice10.mc.protocol.MinecraftConstants;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.adapters.spigot.SpigotAdapters;
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;
@ -40,18 +42,25 @@ import org.geysermc.connector.utils.FileUtils;
import org.geysermc.connector.utils.LanguageUtils;
import org.geysermc.platform.spigot.command.GeyserSpigotCommandExecutor;
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 org.geysermc.platform.spigot.world.manager.*;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.MappingData;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
private GeyserSpigotCommandManager geyserCommandManager;
private GeyserSpigotConfiguration geyserConfig;
private GeyserSpigotLogger geyserLogger;
@ -120,6 +129,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)
@ -130,8 +146,51 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
geyserLogger.debug("Legacy version of Minecraft (1.15.2 or older) detected; not using 3D biomes.");
}
this.geyserWorldManager = new GeyserSpigotWorldManager(isLegacy, use3dBiomes, isViaVersion);
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(connector, isLegacy, isViaVersion);
// Set if we need to use a different method for getting a player's locale
SpigotCommandSender.setUseLegacyLocaleMethod(!isCompatible(Bukkit.getServer().getVersion(), "1.12.0"));
if (connector.getConfig().isUseAdapters()) {
try {
String name = Bukkit.getServer().getClass().getPackage().getName();
String nmsVersion = name.substring(name.lastIndexOf('.') + 1);
SpigotAdapters.registerWorldAdapter(nmsVersion);
if (isViaVersion && isViaVersionNeeded()) {
if (isLegacy) {
// Pre-1.13
this.geyserWorldManager = new GeyserSpigot1_12NativeWorldManager();
} else {
// Post-1.13
this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this, use3dBiomes);
}
} else {
// No ViaVersion
this.geyserWorldManager = new GeyserSpigotNativeWorldManager(use3dBiomes);
}
geyserLogger.debug("Using NMS adapter: " + this.geyserWorldManager.getClass() + ", " + nmsVersion);
} catch (Exception e) {
if (geyserConfig.isDebugMode()) {
geyserLogger.debug("Error while attempting to find NMS adapter. Most likely, this can be safely ignored. :)");
e.printStackTrace();
}
}
} else {
geyserLogger.debug("Not using NMS adapter as it is disabled in the config.");
}
if (this.geyserWorldManager == null) {
// No NMS adapter
if (isLegacy && isViaVersion) {
// Use ViaVersion for converting pre-1.13 block states
this.geyserWorldManager = new GeyserSpigot1_12WorldManager();
} else if (isLegacy) {
// Not sure how this happens - without ViaVersion, we don't know any block states, so just assume everything is air
this.geyserWorldManager = new GeyserSpigotFallbackWorldManager();
} else {
// Post-1.13
this.geyserWorldManager = new GeyserSpigotWorldManager(use3dBiomes);
}
geyserLogger.debug("Using default world manager: " + this.geyserWorldManager.getClass());
}
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(connector, this.geyserWorldManager);
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
@ -140,8 +199,9 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
@Override
public void onDisable() {
if (connector != null)
if (connector != null) {
connector.shutdown();
}
}
@Override
@ -174,6 +234,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
return getDataFolder().toPath();
}
@Override
public BootstrapDumpInfo getDumpInfo() {
return new GeyserSpigotDumpInfo();
}
public boolean isCompatible(String version, String whichVersion) {
int[] currentVersion = parseVersion(version);
int[] otherVersion = parseVersion(whichVersion);
@ -201,15 +266,43 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
String t = stringArray[index].replaceAll("\\D", "");
try {
temp[index] = Integer.parseInt(t);
} catch(NumberFormatException ex) {
} catch (NumberFormatException ex) {
temp[index] = 0;
}
}
return temp;
}
@Override
public BootstrapDumpInfo getDumpInfo() {
return new GeyserSpigotDumpInfo();
/**
* @return the server version before ViaVersion finishes initializing
*/
public ProtocolVersion getServerProtocolVersion() {
String bukkitVersion = Bukkit.getServer().getVersion();
// Turn "(MC: 1.16.4)" into 1.16.4.
String version = bukkitVersion.split("\\(MC: ")[1].split("\\)")[0];
return ProtocolVersion.getClosest(version);
}
/**
* This function should not run unless ViaVersion is installed on the server.
*
* @return true if there is any block mappings difference between the server and client.
*/
private boolean isViaVersionNeeded() {
ProtocolVersion serverVersion = getServerProtocolVersion();
List<Pair<Integer, Protocol>> protocolList = ProtocolRegistry.getProtocolPath(MinecraftConstants.PROTOCOL_VERSION,
serverVersion.getVersion());
if (protocolList == null) {
// No translation needed!
return false;
}
for (int i = protocolList.size() - 1; i >= 0; i--) {
MappingData mappingData = protocolList.get(i).getValue().getMappingData();
if (mappingData != null) {
return true;
}
}
// All mapping data is null, which means client and server block states are the same
return false;
}
}

View File

@ -32,7 +32,6 @@ import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.LanguageUtils;
import java.util.ArrayList;
@ -42,21 +41,17 @@ import java.util.List;
@AllArgsConstructor
public class GeyserSpigotCommandExecutor implements TabExecutor {
private GeyserConnector connector;
private final GeyserConnector connector;
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length > 0) {
if (getCommand(args[0]) != null) {
if (!sender.hasPermission(getCommand(args[0]).getPermission())) {
String message = "";
if (sender instanceof GeyserSession) {
message = LanguageUtils.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", ((GeyserSession) sender).getClientData().getLanguageCode());
} else {
message = LanguageUtils.getLocaleStringLog("geyser.bootstrap.command.permission_fail");
}
SpigotCommandSender commandSender = new SpigotCommandSender(sender);
String message = LanguageUtils.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", commandSender.getLocale());;
sender.sendMessage(ChatColor.RED + message);
commandSender.sendMessage(ChatColor.RED + message);
return true;
}
getCommand(args[0]).execute(new SpigotCommandSender(sender), args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]);
@ -72,7 +67,7 @@ public class GeyserSpigotCommandExecutor implements TabExecutor {
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
if (args.length == 1) {
return Arrays.asList("?", "help", "reload", "shutdown", "stop");
return connector.getCommandManager().getCommandNames();
}
return new ArrayList<>();
}

View File

@ -25,15 +25,33 @@
package org.geysermc.platform.spigot.command;
import lombok.AllArgsConstructor;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.utils.LanguageUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@AllArgsConstructor
public class SpigotCommandSender implements CommandSender {
private org.bukkit.command.CommandSender handle;
/**
* Whether to use {@code Player.getLocale()} or {@code Player.spigot().getLocale()}, depending on version.
* 1.12 or greater should not use the legacy method.
*/
private static boolean USE_LEGACY_METHOD = false;
private static Method LOCALE_METHOD;
private final org.bukkit.command.CommandSender handle;
private final String locale;
public SpigotCommandSender(org.bukkit.command.CommandSender handle) {
this.handle = handle;
this.locale = getSpigotLocale();
// Ensure even Java players' languages are loaded
LanguageUtils.loadGeyserLocale(locale);
}
@Override
public String getName() {
@ -49,4 +67,49 @@ public class SpigotCommandSender implements CommandSender {
public boolean isConsole() {
return handle instanceof ConsoleCommandSender;
}
@Override
public String getLocale() {
return locale;
}
/**
* Set if we are on pre-1.12, and therefore {@code player.getLocale()} doesn't exist and we have to get
* {@code player.spigot().getLocale()}.
*
* @param useLegacyMethod if we are running pre-1.12 and therefore need to use reflection to get the player locale
*/
public static void setUseLegacyLocaleMethod(boolean useLegacyMethod) {
USE_LEGACY_METHOD = useLegacyMethod;
if (USE_LEGACY_METHOD) {
try {
//noinspection JavaReflectionMemberAccess - of course it doesn't exist; that's why we're doing it
LOCALE_METHOD = Player.Spigot.class.getMethod("getLocale");
} catch (NoSuchMethodException e) {
GeyserConnector.getInstance().getLogger().debug("Player.Spigot.getLocale() doesn't exist? Not a big deal but if you're seeing this please report it to the developers!");
}
}
}
/**
* So we only have to do nasty reflection stuff once per command
*
* @return the locale of the Spigot player
*/
private String getSpigotLocale() {
if (handle instanceof Player) {
Player player = (Player) handle;
if (USE_LEGACY_METHOD) {
try {
// sigh
// This was the only option on older Spigot instances and now it's gone
return (String) LOCALE_METHOD.invoke(player.spigot());
} catch (IllegalAccessException | InvocationTargetException ignored) {
}
} else {
return player.getLocale();
}
}
return LanguageUtils.getDefaultLocale();
}
}

View File

@ -36,13 +36,13 @@ import org.bukkit.event.block.BlockPlaceEvent;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.platform.spigot.world.manager.GeyserSpigotWorldManager;
@AllArgsConstructor
public class GeyserSpigotBlockPlaceListener implements Listener {
private final GeyserConnector connector;
private final boolean isLegacy;
private final boolean isViaVersion;
private final GeyserSpigotWorldManager worldManager;
@EventHandler
public void place(final BlockPlaceEvent event) {
@ -52,14 +52,13 @@ public class GeyserSpigotBlockPlaceListener implements Listener {
placeBlockSoundPacket.setSound(SoundEvent.PLACE);
placeBlockSoundPacket.setPosition(Vector3f.from(event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ()));
placeBlockSoundPacket.setBabySound(false);
String javaBlockId;
if (isLegacy) {
javaBlockId = BlockTranslator.getJavaIdBlockMap().inverse().get(GeyserSpigotWorldManager.getLegacyBlock(session,
event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ(), isViaVersion));
if (worldManager.isLegacy()) {
placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(worldManager.getBlockAt(session,
event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ())));
} else {
javaBlockId = event.getBlockPlaced().getBlockData().getAsString();
String javaBlockId = event.getBlockPlaced().getBlockData().getAsString();
placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().getOrDefault(javaBlockId, BlockTranslator.JAVA_AIR_ID)));
}
placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().getOrDefault(javaBlockId, 0)));
placeBlockSoundPacket.setIdentifier(":");
session.sendUpstreamPacket(placeBlockSoundPacket);
session.setLastBlockPlacePosition(null);

View File

@ -0,0 +1,59 @@
/*
* 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.platform.spigot.world.manager;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.geysermc.adapters.spigot.SpigotAdapters;
import org.geysermc.adapters.spigot.SpigotWorldAdapter;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
/**
* Used with ViaVersion and pre-1.13.
*/
public class GeyserSpigot1_12NativeWorldManager extends GeyserSpigot1_12WorldManager {
private final SpigotWorldAdapter adapter;
public GeyserSpigot1_12NativeWorldManager() {
this.adapter = SpigotAdapters.getWorldAdapter();
// Unlike post-1.13, we can't build up a cache of block states, because block entities need some special conversion
}
@Override
public int getBlockAt(GeyserSession session, int x, int y, int z) {
Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
if (player == null) {
return BlockTranslator.JAVA_AIR_ID;
}
// Get block entity storage
BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class);
int blockId = adapter.getBlockAt(player.getWorld(), x, y, z);
return getLegacyBlock(storage, blockId, x, y, z);
}
}

View File

@ -0,0 +1,141 @@
/*
* 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.platform.spigot.world.manager;
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
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.util.List;
/**
* Should be used when ViaVersion is present, no NMS adapter is being used, and we are pre-1.13.
*
* You need ViaVersion to connect to an older server with the Geyser-Spigot plugin.
*/
public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager {
/**
* Specific mapping data for 1.12 to 1.13. Used to convert the 1.12 block into the 1.13 block state.
* (Block IDs did not change between server versions until 1.13 and after)
*/
private final MappingData mappingData1_12to1_13;
/**
* The list of all protocols from the client's version to 1.13.
*/
private final List<Pair<Integer, Protocol>> protocolList;
public GeyserSpigot1_12WorldManager() {
super(false);
this.mappingData1_12to1_13 = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData();
this.protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION,
ProtocolVersion.v1_13.getVersion());
}
@Override
@SuppressWarnings("deprecation")
public int getBlockAt(GeyserSession session, int x, int y, int z) {
Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
if (player == null) {
return BlockTranslator.JAVA_AIR_ID;
}
// Get block entity storage
BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class);
Block block = player.getWorld().getBlockAt(x, y, z);
// Black magic that gets the old block state ID
int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF);
return getLegacyBlock(storage, blockId, x, y, z);
}
/**
*
* @param storage ViaVersion's block entity storage (used to fix block entity state differences)
* @param blockId the pre-1.13 block id
* @param x X coordinate of block
* @param y Y coordinate of block
* @param z Z coordinate of block
* @return the block state updated to the latest Minecraft version
*/
@SuppressWarnings("deprecation")
public int getLegacyBlock(BlockStorage storage, int blockId, int x, int y, int z) {
// Convert block state from old version (1.12.2) -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2
blockId = mappingData1_12to1_13.getNewBlockId(blockId);
// 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;
}
@SuppressWarnings("deprecation")
@Override
public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
if (player == null) {
return;
}
World world = player.getWorld();
// Get block entity storage
BlockStorage storage = Via.getManager().getConnection(player.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++) {
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);
chunk.set(blockX, blockY, blockZ, getLegacyBlock(storage, blockId, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ));
}
}
}
}
@Override
public boolean isLegacy() {
return true;
}
}

View File

@ -0,0 +1,62 @@
/*
* 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.platform.spigot.world.manager;
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
/**
* Should only be used when we know {@link GeyserSpigotWorldManager#getBlockAt(GeyserSession, int, int, int)}
* cannot be accurate. Typically, this is when ViaVersion is not installed but a client still manages to connect.
* If this occurs to you somehow, please let us know!!
*/
public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager {
public GeyserSpigotFallbackWorldManager() {
// Since this is pre-1.13 (and thus pre-1.15), there will never be 3D biomes.
super(false);
}
@Override
public int getBlockAt(GeyserSession session, int x, int y, int z) {
return BlockTranslator.JAVA_AIR_ID;
}
@Override
public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
// Do nothing, since we can't do anything with the chunk
}
@Override
public boolean hasMoreBlockDataThanChunkCache() {
return false;
}
@Override
public boolean isLegacy() {
return true;
}
}

View File

@ -0,0 +1,79 @@
/*
* 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.platform.spigot.world.manager;
import com.github.steveice10.mc.protocol.MinecraftConstants;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntList;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.platform.spigot.GeyserSpigotPlugin;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.data.MappingData;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import java.util.List;
/**
* Used when block IDs need to be translated to the latest version
*/
public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorldManager {
private final Int2IntMap oldToNewBlockId;
public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin, boolean use3dBiomes) {
super(use3dBiomes);
IntList allBlockStates = adapter.getAllBlockStates();
oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size());
ProtocolVersion serverVersion = plugin.getServerProtocolVersion();
List<Pair<Integer, Protocol>> protocolList = ProtocolRegistry.getProtocolPath(MinecraftConstants.PROTOCOL_VERSION,
serverVersion.getVersion());
for (int oldBlockId : allBlockStates) {
int newBlockId = oldBlockId;
// protocolList should *not* be null; we checked for that before initializing this class
for (int i = protocolList.size() - 1; i >= 0; i--) {
MappingData mappingData = protocolList.get(i).getValue().getMappingData();
if (mappingData != null) {
newBlockId = mappingData.getNewBlockStateId(newBlockId);
}
}
oldToNewBlockId.put(oldBlockId, newBlockId);
}
}
@Override
public int getBlockAt(GeyserSession session, int x, int y, int z) {
int nativeBlockId = super.getBlockAt(session, x, y, z);
return oldToNewBlockId.getOrDefault(nativeBlockId, nativeBlockId);
}
@Override
public boolean isLegacy() {
return true;
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.platform.spigot.world.manager;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.geysermc.adapters.spigot.SpigotAdapters;
import org.geysermc.adapters.spigot.SpigotWorldAdapter;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
protected final SpigotWorldAdapter adapter;
public GeyserSpigotNativeWorldManager(boolean use3dBiomes) {
super(use3dBiomes);
adapter = SpigotAdapters.getWorldAdapter();
}
@Override
public int getBlockAt(GeyserSession session, int x, int y, int z) {
Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
if (player == null) {
return BlockTranslator.JAVA_AIR_ID;
}
return adapter.getBlockAt(player.getWorld(), x, y, z);
}
}

View File

@ -23,9 +23,10 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.platform.spigot.world;
package org.geysermc.platform.spigot.world.manager;
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;
@ -33,6 +34,7 @@ import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
@ -41,20 +43,22 @@ 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 java.io.InputStream;
/**
* The base world manager to use when there is no supported NMS revision
*/
public class GeyserSpigotWorldManager extends GeyserWorldManager {
private final boolean isLegacy;
private final boolean use3dBiomes;
/**
* You need ViaVersion to connect to an older server with Geyser.
* However, we still check for ViaVersion in case there's some other way that gets Geyser on a pre-1.13 Bukkit server
* The current client protocol version for ViaVersion usage.
*/
private final boolean isViaVersion;
protected static final int CLIENT_PROTOCOL_VERSION = MinecraftConstants.PROTOCOL_VERSION;
/**
* Whether the server is pre-1.16 and therefore does not support 3D biomes on an API level guaranteed.
*/
private final boolean use3dBiomes;
/**
* Stores a list of {@link Biome} ordinal numbers to Minecraft biome numeric IDs.
*
@ -68,10 +72,8 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
*/
private final Int2IntMap biomeToIdMap = new Int2IntOpenHashMap(Biome.values().length);
public GeyserSpigotWorldManager(boolean isLegacy, boolean use3dBiomes, boolean isViaVersion) {
this.isLegacy = isLegacy;
public GeyserSpigotWorldManager(boolean use3dBiomes) {
this.use3dBiomes = use3dBiomes;
this.isViaVersion = isViaVersion;
// Load the values into the biome-to-ID map
InputStream biomeStream = FileUtils.getResource("biomes.json");
@ -83,8 +85,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");
@ -96,71 +99,26 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
@Override
public int getBlockAt(GeyserSession session, int x, int y, int z) {
Player bukkitPlayer;
if ((this.isLegacy && !this.isViaVersion)
|| session.getPlayerEntity() == null
|| (bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
return BlockTranslator.AIR;
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
return BlockTranslator.JAVA_AIR_ID;
}
World world = bukkitPlayer.getWorld();
if (isLegacy) {
return getLegacyBlock(session, x, y, z, true);
}
//TODO possibly: detect server version for all versions and use ViaVersion for block state mappings like below
return BlockTranslator.getJavaIdBlockMap().getOrDefault(world.getBlockAt(x, y, z).getBlockData().getAsString(), 0);
}
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);
} else {
return BlockTranslator.AIR;
}
}
@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;
}
return BlockTranslator.getJavaIdBlockMap().getOrDefault(world.getBlockAt(x, y, z).getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID);
}
@Override
public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
Player bukkitPlayer;
if ((this.isLegacy && !this.isViaVersion)
|| session.getPlayerEntity() == null
|| (bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
return;
}
World world = bukkitPlayer.getWorld();
if (this.isLegacy) {
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));
}
}
}
} else {
//TODO: see above TODO in getBlockAt
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++) {
Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ);
int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), 0);
chunk.set(blockX, blockY, blockZ, id);
}
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++) {
Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ);
int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID);
chunk.set(blockX, blockY, blockZ, id);
}
}
}
@ -222,4 +180,16 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
public boolean hasPermission(GeyserSession session, String permission) {
return Bukkit.getPlayer(session.getPlayerEntity().getUsername()).hasPermission(permission);
}
/**
* This must be set to true if we are pre-1.13, and {@link BlockData#getAsString() does not exist}.
*
* This should be set to true if we are post-1.13 but before the latest version, and we should convert the old block state id
* to the current one.
*
* @return whether there is a difference between client block state and server block state that requires extra processing
*/
public boolean isLegacy() {
return false;
}
}

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

@ -38,31 +38,29 @@ import org.spongepowered.api.network.status.StatusClient;
import org.spongepowered.api.profile.GameProfile;
import java.lang.reflect.Method;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.util.Optional;
public class GeyserSpongePingPassthrough implements IGeyserPingPassthrough {
private static final GeyserStatusClient STATUS_CLIENT = new GeyserStatusClient();
private static final Cause CAUSE = Cause.of(EventContext.empty(), Sponge.getServer());
private static Method SpongeStatusResponse_create;
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public GeyserPingInfo getPingInformation() {
public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
// come on Sponge, this is in commons, why not expose it :(
ClientPingServerEvent event;
try {
if(SpongeStatusResponse_create == null) {
if (SpongeStatusResponse_create == null) {
Class SpongeStatusResponse = Class.forName("org.spongepowered.common.network.status.SpongeStatusResponse");
Class MinecraftServer = Class.forName("net.minecraft.server.MinecraftServer");
SpongeStatusResponse_create = SpongeStatusResponse.getDeclaredMethod("create", MinecraftServer);
}
Object response = SpongeStatusResponse_create.invoke(null, Sponge.getServer());
event = SpongeEventFactory.createClientPingServerEvent(CAUSE, STATUS_CLIENT, (ClientPingServerEvent.Response) response);
event = SpongeEventFactory.createClientPingServerEvent(CAUSE, new GeyserStatusClient(inetSocketAddress), (ClientPingServerEvent.Response) response);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
@ -76,7 +74,7 @@ public class GeyserSpongePingPassthrough implements IGeyserPingPassthrough {
new GeyserPingInfo.Version(
event.getResponse().getVersion().getName(),
MinecraftConstants.PROTOCOL_VERSION) // thanks for also not exposing this sponge
);
);
event.getResponse().getPlayers().get().getProfiles().stream()
.map(GameProfile::getName)
.map(op -> op.orElseThrow(IllegalStateException::new))
@ -87,11 +85,15 @@ public class GeyserSpongePingPassthrough implements IGeyserPingPassthrough {
@SuppressWarnings("NullableProblems")
private static class GeyserStatusClient implements StatusClient {
private static final InetSocketAddress FAKE_REMOTE = new InetSocketAddress(Inet4Address.getLoopbackAddress(), 69);
private final InetSocketAddress remote;
public GeyserStatusClient(InetSocketAddress remote) {
this.remote = remote;
}
@Override
public InetSocketAddress getAddress() {
return FAKE_REMOTE;
return this.remote;
}
@Override

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

@ -70,7 +70,7 @@ public class GeyserSpongeCommandExecutor implements CommandCallable {
@Override
public List<String> getSuggestions(CommandSource source, String arguments, @Nullable Location<World> targetPosition) throws CommandException {
if (arguments.split(" ").length == 1) {
return Arrays.asList("?", "help", "reload", "shutdown", "stop");
return connector.getCommandManager().getCommandNames();
}
return new ArrayList<>();
}

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,13 +195,21 @@ 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");
}
} catch (IOException ex) {
geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.config.failed"), ex);
System.exit(0);
if (gui == null) {
System.exit(1);
} else {
// Leave the process running so the GUI is still visible
return;
}
}
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
@ -223,4 +278,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

@ -261,14 +261,30 @@ public class GeyserStandaloneGUI {
for (Map.Entry<String, GeyserCommand> command : geyserCommandManager.getCommands().entrySet()) {
// Remove the offhand command and any alias commands to prevent duplicates in the list
if ("offhand".equals(command.getValue().getName()) || command.getValue().getAliases().contains(command.getKey())) {
if (!command.getValue().isExecutableOnConsole() || command.getValue().getAliases().contains(command.getKey())) {
continue;
}
// Create the button that runs the command
JMenuItem commandButton = new JMenuItem(command.getValue().getName());
boolean hasSubCommands = command.getValue().hasSubCommands();
// Add an extra menu if there are more commands that can be run
JMenuItem commandButton = hasSubCommands ? new JMenu(command.getValue().getName()) : new JMenuItem(command.getValue().getName());
commandButton.getAccessibleContext().setAccessibleDescription(command.getValue().getDescription());
commandButton.addActionListener(e -> command.getValue().execute(geyserStandaloneLogger, new String[]{ }));
if (!hasSubCommands) {
commandButton.addActionListener(e -> command.getValue().execute(geyserStandaloneLogger, new String[]{ }));
} else {
// Add a submenu that's the same name as the menu can't be pressed
JMenuItem otherCommandButton = new JMenuItem(command.getValue().getName());
otherCommandButton.getAccessibleContext().setAccessibleDescription(command.getValue().getDescription());
otherCommandButton.addActionListener(e -> command.getValue().execute(geyserStandaloneLogger, new String[]{ }));
commandButton.add(otherCommandButton);
// Add a menu option for all possible subcommands
for (String subCommandName : command.getValue().getSubCommands()) {
JMenuItem item = new JMenuItem(subCommandName);
item.addActionListener(e -> command.getValue().execute(geyserStandaloneLogger, new String[]{subCommandName}));
commandButton.add(item);
}
}
commandsMenu.add(commandButton);
}
@ -291,7 +307,7 @@ public class GeyserStandaloneGUI {
playerTableModel.getDataVector().removeAllElements();
for (GeyserSession player : GeyserConnector.getInstance().getPlayers()) {
Vector row = new Vector();
Vector<String> row = new Vector<>();
row.add(player.getSocketAddress().getHostName());
row.add(player.getPlayerEntity().getUsername());

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>
@ -93,7 +93,16 @@
<artifactSet>
<excludes>
<exclude>com.google.code.gson:*</exclude>
<exclude>io.netty:*</exclude>
<!-- Needed because Velocity provides every dependency except netty-resolver-dns -->
<exclude>io.netty:netty-transport-native-epoll:*</exclude>
<exclude>io.netty:netty-transport-native-unix-common:*</exclude>
<exclude>io.netty:netty-transport-native-kqueue:*</exclude>
<exclude>io.netty:netty-handler:*</exclude>
<exclude>io.netty:netty-common:*</exclude>
<exclude>io.netty:netty-buffer:*</exclude>
<exclude>io.netty:netty-resolver:*</exclude>
<exclude>io.netty:netty-transport:*</exclude>
<exclude>io.netty:netty-codec:*</exclude>
<exclude>org.slf4j:*</exclude>
<exclude>org.ow2.asm:*</exclude>
</excludes>

View File

@ -43,15 +43,13 @@ import java.util.concurrent.ExecutionException;
@AllArgsConstructor
public class GeyserVelocityPingPassthrough implements IGeyserPingPassthrough {
private static final GeyserInboundConnection FAKE_INBOUND_CONNECTION = new GeyserInboundConnection();
private final ProxyServer server;
@Override
public GeyserPingInfo getPingInformation() {
public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
ProxyPingEvent event;
try {
event = server.getEventManager().fire(new ProxyPingEvent(FAKE_INBOUND_CONNECTION, ServerPing.builder()
event = server.getEventManager().fire(new ProxyPingEvent(new GeyserInboundConnection(inetSocketAddress), ServerPing.builder()
.description(server.getConfiguration().getMotdComponent()).onlinePlayers(server.getPlayerCount())
.maximumPlayers(server.getConfiguration().getShowMaxPlayers()).build())).get();
} catch (ExecutionException | InterruptedException e) {
@ -74,11 +72,15 @@ public class GeyserVelocityPingPassthrough implements IGeyserPingPassthrough {
private static class GeyserInboundConnection implements InboundConnection {
private static final InetSocketAddress FAKE_REMOTE = new InetSocketAddress(Inet4Address.getLoopbackAddress(), 69);
private final InetSocketAddress remote;
public GeyserInboundConnection(InetSocketAddress remote) {
this.remote = remote;
}
@Override
public InetSocketAddress getRemoteAddress() {
return FAKE_REMOTE;
return this.remote;
}
@Override

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

@ -25,41 +25,48 @@
package org.geysermc.platform.velocity.command;
import com.velocitypowered.api.command.Command;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand;
import lombok.AllArgsConstructor;
import net.kyori.text.TextComponent;
import org.geysermc.connector.common.ChatColor;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.common.ChatColor;
import org.geysermc.connector.utils.LanguageUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@AllArgsConstructor
public class GeyserVelocityCommandExecutor implements Command {
public class GeyserVelocityCommandExecutor implements SimpleCommand {
private GeyserConnector connector;
private final GeyserConnector connector;
@Override
public void execute(CommandSource source, String[] args) {
if (args.length > 0) {
if (getCommand(args[0]) != null) {
if (!source.hasPermission(getCommand(args[0]).getPermission())) {
// Not ideal to use log here but we dont get a session
source.sendMessage(TextComponent.of(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.bootstrap.command.permission_fail")));
public void execute(Invocation invocation) {
if (invocation.arguments().length > 0) {
if (getCommand(invocation.arguments()[0]) != null) {
if (!invocation.source().hasPermission(getCommand(invocation.arguments()[0]).getPermission())) {
CommandSender sender = new VelocityCommandSender(invocation.source());
sender.sendMessage(ChatColor.RED + LanguageUtils.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.getLocale()));
return;
}
getCommand(args[0]).execute(new VelocityCommandSender(source), args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]);
getCommand(invocation.arguments()[0]).execute(new VelocityCommandSender(invocation.source()), invocation.arguments().length > 1 ? Arrays.copyOfRange(invocation.arguments(), 1, invocation.arguments().length) : new String[0]);
}
} else {
getCommand("help").execute(new VelocityCommandSender(source), new String[0]);
getCommand("help").execute(new VelocityCommandSender(invocation.source()), new String[0]);
}
}
@Override
public List<String> suggest(Invocation invocation) {
if (invocation.arguments().length == 0) {
return connector.getCommandManager().getCommandNames();
}
return new ArrayList<>();
}
private GeyserCommand getCommand(String label) {
return connector.getCommandManager().getCommands().get(label);
}

View File

@ -28,17 +28,21 @@ package org.geysermc.platform.velocity.command;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.ConsoleCommandSource;
import com.velocitypowered.api.proxy.Player;
import lombok.AllArgsConstructor;
import net.kyori.text.TextComponent;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.utils.LanguageUtils;
import java.util.Locale;
@AllArgsConstructor
public class VelocityCommandSender implements CommandSender {
private CommandSource handle;
private final CommandSource handle;
public VelocityCommandSender(CommandSource handle) {
this.handle = handle;
// Ensure even Java players' languages are loaded
LanguageUtils.loadGeyserLocale(getLocale());
}
@Override
public String getName() {
@ -59,4 +63,13 @@ public class VelocityCommandSender implements CommandSender {
public boolean isConsole() {
return handle instanceof ConsoleCommandSource;
}
@Override
public String getLocale() {
if (handle instanceof Player) {
Locale locale = ((Player) handle).getPlayerSettings().getLocale();
return LanguageUtils.formatLocale(locale.getLanguage() + "_" + locale.getCountry());
}
return LanguageUtils.getDefaultLocale();
}
}

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

@ -92,7 +92,7 @@ public class CustomFormWindow extends FormWindow {
}
public void setResponse(String data) {
if (data == null || data.equalsIgnoreCase("null") || data.isEmpty()) {
if (data == null || data.trim().equalsIgnoreCase("null") || data.isEmpty()) {
closed = true;
return;
}
@ -108,7 +108,7 @@ public class CustomFormWindow extends FormWindow {
List<String> componentResponses = new ArrayList<>();
try {
componentResponses = new ObjectMapper().readValue(data, new TypeReference<List<String>>(){});
componentResponses = new ObjectMapper().readValue(data.trim(), new TypeReference<List<String>>(){});
} catch (IOException e) { }
for (String response : componentResponses) {

View File

@ -72,14 +72,14 @@ public class SimpleFormWindow extends FormWindow {
}
public void setResponse(String data) {
if (data == null || data.equalsIgnoreCase("null")) {
if (data == null || data.trim().equalsIgnoreCase("null")) {
closed = true;
return;
}
int buttonID;
try {
buttonID = Integer.parseInt(data);
buttonID = Integer.parseInt(data.trim());
} catch (Exception ex) {
return;
}

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>
@ -31,17 +30,28 @@
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.CloudburstMC.Protocol</groupId>
<artifactId>bedrock-v408</artifactId>
<version>02f46a8700</version>
<groupId>com.nukkitx.protocol</groupId>
<artifactId>bedrock-v422</artifactId>
<version>2.6.1-SNAPSHOT</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>net.sf.trove4j</groupId>
<artifactId>trove</artifactId>
</exclusion>
<!-- Stay on the older version of Network while it's rewritten -->
<exclusion>
<groupId>com.nukkitx.network</groupId>
<artifactId>raknet</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.nukkitx.network</groupId>
<artifactId>raknet</artifactId>
<version>1.6.20</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.nukkitx.fastutil</groupId>
<artifactId>fastutil-int-int-maps</artifactId>
@ -111,13 +121,17 @@
<dependency>
<groupId>com.github.steveice10</groupId>
<artifactId>mcprotocollib</artifactId>
<version>3a69a0614c</version>
<version>26201a4</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</exclusion>
<exclusion>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-gson</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@ -137,23 +151,35 @@
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>com.github.kyoripowered.adventure</groupId>
<groupId>net.kyori</groupId>
<artifactId>adventure-api</artifactId>
<version>557865caef</version>
<version>4.3.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.kyoripowered.adventure</groupId>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-gson</artifactId>
<version>557865caef</version>
<version>4.3.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.kyoripowered.adventure</groupId>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-legacy</artifactId>
<version>557865caef</version>
<version>4.3.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-gson-legacy-impl</artifactId>
<version>4.3.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@ -210,8 +236,12 @@
</includes>
<replacements>
<replacement>
<token>VERSION = ".*"</token>
<value>VERSION = "${project.version} (git-${git.branch}-${git.commit.id.abbrev})"</value>
<token>String VERSION = ".*"</token>
<value>String VERSION = "${project.version} (" + GIT_VERSION + ")"</value>
</replacement>
<replacement>
<token>String GIT_VERSION = ".*"</token>
<value>String GIT_VERSION = "git-${git.branch}-${git.commit.id.abbrev}"</value>
</replacement>
</replacements>
</configuration>
@ -229,8 +259,12 @@
</includes>
<replacements>
<replacement>
<token>VERSION = ".*"</token>
<value>VERSION = "DEV"</value>
<token>String VERSION = ".*"</token>
<value>String VERSION = "DEV"</value>
</replacement>
<replacement>
<token>String GIT_VERSION = ".*"</token>
<value>String GIT_VERSION = "DEV"</value>
</replacement>
</replacements>
</configuration>
@ -252,6 +286,8 @@
<script><![CDATA[
new org.reflections.Reflections("org.geysermc.connector.network.translators")
.save("${project.artifactId}/target/classes/META-INF/reflections/org.geysermc.connector.network.translators-reflections.xml")
new org.reflections.Reflections("org.geysermc.connector.network.translators.collision.translators")
.save("${project.artifactId}/target/classes/META-INF/reflections/org.geysermc.connector.network.translators.collision.translators-reflections.xml")
new org.reflections.Reflections("org.geysermc.connector.network.translators.item")
.save("${project.artifactId}/target/classes/META-INF/reflections/org.geysermc.connector.network.translators.item-reflections.xml")
new org.reflections.Reflections("org.geysermc.connector.network.translators.sound")
@ -283,6 +319,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;
@ -44,6 +44,7 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.BiomeTranslator;
import org.geysermc.connector.network.translators.EntityIdentifierRegistry;
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
import org.geysermc.connector.network.translators.collision.CollisionTranslator;
import org.geysermc.connector.network.translators.effect.EffectRegistry;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.item.ItemTranslator;
@ -54,6 +55,7 @@ import org.geysermc.connector.network.translators.sound.SoundRegistry;
import org.geysermc.connector.network.translators.world.WorldManager;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
import org.geysermc.connector.network.translators.world.block.entity.SkullBlockEntityTranslator;
import org.geysermc.connector.utils.DimensionUtils;
import org.geysermc.connector.utils.LanguageUtils;
import org.geysermc.connector.utils.LocaleUtils;
@ -65,10 +67,7 @@ import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@ -77,9 +76,14 @@ import java.util.concurrent.TimeUnit;
@Getter
public class GeyserConnector {
public static final ObjectMapper JSON_MAPPER = new ObjectMapper().enable(JsonParser.Feature.IGNORE_UNDEFINED).enable(JsonParser.Feature.ALLOW_COMMENTS).disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
public static final ObjectMapper JSON_MAPPER = new ObjectMapper()
.enable(JsonParser.Feature.IGNORE_UNDEFINED)
.enable(JsonParser.Feature.ALLOW_COMMENTS)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES);
public static final String NAME = "Geyser";
public static final String GIT_VERSION = "DEV"; // A fallback for running in IDEs
public static final String VERSION = "DEV"; // A fallback for running in IDEs
private static final String IP_REGEX = "\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b";
@ -134,6 +138,7 @@ public class GeyserConnector {
EntityIdentifierRegistry.init();
ItemRegistry.init();
ItemTranslator.init();
CollisionTranslator.init();
LocaleUtils.init();
PotionMixRegistry.init();
RecipeRegistry.init();
@ -179,8 +184,8 @@ public class GeyserConnector {
remoteServer = new RemoteServer(config.getRemote().getAddress(), remotePort);
authType = AuthType.getByName(config.getRemote().getAuthType());
if (config.isAboveBedrockNetherBuilding())
DimensionUtils.changeBedrockNetherId(); // Apply End dimension ID workaround to Nether
DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether
SkullBlockEntityTranslator.ALLOW_CUSTOM_SKULLS = config.isAllowCustomSkulls();
// https://github.com/GeyserMC/Geyser/issues/957
RakNetConstants.MAXIMUM_MTU_SIZE = (short) config.getMtu();
@ -199,7 +204,6 @@ public class GeyserConnector {
if (config.getMetrics().isEnabled()) {
metrics = new Metrics(this, "GeyserMC", config.getMetrics().getUniqueId(), false, java.util.logging.Logger.getLogger(""));
metrics.addCustomChart(new Metrics.SingleLineChart("servers", () -> 1));
metrics.addCustomChart(new Metrics.SingleLineChart("players", players::size));
// Prevent unwanted words best we can
metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> AuthType.getByName(config.getRemote().getAuthType()).toString().toLowerCase()));
@ -255,7 +259,7 @@ public class GeyserConnector {
message += LanguageUtils.getLocaleStringLog("geyser.core.finish.console");
}
logger.info(message);
if (platformType == PlatformType.STANDALONE) {
logger.warning(LanguageUtils.getLocaleStringLog("geyser.core.movement_warn"));
}
@ -271,7 +275,7 @@ public class GeyserConnector {
// Make a copy to prevent ConcurrentModificationException
final List<GeyserSession> tmpPlayers = new ArrayList<>(players);
for (GeyserSession playerSession : tmpPlayers) {
playerSession.disconnect(LanguageUtils.getPlayerLocaleString("geyser.core.shutdown.kick.message", playerSession.getClientData().getLanguageCode()));
playerSession.disconnect(LanguageUtils.getPlayerLocaleString("geyser.core.shutdown.kick.message", playerSession.getLocale()));
}
CompletableFuture<Void> future = CompletableFuture.runAsync(new Runnable() {
@ -319,6 +323,38 @@ public class GeyserConnector {
players.remove(player);
}
/**
* Gets a player by their current UUID
*
* @param uuid the uuid
* @return the player or <code>null</code> if there is no player online with this UUID
*/
public GeyserSession getPlayerByUuid(UUID uuid) {
for (GeyserSession session : players) {
if (session.getPlayerEntity().getUuid().equals(uuid)) {
return session;
}
}
return null;
}
/**
* Gets a player by their Xbox user identifier
*
* @param xuid the Xbox user identifier
* @return the player or <code>null</code> if there is no player online with this xuid
*/
public GeyserSession getPlayerByXuid(String xuid) {
for (GeyserSession session : players) {
if (session.getAuthData() != null && session.getAuthData().getXboxUUID().equals(xuid)) {
return session;
}
}
return null;
}
public static GeyserConnector start(PlatformType platformType, GeyserBootstrap bootstrap) {
return new GeyserConnector(platformType, bootstrap);
}

View File

@ -31,27 +31,27 @@ import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.defaults.*;
import org.geysermc.connector.utils.LanguageUtils;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
public abstract class CommandManager {
@Getter
private final Map<String, GeyserCommand> commands = Collections.synchronizedMap(new HashMap<>());
private GeyserConnector connector;
private final GeyserConnector connector;
public CommandManager(GeyserConnector connector) {
this.connector = connector;
registerCommand(new HelpCommand(connector, "help", LanguageUtils.getLocaleStringLog("geyser.commands.help.desc"), "geyser.command.help"));
registerCommand(new ListCommand(connector, "list", LanguageUtils.getLocaleStringLog("geyser.commands.list.desc"), "geyser.command.list"));
registerCommand(new ReloadCommand(connector, "reload", LanguageUtils.getLocaleStringLog("geyser.commands.reload.desc"), "geyser.command.reload"));
registerCommand(new StopCommand(connector, "stop", LanguageUtils.getLocaleStringLog("geyser.commands.stop.desc"), "geyser.command.stop"));
registerCommand(new OffhandCommand(connector, "offhand", LanguageUtils.getLocaleStringLog("geyser.commands.offhand.desc"), "geyser.command.offhand"));
registerCommand(new DumpCommand(connector, "dump", LanguageUtils.getLocaleStringLog("geyser.commands.dump.desc"), "geyser.command.dump"));
registerCommand(new VersionCommand(connector, "version", LanguageUtils.getLocaleStringLog("geyser.commands.version.desc"), "geyser.command.version"));
registerCommand(new HelpCommand(connector, "help", "geyser.commands.help.desc", "geyser.command.help"));
registerCommand(new ListCommand(connector, "list", "geyser.commands.list.desc", "geyser.command.list"));
registerCommand(new ReloadCommand(connector, "reload", "geyser.commands.reload.desc", "geyser.command.reload"));
registerCommand(new StopCommand(connector, "stop", "geyser.commands.stop.desc", "geyser.command.stop"));
registerCommand(new OffhandCommand(connector, "offhand", "geyser.commands.offhand.desc", "geyser.command.offhand"));
registerCommand(new DumpCommand(connector, "dump", "geyser.commands.dump.desc", "geyser.command.dump"));
registerCommand(new VersionCommand(connector, "version", "geyser.commands.version.desc", "geyser.command.version"));
registerCommand(new SettingsCommand(connector, "settings", "geyser.commands.settings.desc", "geyser.command.settings"));
registerCommand(new StatisticsCommand(connector, "statistics", "geyser.commands.statistics.desc", "geyser.command.statistics"));
}
public void registerCommand(GeyserCommand command) {
@ -91,6 +91,13 @@ public abstract class CommandManager {
cmd.execute(sender, args);
}
/**
* @return a list of all subcommands under {@code /geyser}.
*/
public List<String> getCommandNames() {
return Arrays.asList(connector.getCommandManager().getCommands().keySet().toArray(new String[0]));
}
/**
* Returns the description of the given command
*

View File

@ -25,6 +25,12 @@
package org.geysermc.connector.command;
import org.geysermc.connector.utils.LanguageUtils;
/**
* Implemented on top of any class that can send a command.
* For example, it wraps around Spigot's CommandSender class.
*/
public interface CommandSender {
String getName();
@ -37,5 +43,17 @@ public interface CommandSender {
void sendMessage(String message);
/**
* @return true if the specified sender is from the console.
*/
boolean isConsole();
/**
* Returns the locale of the command sender. Defaults to the default locale at {@link LanguageUtils#getDefaultLocale()}.
*
* @return the locale of the command sender.
*/
default String getLocale() {
return LanguageUtils.getDefaultLocale();
}
}

View File

@ -30,6 +30,7 @@ import lombok.RequiredArgsConstructor;
import lombok.Setter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Getter
@ -37,6 +38,9 @@ import java.util.List;
public abstract class GeyserCommand {
protected final String name;
/**
* The description of the command - will attempt to be translated.
*/
protected final String description;
protected final String permission;
@ -44,4 +48,31 @@ public abstract class GeyserCommand {
private List<String> aliases = new ArrayList<>();
public abstract void execute(CommandSender sender, String[] args);
/**
* If false, hides the command from being shown on the Geyser Standalone GUI.
*
* @return true if the command can be run on the server console
*/
public boolean isExecutableOnConsole() {
return true;
}
/**
* Used in the GUI to know what subcommands can be run
*
* @return a list of all possible subcommands, or empty if none.
*/
public List<String> getSubCommands() {
return Collections.emptyList();
}
/**
* Shortcut to {@link #getSubCommands()}{@code .isEmpty()}.
*
* @return true if there are subcommand present for this command.
*/
public boolean hasSubCommands() {
return !getSubCommands().isEmpty();
}
}

View File

@ -27,12 +27,10 @@ package org.geysermc.connector.command.defaults;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import org.geysermc.connector.common.ChatColor;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.common.ChatColor;
import org.geysermc.connector.common.serializer.AsteriskSerializer;
import org.geysermc.connector.dump.DumpInfo;
import org.geysermc.connector.utils.LanguageUtils;
@ -40,6 +38,8 @@ import org.geysermc.connector.utils.WebUtils;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class DumpCommand extends GeyserCommand {
@ -73,7 +73,7 @@ public class DumpCommand extends GeyserCommand {
AsteriskSerializer.showSensitive = showSensitive;
sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.collecting"));
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.commands.dump.collecting", sender.getLocale()));
String dumpData = "";
try {
if (offlineDump) {
@ -82,7 +82,7 @@ public class DumpCommand extends GeyserCommand {
dumpData = MAPPER.writeValueAsString(new DumpInfo());
}
} catch (IOException e) {
sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.collect_error"));
sender.sendMessage(ChatColor.RED + LanguageUtils.getPlayerLocaleString("geyser.commands.dump.collect_error", sender.getLocale()));
connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.dump.collect_error_short"), e);
return;
}
@ -90,21 +90,21 @@ public class DumpCommand extends GeyserCommand {
String uploadedDumpUrl = "";
if (offlineDump) {
sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.writing"));
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.commands.dump.writing", sender.getLocale()));
try {
FileOutputStream outputStream = new FileOutputStream(GeyserConnector.getInstance().getBootstrap().getConfigFolder().resolve("dump.json").toFile());
outputStream.write(dumpData.getBytes());
outputStream.close();
} catch (IOException e) {
sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.write_error"));
sender.sendMessage(ChatColor.RED + LanguageUtils.getPlayerLocaleString("geyser.commands.dump.write_error", sender.getLocale()));
connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.dump.write_error_short"), e);
return;
}
uploadedDumpUrl = "dump.json";
} else {
sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.uploading"));
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.commands.dump.uploading", sender.getLocale()));
String response;
JsonNode responseNode;
@ -112,22 +112,27 @@ public class DumpCommand extends GeyserCommand {
response = WebUtils.post(DUMP_URL + "documents", dumpData);
responseNode = MAPPER.readTree(response);
} catch (IOException e) {
sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error"));
sender.sendMessage(ChatColor.RED + LanguageUtils.getPlayerLocaleString("geyser.commands.dump.upload_error", sender.getLocale()));
connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error_short"), e);
return;
}
if (!responseNode.has("key")) {
sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error_short") + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response));
sender.sendMessage(ChatColor.RED + LanguageUtils.getPlayerLocaleString("geyser.commands.dump.upload_error_short", sender.getLocale()) + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response));
return;
}
uploadedDumpUrl = DUMP_URL + responseNode.get("key").asText();
}
sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.message") + " " + ChatColor.DARK_AQUA + uploadedDumpUrl);
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.commands.dump.message", sender.getLocale()) + " " + ChatColor.DARK_AQUA + uploadedDumpUrl);
if (!sender.isConsole()) {
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.commands.dump.created", sender.getName(), uploadedDumpUrl));
}
}
@Override
public List<String> getSubCommands() {
return Arrays.asList("offline", "full");
}
}

View File

@ -25,11 +25,10 @@
package org.geysermc.connector.command.defaults;
import org.geysermc.connector.common.ChatColor;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.common.ChatColor;
import org.geysermc.connector.utils.LanguageUtils;
import java.util.Collections;
@ -52,17 +51,12 @@ public class HelpCommand extends GeyserCommand {
public void execute(CommandSender sender, String[] args) {
int page = 1;
int maxPage = 1;
String header = "";
if (sender instanceof GeyserSession) {
header = LanguageUtils.getPlayerLocaleString("geyser.commands.help.header", ((GeyserSession) sender).getClientData().getLanguageCode(), page, maxPage);
} else {
header = LanguageUtils.getLocaleStringLog("geyser.commands.help.header", page, maxPage);
}
String header = LanguageUtils.getPlayerLocaleString("geyser.commands.help.header", sender.getLocale(), page, maxPage);
sender.sendMessage(header);
Map<String, GeyserCommand> cmds = connector.getCommandManager().getCommands();
List<String> commands = connector.getCommandManager().getCommands().keySet().stream().sorted().collect(Collectors.toList());
commands.forEach(cmd -> sender.sendMessage(ChatColor.YELLOW + "/geyser " + cmd + ChatColor.WHITE + ": " + cmds.get(cmd).getDescription()));
commands.forEach(cmd -> sender.sendMessage(ChatColor.YELLOW + "/geyser " + cmd + ChatColor.WHITE + ": " +
LanguageUtils.getPlayerLocaleString(cmds.get(cmd).getDescription(), sender.getLocale())));
}
}

View File

@ -35,7 +35,7 @@ import java.util.stream.Collectors;
public class ListCommand extends GeyserCommand {
private GeyserConnector connector;
private final GeyserConnector connector;
public ListCommand(GeyserConnector connector, String name, String description, String permission) {
super(name, description, permission);
@ -46,11 +46,9 @@ public class ListCommand extends GeyserCommand {
@Override
public void execute(CommandSender sender, String[] args) {
String message = "";
if (sender instanceof GeyserSession) {
message = LanguageUtils.getPlayerLocaleString("geyser.commands.list.message", ((GeyserSession) sender).getClientData().getLanguageCode(), connector.getPlayers().size(), connector.getPlayers().stream().map(GeyserSession::getName).collect(Collectors.joining(" ")));
} else {
message = LanguageUtils.getLocaleStringLog("geyser.commands.list.message", connector.getPlayers().size(), connector.getPlayers().stream().map(GeyserSession::getName).collect(Collectors.joining(" ")));
}
message = LanguageUtils.getPlayerLocaleString("geyser.commands.list.message", sender.getLocale(),
connector.getPlayers().size(),
connector.getPlayers().stream().map(GeyserSession::getName).collect(Collectors.joining(" ")));
sender.sendMessage(message);
}

View File

@ -36,7 +36,7 @@ import org.geysermc.connector.network.session.GeyserSession;
public class OffhandCommand extends GeyserCommand {
private GeyserConnector connector;
private final GeyserConnector connector;
public OffhandCommand(GeyserConnector connector, String name, String description, String permission) {
super(name, description, permission);
@ -58,7 +58,7 @@ public class OffhandCommand extends GeyserCommand {
session.sendDownstreamPacket(releaseItemPacket);
return;
}
// Needed for Bukkit - sender is not an instance of GeyserSession
// Needed for Spigot - sender is not an instance of GeyserSession
for (GeyserSession session : connector.getPlayers()) {
if (sender.getName().equals(session.getPlayerEntity().getUsername())) {
ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.SWAP_HANDS, new Position(0,0,0),
@ -68,4 +68,9 @@ public class OffhandCommand extends GeyserCommand {
}
}
}
@Override
public boolean isExecutableOnConsole() {
return false;
}
}

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;
@ -47,17 +47,12 @@ public class ReloadCommand extends GeyserCommand {
return;
}
String message = "";
if (sender instanceof GeyserSession) {
message = LanguageUtils.getPlayerLocaleString("geyser.commands.reload.message", ((GeyserSession) sender).getClientData().getLanguageCode());
} else {
message = LanguageUtils.getLocaleStringLog("geyser.commands.reload.message");
}
String message = LanguageUtils.getPlayerLocaleString("geyser.commands.reload.message", sender.getLocale());
sender.sendMessage(message);
for (GeyserSession session : connector.getPlayers()) {
session.disconnect(LanguageUtils.getPlayerLocaleString("geyser.commands.reload.kick", session.getClientData().getLanguageCode()));
session.disconnect(LanguageUtils.getPlayerLocaleString("geyser.commands.reload.kick", session.getLocale()));
}
connector.reload();
}

View File

@ -0,0 +1,68 @@
/*
* 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.command.defaults;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.SettingsUtils;
public class SettingsCommand extends GeyserCommand {
private final GeyserConnector connector;
public SettingsCommand(GeyserConnector connector, String name, String description, String permission) {
super(name, description, permission);
this.connector = connector;
}
@Override
public void execute(CommandSender sender, String[] args) {
// Make sure the sender is a Bedrock edition client
GeyserSession session = null;
if (sender instanceof GeyserSession) {
session = (GeyserSession) sender;
} else {
// Needed for Spigot - sender is not an instance of GeyserSession
for (GeyserSession otherSession : connector.getPlayers()) {
if (sender.getName().equals(otherSession.getPlayerEntity().getUsername())) {
session = otherSession;
break;
}
}
}
if (session == null) return;
SettingsUtils.buildForm(session);
session.sendForm(session.getSettingsForm(), SettingsUtils.SETTINGS_FORM_ID);
}
@Override
public boolean isExecutableOnConsole() {
return false;
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.command.defaults;
import com.github.steveice10.mc.protocol.data.game.ClientRequest;
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientRequestPacket;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.network.session.GeyserSession;
public class StatisticsCommand extends GeyserCommand {
private final GeyserConnector connector;
public StatisticsCommand(GeyserConnector connector, String name, String description, String permission) {
super(name, description, permission);
this.connector = connector;
}
@Override
public void execute(CommandSender sender, String[] args) {
if (sender.isConsole()) {
return;
}
// Make sure the sender is a Bedrock edition client
GeyserSession session = null;
if (sender instanceof GeyserSession) {
session = (GeyserSession) sender;
} else {
// Needed for Spigot - sender is not an instance of GeyserSession
for (GeyserSession otherSession : connector.getPlayers()) {
if (sender.getName().equals(otherSession.getPlayerEntity().getUsername())) {
session = otherSession;
break;
}
}
}
if (session == null) return;
session.setWaitingForStatistics(true);
ClientRequestPacket clientRequestPacket = new ClientRequestPacket(ClientRequest.STATS);
session.sendDownstreamPacket(clientRequestPacket);
}
@Override
public boolean isExecutableOnConsole() {
return false;
}
}

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

@ -61,12 +61,12 @@ public class VersionCommand extends GeyserCommand {
bedrockVersions = BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion();
}
sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.version.version", GeyserConnector.NAME, GeyserConnector.VERSION, MinecraftConstants.GAME_VERSION, bedrockVersions));
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.commands.version.version", sender.getLocale(), GeyserConnector.NAME, GeyserConnector.VERSION, MinecraftConstants.GAME_VERSION, bedrockVersions));
// Disable update checking in dev mode
//noinspection ConstantConditions - changes in production
if (!GeyserConnector.VERSION.equals("DEV")) {
sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.version.checking"));
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.commands.version.checking", sender.getLocale()));
try {
Properties gitProp = new Properties();
gitProp.load(FileUtils.getResource("git.properties"));
@ -76,16 +76,16 @@ public class VersionCommand extends GeyserCommand {
int latestBuildNum = Integer.parseInt(buildXML.replaceAll("<(\\\\)?(/)?buildNumber>", "").trim());
int buildNum = Integer.parseInt(gitProp.getProperty("git.build.number"));
if (latestBuildNum == buildNum) {
sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.version.no_updates"));
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.commands.version.no_updates", sender.getLocale()));
} else {
sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.version.outdated", (latestBuildNum - buildNum), "http://ci.geysermc.org/"));
sender.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.commands.version.outdated", sender.getLocale(), (latestBuildNum - buildNum), "https://ci.geysermc.org/"));
}
} else {
throw new AssertionError("buildNumber missing");
}
} catch (IOException | AssertionError | NumberFormatException e) {
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.version.failed"), e);
sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.version.failed"));
sender.sendMessage(ChatColor.RED + LanguageUtils.getPlayerLocaleString("geyser.commands.version.failed", sender.getLocale()));
}
}
}

View File

@ -71,6 +71,8 @@ public interface GeyserConfiguration {
boolean isShowCooldown();
boolean isShowCoordinates();
String getDefaultLocale();
Path getFloodgateKeyPath();
@ -81,8 +83,12 @@ public interface GeyserConfiguration {
boolean isForceResourcePacks();
boolean isXboxAchievementsEnabled();
int getCacheImages();
boolean isAllowCustomSkulls();
IMetricsInfo getMetrics();
interface IBedrockConfiguration {
@ -105,7 +111,7 @@ public interface GeyserConfiguration {
String getAddress();
int getPort();
void setAddress(String address);
void setPort(int port);
@ -133,6 +139,8 @@ public interface GeyserConfiguration {
int getMtu();
boolean isUseAdapters();
int getConfigVersion();
static void checkGeyserConfiguration(GeyserConfiguration geyserConfig, GeyserLogger geyserLogger) {

View File

@ -89,6 +89,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
@JsonProperty("show-cooldown")
private boolean showCooldown = true;
@JsonProperty("show-coordinates")
private boolean showCoordinates = true;
@JsonProperty("allow-third-party-ears")
private boolean allowThirdPartyEars = false;
@ -101,12 +104,18 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
@JsonProperty("cache-images")
private int cacheImages = 0;
@JsonProperty("allow-custom-skulls")
private boolean allowCustomSkulls = true;
@JsonProperty("above-bedrock-nether-building")
private boolean aboveBedrockNetherBuilding = false;
@JsonProperty("force-resource-packs")
private boolean forceResourcePacks = true;
@JsonProperty("xbox-achievements-enabled")
private boolean xboxAchievementsEnabled = false;
private MetricsInfo metrics = new MetricsInfo();
@Getter
@ -167,6 +176,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
@JsonProperty("mtu")
private int mtu = 1400;
@JsonProperty("use-adapters")
private boolean useAdapters = true;
@JsonProperty("config-version")
private int configVersion = 0;
}

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

@ -35,6 +35,16 @@ import java.util.concurrent.TimeUnit;
public class BoatEntity extends Entity {
/**
* Required when IS_BUOYANT is sent in order for boats to work in the water. <br>
*
* Taken from BDS 1.16.200, with the modification of <code>simulate_waves</code> since Java doesn't bob the boat up and down
* like Bedrock.
*/
private static final String BUOYANCY_DATA = "{\"apply_gravity\":true,\"base_buoyancy\":1.0,\"big_wave_probability\":0.02999999932944775," +
"\"big_wave_speed\":10.0,\"drag_down_on_buoyancy_removed\":0.0,\"liquid_blocks\":[\"minecraft:water\"," +
"\"minecraft:flowing_water\"],\"simulate_waves\":false}}";
private boolean isPaddlingLeft;
private float paddleTimeLeft;
private boolean isPaddlingRight;
@ -45,6 +55,10 @@ public class BoatEntity extends Entity {
public BoatEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position.add(0d, entityType.getOffset(), 0d), motion, rotation.add(90, 0, 90));
// Required to be able to move on land 1.16.200+ or apply gravity not in the water 1.16.100+
metadata.put(EntityData.IS_BUOYANT, (byte) 1);
metadata.put(EntityData.BUOYANCY_DATA, BUOYANCY_DATA);
}
@Override

View File

@ -26,13 +26,13 @@
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 net.kyori.adventure.text.Component;
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 +51,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((Component) entityMetadata.getValue()));
}
super.updateBedrockMetadata(entityMetadata, session);
}

View File

@ -39,6 +39,8 @@ public class EnderCrystalEntity extends Entity {
public EnderCrystalEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
// Bedrock 1.16.100+ - prevents the entity from appearing on fire itself when fire is underneath it
metadata.getFlags().setFlag(EntityFlag.FIRE_IMMUNE, true);
}
@Override

View File

@ -32,11 +32,9 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
import com.github.steveice10.mc.protocol.data.message.Message;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket;
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.EntityDataMap;
@ -46,16 +44,17 @@ import com.nukkitx.protocol.bedrock.packet.*;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import lombok.Getter;
import lombok.Setter;
import net.kyori.adventure.text.Component;
import org.geysermc.connector.entity.attribute.Attribute;
import org.geysermc.connector.entity.attribute.AttributeType;
import org.geysermc.connector.entity.living.ArmorStandEntity;
import org.geysermc.connector.entity.player.PlayerEntity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.inventory.PlayerInventory;
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;
@ -68,8 +67,6 @@ public class Entity {
protected long entityId;
protected long geyserId;
protected String dimension;
protected Vector3f position;
protected Vector3f motion;
@ -101,7 +98,6 @@ public class Entity {
this.rotation = rotation;
this.valid = false;
this.dimension = "minecraft:overworld";
setPosition(position);
@ -319,11 +315,11 @@ public class Entity {
}
break;
case 2: // custom name
if (entityMetadata.getValue() instanceof Message) {
Message message = (Message) entityMetadata.getValue();
if (entityMetadata.getValue() instanceof Component) {
Component message = (Component) 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.getClientData().getLanguageCode(), true));
metadata.put(EntityData.NAMETAG, MessageTranslator.convertMessage(message, session.getLocale()));
}
break;
case 3: // is custom name visible
@ -341,18 +337,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

@ -36,6 +36,7 @@ import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.nbt.NbtType;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket;
import org.geysermc.connector.entity.player.PlayerEntity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.FireworkColor;

View File

@ -36,7 +36,20 @@ import org.geysermc.connector.network.translators.item.ItemTranslator;
public class ItemEntity extends Entity {
public ItemEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
super(entityId, geyserId, entityType, position.add(0d, entityType.getOffset(), 0d), motion, rotation);
}
@Override
public void setMotion(Vector3f motion) {
if (isOnGround())
motion = Vector3f.from(motion.getX(), 0, motion.getZ());
super.setMotion(motion);
}
@Override
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
super.moveAbsolute(session, position.add(0d, this.entityType.getOffset(), 0d), rotation, isOnGround, teleported);
}
@Override
@ -44,7 +57,7 @@ public class ItemEntity extends Entity {
if (entityMetadata.getId() == 7) {
AddItemEntityPacket itemPacket = new AddItemEntityPacket();
itemPacket.setRuntimeEntityId(geyserId);
itemPacket.setPosition(position);
itemPacket.setPosition(position.add(0d, this.entityType.getOffset(), 0d));
itemPacket.setMotion(motion);
itemPacket.setUniqueEntityId(geyserId);
itemPacket.setFromFishing(false);

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;
@ -74,6 +77,9 @@ public class LivingEntity extends Entity {
getHand().equals(ItemData.AIR) && getOffHand().getId() == ItemRegistry.SHIELD.getBedrockId());
metadata.getFlags().setFlag(EntityFlag.USING_ITEM, (xd & 0x01) == 0x01 && !isUsingShield);
metadata.getFlags().setFlag(EntityFlag.BLOCKING, (xd & 0x01) == 0x01);
// Riptide spin attack
metadata.getFlags().setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, (xd & 0x04) == 0x04);
break;
case 8:
metadata.put(EntityData.HEALTH, entityMetadata.getValue());
@ -84,6 +90,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

@ -26,11 +26,140 @@
package org.geysermc.connector.entity;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
import 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;
/**
* 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);
this.lastPosition = position;
}
@Override
public void spawnEntity(GeyserSession session) {
super.spawnEntity(session);
positionUpdater = session.getConnector().getGeneralThreadPool().scheduleAtFixedRate(() -> {
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);
if (entityType == EntityType.THROWN_ENDERPEARL) {
LevelEventPacket particlePacket = new LevelEventPacket();
particlePacket.setType(LevelEventType.PARTICLE_TELEPORT);
particlePacket.setPosition(position);
session.sendUpstreamPacket(particlePacket);
}
return super.despawnEntity(session);
}
@Override
public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) {
position = lastPosition;
super.moveRelative(session, relX, relY, relZ, rotation, isOnGround);
lastPosition = position;
}
@Override
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
super.moveAbsolute(session, position, rotation, isOnGround, teleported);
lastPosition = position;
}
}

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,11 +49,11 @@ 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);
}
super.moveAbsolute(session, position, rotation, isOnGround, teleported);
super.moveAbsolute(session, position, Vector3f.from(rotation.getX(), rotation.getX(), rotation.getX()), isOnGround, teleported);
}
@Override
@ -93,4 +95,10 @@ public class ArmorStandEntity extends LivingEntity {
}
super.updateBedrockMetadata(entityMetadata, session);
}
@Override
public void spawnEntity(GeyserSession session) {
this.rotation = Vector3f.from(rotation.getX(), rotation.getX(), rotation.getX());
super.spawnEntity(session);
}
}

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,15 +27,23 @@ 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.data.inventory.ContainerType;
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 {
public AbstractHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
// Specifies the size of the entity's inventory. Required to place slots in the entity.
metadata.put(EntityData.CONTAINER_BASE_SIZE, 2);
}
@Override
@ -47,6 +55,33 @@ 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);
}
// Set container type if tamed
metadata.put(EntityData.CONTAINER_TYPE, ((xd & 0x02) == 0x02) ? (byte) ContainerType.HORSE.getId() : (byte) 0);
}
// Needed to control horses

View File

@ -27,6 +27,7 @@ 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;
@ -35,6 +36,8 @@ public class ChestedHorseEntity extends AbstractHorseEntity {
public ChestedHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
metadata.put(EntityData.CONTAINER_BASE_SIZE, 16);
}
@Override

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

@ -38,6 +38,8 @@ public class LlamaEntity extends ChestedHorseEntity {
public LlamaEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
metadata.put(EntityData.CONTAINER_STRENGTH_MODIFIER, 3); // Presumably 3 slots for every 1 strength
}
@Override
@ -56,7 +58,7 @@ public class LlamaEntity extends ChestedHorseEntity {
// The damage value is the dye color that Java sends us
// Always going to be a carpet so we can hardcode 171 in BlockTranslator
// The int then short conversion is required or we get a ClassCastException
equipmentPacket.setChestplate(ItemData.of(BlockTranslator.CARPET, (short)((int) entityMetadata.getValue()), 1));
equipmentPacket.setChestplate(ItemData.of(BlockTranslator.CARPET, (short) ((int) entityMetadata.getValue()), 1));
} else {
equipmentPacket.setChestplate(ItemData.AIR);
}

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

@ -60,6 +60,11 @@ public class WolfEntity extends TameableEntity {
// Relies on EntityData.OWNER_EID being set in TameableEntity.java
if (entityMetadata.getId() == 19 && !metadata.getFlags().getFlag(EntityFlag.ANGRY)) {
metadata.put(EntityData.COLOR, collarColor = (byte) (int) entityMetadata.getValue());
if (!metadata.containsKey(EntityData.OWNER_EID)) {
// If a color is set and there is no owner entity ID, set one.
// Otherwise, the entire wolf is set to that color: https://user-images.githubusercontent.com/9083212/99209989-92691200-2792-11eb-911d-9a315c955be9.png
metadata.put(EntityData.OWNER_EID, session.getPlayerEntity().getGeyserId());
}
}
// Wolf anger (1.16+)

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

@ -32,33 +32,60 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
import lombok.Data;
import org.geysermc.connector.entity.living.InsentientEntity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class EnderDragonEntity extends InsentientEntity {
/**
* The Ender Dragon has multiple hit boxes, which
* are each its own invisible entity
*/
private EnderDragonPartEntity head;
private EnderDragonPartEntity neck;
private EnderDragonPartEntity body;
private EnderDragonPartEntity leftWing;
private EnderDragonPartEntity rightWing;
private EnderDragonPartEntity[] tail;
private EnderDragonPartEntity[] allParts;
/**
* A circular buffer that stores a history of
* y and yaw values.
*/
private final Segment[] segmentHistory = new Segment[19];
private int latestSegment = -1;
private boolean hovering;
private ScheduledFuture<?> partPositionUpdater;
public EnderDragonEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
metadata.getFlags().setFlag(EntityFlag.FIRE_IMMUNE, true);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
// Phase
if (entityMetadata.getId() == 15) {
metadata.getFlags().setFlag(EntityFlag.FIRE_IMMUNE, true);
switch ((int) entityMetadata.getValue()) {
int value = (int) entityMetadata.getValue();
if (value == 5) {
// Performing breath attack
case 5:
EntityEventPacket entityEventPacket = new EntityEventPacket();
entityEventPacket.setType(EntityEventType.DRAGON_FLAMING);
entityEventPacket.setRuntimeEntityId(geyserId);
entityEventPacket.setData(0);
session.sendUpstreamPacket(entityEventPacket);
case 6:
case 7:
metadata.getFlags().setFlag(EntityFlag.SITTING, true);
break;
EntityEventPacket entityEventPacket = new EntityEventPacket();
entityEventPacket.setType(EntityEventType.DRAGON_FLAMING);
entityEventPacket.setRuntimeEntityId(geyserId);
entityEventPacket.setData(0);
session.sendUpstreamPacket(entityEventPacket);
}
metadata.getFlags().setFlag(EntityFlag.SITTING, value == 5 || value == 6 || value == 7);
hovering = value == 10;
}
super.updateBedrockMetadata(entityMetadata, session);
}
@ -81,6 +108,118 @@ public class EnderDragonEntity extends InsentientEntity {
valid = true;
session.sendUpstreamPacket(addEntityPacket);
head = new EnderDragonPartEntity(entityId + 1, session.getEntityCache().getNextEntityId().incrementAndGet(), EntityType.ENDER_DRAGON_PART, position, motion, rotation, 1, 1);
neck = new EnderDragonPartEntity(entityId + 2, session.getEntityCache().getNextEntityId().incrementAndGet(), EntityType.ENDER_DRAGON_PART, position, motion, rotation, 3, 3);
body = new EnderDragonPartEntity(entityId + 3, session.getEntityCache().getNextEntityId().incrementAndGet(), EntityType.ENDER_DRAGON_PART, position, motion, rotation, 5, 3);
leftWing = new EnderDragonPartEntity(entityId + 4, session.getEntityCache().getNextEntityId().incrementAndGet(), EntityType.ENDER_DRAGON_PART, position, motion, rotation, 4, 2);
rightWing = new EnderDragonPartEntity(entityId + 5, session.getEntityCache().getNextEntityId().incrementAndGet(), EntityType.ENDER_DRAGON_PART, position, motion, rotation, 4, 2);
tail = new EnderDragonPartEntity[3];
for (int i = 0; i < 3; i++) {
tail[i] = new EnderDragonPartEntity(entityId + 6 + i, session.getEntityCache().getNextEntityId().incrementAndGet(), EntityType.ENDER_DRAGON_PART, position, motion, rotation, 2, 2);
}
allParts = new EnderDragonPartEntity[]{head, neck, body, leftWing, rightWing, tail[0], tail[1], tail[2]};
for (EnderDragonPartEntity part : allParts) {
session.getEntityCache().spawnEntity(part);
}
for (int i = 0; i < segmentHistory.length; i++) {
segmentHistory[i] = new Segment();
segmentHistory[i].yaw = rotation.getZ();
segmentHistory[i].y = position.getY();
}
partPositionUpdater = session.getConnector().getGeneralThreadPool().scheduleAtFixedRate(() -> {
pushSegment();
updateBoundingBoxes(session);
}, 0, 50, TimeUnit.MILLISECONDS);
session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
}
@Override
public boolean despawnEntity(GeyserSession session) {
partPositionUpdater.cancel(true);
for (EnderDragonPartEntity part : allParts) {
part.despawnEntity(session);
}
return super.despawnEntity(session);
}
/**
* Updates the positions of the Ender Dragon's multiple bounding boxes
*
* @param session GeyserSession.
*/
private void updateBoundingBoxes(GeyserSession session) {
Vector3f facingDir = Vector3f.createDirectionDeg(0, rotation.getZ());
Segment baseSegment = getSegment(5);
// Used to angle the head, neck, and tail when the dragon flies up/down
float pitch = (float) Math.toRadians(10 * (baseSegment.getY() - getSegment(10).getY()));
float pitchXZ = (float) Math.cos(pitch);
float pitchY = (float) Math.sin(pitch);
// Lowers the head when the dragon sits/hovers
float headDuck;
if (hovering || metadata.getFlags().getFlag(EntityFlag.SITTING)) {
headDuck = -1f;
} else {
headDuck = baseSegment.y - getSegment(0).y;
}
head.setPosition(facingDir.up(pitchY).mul(pitchXZ, 1, -pitchXZ).mul(6.5f).up(headDuck));
neck.setPosition(facingDir.up(pitchY).mul(pitchXZ, 1, -pitchXZ).mul(5.5f).up(headDuck));
body.setPosition(facingDir.mul(0.5f, 0f, -0.5f));
Vector3f wingPos = Vector3f.createDirectionDeg(0, 90f - rotation.getZ()).mul(4.5f).up(2f);
rightWing.setPosition(wingPos);
leftWing.setPosition(wingPos.mul(-1, 1, -1)); // Mirror horizontally
Vector3f tailBase = facingDir.mul(1.5f);
for (int i = 0; i < tail.length; i++) {
float distance = (i + 1) * 2f;
// Curls the tail when the dragon turns
Segment targetSegment = getSegment(12 + 2 * i);
float angle = rotation.getZ() + targetSegment.yaw - baseSegment.yaw;
float tailYOffset = targetSegment.y - baseSegment.y - (distance + 1.5f) * pitchY + 1.5f;
tail[i].setPosition(Vector3f.createDirectionDeg(0, angle).mul(distance).add(tailBase).mul(-pitchXZ, 1, pitchXZ).up(tailYOffset));
}
// Send updated positions
for (EnderDragonPartEntity part : allParts) {
part.moveAbsolute(session, part.getPosition().add(position), Vector3f.ZERO, false, false);
}
}
/**
* Store the current yaw and y into the circular buffer
*/
private void pushSegment() {
latestSegment = (latestSegment + 1) % segmentHistory.length;
segmentHistory[latestSegment].yaw = rotation.getZ();
segmentHistory[latestSegment].y = position.getY();
}
/**
* Gets the previous yaw and y
* Used to curl the tail and pitch the head and tail up/down
*
* @param index Number of ticks in the past
* @return Segment with the yaw and y
*/
private Segment getSegment(int index) {
index = (latestSegment - index) % segmentHistory.length;
if (index < 0) {
index += segmentHistory.length;
}
return segmentHistory[index];
}
@Data
private static class Segment {
private float yaw;
private float y;
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.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.type.EntityType;
public class EnderDragonPartEntity extends Entity {
public EnderDragonPartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, float width, float height) {
super(entityId, geyserId, entityType, position, motion, rotation);
metadata.put(EntityData.BOUNDING_BOX_WIDTH, width);
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, height);
metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true);
}
}

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

@ -23,18 +23,18 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
package org.geysermc.connector.entity.player;
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;
@ -42,15 +42,17 @@ import com.nukkitx.protocol.bedrock.packet.SetEntityLinkPacket;
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
import lombok.Getter;
import lombok.Setter;
import net.kyori.adventure.text.Component;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.LivingEntity;
import org.geysermc.connector.entity.attribute.Attribute;
import org.geysermc.connector.entity.attribute.AttributeType;
import org.geysermc.connector.entity.living.animal.tameable.ParrotEntity;
import org.geysermc.connector.entity.type.EntityType;
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;
@ -63,9 +65,7 @@ public class PlayerEntity extends LivingEntity {
private GameProfile profile;
private UUID uuid;
private String username;
private long lastSkinUpdate = -1;
private boolean playerList = true; // Player is in the player list
private final EntityEffectCache effectCache;
/**
* Saves the parrot currently on the player's left shoulder; otherwise null
@ -82,14 +82,10 @@ public class PlayerEntity extends LivingEntity {
profile = gameProfile;
uuid = gameProfile.getId();
username = gameProfile.getName();
effectCache = new EntityEffectCache();
if (geyserId == 1) valid = true;
}
@Override
public void spawnEntity(GeyserSession session) {
if (geyserId == 1) return;
AddPlayerPacket addPlayerPacket = new AddPlayerPacket();
addPlayerPacket.setUuid(uuid);
addPlayerPacket.setUsername(username);
@ -160,6 +156,10 @@ public class PlayerEntity extends LivingEntity {
setRotation(rotation);
this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ);
// If this is the player logged in through this Geyser session
if (geyserId == 1) {
session.getCollisionManager().updatePlayerBoundingBox(position);
}
setOnGround(isOnGround);
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
@ -168,6 +168,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);
@ -220,7 +231,18 @@ public class PlayerEntity extends LivingEntity {
@Override
public void setPosition(Vector3f position) {
this.position = position.add(0, entityType.getOffset(), 0);
setPosition(position, true);
}
/**
* Set the player position and specify if the entity type's offset should be added. Set to false when the player
* sends us a move packet where the offset is already added
*
* @param position the new position of the Bedrock player
* @param includeOffset whether to include the offset
*/
public void setPosition(Vector3f position, boolean includeOffset) {
this.position = includeOffset ? position.add(0, entityType.getOffset(), 0) : position;
}
@Override
@ -229,24 +251,18 @@ public class PlayerEntity extends LivingEntity {
if (entityMetadata.getId() == 2) {
String username = this.username;
TextMessage name = (TextMessage) entityMetadata.getValue();
Component name = (Component) entityMetadata.getValue();
if (name != null) {
username = MessageUtils.getBedrockMessage(name);
username = MessageTranslator.convertMessage(name);
}
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

@ -0,0 +1,67 @@
/*
* 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.player;
import com.github.steveice10.mc.auth.data.GameProfile;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.UUID;
/**
* The entity class specifically for a {@link GeyserSession}'s player.
*/
public class SessionPlayerEntity extends PlayerEntity {
private final GeyserSession session;
public SessionPlayerEntity(GeyserSession session) {
super(new GameProfile(UUID.randomUUID(), "unknown"), 1, 1, Vector3f.ZERO, Vector3f.ZERO, Vector3f.ZERO);
valid = true;
this.session = session;
this.session.getCollisionManager().updatePlayerBoundingBox(position);
}
@Override
public void spawnEntity(GeyserSession session) {
// Already logged in
}
@Override
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
session.getCollisionManager().updatePlayerBoundingBox(position);
super.moveAbsolute(session, position, rotation, isOnGround, teleported);
}
@Override
public void setPosition(Vector3f position) {
if (session != null) { // null during entity initialization
session.getCollisionManager().updatePlayerBoundingBox(position);
}
super.setPosition(position);
}
}

View File

@ -0,0 +1,68 @@
/*
* 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.player;
import com.github.steveice10.mc.auth.data.GameProfile;
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 lombok.Getter;
import lombok.Setter;
import org.geysermc.connector.network.session.GeyserSession;
/**
* A wrapper to handle skulls more effectively - skulls have to be treated as entities since there are no
* custom player skulls in Bedrock.
*/
public class SkullPlayerEntity extends PlayerEntity {
/**
* Stores the block state that the skull is associated with. Used to determine if the block in the skull's position
* has changed
*/
@Getter
@Setter
private int blockState;
public SkullPlayerEntity(GameProfile gameProfile, long geyserId, Vector3f position, Vector3f rotation) {
super(gameProfile, 0, geyserId, position, Vector3f.ZERO, rotation);
setPlayerList(false);
//Set bounding box to almost nothing so the skull is able to be broken and not cause entity to cast a shadow
metadata.clear();
metadata.put(EntityData.SCALE, 1.08f);
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.001f);
metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.001f);
metadata.getOrCreateFlags().setFlag(EntityFlag.CAN_SHOW_NAME, false);
metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true); // Until the skin is loaded
}
public void despawnEntity(GeyserSession session, Vector3i position) {
this.despawnEntity(session);
session.getSkullCache().remove(position, this);
}
}

View File

@ -37,6 +37,7 @@ import org.geysermc.connector.entity.living.monster.raid.AbstractIllagerEntity;
import org.geysermc.connector.entity.living.monster.raid.PillagerEntity;
import org.geysermc.connector.entity.living.monster.raid.RaidParticipantEntity;
import org.geysermc.connector.entity.living.monster.raid.SpellcasterIllagerEntity;
import org.geysermc.connector.entity.player.PlayerEntity;
@Getter
public enum EntityType {
@ -47,12 +48,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 +75,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),
@ -86,7 +87,7 @@ public enum EntityType {
ELDER_GUARDIAN(ElderGuardianEntity.class, 50, 1.9975f),
NPC(PlayerEntity.class, 51, 1.8f, 0.6f, 0.6f, 1.62f),
WITHER(WitherEntity.class, 52, 3.5f, 0.9f),
ENDER_DRAGON(EnderDragonEntity.class, 53, 4f, 13f),
ENDER_DRAGON(EnderDragonEntity.class, 53, 0f, 0f),
SHULKER(ShulkerEntity.class, 54, 1f, 1f),
ENDERMITE(MonsterEntity.class, 55, 0.3f, 0.4f),
AGENT(Entity.class, 56, 0f),
@ -99,7 +100,7 @@ public enum EntityType {
ARMOR_STAND(ArmorStandEntity.class, 61, 1.975f, 0.5f),
TRIPOD_CAMERA(Entity.class, 62, 0f),
PLAYER(PlayerEntity.class, 63, 1.8f, 0.6f, 0.6f, 1.62f),
ITEM(ItemEntity.class, 64, 0.25f, 0.25f),
ITEM(ItemEntity.class, 64, 0.25f, 0.25f, 0.25f, 0.125f),
PRIMED_TNT(TNTEntity.class, 65, 0.98f, 0.98f, 0.98f, 0f, "minecraft:tnt"),
FALLING_BLOCK(FallingBlockEntity.class, 66, 0.98f, 0.98f),
MOVING_BLOCK(Entity.class, 67, 0f),
@ -109,7 +110,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 +126,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 +142,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 +154,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"),
@ -166,7 +167,12 @@ public enum EntityType {
/**
* Not an entity in Bedrock, so we replace it with a Pillager
*/
ILLUSIONER(AbstractIllagerEntity.class, 114, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:pillager");
ILLUSIONER(AbstractIllagerEntity.class, 114, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:pillager"),
/**
* Not an entity in Bedrock, but used for the Ender Dragon's multiple hitboxes
*/
ENDER_DRAGON_PART(EnderDragonPartEntity.class, 32, 0, 0, 0, 0, "minecraft:armor_stand");
private static final EntityType[] VALUES = values();

View File

@ -0,0 +1,32 @@
/*
* 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.inventory;
public class AnvilContainer extends Container {
public AnvilContainer(String title, int id, int size, PlayerInventory playerInventory) {
super(title, id, size, playerInventory);
}
}

View File

@ -0,0 +1,40 @@
/*
* 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.inventory;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class BeaconContainer extends Container {
private int primaryId;
private int secondaryId;
public BeaconContainer(String title, int id,int size, PlayerInventory playerInventory) {
super(title, id, size, playerInventory);
}
}

View File

@ -25,7 +25,6 @@
package org.geysermc.connector.inventory;
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
import lombok.Getter;
import lombok.NonNull;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
@ -38,8 +37,8 @@ public class Container extends Inventory {
private final PlayerInventory playerInventory;
private final int containerSize;
public Container(String title, int id, WindowType windowType, int size, PlayerInventory playerInventory) {
super(title, id, windowType, size);
public Container(String title, int id, int size, PlayerInventory playerInventory) {
super(title, id, size);
this.playerInventory = playerInventory;
this.containerSize = this.size + InventoryTranslator.PLAYER_INVENTORY_SIZE;
}

View File

@ -0,0 +1,52 @@
/*
* 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.inventory;
import com.nukkitx.protocol.bedrock.data.inventory.EnchantOptionData;
import lombok.Getter;
public class EnchantingContainer extends Container {
/**
* A cache of what Bedrock sees
*/
@Getter
private final EnchantOptionData[] enchantOptions;
/**
* A mutable cache of what the server sends us
*/
@Getter
private final GeyserEnchantOption[] geyserEnchantOptions;
public EnchantingContainer(String title, int id, int size, PlayerInventory playerInventory) {
super(title, id, size, playerInventory);
enchantOptions = new EnchantOptionData[3];
geyserEnchantOptions = new GeyserEnchantOption[3];
for (int i = 0; i < geyserEnchantOptions.length; i++) {
geyserEnchantOptions[i] = new GeyserEnchantOption(i);
}
}
}

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