Compare commits

...

2 Commits

Author SHA1 Message Date
Camotoy 498b058aba
World border improvements (#488) 2020-06-04 16:56:16 -04:00
Luke 50c4c0b2d8
Initial world border 2020-05-05 14:10:27 +01:00
243 changed files with 7657 additions and 1875 deletions

View File

@ -9,10 +9,14 @@
Geyser is a bridge between Minecraft: Bedrock Edition and Minecraft: Java Edition, closing the gap from those wanting to play true cross-platform. Geyser is a bridge between Minecraft: Bedrock Edition and Minecraft: Java Edition, closing the gap from those wanting to play true cross-platform.
Geyser is an open collaboration project by [CubeCraft Games](https://cubecraft.net).
## What is Geyser? ## What is Geyser?
Geyser is a proxy, bridging the gap between Minecraft: Bedrock Edition and Minecraft: Java Edition servers. Geyser is a proxy, bridging the gap between Minecraft: Bedrock Edition and Minecraft: Java Edition servers.
The ultimate goal of this project is to allow Minecraft: Bedrock Edition users to join Minecraft: Java Edition servers as seamlessly as possible. **Please note, this project is still a work in progress and should not be used on production. Expect bugs!** The ultimate goal of this project is to allow Minecraft: Bedrock Edition users to join Minecraft: Java Edition servers as seamlessly as possible. **Please note, this project is still a work in progress and should not be used on production. Expect bugs!**
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.14.6(0) and Minecraft Java v1.15.2. ### Currently supporting Minecraft Bedrock v1.14.6(0) and Minecraft Java v1.15.2.
## Setting Up ## Setting Up

View File

@ -23,6 +23,12 @@
<version>1.14-R0.1-SNAPSHOT</version> <version>1.14-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>us.myles</groupId>
<artifactId>viaversion</artifactId>
<version>3.0.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
<finalName>${outputName}-Bukkit</finalName> <finalName>${outputName}-Bukkit</finalName>

View File

@ -87,8 +87,28 @@ public class GeyserBukkitConfiguration implements GeyserConfiguration {
} }
@Override @Override
public boolean isPingPassthrough() { public boolean isCommandSuggestions() {
return config.getBoolean("ping-passthrough", false); return config.getBoolean("command-suggestions", true);
}
@Override
public boolean isPassthroughMotd() {
return config.getBoolean("passthrough-motd", false);
}
@Override
public boolean isPassthroughPlayerCounts() {
return config.getBoolean("passthrough-player-counts", false);
}
@Override
public boolean isLegacyPingPassthrough() {
return config.getBoolean("legacy-ping-passthrough", false);
}
@Override
public int getPingPassthroughInterval() {
return config.getInt("ping-passthrough-interval", 3);
} }
@Override @Override
@ -111,6 +131,11 @@ public class GeyserBukkitConfiguration implements GeyserConfiguration {
return config.getBoolean("allow-third-party-capes", true); return config.getBoolean("allow-third-party-capes", true);
} }
@Override
public boolean isAllowThirdPartyEars() {
return config.getBoolean("allow-third-party-ears", false);
}
@Override @Override
public String getDefaultLocale() { public String getDefaultLocale() {
return config.getString("default-locale", "en_us"); return config.getString("default-locale", "en_us");
@ -126,6 +151,11 @@ public class GeyserBukkitConfiguration implements GeyserConfiguration {
return true; // We override this as with Bukkit, we have direct access to the server implementation return true; // We override this as with Bukkit, we have direct access to the server implementation
} }
@Override
public boolean isAboveBedrockNetherBuilding() {
return config.getBoolean("above-bedrock-nether-building", false);
}
@Override @Override
public IMetricsInfo getMetrics() { public IMetricsInfo getMetrics() {
return metricsInfo; return metricsInfo;
@ -203,4 +233,9 @@ public class GeyserBukkitConfiguration implements GeyserConfiguration {
return config.getString("metrics.uuid", "generateduuid"); return config.getString("metrics.uuid", "generateduuid");
} }
} }
@Override
public int getConfigVersion() {
return config.getInt("config-version", 0);
}
} }

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*
*/
package org.geysermc.platform.bukkit;
import lombok.AllArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.server.ServerListPingEvent;
import org.bukkit.util.CachedServerIcon;
import org.geysermc.common.ping.GeyserPingInfo;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import java.net.InetAddress;
import java.util.Collections;
import java.util.Iterator;
@AllArgsConstructor
public class GeyserBukkitPingPassthrough implements IGeyserPingPassthrough {
private final GeyserBukkitLogger logger;
@Override
public GeyserPingInfo getPingInformation() {
try {
ServerListPingEvent event = new GeyserPingEvent(InetAddress.getLocalHost(), Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(), Bukkit.getMaxPlayers());
Bukkit.getPluginManager().callEvent(event);
GeyserPingInfo geyserPingInfo = new GeyserPingInfo(event.getMotd(), event.getNumPlayers(), event.getMaxPlayers());
Bukkit.getOnlinePlayers().forEach(player -> {
geyserPingInfo.addPlayer(player.getName());
});
return geyserPingInfo;
} catch (Exception e) {
logger.debug("Error while getting Bukkit ping passthrough: " + e.toString());
return new GeyserPingInfo(null, 0, 0);
}
}
// These methods are unimplemented on spigot api by default so we add stubs so plugins don't complain
private static class GeyserPingEvent extends ServerListPingEvent {
public GeyserPingEvent(InetAddress address, String motd, int numPlayers, int maxPlayers) {
super(address, motd, numPlayers, maxPlayers);
}
@Override
public void setServerIcon(CachedServerIcon icon) throws IllegalArgumentException, UnsupportedOperationException {
}
@Override
public Iterator<Player> iterator() throws UnsupportedOperationException {
return Collections.EMPTY_LIST.iterator();
}
}
}

View File

@ -28,13 +28,18 @@ package org.geysermc.platform.bukkit;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.common.PlatformType; import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.network.translators.world.WorldManager; import org.geysermc.connector.network.translators.world.WorldManager;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import org.geysermc.platform.bukkit.command.GeyserBukkitCommandExecutor; import org.geysermc.platform.bukkit.command.GeyserBukkitCommandExecutor;
import org.geysermc.platform.bukkit.command.GeyserBukkitCommandManager; import org.geysermc.platform.bukkit.command.GeyserBukkitCommandManager;
import org.geysermc.platform.bukkit.world.GeyserBukkitBlockPlaceListener;
import org.geysermc.platform.bukkit.world.GeyserBukkitWorldManager; import org.geysermc.platform.bukkit.world.GeyserBukkitWorldManager;
import us.myles.ViaVersion.api.Via;
import java.util.UUID; import java.util.UUID;
@ -43,6 +48,8 @@ public class GeyserBukkitPlugin extends JavaPlugin implements GeyserBootstrap {
private GeyserBukkitCommandManager geyserCommandManager; private GeyserBukkitCommandManager geyserCommandManager;
private GeyserBukkitConfiguration geyserConfig; private GeyserBukkitConfiguration geyserConfig;
private GeyserBukkitLogger geyserLogger; private GeyserBukkitLogger geyserLogger;
private IGeyserPingPassthrough geyserBukkitPingPassthrough;
private GeyserBukkitBlockPlaceListener blockPlaceListener;
private GeyserBukkitWorldManager geyserWorldManager; private GeyserBukkitWorldManager geyserWorldManager;
private GeyserConnector connector; private GeyserConnector connector;
@ -59,7 +66,7 @@ public class GeyserBukkitPlugin extends JavaPlugin implements GeyserBootstrap {
// Don't change the ip if its listening on all interfaces // Don't change the ip if its listening on all interfaces
// By default this should be 127.0.0.1 but may need to be changed in some circumstances // By default this should be 127.0.0.1 but may need to be changed in some circumstances
if (!Bukkit.getIp().equals("0.0.0.0")) { if (!Bukkit.getIp().equals("0.0.0.0") && !Bukkit.getIp().equals("")) {
getConfig().set("remote.address", Bukkit.getIp()); getConfig().set("remote.address", Bukkit.getIp());
} }
@ -67,13 +74,39 @@ public class GeyserBukkitPlugin extends JavaPlugin implements GeyserBootstrap {
saveConfig(); saveConfig();
this.geyserLogger = new GeyserBukkitLogger(getLogger(), geyserConfig.isDebugMode()); this.geyserLogger = new GeyserBukkitLogger(getLogger(), geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
geyserConfig.loadFloodgate(this); geyserConfig.loadFloodgate(this);
this.connector = GeyserConnector.start(PlatformType.BUKKIT, this); this.connector = GeyserConnector.start(PlatformType.BUKKIT, this);
if (geyserConfig.isLegacyPingPassthrough()) {
this.geyserBukkitPingPassthrough = GeyserLegacyPingPassthrough.init(connector);
} else {
this.geyserBukkitPingPassthrough = new GeyserBukkitPingPassthrough(geyserLogger);
}
this.geyserCommandManager = new GeyserBukkitCommandManager(this, connector); this.geyserCommandManager = new GeyserBukkitCommandManager(this, connector);
this.geyserWorldManager = new GeyserBukkitWorldManager();
boolean isViaVersion = false;
// Used to determine if Block.getBlockData() is present.
boolean isLegacy = !isCompatible(Bukkit.getServer().getVersion(), "1.13.0");
if (isLegacy)
geyserLogger.debug("Legacy version of Minecraft (1.12.2 or older) detected.");
if (Bukkit.getPluginManager().getPlugin("ViaVersion") != null) {
// TODO: Update when ViaVersion updates
// API changes between 2.2.3 and 3.0.0-SNAPSHOT require this check
if (!Via.getAPI().getVersion().equals("3.0.0-SNAPSHOT") && isLegacy) {
geyserLogger.info("ViaVersion detected but not ViaVersion-ABSTRACTION. Please update your ViaVersion plugin for compatibility with Geyser.");
} else {
isViaVersion = true;
}
}
this.geyserWorldManager = new GeyserBukkitWorldManager(isLegacy, isViaVersion);
this.blockPlaceListener = new GeyserBukkitBlockPlaceListener(connector, isLegacy, isViaVersion);
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
this.getCommand("geyser").setExecutor(new GeyserBukkitCommandExecutor(connector)); this.getCommand("geyser").setExecutor(new GeyserBukkitCommandExecutor(connector));
} }
@ -98,8 +131,47 @@ public class GeyserBukkitPlugin extends JavaPlugin implements GeyserBootstrap {
return this.geyserCommandManager; return this.geyserCommandManager;
} }
@Override
public IGeyserPingPassthrough getGeyserPingPassthrough() {
return geyserBukkitPingPassthrough;
}
@Override @Override
public WorldManager getWorldManager() { public WorldManager getWorldManager() {
return this.geyserWorldManager; return this.geyserWorldManager;
} }
public boolean isCompatible(String version, String whichVersion) {
int[] currentVersion = parseVersion(version);
int[] otherVersion = parseVersion(whichVersion);
int length = Math.max(currentVersion.length, otherVersion.length);
for (int index = 0; index < length; index = index + 1) {
int self = (index < currentVersion.length) ? currentVersion[index] : 0;
int other = (index < otherVersion.length) ? otherVersion[index] : 0;
if (self != other) {
return (self - other) > 0;
}
}
return true;
}
private int[] parseVersion(String versionParam) {
versionParam = (versionParam == null) ? "" : versionParam;
if (versionParam.contains("(MC: ")) {
versionParam = versionParam.split("\\(MC: ")[1];
versionParam = versionParam.split("\\)")[0];
}
String[] stringArray = versionParam.split("[_.-]");
int[] temp = new int[stringArray.length];
for (int index = 0; index <= (stringArray.length - 1); index = index + 1) {
String t = stringArray[index].replaceAll("\\D", "");
try {
temp[index] = Integer.parseInt(t);
} catch(NumberFormatException ex) {
temp[index] = 0;
}
}
return temp;
}
} }

View File

@ -0,0 +1,73 @@
/*
* 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.bukkit.world;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.SoundEvent;
import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
import lombok.AllArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
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;
@AllArgsConstructor
public class GeyserBukkitBlockPlaceListener implements Listener {
private final GeyserConnector connector;
private final boolean isLegacy;
private final boolean isViaVersion;
@EventHandler
public void place(final BlockPlaceEvent event) {
for (GeyserSession session : connector.getPlayers().values()) {
if (event.getPlayer() == Bukkit.getPlayer(session.getPlayerEntity().getUsername())) {
LevelSoundEventPacket placeBlockSoundPacket = new LevelSoundEventPacket();
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(GeyserBukkitWorldManager.getLegacyBlock(session,
event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ(), isViaVersion));
} else {
javaBlockId = event.getBlockPlaced().getBlockData().getAsString();
}
placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().get(javaBlockId)));
placeBlockSoundPacket.setIdentifier(":");
session.sendUpstreamPacket(placeBlockSoundPacket);
session.setLastBlockPlacePosition(null);
session.setLastBlockPlacedId(null);
break;
}
}
}
}

View File

@ -28,18 +28,49 @@ package org.geysermc.platform.bukkit.world;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.block.Block;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.WorldManager; 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.BlockTranslator;
import org.geysermc.platform.bukkit.GeyserBukkitPlugin;
import us.myles.ViaVersion.protocols.protocol1_13_1to1_13.Protocol1_13_1To1_13;
import us.myles.ViaVersion.protocols.protocol1_15to1_14_4.data.MappingData;
@AllArgsConstructor
public class GeyserBukkitWorldManager extends WorldManager { public class GeyserBukkitWorldManager extends WorldManager {
private final boolean isLegacy;
// 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
private final boolean isViaVersion;
@Override @Override
public BlockState getBlockAt(GeyserSession session, int x, int y, int z) { public BlockState getBlockAt(GeyserSession session, int x, int y, int z) {
if (session.getPlayerEntity() == null) { if (session.getPlayerEntity() == null) {
return BlockTranslator.AIR; return BlockTranslator.AIR;
} }
if (isLegacy) {
return getLegacyBlock(session, x, y, z, isViaVersion);
}
return BlockTranslator.getJavaIdBlockMap().get(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getBlockAt(x, y, z).getBlockData().getAsString()); return BlockTranslator.getJavaIdBlockMap().get(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getBlockAt(x, y, z).getBlockData().getAsString());
} }
@SuppressWarnings("deprecation")
public static BlockState getLegacyBlock(GeyserSession session, int x, int y, int z, boolean isViaVersion) {
if (isViaVersion) {
Block block = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().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
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);
return new BlockState(MappingData.blockStateMappings.getNewId(fourteenBlockId));
} else {
return BlockTranslator.AIR;
}
}
} }

View File

@ -3,6 +3,8 @@ name: ${outputName}-Bukkit
author: ${project.organization.name} author: ${project.organization.name}
website: ${project.organization.url} website: ${project.organization.url}
version: ${project.version} version: ${project.version}
softdepend: ["ViaVersion"]
api-version: 1.13
commands: commands:
geyser: geyser:
description: The main command for Geyser. description: The main command for Geyser.

View File

@ -20,7 +20,7 @@
<dependency> <dependency>
<groupId>net.md-5</groupId> <groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId> <artifactId>bungeecord-api</artifactId>
<version>1.14-SNAPSHOT</version> <version>1.15-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@ -80,4 +80,4 @@
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -86,8 +86,28 @@ public class GeyserBungeeConfiguration implements GeyserConfiguration {
} }
@Override @Override
public boolean isPingPassthrough() { public boolean isCommandSuggestions() {
return config.getBoolean("ping-passthrough", false); return config.getBoolean("command-suggestions", true);
}
@Override
public boolean isPassthroughMotd() {
return config.getBoolean("passthrough-motd", false);
}
@Override
public boolean isPassthroughPlayerCounts() {
return config.getBoolean("passthrough-player-counts", false);
}
@Override
public boolean isLegacyPingPassthrough() {
return config.getBoolean("legacy-ping-passthrough", false);
}
@Override
public int getPingPassthroughInterval() {
return config.getInt("ping-passthrough-interval", 3);
} }
@Override @Override
@ -110,6 +130,11 @@ public class GeyserBungeeConfiguration implements GeyserConfiguration {
return config.getBoolean("allow-third-party-capes", true); return config.getBoolean("allow-third-party-capes", true);
} }
@Override
public boolean isAllowThirdPartyEars() {
return config.getBoolean("allow-third-party-ears", false);
}
@Override @Override
public String getDefaultLocale() { public String getDefaultLocale() {
return config.getString("default-locale", "en_us"); return config.getString("default-locale", "en_us");
@ -125,6 +150,11 @@ public class GeyserBungeeConfiguration implements GeyserConfiguration {
return config.getBoolean("cache-chunks", false); return config.getBoolean("cache-chunks", false);
} }
@Override
public boolean isAboveBedrockNetherBuilding() {
return config.getBoolean("above-bedrock-nether-building", false);
}
@Override @Override
public BungeeMetricsInfo getMetrics() { public BungeeMetricsInfo getMetrics() {
return metricsInfo; return metricsInfo;
@ -202,4 +232,9 @@ public class GeyserBungeeConfiguration implements GeyserConfiguration {
return config.getString("metrics.uuid", "generateduuid"); return config.getString("metrics.uuid", "generateduuid");
} }
} }
@Override
public int getConfigVersion() {
return config.getInt("config-version", 0);
}
} }

View File

@ -0,0 +1,180 @@
/*
* 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.bungeecord;
import lombok.AllArgsConstructor;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.event.ProxyPingEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.protocol.ProtocolConstants;
import org.geysermc.common.ping.GeyserPingInfo;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Arrays;
import java.util.UUID;
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() {
CompletableFuture<ProxyPingEvent> future = new CompletableFuture<>();
proxyServer.getPluginManager().callEvent(new ProxyPingEvent(PENDING_CONNECTION, getPingInfo(), (event, throwable) -> {
if (throwable != null) future.completeExceptionally(throwable);
else future.complete(event);
}));
ProxyPingEvent event = future.join();
GeyserPingInfo geyserPingInfo = new GeyserPingInfo(
event.getResponse().getDescription(),
event.getResponse().getPlayers().getOnline(),
event.getResponse().getPlayers().getMax()
);
if (event.getResponse().getPlayers().getSample() != null) {
Arrays.stream(event.getResponse().getPlayers().getSample()).forEach(proxiedPlayer -> {
geyserPingInfo.addPlayer(proxiedPlayer.getName());
});
}
return geyserPingInfo;
}
// This is static so pending connection can use it
private static ListenerInfo getDefaultListener() {
return ProxyServer.getInstance().getConfig().getListeners().iterator().next();
}
private ServerPing getPingInfo() {
return new ServerPing(
new ServerPing.Protocol(proxyServer.getName() + " " + proxyServer.getGameVersion(), ProtocolConstants.SUPPORTED_VERSION_IDS.get(ProtocolConstants.SUPPORTED_VERSION_IDS.size() - 1)),
new ServerPing.Players(getDefaultListener().getMaxPlayers(), proxyServer.getOnlineCount(), null),
getDefaultListener().getMotd(), proxyServer.getConfig().getFaviconObject()
);
}
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);
@Override
public String getName() {
throw new UnsupportedOperationException();
}
@Override
public int getVersion() {
return ProtocolConstants.SUPPORTED_VERSION_IDS.get(ProtocolConstants.SUPPORTED_VERSION_IDS.size() - 1);
}
@Override
public InetSocketAddress getVirtualHost() {
return null;
}
@Override
public ListenerInfo getListener() {
return getDefaultListener();
}
@Override
public String getUUID() {
return FAKE_UUID.toString();
}
@Override
public UUID getUniqueId() {
return FAKE_UUID;
}
@Override
public void setUniqueId(UUID uuid) {
throw new UnsupportedOperationException();
}
@Override
public boolean isOnlineMode() {
return true;
}
@Override
public void setOnlineMode(boolean b) {
throw new UnsupportedOperationException();
}
@Override
public boolean isLegacy() {
return false;
}
@Override
public InetSocketAddress getAddress() {
return FAKE_REMOTE;
}
@Override
public SocketAddress getSocketAddress() {
return getAddress();
}
@Override
public void disconnect(String s) {
throw new UnsupportedOperationException();
}
@Override
public void disconnect(BaseComponent... baseComponents) {
throw new UnsupportedOperationException();
}
@Override
public void disconnect(BaseComponent baseComponent) {
throw new UnsupportedOperationException();
}
@Override
public boolean isConnected() {
return false;
}
@Override
public Unsafe unsafe() {
throw new UnsupportedOperationException();
}
}
}

View File

@ -31,9 +31,12 @@ import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider; import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration; import net.md_5.bungee.config.YamlConfiguration;
import org.geysermc.common.PlatformType; import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import org.geysermc.platform.bungeecord.command.GeyserBungeeCommandExecutor; import org.geysermc.platform.bungeecord.command.GeyserBungeeCommandExecutor;
import org.geysermc.platform.bungeecord.command.GeyserBungeeCommandManager; import org.geysermc.platform.bungeecord.command.GeyserBungeeCommandManager;
@ -50,6 +53,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
private GeyserBungeeCommandManager geyserCommandManager; private GeyserBungeeCommandManager geyserCommandManager;
private GeyserBungeeConfiguration geyserConfig; private GeyserBungeeConfiguration geyserConfig;
private GeyserBungeeLogger geyserLogger; private GeyserBungeeLogger geyserLogger;
private IGeyserPingPassthrough geyserBungeePingPassthrough;
private GeyserConnector connector; private GeyserConnector connector;
@ -91,7 +95,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
// Don't change the ip if its listening on all interfaces // Don't change the ip if its listening on all interfaces
// By default this should be 127.0.0.1 but may need to be changed in some circumstances // By default this should be 127.0.0.1 but may need to be changed in some circumstances
if (!javaAddr.getHostString().equals("0.0.0.0")) { if (!javaAddr.getHostString().equals("0.0.0.0") && !javaAddr.getHostString().equals("")) {
configuration.set("remote.address", javaAddr.getHostString()); configuration.set("remote.address", javaAddr.getHostString());
} }
@ -116,6 +120,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
} }
this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode()); this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
geyserConfig.loadFloodgate(this); geyserConfig.loadFloodgate(this);
@ -123,6 +128,12 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
this.geyserCommandManager = new GeyserBungeeCommandManager(connector); this.geyserCommandManager = new GeyserBungeeCommandManager(connector);
if (geyserConfig.isLegacyPingPassthrough()) {
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(connector);
} else {
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
}
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(connector)); this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(connector));
} }
@ -145,4 +156,9 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
public CommandManager getGeyserCommandManager() { public CommandManager getGeyserCommandManager() {
return this.geyserCommandManager; return this.geyserCommandManager;
} }
@Override
public IGeyserPingPassthrough getGeyserPingPassthrough() {
return geyserBungeePingPassthrough;
}
} }

View File

@ -80,8 +80,28 @@ public class GeyserSpongeConfiguration implements GeyserConfiguration {
} }
@Override @Override
public boolean isPingPassthrough() { public boolean isCommandSuggestions() {
return node.getNode("ping-passthrough").getBoolean(false); return node.getNode("command-suggestions").getBoolean(true);
}
@Override
public boolean isPassthroughMotd() {
return node.getNode("passthrough-motd").getBoolean(false);
}
@Override
public boolean isPassthroughPlayerCounts() {
return node.getNode("passthrough-player-counts").getBoolean(false);
}
@Override
public boolean isLegacyPingPassthrough() {
return node.getNode("legacy-ping-passthrough").getBoolean(false);
}
@Override
public int getPingPassthroughInterval() {
return node.getNode("ping-passthrough-interval").getInt(3);
} }
@Override @Override
@ -104,6 +124,11 @@ public class GeyserSpongeConfiguration implements GeyserConfiguration {
return node.getNode("allow-third-party-capes").getBoolean(true); return node.getNode("allow-third-party-capes").getBoolean(true);
} }
@Override
public boolean isAllowThirdPartyEars() {
return node.getNode("allow-third-party-ears").getBoolean(false);
}
@Override @Override
public String getDefaultLocale() { public String getDefaultLocale() {
return node.getNode("default-locale").getString("en_us"); return node.getNode("default-locale").getString("en_us");
@ -119,6 +144,11 @@ public class GeyserSpongeConfiguration implements GeyserConfiguration {
return node.getNode("cache-chunks").getBoolean(false); return node.getNode("cache-chunks").getBoolean(false);
} }
@Override
public boolean isAboveBedrockNetherBuilding() {
return node.getNode("above-bedrock-nether-building").getBoolean(false);
}
@Override @Override
public SpongeMetricsInfo getMetrics() { public SpongeMetricsInfo getMetrics() {
return metricsInfo; return metricsInfo;
@ -202,4 +232,9 @@ public class GeyserSpongeConfiguration implements GeyserConfiguration {
return node.getNode("metrics").getNode("uuid").getString("generateduuid"); return node.getNode("metrics").getNode("uuid").getString("generateduuid");
} }
} }
@Override
public int getConfigVersion() {
return node.getNode("config-version").getInt(0);
}
} }

View File

@ -0,0 +1,99 @@
/*
* 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.sponge;
import org.geysermc.common.ping.GeyserPingInfo;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import org.spongepowered.api.MinecraftVersion;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.event.SpongeEventFactory;
import org.spongepowered.api.event.cause.Cause;
import org.spongepowered.api.event.cause.EventContext;
import org.spongepowered.api.event.server.ClientPingServerEvent;
import org.spongepowered.api.network.status.StatusClient;
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() {
// come on Sponge, this is in commons, why not expose it :(
ClientPingServerEvent event;
try {
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);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
Sponge.getEventManager().post(event);
GeyserPingInfo geyserPingInfo = new GeyserPingInfo(
event.getResponse().getDescription().toPlain(),
event.getResponse().getPlayers().orElseThrow(IllegalStateException::new).getOnline(),
event.getResponse().getPlayers().orElseThrow(IllegalStateException::new).getMax());
event.getResponse().getPlayers().get().getProfiles().forEach(player -> {
geyserPingInfo.addPlayer(player.getName().orElseThrow(IllegalStateException::new));
});
return geyserPingInfo;
}
@SuppressWarnings("NullableProblems")
private static class GeyserStatusClient implements StatusClient {
private static final InetSocketAddress FAKE_REMOTE = new InetSocketAddress(Inet4Address.getLoopbackAddress(), 69);
@Override
public InetSocketAddress getAddress() {
return FAKE_REMOTE;
}
@Override
public MinecraftVersion getVersion() {
return Sponge.getPlatform().getMinecraftVersion();
}
@Override
public Optional<InetSocketAddress> getVirtualHost() {
return Optional.empty();
}
}
}

View File

@ -30,9 +30,12 @@ import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.loader.ConfigurationLoader; import ninja.leaping.configurate.loader.ConfigurationLoader;
import ninja.leaping.configurate.yaml.YAMLConfigurationLoader; import ninja.leaping.configurate.yaml.YAMLConfigurationLoader;
import org.geysermc.common.PlatformType; import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.FileUtils;
import org.geysermc.platform.sponge.command.GeyserSpongeCommandExecutor; import org.geysermc.platform.sponge.command.GeyserSpongeCommandExecutor;
import org.geysermc.platform.sponge.command.GeyserSpongeCommandManager; import org.geysermc.platform.sponge.command.GeyserSpongeCommandManager;
@ -62,6 +65,7 @@ public class GeyserSpongePlugin implements GeyserBootstrap {
private GeyserSpongeCommandManager geyserCommandManager; private GeyserSpongeCommandManager geyserCommandManager;
private GeyserSpongeConfiguration geyserConfig; private GeyserSpongeConfiguration geyserConfig;
private GeyserSpongeLogger geyserLogger; private GeyserSpongeLogger geyserLogger;
private IGeyserPingPassthrough geyserSpongePingPassthrough;
private GeyserConnector connector; private GeyserConnector connector;
@ -97,7 +101,7 @@ public class GeyserSpongePlugin implements GeyserBootstrap {
// Don't change the ip if its listening on all interfaces // Don't change the ip if its listening on all interfaces
// By default this should be 127.0.0.1 but may need to be changed in some circumstances // By default this should be 127.0.0.1 but may need to be changed in some circumstances
if (!javaAddr.getHostString().equals("0.0.0.0")) { if (!javaAddr.getHostString().equals("0.0.0.0") && !javaAddr.getHostString().equals("")) {
serverIP.setValue("127.0.0.1"); serverIP.setValue("127.0.0.1");
} }
@ -105,9 +109,16 @@ public class GeyserSpongePlugin implements GeyserBootstrap {
} }
this.geyserLogger = new GeyserSpongeLogger(logger, geyserConfig.isDebugMode()); this.geyserLogger = new GeyserSpongeLogger(logger, geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.connector = GeyserConnector.start(PlatformType.SPONGE, this); this.connector = GeyserConnector.start(PlatformType.SPONGE, this);
this.geyserCommandManager = new GeyserSpongeCommandManager(Sponge.getCommandManager(), connector);
if (geyserConfig.isLegacyPingPassthrough()) {
this.geyserSpongePingPassthrough = GeyserLegacyPingPassthrough.init(connector);
} else {
this.geyserSpongePingPassthrough = new GeyserSpongePingPassthrough();
}
this.geyserCommandManager = new GeyserSpongeCommandManager(Sponge.getCommandManager(), connector);
Sponge.getCommandManager().register(this, new GeyserSpongeCommandExecutor(connector), "geyser"); Sponge.getCommandManager().register(this, new GeyserSpongeCommandExecutor(connector), "geyser");
} }
@ -131,6 +142,11 @@ public class GeyserSpongePlugin implements GeyserBootstrap {
return this.geyserCommandManager; return this.geyserCommandManager;
} }
@Override
public IGeyserPingPassthrough getGeyserPingPassthrough() {
return geyserSpongePingPassthrough;
}
@Listener @Listener
public void onServerStart(GameStartedServerEvent event) { public void onServerStart(GameStartedServerEvent event) {
onEnable(); onEnable();

View File

@ -26,10 +26,12 @@
package org.geysermc.platform.standalone; package org.geysermc.platform.standalone;
import org.geysermc.common.PlatformType; import org.geysermc.common.PlatformType;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
import org.geysermc.connector.GeyserConfiguration; import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.FileUtils;
import org.geysermc.platform.standalone.command.GeyserCommandManager; import org.geysermc.platform.standalone.command.GeyserCommandManager;
@ -40,8 +42,9 @@ import java.util.UUID;
public class GeyserStandaloneBootstrap implements GeyserBootstrap { public class GeyserStandaloneBootstrap implements GeyserBootstrap {
private GeyserCommandManager geyserCommandManager; private GeyserCommandManager geyserCommandManager;
private GeyserConfiguration geyserConfig; private GeyserStandaloneConfiguration geyserConfig;
private GeyserStandaloneLogger geyserLogger; private GeyserStandaloneLogger geyserLogger;
private IGeyserPingPassthrough geyserPingPassthrough;
private GeyserConnector connector; private GeyserConnector connector;
@ -62,9 +65,13 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
geyserLogger.severe("Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!", ex); geyserLogger.severe("Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!", ex);
System.exit(0); System.exit(0);
} }
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
connector = GeyserConnector.start(PlatformType.STANDALONE, this); connector = GeyserConnector.start(PlatformType.STANDALONE, this);
geyserCommandManager = new GeyserCommandManager(connector); geyserCommandManager = new GeyserCommandManager(connector);
geyserPingPassthrough = GeyserLegacyPingPassthrough.init(connector);
geyserLogger.start(); geyserLogger.start();
} }
@ -88,4 +95,9 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
public CommandManager getGeyserCommandManager() { public CommandManager getGeyserCommandManager() {
return geyserCommandManager; return geyserCommandManager;
} }
@Override
public IGeyserPingPassthrough getGeyserPingPassthrough() {
return geyserPingPassthrough;
}
} }

View File

@ -47,8 +47,20 @@ public class GeyserStandaloneConfiguration implements GeyserConfiguration {
private Map<String, UserAuthenticationInfo> userAuths; private Map<String, UserAuthenticationInfo> userAuths;
@JsonProperty("ping-passthrough") @JsonProperty("command-suggestions")
private boolean pingPassthrough; private boolean isCommandSuggestions;
@JsonProperty("passthrough-motd")
private boolean isPassthroughMotd;
@JsonProperty("passthrough-player-counts")
private boolean isPassthroughPlayerCounts;
@JsonProperty("legacy-ping-passthrough")
private boolean isLegacyPingPassthrough;
@JsonProperty("ping-passthrough-interval")
private int pingPassthroughInterval;
@JsonProperty("max-players") @JsonProperty("max-players")
private int maxPlayers; private int maxPlayers;
@ -62,12 +74,18 @@ public class GeyserStandaloneConfiguration implements GeyserConfiguration {
@JsonProperty("allow-third-party-capes") @JsonProperty("allow-third-party-capes")
private boolean allowThirdPartyCapes; private boolean allowThirdPartyCapes;
@JsonProperty("allow-third-party-ears")
private boolean allowThirdPartyEars;
@JsonProperty("default-locale") @JsonProperty("default-locale")
private String defaultLocale; private String defaultLocale;
@JsonProperty("cache-chunks") @JsonProperty("cache-chunks")
private boolean cacheChunks; private boolean cacheChunks;
@JsonProperty("above-bedrock-nether-building")
private boolean isAboveBedrockNetherBuilding;
private MetricsInfo metrics; private MetricsInfo metrics;
@Override @Override
@ -112,4 +130,7 @@ public class GeyserStandaloneConfiguration implements GeyserConfiguration {
@JsonProperty("uuid") @JsonProperty("uuid")
private String uniqueId; private String uniqueId;
} }
@JsonProperty("config-version")
private int configVersion;
} }

View File

@ -52,8 +52,20 @@ public class GeyserVelocityConfiguration implements GeyserConfiguration {
private Map<String, UserAuthenticationInfo> userAuths; private Map<String, UserAuthenticationInfo> userAuths;
@JsonProperty("ping-passthrough") @JsonProperty("command-suggestions")
private boolean pingPassthrough; private boolean commandSuggestions;
@JsonProperty("passthrough-motd")
private boolean isPassthroughMotd;
@JsonProperty("passthrough-player-counts")
private boolean isPassthroughPlayerCounts;
@JsonProperty("legacy-ping-passthrough")
private boolean isLegacyPingPassthrough;
@JsonProperty("ping-passthrough-interval")
private int pingPassthroughInterval;
@JsonProperty("max-players") @JsonProperty("max-players")
private int maxPlayers; private int maxPlayers;
@ -67,19 +79,25 @@ public class GeyserVelocityConfiguration implements GeyserConfiguration {
@JsonProperty("allow-third-party-capes") @JsonProperty("allow-third-party-capes")
private boolean allowThirdPartyCapes; private boolean allowThirdPartyCapes;
@JsonProperty("allow-third-party-ears")
private boolean allowThirdPartyEars;
@JsonProperty("default-locale") @JsonProperty("default-locale")
private String defaultLocale; private String defaultLocale;
@JsonProperty("cache-chunks") @JsonProperty("cache-chunks")
private boolean cacheChunks; private boolean cacheChunks;
@JsonProperty("above-bedrock-nether-building")
private boolean aboveBedrockNetherBuilding;
private MetricsInfo metrics; private MetricsInfo metrics;
private Path floodgateKey; private Path floodgateKey;
public void loadFloodgate(GeyserVelocityPlugin plugin, ProxyServer proxyServer, File dataFolder) { public void loadFloodgate(GeyserVelocityPlugin plugin, ProxyServer proxyServer, File dataFolder) {
Optional<PluginContainer> floodgate = proxyServer.getPluginManager().getPlugin("floodgate"); Optional<PluginContainer> floodgate = proxyServer.getPluginManager().getPlugin("floodgate");
floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(dataFolder.toString(), floodgateKeyFile.isEmpty() ? floodgateKeyFile : "public-key.pem"), floodgate.get(), Paths.get("plugins/floodgate/")); floodgate.ifPresent(it -> floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(dataFolder.toString(), floodgateKeyFile.isEmpty() ? floodgateKeyFile : "public-key.pem"), it, Paths.get("plugins/floodgate/")));
} }
@Override @Override
@ -127,4 +145,7 @@ public class GeyserVelocityConfiguration implements GeyserConfiguration {
@JsonProperty("uuid") @JsonProperty("uuid")
private String uniqueId; private String uniqueId;
} }
@JsonProperty("config-version")
private int configVersion;
} }

View File

@ -0,0 +1,98 @@
/*
* 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.velocity;
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.InboundConnection;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.ServerPing;
import lombok.AllArgsConstructor;
import net.kyori.text.TextComponent;
import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
import org.geysermc.common.ping.GeyserPingInfo;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.util.Optional;
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() {
ProxyPingEvent event;
try {
event = server.getEventManager().fire(new ProxyPingEvent(FAKE_INBOUND_CONNECTION, ServerPing.builder()
.description(server.getConfiguration().getMotdComponent()).onlinePlayers(server.getPlayerCount())
.maximumPlayers(server.getConfiguration().getShowMaxPlayers()).build())).get();
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
GeyserPingInfo geyserPingInfo = new GeyserPingInfo(
LegacyComponentSerializer.INSTANCE.serialize(event.getPing().getDescription(), '§'),
event.getPing().getPlayers().orElseThrow(IllegalStateException::new).getOnline(),
event.getPing().getPlayers().orElseThrow(IllegalStateException::new).getMax()
);
event.getPing().getPlayers().get().getSample().forEach(player -> {
geyserPingInfo.addPlayer(player.getName());
});
return geyserPingInfo;
}
private static class GeyserInboundConnection implements InboundConnection {
private static final InetSocketAddress FAKE_REMOTE = new InetSocketAddress(Inet4Address.getLoopbackAddress(), 69);
@Override
public InetSocketAddress getRemoteAddress() {
return FAKE_REMOTE;
}
@Override
public Optional<InetSocketAddress> getVirtualHost() {
return Optional.empty();
}
@Override
public boolean isActive() {
return false;
}
@Override
public ProtocolVersion getProtocolVersion() {
return ProtocolVersion.MAXIMUM_VERSION;
}
}
}

View File

@ -35,8 +35,11 @@ import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import org.geysermc.common.PlatformType; import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.FileUtils;
import org.geysermc.platform.velocity.command.GeyserVelocityCommandExecutor; import org.geysermc.platform.velocity.command.GeyserVelocityCommandExecutor;
import org.geysermc.platform.velocity.command.GeyserVelocityCommandManager; import org.geysermc.platform.velocity.command.GeyserVelocityCommandManager;
@ -62,6 +65,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
private GeyserVelocityCommandManager geyserCommandManager; private GeyserVelocityCommandManager geyserCommandManager;
private GeyserVelocityConfiguration geyserConfig; private GeyserVelocityConfiguration geyserConfig;
private GeyserVelocityLogger geyserLogger; private GeyserVelocityLogger geyserLogger;
private IGeyserPingPassthrough geyserPingPassthrough;
private GeyserConnector connector; private GeyserConnector connector;
@ -83,13 +87,14 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
// Don't change the ip if its listening on all interfaces // Don't change the ip if its listening on all interfaces
// By default this should be 127.0.0.1 but may need to be changed in some circumstances // By default this should be 127.0.0.1 but may need to be changed in some circumstances
if (!javaAddr.getHostString().equals("0.0.0.0")) { if (!javaAddr.getHostString().equals("0.0.0.0") && !javaAddr.getHostString().equals("")) {
geyserConfig.getRemote().setAddress(javaAddr.getHostString()); geyserConfig.getRemote().setAddress(javaAddr.getHostString());
} }
geyserConfig.getRemote().setPort(javaAddr.getPort()); geyserConfig.getRemote().setPort(javaAddr.getPort());
this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode()); this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
geyserConfig.loadFloodgate(this, proxyServer, configDir); geyserConfig.loadFloodgate(this, proxyServer, configDir);
@ -97,6 +102,11 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
this.geyserCommandManager = new GeyserVelocityCommandManager(connector); this.geyserCommandManager = new GeyserVelocityCommandManager(connector);
this.commandManager.register(new GeyserVelocityCommandExecutor(connector), "geyser"); this.commandManager.register(new GeyserVelocityCommandExecutor(connector), "geyser");
if (geyserConfig.isLegacyPingPassthrough()) {
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(connector);
} else {
this.geyserPingPassthrough = new GeyserVelocityPingPassthrough(proxyServer);
}
} }
@Override @Override
@ -119,6 +129,11 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
return this.geyserCommandManager; return this.geyserCommandManager;
} }
@Override
public IGeyserPingPassthrough getGeyserPingPassthrough() {
return geyserPingPassthrough;
}
@Subscribe @Subscribe
public void onInit(ProxyInitializeEvent event) { public void onInit(ProxyInitializeEvent event) {
onEnable(); onEnable();

View File

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

View File

@ -43,7 +43,8 @@ public enum DeviceOS {
ORBIS("PS4"), ORBIS("PS4"),
NX("Switch"), NX("Switch"),
SWITCH("Switch"), SWITCH("Switch"),
XBOX_ONE("Xbox One"); XBOX_ONE("Xbox One"),
WIN_PHONE("Windows Phone");
private static final DeviceOS[] VALUES = values(); private static final DeviceOS[] VALUES = values();

View File

@ -98,14 +98,8 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.steveice10</groupId> <groupId>com.github.steveice10</groupId>
<artifactId>opennbt</artifactId> <artifactId>mcprotocollib</artifactId>
<version>1.4-SNAPSHOT</version> <version>4c315aa206</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.steveice10</groupId>
<artifactId>packetlib</artifactId>
<version>1.5-SNAPSHOT</version>
<scope>compile</scope> <scope>compile</scope>
<exclusions> <exclusions>
<exclusion> <exclusion>
@ -115,31 +109,11 @@
</exclusions> </exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.steveice10</groupId> <groupId>io.netty</groupId>
<artifactId>mcauthlib</artifactId> <artifactId>netty-resolver-dns</artifactId>
<version>1.3-SNAPSHOT</version> <version>4.1.43.Final</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>com.github.steveice10</groupId>
<artifactId>mcprotocollib</artifactId>
<version>1.15.2-1-SNAPSHOT</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>com.github.steveice10</groupId>
<artifactId>opennbt</artifactId>
</exclusion>
<exclusion>
<groupId>com.github.steveice10</groupId>
<artifactId>packetlib</artifactId>
</exclusion>
<exclusion>
<groupId>com.github.steveice10</groupId>
<artifactId>mcauthlib</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>org.reflections</groupId> <groupId>org.reflections</groupId>
<artifactId>reflections</artifactId> <artifactId>reflections</artifactId>

View File

@ -31,13 +31,24 @@ import java.util.Map;
public interface GeyserConfiguration { public interface GeyserConfiguration {
// Modify this when you update the config
int CURRENT_CONFIG_VERSION = 3;
IBedrockConfiguration getBedrock(); IBedrockConfiguration getBedrock();
IRemoteConfiguration getRemote(); IRemoteConfiguration getRemote();
Map<String, ? extends IUserAuthenticationInfo> getUserAuths(); Map<String, ? extends IUserAuthenticationInfo> getUserAuths();
boolean isPingPassthrough(); boolean isCommandSuggestions();
boolean isPassthroughMotd();
boolean isPassthroughPlayerCounts();
boolean isLegacyPingPassthrough();
int getPingPassthroughInterval();
int getMaxPlayers(); int getMaxPlayers();
@ -47,10 +58,14 @@ public interface GeyserConfiguration {
boolean isAllowThirdPartyCapes(); boolean isAllowThirdPartyCapes();
boolean isAllowThirdPartyEars();
String getDefaultLocale(); String getDefaultLocale();
Path getFloodgateKeyFile(); Path getFloodgateKeyFile();
boolean isAboveBedrockNetherBuilding();
boolean isCacheChunks(); boolean isCacheChunks();
IMetricsInfo getMetrics(); IMetricsInfo getMetrics();
@ -87,4 +102,14 @@ public interface GeyserConfiguration {
String getUniqueId(); String getUniqueId();
} }
int getConfigVersion();
static void checkGeyserConfiguration(GeyserConfiguration geyserConfig, GeyserLogger geyserLogger) {
if (geyserConfig.getConfigVersion() < CURRENT_CONFIG_VERSION) {
geyserLogger.warning("Your Geyser config is out of date! Please regenerate your config when possible.");
} else if (geyserConfig.getConfigVersion() > CURRENT_CONFIG_VERSION) {
geyserLogger.warning("Your Geyser config is too new! Errors may occur.");
}
}
} }

View File

@ -25,6 +25,8 @@
package org.geysermc.connector; package org.geysermc.connector;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
import com.nukkitx.protocol.bedrock.BedrockServer; import com.nukkitx.protocol.bedrock.BedrockServer;
import com.nukkitx.protocol.bedrock.v390.Bedrock_v390; import com.nukkitx.protocol.bedrock.v390.Bedrock_v390;
@ -37,10 +39,20 @@ import org.geysermc.connector.metrics.Metrics;
import org.geysermc.connector.network.ConnectorServerEventHandler; import org.geysermc.connector.network.ConnectorServerEventHandler;
import org.geysermc.connector.network.remote.RemoteServer; import org.geysermc.connector.network.remote.RemoteServer;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.Translators; 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.item.ItemRegistry;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.network.translators.sound.SoundHandlerRegistry;
import org.geysermc.connector.network.translators.world.WorldManager; import org.geysermc.connector.network.translators.world.WorldManager;
import org.geysermc.connector.thread.PingPassthroughThread; import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.Toolbox; import org.geysermc.connector.network.translators.effect.EffectRegistry;
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
import org.geysermc.connector.utils.DimensionUtils;
import org.geysermc.connector.utils.DockerCheck;
import org.geysermc.connector.utils.LocaleUtils;
import org.geysermc.connector.network.translators.sound.SoundRegistry;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.text.DecimalFormat; import java.text.DecimalFormat;
@ -54,6 +66,8 @@ import java.util.concurrent.TimeUnit;
@Getter @Getter
public class GeyserConnector { public class GeyserConnector {
public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
public static final BedrockPacketCodec BEDROCK_PACKET_CODEC = Bedrock_v390.V390_CODEC; public static final BedrockPacketCodec BEDROCK_PACKET_CODEC = Bedrock_v390.V390_CODEC;
public static final String NAME = "Geyser"; public static final String NAME = "Geyser";
@ -69,7 +83,6 @@ public class GeyserConnector {
private boolean shuttingDown = false; private boolean shuttingDown = false;
private final ScheduledExecutorService generalThreadPool; private final ScheduledExecutorService generalThreadPool;
private PingPassthroughThread passthroughThread;
private BedrockServer bedrockServer; private BedrockServer bedrockServer;
private PlatformType platformType; private PlatformType platformType;
@ -99,15 +112,29 @@ public class GeyserConnector {
logger.setDebug(config.isDebugMode()); logger.setDebug(config.isDebugMode());
Toolbox.init(); PacketTranslatorRegistry.init();
Translators.start();
/* Initialize translators and registries */
BiomeTranslator.init();
BlockTranslator.init();
BlockEntityTranslator.init();
EffectRegistry.init();
EntityIdentifierRegistry.init();
ItemRegistry.init();
ItemTranslator.init();
LocaleUtils.init();
SoundRegistry.init();
SoundHandlerRegistry.init();
if (platformType != PlatformType.STANDALONE) {
DockerCheck.check(bootstrap);
}
remoteServer = new RemoteServer(config.getRemote().getAddress(), config.getRemote().getPort()); remoteServer = new RemoteServer(config.getRemote().getAddress(), config.getRemote().getPort());
authType = AuthType.getByName(config.getRemote().getAuthType()); authType = AuthType.getByName(config.getRemote().getAuthType());
passthroughThread = new PingPassthroughThread(this); if (config.isAboveBedrockNetherBuilding())
if (config.isPingPassthrough()) DimensionUtils.changeBedrockNetherId(); // Apply End dimension ID workaround to Nether
generalThreadPool.scheduleAtFixedRate(passthroughThread, 1, 1, TimeUnit.SECONDS);
bedrockServer = new BedrockServer(new InetSocketAddress(config.getBedrock().getAddress(), config.getBedrock().getPort())); bedrockServer = new BedrockServer(new InetSocketAddress(config.getBedrock().getAddress(), config.getBedrock().getPort()));
bedrockServer.setHandler(new ConnectorServerEventHandler(this)); bedrockServer.setHandler(new ConnectorServerEventHandler(this));

View File

@ -26,6 +26,7 @@
package org.geysermc.connector.bootstrap; package org.geysermc.connector.bootstrap;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import org.geysermc.connector.GeyserConfiguration; import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.GeyserLogger; import org.geysermc.connector.GeyserLogger;
import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.command.CommandManager;
@ -67,6 +68,13 @@ public interface GeyserBootstrap {
*/ */
CommandManager getGeyserCommandManager(); CommandManager getGeyserCommandManager();
/**
* Returns the current PingPassthrough manager
*
* @return The current PingPassthrough manager
*/
IGeyserPingPassthrough getGeyserPingPassthrough();
/** /**
* Returns the current WorldManager * Returns the current WorldManager
* *

View File

@ -55,7 +55,17 @@ public class OffhandCommand extends GeyserCommand {
GeyserSession session = (GeyserSession) sender; GeyserSession session = (GeyserSession) sender;
ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.SWAP_HANDS, new Position(0,0,0), ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.SWAP_HANDS, new Position(0,0,0),
BlockFace.DOWN); BlockFace.DOWN);
session.getDownstream().getSession().send(releaseItemPacket); session.sendDownstreamPacket(releaseItemPacket);
return;
}
// Needed for Bukkit - sender is not an instance of GeyserSession
for (GeyserSession session : connector.getPlayers().values()) {
if (sender.getName().equals(session.getPlayerEntity().getUsername())) {
ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.SWAP_HANDS, new Position(0,0,0),
BlockFace.DOWN);
session.sendDownstreamPacket(releaseItemPacket);
break;
}
} }
} }
} }

View File

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

View File

@ -0,0 +1,61 @@
/*
* 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.github.steveice10.mc.protocol.data.game.world.particle.Particle;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.effect.EffectRegistry;
public class AreaEffectCloudEntity extends Entity {
public AreaEffectCloudEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
// Without this the cloud doesn't appear,
metadata.put(EntityData.AREA_EFFECT_CLOUD_DURATION, 600);
// This disabled client side shrink of the cloud
metadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS_PER_TICK, 0.0f);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7) {
metadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS, (float) entityMetadata.getValue());
metadata.put(EntityData.BOUNDING_BOX_WIDTH, 2.0f * (float) entityMetadata.getValue());
} else if (entityMetadata.getId() == 10) {
Particle particle = (Particle) entityMetadata.getValue();
metadata.put(EntityData.AREA_EFFECT_CLOUD_PARTICLE_ID, EffectRegistry.getParticleString(particle.getType()));
} else if (entityMetadata.getId() == 8) {
metadata.put(EntityData.POTION_COLOR, entityMetadata.getValue());
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View File

@ -0,0 +1,138 @@
/*
* 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 com.nukkitx.protocol.bedrock.data.EntityData;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.concurrent.TimeUnit;
public class BoatEntity extends Entity {
private boolean isPaddlingLeft;
private float paddleTimeLeft;
private boolean isPaddlingRight;
private float paddleTimeRight;
// Looks too fast and too choppy with 0.1f, which is how I believe the Microsoftian client handles it
private final float ROWING_SPEED = 0.05f;
public BoatEntity(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(0, 0, 90));
}
@Override
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
// Rotation is basically only called when entering/exiting a boat.
// We don't include the rotation (y) as it causes the boat to appear sideways
super.moveAbsolute(session, position.add(0d, this.entityType.getOffset(), 0d), Vector3f.from(0, 0, rotation.getZ() + 90), isOnGround, teleported);
}
@Override
public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) {
super.moveRelative(session, relX, relY, relZ, Vector3f.from(0, 0, rotation.getZ()), isOnGround);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
// Time since last hit
if (entityMetadata.getId() == 7) {
metadata.put(EntityData.HURT_TIME, entityMetadata.getValue());
}
// Rocking direction
if (entityMetadata.getId() == 8) {
metadata.put(EntityData.HURT_DIRECTION, entityMetadata.getValue());
}
// 'Health' in Bedrock, damage taken in Java
if (entityMetadata.getId() == 9) {
// Not exactly health but it makes motion in Bedrock
metadata.put(EntityData.HEALTH, 40 - ((int) (float) entityMetadata.getValue()));
}
if (entityMetadata.getId() == 10) {
metadata.put(EntityData.VARIANT, entityMetadata.getValue());
} else if (entityMetadata.getId() == 11) {
isPaddlingLeft = (boolean) entityMetadata.getValue();
if (!isPaddlingLeft) {
metadata.put(EntityData.PADDLE_TIME_LEFT, 0f);
}
else {
// Java sends simply "true" and "false" (is_paddling_left), Bedrock keeps sending packets as you're rowing
// This is an asynchronous method that emulates Bedrock rowing until "false" is sent.
paddleTimeLeft = 0f;
session.getConnector().getGeneralThreadPool().execute(() ->
updateLeftPaddle(session, entityMetadata)
);
}
}
else if (entityMetadata.getId() == 12) {
isPaddlingRight = (boolean) entityMetadata.getValue();
if (!isPaddlingRight) {
metadata.put(EntityData.PADDLE_TIME_RIGHT, 0f);
} else {
paddleTimeRight = 0f;
session.getConnector().getGeneralThreadPool().execute(() ->
updateRightPaddle(session, entityMetadata)
);
}
} else if (entityMetadata.getId() == 13) {
// Possibly - I don't think this does anything?
metadata.put(EntityData.BOAT_BUBBLE_TIME, entityMetadata.getValue());
}
super.updateBedrockMetadata(entityMetadata, session);
}
public void updateLeftPaddle(GeyserSession session, EntityMetadata entityMetadata) {
if (isPaddlingLeft) {
paddleTimeLeft += ROWING_SPEED;
metadata.put(EntityData.PADDLE_TIME_LEFT, paddleTimeLeft);
super.updateBedrockMetadata(entityMetadata, session);
session.getConnector().getGeneralThreadPool().schedule(() ->
updateLeftPaddle(session, entityMetadata),
100,
TimeUnit.MILLISECONDS
);
}}
public void updateRightPaddle(GeyserSession session, EntityMetadata entityMetadata) {
if (isPaddlingRight) {
paddleTimeRight += ROWING_SPEED;
metadata.put(EntityData.PADDLE_TIME_RIGHT, paddleTimeRight);
super.updateBedrockMetadata(entityMetadata, session);
session.getConnector().getGeneralThreadPool().schedule(() ->
updateRightPaddle(session, entityMetadata),
100,
TimeUnit.MILLISECONDS
);
}}
}

View File

@ -0,0 +1,88 @@
/*
* 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 com.nukkitx.protocol.bedrock.data.EntityData;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
/**
* This class is used as a base for minecarts with a default block to display like furnaces and spawners
*/
public class DefaultBlockMinecartEntity extends MinecartEntity {
public int customBlock = 0;
public int customBlockOffset = 0;
public boolean showCustomBlock = false;
public DefaultBlockMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
updateDefaultBlockMetadata();
metadata.put(EntityData.HAS_DISPLAY, (byte) 1);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
// Custom block
if (entityMetadata.getId() == 10) {
customBlock = (int) entityMetadata.getValue();
if (showCustomBlock) {
metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(customBlock));
}
}
// Custom block offset
if (entityMetadata.getId() == 11) {
customBlockOffset = (int) entityMetadata.getValue();
if (showCustomBlock) {
metadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset);
}
}
// If the custom block should be enabled
if (entityMetadata.getId() == 12) {
if ((boolean) entityMetadata.getValue()) {
showCustomBlock = true;
metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(customBlock));
metadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset);
} else {
showCustomBlock = false;
updateDefaultBlockMetadata();
}
}
super.updateBedrockMetadata(entityMetadata, session);
}
public void updateDefaultBlockMetadata() { }
}

View File

@ -31,7 +31,6 @@ import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i; import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.EntityData; import com.nukkitx.protocol.bedrock.data.EntityData;
import com.nukkitx.protocol.bedrock.data.EntityFlag; import com.nukkitx.protocol.bedrock.data.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;

View File

@ -27,32 +27,40 @@ 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.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType; import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; 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.entity.player.PlayerAction;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.github.steveice10.mc.protocol.data.message.TextMessage; import com.github.steveice10.mc.protocol.data.message.TextMessage;
import com.github.steveice10.mc.protocol.data.message.TranslationMessage;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.*; import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.EntityData;
import com.nukkitx.protocol.bedrock.data.EntityDataMap;
import com.nukkitx.protocol.bedrock.data.EntityFlag;
import com.nukkitx.protocol.bedrock.data.EntityFlags;
import com.nukkitx.protocol.bedrock.packet.*; import com.nukkitx.protocol.bedrock.packet.*;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.geysermc.connector.entity.attribute.Attribute; import org.geysermc.connector.entity.attribute.Attribute;
import org.geysermc.connector.entity.attribute.AttributeType; import org.geysermc.connector.entity.attribute.AttributeType;
import org.geysermc.connector.entity.living.ArmorStandEntity;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.utils.AttributeUtils; import org.geysermc.connector.utils.AttributeUtils;
import org.geysermc.connector.utils.ChunkUtils;
import org.geysermc.connector.utils.MessageUtils; import org.geysermc.connector.utils.MessageUtils;
import java.util.*; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Getter @Getter
@Setter @Setter
@ -76,7 +84,7 @@ public class Entity {
protected boolean valid; protected boolean valid;
protected LongSet passengers = new LongOpenHashSet(); protected LongOpenHashSet passengers = new LongOpenHashSet();
protected Map<AttributeType, Attribute> attributes = new HashMap<>(); protected Map<AttributeType, Attribute> attributes = new HashMap<>();
protected EntityDataMap metadata = new EntityDataMap(); protected EntityDataMap metadata = new EntityDataMap();
@ -94,7 +102,7 @@ public class Entity {
metadata.put(EntityData.SCALE, 1f); metadata.put(EntityData.SCALE, 1f);
metadata.put(EntityData.COLOR, 0); metadata.put(EntityData.COLOR, 0);
metadata.put(EntityData.MAX_AIR, (short) 400); metadata.put(EntityData.MAX_AIR, (short) 300);
metadata.put(EntityData.AIR, (short) 0); metadata.put(EntityData.AIR, (short) 0);
metadata.put(EntityData.LEAD_HOLDER_EID, -1L); metadata.put(EntityData.LEAD_HOLDER_EID, -1L);
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, entityType.getHeight()); metadata.put(EntityData.BOUNDING_BOX_HEIGHT, entityType.getHeight());
@ -119,7 +127,7 @@ public class Entity {
addEntityPacket.getMetadata().putAll(metadata); addEntityPacket.getMetadata().putAll(metadata);
valid = true; valid = true;
session.getUpstream().sendPacket(addEntityPacket); session.sendUpstreamPacket(addEntityPacket);
session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")"); session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
} }
@ -135,7 +143,7 @@ public class Entity {
RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket(); RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket();
removeEntityPacket.setUniqueEntityId(geyserId); removeEntityPacket.setUniqueEntityId(geyserId);
session.getUpstream().sendPacket(removeEntityPacket); session.sendUpstreamPacket(removeEntityPacket);
valid = false; valid = false;
return true; return true;
@ -156,7 +164,7 @@ public class Entity {
moveEntityPacket.setOnGround(isOnGround); moveEntityPacket.setOnGround(isOnGround);
moveEntityPacket.setTeleported(false); moveEntityPacket.setTeleported(false);
session.getUpstream().sendPacket(moveEntityPacket); session.sendUpstreamPacket(moveEntityPacket);
} }
public void moveAbsolute(GeyserSession session, Vector3f position, float yaw, float pitch, boolean isOnGround, boolean teleported) { public void moveAbsolute(GeyserSession session, Vector3f position, float yaw, float pitch, boolean isOnGround, boolean teleported) {
@ -174,7 +182,7 @@ public class Entity {
moveEntityPacket.setOnGround(isOnGround); moveEntityPacket.setOnGround(isOnGround);
moveEntityPacket.setTeleported(teleported); moveEntityPacket.setTeleported(teleported);
session.getUpstream().sendPacket(moveEntityPacket); session.sendUpstreamPacket(moveEntityPacket);
} }
public void updateBedrockAttributes(GeyserSession session) { public void updateBedrockAttributes(GeyserSession session) {
@ -191,7 +199,7 @@ public class Entity {
UpdateAttributesPacket updateAttributesPacket = new UpdateAttributesPacket(); UpdateAttributesPacket updateAttributesPacket = new UpdateAttributesPacket();
updateAttributesPacket.setRuntimeEntityId(geyserId); updateAttributesPacket.setRuntimeEntityId(geyserId);
updateAttributesPacket.setAttributes(attributes); updateAttributesPacket.setAttributes(attributes);
session.getUpstream().sendPacket(updateAttributesPacket); session.sendUpstreamPacket(updateAttributesPacket);
} }
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
@ -205,38 +213,55 @@ public class Entity {
metadata.getFlags().setFlag(EntityFlag.SWIMMING, (xd & 0x10) == 0x10); metadata.getFlags().setFlag(EntityFlag.SWIMMING, (xd & 0x10) == 0x10);
metadata.getFlags().setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80); metadata.getFlags().setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80);
if ((xd & 0x20) == 0x20) {
// Armour stands are handled in their own class
if (!this.is(ArmorStandEntity.class)) {
metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true);
}
} else {
metadata.getFlags().setFlag(EntityFlag.INVISIBLE, false);
}
// Shield code // Shield code
if (session.getPlayerEntity().getEntityId() == entityId && metadata.getFlags().getFlag(EntityFlag.SNEAKING)) { if (session.getPlayerEntity().getEntityId() == entityId && metadata.getFlags().getFlag(EntityFlag.SNEAKING)) {
if ((session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() == ItemTranslator.SHIELD) || if ((session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() == ItemRegistry.SHIELD) ||
(session.getInventoryCache().getPlayerInventory().getItem(45) != null && session.getInventoryCache().getPlayerInventory().getItem(45).getId() == ItemTranslator.SHIELD)) { (session.getInventoryCache().getPlayerInventory().getItem(45) != null && session.getInventoryCache().getPlayerInventory().getItem(45).getId() == ItemRegistry.SHIELD)) {
ClientPlayerUseItemPacket useItemPacket; ClientPlayerUseItemPacket useItemPacket;
metadata.getFlags().setFlag(EntityFlag.BLOCKING, true); metadata.getFlags().setFlag(EntityFlag.BLOCKING, true);
if (session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() == ItemTranslator.SHIELD) { if (session.getInventory().getItemInHand() != null && session.getInventory().getItemInHand().getId() == ItemRegistry.SHIELD) {
useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND); useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
} }
// Else we just assume it's the offhand, to simplify logic and to assure the packet gets sent // Else we just assume it's the offhand, to simplify logic and to assure the packet gets sent
else { else {
useItemPacket = new ClientPlayerUseItemPacket(Hand.OFF_HAND); useItemPacket = new ClientPlayerUseItemPacket(Hand.OFF_HAND);
} }
session.getDownstream().getSession().send(useItemPacket); session.sendDownstreamPacket(useItemPacket);
} }
} else if (session.getPlayerEntity().getEntityId() == entityId && !metadata.getFlags().getFlag(EntityFlag.SNEAKING) && metadata.getFlags().getFlag(EntityFlag.BLOCKING)) { } else if (session.getPlayerEntity().getEntityId() == entityId && !metadata.getFlags().getFlag(EntityFlag.SNEAKING) && metadata.getFlags().getFlag(EntityFlag.BLOCKING)) {
metadata.getFlags().setFlag(EntityFlag.BLOCKING, false); metadata.getFlags().setFlag(EntityFlag.BLOCKING, false);
metadata.getFlags().setFlag(EntityFlag.DISABLE_BLOCKING, true); metadata.getFlags().setFlag(EntityFlag.DISABLE_BLOCKING, true);
ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, new Position(0,0,0), BlockFace.DOWN); ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, new Position(0, 0, 0), BlockFace.DOWN);
session.getDownstream().getSession().send(releaseItemPacket); session.sendDownstreamPacket(releaseItemPacket);
} }
// metadata.getFlags().setFlag(EntityFlag.INVISIBLE, (xd & 0x20) == 0x20); }
if ((xd & 0x20) == 0x20) break;
metadata.put(EntityData.SCALE, 0.0f); case 1: // Air/bubbles
else if ((int) entityMetadata.getValue() == 300) {
metadata.put(EntityData.SCALE, scale); metadata.put(EntityData.AIR, (short) 0); // Otherwise the bubble counter remains in the UI
} else {
metadata.put(EntityData.AIR, (short) (int) entityMetadata.getValue());
} }
break; break;
case 2: // custom name case 2: // custom name
TextMessage name = (TextMessage) entityMetadata.getValue(); if (entityMetadata.getValue() instanceof TextMessage) {
if (name != null) TextMessage name = (TextMessage) entityMetadata.getValue();
metadata.put(EntityData.NAMETAG, MessageUtils.getBedrockMessage(name)); if (name != null)
metadata.put(EntityData.NAMETAG, MessageUtils.getBedrockMessage(name));
} else if (entityMetadata.getValue() instanceof TranslationMessage) {
TranslationMessage message = (TranslationMessage) entityMetadata.getValue();
if (message != null)
metadata.put(EntityData.NAMETAG, MessageUtils.getTranslatedBedrockMessage(message, session.getClientData().getLanguageCode(), true));
}
break; break;
case 3: // is custom name visible case 3: // is custom name visible
if (!this.is(PlayerEntity.class)) if (!this.is(PlayerEntity.class))
@ -248,6 +273,32 @@ public class Entity {
case 5: // no gravity case 5: // no gravity
metadata.getFlags().setFlag(EntityFlag.HAS_GRAVITY, !(boolean) entityMetadata.getValue()); metadata.getFlags().setFlag(EntityFlag.HAS_GRAVITY, !(boolean) entityMetadata.getValue());
break; break;
case 6: // Pose change
if (entityMetadata.getValue().equals(Pose.SLEEPING)) {
metadata.getFlags().setFlag(EntityFlag.SLEEPING, true);
// Has to be a byte or it does not work
metadata.put(EntityData.CAN_START_SLEEP, (byte) 2);
if (entityId == session.getPlayerEntity().getEntityId()) {
Vector3i lastInteractionPos = session.getLastInteractionPosition();
metadata.put(EntityData.BED_RESPAWN_POS, lastInteractionPos);
if (session.getConnector().getConfig().isCacheChunks()) {
BlockState 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_RESPAWN_POS, 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)) {
metadata.getFlags().setFlag(EntityFlag.SLEEPING, false);
metadata.put(EntityData.BOUNDING_BOX_WIDTH, getEntityType().getWidth());
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, getEntityType().getHeight());
metadata.put(EntityData.CAN_START_SLEEP, (byte) 0);
}
break;
case 7: // blocking case 7: // blocking
if (entityMetadata.getType() == MetadataType.BYTE) { if (entityMetadata.getType() == MetadataType.BYTE) {
byte xd = (byte) entityMetadata.getValue(); byte xd = (byte) entityMetadata.getValue();
@ -265,11 +316,12 @@ public class Entity {
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket(); SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
entityDataPacket.setRuntimeEntityId(geyserId); entityDataPacket.setRuntimeEntityId(geyserId);
entityDataPacket.getMetadata().putAll(metadata); entityDataPacket.getMetadata().putAll(metadata);
session.getUpstream().sendPacket(entityDataPacket); session.sendUpstreamPacket(entityDataPacket);
} }
/** /**
* x = Pitch, y = HeadYaw, z = Yaw * x = Pitch, y = HeadYaw, z = Yaw
*
* @return the bedrock rotation * @return the bedrock rotation
*/ */
public Vector3f getBedrockRotation() { public Vector3f getBedrockRotation() {

View File

@ -27,13 +27,9 @@ package org.geysermc.connector.entity;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData; import com.nukkitx.protocol.bedrock.data.EntityData;
import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class ExpOrbEntity extends Entity { public class ExpOrbEntity extends Entity {
private int amount; private int amount;

View File

@ -0,0 +1,139 @@
/*
* 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.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.nbt.CompoundTagBuilder;
import com.nukkitx.protocol.bedrock.data.EntityData;
import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.FireworkColor;
import org.geysermc.connector.utils.MathUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.OptionalInt;
public class FireworkEntity extends Entity {
public FireworkEntity(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() == 7) {
ItemStack item = (ItemStack) entityMetadata.getValue();
CompoundTag tag = item.getNbt();
if (tag == null) {
return;
}
CompoundTag fireworks = tag.get("Fireworks");
CompoundTagBuilder fireworksBuilder = CompoundTagBuilder.builder();
if (fireworks.get("Flight") != null) {
fireworksBuilder.byteTag("Flight", MathUtils.convertByte(fireworks.get("Flight").getValue()));
}
List<com.nukkitx.nbt.tag.CompoundTag> explosions = new ArrayList<>();
if (fireworks.get("Explosions") != null) {
for (Tag effect : ((ListTag) fireworks.get("Explosions")).getValue()) {
CompoundTag effectData = (CompoundTag) effect;
CompoundTagBuilder effectBuilder = CompoundTagBuilder.builder();
if (effectData.get("Type") != null) {
effectBuilder.byteTag("FireworkType", MathUtils.convertByte(effectData.get("Type").getValue()));
}
if (effectData.get("Colors") != null) {
int[] oldColors = (int[]) effectData.get("Colors").getValue();
byte[] colors = new byte[oldColors.length];
int i = 0;
for (int color : oldColors) {
colors[i++] = FireworkColor.fromJavaID(color).getBedrockID();
}
effectBuilder.byteArrayTag("FireworkColor", colors);
}
if (effectData.get("FadeColors") != null) {
int[] oldColors = (int[]) effectData.get("FadeColors").getValue();
byte[] colors = new byte[oldColors.length];
int i = 0;
for (int color : oldColors) {
colors[i++] = FireworkColor.fromJavaID(color).getBedrockID();
}
effectBuilder.byteArrayTag("FireworkFade", colors);
}
if (effectData.get("Trail") != null) {
effectBuilder.byteTag("FireworkTrail", MathUtils.convertByte(effectData.get("Trail").getValue()));
}
if (effectData.get("Flicker") != null) {
effectBuilder.byteTag("FireworkFlicker", MathUtils.convertByte(effectData.get("Flicker").getValue()));
}
explosions.add(effectBuilder.buildRootTag());
}
}
fireworksBuilder.tag(new com.nukkitx.nbt.tag.ListTag<>("Explosions", com.nukkitx.nbt.tag.CompoundTag.class, explosions));
metadata.put(EntityData.DISPLAY_ITEM, CompoundTagBuilder.builder().tag(fireworksBuilder.build("Fireworks")).buildRootTag());
} else if (entityMetadata.getId() == 8 && !entityMetadata.getValue().equals(OptionalInt.empty()) && ((OptionalInt) entityMetadata.getValue()).getAsInt() == session.getPlayerEntity().getEntityId()) {
//Checks if the firework has an entity ID (used when a player is gliding) and checks to make sure the player that is gliding is the one getting sent the packet or else every player near the gliding player will boost too.
PlayerEntity entity = session.getPlayerEntity();
float yaw = entity.getRotation().getX();
float pitch = entity.getRotation().getY();
//Uses math from NukkitX
entity.setMotion(Vector3f.from(
-Math.sin(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2,
-Math.sin(Math.toRadians(pitch)) * 2,
Math.cos(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2));
//Need to update the EntityMotionPacket or else the player won't boost
SetEntityMotionPacket entityMotionPacket = new SetEntityMotionPacket();
entityMotionPacket.setRuntimeEntityId(entity.getGeyserId());
entityMotionPacket.setMotion(entity.getMotion());
session.sendUpstreamPacket(entityMotionPacket);
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View File

@ -25,13 +25,44 @@
package org.geysermc.connector.entity; 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.object.ProjectileData;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.packet.AddEntityPacket; import com.nukkitx.protocol.bedrock.data.EntityData;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
public class FishingHookEntity extends Entity { public class FishingHookEntity extends Entity {
public FishingHookEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { public FishingHookEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, ProjectileData data) {
super(entityId, geyserId, entityType, position, motion, rotation); super(entityId, geyserId, entityType, position, motion, rotation);
for (GeyserSession session : GeyserConnector.getInstance().getPlayers().values()) {
Entity entity = session.getEntityCache().getEntityByJavaId(data.getOwnerId());
if (entity == null && session.getPlayerEntity().getEntityId() == data.getOwnerId()) {
entity = session.getPlayerEntity();
}
if (entity != null) {
this.metadata.put(EntityData.OWNER_EID, entity.getGeyserId());
return;
}
}
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7) {
Entity entity = session.getEntityCache().getEntityByJavaId((Integer) entityMetadata.getValue() - 1);
if (entity == null && session.getPlayerEntity().getEntityId() == (Integer) entityMetadata.getValue() - 1) {
entity = session.getPlayerEntity();
}
if (entity != null) {
metadata.put(EntityData.TARGET_EID, entity.getGeyserId());
}
}
super.updateBedrockMetadata(entityMetadata, session);
} }
} }

View File

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

View File

@ -31,7 +31,7 @@ import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.packet.AddItemEntityPacket; import com.nukkitx.protocol.bedrock.packet.AddItemEntityPacket;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.Translators; import org.geysermc.connector.network.translators.item.ItemTranslator;
public class ItemEntity extends Entity { public class ItemEntity extends Entity {
@ -49,8 +49,8 @@ public class ItemEntity extends Entity {
itemPacket.setUniqueEntityId(geyserId); itemPacket.setUniqueEntityId(geyserId);
itemPacket.setFromFishing(false); itemPacket.setFromFishing(false);
itemPacket.getMetadata().putAll(metadata); itemPacket.getMetadata().putAll(metadata);
itemPacket.setItemInHand(Translators.getItemTranslator().translateToBedrock(session, (ItemStack) entityMetadata.getValue())); itemPacket.setItemInHand(ItemTranslator.translateToBedrock(session, (ItemStack) entityMetadata.getValue()));
session.getUpstream().sendPacket(itemPacket); session.sendUpstreamPacket(itemPacket);
} }
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);

View File

@ -27,7 +27,7 @@ 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.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.entity.type.object.HangingDirection; import com.github.steveice10.mc.protocol.data.game.entity.object.HangingDirection;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i; import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.nbt.CompoundTagBuilder; import com.nukkitx.nbt.CompoundTagBuilder;
@ -38,10 +38,10 @@ import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.Translators;
import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.Toolbox;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -87,20 +87,23 @@ public class ItemFrameEntity extends Entity {
@Override @Override
public void spawnEntity(GeyserSession session) { public void spawnEntity(GeyserSession session) {
session.getItemFrameCache().put(bedrockPosition, entityId); session.getItemFrameCache().put(bedrockPosition, entityId);
updateBlock(session); // Delay is required, or else loading in frames on chunk load is sketchy at best
session.getConnector().getGeneralThreadPool().schedule(() -> {
updateBlock(session);
session.getConnector().getLogger().debug("Spawned item frame at location " + bedrockPosition + " with java id " + entityId);
}, 500, TimeUnit.MILLISECONDS);
valid = true; valid = true;
session.getConnector().getLogger().debug("Spawned item frame at location " + bedrockPosition + " with java id " + entityId);
} }
@Override @Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7 && entityMetadata.getValue() != null) { if (entityMetadata.getId() == 7 && entityMetadata.getValue() != null) {
ItemData itemData = Translators.getItemTranslator().translateToBedrock(session, (ItemStack) entityMetadata.getValue()); ItemData itemData = ItemTranslator.translateToBedrock(session, (ItemStack) entityMetadata.getValue());
ItemEntry itemEntry = Translators.getItemTranslator().getItem((ItemStack) entityMetadata.getValue()); ItemEntry itemEntry = ItemRegistry.getItem((ItemStack) entityMetadata.getValue());
CompoundTagBuilder builder = CompoundTag.builder(); CompoundTagBuilder builder = CompoundTag.builder();
String blockName = ""; String blockName = "";
for (StartGamePacket.ItemEntry startGamePacketItemEntry: Toolbox.ITEMS) { for (StartGamePacket.ItemEntry startGamePacketItemEntry : ItemRegistry.ITEMS) {
if (startGamePacketItemEntry.getId() == (short) itemEntry.getBedrockId()) { if (startGamePacketItemEntry.getId() == (short) itemEntry.getBedrockId()) {
blockName = startGamePacketItemEntry.getIdentifier(); blockName = startGamePacketItemEntry.getIdentifier();
break; break;
@ -149,7 +152,7 @@ public class ItemFrameEntity extends Entity {
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NONE); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NONE);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
session.getUpstream().sendPacket(updateBlockPacket); session.sendUpstreamPacket(updateBlockPacket);
session.getItemFrameCache().remove(position, entityId); session.getItemFrameCache().remove(position, entityId);
valid = false; valid = false;
return true; return true;
@ -170,27 +173,24 @@ public class ItemFrameEntity extends Entity {
* @param session GeyserSession. * @param session GeyserSession.
*/ */
public void updateBlock(GeyserSession session) { public void updateBlock(GeyserSession session) {
// Delay is required, or else loading in frames on chunk load is sketchy at best UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
session.getConnector().getGeneralThreadPool().schedule(() -> { updateBlockPacket.setDataLayer(0);
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); updateBlockPacket.setBlockPosition(bedrockPosition);
updateBlockPacket.setDataLayer(0); updateBlockPacket.setRuntimeId(bedrockRuntimeId);
updateBlockPacket.setBlockPosition(bedrockPosition); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY);
updateBlockPacket.setRuntimeId(bedrockRuntimeId); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NONE);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NONE); session.sendUpstreamPacket(updateBlockPacket);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
session.getUpstream().sendPacket(updateBlockPacket);
BlockEntityDataPacket blockEntityDataPacket = new BlockEntityDataPacket(); BlockEntityDataPacket blockEntityDataPacket = new BlockEntityDataPacket();
blockEntityDataPacket.setBlockPosition(bedrockPosition); blockEntityDataPacket.setBlockPosition(bedrockPosition);
if (cachedTag != null) { if (cachedTag != null) {
blockEntityDataPacket.setData(cachedTag); blockEntityDataPacket.setData(cachedTag);
} else { } else {
blockEntityDataPacket.setData(getDefaultTag()); blockEntityDataPacket.setData(getDefaultTag());
} }
session.getUpstream().sendPacket(blockEntityDataPacket); session.sendUpstreamPacket(blockEntityDataPacket);
}, 500, TimeUnit.MILLISECONDS);
} }
/** /**

View File

@ -0,0 +1,39 @@
/*
* 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.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.type.EntityType;
public class LeashKnotEntity extends Entity {
public LeashKnotEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
// Position is incorrect by default
super(entityId, geyserId, entityType, position.add(0.5f, 0.25f, 0.5f), motion, rotation);
}
}

View File

@ -96,8 +96,8 @@ public class LivingEntity extends Entity {
offHandPacket.setInventorySlot(0); offHandPacket.setInventorySlot(0);
offHandPacket.setContainerId(ContainerId.OFFHAND); offHandPacket.setContainerId(ContainerId.OFFHAND);
session.getUpstream().sendPacket(armorEquipmentPacket); session.sendUpstreamPacket(armorEquipmentPacket);
session.getUpstream().sendPacket(handPacket); session.sendUpstreamPacket(handPacket);
session.getUpstream().sendPacket(offHandPacket); session.sendUpstreamPacket(offHandPacket);
} }
} }

View File

@ -25,12 +25,59 @@
package org.geysermc.connector.entity; package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
public class MinecartEntity extends Entity { public class MinecartEntity extends Entity {
public MinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { public MinecartEntity(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 updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7) {
metadata.put(EntityData.HEALTH, entityMetadata.getValue());
}
// Direction in which the minecart is shaking
if (entityMetadata.getId() == 8) {
metadata.put(EntityData.HURT_DIRECTION, entityMetadata.getValue());
}
// Power in Java, time in Bedrock
if (entityMetadata.getId() == 9) {
metadata.put(EntityData.HURT_TIME, Math.min((int) (float) entityMetadata.getValue(), 15));
}
if (!(this instanceof DefaultBlockMinecartEntity)) { // Handled in the DefaultBlockMinecartEntity class
// Custom block
if (entityMetadata.getId() == 10) {
metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId((int) entityMetadata.getValue()));
}
// Custom block offset
if (entityMetadata.getId() == 11) {
metadata.put(EntityData.DISPLAY_OFFSET, entityMetadata.getValue());
}
// If the custom block should be enabled
if (entityMetadata.getId() == 12) {
// Needs a byte based off of Java's boolean
metadata.put(EntityData.HAS_DISPLAY, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0));
}
}
super.updateBedrockMetadata(entityMetadata, session);
}
@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);
} }
} }

View File

@ -55,7 +55,7 @@ public class PaintingEntity extends Entity {
addPaintingPacket.setName(paintingName.getBedrockName()); addPaintingPacket.setName(paintingName.getBedrockName());
addPaintingPacket.setPosition(fixOffset(true)); addPaintingPacket.setPosition(fixOffset(true));
addPaintingPacket.setDirection(direction); addPaintingPacket.setDirection(direction);
session.getUpstream().sendPacket(addPaintingPacket); session.sendUpstreamPacket(addPaintingPacket);
valid = true; valid = true;
@ -67,7 +67,7 @@ public class PaintingEntity extends Entity {
Vector3f position = super.position; Vector3f position = super.position;
position = position.add(0.5, 0.5, 0.5); position = position.add(0.5, 0.5, 0.5);
double widthOffset = paintingName.getWidth() > 1 ? 0.5 : 0; double widthOffset = paintingName.getWidth() > 1 ? 0.5 : 0;
double heightOffset = paintingName.getHeight() > 1 ? 0.5 : 0; double heightOffset = paintingName.getHeight() > 1 && paintingName.getHeight() != 3 ? 0.5 : 0;
switch (direction) { switch (direction) {
case 0: return position.add(widthOffset, heightOffset, OFFSET); case 0: return position.add(widthOffset, heightOffset, OFFSET);

View File

@ -26,15 +26,13 @@
package org.geysermc.connector.entity; package org.geysermc.connector.entity;
import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.mc.protocol.data.game.entity.Effect;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.message.TextMessage; 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.Vector3f;
import com.nukkitx.protocol.bedrock.data.CommandPermission; import com.nukkitx.protocol.bedrock.data.*;
import com.nukkitx.protocol.bedrock.data.EntityData; import com.nukkitx.protocol.bedrock.packet.*;
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
import com.nukkitx.protocol.bedrock.packet.AddPlayerPacket;
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
import com.nukkitx.protocol.bedrock.packet.PlayerListPacket;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@ -47,7 +45,10 @@ import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.network.session.cache.EntityEffectCache; import org.geysermc.connector.network.session.cache.EntityEffectCache;
import org.geysermc.connector.utils.SkinUtils; import org.geysermc.connector.utils.SkinUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Getter @Setter @Getter @Setter
public class PlayerEntity extends LivingEntity { public class PlayerEntity extends LivingEntity {
@ -56,8 +57,12 @@ public class PlayerEntity extends LivingEntity {
private String username; private String username;
private long lastSkinUpdate = -1; private long lastSkinUpdate = -1;
private boolean playerList = true; private boolean playerList = true;
private boolean onGround;
private final EntityEffectCache effectCache; private final EntityEffectCache effectCache;
private Entity leftParrot;
private Entity rightParrot;
public PlayerEntity(GameProfile gameProfile, long entityId, long geyserId, Vector3f position, Vector3f motion, Vector3f rotation) { public PlayerEntity(GameProfile gameProfile, long entityId, long geyserId, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, EntityType.PLAYER, position, motion, rotation); super(entityId, geyserId, EntityType.PLAYER, position, motion, rotation);
@ -93,8 +98,13 @@ public class PlayerEntity extends LivingEntity {
addPlayerPacket.setPlatformChatId(""); addPlayerPacket.setPlatformChatId("");
addPlayerPacket.getMetadata().putAll(metadata); addPlayerPacket.getMetadata().putAll(metadata);
long linkedEntityId = session.getEntityCache().getCachedPlayerEntityLink(entityId);
if (linkedEntityId != -1) {
addPlayerPacket.getEntityLinks().add(new EntityLink(session.getEntityCache().getEntityByJavaId(linkedEntityId).getGeyserId(), geyserId, EntityLink.Type.RIDER, false));
}
valid = true; valid = true;
session.getUpstream().sendPacket(addPlayerPacket); session.sendUpstreamPacket(addPlayerPacket);
updateEquipment(session); updateEquipment(session);
updateBedrockAttributes(session); updateBedrockAttributes(session);
@ -108,7 +118,7 @@ public class PlayerEntity extends LivingEntity {
PlayerListPacket playerList = new PlayerListPacket(); PlayerListPacket playerList = new PlayerListPacket();
playerList.setAction(PlayerListPacket.Action.ADD); playerList.setAction(PlayerListPacket.Action.ADD);
playerList.getEntries().add(SkinUtils.buildDefaultEntry(profile, geyserId)); playerList.getEntries().add(SkinUtils.buildDefaultEntry(profile, geyserId));
session.getUpstream().sendPacket(playerList); session.sendUpstreamPacket(playerList);
} }
} }
@ -124,7 +134,7 @@ public class PlayerEntity extends LivingEntity {
PlayerListPacket playerList = new PlayerListPacket(); PlayerListPacket playerList = new PlayerListPacket();
playerList.setAction(PlayerListPacket.Action.REMOVE); playerList.setAction(PlayerListPacket.Action.REMOVE);
playerList.getEntries().add(new PlayerListPacket.Entry(uuid)); playerList.getEntries().add(new PlayerListPacket.Entry(uuid));
session.getUpstream().sendPacket(playerList); session.sendUpstreamPacket(playerList);
}); });
} }
} }
@ -134,6 +144,8 @@ public class PlayerEntity extends LivingEntity {
setPosition(position); setPosition(position);
setRotation(rotation); setRotation(rotation);
this.onGround = isOnGround;
MovePlayerPacket movePlayerPacket = new MovePlayerPacket(); MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
movePlayerPacket.setRuntimeEntityId(geyserId); movePlayerPacket.setRuntimeEntityId(geyserId);
movePlayerPacket.setPosition(this.position); movePlayerPacket.setPosition(this.position);
@ -145,7 +157,13 @@ public class PlayerEntity extends LivingEntity {
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.UNKNOWN); movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.UNKNOWN);
} }
session.getUpstream().sendPacket(movePlayerPacket); session.sendUpstreamPacket(movePlayerPacket);
if (leftParrot != null) {
leftParrot.moveAbsolute(session, position, rotation, true, teleported);
}
if (rightParrot != null) {
rightParrot.moveAbsolute(session, position, rotation, true, teleported);
}
} }
@Override @Override
@ -153,13 +171,21 @@ public class PlayerEntity extends LivingEntity {
setRotation(rotation); setRotation(rotation);
this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ); this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ);
this.onGround = isOnGround;
MovePlayerPacket movePlayerPacket = new MovePlayerPacket(); MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
movePlayerPacket.setRuntimeEntityId(geyserId); movePlayerPacket.setRuntimeEntityId(geyserId);
movePlayerPacket.setPosition(position); movePlayerPacket.setPosition(position);
movePlayerPacket.setRotation(getBedrockRotation()); movePlayerPacket.setRotation(getBedrockRotation());
movePlayerPacket.setOnGround(isOnGround); movePlayerPacket.setOnGround(isOnGround);
movePlayerPacket.setMode(MovePlayerPacket.Mode.NORMAL); movePlayerPacket.setMode(MovePlayerPacket.Mode.NORMAL);
session.getUpstream().sendPacket(movePlayerPacket); session.sendUpstreamPacket(movePlayerPacket);
if (leftParrot != null) {
leftParrot.moveRelative(session, relX, relY, relZ, rotation, true);
}
if (rightParrot != null) {
rightParrot.moveRelative(session, relX, relY, relZ, rotation, true);
}
} }
@Override @Override
@ -188,5 +214,54 @@ public class PlayerEntity extends LivingEntity {
metadata.put(EntityData.NAMETAG, team.getPrefix() + MessageUtils.toChatColor(team.getColor()) + username + team.getSuffix()); metadata.put(EntityData.NAMETAG, team.getPrefix() + MessageUtils.toChatColor(team.getColor()) + username + team.getSuffix());
} }
} }
// Extra hearts - is not metadata but an attribute on Bedrock
if (entityMetadata.getId() == 14) {
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
attributesPacket.setRuntimeEntityId(geyserId);
List<Attribute> attributes = new ArrayList<>();
// Setting to a higher maximum since plugins/datapacks can probably extend the Bedrock soft limit
attributes.add(new Attribute("minecraft:absorption", 0.0f, 1024f, (float) entityMetadata.getValue(), 0.0f));
attributesPacket.setAttributes(attributes);
session.sendUpstreamPacket(attributesPacket);
}
// Parrot occupying shoulder
if (entityMetadata.getId() == 18 || entityMetadata.getId() == 19) {
CompoundTag tag = (CompoundTag) entityMetadata.getValue();
if (tag != null && !tag.isEmpty()) {
// The parrot is a separate entity in Bedrock, but part of the player entity in Java
Entity parrot = new Entity(0, session.getEntityCache().getNextEntityId().incrementAndGet(),
EntityType.PARROT, position, motion, rotation);
parrot.spawnEntity(session);
parrot.getMetadata().put(EntityData.VARIANT, tag.get("Variant").getValue());
// Different position whether the parrot is left or right
float offset = (entityMetadata.getId() == 18) ? 0.4f : -0.4f;
parrot.getMetadata().put(EntityData.RIDER_SEAT_POSITION, Vector3f.from(offset, -0.22, -0.1));
parrot.getMetadata().put(EntityData.RIDER_ROTATION_LOCKED, 1);
parrot.updateBedrockMetadata(session);
SetEntityLinkPacket linkPacket = new SetEntityLinkPacket();
EntityLink.Type type = (entityMetadata.getId() == 18) ? EntityLink.Type.RIDER : EntityLink.Type.PASSENGER;
linkPacket.setEntityLink(new EntityLink(geyserId, parrot.getGeyserId(), type, false));
// Delay, or else spawned-in players won't get the link
// TODO: Find a better solution. This problem also exists with item frames
session.getConnector().getGeneralThreadPool().schedule(() -> session.sendUpstreamPacket(linkPacket), 500, TimeUnit.MILLISECONDS);
if (entityMetadata.getId() == 18) {
leftParrot = parrot;
} else {
rightParrot = parrot;
}
} else {
Entity parrot = (entityMetadata.getId() == 18 ? leftParrot : rightParrot);
if (parrot != null) {
parrot.despawnEntity(session);
if (entityMetadata.getId() == 18) {
leftParrot = null;
} else {
rightParrot = null;
}
}
}
}
} }
} }

View File

@ -0,0 +1,44 @@
/*
* 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.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity {
public SpawnerMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@Override
public void updateDefaultBlockMetadata() {
metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(BlockTranslator.JAVA_RUNTIME_SPAWNER_ID));
metadata.put(EntityData.DISPLAY_OFFSET, 6);
}
}

View File

@ -23,23 +23,14 @@
* @link https://github.com/GeyserMC/Geyser * @link https://github.com/GeyserMC/Geyser
*/ */
package org.geysermc.connector.network.translators; package org.geysermc.connector.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.entity.type.EntityType;
public class NbtItemStackTranslator { public class TippedArrowEntity extends AbstractArrowEntity {
public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
public TippedArrowEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
} }
public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
}
public boolean acceptItem(ItemEntry itemEntry) {
return true;
}
} }

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityFlag;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
public class TridentEntity extends AbstractArrowEntity {
public TridentEntity(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() == 11) {
metadata.getFlags().setFlag(EntityFlag.ENCHANTED, (boolean) entityMetadata.getValue());
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View File

@ -38,6 +38,7 @@ public enum AttributeType {
MOVEMENT_SPEED("generic.movementSpeed", "minecraft:movement", 0f, 1024f, 0.1f), MOVEMENT_SPEED("generic.movementSpeed", "minecraft:movement", 0f, 1024f, 0.1f),
FLYING_SPEED("generic.flyingSpeed", "minecraft:movement", 0.0f, 1024.0f, 0.4000000059604645f), FLYING_SPEED("generic.flyingSpeed", "minecraft:movement", 0.0f, 1024.0f, 0.4000000059604645f),
ATTACK_DAMAGE("generic.attackDamage", "minecraft:attack_damage", 0f, 2048f, 1f), ATTACK_DAMAGE("generic.attackDamage", "minecraft:attack_damage", 0f, 2048f, 1f),
HORSE_JUMP_STRENGTH("horse.jumpStrength", "minecraft:horse.jump_strength", 0.0f, 2.0f, 0.7f),
// Java Attributes // Java Attributes
ARMOR("generic.armor", null, 0f, 30f, 0f), ARMOR("generic.armor", null, 0f, 30f, 0f),

View File

@ -25,28 +25,18 @@
package org.geysermc.connector.entity.living; 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.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData;
import com.nukkitx.protocol.bedrock.data.EntityFlag; import com.nukkitx.protocol.bedrock.data.EntityFlag;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
public class AbstractFishEntity extends WaterEntity { public class AbstractFishEntity extends WaterEntity {
public AbstractFishEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { public AbstractFishEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation); super(entityId, geyserId, entityType, position, motion, rotation);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
metadata.getFlags().setFlag(EntityFlag.CAN_SWIM, true); metadata.getFlags().setFlag(EntityFlag.CAN_SWIM, true);
metadata.getFlags().setFlag(EntityFlag.BREATHING, true); metadata.getFlags().setFlag(EntityFlag.BREATHING, true);
metadata.getFlags().setFlag(EntityFlag.CAN_CLIMB, false); metadata.getFlags().setFlag(EntityFlag.CAN_CLIMB, false);
metadata.getFlags().setFlag(EntityFlag.HAS_GRAVITY, false); metadata.getFlags().setFlag(EntityFlag.HAS_GRAVITY, false);
metadata.put(EntityData.AIR, (short) 400);
super.updateBedrockMetadata(entityMetadata, session);
} }
} }

View File

@ -44,6 +44,9 @@ public class AgeableEntity extends CreatureEntity {
boolean isBaby = (boolean) entityMetadata.getValue(); boolean isBaby = (boolean) entityMetadata.getValue();
metadata.put(EntityData.SCALE, isBaby ? .55f : 1f); metadata.put(EntityData.SCALE, isBaby ? .55f : 1f);
metadata.getFlags().setFlag(EntityFlag.BABY, isBaby); metadata.getFlags().setFlag(EntityFlag.BABY, isBaby);
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, entityType.getHeight() * (isBaby ? 0.55f : 1f));
metadata.put(EntityData.BOUNDING_BOX_WIDTH, entityType.getWidth() * (isBaby ? 0.55f : 1f));
} }
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);

View File

@ -35,16 +35,60 @@ import org.geysermc.connector.network.session.GeyserSession;
public class ArmorStandEntity extends LivingEntity { public class ArmorStandEntity extends LivingEntity {
// These are used to store the state of the armour stand for use when handling invisibility
private boolean isMarker = false;
private boolean isInvisible = false;
private boolean isSmall = false;
public ArmorStandEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { public ArmorStandEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation); super(entityId, geyserId, entityType, position, motion, rotation);
} }
@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) {
position = position.add(0d, entityType.getHeight() * (isSmall ? 0.55d : 1d), 0d);
}
super.moveAbsolute(session, position, rotation, isOnGround, teleported);
}
@Override @Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getType() == MetadataType.BYTE) { if (entityMetadata.getId() == 0 && entityMetadata.getType() == MetadataType.BYTE) {
byte xd = (byte) entityMetadata.getValue(); byte xd = (byte) entityMetadata.getValue();
if ((xd & 0x01) == 0x01 && (metadata.get(EntityData.SCALE) != null && !metadata.get(EntityData.SCALE).equals(0.0f))) {
metadata.put(EntityData.SCALE, .55f); // Check if the armour stand is invisible and store accordingly
if ((xd & 0x20) == 0x20) {
metadata.put(EntityData.SCALE, 0.0f);
isInvisible = true;
}
} else if (entityMetadata.getId() == 14 && entityMetadata.getType() == MetadataType.BYTE) {
byte xd = (byte) entityMetadata.getValue();
// isSmall
if ((xd & 0x01) == 0x01) {
isSmall = true;
if (metadata.getFloat(EntityData.SCALE) != 0.55f && metadata.getFloat(EntityData.SCALE) != 0.0f) {
metadata.put(EntityData.SCALE, 0.55f);
}
if (metadata.get(EntityData.BOUNDING_BOX_WIDTH) != null && metadata.get(EntityData.BOUNDING_BOX_WIDTH).equals(0.5f)) {
metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.25f);
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.9875f);
}
} else if (metadata.get(EntityData.BOUNDING_BOX_WIDTH) != null && metadata.get(EntityData.BOUNDING_BOX_WIDTH).equals(0.25f)) {
metadata.put(EntityData.BOUNDING_BOX_WIDTH, entityType.getWidth());
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, entityType.getHeight());
}
// setMarker
if ((xd & 0x10) == 0x10 && (metadata.get(EntityData.BOUNDING_BOX_WIDTH) == null || !metadata.get(EntityData.BOUNDING_BOX_WIDTH).equals(0.0f))) {
metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.0f);
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f);
isMarker = true;
} }
} }
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);

View File

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

View File

@ -23,14 +23,14 @@
* @link https://github.com/GeyserMC/Geyser * @link https://github.com/GeyserMC/Geyser
*/ */
package org.geysermc.connector.entity; package org.geysermc.connector.entity.living;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
public class ArrowEntity extends Entity { public class SquidEntity extends WaterEntity {
public ArrowEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { public SquidEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation); super(entityId, geyserId, entityType, position, motion, rotation);
} }
} }

View File

@ -26,11 +26,14 @@
package org.geysermc.connector.entity.living; package org.geysermc.connector.entity.living;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
public class WaterEntity extends CreatureEntity { public class WaterEntity extends CreatureEntity {
public WaterEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { public WaterEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation); super(entityId, geyserId, entityType, position, motion, rotation);
metadata.put(EntityData.AIR, (short) 400);
} }
} }

View File

@ -28,7 +28,6 @@ package org.geysermc.connector.entity.living.animal;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData; import com.nukkitx.protocol.bedrock.data.EntityData;
import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import org.geysermc.connector.entity.living.AbstractFishEntity; import org.geysermc.connector.entity.living.AbstractFishEntity;

View File

@ -27,25 +27,69 @@ package org.geysermc.connector.entity.living.animal.horse;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.Attribute;
import com.nukkitx.protocol.bedrock.data.EntityFlag; import com.nukkitx.protocol.bedrock.data.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
import org.geysermc.connector.entity.attribute.AttributeType;
import org.geysermc.connector.entity.living.animal.AnimalEntity; import org.geysermc.connector.entity.living.animal.AnimalEntity;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.AttributeUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class AbstractHorseEntity extends AnimalEntity { public class AbstractHorseEntity extends AnimalEntity {
// For updating the horse visual easier
private float health = 20f;
public AbstractHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { public AbstractHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation); super(entityId, geyserId, entityType, position, motion, rotation);
} }
@Override @Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 8) {
health = (float) entityMetadata.getValue();
updateBedrockAttributes(session);
}
if (entityMetadata.getId() == 16) { if (entityMetadata.getId() == 16) {
byte xd = (byte) entityMetadata.getValue(); byte xd = (byte) entityMetadata.getValue();
metadata.getFlags().setFlag(EntityFlag.TAMED, (xd & 0x02) == 0x02); metadata.getFlags().setFlag(EntityFlag.TAMED, (xd & 0x02) == 0x02);
metadata.getFlags().setFlag(EntityFlag.SADDLED, (xd & 0x04) == 0x04); metadata.getFlags().setFlag(EntityFlag.SADDLED, (xd & 0x04) == 0x04);
metadata.getFlags().setFlag(EntityFlag.EATING, (xd & 0x10) == 0x10); metadata.getFlags().setFlag(EntityFlag.EATING, (xd & 0x10) == 0x10);
metadata.getFlags().setFlag(EntityFlag.STANDING, (xd & 0x20) == 0x20);
} }
// Needed to control horses
metadata.getFlags().setFlag(EntityFlag.CAN_POWER_JUMP, true);
metadata.getFlags().setFlag(EntityFlag.WASD_CONTROLLED, true);
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);
} }
@Override
public void updateBedrockAttributes(GeyserSession session) {
if (!valid) return;
float maxHealth = attributes.containsKey(AttributeType.MAX_HEALTH) ? attributes.get(AttributeType.MAX_HEALTH).getValue() : 20f;
List<com.nukkitx.protocol.bedrock.data.Attribute> attributesLocal = new ArrayList<>();
for (Map.Entry<AttributeType, org.geysermc.connector.entity.attribute.Attribute> entry : this.attributes.entrySet()) {
if (!entry.getValue().getType().isBedrockAttribute())
continue;
attributesLocal.add(AttributeUtils.getBedrockAttribute(entry.getValue()));
}
attributesLocal.add(new Attribute("minecraft:health", 0.0f, maxHealth, health, maxHealth));
UpdateAttributesPacket updateAttributesPacket = new UpdateAttributesPacket();
updateAttributesPacket.setRuntimeEntityId(geyserId);
updateAttributesPacket.setAttributes(attributesLocal);
session.sendUpstreamPacket(updateAttributesPacket);
}
} }

View File

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

View File

@ -65,7 +65,7 @@ public class LlamaEntity extends ChestedHorseEntity {
equipmentPacket.setHelmet(ItemData.AIR); equipmentPacket.setHelmet(ItemData.AIR);
equipmentPacket.setLeggings(ItemData.AIR); equipmentPacket.setLeggings(ItemData.AIR);
session.getUpstream().sendPacket(equipmentPacket); session.sendUpstreamPacket(equipmentPacket);
} }
// Color of the llama // Color of the llama
if (entityMetadata.getId() == 21) { if (entityMetadata.getId() == 21) {

View File

@ -26,11 +26,19 @@
package org.geysermc.connector.entity.living.animal.horse; package org.geysermc.connector.entity.living.animal.horse;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
public class TraderLlamaEntity extends LlamaEntity { public class TraderLlamaEntity extends LlamaEntity {
public TraderLlamaEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { public TraderLlamaEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation); super(entityId, geyserId, entityType, position, motion, rotation);
} }
@Override
public void spawnEntity(GeyserSession session) {
this.metadata.put(EntityData.MARK_VARIANT, 1);
super.spawnEntity(session);
}
} }

View File

@ -39,8 +39,8 @@ public class CreeperEntity extends MonsterEntity {
@Override @Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 15 && (int) entityMetadata.getValue() > 0) { if (entityMetadata.getId() == 15) {
metadata.getFlags().setFlag(EntityFlag.IGNITED, true); metadata.getFlags().setFlag(EntityFlag.IGNITED, (int) entityMetadata.getValue() == 1);
} }
if (entityMetadata.getId() == 16) { if (entityMetadata.getId() == 16) {
metadata.getFlags().setFlag(EntityFlag.POWERED, (boolean) entityMetadata.getValue()); metadata.getFlags().setFlag(EntityFlag.POWERED, (boolean) entityMetadata.getValue());

View File

@ -0,0 +1,41 @@
/*
* 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.EntityFlag;
import org.geysermc.connector.entity.type.EntityType;
public class ElderGuardianEntity extends GuardianEntity {
public ElderGuardianEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
// Otherwise it just looks like a normal guardian but bigger
metadata.getFlags().setFlag(EntityFlag.ELDER, true);
}
}

View File

@ -53,7 +53,7 @@ public class EnderDragonEntity extends InsentientEntity {
entityEventPacket.setType(EntityEventType.DRAGON_FLAMING); entityEventPacket.setType(EntityEventType.DRAGON_FLAMING);
entityEventPacket.setRuntimeEntityId(geyserId); entityEventPacket.setRuntimeEntityId(geyserId);
entityEventPacket.setData(0); entityEventPacket.setData(0);
session.getUpstream().sendPacket(entityEventPacket); session.sendUpstreamPacket(entityEventPacket);
case 6: case 6:
case 7: case 7:
metadata.getFlags().setFlag(EntityFlag.SITTING, true); metadata.getFlags().setFlag(EntityFlag.SITTING, true);
@ -79,7 +79,7 @@ public class EnderDragonEntity extends InsentientEntity {
addEntityPacket.getAttributes().add(new Attribute("minecraft:health", 0.0f, 200f, 200f, 200f)); addEntityPacket.getAttributes().add(new Attribute("minecraft:health", 0.0f, 200f, 200f, 200f));
valid = true; valid = true;
session.getUpstream().sendPacket(addEntityPacket); session.sendUpstreamPacket(addEntityPacket);
session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")"); session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
} }

View File

@ -0,0 +1,39 @@
/*
* 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.EntityData;
import org.geysermc.connector.entity.type.EntityType;
public class GiantEntity extends MonsterEntity {
public GiantEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
metadata.put(EntityData.SCALE, 6f);
}
}

View File

@ -42,8 +42,14 @@ public class GuardianEntity extends MonsterEntity {
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 16) { if (entityMetadata.getId() == 16) {
Entity entity = session.getEntityCache().getEntityByJavaId((int) entityMetadata.getValue()); Entity entity = session.getEntityCache().getEntityByJavaId((int) entityMetadata.getValue());
if (entity == null && session.getPlayerEntity().getEntityId() == (Integer) entityMetadata.getValue()) {
entity = session.getPlayerEntity();
}
if (entity != null) { if (entity != null) {
metadata.put(EntityData.TARGET_EID, entity.getGeyserId()); metadata.put(EntityData.TARGET_EID, entity.getGeyserId());
} else {
metadata.put(EntityData.TARGET_EID, (long) 0);
} }
} }

View File

@ -0,0 +1,77 @@
/*
* 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.EntityData;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
public class WitherEntity extends MonsterEntity {
public WitherEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
metadata.put(EntityData.WITHER_AERIAL_ATTACK, (short) 1);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
long targetID = 0;
if (entityMetadata.getId() >= 15 && entityMetadata.getId() <= 17) {
Entity entity = session.getEntityCache().getEntityByJavaId((int) entityMetadata.getValue());
if (entity == null && session.getPlayerEntity().getEntityId() == (Integer) entityMetadata.getValue()) {
entity = session.getPlayerEntity();
}
if (entity != null) {
targetID = entity.getGeyserId();
}
}
if (entityMetadata.getId() == 15) {
metadata.put(EntityData.WITHER_TARGET_1, targetID);
} else if (entityMetadata.getId() == 16) {
metadata.put(EntityData.WITHER_TARGET_2, targetID);
} else if (entityMetadata.getId() == 17) {
metadata.put(EntityData.WITHER_TARGET_3, targetID);
} else if (entityMetadata.getId() == 18) {
metadata.put(EntityData.WITHER_INVULNERABLE_TICKS, (int) entityMetadata.getValue());
// Show the shield for the first few seconds of spawning (like Java)
if ((int) entityMetadata.getValue() >= 165) {
metadata.put(EntityData.WITHER_AERIAL_ATTACK, (short) 0);
} else {
metadata.put(EntityData.WITHER_AERIAL_ATTACK, (short) 1);
}
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View File

@ -47,7 +47,7 @@ public enum EntityType {
WOLF(WolfEntity.class, 14, 0.85f, 0.6f), WOLF(WolfEntity.class, 14, 0.85f, 0.6f),
VILLAGER(VillagerEntity.class, 15, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:villager_v2"), VILLAGER(VillagerEntity.class, 15, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:villager_v2"),
MOOSHROOM(AnimalEntity.class, 16, 1.4f, 0.9f), MOOSHROOM(AnimalEntity.class, 16, 1.4f, 0.9f),
SQUID(WaterEntity.class, 17, 0.8f), SQUID(SquidEntity.class, 17, 0.8f),
RABBIT(RabbitEntity.class, 18, 0.5f, 0.4f), RABBIT(RabbitEntity.class, 18, 0.5f, 0.4f),
BAT(AmbientEntity.class, 19, 0.9f, 0.5f), BAT(AmbientEntity.class, 19, 0.9f, 0.5f),
IRON_GOLEM(GolemEntity.class, 20, 2.7f, 1.4f), IRON_GOLEM(GolemEntity.class, 20, 2.7f, 1.4f),
@ -64,16 +64,17 @@ public enum EntityType {
PARROT(ParrotEntity.class, 30, 0.9f, 0.5f), PARROT(ParrotEntity.class, 30, 0.9f, 0.5f),
DOLPHIN(WaterEntity.class, 31, 0.6f, 0.9f), DOLPHIN(WaterEntity.class, 31, 0.6f, 0.9f),
ZOMBIE(ZombieEntity.class, 32, 1.8f, 0.6f, 0.6f, 1.62f), ZOMBIE(ZombieEntity.class, 32, 1.8f, 0.6f, 0.6f, 1.62f),
GIANT(GiantEntity.class, 32, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:zombie"),
CREEPER(CreeperEntity.class, 33, 1.7f, 0.6f, 0.6f, 1.62f), CREEPER(CreeperEntity.class, 33, 1.7f, 0.6f, 0.6f, 1.62f),
SKELETON(AbstractSkeletonEntity.class, 34, 1.8f, 0.6f, 0.6f, 1.62f), SKELETON(AbstractSkeletonEntity.class, 34, 1.8f, 0.6f, 0.6f, 1.62f),
SPIDER(SpiderEntity.class, 35, 0.9f, 1.4f, 1.4f, 1f), SPIDER(SpiderEntity.class, 35, 0.9f, 1.4f, 1.4f, 1f),
ZOMBIE_PIGMAN(MonsterEntity.class, 36, 1.8f, 0.6f, 0.6f, 1.62f), ZOMBIE_PIGMAN(MonsterEntity.class, 36, 1.8f, 0.6f, 0.6f, 1.62f),
SLIME(InsentientEntity.class, 37, 0.51f), SLIME(SlimeEntity.class, 37, 0.51f),
ENDERMAN(EndermanEntity.class, 38, 2.9f, 0.6f), ENDERMAN(EndermanEntity.class, 38, 2.9f, 0.6f),
SILVERFISH(MonsterEntity.class, 39, 0.3f, 0.4f), SILVERFISH(MonsterEntity.class, 39, 0.3f, 0.4f),
CAVE_SPIDER(MonsterEntity.class, 40, 0.5f, 0.7f), CAVE_SPIDER(MonsterEntity.class, 40, 0.5f, 0.7f),
GHAST(FlyingEntity.class, 41, 4.0f), GHAST(FlyingEntity.class, 41, 4.0f),
MAGMA_CUBE(InsentientEntity.class, 42, 0.51f), MAGMA_CUBE(SlimeEntity.class, 42, 0.51f),
BLAZE(BlazeEntity.class, 43, 1.8f, 0.6f), BLAZE(BlazeEntity.class, 43, 1.8f, 0.6f),
ZOMBIE_VILLAGER(ZombieEntity.class, 44, 1.8f, 0.6f, 0.6f, 1.62f), ZOMBIE_VILLAGER(ZombieEntity.class, 44, 1.8f, 0.6f, 0.6f, 1.62f),
WITCH(RaidParticipantEntity.class, 45, 1.8f, 0.6f, 0.6f, 1.62f), WITCH(RaidParticipantEntity.class, 45, 1.8f, 0.6f, 0.6f, 1.62f),
@ -81,9 +82,9 @@ public enum EntityType {
HUSK(ZombieEntity.class, 47, 1.8f, 0.6f, 0.6f, 1.62f), HUSK(ZombieEntity.class, 47, 1.8f, 0.6f, 0.6f, 1.62f),
WITHER_SKELETON(AbstractSkeletonEntity.class, 48, 2.4f, 0.7f), WITHER_SKELETON(AbstractSkeletonEntity.class, 48, 2.4f, 0.7f),
GUARDIAN(GuardianEntity.class, 49, 0.85f), GUARDIAN(GuardianEntity.class, 49, 0.85f),
ELDER_GUARDIAN(GuardianEntity.class, 50, 1.9975f), ELDER_GUARDIAN(ElderGuardianEntity.class, 50, 1.9975f),
NPC(PlayerEntity.class, 51, 1.8f, 0.6f, 0.6f, 1.62f), NPC(PlayerEntity.class, 51, 1.8f, 0.6f, 0.6f, 1.62f),
WITHER(MonsterEntity.class, 52, 3.5f, 0.9f), WITHER(WitherEntity.class, 52, 3.5f, 0.9f),
ENDER_DRAGON(EnderDragonEntity.class, 53, 4f, 13f), ENDER_DRAGON(EnderDragonEntity.class, 53, 4f, 13f),
SHULKER(ShulkerEntity.class, 54, 1f, 1f), SHULKER(ShulkerEntity.class, 54, 1f, 1f),
ENDERMITE(MonsterEntity.class, 55, 0.3f, 0.4f), ENDERMITE(MonsterEntity.class, 55, 0.3f, 0.4f),
@ -94,50 +95,52 @@ public enum EntityType {
PHANTOM(FlyingEntity.class, 58, 0.5f, 0.9f, 0.9f, 0.6f), PHANTOM(FlyingEntity.class, 58, 0.5f, 0.9f, 0.9f, 0.6f),
RAVAGER(RaidParticipantEntity.class, 59, 1.9f, 1.2f), RAVAGER(RaidParticipantEntity.class, 59, 1.9f, 1.2f),
ARMOR_STAND(ArmorStandEntity.class, 61, 0f), ARMOR_STAND(ArmorStandEntity.class, 61, 1.975f, 0.5f),
TRIPOD_CAMERA(Entity.class, 62, 0f), TRIPOD_CAMERA(Entity.class, 62, 0f),
PLAYER(PlayerEntity.class, 63, 1.8f, 0.6f, 0.6f, 1.62f), 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),
TNT(TNTEntity.class, 65, 0.98f, 0.98f), PRIMED_TNT(TNTEntity.class, 65, 0.98f, 0.98f, 0.98f, 0f, "minecraft:tnt"),
FALLING_BLOCK(FallingBlockEntity.class, 66, 0.98f, 0.98f), FALLING_BLOCK(FallingBlockEntity.class, 66, 0.98f, 0.98f),
MOVING_BLOCK(Entity.class, 67, 0f), MOVING_BLOCK(Entity.class, 67, 0f),
EXPERIENCE_BOTTLE(ThrowableEntity.class, 68, 0.25f, 0.25f, 0f, 0f, "minecraft:xp_bottle"), THROWN_EXP_BOTTLE(ThrowableEntity.class, 68, 0.25f, 0.25f, 0f, 0f, "minecraft:xp_bottle"),
EXPERIENCE_ORB(ExpOrbEntity.class, 69, 0f, 0f, 0f, 0f, "minecraft:xp_orb"), EXPERIENCE_ORB(ExpOrbEntity.class, 69, 0f, 0f, 0f, 0f, "minecraft:xp_orb"),
EYE_OF_ENDER(Entity.class, 70, 0f), EYE_OF_ENDER(Entity.class, 70, 0.25f, 0.25f, 0f, 0f, "minecraft:eye_of_ender_signal"),
END_CRYSTAL(EnderCrystalEntity.class, 71, 0f, 0f, 0f, 0f, "minecraft:ender_crystal"), END_CRYSTAL(EnderCrystalEntity.class, 71, 2.0f, 2.0f, 2.0f, 0f, "minecraft:ender_crystal"),
FIREWORK_ROCKET(Entity.class, 72, 0f), FIREWORK_ROCKET(FireworkEntity.class, 72, 0.25f, 0.25f, 0.25f, 0f, "minecraft:fireworks_rocket"),
TRIDENT(ArrowEntity.class, 73, 0f), TRIDENT(TridentEntity.class, 73, 0f, 0f, 0f, 0f, "minecraft:thrown_trident"),
TURTLE(AnimalEntity.class, 74, 0.4f, 1.2f), TURTLE(AnimalEntity.class, 74, 0.4f, 1.2f),
CAT(CatEntity.class, 75, 0.35f, 0.3f), CAT(CatEntity.class, 75, 0.35f, 0.3f),
SHULKER_BULLET(Entity.class, 76, 0f), SHULKER_BULLET(Entity.class, 76, 0.3125f),
FISHING_BOBBER(FishingHookEntity.class, 77, 0f, 0f, 0f, 0f, "minecraft:fishing_hook"), FISHING_BOBBER(FishingHookEntity.class, 77, 0f, 0f, 0f, 0f, "minecraft:fishing_hook"),
CHALKBOARD(Entity.class, 78, 0f), CHALKBOARD(Entity.class, 78, 0f),
DRAGON_FIREBALL(ItemedFireballEntity.class, 79, 0f), DRAGON_FIREBALL(ItemedFireballEntity.class, 79, 1.0f),
ARROW(ArrowEntity.class, 80, 0.25f, 0.25f), ARROW(TippedArrowEntity.class, 80, 0.25f, 0.25f),
SNOWBALL(ThrowableEntity.class, 81, 0f), SPECTRAL_ARROW(AbstractArrowEntity.class, 80, 0.25f, 0.25f, 0.25f, 0f, "minecraft:arrow"),
EGG(ThrowableEntity.class, 82, 0f), SNOWBALL(ThrowableEntity.class, 81, 0.25f),
THROWN_EGG(ThrowableEntity.class, 82, 0.25f, 0.25f, 0.25f, 0f, "minecraft:egg"),
PAINTING(PaintingEntity.class, 83, 0f), PAINTING(PaintingEntity.class, 83, 0f),
MINECART(MinecartEntity.class, 84, 0f), MINECART(MinecartEntity.class, 84, 0.7f, 0.98f, 0.98f, 0.35f),
FIREBALL(ItemedFireballEntity.class, 85, 0f), FIREBALL(ItemedFireballEntity.class, 85, 1.0f),
POTION(ThrowableEntity.class, 86, 0f), THROWN_POTION(ThrowableEntity.class, 86, 0.25f, 0.25f, 0.25f, 0f, "minecraft:splash_potion"),
ENDER_PEARL(ThrowableEntity.class, 87, 0f), THROWN_ENDERPEARL(ThrowableEntity.class, 87, 0.25f, 0.25f, 0.25f, 0f, "minecraft:ender_pearl"),
LEASH_KNOT(Entity.class, 88, 0f), LEASH_KNOT(LeashKnotEntity.class, 88, 0.5f, 0.375f),
WITHER_SKULL(Entity.class, 89, 0f), WITHER_SKULL(Entity.class, 89, 0.3125f),
BOAT(Entity.class, 90, 0.7f, 1.6f, 1.6f, 0.35f), BOAT(BoatEntity.class, 90, 0.7f, 1.6f, 1.6f, 0.35f),
WITHER_SKULL_DANGEROUS(Entity.class, 91, 0f), WITHER_SKULL_DANGEROUS(Entity.class, 91, 0f),
LIGHTNING_BOLT(Entity.class, 93, 0f), LIGHTNING_BOLT(Entity.class, 93, 0f),
SMALL_FIREBALL(ItemedFireballEntity.class, 94, 0f), SMALL_FIREBALL(ItemedFireballEntity.class, 94, 0.3125f),
AREA_EFFECT_CLOUD(Entity.class, 95, 0f), AREA_EFFECT_CLOUD(AreaEffectCloudEntity.class, 95, 0.5f, 1.0f),
HOPPER_MINECART(MinecartEntity.class, 96, 0f), MINECART_HOPPER(MinecartEntity.class, 96, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:hopper_minecart"),
TNT_MINECART(MinecartEntity.class, 97, 0f), MINECART_TNT(MinecartEntity.class, 97, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:tnt_minecart"),
CHEST_MINECART(MinecartEntity.class, 98, 0f), MINECART_CHEST(MinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:chest_minecart"),
MINECART_FURNACE(FurnaceMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"),
COMMAND_BLOCK_MINECART(MinecartEntity.class, 100, 0f), MINECART_SPAWNER(SpawnerMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"),
MINECART_COMMAND_BLOCK(MinecartEntity.class, 100, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:command_block_minecart"),
LINGERING_POTION(ThrowableEntity.class, 101, 0f), LINGERING_POTION(ThrowableEntity.class, 101, 0f),
LLAMA_SPIT(Entity.class, 102, 0f), LLAMA_SPIT(Entity.class, 102, 0.25f),
EVOKER_FANGS(Entity.class, 103, 0f), EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f, 0.5f, 0f, "minecraft:evocation_fang"),
EVOKER(SpellcasterIllagerEntity.class, 104, 0f), EVOKER(SpellcasterIllagerEntity.class, 104, 1.95f, 0.6f, 0.6f, 0f, "minecraft:evocation_illager"),
VEX(MonsterEntity.class, 105, 0f), VEX(MonsterEntity.class, 105, 0.8f, 0.4f),
ICE_BOMB(Entity.class, 106, 0f), ICE_BOMB(Entity.class, 106, 0f),
BALLOON(Entity.class, 107, 0f), //TODO BALLOON(Entity.class, 107, 0f), //TODO
PUFFERFISH(PufferFishEntity.class, 108, 0.7f, 0.7f), PUFFERFISH(PufferFishEntity.class, 108, 0.7f, 0.7f),
@ -152,7 +155,14 @@ public enum EntityType {
/** /**
* Item frames are handled differently since they are a block in Bedrock. * Item frames are handled differently since they are a block in Bedrock.
*/ */
ITEM_FRAME(ItemFrameEntity.class, 0, 0, 0); ITEM_FRAME(ItemFrameEntity.class, 0, 0, 0),
/**
* 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");
private static final EntityType[] VALUES = values();
private Class<? extends Entity> entityClass; private Class<? extends Entity> entityClass;
private final int type; private final int type;
@ -163,7 +173,7 @@ public enum EntityType {
private String identifier; private String identifier;
EntityType(Class<? extends Entity> entityClass, int type, float height) { EntityType(Class<? extends Entity> entityClass, int type, float height) {
this(entityClass, type, height, 0f); this(entityClass, type, height, height);
} }
EntityType(Class<? extends Entity> entityClass, int type, float height, float width) { EntityType(Class<? extends Entity> entityClass, int type, float height, float width) {
@ -189,4 +199,14 @@ public enum EntityType {
this.offset = offset + 0.00001f; this.offset = offset + 0.00001f;
this.identifier = identifier; this.identifier = identifier;
} }
public static EntityType getFromIdentifier(String identifier) {
for (EntityType type : VALUES) {
if (type.identifier.equals(identifier)) {
return type;
}
}
return null;
}
} }

View File

@ -25,11 +25,12 @@
package org.geysermc.connector.network; package org.geysermc.connector.network;
import com.github.steveice10.mc.protocol.data.status.ServerStatusInfo; import com.github.steveice10.mc.protocol.data.message.Message;
import com.nukkitx.protocol.bedrock.BedrockPong; import com.nukkitx.protocol.bedrock.*;
import com.nukkitx.protocol.bedrock.BedrockServerEventHandler; import io.netty.channel.ChannelHandlerContext;
import com.nukkitx.protocol.bedrock.BedrockServerSession; import io.netty.channel.socket.DatagramPacket;
import org.geysermc.common.ping.GeyserPingInfo;
import org.geysermc.connector.ping.IGeyserPingPassthrough;
import org.geysermc.connector.GeyserConfiguration; import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
@ -39,7 +40,7 @@ import java.net.InetSocketAddress;
public class ConnectorServerEventHandler implements BedrockServerEventHandler { public class ConnectorServerEventHandler implements BedrockServerEventHandler {
private GeyserConnector connector; private final GeyserConnector connector;
public ConnectorServerEventHandler(GeyserConnector connector) { public ConnectorServerEventHandler(GeyserConnector connector) {
this.connector = connector; this.connector = connector;
@ -56,29 +57,39 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
connector.getLogger().debug(inetSocketAddress + " has pinged you!"); connector.getLogger().debug(inetSocketAddress + " has pinged you!");
GeyserConfiguration config = connector.getConfig(); GeyserConfiguration config = connector.getConfig();
ServerStatusInfo serverInfo = connector.getPassthroughThread().getInfo();
GeyserPingInfo pingInfo = null;
if (config.isPassthroughMotd() || config.isPassthroughPlayerCounts()) {
IGeyserPingPassthrough pingPassthrough = connector.getBootstrap().getGeyserPingPassthrough();
pingInfo = pingPassthrough.getPingInformation();
}
BedrockPong pong = new BedrockPong(); BedrockPong pong = new BedrockPong();
pong.setEdition("MCPE"); pong.setEdition("MCPE");
pong.setGameType("Default"); pong.setGameType("Default");
pong.setNintendoLimited(false); pong.setNintendoLimited(false);
pong.setProtocolVersion(GeyserConnector.BEDROCK_PACKET_CODEC.getProtocolVersion()); pong.setProtocolVersion(GeyserConnector.BEDROCK_PACKET_CODEC.getProtocolVersion());
pong.setVersion(GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion()); pong.setVersion(null); // Server tries to connect either way and it looks better
pong.setIpv4Port(config.getBedrock().getPort()); pong.setIpv4Port(config.getBedrock().getPort());
if (connector.getConfig().isPingPassthrough() && serverInfo != null) {
String[] motd = MessageUtils.getBedrockMessage(serverInfo.getDescription()).split("\n"); if (config.isPassthroughMotd() && pingInfo != null && pingInfo.motd != null) {
String[] motd = MessageUtils.getBedrockMessage(Message.fromString(pingInfo.motd)).split("\n");
String mainMotd = motd[0]; // First line of the motd. String mainMotd = motd[0]; // First line of the motd.
String subMotd = (motd.length != 1) ? motd[1] : ""; // Second line of the motd if present, otherwise blank. String subMotd = (motd.length != 1) ? motd[1] : ""; // Second line of the motd if present, otherwise blank.
pong.setMotd(mainMotd.trim()); pong.setMotd(mainMotd.trim());
pong.setSubMotd(subMotd.trim()); // Trimmed to shift it to the left, prevents the universe from collapsing on us just because we went 2 characters over the text box's limit. pong.setSubMotd(subMotd.trim()); // Trimmed to shift it to the left, prevents the universe from collapsing on us just because we went 2 characters over the text box's limit.
pong.setPlayerCount(serverInfo.getPlayerInfo().getOnlinePlayers()); } else {
pong.setMaximumPlayerCount(serverInfo.getPlayerInfo().getMaxPlayers()); pong.setMotd(config.getBedrock().getMotd1());
pong.setSubMotd(config.getBedrock().getMotd2());
}
if (config.isPassthroughPlayerCounts() && pingInfo != null) {
pong.setPlayerCount(pingInfo.currentPlayerCount);
pong.setMaximumPlayerCount(pingInfo.maxPlayerCount);
} else { } else {
pong.setPlayerCount(connector.getPlayers().size()); pong.setPlayerCount(connector.getPlayers().size());
pong.setMaximumPlayerCount(config.getMaxPlayers()); pong.setMaximumPlayerCount(config.getMaxPlayers());
pong.setMotd(config.getBedrock().getMotd1());
pong.setMotd(config.getBedrock().getMotd2());
} }
//Bedrock will not even attempt a connection if the client thinks the server is full //Bedrock will not even attempt a connection if the client thinks the server is full
@ -105,4 +116,9 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
}); });
bedrockServerSession.setPacketCodec(GeyserConnector.BEDROCK_PACKET_CODEC); bedrockServerSession.setPacketCodec(GeyserConnector.BEDROCK_PACKET_CODEC);
} }
@Override
public void onUnhandledDatagram(ChannelHandlerContext ctx, DatagramPacket packet) {
new QueryPacketHandler(connector, packet.sender(), packet.content());
}
} }

View File

@ -0,0 +1,268 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*
*/
package org.geysermc.connector.network;
import com.github.steveice10.mc.protocol.data.message.Message;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import org.geysermc.common.ping.GeyserPingInfo;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.utils.MessageUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
public class QueryPacketHandler {
public static final byte HANDSHAKE = 0x09;
public static final byte STATISTICS = 0x00;
private GeyserConnector connector;
private InetSocketAddress sender;
private byte type;
private int sessionId;
private byte[] token;
/**
* The Query packet handler instance
* @param connector Geyser Connector
* @param sender The Sender IP/Port for the Query
* @param buffer The Query data
*/
public QueryPacketHandler(GeyserConnector connector, InetSocketAddress sender, ByteBuf buffer) {
if(!isQueryPacket(buffer))
return;
this.connector = connector;
this.sender = sender;
this.type = buffer.readByte();
this.sessionId = buffer.readInt();
regenerateToken();
handle();
}
/**
* Checks the packet is in fact a query packet
* @param buffer Query data
* @return if the packet is a query packet
*/
private boolean isQueryPacket(ByteBuf buffer) {
return (buffer.readableBytes() >= 2) ? buffer.readUnsignedShort() == 65277 : false;
}
/**
* Handles the query
*/
private void handle() {
switch (type) {
case HANDSHAKE:
sendToken();
case STATISTICS:
sendQueryData();
}
}
/**
* Sends the token to the sender
*/
private void sendToken() {
ByteBuf reply = ByteBufAllocator.DEFAULT.ioBuffer(10);
reply.writeByte(HANDSHAKE);
reply.writeInt(sessionId);
reply.writeBytes(getTokenString(this.token, this.sender.getAddress()));
reply.writeByte(0);
sendPacket(reply);
}
/**
* Sends the query data to the sender
*/
private void sendQueryData() {
ByteBuf reply = ByteBufAllocator.DEFAULT.ioBuffer(64);
reply.writeByte(STATISTICS);
reply.writeInt(sessionId);
// Game Info
reply.writeBytes(getGameData());
// Players
reply.writeBytes(getPlayers());
sendPacket(reply);
}
/**
* Gets the game data for the query
* @return the game data for the query
*/
private byte[] getGameData() {
ByteArrayOutputStream query = new ByteArrayOutputStream();
GeyserPingInfo pingInfo = null;
String motd;
String currentPlayerCount;
String maxPlayerCount;
if (connector.getConfig().isPassthroughMotd() || connector.getConfig().isPassthroughPlayerCounts()) {
pingInfo = connector.getBootstrap().getGeyserPingPassthrough().getPingInformation();
}
if (connector.getConfig().isPassthroughMotd() && pingInfo != null) {
String[] javaMotd = MessageUtils.getBedrockMessage(Message.fromString(pingInfo.motd)).split("\n");
motd = javaMotd[0].trim(); // First line of the motd.
} else {
motd = connector.getConfig().getBedrock().getMotd1();
}
// If passthrough player counts is enabled lets get players from the server
if (connector.getConfig().isPassthroughPlayerCounts() && pingInfo != null) {
currentPlayerCount = String.valueOf(pingInfo.currentPlayerCount);
maxPlayerCount = String.valueOf(pingInfo.maxPlayerCount);
} else {
currentPlayerCount = String.valueOf(connector.getPlayers().size());
maxPlayerCount = String.valueOf(connector.getConfig().getMaxPlayers());
}
// Create a hashmap of all game data needed in the query
Map<String, String> gameData = new HashMap<String, String>();
gameData.put("hostname", motd);
gameData.put("gametype", "SMP");
gameData.put("game_id", "MINECRAFT");
gameData.put("version", GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion());
gameData.put("plugins", "");
gameData.put("map", GeyserConnector.NAME);
gameData.put("numplayers", currentPlayerCount);
gameData.put("maxplayers", maxPlayerCount);
gameData.put("hostport", String.valueOf(connector.getConfig().getBedrock().getPort()));
gameData.put("hostip", connector.getConfig().getBedrock().getAddress());
try {
// Blank Buffer Bytes
query.write("GeyserMC".getBytes());
query.write((byte) 0x00);
query.write((byte) 128);
query.write((byte) 0x00);
// Fills the game data
for(Map.Entry<String, String> entry : gameData.entrySet()) {
query.write(entry.getKey().getBytes());
query.write((byte) 0x00);
query.write(entry.getValue().getBytes());
query.write((byte) 0x00);
}
// Final byte to show the end of the game data
query.write(new byte[]{0x00, 0x01});
return query.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return new byte[0];
}
}
private byte[] getPlayers() {
ByteArrayOutputStream query = new ByteArrayOutputStream();
GeyserPingInfo pingInfo = null;
if (connector.getConfig().isPassthroughMotd() || connector.getConfig().isPassthroughPlayerCounts()) {
pingInfo = connector.getBootstrap().getGeyserPingPassthrough().getPingInformation();
}
try {
// Start the player section
query.write("player_".getBytes());
query.write(new byte[]{0x00, 0x00});
// Fill player names
if(pingInfo != null) {
for (String username : pingInfo.getPlayers()) {
query.write(username.getBytes());
query.write((byte) 0x00);
}
}
// Final byte to show the end of the player data
query.write((byte) 0x00);
return query.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return new byte[0];
}
}
/**
* Sends a packet to the sender
* @param data packet data
*/
private void sendPacket(ByteBuf data) {
connector.getBedrockServer().getRakNet().send(sender, data);
}
/**
* Regenerates a token
*/
public void regenerateToken() {
byte[] token = new byte[16];
for (int i = 0; i < 16; i++) {
token[i] = (byte) new Random().nextInt(255);
}
this.token = token;
}
/**
* Gets an MD5 token for the current IP/Port.
* This should reset every 30 seconds but a new one is generated per instance
* Seems wasteful to code something in to clear it when it has no use.
* @param token the token
* @param address the address
* @return an MD5 token for the current IP/Port
*/
public static byte[] getTokenString(byte[] token, InetAddress address) {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(address.toString().getBytes(StandardCharsets.UTF_8));
digest.update(token);
return Arrays.copyOf(digest.digest(), 4);
} catch (NoSuchAlgorithmException e) {
return ByteBuffer.allocate(4).putInt(ThreadLocalRandom.current().nextInt()).array();
}
}
}

View File

@ -31,7 +31,7 @@ import org.geysermc.common.AuthType;
import org.geysermc.connector.GeyserConfiguration; import org.geysermc.connector.GeyserConfiguration;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.Registry; import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
import org.geysermc.connector.utils.LoginEncryptionUtils; import org.geysermc.connector.utils.LoginEncryptionUtils;
public class UpstreamPacketHandler extends LoggingPacketHandler { public class UpstreamPacketHandler extends LoggingPacketHandler {
@ -41,7 +41,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
} }
private boolean translateAndDefault(BedrockPacket packet) { private boolean translateAndDefault(BedrockPacket packet) {
return Registry.BEDROCK.translate(packet.getClass(), packet, session); return PacketTranslatorRegistry.BEDROCK_TRANSLATOR.translate(packet.getClass(), packet, session);
} }
@Override @Override
@ -58,10 +58,10 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
PlayStatusPacket playStatus = new PlayStatusPacket(); PlayStatusPacket playStatus = new PlayStatusPacket();
playStatus.setStatus(PlayStatusPacket.Status.LOGIN_SUCCESS); playStatus.setStatus(PlayStatusPacket.Status.LOGIN_SUCCESS);
session.getUpstream().sendPacket(playStatus); session.sendUpstreamPacket(playStatus);
ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket(); ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket();
session.getUpstream().sendPacket(resourcePacksInfo); session.sendUpstreamPacket(resourcePacksInfo);
return true; return true;
} }
@ -77,7 +77,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
stack.setExperimental(false); stack.setExperimental(false);
stack.setForcedToAccept(false); stack.setForcedToAccept(false);
stack.setGameVersion("*"); stack.setGameVersion("*");
session.getUpstream().sendPacket(stack); session.sendUpstreamPacket(stack);
break; break;
default: default:
session.disconnect("disconnectionScreen.resourcePack"); session.disconnect("disconnectionScreen.resourcePack");
@ -110,15 +110,19 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
} }
@Override @Override
public boolean handle(MovePlayerPacket packet) { public boolean handle(SetLocalPlayerAsInitializedPacket packet) {
if (!session.isLoggedIn() && !session.isLoggingIn() && session.getConnector().getAuthType() == AuthType.ONLINE) { if (!session.isLoggedIn() && !session.isLoggingIn() && session.getConnector().getAuthType() == AuthType.ONLINE) {
// TODO it is safer to key authentication on something that won't change (UUID, not username) // TODO it is safer to key authentication on something that won't change (UUID, not username)
if (!couldLoginUserByName(session.getAuthData().getName())) { if (!couldLoginUserByName(session.getAuthData().getName())) {
LoginEncryptionUtils.showLoginWindow(session); LoginEncryptionUtils.showLoginWindow(session);
} }
// else we were able to log the user in // else we were able to log the user in
return true;
} }
return translateAndDefault(packet);
}
@Override
public boolean handle(MovePlayerPacket packet) {
if (session.isLoggingIn()) { if (session.isLoggingIn()) {
session.sendMessage("Please wait until you are logged in..."); session.sendMessage("Please wait until you are logged in...");
} }

View File

@ -29,11 +29,13 @@ import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.mc.auth.exception.request.InvalidCredentialsException; import com.github.steveice10.mc.auth.exception.request.InvalidCredentialsException;
import com.github.steveice10.mc.auth.exception.request.RequestException; import com.github.steveice10.mc.auth.exception.request.RequestException;
import com.github.steveice10.mc.protocol.MinecraftProtocol; import com.github.steveice10.mc.protocol.MinecraftProtocol;
import com.github.steveice10.mc.protocol.data.SubProtocol;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket;
import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket; import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket;
import com.github.steveice10.mc.protocol.packet.login.server.LoginSuccessPacket;
import com.github.steveice10.packetlib.Client; import com.github.steveice10.packetlib.Client;
import com.github.steveice10.packetlib.event.session.*; import com.github.steveice10.packetlib.event.session.*;
import com.github.steveice10.packetlib.packet.Packet; import com.github.steveice10.packetlib.packet.Packet;
@ -41,6 +43,7 @@ import com.github.steveice10.packetlib.tcp.TcpSessionFactory;
import com.nukkitx.math.GenericMath; import com.nukkitx.math.GenericMath;
import com.nukkitx.math.TrigMath; import com.nukkitx.math.TrigMath;
import com.nukkitx.math.vector.*; import com.nukkitx.math.vector.*;
import com.nukkitx.protocol.bedrock.BedrockPacket;
import com.nukkitx.protocol.bedrock.BedrockServerSession; import com.nukkitx.protocol.bedrock.BedrockServerSession;
import com.nukkitx.protocol.bedrock.data.ContainerId; import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.data.GamePublishSetting; import com.nukkitx.protocol.bedrock.data.GamePublishSetting;
@ -55,17 +58,22 @@ import org.geysermc.common.AuthType;
import org.geysermc.common.window.FormWindow; import org.geysermc.common.window.FormWindow;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.entity.PlayerEntity;
import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.inventory.PlayerInventory;
import org.geysermc.connector.network.remote.RemoteServer; import org.geysermc.connector.network.remote.RemoteServer;
import org.geysermc.connector.network.session.auth.AuthData; import org.geysermc.connector.network.session.auth.AuthData;
import org.geysermc.connector.network.session.auth.BedrockClientData; import org.geysermc.connector.network.session.auth.BedrockClientData;
import org.geysermc.connector.network.session.cache.*; import org.geysermc.connector.network.session.cache.*;
import org.geysermc.connector.network.translators.Registry; import org.geysermc.connector.network.translators.world.WorldBorder;
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.item.ItemRegistry;
import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.utils.ChunkUtils;
import org.geysermc.connector.utils.LocaleUtils; import org.geysermc.connector.utils.LocaleUtils;
import org.geysermc.connector.utils.Toolbox; import org.geysermc.connector.utils.SkinUtils;
import org.geysermc.floodgate.util.BedrockData; import org.geysermc.floodgate.util.BedrockData;
import org.geysermc.floodgate.util.EncryptionUtil; import org.geysermc.floodgate.util.EncryptionUtil;
@ -153,9 +161,19 @@ public class GeyserSession implements CommandSender {
private boolean manyDimPackets = false; private boolean manyDimPackets = false;
private ServerRespawnPacket lastDimPacket = null; private ServerRespawnPacket lastDimPacket = null;
@Setter
private Entity ridingVehicleEntity;
@Setter @Setter
private int craftSlot = 0; private int craftSlot = 0;
@Setter
private WorldBorder worldBorder;
@Setter
private long lastWindowCloseTime = 0;
private MinecraftProtocol protocol;
public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) { public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) {
this.connector = connector; this.connector = connector;
this.upstream = new UpstreamSession(bedrockServerSession); this.upstream = new UpstreamSession(bedrockServerSession);
@ -184,16 +202,16 @@ public class GeyserSession implements CommandSender {
ChunkUtils.sendEmptyChunks(this, playerEntity.getPosition().toInt(), 0, false); ChunkUtils.sendEmptyChunks(this, playerEntity.getPosition().toInt(), 0, false);
BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket(); BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket();
biomeDefinitionListPacket.setTag(Toolbox.BIOMES); biomeDefinitionListPacket.setTag(BiomeTranslator.BIOMES);
upstream.sendPacket(biomeDefinitionListPacket); upstream.sendPacket(biomeDefinitionListPacket);
AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket(); AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket();
entityPacket.setTag(Toolbox.ENTITY_IDENTIFIERS); entityPacket.setTag(EntityIdentifierRegistry.ENTITY_IDENTIFIERS);
upstream.sendPacket(entityPacket); upstream.sendPacket(entityPacket);
InventoryContentPacket creativePacket = new InventoryContentPacket(); InventoryContentPacket creativePacket = new InventoryContentPacket();
creativePacket.setContainerId(ContainerId.CREATIVE); creativePacket.setContainerId(ContainerId.CREATIVE);
creativePacket.setContents(Toolbox.CREATIVE_ITEMS); creativePacket.setContents(ItemRegistry.CREATIVE_ITEMS);
upstream.sendPacket(creativePacket); upstream.sendPacket(creativePacket);
PlayStatusPacket playStatusPacket = new PlayStatusPacket(); PlayStatusPacket playStatusPacket = new PlayStatusPacket();
@ -201,6 +219,17 @@ public class GeyserSession implements CommandSender {
upstream.sendPacket(playStatusPacket); upstream.sendPacket(playStatusPacket);
} }
public void fetchOurSkin(PlayerListPacket.Entry entry) {
PlayerSkinPacket playerSkinPacket = new PlayerSkinPacket();
playerSkinPacket.setUuid(authData.getUUID());
playerSkinPacket.setSkin(entry.getSkin());
playerSkinPacket.setOldSkinName("OldName");
playerSkinPacket.setNewSkinName("NewName");
playerSkinPacket.setTrustedSkin(true);
upstream.sendPacket(playerSkinPacket);
getConnector().getLogger().debug("Sending skin for " + playerEntity.getUsername() + " " + authData.getUUID());
}
public void login() { public void login() {
if (connector.getAuthType() != AuthType.ONLINE) { if (connector.getAuthType() != AuthType.ONLINE) {
connector.getLogger().info( connector.getLogger().info(
@ -227,7 +256,6 @@ public class GeyserSession implements CommandSender {
// new thread so clients don't timeout // new thread so clients don't timeout
new Thread(() -> { new Thread(() -> {
try { try {
MinecraftProtocol protocol;
if (password != null && !password.isEmpty()) { if (password != null && !password.isEmpty()) {
protocol = new MinecraftProtocol(username, password); protocol = new MinecraftProtocol(username, password);
} else { } else {
@ -325,13 +353,33 @@ public class GeyserSession implements CommandSender {
lastDimPacket = event.getPacket(); lastDimPacket = event.getPacket();
return; return;
} else if (lastDimPacket != null) { } else if (lastDimPacket != null) {
Registry.JAVA.translate(lastDimPacket.getClass(), lastDimPacket, GeyserSession.this); PacketTranslatorRegistry.JAVA_TRANSLATOR.translate(lastDimPacket.getClass(), lastDimPacket, GeyserSession.this);
lastDimPacket = null; lastDimPacket = null;
} }
Registry.JAVA.translate(event.getPacket().getClass(), event.getPacket(), GeyserSession.this); // Required, or else Floodgate players break with Bukkit chunk caching
if (event.getPacket() instanceof LoginSuccessPacket) {
GameProfile profile = ((LoginSuccessPacket) event.getPacket()).getProfile();
playerEntity.setUsername(profile.getName());
playerEntity.setUuid(profile.getId());
// Check if they are not using a linked account
if (connector.getAuthType() == AuthType.OFFLINE || playerEntity.getUuid().getMostSignificantBits() == 0) {
SkinUtils.handleBedrockSkin(playerEntity, clientData);
}
}
PacketTranslatorRegistry.JAVA_TRANSLATOR.translate(event.getPacket().getClass(), event.getPacket(), GeyserSession.this);
} }
} }
@Override
public void packetError(PacketErrorEvent event) {
connector.getLogger().warning("Downstream packet error! " + event.getCause().getMessage());
if (connector.getConfig().isDebugMode())
event.getCause().printStackTrace();
event.setSuppress(true);
}
}); });
downstream.getSession().connect(); downstream.getSession().connect();
@ -392,6 +440,17 @@ public class GeyserSession implements CommandSender {
upstream.sendPacket(textPacket); upstream.sendPacket(textPacket);
} }
public void sendActionBar(String text) {
SetTitlePacket setTitlePacket = new SetTitlePacket();
setTitlePacket.setType(SetTitlePacket.Type.SET_ACTIONBAR_MESSAGE);
setTitlePacket.setText(text);
setTitlePacket.setFadeInTime(0);
setTitlePacket.setStayTime(0);
setTitlePacket.setFadeOutTime(0);
upstream.sendPacket(setTitlePacket);
}
@Override @Override
public boolean isConsole() { public boolean isConsole() {
return false; return false;
@ -465,7 +524,7 @@ public class GeyserSession implements CommandSender {
startGamePacket.setEnchantmentSeed(0); startGamePacket.setEnchantmentSeed(0);
startGamePacket.setMultiplayerCorrelationId(""); startGamePacket.setMultiplayerCorrelationId("");
startGamePacket.setBlockPalette(BlockTranslator.BLOCKS); startGamePacket.setBlockPalette(BlockTranslator.BLOCKS);
startGamePacket.setItemEntries(Toolbox.ITEMS); startGamePacket.setItemEntries(ItemRegistry.ITEMS);
startGamePacket.setVanillaVersion("*"); startGamePacket.setVanillaVersion("*");
// startGamePacket.setMovementServerAuthoritative(true); // startGamePacket.setMovementServerAuthoritative(true);
upstream.sendPacket(startGamePacket); upstream.sendPacket(startGamePacket);
@ -481,8 +540,47 @@ public class GeyserSession implements CommandSender {
int teleportId = teleportCache.getTeleportConfirmId(); int teleportId = teleportCache.getTeleportConfirmId();
teleportCache = null; teleportCache = null;
ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(teleportId); ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(teleportId);
getDownstream().getSession().send(teleportConfirmPacket); sendDownstreamPacket(teleportConfirmPacket);
} }
return true; return true;
} }
/**
* Queue a packet to be sent to player.
*
* @param packet the bedrock packet from the NukkitX protocol lib
*/
public void sendUpstreamPacket(BedrockPacket packet) {
if (upstream != null && !upstream.isClosed()) {
upstream.sendPacket(packet);
} else {
connector.getLogger().debug("Tried to send upstream packet " + packet.getClass().getSimpleName() + " but the session was null");
}
}
/**
* Send a packet immediately to the player.
*
* @param packet the bedrock packet from the NukkitX protocol lib
*/
public void sendUpstreamPacketImmediately(BedrockPacket packet) {
if (upstream != null && !upstream.isClosed()) {
upstream.sendPacketImmediately(packet);
} else {
connector.getLogger().debug("Tried to send upstream packet " + packet.getClass().getSimpleName() + " immediately but the session was null");
}
}
/**
* Send a packet to the remote server.
*
* @param packet the java edition packet from MCProtocolLib
*/
public void sendDownstreamPacket(Packet packet) {
if (downstream != null && downstream.getSession() != null && protocol.getSubProtocol().equals(SubProtocol.GAME)) {
downstream.getSession().send(packet);
} else {
connector.getLogger().debug("Tried to send downstream packet " + packet.getClass().getSimpleName() + " before connected to the server");
}
}
} }

View File

@ -24,16 +24,26 @@ public class BedrockClientData {
private String skinId; private String skinId;
@JsonProperty(value = "SkinData") @JsonProperty(value = "SkinData")
private String skinData; private String skinData;
@JsonProperty(value = "SkinImageHeight")
private int skinImageHeight;
@JsonProperty(value = "SkinImageWidth")
private int skinImageWidth;
@JsonProperty(value = "CapeId") @JsonProperty(value = "CapeId")
private String capeId; private String capeId;
@JsonProperty(value = "CapeData") @JsonProperty(value = "CapeData")
private byte[] capeData; private byte[] capeData;
@JsonProperty(value = "CapeImageHeight")
private int capeImageHeight;
@JsonProperty(value = "CapeImageWidth")
private int capeImageWidth;
@JsonProperty(value = "CapeOnClassicSkin") @JsonProperty(value = "CapeOnClassicSkin")
private boolean capeOnClassicSkin; private boolean capeOnClassicSkin;
@JsonProperty(value = "SkinResourcePatch") @JsonProperty(value = "SkinResourcePatch")
private String geometryName; private String geometryName;
@JsonProperty(value = "SkinGeometryData") @JsonProperty(value = "SkinGeometryData")
private String geometryData; private String geometryData;
@JsonProperty(value = "PersonaSkin")
private boolean personaSkin;
@JsonProperty(value = "PremiumSkin") @JsonProperty(value = "PremiumSkin")
private boolean premiumSkin; private boolean premiumSkin;
@ -60,6 +70,15 @@ public class BedrockClientData {
@JsonProperty(value = "ClientRandomId") @JsonProperty(value = "ClientRandomId")
private long clientRandomId; private long clientRandomId;
@JsonProperty(value = "ArmSize")
private String armSize;
@JsonProperty(value = "SkinAnimationData")
private String skinAnimationData;
@JsonProperty(value = "SkinColor")
private String skinColor;
@JsonProperty(value = "ThirdPartyNameOnly")
private boolean thirdPartyNameOnly;
public enum UIProfile { public enum UIProfile {
@JsonEnumDefaultValue @JsonEnumDefaultValue
CLASSIC, CLASSIC,

View File

@ -62,7 +62,7 @@ public class BossBar {
bossEventPacket.setOverlay(overlay); bossEventPacket.setOverlay(overlay);
bossEventPacket.setDarkenSky(darkenSky); bossEventPacket.setDarkenSky(darkenSky);
session.getUpstream().sendPacket(bossEventPacket); session.sendUpstreamPacket(bossEventPacket);
} }
public void updateTitle(Message title) { public void updateTitle(Message title) {
@ -72,7 +72,7 @@ public class BossBar {
bossEventPacket.setAction(BossEventPacket.Action.TITLE); bossEventPacket.setAction(BossEventPacket.Action.TITLE);
bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(title, session.getClientData().getLanguageCode())); bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(title, session.getClientData().getLanguageCode()));
session.getUpstream().sendPacket(bossEventPacket); session.sendUpstreamPacket(bossEventPacket);
} }
public void updateHealth(float health) { public void updateHealth(float health) {
@ -82,7 +82,7 @@ public class BossBar {
bossEventPacket.setAction(BossEventPacket.Action.HEALTH_PERCENTAGE); bossEventPacket.setAction(BossEventPacket.Action.HEALTH_PERCENTAGE);
bossEventPacket.setHealthPercentage(health); bossEventPacket.setHealthPercentage(health);
session.getUpstream().sendPacket(bossEventPacket); session.sendUpstreamPacket(bossEventPacket);
} }
public void removeBossBar() { public void removeBossBar() {
@ -90,7 +90,7 @@ public class BossBar {
bossEventPacket.setBossUniqueEntityId(entityId); bossEventPacket.setBossUniqueEntityId(entityId);
bossEventPacket.setAction(BossEventPacket.Action.HIDE); bossEventPacket.setAction(BossEventPacket.Action.HIDE);
session.getUpstream().sendPacket(bossEventPacket); session.sendUpstreamPacket(bossEventPacket);
removeBossEntity(); removeBossEntity();
} }
@ -104,18 +104,21 @@ public class BossBar {
addEntityPacket.setRuntimeEntityId(entityId); addEntityPacket.setRuntimeEntityId(entityId);
addEntityPacket.setIdentifier("minecraft:creeper"); addEntityPacket.setIdentifier("minecraft:creeper");
addEntityPacket.setEntityType(33); addEntityPacket.setEntityType(33);
addEntityPacket.setPosition(session.getPlayerEntity().getPosition()); addEntityPacket.setPosition(session.getPlayerEntity().getPosition().sub(0D, -10D, 0D));
addEntityPacket.setRotation(Vector3f.ZERO); addEntityPacket.setRotation(Vector3f.ZERO);
addEntityPacket.setMotion(Vector3f.ZERO); addEntityPacket.setMotion(Vector3f.ZERO);
addEntityPacket.getMetadata().put(EntityData.SCALE, 0.01F); // scale = 0 doesn't work? addEntityPacket.getMetadata()
.putFloat(EntityData.SCALE, 0F)
.putFloat(EntityData.BOUNDING_BOX_WIDTH, 0F)
.putFloat(EntityData.BOUNDING_BOX_HEIGHT, 0F);
session.getUpstream().sendPacket(addEntityPacket); session.sendUpstreamPacket(addEntityPacket);
} }
private void removeBossEntity() { private void removeBossEntity() {
RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket(); RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket();
removeEntityPacket.setUniqueEntityId(entityId); removeEntityPacket.setUniqueEntityId(entityId);
session.getUpstream().sendPacket(removeEntityPacket); session.sendUpstreamPacket(removeEntityPacket);
} }
} }

View File

@ -26,8 +26,6 @@
package org.geysermc.connector.network.session.cache; package org.geysermc.connector.network.session.cache;
import it.unimi.dsi.fastutil.longs.*; import it.unimi.dsi.fastutil.longs.*;
import it.unimi.dsi.fastutil.objects.Object2LongMap;
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import lombok.Getter; import lombok.Getter;
import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.Entity;
@ -49,6 +47,7 @@ public class EntityCache {
private Long2LongMap entityIdTranslations = Long2LongMaps.synchronize(new Long2LongOpenHashMap()); private Long2LongMap entityIdTranslations = Long2LongMaps.synchronize(new Long2LongOpenHashMap());
private Map<UUID, PlayerEntity> playerEntities = Collections.synchronizedMap(new HashMap<>()); private Map<UUID, PlayerEntity> playerEntities = Collections.synchronizedMap(new HashMap<>());
private Map<UUID, BossBar> bossBars = Collections.synchronizedMap(new HashMap<>()); private Map<UUID, BossBar> bossBars = Collections.synchronizedMap(new HashMap<>());
private Long2LongMap cachedPlayerEntityLinks = Long2LongMaps.synchronize(new Long2LongOpenHashMap());
@Getter @Getter
private AtomicLong nextEntityId = new AtomicLong(2L); private AtomicLong nextEntityId = new AtomicLong(2L);
@ -148,4 +147,12 @@ public class EntityCache {
playerEntities = null; playerEntities = null;
bossBars = null; bossBars = null;
} }
public long getCachedPlayerEntityLink(long playerId) {
return cachedPlayerEntityLinks.getOrDefault(playerId, -1);
}
public void addCachedPlayerEntityLink(long playerId, long linkedEntityId) {
cachedPlayerEntityLinks.put(playerId, linkedEntityId);
}
} }

View File

@ -66,7 +66,7 @@ public class WindowCache {
formRequestPacket.setFormId(id); formRequestPacket.setFormId(id);
formRequestPacket.setFormData(windows.get(id).getJSONData()); formRequestPacket.setFormData(windows.get(id).getJSONData());
session.getUpstream().sendPacket(formRequestPacket); session.sendUpstreamPacket(formRequestPacket);
} }
public void showWindow(FormWindow window, int id) { public void showWindow(FormWindow window, int id) {
@ -74,7 +74,7 @@ public class WindowCache {
formRequestPacket.setFormId(id); formRequestPacket.setFormId(id);
formRequestPacket.setFormData(window.getJSONData()); formRequestPacket.setFormData(window.getJSONData());
session.getUpstream().sendPacket(formRequestPacket); session.sendUpstreamPacket(formRequestPacket);
addWindow(window, id); addWindow(window, id);
} }

View File

@ -1,14 +1,72 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*
*/
package org.geysermc.connector.network.translators; package org.geysermc.connector.network.translators;
import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.nbt.stream.NBTInputStream;
import com.nukkitx.nbt.tag.CompoundTag;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.utils.FileUtils;
import java.io.InputStream;
import java.util.Arrays; import java.util.Arrays;
//Based off of ProtocolSupport's LegacyBiomeData.java https://github.com/ProtocolSupport/ProtocolSupport/blob/b2cad35977f3fcb65bee57b9e14fc9c975f71d32/src/protocolsupport/protocol/typeremapper/legacy/LegacyBiomeData.java // Based off of ProtocolSupport's LegacyBiomeData.java:
//Array index formula by https://wiki.vg/Chunk_Format // https://github.com/ProtocolSupport/ProtocolSupport/blob/b2cad35977f3fcb65bee57b9e14fc9c975f71d32/src/protocolsupport/protocol/typeremapper/legacy/LegacyBiomeData.java
// Array index formula by https://wiki.vg/Chunk_Format
public class BiomeTranslator { public class BiomeTranslator {
public static final CompoundTag BIOMES;
private BiomeTranslator() {
}
public static void init() {
// no-op
}
static {
/* Load biomes */
InputStream stream = FileUtils.getResource("bedrock/biome_definitions.dat");
CompoundTag biomesTag;
try (NBTInputStream biomenbtInputStream = NbtUtils.createNetworkReader(stream)){
biomesTag = (CompoundTag) biomenbtInputStream.readTag();
BIOMES = biomesTag;
} catch (Exception ex) {
GeyserConnector.getInstance().getLogger().warning("Failed to get biomes from biome definitions, is there something wrong with the file?");
throw new AssertionError(ex);
}
}
public static byte[] toBedrockBiome(int[] biomeData) { public static byte[] toBedrockBiome(int[] biomeData) {
byte[] bedrockData = new byte[256]; byte[] bedrockData = new byte[256];
if(biomeData == null) { if (biomeData == null) {
return bedrockData; return bedrockData;
} }
@ -24,12 +82,12 @@ public class BiomeTranslator {
return bedrockData; return bedrockData;
} }
protected static void fillArray(int z, int x, byte[] legacyBiomeData, int biomeId) { private static void fillArray(int z, int x, byte[] legacyBiomeData, int biomeId) {
int offset = (z << 4) | x; int offset = (z << 4) | x;
Arrays.fill(legacyBiomeData, offset, offset + 4, (byte) biomeId); Arrays.fill(legacyBiomeData, offset, offset + 4, (byte) biomeId);
} }
protected static byte biomeID(int[] biomeData, int x, int z) { private static byte biomeID(int[] biomeData, int x, int z) {
return (byte) biomeData[((z >> 2) & 3) << 2 | ((x >> 2) & 3)]; return (byte) biomeData[((z >> 2) & 3) << 2 | ((x >> 2) & 3)];
} }
} }

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*
*/
package org.geysermc.connector.network.translators;
import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.nbt.stream.NBTInputStream;
import com.nukkitx.nbt.tag.CompoundTag;
import org.geysermc.connector.utils.FileUtils;
import java.io.InputStream;
/**
* Registry for entity identifiers.
*/
public class EntityIdentifierRegistry {
public static CompoundTag ENTITY_IDENTIFIERS;
private EntityIdentifierRegistry() {
}
public static void init() {
// no-op
}
static {
/* Load entity identifiers */
InputStream stream = FileUtils.getResource("bedrock/entity_identifiers.dat");
try (NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream)) {
ENTITY_IDENTIFIERS = (CompoundTag) nbtInputStream.readTag();
} catch (Exception e) {
throw new AssertionError("Unable to get entities from entity identifiers", e);
}
}
}

View File

@ -1,250 +0,0 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.network.translators;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.message.Message;
import com.github.steveice10.opennbt.tag.builtin.*;
import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.nbt.tag.Tag;
import com.nukkitx.protocol.bedrock.data.ItemData;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.utils.MessageUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class ItemStackTranslator {
public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) {
if (itemStack == null) {
return ItemData.AIR;
}
if (itemStack.getNbt() == null) {
return ItemData.of(itemEntry.getBedrockId(), (short) itemEntry.getBedrockData(), itemStack.getAmount());
}
return ItemData.of(itemEntry.getBedrockId(), (short) itemEntry.getBedrockData(), itemStack.getAmount(), this.translateNbtToBedrock(itemStack.getNbt()));
}
public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) {
if (itemData == null) return null;
if (itemData.getTag() == null) {
return new ItemStack(itemEntry.getJavaId(), itemData.getCount(), new com.github.steveice10.opennbt.tag.builtin.CompoundTag(""));
}
return new ItemStack(itemEntry.getJavaId(), itemData.getCount(), this.translateToJavaNBT(itemData.getTag()));
}
public abstract List<ItemEntry> getAppliedItems();
public CompoundTag translateNbtToBedrock(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag) {
Map<String, Tag<?>> javaValue = new HashMap<String, Tag<?>>();
if (tag.getValue() != null && !tag.getValue().isEmpty()) {
for (String str : tag.getValue().keySet()) {
com.github.steveice10.opennbt.tag.builtin.Tag javaTag = tag.get(str);
com.nukkitx.nbt.tag.Tag translatedTag = translateToBedrockNBT(javaTag);
if (translatedTag == null)
continue;
javaValue.put(translatedTag.getName(), translatedTag);
}
}
com.nukkitx.nbt.tag.CompoundTag bedrockTag = new com.nukkitx.nbt.tag.CompoundTag(tag.getName(), javaValue);
return bedrockTag;
}
private com.nukkitx.nbt.tag.Tag translateToBedrockNBT(com.github.steveice10.opennbt.tag.builtin.Tag tag) {
if (tag instanceof ByteArrayTag) {
ByteArrayTag byteArrayTag = (ByteArrayTag) tag;
return new com.nukkitx.nbt.tag.ByteArrayTag(byteArrayTag.getName(), byteArrayTag.getValue());
}
if (tag instanceof ByteTag) {
ByteTag byteTag = (ByteTag) tag;
return new com.nukkitx.nbt.tag.ByteTag(byteTag.getName(), byteTag.getValue());
}
if (tag instanceof DoubleTag) {
DoubleTag doubleTag = (DoubleTag) tag;
return new com.nukkitx.nbt.tag.DoubleTag(doubleTag.getName(), doubleTag.getValue());
}
if (tag instanceof FloatTag) {
FloatTag floatTag = (FloatTag) tag;
return new com.nukkitx.nbt.tag.FloatTag(floatTag.getName(), floatTag.getValue());
}
if (tag instanceof IntArrayTag) {
IntArrayTag intArrayTag = (IntArrayTag) tag;
return new com.nukkitx.nbt.tag.IntArrayTag(intArrayTag.getName(), intArrayTag.getValue());
}
if (tag instanceof IntTag) {
IntTag intTag = (IntTag) tag;
return new com.nukkitx.nbt.tag.IntTag(intTag.getName(), intTag.getValue());
}
if (tag instanceof LongArrayTag) {
LongArrayTag longArrayTag = (LongArrayTag) tag;
return new com.nukkitx.nbt.tag.LongArrayTag(longArrayTag.getName(), longArrayTag.getValue());
}
if (tag instanceof LongTag) {
LongTag longTag = (LongTag) tag;
return new com.nukkitx.nbt.tag.LongTag(longTag.getName(), longTag.getValue());
}
if (tag instanceof ShortTag) {
ShortTag shortTag = (ShortTag) tag;
return new com.nukkitx.nbt.tag.ShortTag(shortTag.getName(), shortTag.getValue());
}
if (tag instanceof StringTag) {
StringTag stringTag = (StringTag) tag;
return new com.nukkitx.nbt.tag.StringTag(stringTag.getName(), stringTag.getValue());
}
if (tag instanceof ListTag) {
ListTag listTag = (ListTag) tag;
List<Tag> tagList = new ArrayList<>();
for (com.github.steveice10.opennbt.tag.builtin.Tag value : listTag) {
tagList.add(translateToBedrockNBT(value));
}
Class clazz = CompoundTag.class;
if (!tagList.isEmpty()) {
clazz = tagList.get(0).getClass();
}
return new com.nukkitx.nbt.tag.ListTag(listTag.getName(), clazz, tagList);
}
if (tag instanceof com.github.steveice10.opennbt.tag.builtin.CompoundTag) {
com.github.steveice10.opennbt.tag.builtin.CompoundTag compoundTag = (com.github.steveice10.opennbt.tag.builtin.CompoundTag) tag;
return translateNbtToBedrock(compoundTag);
}
return null;
}
public com.github.steveice10.opennbt.tag.builtin.CompoundTag translateToJavaNBT(com.nukkitx.nbt.tag.CompoundTag tag) {
com.github.steveice10.opennbt.tag.builtin.CompoundTag javaTag = new com.github.steveice10.opennbt.tag.builtin.CompoundTag(tag.getName());
Map<String, com.github.steveice10.opennbt.tag.builtin.Tag> javaValue = javaTag.getValue();
if (tag.getValue() != null && !tag.getValue().isEmpty()) {
for (String str : tag.getValue().keySet()) {
com.nukkitx.nbt.tag.Tag bedrockTag = tag.get(str);
com.github.steveice10.opennbt.tag.builtin.Tag translatedTag = translateToJavaNBT(bedrockTag);
if (translatedTag == null)
continue;
javaValue.put(translatedTag.getName(), translatedTag);
}
}
javaTag.setValue(javaValue);
return javaTag;
}
private com.github.steveice10.opennbt.tag.builtin.Tag translateToJavaNBT(com.nukkitx.nbt.tag.Tag tag) {
if (tag instanceof com.nukkitx.nbt.tag.ByteArrayTag) {
com.nukkitx.nbt.tag.ByteArrayTag byteArrayTag = (com.nukkitx.nbt.tag.ByteArrayTag) tag;
return new ByteArrayTag(byteArrayTag.getName(), byteArrayTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.ByteTag) {
com.nukkitx.nbt.tag.ByteTag byteTag = (com.nukkitx.nbt.tag.ByteTag) tag;
return new ByteTag(byteTag.getName(), byteTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.DoubleTag) {
com.nukkitx.nbt.tag.DoubleTag doubleTag = (com.nukkitx.nbt.tag.DoubleTag) tag;
return new DoubleTag(doubleTag.getName(), doubleTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.FloatTag) {
com.nukkitx.nbt.tag.FloatTag floatTag = (com.nukkitx.nbt.tag.FloatTag) tag;
return new FloatTag(floatTag.getName(), floatTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.IntArrayTag) {
com.nukkitx.nbt.tag.IntArrayTag intArrayTag = (com.nukkitx.nbt.tag.IntArrayTag) tag;
return new IntArrayTag(intArrayTag.getName(), intArrayTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.IntTag) {
com.nukkitx.nbt.tag.IntTag intTag = (com.nukkitx.nbt.tag.IntTag) tag;
return new IntTag(intTag.getName(), intTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.LongArrayTag) {
com.nukkitx.nbt.tag.LongArrayTag longArrayTag = (com.nukkitx.nbt.tag.LongArrayTag) tag;
return new LongArrayTag(longArrayTag.getName(), longArrayTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.LongTag) {
com.nukkitx.nbt.tag.LongTag longTag = (com.nukkitx.nbt.tag.LongTag) tag;
return new LongTag(longTag.getName(), longTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.ShortTag) {
com.nukkitx.nbt.tag.ShortTag shortTag = (com.nukkitx.nbt.tag.ShortTag) tag;
return new ShortTag(shortTag.getName(), shortTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.StringTag) {
com.nukkitx.nbt.tag.StringTag stringTag = (com.nukkitx.nbt.tag.StringTag) tag;
return new StringTag(stringTag.getName(), stringTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.ListTag) {
com.nukkitx.nbt.tag.ListTag listTag = (com.nukkitx.nbt.tag.ListTag) tag;
List<com.github.steveice10.opennbt.tag.builtin.Tag> tags = new ArrayList<>();
for (Object value : listTag.getValue()) {
if (!(value instanceof com.nukkitx.nbt.tag.Tag))
continue;
com.nukkitx.nbt.tag.Tag tagValue = (com.nukkitx.nbt.tag.Tag) value;
com.github.steveice10.opennbt.tag.builtin.Tag javaTag = translateToJavaNBT(tagValue);
if (javaTag != null)
tags.add(javaTag);
}
return new ListTag(listTag.getName(), tags);
}
if (tag instanceof com.nukkitx.nbt.tag.CompoundTag) {
com.nukkitx.nbt.tag.CompoundTag compoundTag = (com.nukkitx.nbt.tag.CompoundTag) tag;
return translateToJavaNBT(compoundTag);
}
return null;
}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.network.translators;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerKeepAlivePacket;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdateLightPacket;
import com.github.steveice10.packetlib.packet.Packet;
import com.nukkitx.protocol.bedrock.BedrockPacket;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.reflections.Reflections;
import java.util.HashMap;
import java.util.Map;
public class PacketTranslatorRegistry<T> {
private final Map<Class<? extends T>, PacketTranslator<? extends T>> translators = new HashMap<>();
public static final PacketTranslatorRegistry<Packet> JAVA_TRANSLATOR = new PacketTranslatorRegistry<>();
public static final PacketTranslatorRegistry<BedrockPacket> BEDROCK_TRANSLATOR = new PacketTranslatorRegistry<>();
private static final ObjectArrayList<Class<?>> IGNORED_PACKETS = new ObjectArrayList<>();
static {
Reflections ref = new Reflections("org.geysermc.connector.network.translators");
for (Class<?> clazz : ref.getTypesAnnotatedWith(Translator.class)) {
Class<?> packet = clazz.getAnnotation(Translator.class).packet();
GeyserConnector.getInstance().getLogger().debug("Found annotated translator: " + clazz.getCanonicalName() + " : " + packet.getSimpleName());
try {
if (Packet.class.isAssignableFrom(packet)) {
Class<? extends Packet> targetPacket = (Class<? extends Packet>) packet;
PacketTranslator<? extends Packet> translator = (PacketTranslator<? extends Packet>) clazz.newInstance();
JAVA_TRANSLATOR.translators.put(targetPacket, translator);
} else if (BedrockPacket.class.isAssignableFrom(packet)) {
Class<? extends BedrockPacket> targetPacket = (Class<? extends BedrockPacket>) packet;
PacketTranslator<? extends BedrockPacket> translator = (PacketTranslator<? extends BedrockPacket>) clazz.newInstance();
BEDROCK_TRANSLATOR.translators.put(targetPacket, translator);
} else {
GeyserConnector.getInstance().getLogger().error("Class " + clazz.getCanonicalName() + " is annotated as a translator but has an invalid target packet.");
}
} catch (InstantiationException | IllegalAccessException e) {
GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated translator " + clazz.getCanonicalName() + ".");
}
}
IGNORED_PACKETS.add(ServerKeepAlivePacket.class); // Handled by MCProtocolLib
IGNORED_PACKETS.add(ServerUpdateLightPacket.class); // Light is handled on Bedrock for us
}
private PacketTranslatorRegistry() {
}
public static void init() {
// no-op
}
@SuppressWarnings("unchecked")
public <P extends T> boolean translate(Class<? extends P> clazz, P packet, GeyserSession session) {
if (!session.getUpstream().isClosed() && !session.isClosed()) {
try {
if (translators.containsKey(clazz)) {
((PacketTranslator<P>) translators.get(clazz)).translate(packet, session);
return true;
} else {
if (!IGNORED_PACKETS.contains(clazz))
GeyserConnector.getInstance().getLogger().debug("Could not find packet for " + (packet.toString().length() > 25 ? packet.getClass().getSimpleName() : packet));
}
} catch (Throwable ex) {
GeyserConnector.getInstance().getLogger().error("Could not translate packet " + packet.getClass().getSimpleName(), ex);
ex.printStackTrace();
}
}
return false;
}
}

View File

@ -1,68 +0,0 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.network.translators;
import java.util.HashMap;
import java.util.Map;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import com.github.steveice10.packetlib.packet.Packet;
import com.nukkitx.protocol.bedrock.BedrockPacket;
public class Registry<T> {
private final Map<Class<? extends T>, PacketTranslator<? extends T>> MAP = new HashMap<>();
public static final Registry<Packet> JAVA = new Registry<>();
public static final Registry<BedrockPacket> BEDROCK = new Registry<>();
public static void registerJava(Class<? extends Packet> targetPacket, PacketTranslator<? extends Packet> translator) {
JAVA.MAP.put(targetPacket, translator);
}
public static void registerBedrock(Class<? extends BedrockPacket> targetPacket, PacketTranslator<? extends BedrockPacket> translator) {
BEDROCK.MAP.put(targetPacket, translator);
}
@SuppressWarnings("unchecked")
public <P extends T> boolean translate(Class<? extends P> clazz, P packet, GeyserSession session) {
if (!session.getUpstream().isClosed() && !session.isClosed()) {
try {
if (MAP.containsKey(clazz)) {
((PacketTranslator<P>) MAP.get(clazz)).translate(packet, session);
return true;
} else {
GeyserConnector.getInstance().getLogger().debug("Could not find packet for " + (packet.toString().length() > 25 ? packet.getClass().getSimpleName() : packet));
}
} catch (Throwable ex) {
GeyserConnector.getInstance().getLogger().error("Could not translate packet " + packet.getClass().getSimpleName(), ex);
ex.printStackTrace();
}
}
return false;
}
}

View File

@ -1,175 +0,0 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.network.translators;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
import com.nukkitx.protocol.bedrock.data.ContainerType;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.network.translators.world.block.entity.*;
import org.geysermc.connector.network.translators.inventory.*;
import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.reflections.Reflections;
import com.github.steveice10.packetlib.packet.Packet;
import com.nukkitx.nbt.CompoundTagBuilder;
import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.nbt.stream.NBTOutputStream;
import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.protocol.bedrock.BedrockPacket;
import lombok.Getter;
public class Translators {
@Getter
private static ItemTranslator itemTranslator;
@Getter
private static Map<WindowType, InventoryTranslator> inventoryTranslators = new HashMap<>();
@Getter
private static Map<String, BlockEntityTranslator> blockEntityTranslators = new HashMap<>();
@Getter
private static ObjectArrayList<RequiresBlockState> requiresBlockStateMap = new ObjectArrayList<>();
private static final CompoundTag EMPTY_TAG = CompoundTagBuilder.builder().buildRootTag();
public static final byte[] EMPTY_LEVEL_CHUNK_DATA;
static {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
outputStream.write(new byte[258]); // Biomes + Border Size + Extra Data Size
try (NBTOutputStream stream = NbtUtils.createNetworkWriter(outputStream)) {
stream.write(EMPTY_TAG);
}
EMPTY_LEVEL_CHUNK_DATA = outputStream.toByteArray();
}catch (IOException e) {
throw new AssertionError("Unable to generate empty level chunk data");
}
}
@SuppressWarnings("unchecked")
public static void start() {
Reflections ref = new Reflections("org.geysermc.connector.network.translators");
for (Class<?> clazz : ref.getTypesAnnotatedWith(Translator.class)) {
Class<?> packet = clazz.getAnnotation(Translator.class).packet();
GeyserConnector.getInstance().getLogger().debug("Found annotated translator: " + clazz.getCanonicalName() + " : " + packet.getSimpleName());
try {
if (Packet.class.isAssignableFrom(packet)) {
Class<? extends Packet> targetPacket = (Class<? extends Packet>) packet;
PacketTranslator<? extends Packet> translator = (PacketTranslator<? extends Packet>) clazz.newInstance();
Registry.registerJava(targetPacket, translator);
} else if (BedrockPacket.class.isAssignableFrom(packet)) {
Class<? extends BedrockPacket> targetPacket = (Class<? extends BedrockPacket>) packet;
PacketTranslator<? extends BedrockPacket> translator = (PacketTranslator<? extends BedrockPacket>) clazz.newInstance();
Registry.registerBedrock(targetPacket, translator);
} else {
GeyserConnector.getInstance().getLogger().error("Class " + clazz.getCanonicalName() + " is annotated as a translator but has an invalid target packet.");
}
} catch (InstantiationException | IllegalAccessException e) {
GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated translator " + clazz.getCanonicalName() + ".");
}
}
itemTranslator = new ItemTranslator();
itemTranslator.init();
BlockTranslator.init();
registerBlockEntityTranslators();
registerInventoryTranslators();
}
private static void registerBlockEntityTranslators() {
Reflections ref = new Reflections("org.geysermc.connector.network.translators.world.block.entity");
for (Class<?> clazz : ref.getTypesAnnotatedWith(BlockEntity.class)) {
GeyserConnector.getInstance().getLogger().debug("Found annotated block entity: " + clazz.getCanonicalName());
try {
blockEntityTranslators.put(clazz.getAnnotation(BlockEntity.class).name(), (BlockEntityTranslator) clazz.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated block entity " + clazz.getCanonicalName() + ".");
}
}
for (Class<?> clazz : ref.getSubTypesOf(RequiresBlockState.class)) {
GeyserConnector.getInstance().getLogger().debug("Found block entity that requires block state: " + clazz.getCanonicalName());
try {
requiresBlockStateMap.add((RequiresBlockState) clazz.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
GeyserConnector.getInstance().getLogger().error("Could not instantiate required block state " + clazz.getCanonicalName() + ".");
}
}
}
private static void registerInventoryTranslators() {
inventoryTranslators.put(null, new PlayerInventoryTranslator()); //player inventory
inventoryTranslators.put(WindowType.GENERIC_9X1, new SingleChestInventoryTranslator(9));
inventoryTranslators.put(WindowType.GENERIC_9X2, new SingleChestInventoryTranslator(18));
inventoryTranslators.put(WindowType.GENERIC_9X3, new SingleChestInventoryTranslator(27));
inventoryTranslators.put(WindowType.GENERIC_9X4, new DoubleChestInventoryTranslator(36));
inventoryTranslators.put(WindowType.GENERIC_9X5, new DoubleChestInventoryTranslator(45));
inventoryTranslators.put(WindowType.GENERIC_9X6, new DoubleChestInventoryTranslator(54));
inventoryTranslators.put(WindowType.BREWING_STAND, new BrewingInventoryTranslator());
inventoryTranslators.put(WindowType.ANVIL, new AnvilInventoryTranslator());
inventoryTranslators.put(WindowType.CRAFTING, new CraftingInventoryTranslator());
inventoryTranslators.put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator());
//inventoryTranslators.put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO
InventoryTranslator furnace = new FurnaceInventoryTranslator();
inventoryTranslators.put(WindowType.FURNACE, furnace);
inventoryTranslators.put(WindowType.BLAST_FURNACE, furnace);
inventoryTranslators.put(WindowType.SMOKER, furnace);
InventoryUpdater containerUpdater = new ContainerInventoryUpdater();
inventoryTranslators.put(WindowType.GENERIC_3X3, new BlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER, containerUpdater));
inventoryTranslators.put(WindowType.HOPPER, new BlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, containerUpdater));
inventoryTranslators.put(WindowType.SHULKER_BOX, new BlockInventoryTranslator(27, "minecraft:shulker_box[facing=north]", ContainerType.CONTAINER, containerUpdater));
//inventoryTranslators.put(WindowType.BEACON, new BlockInventoryTranslator(1, "minecraft:beacon", ContainerType.BEACON)); //TODO
}
}

View File

@ -64,44 +64,44 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
break; break;
case START_SWIMMING: case START_SWIMMING:
ClientPlayerStatePacket startSwimPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_SPRINTING); ClientPlayerStatePacket startSwimPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_SPRINTING);
session.getDownstream().getSession().send(startSwimPacket); session.sendDownstreamPacket(startSwimPacket);
break; break;
case STOP_SWIMMING: case STOP_SWIMMING:
ClientPlayerStatePacket stopSwimPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.STOP_SPRINTING); ClientPlayerStatePacket stopSwimPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.STOP_SPRINTING);
session.getDownstream().getSession().send(stopSwimPacket); session.sendDownstreamPacket(stopSwimPacket);
break; break;
case START_GLIDE: case START_GLIDE:
case STOP_GLIDE: case STOP_GLIDE:
ClientPlayerStatePacket glidePacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_ELYTRA_FLYING); ClientPlayerStatePacket glidePacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_ELYTRA_FLYING);
session.getDownstream().getSession().send(glidePacket); session.sendDownstreamPacket(glidePacket);
break; break;
case START_SNEAK: case START_SNEAK:
ClientPlayerStatePacket startSneakPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_SNEAKING); ClientPlayerStatePacket startSneakPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_SNEAKING);
session.getDownstream().getSession().send(startSneakPacket); session.sendDownstreamPacket(startSneakPacket);
session.setSneaking(true); session.setSneaking(true);
break; break;
case STOP_SNEAK: case STOP_SNEAK:
ClientPlayerStatePacket stopSneakPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.STOP_SNEAKING); ClientPlayerStatePacket stopSneakPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.STOP_SNEAKING);
session.getDownstream().getSession().send(stopSneakPacket); session.sendDownstreamPacket(stopSneakPacket);
session.setSneaking(false); session.setSneaking(false);
break; break;
case START_SPRINT: case START_SPRINT:
ClientPlayerStatePacket startSprintPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_SPRINTING); ClientPlayerStatePacket startSprintPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_SPRINTING);
session.getDownstream().getSession().send(startSprintPacket); session.sendDownstreamPacket(startSprintPacket);
session.setSprinting(true); session.setSprinting(true);
break; break;
case STOP_SPRINT: case STOP_SPRINT:
ClientPlayerStatePacket stopSprintPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.STOP_SPRINTING); ClientPlayerStatePacket stopSprintPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.STOP_SPRINTING);
session.getDownstream().getSession().send(stopSprintPacket); session.sendDownstreamPacket(stopSprintPacket);
session.setSprinting(false); session.setSprinting(false);
break; break;
case DROP_ITEM: case DROP_ITEM:
ClientPlayerActionPacket dropItemPacket = new ClientPlayerActionPacket(PlayerAction.DROP_ITEM, position, BlockFace.values()[packet.getFace()]); ClientPlayerActionPacket dropItemPacket = new ClientPlayerActionPacket(PlayerAction.DROP_ITEM, position, BlockFace.values()[packet.getFace()]);
session.getDownstream().getSession().send(dropItemPacket); session.sendDownstreamPacket(dropItemPacket);
break; break;
case STOP_SLEEP: case STOP_SLEEP:
ClientPlayerStatePacket stopSleepingPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.LEAVE_BED); ClientPlayerStatePacket stopSleepingPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.LEAVE_BED);
session.getDownstream().getSession().send(stopSleepingPacket); session.sendDownstreamPacket(stopSleepingPacket);
break; break;
case BLOCK_INTERACT: case BLOCK_INTERACT:
// Handled in BedrockInventoryTransactionTranslator // Handled in BedrockInventoryTransactionTranslator
@ -109,19 +109,19 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
case START_BREAK: case START_BREAK:
ClientPlayerActionPacket startBreakingPacket = new ClientPlayerActionPacket(PlayerAction.START_DIGGING, new Position(packet.getBlockPosition().getX(), ClientPlayerActionPacket startBreakingPacket = new ClientPlayerActionPacket(PlayerAction.START_DIGGING, new Position(packet.getBlockPosition().getX(),
packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()), BlockFace.values()[packet.getFace()]); packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()), BlockFace.values()[packet.getFace()]);
session.getDownstream().getSession().send(startBreakingPacket); session.sendDownstreamPacket(startBreakingPacket);
break; break;
case CONTINUE_BREAK: case CONTINUE_BREAK:
LevelEventPacket continueBreakPacket = new LevelEventPacket(); LevelEventPacket continueBreakPacket = new LevelEventPacket();
continueBreakPacket.setType(LevelEventType.PUNCH_BLOCK); continueBreakPacket.setType(LevelEventType.PUNCH_BLOCK);
continueBreakPacket.setData(BlockTranslator.getBedrockBlockId(session.getBreakingBlock() == null ? BlockTranslator.AIR : session.getBreakingBlock())); continueBreakPacket.setData(BlockTranslator.getBedrockBlockId(session.getBreakingBlock() == null ? BlockTranslator.AIR : session.getBreakingBlock()));
continueBreakPacket.setPosition(packet.getBlockPosition().toFloat()); continueBreakPacket.setPosition(packet.getBlockPosition().toFloat());
session.getUpstream().sendPacket(continueBreakPacket); session.sendUpstreamPacket(continueBreakPacket);
break; break;
case ABORT_BREAK: case ABORT_BREAK:
ClientPlayerActionPacket abortBreakingPacket = new ClientPlayerActionPacket(PlayerAction.CANCEL_DIGGING, new Position(packet.getBlockPosition().getX(), ClientPlayerActionPacket abortBreakingPacket = new ClientPlayerActionPacket(PlayerAction.CANCEL_DIGGING, new Position(packet.getBlockPosition().getX(),
packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()), BlockFace.DOWN); packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()), BlockFace.DOWN);
session.getDownstream().getSession().send(abortBreakingPacket); session.sendDownstreamPacket(abortBreakingPacket);
break; break;
case STOP_BREAK: case STOP_BREAK:
// Handled in BedrockInventoryTransactionTranslator // Handled in BedrockInventoryTransactionTranslator
@ -131,7 +131,7 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
//sometimes the client doesn't feel like loading //sometimes the client doesn't feel like loading
PlayStatusPacket spawnPacket = new PlayStatusPacket(); PlayStatusPacket spawnPacket = new PlayStatusPacket();
spawnPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN); spawnPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN);
session.getUpstream().sendPacket(spawnPacket); session.sendUpstreamPacket(spawnPacket);
entity.updateBedrockAttributes(session); entity.updateBedrockAttributes(session);
session.getEntityCache().updateBossBars(); session.getEntityCache().updateBossBars();
} }

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*
*/
package org.geysermc.connector.network.translators.bedrock;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerAbilitiesPacket;
import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
@Translator(packet = AdventureSettingsPacket.class)
public class BedrockAdventureSettingsTranslator extends PacketTranslator<AdventureSettingsPacket> {
@Override
public void translate(AdventureSettingsPacket packet, GeyserSession session) {
// Only canFly and flying are used by the server
// https://wiki.vg/Protocol#Player_Abilities_.28serverbound.29
boolean canFly = packet.getFlags().contains(AdventureSettingsPacket.Flag.MAY_FLY);
boolean flying = packet.getFlags().contains(AdventureSettingsPacket.Flag.FLYING);
boolean creative = session.getGameMode() == GameMode.CREATIVE;
ClientPlayerAbilitiesPacket abilitiesPacket = new ClientPlayerAbilitiesPacket(
false, canFly, flying, creative, 0.05f, 0.1f
);
session.sendDownstreamPacket(abilitiesPacket);
}
}

View File

@ -31,6 +31,7 @@ import org.geysermc.connector.network.translators.Translator;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerSwingArmPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerSwingArmPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientSteerBoatPacket;
import com.nukkitx.protocol.bedrock.packet.AnimatePacket; import com.nukkitx.protocol.bedrock.packet.AnimatePacket;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -38,6 +39,9 @@ import java.util.concurrent.TimeUnit;
@Translator(packet = AnimatePacket.class) @Translator(packet = AnimatePacket.class)
public class BedrockAnimateTranslator extends PacketTranslator<AnimatePacket> { public class BedrockAnimateTranslator extends PacketTranslator<AnimatePacket> {
private boolean isSteeringLeft;
private boolean isSteeringRight;
@Override @Override
public void translate(AnimatePacket packet, GeyserSession session) { public void translate(AnimatePacket packet, GeyserSession session) {
// Stop the player sending animations before they have fully spawned into the server // Stop the player sending animations before they have fully spawned into the server
@ -49,11 +53,23 @@ public class BedrockAnimateTranslator extends PacketTranslator<AnimatePacket> {
case SWING_ARM: case SWING_ARM:
// Delay so entity damage can be processed first // Delay so entity damage can be processed first
session.getConnector().getGeneralThreadPool().schedule(() -> session.getConnector().getGeneralThreadPool().schedule(() ->
session.getDownstream().getSession().send(new ClientPlayerSwingArmPacket(Hand.MAIN_HAND)), session.sendDownstreamPacket(new ClientPlayerSwingArmPacket(Hand.MAIN_HAND)),
25, 25,
TimeUnit.MILLISECONDS TimeUnit.MILLISECONDS
); );
break; break;
// These two might need to be flipped, but my recommendation is getting moving working first
case ROW_LEFT:
// Packet value is a float of how long one has been rowing, so we convert that into a boolean
isSteeringLeft = packet.getRowingTime() > 0.0;
ClientSteerBoatPacket steerLeftPacket = new ClientSteerBoatPacket(isSteeringRight, isSteeringLeft);
session.sendDownstreamPacket(steerLeftPacket);
break;
case ROW_RIGHT:
isSteeringRight = packet.getRowingTime() > 0.0;
ClientSteerBoatPacket steerRightPacket = new ClientSteerBoatPacket(isSteeringRight, isSteeringLeft);
session.sendDownstreamPacket(steerRightPacket);
break;
} }
} }
} }

View File

@ -79,7 +79,7 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator<BlockEnti
// Put the final line on since it isn't done in the for loop // Put the final line on since it isn't done in the for loop
if (iterator < lines.length) lines[iterator] = newMessage.toString(); if (iterator < lines.length) lines[iterator] = newMessage.toString();
ClientUpdateSignPacket clientUpdateSignPacket = new ClientUpdateSignPacket(pos, lines); ClientUpdateSignPacket clientUpdateSignPacket = new ClientUpdateSignPacket(pos, lines);
session.getDownstream().getSession().send(clientUpdateSignPacket); session.sendDownstreamPacket(clientUpdateSignPacket);
//TODO (potentially): originally I was going to update the sign blocks so Bedrock and Java users would match visually //TODO (potentially): originally I was going to update the sign blocks so Bedrock and Java users would match visually
// However Java can still store a lot per-line and visuals are still messed up so that doesn't work // However Java can still store a lot per-line and visuals are still messed up so that doesn't work

View File

@ -0,0 +1,101 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*
*/
package org.geysermc.connector.network.translators.bedrock;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientMoveItemToHotbarPacket;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.packet.BlockPickRequestPacket;
import com.nukkitx.protocol.bedrock.packet.PlayerHotbarPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
@Translator(packet = BlockPickRequestPacket.class)
public class BedrockBlockPickRequestPacketTranslator extends PacketTranslator<BlockPickRequestPacket> {
@Override
public void translate(BlockPickRequestPacket packet, GeyserSession session) {
Vector3i vector = packet.getBlockPosition();
BlockState blockToPick = session.getConnector().getWorldManager().getBlockAt(session, vector.getX(), vector.getY(), vector.getZ());
// Block is air - chunk caching is probably off
if (blockToPick.getId() == 0) {
return;
}
// Get the inventory to choose a slot to pick
Inventory inventory = session.getInventoryCache().getOpenInventory();
if (inventory == null) {
inventory = session.getInventory();
}
String targetIdentifier = BlockTranslator.getJavaIdBlockMap().inverse().get(blockToPick).split("\\[")[0];
// Check hotbar for item
for (int i = 36; i < 45; i++) {
if (inventory.getItem(i) == null) {
continue;
}
ItemEntry item = ItemRegistry.getItem(inventory.getItem(i));
// If this isn't the item we're looking for
if (!item.getJavaIdentifier().equals(targetIdentifier)) {
continue;
}
PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket();
hotbarPacket.setContainerId(0);
// Java inventory slot to hotbar slot ID
hotbarPacket.setSelectedHotbarSlot(i - 36);
hotbarPacket.setSelectHotbarSlot(true);
session.sendUpstreamPacket(hotbarPacket);
session.getInventory().setHeldItemSlot(i - 36);
// Don't check inventory if item was in hotbar
return;
}
// Check inventory for item
for (int i = 9; i < 36; i++) {
if (inventory.getItem(i) == null) {
continue;
}
ItemEntry item = ItemRegistry.getItem(inventory.getItem(i));
// If this isn't the item we're looking for
if (!item.getJavaIdentifier().equals(targetIdentifier)) {
continue;
}
ClientMoveItemToHotbarPacket packetToSend = new ClientMoveItemToHotbarPacket(i); // https://wiki.vg/Protocol#Pick_Item
session.sendDownstreamPacket(packetToSend);
return;
}
}
}

View File

@ -53,7 +53,7 @@ public class BedrockCommandRequestTranslator extends PacketTranslator<CommandReq
} }
ClientChatPacket chatPacket = new ClientChatPacket(message); ClientChatPacket chatPacket = new ClientChatPacket(message);
session.getDownstream().getSession().send(chatPacket); session.sendDownstreamPacket(chatPacket);
} }
} }
} }

View File

@ -38,17 +38,21 @@ public class BedrockContainerCloseTranslator extends PacketTranslator<ContainerC
@Override @Override
public void translate(ContainerClosePacket packet, GeyserSession session) { public void translate(ContainerClosePacket packet, GeyserSession session) {
session.setLastWindowCloseTime(0);
byte windowId = packet.getWindowId(); byte windowId = packet.getWindowId();
Inventory openInventory = session.getInventoryCache().getOpenInventory();
if (windowId == -1) { //player inventory or crafting table if (windowId == -1) { //player inventory or crafting table
Inventory openInventory = session.getInventoryCache().getOpenInventory();
if (openInventory != null) { if (openInventory != null) {
windowId = (byte) openInventory.getId(); windowId = (byte) openInventory.getId();
} else { } else {
windowId = 0; windowId = 0;
} }
} }
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId);
session.getDownstream().getSession().send(closeWindowPacket); if (windowId == 0 || (openInventory != null && openInventory.getId() == windowId)) {
InventoryUtils.closeInventory(session, windowId); ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId);
session.getDownstream().getSession().send(closeWindowPacket);
InventoryUtils.closeInventory(session, windowId);
}
} }
} }

View File

@ -39,7 +39,7 @@ public class BedrockEntityEventTranslator extends PacketTranslator<EntityEventPa
switch (packet.getType()) { switch (packet.getType()) {
// Resend the packet so we get the eating sounds // Resend the packet so we get the eating sounds
case EATING_ITEM: case EATING_ITEM:
session.getUpstream().sendPacket(packet); session.sendUpstreamPacket(packet);
return; return;
} }
session.getConnector().getLogger().debug("Did not translate incoming EntityEventPacket: " + packet.toString()); session.getConnector().getLogger().debug("Did not translate incoming EntityEventPacket: " + packet.toString());

View File

@ -25,6 +25,8 @@
package org.geysermc.connector.network.translators.bedrock; package org.geysermc.connector.network.translators.bedrock;
import com.nukkitx.protocol.bedrock.data.EntityData;
import com.nukkitx.protocol.bedrock.data.EntityFlag;
import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.PacketTranslator;
@ -32,9 +34,11 @@ import org.geysermc.connector.network.translators.Translator;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction; import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction;
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerState;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerStatePacket;
import com.nukkitx.protocol.bedrock.packet.InteractPacket; import com.nukkitx.protocol.bedrock.packet.InteractPacket;
import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.network.translators.item.ItemRegistry;
@Translator(packet = InteractPacket.class) @Translator(packet = InteractPacket.class)
public class BedrockInteractTranslator extends PacketTranslator<InteractPacket> { public class BedrockInteractTranslator extends PacketTranslator<InteractPacket> {
@ -47,17 +51,69 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
switch (packet.getAction()) { switch (packet.getAction()) {
case INTERACT: case INTERACT:
if (session.getInventory().getItem(session.getInventory().getHeldItemSlot() + 36).getId() == ItemTranslator.SHIELD) { if (session.getInventory().getItem(session.getInventory().getHeldItemSlot() + 36).getId() == ItemRegistry.SHIELD) {
break; break;
} }
ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(), ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
InteractAction.INTERACT, Hand.MAIN_HAND); InteractAction.INTERACT, Hand.MAIN_HAND);
session.getDownstream().getSession().send(interactPacket); session.sendDownstreamPacket(interactPacket);
break; break;
case DAMAGE: case DAMAGE:
ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(), ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
InteractAction.ATTACK, Hand.MAIN_HAND); InteractAction.ATTACK, Hand.MAIN_HAND);
session.getDownstream().getSession().send(attackPacket); session.sendDownstreamPacket(attackPacket);
break;
case LEAVE_VEHICLE:
ClientPlayerStatePacket sneakPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_SNEAKING);
session.sendDownstreamPacket(sneakPacket);
session.setRidingVehicleEntity(null);
break;
case MOUSEOVER:
// Handle the buttons for mobile - "Mount", etc; and the suggestions for console - "ZL: Mount", etc
if (packet.getRuntimeEntityId() != 0) {
Entity interactEntity = session.getEntityCache().getEntityByGeyserId(packet.getRuntimeEntityId());
if (interactEntity == null)
return;
String interactiveTag;
switch (interactEntity.getEntityType()) {
case PIG:
if (interactEntity.getMetadata().getFlags().getFlag(EntityFlag.SADDLED)) {
interactiveTag = "action.interact.mount";
} else interactiveTag = "";
break;
case HORSE:
case SKELETON_HORSE:
case ZOMBIE_HORSE:
case DONKEY:
case MULE:
case LLAMA:
case TRADER_LLAMA:
if (interactEntity.getMetadata().getFlags().getFlag(EntityFlag.TAMED)) {
interactiveTag = "action.interact.ride.horse";
} else {
interactiveTag = "action.interact.mount";
}
break;
case BOAT:
interactiveTag = "action.interact.ride.boat";
break;
case MINECART:
interactiveTag = "action.interact.ride.minecart";
break;
default:
return; // No need to process any further since there is no interactive tag
}
session.getPlayerEntity().getMetadata().put(EntityData.INTERACTIVE_TAG, interactiveTag);
session.getPlayerEntity().updateBedrockMetadata(session);
} else {
if (!(session.getPlayerEntity().getMetadata().get(EntityData.INTERACTIVE_TAG) == null) ||
!(session.getPlayerEntity().getMetadata().get(EntityData.INTERACTIVE_TAG) == "")) {
// No interactive tag should be sent
session.getPlayerEntity().getMetadata().remove(EntityData.INTERACTIVE_TAG);
session.getPlayerEntity().updateBedrockMetadata(session);
}
}
break; break;
} }
} }

View File

@ -25,6 +25,7 @@
package org.geysermc.connector.network.translators.bedrock; package org.geysermc.connector.network.translators.bedrock;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
@ -48,9 +49,9 @@ import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.network.translators.Translators; import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.sound.EntitySoundInteractionHandler; import org.geysermc.connector.network.translators.sound.EntitySoundInteractionHandler;
import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.InventoryUtils; import org.geysermc.connector.utils.InventoryUtils;
@ -64,12 +65,12 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
case NORMAL: case NORMAL:
Inventory inventory = session.getInventoryCache().getOpenInventory(); Inventory inventory = session.getInventoryCache().getOpenInventory();
if (inventory == null) inventory = session.getInventory(); if (inventory == null) inventory = session.getInventory();
Translators.getInventoryTranslators().get(inventory.getWindowType()).translateActions(session, inventory, packet.getActions()); InventoryTranslator.INVENTORY_TRANSLATORS.get(inventory.getWindowType()).translateActions(session, inventory, packet.getActions());
break; break;
case INVENTORY_MISMATCH: case INVENTORY_MISMATCH:
Inventory inv = session.getInventoryCache().getOpenInventory(); Inventory inv = session.getInventoryCache().getOpenInventory();
if (inv == null) inv = session.getInventory(); if (inv == null) inv = session.getInventory();
Translators.getInventoryTranslators().get(inv.getWindowType()).updateInventory(session, inv); InventoryTranslator.INVENTORY_TRANSLATORS.get(inv.getWindowType()).updateInventory(session, inv);
InventoryUtils.updateCursor(session); InventoryUtils.updateCursor(session);
break; break;
case ITEM_USE: case ITEM_USE:
@ -84,8 +85,8 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
InteractAction.INTERACT, Hand.MAIN_HAND); InteractAction.INTERACT, Hand.MAIN_HAND);
ClientPlayerInteractEntityPacket interactAtPacket = new ClientPlayerInteractEntityPacket((int) ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition()), ClientPlayerInteractEntityPacket interactAtPacket = new ClientPlayerInteractEntityPacket((int) ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition()),
InteractAction.INTERACT_AT, vector.getX(), vector.getY(), vector.getZ(), Hand.MAIN_HAND); InteractAction.INTERACT_AT, vector.getX(), vector.getY(), vector.getZ(), Hand.MAIN_HAND);
session.getDownstream().getSession().send(interactPacket); session.sendDownstreamPacket(interactPacket);
session.getDownstream().getSession().send(interactAtPacket); session.sendDownstreamPacket(interactAtPacket);
break; break;
} }
@ -95,7 +96,14 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
Hand.MAIN_HAND, Hand.MAIN_HAND,
packet.getClickPosition().getX(), packet.getClickPosition().getY(), packet.getClickPosition().getZ(), packet.getClickPosition().getX(), packet.getClickPosition().getY(), packet.getClickPosition().getZ(),
false); false);
session.getDownstream().getSession().send(blockPacket); session.sendDownstreamPacket(blockPacket);
// Otherwise boats will not be able to be placed in survival
if (packet.getItemInHand() != null && packet.getItemInHand().getId() == ItemRegistry.BOAT) {
ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
session.sendDownstreamPacket(itemPacket);
}
Vector3i blockPos = packet.getBlockPosition(); Vector3i blockPos = packet.getBlockPosition();
// TODO: Find a better way to do this? // TODO: Find a better way to do this?
switch (packet.getFace()) { switch (packet.getFace()) {
@ -118,7 +126,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
blockPos = blockPos.add(1, 0, 0); blockPos = blockPos.add(1, 0, 0);
break; break;
} }
ItemEntry handItem = Translators.getItemTranslator().getItem(packet.getItemInHand()); ItemEntry handItem = ItemRegistry.getItem(packet.getItemInHand());
if (handItem.isBlock()) { if (handItem.isBlock()) {
session.setLastBlockPlacePosition(blockPos); session.setLastBlockPlacePosition(blockPos);
session.setLastBlockPlacedId(handItem.getJavaIdentifier()); session.setLastBlockPlacedId(handItem.getJavaIdentifier());
@ -127,11 +135,14 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
session.setInteracting(true); session.setInteracting(true);
break; break;
case 1: case 1:
if (session.getInventory().getItem(session.getInventory().getHeldItemSlot() + 36).getId() == ItemTranslator.SHIELD) { ItemStack shieldSlot = session.getInventory().getItem(session.getInventory().getHeldItemSlot() + 36);
if (shieldSlot != null && shieldSlot.getId() == ItemRegistry.SHIELD) {
break; break;
} // Handled in Entity.java } // Handled in Entity.java
ClientPlayerUseItemPacket useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND); ClientPlayerUseItemPacket useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
session.getDownstream().getSession().send(useItemPacket); session.sendDownstreamPacket(useItemPacket);
// Used for sleeping in beds
session.setLastInteractionPosition(packet.getBlockPosition());
break; break;
case 2: case 2:
BlockState blockState = session.getConnector().getWorldManager().getBlockAt(session, packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()); BlockState blockState = session.getConnector().getWorldManager().getBlockAt(session, packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ());
@ -144,21 +155,21 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
blockBreakPacket.setType(LevelEventType.DESTROY); blockBreakPacket.setType(LevelEventType.DESTROY);
blockBreakPacket.setPosition(packet.getBlockPosition().toFloat()); blockBreakPacket.setPosition(packet.getBlockPosition().toFloat());
blockBreakPacket.setData(BlockTranslator.getBedrockBlockId(blockState)); blockBreakPacket.setData(BlockTranslator.getBedrockBlockId(blockState));
session.getUpstream().sendPacket(blockBreakPacket); session.sendUpstreamPacket(blockBreakPacket);
} }
if (ItemFrameEntity.positionContainsItemFrame(session, packet.getBlockPosition()) && if (ItemFrameEntity.positionContainsItemFrame(session, packet.getBlockPosition()) &&
session.getEntityCache().getEntityByJavaId(ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition())) != null) { session.getEntityCache().getEntityByJavaId(ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition())) != null) {
ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition()), ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) ItemFrameEntity.getItemFrameEntityId(session, packet.getBlockPosition()),
InteractAction.ATTACK); InteractAction.ATTACK);
session.getDownstream().getSession().send(attackPacket); session.sendDownstreamPacket(attackPacket);
break; break;
} }
PlayerAction action = session.getGameMode() == GameMode.CREATIVE ? PlayerAction.START_DIGGING : PlayerAction.FINISH_DIGGING; PlayerAction action = session.getGameMode() == GameMode.CREATIVE ? PlayerAction.START_DIGGING : PlayerAction.FINISH_DIGGING;
Position pos = new Position(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()); Position pos = new Position(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ());
ClientPlayerActionPacket breakPacket = new ClientPlayerActionPacket(action, pos, BlockFace.values()[packet.getFace()]); ClientPlayerActionPacket breakPacket = new ClientPlayerActionPacket(action, pos, BlockFace.values()[packet.getFace()]);
session.getDownstream().getSession().send(breakPacket); session.sendDownstreamPacket(breakPacket);
break; break;
} }
break; break;
@ -167,7 +178,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
// Followed to the Minecraft Protocol specification outlined at wiki.vg // Followed to the Minecraft Protocol specification outlined at wiki.vg
ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, new Position(0,0,0), ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, new Position(0,0,0),
BlockFace.DOWN); BlockFace.DOWN);
session.getDownstream().getSession().send(releaseItemPacket); session.sendDownstreamPacket(releaseItemPacket);
} }
break; break;
case ITEM_USE_ON_ENTITY: case ITEM_USE_ON_ENTITY:
@ -183,15 +194,15 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
InteractAction.INTERACT, Hand.MAIN_HAND); InteractAction.INTERACT, Hand.MAIN_HAND);
ClientPlayerInteractEntityPacket interactAtPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(), ClientPlayerInteractEntityPacket interactAtPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
InteractAction.INTERACT_AT, vector.getX(), vector.getY(), vector.getZ(), Hand.MAIN_HAND); InteractAction.INTERACT_AT, vector.getX(), vector.getY(), vector.getZ(), Hand.MAIN_HAND);
session.getDownstream().getSession().send(interactPacket); session.sendDownstreamPacket(interactPacket);
session.getDownstream().getSession().send(interactAtPacket); session.sendDownstreamPacket(interactAtPacket);
EntitySoundInteractionHandler.handleEntityInteraction(session, vector, entity); EntitySoundInteractionHandler.handleEntityInteraction(session, vector, entity);
break; break;
case 1: //Attack case 1: //Attack
ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(), ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
InteractAction.ATTACK); InteractAction.ATTACK);
session.getDownstream().getSession().send(attackPacket); session.sendDownstreamPacket(attackPacket);
break; break;
} }
break; break;

View File

@ -51,7 +51,7 @@ public class BedrockItemFrameDropItemTranslator extends PacketTranslator<ItemFra
Vector3i position = Vector3i.from(packet.getBlockPosition().getX(), y, packet.getBlockPosition().getZ()); Vector3i position = Vector3i.from(packet.getBlockPosition().getX(), y, packet.getBlockPosition().getZ());
ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) ItemFrameEntity.getItemFrameEntityId(session, position), ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) ItemFrameEntity.getItemFrameEntityId(session, position),
InteractAction.ATTACK, Hand.MAIN_HAND); InteractAction.ATTACK, Hand.MAIN_HAND);
session.getDownstream().getSession().send(interactPacket); session.sendDownstreamPacket(interactPacket);
} }
} }

View File

@ -36,6 +36,6 @@ public class BedrockLevelSoundEventTranslator extends PacketTranslator<LevelSoun
@Override @Override
public void translate(LevelSoundEventPacket packet, GeyserSession session) { public void translate(LevelSoundEventPacket packet, GeyserSession session) {
// lol what even :thinking: // lol what even :thinking:
session.getUpstream().sendPacket(packet); session.sendUpstreamPacket(packet);
} }
} }

View File

@ -46,6 +46,6 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
session.getInventory().setHeldItemSlot(packet.getHotbarSlot()); session.getInventory().setHeldItemSlot(packet.getHotbarSlot());
ClientPlayerChangeHeldItemPacket changeHeldItemPacket = new ClientPlayerChangeHeldItemPacket(packet.getHotbarSlot()); ClientPlayerChangeHeldItemPacket changeHeldItemPacket = new ClientPlayerChangeHeldItemPacket(packet.getHotbarSlot());
session.getDownstream().getSession().send(changeHeldItemPacket); session.sendDownstreamPacket(changeHeldItemPacket);
} }
} }

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*
*/
package org.geysermc.connector.network.translators.bedrock;
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientVehicleMovePacket;
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
// Used for horses
@Translator(packet = MoveEntityAbsolutePacket.class)
public class BedrockMoveEntityAbsoluteTranslator extends PacketTranslator<MoveEntityAbsolutePacket> {
@Override
public void translate(MoveEntityAbsolutePacket packet, GeyserSession session) {
ClientVehicleMovePacket clientVehicleMovePacket = new ClientVehicleMovePacket(
packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ(),
packet.getRotation().getY() - 90, packet.getRotation().getX()
);
session.sendDownstreamPacket(clientVehicleMovePacket);
}
}

View File

@ -55,7 +55,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
moveEntityBack.setRotation(entity.getBedrockRotation()); moveEntityBack.setRotation(entity.getBedrockRotation());
moveEntityBack.setTeleported(true); moveEntityBack.setTeleported(true);
moveEntityBack.setOnGround(true); moveEntityBack.setOnGround(true);
session.getUpstream().sendPacketImmediately(moveEntityBack); session.sendUpstreamPacketImmediately(moveEntityBack);
return; return;
} }
@ -85,6 +85,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
Vector3f rotation = Vector3f.from(packet.getRotation().getY(), packet.getRotation().getX(), packet.getRotation().getY()); Vector3f rotation = Vector3f.from(packet.getRotation().getY(), packet.getRotation().getX(), packet.getRotation().getY());
entity.setPosition(packet.getPosition().sub(0, EntityType.PLAYER.getOffset(), 0)); entity.setPosition(packet.getPosition().sub(0, EntityType.PLAYER.getOffset(), 0));
entity.setRotation(rotation); entity.setRotation(rotation);
entity.setOnGround(packet.isOnGround());
/* /*
boolean colliding = false; boolean colliding = false;
@ -97,7 +98,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
if (!colliding) if (!colliding)
*/ */
session.getDownstream().getSession().send(playerPositionRotationPacket); session.sendDownstreamPacket(playerPositionRotationPacket);
} }
public boolean isValidMove(GeyserSession session, MovePlayerPacket.Mode mode, Vector3f currentPosition, Vector3f newPosition) { public boolean isValidMove(GeyserSession session, MovePlayerPacket.Mode mode, Vector3f currentPosition, Vector3f newPosition) {
@ -130,13 +131,13 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket(); SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
entityDataPacket.setRuntimeEntityId(entity.getGeyserId()); entityDataPacket.setRuntimeEntityId(entity.getGeyserId());
entityDataPacket.getMetadata().putAll(entity.getMetadata()); entityDataPacket.getMetadata().putAll(entity.getMetadata());
session.getUpstream().sendPacket(entityDataPacket); session.sendUpstreamPacket(entityDataPacket);
MovePlayerPacket movePlayerPacket = new MovePlayerPacket(); MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId()); movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
movePlayerPacket.setPosition(entity.getPosition()); movePlayerPacket.setPosition(entity.getPosition());
movePlayerPacket.setRotation(entity.getBedrockRotation()); movePlayerPacket.setRotation(entity.getBedrockRotation());
movePlayerPacket.setMode(MovePlayerPacket.Mode.RESET); movePlayerPacket.setMode(MovePlayerPacket.Mode.RESET);
session.getUpstream().sendPacket(movePlayerPacket); session.sendUpstreamPacket(movePlayerPacket);
} }
} }

View File

@ -0,0 +1,21 @@
package org.geysermc.connector.network.translators.bedrock;
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientSteerVehiclePacket;
import com.nukkitx.protocol.bedrock.packet.PlayerInputPacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
// Makes minecarts respond to player input
@Translator(packet = PlayerInputPacket.class)
public class BedrockPlayerInputTranslator extends PacketTranslator<PlayerInputPacket> {
@Override
public void translate(PlayerInputPacket packet, GeyserSession session) {
ClientSteerVehiclePacket clientSteerVehiclePacket = new ClientSteerVehiclePacket(
packet.getInputMotion().getX(), packet.getInputMotion().getY(), packet.isJumping(), packet.isSneaking()
);
session.sendDownstreamPacket(clientSteerVehiclePacket);
}
}

View File

@ -44,10 +44,10 @@ public class BedrockRespawnTranslator extends PacketTranslator<RespawnPacket> {
respawnPacket.setRuntimeEntityId(0); respawnPacket.setRuntimeEntityId(0);
respawnPacket.setPosition(Vector3f.ZERO); respawnPacket.setPosition(Vector3f.ZERO);
respawnPacket.setState(RespawnPacket.State.SERVER_SEARCHING); respawnPacket.setState(RespawnPacket.State.SERVER_SEARCHING);
session.getUpstream().sendPacket(respawnPacket); session.sendUpstreamPacket(respawnPacket);
ClientRequestPacket javaRespawnPacket = new ClientRequestPacket(ClientRequest.RESPAWN); ClientRequestPacket javaRespawnPacket = new ClientRequestPacket(ClientRequest.RESPAWN);
session.getDownstream().getSession().send(javaRespawnPacket); session.sendDownstreamPacket(javaRespawnPacket);
} }
} }
} }

View File

@ -40,7 +40,7 @@ public class BedrockShowCreditsTranslator extends PacketTranslator<ShowCreditsPa
public void translate(ShowCreditsPacket packet, GeyserSession session) { public void translate(ShowCreditsPacket packet, GeyserSession session) {
if (packet.getStatus() == ShowCreditsPacket.Status.END_CREDITS) { if (packet.getStatus() == ShowCreditsPacket.Status.END_CREDITS) {
ClientRequestPacket javaRespawnPacket = new ClientRequestPacket(ClientRequest.RESPAWN); ClientRequestPacket javaRespawnPacket = new ClientRequestPacket(ClientRequest.RESPAWN);
session.getDownstream().getSession().send(javaRespawnPacket); session.sendDownstreamPacket(javaRespawnPacket);
} }
} }
} }

View File

@ -45,6 +45,6 @@ public class BedrockTextTranslator extends PacketTranslator<TextPacket> {
} }
ClientChatPacket chatPacket = new ClientChatPacket(message); ClientChatPacket chatPacket = new ClientChatPacket(message);
session.getDownstream().getSession().send(chatPacket); session.sendDownstreamPacket(chatPacket);
} }
} }

View File

@ -1,49 +1,50 @@
/* /*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in * The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. * all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * 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 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
* *
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/ */
package org.geysermc.connector.utils; package org.geysermc.connector.network.translators.effect;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.github.steveice10.mc.protocol.data.game.world.particle.ParticleType; import com.github.steveice10.mc.protocol.data.game.world.particle.ParticleType;
import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.data.SoundEvent;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.NonNull; import lombok.NonNull;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.translators.effect.Effect; import org.geysermc.connector.utils.FileUtils;
import java.io.InputStream; import java.io.InputStream;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
public class EffectUtils { /**
* Registry for particles and effects.
*/
public class EffectRegistry {
public static final Map<String, Effect> EFFECTS = new HashMap<>(); public static final Map<String, Effect> EFFECTS = new HashMap<>();
public static final Int2ObjectMap<SoundEvent> RECORDS = new Int2ObjectOpenHashMap<>(); public static final Int2ObjectMap<SoundEvent> RECORDS = new Int2ObjectOpenHashMap<>();
@ -57,10 +58,10 @@ public class EffectUtils {
static { static {
/* Load particles */ /* Load particles */
InputStream particleStream = Toolbox.getResource("mappings/particles.json"); InputStream particleStream = FileUtils.getResource("mappings/particles.json");
JsonNode particleEntries; JsonNode particleEntries;
try { try {
particleEntries = Toolbox.JSON_MAPPER.readTree(particleStream); particleEntries = GeyserConnector.JSON_MAPPER.readTree(particleStream);
} catch (Exception e) { } catch (Exception e) {
throw new AssertionError("Unable to load particle map", e); throw new AssertionError("Unable to load particle map", e);
} }
@ -69,10 +70,10 @@ public class EffectUtils {
while (particlesIterator.hasNext()) { while (particlesIterator.hasNext()) {
Map.Entry<String, JsonNode> entry = particlesIterator.next(); Map.Entry<String, JsonNode> entry = particlesIterator.next();
try { try {
setIdentifier(ParticleType.valueOf(entry.getKey().toUpperCase()), LevelEventType.valueOf(entry.getValue().asText().toUpperCase())); particleTypeMap.put(ParticleType.valueOf(entry.getKey().toUpperCase()), LevelEventType.valueOf(entry.getValue().asText().toUpperCase()));
} catch (IllegalArgumentException e1) { } catch (IllegalArgumentException e1) {
try { try {
setIdentifier(ParticleType.valueOf(entry.getKey().toUpperCase()), entry.getValue().asText()); particleStringMap.put(ParticleType.valueOf(entry.getKey().toUpperCase()), entry.getValue().asText());
GeyserConnector.getInstance().getLogger().debug("Force to map particle " GeyserConnector.getInstance().getLogger().debug("Force to map particle "
+ entry.getKey() + entry.getKey()
+ "=>" + "=>"
@ -85,10 +86,10 @@ public class EffectUtils {
} }
/* Load effects */ /* Load effects */
InputStream effectsStream = Toolbox.getResource("mappings/effects.json"); InputStream effectsStream = FileUtils.getResource("mappings/effects.json");
JsonNode effects; JsonNode effects;
try { try {
effects = Toolbox.JSON_MAPPER.readTree(effectsStream); effects = GeyserConnector.JSON_MAPPER.readTree(effectsStream);
} catch (Exception e) { } catch (Exception e) {
throw new AssertionError("Unable to load effects mappings", e); throw new AssertionError("Unable to load effects mappings", e);
} }
@ -112,14 +113,6 @@ public class EffectUtils {
} }
} }
public static void setIdentifier(ParticleType type, LevelEventType identifier) {
particleTypeMap.put(type, identifier);
}
public static void setIdentifier(ParticleType type, String identifier) {
particleStringMap.put(type, identifier);
}
public static LevelEventType getParticleLevelEventType(@NonNull ParticleType type) { public static LevelEventType getParticleLevelEventType(@NonNull ParticleType type) {
return particleTypeMap.getOrDefault(type, null); return particleTypeMap.getOrDefault(type, null);
} }
@ -127,5 +120,4 @@ public class EffectUtils {
public static String getParticleString(@NonNull ParticleType type){ public static String getParticleString(@NonNull ParticleType type){
return particleStringMap.getOrDefault(type, null); return particleStringMap.getOrDefault(type, null);
} }
} }

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