Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-26 00:00:41 +01:00
Commit
6df89ed679
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@ -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
3
.gitignore
vendored
@ -242,4 +242,5 @@ logs/
|
||||
public-key.pem
|
||||
locales/
|
||||
/cache/
|
||||
/packs/
|
||||
/packs/
|
||||
/dump.json
|
1
Jenkinsfile
vendored
1
Jenkinsfile
vendored
@ -24,6 +24,7 @@ pipeline {
|
||||
when {
|
||||
branch "master"
|
||||
}
|
||||
|
||||
steps {
|
||||
sh 'mvn javadoc:jar source:jar deploy -DskipTests'
|
||||
}
|
||||
|
30
README.md
30
README.md
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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<>();
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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()),
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<>();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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<>();
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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;
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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())));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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+)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren