Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-11-04 23:30:17 +01:00
Merge remote-tracking branch 'origin/master' into floodgate-2.0
# Conflicts: # connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java # connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockNetworkStackLatencyTranslator.java # connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java # connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java
Dieser Commit ist enthalten in:
Commit
cd13e03730
28
README.md
28
README.md
@ -18,7 +18,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
|
||||
|
||||
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have now joined us here!
|
||||
|
||||
### Currently supporting Minecraft Bedrock v1.16.100 and Minecraft Java v1.16.4.
|
||||
### Currently supporting Minecraft Bedrock v1.16.100/v1.16.101/v1.16.200 and Minecraft Java v1.16.4.
|
||||
|
||||
## Setting Up
|
||||
Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set up Geyser.
|
||||
@ -34,16 +34,24 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set
|
||||
- Test Server: `test.geysermc.org` port `25565` for Java and `19132` for Bedrock
|
||||
|
||||
## What's Left to be Added/Fixed
|
||||
- The Following Inventories
|
||||
- [ ] Enchantment Table (as a proper GUI)
|
||||
- [ ] Beacon
|
||||
- [ ] Cartography Table
|
||||
- [ ] Stonecutter
|
||||
- [ ] Structure Block
|
||||
- [ ] Horse Inventory
|
||||
- [ ] Loom
|
||||
- [ ] Smithing Table
|
||||
- Lecterns
|
||||
- Near-perfect movement (to the point where anticheat on large servers is unlikely to ban you)
|
||||
- Resource pack conversion/CustomModelData
|
||||
- Some Entity Flags
|
||||
- The Following Inventories
|
||||
- Enchantment Table (as a proper GUI)
|
||||
- Beacon
|
||||
- Cartography Table
|
||||
- Stonecutter
|
||||
- Structure Block
|
||||
- Horse Inventory
|
||||
- Loom
|
||||
- Smithing Table
|
||||
|
||||
## What can't be fixed
|
||||
The following things can't be fixed because of Bedrock limitations. They might be fixable in the future, but not as of now.
|
||||
|
||||
- Custom heads in inventories
|
||||
|
||||
## Compiling
|
||||
1. Clone the repo to your computer
|
||||
|
@ -47,14 +47,12 @@ import java.util.concurrent.CompletableFuture;
|
||||
@AllArgsConstructor
|
||||
public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, Listener {
|
||||
|
||||
private static final GeyserPendingConnection PENDING_CONNECTION = new GeyserPendingConnection();
|
||||
|
||||
private final ProxyServer proxyServer;
|
||||
|
||||
@Override
|
||||
public GeyserPingInfo getPingInformation() {
|
||||
public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
|
||||
CompletableFuture<ProxyPingEvent> future = new CompletableFuture<>();
|
||||
proxyServer.getPluginManager().callEvent(new ProxyPingEvent(PENDING_CONNECTION, getPingInfo(), (event, throwable) -> {
|
||||
proxyServer.getPluginManager().callEvent(new ProxyPingEvent(new GeyserPendingConnection(inetSocketAddress), getPingInfo(), (event, throwable) -> {
|
||||
if (throwable != null) future.completeExceptionally(throwable);
|
||||
else future.complete(event);
|
||||
}));
|
||||
@ -89,7 +87,12 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
|
||||
private static class GeyserPendingConnection implements PendingConnection {
|
||||
|
||||
private static final UUID FAKE_UUID = UUID.nameUUIDFromBytes("geyser!internal".getBytes());
|
||||
private static final InetSocketAddress FAKE_REMOTE = new InetSocketAddress(Inet4Address.getLoopbackAddress(), 69);
|
||||
|
||||
private final InetSocketAddress remote;
|
||||
|
||||
public GeyserPendingConnection(InetSocketAddress remote) {
|
||||
this.remote = remote;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
@ -143,7 +146,7 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress() {
|
||||
return FAKE_REMOTE;
|
||||
return remote;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -67,7 +67,7 @@ public class GeyserBungeeCommandExecutor extends Command implements TabExecutor
|
||||
@Override
|
||||
public Iterable<String> onTabComplete(CommandSender sender, String[] args) {
|
||||
if (args.length == 1) {
|
||||
return Arrays.asList("?", "help", "reload", "shutdown", "stop");
|
||||
return connector.getCommandManager().getCommandNames();
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
@ -29,6 +29,11 @@
|
||||
<version>3.2.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.geysermc.adapters</groupId>
|
||||
<artifactId>spigot-all</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<finalName>${outputName}-Spigot</finalName>
|
||||
|
@ -35,6 +35,7 @@ import org.geysermc.connector.common.ping.GeyserPingInfo;
|
||||
import org.geysermc.connector.ping.IGeyserPingPassthrough;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
||||
@ -44,9 +45,9 @@ public class GeyserSpigotPingPassthrough implements IGeyserPingPassthrough {
|
||||
private final GeyserSpigotLogger logger;
|
||||
|
||||
@Override
|
||||
public GeyserPingInfo getPingInformation() {
|
||||
public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
|
||||
try {
|
||||
ServerListPingEvent event = new GeyserPingEvent(InetAddress.getLocalHost(), Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(), Bukkit.getMaxPlayers());
|
||||
ServerListPingEvent event = new GeyserPingEvent(inetSocketAddress.getAddress(), Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(), Bukkit.getMaxPlayers());
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
GeyserPingInfo geyserPingInfo = new GeyserPingInfo(event.getMotd(),
|
||||
new GeyserPingInfo.Players(event.getMaxPlayers(), event.getNumPlayers()),
|
||||
|
@ -25,8 +25,10 @@
|
||||
|
||||
package org.geysermc.platform.spigot;
|
||||
|
||||
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.geysermc.adapters.spigot.SpigotAdapters;
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
||||
@ -42,18 +44,23 @@ import org.geysermc.platform.spigot.command.GeyserSpigotCommandExecutor;
|
||||
import org.geysermc.platform.spigot.command.GeyserSpigotCommandManager;
|
||||
import org.geysermc.platform.spigot.command.SpigotCommandSender;
|
||||
import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener;
|
||||
import org.geysermc.platform.spigot.world.GeyserSpigotWorldManager;
|
||||
import org.geysermc.platform.spigot.world.manager.*;
|
||||
import us.myles.ViaVersion.api.Pair;
|
||||
import us.myles.ViaVersion.api.Via;
|
||||
import us.myles.ViaVersion.api.data.MappingData;
|
||||
import us.myles.ViaVersion.api.protocol.Protocol;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
|
||||
private GeyserSpigotCommandManager geyserCommandManager;
|
||||
private GeyserSpigotConfiguration geyserConfig;
|
||||
private GeyserSpigotLogger geyserLogger;
|
||||
@ -142,8 +149,48 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
// Set if we need to use a different method for getting a player's locale
|
||||
SpigotCommandSender.setUseLegacyLocaleMethod(!isCompatible(Bukkit.getServer().getVersion(), "1.12.0"));
|
||||
|
||||
this.geyserWorldManager = new GeyserSpigotWorldManager(isLegacy, use3dBiomes, isViaVersion);
|
||||
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(connector, isLegacy, isViaVersion);
|
||||
if (connector.getConfig().isUseAdapters()) {
|
||||
try {
|
||||
String name = Bukkit.getServer().getClass().getPackage().getName();
|
||||
String nmsVersion = name.substring(name.lastIndexOf('.') + 1);
|
||||
SpigotAdapters.registerWorldAdapter(nmsVersion);
|
||||
if (isViaVersion && isViaVersionNeeded()) {
|
||||
if (isLegacy) {
|
||||
// Pre-1.13
|
||||
this.geyserWorldManager = new GeyserSpigot1_12NativeWorldManager();
|
||||
} else {
|
||||
// Post-1.13
|
||||
this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this, use3dBiomes);
|
||||
}
|
||||
} else {
|
||||
// No ViaVersion
|
||||
this.geyserWorldManager = new GeyserSpigotNativeWorldManager(use3dBiomes);
|
||||
}
|
||||
geyserLogger.debug("Using NMS adapter: " + this.geyserWorldManager.getClass() + ", " + nmsVersion);
|
||||
} catch (Exception e) {
|
||||
if (geyserConfig.isDebugMode()) {
|
||||
geyserLogger.debug("Error while attempting to find NMS adapter. Most likely, this can be safely ignored. :)");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
geyserLogger.debug("Not using NMS adapter as it is disabled in the config.");
|
||||
}
|
||||
if (this.geyserWorldManager == null) {
|
||||
// No NMS adapter
|
||||
if (isLegacy && isViaVersion) {
|
||||
// Use ViaVersion for converting pre-1.13 block states
|
||||
this.geyserWorldManager = new GeyserSpigot1_12WorldManager();
|
||||
} else if (isLegacy) {
|
||||
// Not sure how this happens - without ViaVersion, we don't know any block states, so just assume everything is air
|
||||
this.geyserWorldManager = new GeyserSpigotFallbackWorldManager();
|
||||
} else {
|
||||
// Post-1.13
|
||||
this.geyserWorldManager = new GeyserSpigotWorldManager(use3dBiomes);
|
||||
}
|
||||
geyserLogger.debug("Using default world manager: " + this.geyserWorldManager.getClass());
|
||||
}
|
||||
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(connector, this.geyserWorldManager);
|
||||
|
||||
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
|
||||
|
||||
@ -152,8 +199,9 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (connector != null)
|
||||
if (connector != null) {
|
||||
connector.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -186,6 +234,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
return getDataFolder().toPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BootstrapDumpInfo getDumpInfo() {
|
||||
return new GeyserSpigotDumpInfo();
|
||||
}
|
||||
|
||||
public boolean isCompatible(String version, String whichVersion) {
|
||||
int[] currentVersion = parseVersion(version);
|
||||
int[] otherVersion = parseVersion(whichVersion);
|
||||
@ -213,15 +266,43 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
String t = stringArray[index].replaceAll("\\D", "");
|
||||
try {
|
||||
temp[index] = Integer.parseInt(t);
|
||||
} catch(NumberFormatException ex) {
|
||||
} catch (NumberFormatException ex) {
|
||||
temp[index] = 0;
|
||||
}
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BootstrapDumpInfo getDumpInfo() {
|
||||
return new GeyserSpigotDumpInfo();
|
||||
/**
|
||||
* @return the server version before ViaVersion finishes initializing
|
||||
*/
|
||||
public ProtocolVersion getServerProtocolVersion() {
|
||||
String bukkitVersion = Bukkit.getServer().getVersion();
|
||||
// Turn "(MC: 1.16.4)" into 1.16.4.
|
||||
String version = bukkitVersion.split("\\(MC: ")[1].split("\\)")[0];
|
||||
return ProtocolVersion.getClosest(version);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should not run unless ViaVersion is installed on the server.
|
||||
*
|
||||
* @return true if there is any block mappings difference between the server and client.
|
||||
*/
|
||||
private boolean isViaVersionNeeded() {
|
||||
ProtocolVersion serverVersion = getServerProtocolVersion();
|
||||
List<Pair<Integer, Protocol>> protocolList = ProtocolRegistry.getProtocolPath(MinecraftConstants.PROTOCOL_VERSION,
|
||||
serverVersion.getVersion());
|
||||
if (protocolList == null) {
|
||||
// No translation needed!
|
||||
return false;
|
||||
}
|
||||
for (int i = protocolList.size() - 1; i >= 0; i--) {
|
||||
MappingData mappingData = protocolList.get(i).getValue().getMappingData();
|
||||
if (mappingData != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// All mapping data is null, which means client and server block states are the same
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ public class GeyserSpigotCommandExecutor implements TabExecutor {
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (args.length == 1) {
|
||||
return Arrays.asList("?", "help", "reload", "shutdown", "stop");
|
||||
return connector.getCommandManager().getCommandNames();
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
@ -36,13 +36,13 @@ import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.platform.spigot.world.manager.GeyserSpigotWorldManager;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class GeyserSpigotBlockPlaceListener implements Listener {
|
||||
|
||||
private final GeyserConnector connector;
|
||||
private final boolean isLegacy;
|
||||
private final boolean isViaVersion;
|
||||
private final GeyserSpigotWorldManager worldManager;
|
||||
|
||||
@EventHandler
|
||||
public void place(final BlockPlaceEvent event) {
|
||||
@ -52,14 +52,13 @@ public class GeyserSpigotBlockPlaceListener implements Listener {
|
||||
placeBlockSoundPacket.setSound(SoundEvent.PLACE);
|
||||
placeBlockSoundPacket.setPosition(Vector3f.from(event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ()));
|
||||
placeBlockSoundPacket.setBabySound(false);
|
||||
String javaBlockId;
|
||||
if (isLegacy) {
|
||||
javaBlockId = BlockTranslator.getJavaIdBlockMap().inverse().get(GeyserSpigotWorldManager.getLegacyBlock(session,
|
||||
event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ(), isViaVersion));
|
||||
if (worldManager.isLegacy()) {
|
||||
placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(worldManager.getBlockAt(session,
|
||||
event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ())));
|
||||
} else {
|
||||
javaBlockId = event.getBlockPlaced().getBlockData().getAsString();
|
||||
String javaBlockId = event.getBlockPlaced().getBlockData().getAsString();
|
||||
placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().getOrDefault(javaBlockId, BlockTranslator.JAVA_AIR_ID)));
|
||||
}
|
||||
placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().getOrDefault(javaBlockId, 0)));
|
||||
placeBlockSoundPacket.setIdentifier(":");
|
||||
session.sendUpstreamPacket(placeBlockSoundPacket);
|
||||
session.setLastBlockPlacePosition(null);
|
||||
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.platform.spigot.world.manager;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.geysermc.adapters.spigot.SpigotAdapters;
|
||||
import org.geysermc.adapters.spigot.SpigotWorldAdapter;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import us.myles.ViaVersion.api.Via;
|
||||
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
|
||||
|
||||
/**
|
||||
* Used with ViaVersion and pre-1.13.
|
||||
*/
|
||||
public class GeyserSpigot1_12NativeWorldManager extends GeyserSpigot1_12WorldManager {
|
||||
private final SpigotWorldAdapter adapter;
|
||||
|
||||
public GeyserSpigot1_12NativeWorldManager() {
|
||||
this.adapter = SpigotAdapters.getWorldAdapter();
|
||||
// Unlike post-1.13, we can't build up a cache of block states, because block entities need some special conversion
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
||||
Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
|
||||
if (player == null) {
|
||||
return BlockTranslator.JAVA_AIR_ID;
|
||||
}
|
||||
// Get block entity storage
|
||||
BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class);
|
||||
int blockId = adapter.getBlockAt(player.getWorld(), x, y, z);
|
||||
return getLegacyBlock(storage, blockId, x, y, z);
|
||||
}
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.platform.spigot.world.manager;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import us.myles.ViaVersion.api.Pair;
|
||||
import us.myles.ViaVersion.api.Via;
|
||||
import us.myles.ViaVersion.api.data.MappingData;
|
||||
import us.myles.ViaVersion.api.minecraft.Position;
|
||||
import us.myles.ViaVersion.api.protocol.Protocol;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
|
||||
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
|
||||
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Should be used when ViaVersion is present, no NMS adapter is being used, and we are pre-1.13.
|
||||
*
|
||||
* You need ViaVersion to connect to an older server with the Geyser-Spigot plugin.
|
||||
*/
|
||||
public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager {
|
||||
/**
|
||||
* Specific mapping data for 1.12 to 1.13. Used to convert the 1.12 block into the 1.13 block state.
|
||||
* (Block IDs did not change between server versions until 1.13 and after)
|
||||
*/
|
||||
private final MappingData mappingData1_12to1_13;
|
||||
|
||||
/**
|
||||
* The list of all protocols from the client's version to 1.13.
|
||||
*/
|
||||
private final List<Pair<Integer, Protocol>> protocolList;
|
||||
|
||||
public GeyserSpigot1_12WorldManager() {
|
||||
super(false);
|
||||
this.mappingData1_12to1_13 = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData();
|
||||
this.protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION,
|
||||
ProtocolVersion.v1_13.getVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
||||
Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
|
||||
if (player == null) {
|
||||
return BlockTranslator.JAVA_AIR_ID;
|
||||
}
|
||||
// Get block entity storage
|
||||
BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class);
|
||||
Block block = player.getWorld().getBlockAt(x, y, z);
|
||||
// Black magic that gets the old block state ID
|
||||
int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF);
|
||||
return getLegacyBlock(storage, blockId, x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param storage ViaVersion's block entity storage (used to fix block entity state differences)
|
||||
* @param blockId the pre-1.13 block id
|
||||
* @param x X coordinate of block
|
||||
* @param y Y coordinate of block
|
||||
* @param z Z coordinate of block
|
||||
* @return the block state updated to the latest Minecraft version
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public int getLegacyBlock(BlockStorage storage, int blockId, int x, int y, int z) {
|
||||
// Convert block state from old version (1.12.2) -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2
|
||||
blockId = mappingData1_12to1_13.getNewBlockId(blockId);
|
||||
// Translate block entity differences - some information was stored in block tags and not block states
|
||||
if (storage.isWelcome(blockId)) { // No getOrDefault method
|
||||
BlockStorage.ReplacementData data = storage.get(new Position(x, (short) y, z));
|
||||
if (data != null && data.getReplacement() != -1) {
|
||||
blockId = data.getReplacement();
|
||||
}
|
||||
}
|
||||
for (int i = protocolList.size() - 1; i >= 0; i--) {
|
||||
MappingData mappingData = protocolList.get(i).getValue().getMappingData();
|
||||
if (mappingData != null) {
|
||||
blockId = mappingData.getNewBlockStateId(blockId);
|
||||
}
|
||||
}
|
||||
return blockId;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
|
||||
Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
World world = player.getWorld();
|
||||
// Get block entity storage
|
||||
BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class);
|
||||
for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
|
||||
for (int blockZ = 0; blockZ < 16; blockZ++) {
|
||||
for (int blockX = 0; blockX < 16; blockX++) {
|
||||
Block block = world.getBlockAt(x, y, z);
|
||||
// Black magic that gets the old block state ID
|
||||
int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF);
|
||||
chunk.set(blockX, blockY, blockZ, getLegacyBlock(storage, blockId, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLegacy() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.platform.spigot.world.manager;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
|
||||
/**
|
||||
* Should only be used when we know {@link GeyserSpigotWorldManager#getBlockAt(GeyserSession, int, int, int)}
|
||||
* cannot be accurate. Typically, this is when ViaVersion is not installed but a client still manages to connect.
|
||||
* If this occurs to you somehow, please let us know!!
|
||||
*/
|
||||
public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager {
|
||||
public GeyserSpigotFallbackWorldManager() {
|
||||
// Since this is pre-1.13 (and thus pre-1.15), there will never be 3D biomes.
|
||||
super(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
||||
return BlockTranslator.JAVA_AIR_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
|
||||
// Do nothing, since we can't do anything with the chunk
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMoreBlockDataThanChunkCache() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLegacy() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.platform.spigot.world.manager;
|
||||
|
||||
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.platform.spigot.GeyserSpigotPlugin;
|
||||
import us.myles.ViaVersion.api.Pair;
|
||||
import us.myles.ViaVersion.api.data.MappingData;
|
||||
import us.myles.ViaVersion.api.protocol.Protocol;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Used when block IDs need to be translated to the latest version
|
||||
*/
|
||||
public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorldManager {
|
||||
|
||||
private final Int2IntMap oldToNewBlockId;
|
||||
|
||||
public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin, boolean use3dBiomes) {
|
||||
super(use3dBiomes);
|
||||
IntList allBlockStates = adapter.getAllBlockStates();
|
||||
oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size());
|
||||
ProtocolVersion serverVersion = plugin.getServerProtocolVersion();
|
||||
List<Pair<Integer, Protocol>> protocolList = ProtocolRegistry.getProtocolPath(MinecraftConstants.PROTOCOL_VERSION,
|
||||
serverVersion.getVersion());
|
||||
for (int oldBlockId : allBlockStates) {
|
||||
int newBlockId = oldBlockId;
|
||||
// protocolList should *not* be null; we checked for that before initializing this class
|
||||
for (int i = protocolList.size() - 1; i >= 0; i--) {
|
||||
MappingData mappingData = protocolList.get(i).getValue().getMappingData();
|
||||
if (mappingData != null) {
|
||||
newBlockId = mappingData.getNewBlockStateId(newBlockId);
|
||||
}
|
||||
}
|
||||
oldToNewBlockId.put(oldBlockId, newBlockId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
||||
int nativeBlockId = super.getBlockAt(session, x, y, z);
|
||||
return oldToNewBlockId.getOrDefault(nativeBlockId, nativeBlockId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLegacy() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.platform.spigot.world.manager;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.geysermc.adapters.spigot.SpigotAdapters;
|
||||
import org.geysermc.adapters.spigot.SpigotWorldAdapter;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
|
||||
public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
|
||||
protected final SpigotWorldAdapter adapter;
|
||||
|
||||
public GeyserSpigotNativeWorldManager(boolean use3dBiomes) {
|
||||
super(use3dBiomes);
|
||||
adapter = SpigotAdapters.getWorldAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
||||
Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
|
||||
if (player == null) {
|
||||
return BlockTranslator.JAVA_AIR_ID;
|
||||
}
|
||||
return adapter.getBlockAt(player.getWorld(), x, y, z);
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.platform.spigot.world;
|
||||
package org.geysermc.platform.spigot.world.manager;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
||||
@ -34,6 +34,7 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
@ -42,38 +43,22 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.utils.FileUtils;
|
||||
import org.geysermc.connector.utils.GameRule;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import us.myles.ViaVersion.api.Pair;
|
||||
import us.myles.ViaVersion.api.Via;
|
||||
import us.myles.ViaVersion.api.data.MappingData;
|
||||
import us.myles.ViaVersion.api.minecraft.Position;
|
||||
import us.myles.ViaVersion.api.protocol.Protocol;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
|
||||
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
|
||||
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The base world manager to use when there is no supported NMS revision
|
||||
*/
|
||||
public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
||||
/**
|
||||
* The current client protocol version for ViaVersion usage.
|
||||
*/
|
||||
private static final int CLIENT_PROTOCOL_VERSION = MinecraftConstants.PROTOCOL_VERSION;
|
||||
protected static final int CLIENT_PROTOCOL_VERSION = MinecraftConstants.PROTOCOL_VERSION;
|
||||
|
||||
/**
|
||||
* Whether the server is pre-1.13.
|
||||
*/
|
||||
private final boolean isLegacy;
|
||||
/**
|
||||
* Whether the server is pre-1.16 and therefore does not support 3D biomes on an API level guaranteed.
|
||||
*/
|
||||
private final boolean use3dBiomes;
|
||||
/**
|
||||
* You need ViaVersion to connect to an older server with Geyser.
|
||||
* 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;
|
||||
/**
|
||||
* Stores a list of {@link Biome} ordinal numbers to Minecraft biome numeric IDs.
|
||||
*
|
||||
@ -87,10 +72,8 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
||||
*/
|
||||
private final Int2IntMap biomeToIdMap = new Int2IntOpenHashMap(Biome.values().length);
|
||||
|
||||
public GeyserSpigotWorldManager(boolean isLegacy, boolean use3dBiomes, boolean isViaVersion) {
|
||||
this.isLegacy = isLegacy;
|
||||
public GeyserSpigotWorldManager(boolean use3dBiomes) {
|
||||
this.use3dBiomes = use3dBiomes;
|
||||
this.isViaVersion = isViaVersion;
|
||||
|
||||
// Load the values into the biome-to-ID map
|
||||
InputStream biomeStream = FileUtils.getResource("biomes.json");
|
||||
@ -116,83 +99,26 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
||||
@Override
|
||||
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
||||
Player bukkitPlayer;
|
||||
if ((this.isLegacy && !this.isViaVersion)
|
||||
|| session.getPlayerEntity() == null
|
||||
|| (bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
||||
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
||||
return BlockTranslator.JAVA_AIR_ID;
|
||||
}
|
||||
World world = bukkitPlayer.getWorld();
|
||||
if (isLegacy) {
|
||||
return getLegacyBlock(session, x, y, z, true);
|
||||
}
|
||||
//TODO possibly: detect server version for all versions and use ViaVersion for block state mappings like below
|
||||
return BlockTranslator.getJavaIdBlockMap().getOrDefault(world.getBlockAt(x, y, z).getBlockData().getAsString(), 0);
|
||||
}
|
||||
|
||||
public static int getLegacyBlock(GeyserSession session, int x, int y, int z, boolean isViaVersion) {
|
||||
if (isViaVersion) {
|
||||
Player bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
|
||||
// Get block entity storage
|
||||
BlockStorage storage = Via.getManager().getConnection(bukkitPlayer.getUniqueId()).get(BlockStorage.class);
|
||||
return getLegacyBlock(storage, bukkitPlayer.getWorld(), x, y, z);
|
||||
} else {
|
||||
return BlockTranslator.JAVA_AIR_ID;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static int getLegacyBlock(BlockStorage storage, World world, int x, int y, int z) {
|
||||
Block block = world.getBlockAt(x, y, z);
|
||||
// Black magic that gets the old block state ID
|
||||
int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF);
|
||||
// Convert block state from old version (1.12.2) -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2
|
||||
blockId = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData().getNewBlockId(blockId);
|
||||
List<Pair<Integer, Protocol>> protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION,
|
||||
ProtocolVersion.v1_13.getId());
|
||||
// Translate block entity differences - some information was stored in block tags and not block states
|
||||
if (storage.isWelcome(blockId)) { // No getOrDefault method
|
||||
BlockStorage.ReplacementData data = storage.get(new Position(x, (short) y, z));
|
||||
if (data != null && data.getReplacement() != -1) {
|
||||
blockId = data.getReplacement();
|
||||
}
|
||||
}
|
||||
for (int i = protocolList.size() - 1; i >= 0; i--) {
|
||||
MappingData mappingData = protocolList.get(i).getValue().getMappingData();
|
||||
if (mappingData != null) {
|
||||
blockId = mappingData.getNewBlockStateId(blockId);
|
||||
}
|
||||
}
|
||||
return blockId;
|
||||
return BlockTranslator.getJavaIdBlockMap().getOrDefault(world.getBlockAt(x, y, z).getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
|
||||
Player bukkitPlayer;
|
||||
if ((this.isLegacy && !this.isViaVersion)
|
||||
|| session.getPlayerEntity() == null
|
||||
|| (bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
||||
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
||||
return;
|
||||
}
|
||||
World world = bukkitPlayer.getWorld();
|
||||
if (this.isLegacy) {
|
||||
// Get block entity storage
|
||||
BlockStorage storage = Via.getManager().getConnection(bukkitPlayer.getUniqueId()).get(BlockStorage.class);
|
||||
for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
|
||||
for (int blockZ = 0; blockZ < 16; blockZ++) {
|
||||
for (int blockX = 0; blockX < 16; blockX++) {
|
||||
chunk.set(blockX, blockY, blockZ, getLegacyBlock(storage, world, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//TODO: see above TODO in getBlockAt
|
||||
for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
|
||||
for (int blockZ = 0; blockZ < 16; blockZ++) {
|
||||
for (int blockX = 0; blockX < 16; blockX++) {
|
||||
Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ);
|
||||
int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID);
|
||||
chunk.set(blockX, blockY, blockZ, id);
|
||||
}
|
||||
for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
|
||||
for (int blockZ = 0; blockZ < 16; blockZ++) {
|
||||
for (int blockX = 0; blockX < 16; blockX++) {
|
||||
Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ);
|
||||
int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID);
|
||||
chunk.set(blockX, blockY, blockZ, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -254,4 +180,16 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
||||
public boolean hasPermission(GeyserSession session, String permission) {
|
||||
return Bukkit.getPlayer(session.getPlayerEntity().getUsername()).hasPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* This must be set to true if we are pre-1.13, and {@link BlockData#getAsString() does not exist}.
|
||||
*
|
||||
* This should be set to true if we are post-1.13 but before the latest version, and we should convert the old block state id
|
||||
* to the current one.
|
||||
*
|
||||
* @return whether there is a difference between client block state and server block state that requires extra processing
|
||||
*/
|
||||
public boolean isLegacy() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -38,31 +38,29 @@ import org.spongepowered.api.network.status.StatusClient;
|
||||
import org.spongepowered.api.profile.GameProfile;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Optional;
|
||||
|
||||
public class GeyserSpongePingPassthrough implements IGeyserPingPassthrough {
|
||||
|
||||
private static final GeyserStatusClient STATUS_CLIENT = new GeyserStatusClient();
|
||||
private static final Cause CAUSE = Cause.of(EventContext.empty(), Sponge.getServer());
|
||||
|
||||
private static Method SpongeStatusResponse_create;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Override
|
||||
public GeyserPingInfo getPingInformation() {
|
||||
public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
|
||||
// come on Sponge, this is in commons, why not expose it :(
|
||||
ClientPingServerEvent event;
|
||||
try {
|
||||
if(SpongeStatusResponse_create == null) {
|
||||
if (SpongeStatusResponse_create == null) {
|
||||
Class SpongeStatusResponse = Class.forName("org.spongepowered.common.network.status.SpongeStatusResponse");
|
||||
Class MinecraftServer = Class.forName("net.minecraft.server.MinecraftServer");
|
||||
SpongeStatusResponse_create = SpongeStatusResponse.getDeclaredMethod("create", MinecraftServer);
|
||||
}
|
||||
|
||||
Object response = SpongeStatusResponse_create.invoke(null, Sponge.getServer());
|
||||
event = SpongeEventFactory.createClientPingServerEvent(CAUSE, STATUS_CLIENT, (ClientPingServerEvent.Response) response);
|
||||
event = SpongeEventFactory.createClientPingServerEvent(CAUSE, new GeyserStatusClient(inetSocketAddress), (ClientPingServerEvent.Response) response);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@ -76,7 +74,7 @@ public class GeyserSpongePingPassthrough implements IGeyserPingPassthrough {
|
||||
new GeyserPingInfo.Version(
|
||||
event.getResponse().getVersion().getName(),
|
||||
MinecraftConstants.PROTOCOL_VERSION) // thanks for also not exposing this sponge
|
||||
);
|
||||
);
|
||||
event.getResponse().getPlayers().get().getProfiles().stream()
|
||||
.map(GameProfile::getName)
|
||||
.map(op -> op.orElseThrow(IllegalStateException::new))
|
||||
@ -87,11 +85,15 @@ public class GeyserSpongePingPassthrough implements IGeyserPingPassthrough {
|
||||
@SuppressWarnings("NullableProblems")
|
||||
private static class GeyserStatusClient implements StatusClient {
|
||||
|
||||
private static final InetSocketAddress FAKE_REMOTE = new InetSocketAddress(Inet4Address.getLoopbackAddress(), 69);
|
||||
private final InetSocketAddress remote;
|
||||
|
||||
public GeyserStatusClient(InetSocketAddress remote) {
|
||||
this.remote = remote;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress() {
|
||||
return FAKE_REMOTE;
|
||||
return this.remote;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -70,7 +70,7 @@ public class GeyserSpongeCommandExecutor implements CommandCallable {
|
||||
@Override
|
||||
public List<String> getSuggestions(CommandSource source, String arguments, @Nullable Location<World> targetPosition) throws CommandException {
|
||||
if (arguments.split(" ").length == 1) {
|
||||
return Arrays.asList("?", "help", "reload", "shutdown", "stop");
|
||||
return connector.getCommandManager().getCommandNames();
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
@ -204,7 +204,12 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
System.exit(0);
|
||||
if (gui == null) {
|
||||
System.exit(1);
|
||||
} else {
|
||||
// Leave the process running so the GUI is still visible
|
||||
return;
|
||||
}
|
||||
}
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
|
||||
|
@ -93,7 +93,16 @@
|
||||
<artifactSet>
|
||||
<excludes>
|
||||
<exclude>com.google.code.gson:*</exclude>
|
||||
<exclude>io.netty:*</exclude>
|
||||
<!-- Needed because Velocity provides every dependency except netty-resolver-dns -->
|
||||
<exclude>io.netty:netty-transport-native-epoll:*</exclude>
|
||||
<exclude>io.netty:netty-transport-native-unix-common:*</exclude>
|
||||
<exclude>io.netty:netty-transport-native-kqueue:*</exclude>
|
||||
<exclude>io.netty:netty-handler:*</exclude>
|
||||
<exclude>io.netty:netty-common:*</exclude>
|
||||
<exclude>io.netty:netty-buffer:*</exclude>
|
||||
<exclude>io.netty:netty-resolver:*</exclude>
|
||||
<exclude>io.netty:netty-transport:*</exclude>
|
||||
<exclude>io.netty:netty-codec:*</exclude>
|
||||
<exclude>org.slf4j:*</exclude>
|
||||
<exclude>org.ow2.asm:*</exclude>
|
||||
</excludes>
|
||||
|
@ -43,15 +43,13 @@ import java.util.concurrent.ExecutionException;
|
||||
@AllArgsConstructor
|
||||
public class GeyserVelocityPingPassthrough implements IGeyserPingPassthrough {
|
||||
|
||||
private static final GeyserInboundConnection FAKE_INBOUND_CONNECTION = new GeyserInboundConnection();
|
||||
|
||||
private final ProxyServer server;
|
||||
|
||||
@Override
|
||||
public GeyserPingInfo getPingInformation() {
|
||||
public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
|
||||
ProxyPingEvent event;
|
||||
try {
|
||||
event = server.getEventManager().fire(new ProxyPingEvent(FAKE_INBOUND_CONNECTION, ServerPing.builder()
|
||||
event = server.getEventManager().fire(new ProxyPingEvent(new GeyserInboundConnection(inetSocketAddress), ServerPing.builder()
|
||||
.description(server.getConfiguration().getMotdComponent()).onlinePlayers(server.getPlayerCount())
|
||||
.maximumPlayers(server.getConfiguration().getShowMaxPlayers()).build())).get();
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
@ -74,11 +72,15 @@ public class GeyserVelocityPingPassthrough implements IGeyserPingPassthrough {
|
||||
|
||||
private static class GeyserInboundConnection implements InboundConnection {
|
||||
|
||||
private static final InetSocketAddress FAKE_REMOTE = new InetSocketAddress(Inet4Address.getLoopbackAddress(), 69);
|
||||
private final InetSocketAddress remote;
|
||||
|
||||
public GeyserInboundConnection(InetSocketAddress remote) {
|
||||
this.remote = remote;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getRemoteAddress() {
|
||||
return FAKE_REMOTE;
|
||||
return this.remote;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -25,8 +25,8 @@
|
||||
|
||||
package org.geysermc.platform.velocity.command;
|
||||
|
||||
import com.velocitypowered.api.command.Command;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.command.SimpleCommand;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.command.CommandSender;
|
||||
@ -34,29 +34,39 @@ import org.geysermc.connector.command.GeyserCommand;
|
||||
import org.geysermc.connector.common.ChatColor;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class GeyserVelocityCommandExecutor implements Command {
|
||||
public class GeyserVelocityCommandExecutor implements SimpleCommand {
|
||||
|
||||
private final GeyserConnector connector;
|
||||
|
||||
@Override
|
||||
public void execute(CommandSource source, String[] args) {
|
||||
if (args.length > 0) {
|
||||
if (getCommand(args[0]) != null) {
|
||||
if (!source.hasPermission(getCommand(args[0]).getPermission())) {
|
||||
CommandSender sender = new VelocityCommandSender(source);
|
||||
public void execute(Invocation invocation) {
|
||||
if (invocation.arguments().length > 0) {
|
||||
if (getCommand(invocation.arguments()[0]) != null) {
|
||||
if (!invocation.source().hasPermission(getCommand(invocation.arguments()[0]).getPermission())) {
|
||||
CommandSender sender = new VelocityCommandSender(invocation.source());
|
||||
sender.sendMessage(ChatColor.RED + LanguageUtils.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.getLocale()));
|
||||
return;
|
||||
}
|
||||
getCommand(args[0]).execute(new VelocityCommandSender(source), args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]);
|
||||
getCommand(invocation.arguments()[0]).execute(new VelocityCommandSender(invocation.source()), invocation.arguments().length > 1 ? Arrays.copyOfRange(invocation.arguments(), 1, invocation.arguments().length) : new String[0]);
|
||||
}
|
||||
} else {
|
||||
getCommand("help").execute(new VelocityCommandSender(source), new String[0]);
|
||||
getCommand("help").execute(new VelocityCommandSender(invocation.source()), new String[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggest(Invocation invocation) {
|
||||
if (invocation.arguments().length == 0) {
|
||||
return connector.getCommandManager().getCommandNames();
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private GeyserCommand getCommand(String label) {
|
||||
return connector.getCommandManager().getCommands().get(label);
|
||||
}
|
||||
|
@ -30,16 +30,27 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.CloudburstMC.Protocol</groupId>
|
||||
<artifactId>bedrock-v419</artifactId>
|
||||
<version>ce59d39118</version>
|
||||
<artifactId>bedrock-v422</artifactId>
|
||||
<version>87d862d69d</version>
|
||||
<scope>compile</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>net.sf.trove4j</groupId>
|
||||
<artifactId>trove</artifactId>
|
||||
</exclusion>
|
||||
<!-- Stay on the older version of Network while it's rewritten -->
|
||||
<exclusion>
|
||||
<groupId>com.nukkitx.network</groupId>
|
||||
<artifactId>raknet</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.nukkitx.network</groupId>
|
||||
<artifactId>raknet</artifactId>
|
||||
<version>1.6.20</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.nukkitx.fastutil</groupId>
|
||||
<artifactId>fastutil-int-int-maps</artifactId>
|
||||
@ -109,13 +120,17 @@
|
||||
<dependency>
|
||||
<groupId>com.github.steveice10</groupId>
|
||||
<artifactId>mcprotocollib</artifactId>
|
||||
<version>86e1901be5</version>
|
||||
<version>26201a4</version>
|
||||
<scope>compile</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-text-serializer-gson</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@ -137,19 +152,25 @@
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-api</artifactId>
|
||||
<version>4.1.1</version>
|
||||
<version>4.3.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.kyoripowered.adventure</groupId>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-text-serializer-gson</artifactId>
|
||||
<version>4d8a67d798</version>
|
||||
<version>4.3.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.kyoripowered.adventure</groupId>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-text-serializer-legacy</artifactId>
|
||||
<version>0599048</version>
|
||||
<version>4.3.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-text-serializer-gson-legacy-impl</artifactId>
|
||||
<version>4.3.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@ -214,8 +235,12 @@
|
||||
</includes>
|
||||
<replacements>
|
||||
<replacement>
|
||||
<token>VERSION = ".*"</token>
|
||||
<value>VERSION = "${project.version} (git-${git.branch}-${git.commit.id.abbrev})"</value>
|
||||
<token>String VERSION = ".*"</token>
|
||||
<value>String VERSION = "${project.version} (" + GIT_VERSION + ")"</value>
|
||||
</replacement>
|
||||
<replacement>
|
||||
<token>String GIT_VERSION = ".*"</token>
|
||||
<value>String GIT_VERSION = "git-${git.branch}-${git.commit.id.abbrev}"</value>
|
||||
</replacement>
|
||||
</replacements>
|
||||
</configuration>
|
||||
@ -233,8 +258,12 @@
|
||||
</includes>
|
||||
<replacements>
|
||||
<replacement>
|
||||
<token>VERSION = ".*"</token>
|
||||
<value>VERSION = "DEV"</value>
|
||||
<token>String VERSION = ".*"</token>
|
||||
<value>String VERSION = "DEV"</value>
|
||||
</replacement>
|
||||
<replacement>
|
||||
<token>String GIT_VERSION = ".*"</token>
|
||||
<value>String GIT_VERSION = "DEV"</value>
|
||||
</replacement>
|
||||
</replacements>
|
||||
</configuration>
|
||||
@ -256,6 +285,8 @@
|
||||
<script><![CDATA[
|
||||
new org.reflections.Reflections("org.geysermc.connector.network.translators")
|
||||
.save("${project.artifactId}/target/classes/META-INF/reflections/org.geysermc.connector.network.translators-reflections.xml")
|
||||
new org.reflections.Reflections("org.geysermc.connector.network.translators.collision.translators")
|
||||
.save("${project.artifactId}/target/classes/META-INF/reflections/org.geysermc.connector.network.translators.collision.translators-reflections.xml")
|
||||
new org.reflections.Reflections("org.geysermc.connector.network.translators.item")
|
||||
.save("${project.artifactId}/target/classes/META-INF/reflections/org.geysermc.connector.network.translators.item-reflections.xml")
|
||||
new org.reflections.Reflections("org.geysermc.connector.network.translators.sound")
|
||||
|
@ -54,6 +54,8 @@ import org.geysermc.connector.network.translators.sound.SoundRegistry;
|
||||
import org.geysermc.connector.network.translators.world.WorldManager;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
|
||||
import org.geysermc.connector.network.translators.collision.CollisionTranslator;
|
||||
import org.geysermc.connector.network.translators.world.block.entity.SkullBlockEntityTranslator;
|
||||
import org.geysermc.connector.utils.DimensionUtils;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
@ -82,9 +84,14 @@ import java.util.concurrent.TimeUnit;
|
||||
@Getter
|
||||
public class GeyserConnector {
|
||||
|
||||
public static final ObjectMapper JSON_MAPPER = new ObjectMapper().enable(JsonParser.Feature.IGNORE_UNDEFINED).enable(JsonParser.Feature.ALLOW_COMMENTS).disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||
public static final ObjectMapper JSON_MAPPER = new ObjectMapper()
|
||||
.enable(JsonParser.Feature.IGNORE_UNDEFINED)
|
||||
.enable(JsonParser.Feature.ALLOW_COMMENTS)
|
||||
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
|
||||
.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES);
|
||||
|
||||
public static final String NAME = "Geyser";
|
||||
public static final String GIT_VERSION = "DEV"; // A fallback for running in IDEs
|
||||
public static final String VERSION = "DEV"; // A fallback for running in IDEs
|
||||
|
||||
private static final String IP_REGEX = "\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b";
|
||||
@ -141,6 +148,7 @@ public class GeyserConnector {
|
||||
EntityIdentifierRegistry.init();
|
||||
ItemRegistry.init();
|
||||
ItemTranslator.init();
|
||||
CollisionTranslator.init();
|
||||
LocaleUtils.init();
|
||||
PotionMixRegistry.init();
|
||||
RecipeRegistry.init();
|
||||
@ -198,6 +206,7 @@ public class GeyserConnector {
|
||||
}
|
||||
|
||||
DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether
|
||||
SkullBlockEntityTranslator.ALLOW_CUSTOM_SKULLS = config.isAllowCustomSkulls();
|
||||
|
||||
// https://github.com/GeyserMC/Geyser/issues/957
|
||||
RakNetConstants.MAXIMUM_MTU_SIZE = (short) config.getMtu();
|
||||
@ -216,7 +225,6 @@ public class GeyserConnector {
|
||||
|
||||
if (config.getMetrics().isEnabled()) {
|
||||
metrics = new Metrics(this, "GeyserMC", config.getMetrics().getUniqueId(), false, java.util.logging.Logger.getLogger(""));
|
||||
metrics.addCustomChart(new Metrics.SingleLineChart("servers", () -> 1));
|
||||
metrics.addCustomChart(new Metrics.SingleLineChart("players", players::size));
|
||||
// Prevent unwanted words best we can
|
||||
metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> AuthType.getByName(config.getRemote().getAuthType()).toString().toLowerCase()));
|
||||
|
@ -31,9 +31,7 @@ import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.command.defaults.*;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class CommandManager {
|
||||
|
||||
@ -52,6 +50,7 @@ public abstract class CommandManager {
|
||||
registerCommand(new OffhandCommand(connector, "offhand", "geyser.commands.offhand.desc", "geyser.command.offhand"));
|
||||
registerCommand(new DumpCommand(connector, "dump", "geyser.commands.dump.desc", "geyser.command.dump"));
|
||||
registerCommand(new VersionCommand(connector, "version", "geyser.commands.version.desc", "geyser.command.version"));
|
||||
registerCommand(new SettingsCommand(connector, "settings", "geyser.commands.settings.desc", "geyser.command.settings"));
|
||||
registerCommand(new StatisticsCommand(connector, "statistics", "geyser.commands.statistics.desc", "geyser.command.statistics"));
|
||||
}
|
||||
|
||||
@ -92,6 +91,13 @@ public abstract class CommandManager {
|
||||
cmd.execute(sender, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a list of all subcommands under {@code /geyser}.
|
||||
*/
|
||||
public List<String> getCommandNames() {
|
||||
return Arrays.asList(connector.getCommandManager().getCommands().keySet().toArray(new String[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the given command
|
||||
*
|
||||
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.command.defaults;
|
||||
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.command.CommandSender;
|
||||
import org.geysermc.connector.command.GeyserCommand;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.utils.SettingsUtils;
|
||||
|
||||
public class SettingsCommand extends GeyserCommand {
|
||||
|
||||
private final GeyserConnector connector;
|
||||
|
||||
public SettingsCommand(GeyserConnector connector, String name, String description, String permission) {
|
||||
super(name, description, permission);
|
||||
|
||||
this.connector = connector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
// Make sure the sender is a Bedrock edition client
|
||||
GeyserSession session = null;
|
||||
if (sender instanceof GeyserSession) {
|
||||
session = (GeyserSession) sender;
|
||||
} else {
|
||||
// Needed for Spigot - sender is not an instance of GeyserSession
|
||||
for (GeyserSession otherSession : connector.getPlayers()) {
|
||||
if (sender.getName().equals(otherSession.getPlayerEntity().getUsername())) {
|
||||
session = otherSession;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (session == null) return;
|
||||
SettingsUtils.buildForm(session);
|
||||
session.sendForm(session.getSettingsForm(), SettingsUtils.SETTINGS_FORM_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExecutableOnConsole() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -71,6 +71,8 @@ public interface GeyserConfiguration {
|
||||
|
||||
boolean isShowCooldown();
|
||||
|
||||
boolean isShowCoordinates();
|
||||
|
||||
String getDefaultLocale();
|
||||
|
||||
Path getFloodgateKeyPath();
|
||||
@ -85,6 +87,8 @@ public interface GeyserConfiguration {
|
||||
|
||||
int getCacheImages();
|
||||
|
||||
boolean isAllowCustomSkulls();
|
||||
|
||||
IMetricsInfo getMetrics();
|
||||
|
||||
interface IBedrockConfiguration {
|
||||
@ -107,7 +111,7 @@ public interface GeyserConfiguration {
|
||||
String getAddress();
|
||||
|
||||
int getPort();
|
||||
|
||||
|
||||
void setAddress(String address);
|
||||
|
||||
void setPort(int port);
|
||||
@ -135,6 +139,8 @@ public interface GeyserConfiguration {
|
||||
|
||||
int getMtu();
|
||||
|
||||
boolean isUseAdapters();
|
||||
|
||||
int getConfigVersion();
|
||||
|
||||
static void checkGeyserConfiguration(GeyserConfiguration geyserConfig, GeyserLogger geyserLogger) {
|
||||
|
@ -89,6 +89,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
||||
@JsonProperty("show-cooldown")
|
||||
private boolean showCooldown = true;
|
||||
|
||||
@JsonProperty("show-coordinates")
|
||||
private boolean showCoordinates = true;
|
||||
|
||||
@JsonProperty("allow-third-party-ears")
|
||||
private boolean allowThirdPartyEars = false;
|
||||
|
||||
@ -101,6 +104,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
||||
@JsonProperty("cache-images")
|
||||
private int cacheImages = 0;
|
||||
|
||||
@JsonProperty("allow-custom-skulls")
|
||||
private boolean allowCustomSkulls = true;
|
||||
|
||||
@JsonProperty("above-bedrock-nether-building")
|
||||
private boolean aboveBedrockNetherBuilding = false;
|
||||
|
||||
@ -170,6 +176,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
||||
@JsonProperty("mtu")
|
||||
private int mtu = 1400;
|
||||
|
||||
@JsonProperty("use-adapters")
|
||||
private boolean useAdapters = true;
|
||||
|
||||
@JsonProperty("config-version")
|
||||
private int configVersion = 0;
|
||||
}
|
||||
|
@ -35,6 +35,16 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BoatEntity extends Entity {
|
||||
|
||||
/**
|
||||
* Required when IS_BUOYANT is sent in order for boats to work in the water. <br>
|
||||
*
|
||||
* Taken from BDS 1.16.200, with the modification of <code>simulate_waves</code> since Java doesn't bob the boat up and down
|
||||
* like Bedrock.
|
||||
*/
|
||||
private static final String BUOYANCY_DATA = "{\"apply_gravity\":true,\"base_buoyancy\":1.0,\"big_wave_probability\":0.02999999932944775," +
|
||||
"\"big_wave_speed\":10.0,\"drag_down_on_buoyancy_removed\":0.0,\"liquid_blocks\":[\"minecraft:water\"," +
|
||||
"\"minecraft:flowing_water\"],\"simulate_waves\":false}}";
|
||||
|
||||
private boolean isPaddlingLeft;
|
||||
private float paddleTimeLeft;
|
||||
private boolean isPaddlingRight;
|
||||
@ -45,6 +55,10 @@ public class BoatEntity extends Entity {
|
||||
|
||||
public BoatEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position.add(0d, entityType.getOffset(), 0d), motion, rotation.add(90, 0, 90));
|
||||
|
||||
// Required to be able to move on land 1.16.200+ or apply gravity not in the water 1.16.100+
|
||||
metadata.put(EntityData.IS_BUOYANT, (byte) 1);
|
||||
metadata.put(EntityData.BUOYANCY_DATA, BUOYANCY_DATA);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -28,6 +28,7 @@ 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.entity.EntityData;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
@ -50,7 +51,7 @@ public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity {
|
||||
metadata.put(EntityData.COMMAND_BLOCK_COMMAND, entityMetadata.getValue());
|
||||
}
|
||||
if (entityMetadata.getId() == 14) {
|
||||
metadata.put(EntityData.COMMAND_BLOCK_LAST_OUTPUT, MessageTranslator.convertMessage(entityMetadata.getValue().toString()));
|
||||
metadata.put(EntityData.COMMAND_BLOCK_LAST_OUTPUT, MessageTranslator.convertMessage((Component) entityMetadata.getValue()));
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
|
@ -39,6 +39,8 @@ public class EnderCrystalEntity extends Entity {
|
||||
public EnderCrystalEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
|
||||
// Bedrock 1.16.100+ - prevents the entity from appearing on fire itself when fire is underneath it
|
||||
metadata.getFlags().setFlag(EntityFlag.FIRE_IMMUNE, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -32,11 +32,9 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
|
||||
import com.github.steveice10.mc.protocol.data.message.Message;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.math.vector.Vector3i;
|
||||
import com.nukkitx.protocol.bedrock.data.AttributeData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap;
|
||||
@ -46,14 +44,15 @@ import com.nukkitx.protocol.bedrock.packet.*;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.geysermc.connector.entity.attribute.Attribute;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.entity.living.ArmorStandEntity;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||
import org.geysermc.connector.utils.AttributeUtils;
|
||||
import org.geysermc.connector.utils.ChunkUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -314,11 +313,11 @@ public class Entity {
|
||||
}
|
||||
break;
|
||||
case 2: // custom name
|
||||
if (entityMetadata.getValue() instanceof Message) {
|
||||
Message message = (Message) entityMetadata.getValue();
|
||||
if (entityMetadata.getValue() instanceof Component) {
|
||||
Component message = (Component) entityMetadata.getValue();
|
||||
if (message != null)
|
||||
// Always translate even if it's a TextMessage since there could be translatable parameters
|
||||
metadata.put(EntityData.NAMETAG, MessageTranslator.convertMessage(message.toString(), session.getLocale()));
|
||||
metadata.put(EntityData.NAMETAG, MessageTranslator.convertMessage(message, session.getLocale()));
|
||||
}
|
||||
break;
|
||||
case 3: // is custom name visible
|
||||
|
@ -36,6 +36,7 @@ import com.nukkitx.nbt.NbtMapBuilder;
|
||||
import com.nukkitx.nbt.NbtType;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.utils.FireworkColor;
|
||||
|
@ -36,7 +36,20 @@ import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||
public class ItemEntity extends Entity {
|
||||
|
||||
public ItemEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
super(entityId, geyserId, entityType, position.add(0d, entityType.getOffset(), 0d), motion, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMotion(Vector3f motion) {
|
||||
if (isOnGround())
|
||||
motion = Vector3f.from(motion.getX(), 0, motion.getZ());
|
||||
|
||||
super.setMotion(motion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
|
||||
super.moveAbsolute(session, position.add(0d, this.entityType.getOffset(), 0d), rotation, isOnGround, teleported);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -44,7 +57,7 @@ public class ItemEntity extends Entity {
|
||||
if (entityMetadata.getId() == 7) {
|
||||
AddItemEntityPacket itemPacket = new AddItemEntityPacket();
|
||||
itemPacket.setRuntimeEntityId(geyserId);
|
||||
itemPacket.setPosition(position);
|
||||
itemPacket.setPosition(position.add(0d, this.entityType.getOffset(), 0d));
|
||||
itemPacket.setMotion(motion);
|
||||
itemPacket.setUniqueEntityId(geyserId);
|
||||
itemPacket.setFromFishing(false);
|
||||
|
@ -77,6 +77,9 @@ public class LivingEntity extends Entity {
|
||||
getHand().equals(ItemData.AIR) && getOffHand().getId() == ItemRegistry.SHIELD.getBedrockId());
|
||||
metadata.getFlags().setFlag(EntityFlag.USING_ITEM, (xd & 0x01) == 0x01 && !isUsingShield);
|
||||
metadata.getFlags().setFlag(EntityFlag.BLOCKING, (xd & 0x01) == 0x01);
|
||||
|
||||
// Riptide spin attack
|
||||
metadata.getFlags().setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, (xd & 0x04) == 0x04);
|
||||
break;
|
||||
case 8:
|
||||
metadata.put(EntityData.HEALTH, entityMetadata.getValue());
|
||||
|
@ -53,7 +53,7 @@ public class ArmorStandEntity extends LivingEntity {
|
||||
position = position.add(0d, entityType.getHeight() * (isSmall ? 0.55d : 1d), 0d);
|
||||
}
|
||||
|
||||
super.moveAbsolute(session, position, rotation, isOnGround, teleported);
|
||||
super.moveAbsolute(session, position, Vector3f.from(rotation.getX(), rotation.getX(), rotation.getX()), isOnGround, teleported);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -95,4 +95,10 @@ public class ArmorStandEntity extends LivingEntity {
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnEntity(GeyserSession session) {
|
||||
this.rotation = Vector3f.from(rotation.getX(), rotation.getX(), rotation.getX());
|
||||
super.spawnEntity(session);
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,11 @@ public class WolfEntity extends TameableEntity {
|
||||
// Relies on EntityData.OWNER_EID being set in TameableEntity.java
|
||||
if (entityMetadata.getId() == 19 && !metadata.getFlags().getFlag(EntityFlag.ANGRY)) {
|
||||
metadata.put(EntityData.COLOR, collarColor = (byte) (int) entityMetadata.getValue());
|
||||
if (!metadata.containsKey(EntityData.OWNER_EID)) {
|
||||
// If a color is set and there is no owner entity ID, set one.
|
||||
// Otherwise, the entire wolf is set to that color: https://user-images.githubusercontent.com/9083212/99209989-92691200-2792-11eb-911d-9a315c955be9.png
|
||||
metadata.put(EntityData.OWNER_EID, session.getPlayerEntity().getGeyserId());
|
||||
}
|
||||
}
|
||||
|
||||
// Wolf anger (1.16+)
|
||||
|
@ -23,11 +23,10 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.entity;
|
||||
package org.geysermc.connector.entity.player;
|
||||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.message.TextMessage;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.math.vector.Vector3i;
|
||||
@ -43,12 +42,14 @@ import com.nukkitx.protocol.bedrock.packet.SetEntityLinkPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.LivingEntity;
|
||||
import org.geysermc.connector.entity.attribute.Attribute;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.entity.living.animal.tameable.ParrotEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.session.cache.EntityEffectCache;
|
||||
import org.geysermc.connector.scoreboard.Team;
|
||||
import org.geysermc.connector.utils.AttributeUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
@ -64,9 +65,7 @@ public class PlayerEntity extends LivingEntity {
|
||||
private GameProfile profile;
|
||||
private UUID uuid;
|
||||
private String username;
|
||||
private long lastSkinUpdate = -1;
|
||||
private boolean playerList = true; // Player is in the player list
|
||||
private final EntityEffectCache effectCache;
|
||||
|
||||
/**
|
||||
* Saves the parrot currently on the player's left shoulder; otherwise null
|
||||
@ -83,14 +82,10 @@ public class PlayerEntity extends LivingEntity {
|
||||
profile = gameProfile;
|
||||
uuid = gameProfile.getId();
|
||||
username = gameProfile.getName();
|
||||
effectCache = new EntityEffectCache();
|
||||
if (geyserId == 1) valid = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnEntity(GeyserSession session) {
|
||||
if (geyserId == 1) return;
|
||||
|
||||
AddPlayerPacket addPlayerPacket = new AddPlayerPacket();
|
||||
addPlayerPacket.setUuid(uuid);
|
||||
addPlayerPacket.setUsername(username);
|
||||
@ -161,6 +156,10 @@ public class PlayerEntity extends LivingEntity {
|
||||
setRotation(rotation);
|
||||
this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ);
|
||||
|
||||
// If this is the player logged in through this Geyser session
|
||||
if (geyserId == 1) {
|
||||
session.getCollisionManager().updatePlayerBoundingBox(position);
|
||||
}
|
||||
setOnGround(isOnGround);
|
||||
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
@ -232,7 +231,18 @@ public class PlayerEntity extends LivingEntity {
|
||||
|
||||
@Override
|
||||
public void setPosition(Vector3f position) {
|
||||
this.position = position.add(0, entityType.getOffset(), 0);
|
||||
setPosition(position, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the player position and specify if the entity type's offset should be added. Set to false when the player
|
||||
* sends us a move packet where the offset is already added
|
||||
*
|
||||
* @param position the new position of the Bedrock player
|
||||
* @param includeOffset whether to include the offset
|
||||
*/
|
||||
public void setPosition(Vector3f position, boolean includeOffset) {
|
||||
this.position = includeOffset ? position.add(0, entityType.getOffset(), 0) : position;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -241,9 +251,9 @@ public class PlayerEntity extends LivingEntity {
|
||||
|
||||
if (entityMetadata.getId() == 2) {
|
||||
String username = this.username;
|
||||
TextMessage name = (TextMessage) entityMetadata.getValue();
|
||||
Component name = (Component) entityMetadata.getValue();
|
||||
if (name != null) {
|
||||
username = MessageTranslator.convertMessage(name.toString());
|
||||
username = MessageTranslator.convertMessage(name);
|
||||
}
|
||||
Team team = session.getWorldCache().getScoreboard().getTeamFor(username);
|
||||
if (team != null) {
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.entity.player;
|
||||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* The entity class specifically for a {@link GeyserSession}'s player.
|
||||
*/
|
||||
public class SessionPlayerEntity extends PlayerEntity {
|
||||
|
||||
private final GeyserSession session;
|
||||
|
||||
public SessionPlayerEntity(GeyserSession session) {
|
||||
super(new GameProfile(UUID.randomUUID(), "unknown"), 1, 1, Vector3f.ZERO, Vector3f.ZERO, Vector3f.ZERO);
|
||||
|
||||
valid = true;
|
||||
this.session = session;
|
||||
this.session.getCollisionManager().updatePlayerBoundingBox(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnEntity(GeyserSession session) {
|
||||
// Already logged in
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
|
||||
session.getCollisionManager().updatePlayerBoundingBox(position);
|
||||
super.moveAbsolute(session, position, rotation, isOnGround, teleported);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPosition(Vector3f position) {
|
||||
if (session != null) { // null during entity initialization
|
||||
session.getCollisionManager().updatePlayerBoundingBox(position);
|
||||
}
|
||||
super.setPosition(position);
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.entity.player;
|
||||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.math.vector.Vector3i;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
/**
|
||||
* A wrapper to handle skulls more effectively - skulls have to be treated as entities since there are no
|
||||
* custom player skulls in Bedrock.
|
||||
*/
|
||||
public class SkullPlayerEntity extends PlayerEntity {
|
||||
|
||||
/**
|
||||
* Stores the block state that the skull is associated with. Used to determine if the block in the skull's position
|
||||
* has changed
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
private int blockState;
|
||||
|
||||
public SkullPlayerEntity(GameProfile gameProfile, long geyserId, Vector3f position, Vector3f rotation) {
|
||||
super(gameProfile, 0, geyserId, position, Vector3f.ZERO, rotation);
|
||||
setPlayerList(false);
|
||||
|
||||
//Set bounding box to almost nothing so the skull is able to be broken and not cause entity to cast a shadow
|
||||
metadata.clear();
|
||||
metadata.put(EntityData.SCALE, 1.08f);
|
||||
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.001f);
|
||||
metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.001f);
|
||||
metadata.getOrCreateFlags().setFlag(EntityFlag.CAN_SHOW_NAME, false);
|
||||
metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true); // Until the skin is loaded
|
||||
}
|
||||
|
||||
public void despawnEntity(GeyserSession session, Vector3i position) {
|
||||
this.despawnEntity(session);
|
||||
session.getSkullCache().remove(position, this);
|
||||
}
|
||||
}
|
@ -37,6 +37,7 @@ import org.geysermc.connector.entity.living.monster.raid.AbstractIllagerEntity;
|
||||
import org.geysermc.connector.entity.living.monster.raid.PillagerEntity;
|
||||
import org.geysermc.connector.entity.living.monster.raid.RaidParticipantEntity;
|
||||
import org.geysermc.connector.entity.living.monster.raid.SpellcasterIllagerEntity;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
|
||||
@Getter
|
||||
public enum EntityType {
|
||||
@ -99,7 +100,7 @@ public enum EntityType {
|
||||
ARMOR_STAND(ArmorStandEntity.class, 61, 1.975f, 0.5f),
|
||||
TRIPOD_CAMERA(Entity.class, 62, 0f),
|
||||
PLAYER(PlayerEntity.class, 63, 1.8f, 0.6f, 0.6f, 1.62f),
|
||||
ITEM(ItemEntity.class, 64, 0.25f, 0.25f),
|
||||
ITEM(ItemEntity.class, 64, 0.25f, 0.25f, 0.25f, 0.125f),
|
||||
PRIMED_TNT(TNTEntity.class, 65, 0.98f, 0.98f, 0.98f, 0f, "minecraft:tnt"),
|
||||
FALLING_BLOCK(FallingBlockEntity.class, 66, 0.98f, 0.98f),
|
||||
MOVING_BLOCK(Entity.class, 67, 0f),
|
||||
|
@ -27,6 +27,7 @@ package org.geysermc.connector.network;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
|
||||
import com.nukkitx.protocol.bedrock.v419.Bedrock_v419;
|
||||
import com.nukkitx.protocol.bedrock.v422.Bedrock_v422;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -39,13 +40,16 @@ public class BedrockProtocol {
|
||||
* Default Bedrock codec that should act as a fallback. Should represent the latest available
|
||||
* release of the game that Geyser supports.
|
||||
*/
|
||||
public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v419.V419_CODEC;
|
||||
public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v422.V422_CODEC;
|
||||
/**
|
||||
* A list of all supported Bedrock versions that can join Geyser
|
||||
*/
|
||||
public static final List<BedrockPacketCodec> SUPPORTED_BEDROCK_CODECS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v419.V419_CODEC.toBuilder()
|
||||
.minecraftVersion("1.16.100/1.16.101") // We change this as 1.16.100.60 is a beta
|
||||
.build());
|
||||
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC);
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
|
||||
GeyserPingInfo pingInfo = null;
|
||||
if (config.isPassthroughMotd() || config.isPassthroughPlayerCounts()) {
|
||||
IGeyserPingPassthrough pingPassthrough = connector.getBootstrap().getGeyserPingPassthrough();
|
||||
pingInfo = pingPassthrough.getPingInformation();
|
||||
pingInfo = pingPassthrough.getPingInformation(inetSocketAddress);
|
||||
}
|
||||
|
||||
BedrockPong pong = new BedrockPong();
|
||||
|
@ -174,7 +174,7 @@ public class QueryPacketHandler {
|
||||
gameData.put("hostname", motd);
|
||||
gameData.put("gametype", "SMP");
|
||||
gameData.put("game_id", "MINECRAFT");
|
||||
gameData.put("version", BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion());
|
||||
gameData.put("version", GeyserConnector.NAME + " (" + GeyserConnector.GIT_VERSION + ") " + BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion());
|
||||
gameData.put("plugins", "");
|
||||
gameData.put("map", map);
|
||||
gameData.put("numplayers", currentPlayerCount);
|
||||
|
@ -80,7 +80,9 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||
ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket();
|
||||
for(ResourcePack resourcePack : ResourcePack.PACKS.values()) {
|
||||
ResourcePackManifest.Header header = resourcePack.getManifest().getHeader();
|
||||
resourcePacksInfo.getResourcePackInfos().add(new ResourcePacksInfoPacket.Entry(header.getUuid().toString(), header.getVersionString(), resourcePack.getFile().length(), "", "", "", false));
|
||||
resourcePacksInfo.getResourcePackInfos().add(new ResourcePacksInfoPacket.Entry(
|
||||
header.getUuid().toString(), header.getVersionString(), resourcePack.getFile().length(),
|
||||
"", "", "", false, false));
|
||||
}
|
||||
resourcePacksInfo.setForcedToAccept(GeyserConnector.getInstance().getConfig().isForceResourcePacks());
|
||||
session.sendUpstreamPacket(resourcePacksInfo);
|
||||
@ -178,7 +180,13 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||
@Override
|
||||
public boolean handle(MovePlayerPacket packet) {
|
||||
if (session.isLoggingIn()) {
|
||||
session.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.auth.login.wait", session.getLocale()));
|
||||
SetTitlePacket titlePacket = new SetTitlePacket();
|
||||
titlePacket.setType(SetTitlePacket.Type.ACTIONBAR);
|
||||
titlePacket.setText(LanguageUtils.getPlayerLocaleString("geyser.auth.login.wait", session.getLocale()));
|
||||
titlePacket.setFadeInTime(0);
|
||||
titlePacket.setFadeOutTime(1);
|
||||
titlePacket.setStayTime(2);
|
||||
session.sendUpstreamPacket(titlePacket);
|
||||
}
|
||||
|
||||
return translateAndDefault(packet);
|
||||
|
@ -35,6 +35,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||
import com.github.steveice10.mc.protocol.data.game.statistic.Statistic;
|
||||
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
|
||||
import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket;
|
||||
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;
|
||||
@ -49,11 +50,14 @@ import com.nukkitx.protocol.bedrock.BedrockServerSession;
|
||||
import com.nukkitx.protocol.bedrock.data.*;
|
||||
import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
||||
import com.nukkitx.protocol.bedrock.packet.*;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
@ -61,7 +65,8 @@ import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.command.CommandSender;
|
||||
import org.geysermc.connector.common.AuthType;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.entity.player.SessionPlayerEntity;
|
||||
import org.geysermc.connector.entity.player.SkullPlayerEntity;
|
||||
import org.geysermc.connector.inventory.PlayerInventory;
|
||||
import org.geysermc.connector.network.remote.RemoteServer;
|
||||
import org.geysermc.connector.network.session.auth.AuthData;
|
||||
@ -71,8 +76,10 @@ 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.chat.MessageTranslator;
|
||||
import org.geysermc.connector.network.translators.collision.CollisionManager;
|
||||
import org.geysermc.connector.network.translators.inventory.EnchantmentInventoryTranslator;
|
||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||
import org.geysermc.connector.skin.SkinManager;
|
||||
import org.geysermc.connector.utils.*;
|
||||
import org.geysermc.cumulus.Form;
|
||||
import org.geysermc.cumulus.util.FormBuilder;
|
||||
@ -82,6 +89,7 @@ import org.geysermc.floodgate.util.BedrockData;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@ -97,18 +105,23 @@ public class GeyserSession implements CommandSender {
|
||||
@Setter
|
||||
private BedrockClientData clientData;
|
||||
|
||||
private PlayerEntity playerEntity;
|
||||
private final SessionPlayerEntity playerEntity;
|
||||
private PlayerInventory inventory;
|
||||
|
||||
private ChunkCache chunkCache;
|
||||
private EntityCache entityCache;
|
||||
private EntityEffectCache effectCache;
|
||||
private InventoryCache inventoryCache;
|
||||
private WorldCache worldCache;
|
||||
private FormCache formCache;
|
||||
@Setter
|
||||
private TeleportCache teleportCache;
|
||||
private final Int2ObjectMap<TeleportCache> teleportMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
@Getter
|
||||
/**
|
||||
* Stores session collision
|
||||
*/
|
||||
private final CollisionManager collisionManager;
|
||||
|
||||
private final Map<Vector3i, SkullPlayerEntity> skullCache = new ConcurrentHashMap<>();
|
||||
private final Long2ObjectMap<ClientboundMapItemDataPacket> storedMaps = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
|
||||
|
||||
/**
|
||||
@ -133,7 +146,6 @@ public class GeyserSession implements CommandSender {
|
||||
|
||||
private final AtomicInteger pendingDimSwitches = new AtomicInteger(0);
|
||||
|
||||
@Setter
|
||||
private boolean sneaking;
|
||||
|
||||
@Setter
|
||||
@ -181,12 +193,6 @@ public class GeyserSession implements CommandSender {
|
||||
@Setter
|
||||
private long lastWindowCloseTime = 0;
|
||||
|
||||
/**
|
||||
* Saves the timestamp of the last keep alive packet
|
||||
*/
|
||||
@Setter
|
||||
private long lastKeepAliveTimestamp = 0;
|
||||
|
||||
@Setter
|
||||
private VillagerTrade[] villagerTrades;
|
||||
@Setter
|
||||
@ -210,6 +216,17 @@ public class GeyserSession implements CommandSender {
|
||||
@Setter
|
||||
private long lastHitTime;
|
||||
|
||||
/**
|
||||
* Saves if the client is steering left on a boat.
|
||||
*/
|
||||
@Setter
|
||||
private boolean steeringLeft;
|
||||
/**
|
||||
* Saves if the client is steering right on a boat.
|
||||
*/
|
||||
@Setter
|
||||
private boolean steeringRight;
|
||||
|
||||
/**
|
||||
* Store the last time the player interacted. Used to fix a right-click spam bug.
|
||||
* See https://github.com/GeyserMC/Geyser/issues/503 for context.
|
||||
@ -307,11 +324,14 @@ public class GeyserSession implements CommandSender {
|
||||
|
||||
this.chunkCache = new ChunkCache(this);
|
||||
this.entityCache = new EntityCache(this);
|
||||
this.effectCache = new EntityEffectCache();
|
||||
this.inventoryCache = new InventoryCache(this);
|
||||
this.worldCache = new WorldCache(this);
|
||||
this.formCache = new FormCache(this);
|
||||
|
||||
this.playerEntity = new PlayerEntity(new GameProfile(UUID.randomUUID(), "unknown"), 1, 1, Vector3f.ZERO, Vector3f.ZERO, Vector3f.ZERO);
|
||||
this.collisionManager = new CollisionManager(this);
|
||||
|
||||
this.playerEntity = new SessionPlayerEntity(this);
|
||||
this.inventory = new PlayerInventory();
|
||||
|
||||
this.spawned = false;
|
||||
@ -333,6 +353,9 @@ public class GeyserSession implements CommandSender {
|
||||
startGame();
|
||||
this.remoteServer = remoteServer;
|
||||
|
||||
// Set the hardcoded shield ID to the ID we just defined in StartGamePacket
|
||||
upstream.getSession().getHardcodedBlockingId().set(ItemRegistry.SHIELD.getBedrockId());
|
||||
|
||||
ChunkUtils.sendEmptyChunks(this, playerEntity.getPosition().toInt(), 0, false);
|
||||
|
||||
BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket();
|
||||
@ -367,6 +390,8 @@ public class GeyserSession implements CommandSender {
|
||||
// Don't let the client modify the inventory on death
|
||||
// Setting this to true allows keep inventory to work if enabled but doesn't break functionality being false
|
||||
gamerulePacket.getGameRules().add(new GameRuleData<>("keepinventory", true));
|
||||
// Ensure client doesn't try and do anything funky; the server handles this for us
|
||||
gamerulePacket.getGameRules().add(new GameRuleData<>("spawnradius", 0));
|
||||
upstream.sendPacket(gamerulePacket);
|
||||
}
|
||||
|
||||
@ -508,7 +533,7 @@ public class GeyserSession implements CommandSender {
|
||||
|
||||
// Check if they are not using a linked account
|
||||
if (connector.getAuthType() == AuthType.OFFLINE || playerEntity.getUuid().getMostSignificantBits() == 0) {
|
||||
SkinUtils.handleBedrockSkin(playerEntity, clientData);
|
||||
SkinManager.handleBedrockSkin(playerEntity, clientData);
|
||||
}
|
||||
}
|
||||
|
||||
@ -550,6 +575,7 @@ public class GeyserSession implements CommandSender {
|
||||
|
||||
this.chunkCache = null;
|
||||
this.entityCache = null;
|
||||
this.effectCache = null;
|
||||
this.worldCache = null;
|
||||
this.inventoryCache = null;
|
||||
this.formCache = null;
|
||||
@ -565,6 +591,12 @@ public class GeyserSession implements CommandSender {
|
||||
this.authData = authData;
|
||||
}
|
||||
|
||||
public void setSneaking(boolean sneaking) {
|
||||
this.sneaking = sneaking;
|
||||
collisionManager.updatePlayerBoundingBox();
|
||||
collisionManager.updateScaffoldingFlags();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return authData.getName();
|
||||
@ -637,7 +669,7 @@ public class GeyserSession implements CommandSender {
|
||||
startGamePacket.setLightningLevel(0);
|
||||
startGamePacket.setMultiplayerGame(true);
|
||||
startGamePacket.setBroadcastingToLan(true);
|
||||
startGamePacket.getGamerules().add(new GameRuleData<>("showcoordinates", true));
|
||||
startGamePacket.getGamerules().add(new GameRuleData<>("showcoordinates", connector.getConfig().isShowCoordinates()));
|
||||
startGamePacket.setPlatformBroadcastMode(GamePublishSetting.PUBLIC);
|
||||
startGamePacket.setXblBroadcastMode(GamePublishSetting.PUBLIC);
|
||||
startGamePacket.setCommandsEnabled(!connector.getConfig().isXboxAchievementsEnabled());
|
||||
@ -668,18 +700,75 @@ public class GeyserSession implements CommandSender {
|
||||
upstream.sendPacket(startGamePacket);
|
||||
}
|
||||
|
||||
public boolean confirmTeleport(Vector3d position) {
|
||||
if (teleportCache != null) {
|
||||
if (!teleportCache.canConfirm(position)) {
|
||||
GeyserConnector.getInstance().getLogger().debug("Unconfirmed Teleport " + teleportCache.getTeleportConfirmId()
|
||||
+ " Ignore movement " + position + " expected " + teleportCache);
|
||||
return false;
|
||||
public void addTeleport(TeleportCache teleportCache) {
|
||||
teleportMap.put(teleportCache.getTeleportConfirmId(), teleportCache);
|
||||
|
||||
ObjectIterator<Int2ObjectMap.Entry<TeleportCache>> it = teleportMap.int2ObjectEntrySet().iterator();
|
||||
|
||||
// Remove any teleports with a higher number - maybe this is a world change that reset the ID to 0?
|
||||
while (it.hasNext()) {
|
||||
Int2ObjectMap.Entry<TeleportCache> entry = it.next();
|
||||
int nextID = entry.getValue().getTeleportConfirmId();
|
||||
if (nextID > teleportCache.getTeleportConfirmId()) {
|
||||
it.remove();
|
||||
}
|
||||
int teleportId = teleportCache.getTeleportConfirmId();
|
||||
teleportCache = null;
|
||||
ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(teleportId);
|
||||
sendDownstreamPacket(teleportConfirmPacket);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean confirmTeleport(Vector3d position) {
|
||||
if (teleportMap.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
int teleportID = -1;
|
||||
|
||||
for (Int2ObjectMap.Entry<TeleportCache> entry : teleportMap.int2ObjectEntrySet()) {
|
||||
if (entry.getValue().canConfirm(position)) {
|
||||
if (entry.getValue().getTeleportConfirmId() > teleportID) {
|
||||
teleportID = entry.getValue().getTeleportConfirmId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ObjectIterator<Int2ObjectMap.Entry<TeleportCache>> it = teleportMap.int2ObjectEntrySet().iterator();
|
||||
|
||||
if (teleportID != -1) {
|
||||
// Confirm the current teleport and any earlier ones
|
||||
while (it.hasNext()) {
|
||||
TeleportCache entry = it.next().getValue();
|
||||
int nextID = entry.getTeleportConfirmId();
|
||||
if (nextID <= teleportID) {
|
||||
ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(nextID);
|
||||
sendDownstreamPacket(teleportConfirmPacket);
|
||||
// Servers (especially ones like Hypixel) expect exact coordinates given back to them.
|
||||
ClientPlayerPositionRotationPacket positionPacket = new ClientPlayerPositionRotationPacket(playerEntity.isOnGround(),
|
||||
entry.getX(), entry.getY(), entry.getZ(), entry.getYaw(), entry.getPitch());
|
||||
sendDownstreamPacket(positionPacket);
|
||||
it.remove();
|
||||
connector.getLogger().debug("Confirmed teleport " + nextID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (teleportMap.size() > 0) {
|
||||
int resendID = -1;
|
||||
for (Int2ObjectMap.Entry<TeleportCache> entry : teleportMap.int2ObjectEntrySet()) {
|
||||
TeleportCache teleport = entry.getValue();
|
||||
teleport.incrementUnconfirmedFor();
|
||||
if (teleport.shouldResend()) {
|
||||
if (teleport.getTeleportConfirmId() >= resendID) {
|
||||
resendID = teleport.getTeleportConfirmId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (resendID != -1) {
|
||||
connector.getLogger().debug("Resending teleport " + resendID);
|
||||
TeleportCache teleport = teleportMap.get(resendID);
|
||||
getPlayerEntity().moveAbsolute(this, Vector3f.from(teleport.getX(), teleport.getY(), teleport.getZ()),
|
||||
teleport.getYaw(), teleport.getPitch(), playerEntity.isOnGround(), true);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -25,13 +25,13 @@
|
||||
|
||||
package org.geysermc.connector.network.session.cache;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.message.Message;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.BossEventPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.RemoveEntityPacket;
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
@ -41,7 +41,7 @@ public class BossBar {
|
||||
private GeyserSession session;
|
||||
|
||||
private long entityId;
|
||||
private Message title;
|
||||
private Component title;
|
||||
private float health;
|
||||
private int color;
|
||||
private int overlay;
|
||||
@ -58,7 +58,7 @@ public class BossBar {
|
||||
BossEventPacket bossEventPacket = new BossEventPacket();
|
||||
bossEventPacket.setBossUniqueEntityId(entityId);
|
||||
bossEventPacket.setAction(BossEventPacket.Action.CREATE);
|
||||
bossEventPacket.setTitle(MessageTranslator.convertMessage(title.toString(), session.getLocale()));
|
||||
bossEventPacket.setTitle(MessageTranslator.convertMessage(title, session.getLocale()));
|
||||
bossEventPacket.setHealthPercentage(health);
|
||||
bossEventPacket.setColor(color); //ignored by client
|
||||
bossEventPacket.setOverlay(overlay);
|
||||
@ -67,12 +67,12 @@ public class BossBar {
|
||||
session.sendUpstreamPacket(bossEventPacket);
|
||||
}
|
||||
|
||||
public void updateTitle(Message title) {
|
||||
public void updateTitle(Component title) {
|
||||
this.title = title;
|
||||
BossEventPacket bossEventPacket = new BossEventPacket();
|
||||
bossEventPacket.setBossUniqueEntityId(entityId);
|
||||
bossEventPacket.setAction(BossEventPacket.Action.UPDATE_NAME);
|
||||
bossEventPacket.setTitle(MessageTranslator.convertMessage(title.toString(), session.getLocale()));
|
||||
bossEventPacket.setTitle(MessageTranslator.convertMessage(title, session.getLocale()));
|
||||
|
||||
session.sendUpstreamPacket(bossEventPacket);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import it.unimi.dsi.fastutil.longs.*;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
import java.util.*;
|
||||
|
@ -26,23 +26,47 @@
|
||||
package org.geysermc.connector.network.session.cache;
|
||||
|
||||
import com.nukkitx.math.vector.Vector3d;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
/**
|
||||
* Represents a teleport ID and corresponding coordinates that need to be confirmed. <br>
|
||||
*
|
||||
* The vanilla Java client, after getting a
|
||||
* {@link com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerPositionRotationPacket},
|
||||
* adjusts the player's positions and immediately sends a teleport back. However, we want to acknowledge that the
|
||||
* Bedrock player actually moves close to that point, so we store the teleport until we get a movement packet from
|
||||
* Bedrock that the teleport was successful.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Data
|
||||
public class TeleportCache {
|
||||
|
||||
private static final double ERROR = 0.2;
|
||||
private static final double ERROR_Y = 0.5;
|
||||
private static final double ERROR_X_AND_Z = 0.1;
|
||||
private static final double ERROR_Y = 0.1;
|
||||
|
||||
private double x, y, z;
|
||||
private int teleportConfirmId;
|
||||
/**
|
||||
* How many move packets the teleport can be unconfirmed for before it gets resent to the client
|
||||
*/
|
||||
private static final int RESEND_THRESHOLD = 5;
|
||||
|
||||
private final double x, y, z;
|
||||
private final float pitch, yaw;
|
||||
private final int teleportConfirmId;
|
||||
|
||||
private int unconfirmedFor = 0;
|
||||
|
||||
public boolean canConfirm(Vector3d position) {
|
||||
return (Math.abs(this.x - position.getX()) < ERROR &&
|
||||
return (Math.abs(this.x - position.getX()) < ERROR_X_AND_Z &&
|
||||
Math.abs(this.y - position.getY()) < ERROR_Y &&
|
||||
Math.abs(this.z - position.getZ()) < ERROR);
|
||||
Math.abs(this.z - position.getZ()) < ERROR_X_AND_Z);
|
||||
}
|
||||
|
||||
public void incrementUnconfirmedFor() {
|
||||
unconfirmedFor++;
|
||||
}
|
||||
|
||||
public boolean shouldResend() {
|
||||
return unconfirmedFor >= RESEND_THRESHOLD;
|
||||
}
|
||||
}
|
||||
|
@ -25,23 +25,19 @@
|
||||
|
||||
package org.geysermc.connector.network.translators.bedrock;
|
||||
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerSwingArmPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientSteerBoatPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.AnimatePacket;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Translator(packet = AnimatePacket.class)
|
||||
public class BedrockAnimateTranslator extends PacketTranslator<AnimatePacket> {
|
||||
|
||||
private boolean isSteeringLeft;
|
||||
private boolean isSteeringRight;
|
||||
|
||||
@Override
|
||||
public void translate(AnimatePacket packet, GeyserSession session) {
|
||||
// Stop the player sending animations before they have fully spawned into the server
|
||||
@ -61,13 +57,13 @@ public class BedrockAnimateTranslator extends PacketTranslator<AnimatePacket> {
|
||||
// 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.setSteeringLeft(packet.getRowingTime() > 0.0);
|
||||
ClientSteerBoatPacket steerLeftPacket = new ClientSteerBoatPacket(session.isSteeringLeft(), session.isSteeringRight());
|
||||
session.sendDownstreamPacket(steerLeftPacket);
|
||||
break;
|
||||
case ROW_RIGHT:
|
||||
isSteeringRight = packet.getRowingTime() > 0.0;
|
||||
ClientSteerBoatPacket steerRightPacket = new ClientSteerBoatPacket(isSteeringRight, isSteeringLeft);
|
||||
session.setSteeringRight(packet.getRowingTime() > 0.0);
|
||||
ClientSteerBoatPacket steerRightPacket = new ClientSteerBoatPacket(session.isSteeringLeft(), session.isSteeringRight());
|
||||
session.sendDownstreamPacket(steerRightPacket);
|
||||
break;
|
||||
}
|
||||
|
@ -27,19 +27,28 @@ 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.entity.BoatEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
// Used for horses
|
||||
/**
|
||||
* Sent by the client when moving a horse.
|
||||
*/
|
||||
@Translator(packet = MoveEntityAbsolutePacket.class)
|
||||
public class BedrockMoveEntityAbsoluteTranslator extends PacketTranslator<MoveEntityAbsolutePacket> {
|
||||
|
||||
@Override
|
||||
public void translate(MoveEntityAbsolutePacket packet, GeyserSession session) {
|
||||
|
||||
float y = packet.getPosition().getY();
|
||||
if (session.getRidingVehicleEntity() instanceof BoatEntity) {
|
||||
// Remove some Y position to prevents boats from looking like they're floating in water
|
||||
// Not by the full boat offset because 1.16.100 complains and that's probably not good for the future
|
||||
y -= (EntityType.BOAT.getOffset() - 0.5f);
|
||||
}
|
||||
ClientVehicleMovePacket clientVehicleMovePacket = new ClientVehicleMovePacket(
|
||||
packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ(),
|
||||
packet.getPosition().getX(), y, packet.getPosition().getZ(),
|
||||
packet.getRotation().getY() - 90, packet.getRotation().getX()
|
||||
);
|
||||
session.sendDownstreamPacket(clientVehicleMovePacket);
|
||||
|
@ -34,6 +34,7 @@ import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.AttributeUtils;
|
||||
import org.geysermc.floodgate.util.DeviceOs;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -46,9 +47,20 @@ public class BedrockNetworkStackLatencyTranslator extends PacketTranslator<Netwo
|
||||
|
||||
@Override
|
||||
public void translate(NetworkStackLatencyPacket packet, GeyserSession session) {
|
||||
long pingId;
|
||||
// so apparently, as of 1.16.200
|
||||
// PS4 divides the network stack latency timestamp FOR US!!!
|
||||
// WTF
|
||||
if (session.getClientData().getDeviceOs().equals(DeviceOs.PS4)) {
|
||||
// Ignore the weird DeviceOS, our order is wrong and will be fixed in Floodgate 2.0
|
||||
pingId = packet.getTimestamp();
|
||||
} else {
|
||||
pingId = packet.getTimestamp() / 1000;
|
||||
}
|
||||
|
||||
// negative timestamps are used as hack to fix the url image loading bug
|
||||
if (packet.getTimestamp() > 0) {
|
||||
// The client sends a timestamp back but it's rounded and therefore unreliable when we need the exact number
|
||||
ClientKeepAlivePacket keepAlivePacket = new ClientKeepAlivePacket(session.getLastKeepAliveTimestamp());
|
||||
ClientKeepAlivePacket keepAlivePacket = new ClientKeepAlivePacket(pingId);
|
||||
session.sendDownstreamPacket(keepAlivePacket);
|
||||
return;
|
||||
}
|
||||
|
@ -31,7 +31,9 @@ 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
|
||||
/**
|
||||
* Sent by the client for minecarts and boats.
|
||||
*/
|
||||
@Translator(packet = PlayerInputPacket.class)
|
||||
public class BedrockPlayerInputTranslator extends PacketTranslator<PlayerInputPacket> {
|
||||
|
||||
|
@ -31,7 +31,7 @@ import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.RespawnPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
@ -25,13 +25,14 @@
|
||||
|
||||
package org.geysermc.connector.network.translators.bedrock;
|
||||
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.SkinUtils;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket;
|
||||
import org.geysermc.connector.skin.SkinManager;
|
||||
import org.geysermc.connector.skin.SkullSkinManager;
|
||||
|
||||
@Translator(packet = SetLocalPlayerAsInitializedPacket.class)
|
||||
public class BedrockSetLocalPlayerAsInitializedTranslator extends PacketTranslator<SetLocalPlayerAsInitializedPacket> {
|
||||
@ -44,10 +45,20 @@ public class BedrockSetLocalPlayerAsInitializedTranslator extends PacketTranslat
|
||||
|
||||
for (PlayerEntity entity : session.getEntityCache().getEntitiesByType(PlayerEntity.class)) {
|
||||
if (!entity.isValid()) {
|
||||
SkinUtils.requestAndHandleSkinAndCape(entity, session, null);
|
||||
SkinManager.requestAndHandleSkinAndCape(entity, session, null);
|
||||
entity.sendPlayer(session);
|
||||
}
|
||||
}
|
||||
|
||||
// Send Skulls
|
||||
for (PlayerEntity entity : session.getSkullCache().values()) {
|
||||
entity.spawnEntity(session);
|
||||
|
||||
SkullSkinManager.requestAndHandleSkin(entity, session, (skin) -> {
|
||||
entity.getMetadata().getFlags().setFlag(EntityFlag.INVISIBLE, false);
|
||||
entity.updateBedrockMetadata(session);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,13 +42,13 @@ public class BedrockEntityEventTranslator extends PacketTranslator<EntityEventPa
|
||||
@Override
|
||||
public void translate(EntityEventPacket packet, GeyserSession session) {
|
||||
switch (packet.getType()) {
|
||||
// Resend the packet so we get the eating sounds
|
||||
case EATING_ITEM:
|
||||
// Resend the packet so we get the eating sounds
|
||||
session.sendUpstreamPacket(packet);
|
||||
return;
|
||||
case COMPLETE_TRADE:
|
||||
ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(packet.getData());
|
||||
session.getDownstream().getSession().send(selectTradePacket);
|
||||
session.sendDownstreamPacket(selectTradePacket);
|
||||
|
||||
Entity villager = session.getPlayerEntity();
|
||||
Inventory openInventory = session.getInventoryCache().getOpenInventory();
|
||||
|
@ -43,6 +43,7 @@ import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.network.translators.collision.CollisionManager;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.utils.BlockUtils;
|
||||
|
||||
|
@ -27,18 +27,20 @@ package org.geysermc.connector.network.translators.bedrock.entity.player;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerRotationPacket;
|
||||
import com.github.steveice10.packetlib.packet.Packet;
|
||||
import com.nukkitx.math.vector.Vector3d;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
|
||||
import org.geysermc.connector.common.ChatColor;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.network.translators.collision.CollisionManager;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -48,7 +50,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||
@Override
|
||||
public void translate(MovePlayerPacket packet, GeyserSession session) {
|
||||
PlayerEntity entity = session.getPlayerEntity();
|
||||
if (entity == null || !session.isSpawned() || session.getPendingDimSwitches().get() > 0) return;
|
||||
if (!session.isSpawned() || session.getPendingDimSwitches().get() > 0) return;
|
||||
|
||||
if (!session.getUpstream().isInitialized()) {
|
||||
MoveEntityAbsolutePacket moveEntityBack = new MoveEntityAbsolutePacket();
|
||||
@ -65,27 +67,53 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||
session.getMovementSendIfIdle().cancel(true);
|
||||
}
|
||||
|
||||
Vector3d position = adjustBedrockPosition(packet.getPosition(), packet.isOnGround());
|
||||
if (session.confirmTeleport(packet.getPosition().toDouble().sub(0, EntityType.PLAYER.getOffset(), 0))) {
|
||||
// head yaw, pitch, head yaw
|
||||
Vector3f rotation = Vector3f.from(packet.getRotation().getY(), packet.getRotation().getX(), packet.getRotation().getY());
|
||||
|
||||
if(!session.confirmTeleport(position)){
|
||||
return;
|
||||
boolean positionChanged = !entity.getPosition().equals(packet.getPosition());
|
||||
boolean rotationChanged = !entity.getRotation().equals(rotation);
|
||||
|
||||
// If only the pitch and yaw changed
|
||||
// This isn't needed, but it makes the packets closer to vanilla
|
||||
// It also means you can't "lag back" while only looking, in theory
|
||||
if (!positionChanged && rotationChanged) {
|
||||
ClientPlayerRotationPacket playerRotationPacket = new ClientPlayerRotationPacket(
|
||||
packet.isOnGround(), packet.getRotation().getY(), packet.getRotation().getX());
|
||||
|
||||
entity.setRotation(rotation);
|
||||
entity.setOnGround(packet.isOnGround());
|
||||
|
||||
session.sendDownstreamPacket(playerRotationPacket);
|
||||
} else {
|
||||
Vector3d position = adjustBedrockPosition(session, packet.getPosition(), packet.isOnGround());
|
||||
if (position != null) { // A null return value cancels the packet
|
||||
if (isValidMove(session, packet.getMode(), entity.getPosition(), packet.getPosition())) {
|
||||
Packet movePacket;
|
||||
if (rotationChanged) {
|
||||
// Send rotation updates as well
|
||||
movePacket = new ClientPlayerPositionRotationPacket(packet.isOnGround(), position.getX(), position.getY(), position.getZ(),
|
||||
packet.getRotation().getY(), packet.getRotation().getX());
|
||||
entity.setRotation(rotation);
|
||||
} else {
|
||||
// Rotation did not change; don't send an update with rotation
|
||||
movePacket = new ClientPlayerPositionPacket(packet.isOnGround(), position.getX(), position.getY(), position.getZ());
|
||||
}
|
||||
|
||||
entity.setPosition(packet.getPosition(), false);
|
||||
entity.setOnGround(packet.isOnGround());
|
||||
|
||||
// Send final movement changes
|
||||
session.sendDownstreamPacket(movePacket);
|
||||
} else {
|
||||
// Not a valid move
|
||||
session.getConnector().getLogger().debug("Recalculating position...");
|
||||
recalculatePosition(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValidMove(session, packet.getMode(), entity.getPosition(), packet.getPosition())) {
|
||||
session.getConnector().getLogger().debug("Recalculating position...");
|
||||
recalculatePosition(session, entity, entity.getPosition());
|
||||
return;
|
||||
}
|
||||
|
||||
ClientPlayerPositionRotationPacket playerPositionRotationPacket = new ClientPlayerPositionRotationPacket(
|
||||
packet.isOnGround(), position.getX(), position.getY(), position.getZ(), packet.getRotation().getY(), packet.getRotation().getX()
|
||||
);
|
||||
|
||||
// head yaw, pitch, head yaw
|
||||
Vector3f rotation = Vector3f.from(packet.getRotation().getY(), packet.getRotation().getX(), packet.getRotation().getY());
|
||||
entity.setPosition(packet.getPosition().sub(0, EntityType.PLAYER.getOffset(), 0));
|
||||
entity.setRotation(rotation);
|
||||
entity.setOnGround(packet.isOnGround());
|
||||
// Move parrots to match if applicable
|
||||
if (entity.getLeftParrot() != null) {
|
||||
entity.getLeftParrot().moveAbsolute(session, entity.getPosition(), entity.getRotation(), true, false);
|
||||
@ -94,42 +122,11 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||
entity.getRightParrot().moveAbsolute(session, entity.getPosition(), entity.getRotation(), true, false);
|
||||
}
|
||||
|
||||
/*
|
||||
boolean colliding = false;
|
||||
Position position = new Position((int) packet.getPosition().getX(),
|
||||
(int) Math.ceil(javaY * 2) / 2, (int) packet.getPosition().getZ());
|
||||
|
||||
BlockEntry block = session.getChunkCache().getBlockAt(position);
|
||||
if (!block.getJavaIdentifier().contains("air"))
|
||||
colliding = true;
|
||||
|
||||
if (!colliding)
|
||||
*/
|
||||
session.sendDownstreamPacket(playerPositionRotationPacket);
|
||||
|
||||
// Schedule a position send loop if the player is idle
|
||||
session.setMovementSendIfIdle(session.getConnector().getGeneralThreadPool().schedule(() -> sendPositionIfIdle(session),
|
||||
3, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the Bedrock position before sending to the Java server to account for inaccuracies in movement between
|
||||
* the two versions.
|
||||
*
|
||||
* @param position the current Bedrock position of the client
|
||||
* @param onGround whether the Bedrock player is on the ground
|
||||
* @return the position to send to the Java server.
|
||||
*/
|
||||
private Vector3d adjustBedrockPosition(Vector3f position, boolean onGround) {
|
||||
// We need to parse the float as a string since casting a float to a double causes us to
|
||||
// lose precision and thus, causes players to get stuck when walking near walls
|
||||
double javaY = position.getY() - EntityType.PLAYER.getOffset();
|
||||
if (onGround) javaY = Math.ceil(javaY * 2) / 2;
|
||||
|
||||
return Vector3d.from(Double.parseDouble(Float.toString(position.getX())), javaY,
|
||||
Double.parseDouble(Float.toString(position.getZ())));
|
||||
}
|
||||
|
||||
public boolean isValidMove(GeyserSession session, MovePlayerPacket.Mode mode, Vector3f currentPosition, Vector3f newPosition) {
|
||||
if (mode != MovePlayerPacket.Mode.NORMAL)
|
||||
return true;
|
||||
@ -155,7 +152,53 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||
return true;
|
||||
}
|
||||
|
||||
public void recalculatePosition(GeyserSession session, Entity entity, Vector3f currentPosition) {
|
||||
/**
|
||||
* Adjust the Bedrock position before sending to the Java server to account for inaccuracies in movement between
|
||||
* the two versions.
|
||||
*
|
||||
* @param session the current GeyserSession
|
||||
* @param bedrockPosition the current Bedrock position of the client
|
||||
* @param onGround whether the Bedrock player is on the ground
|
||||
* @return the position to send to the Java server, or null to cancel sending the packet
|
||||
*/
|
||||
private Vector3d adjustBedrockPosition(GeyserSession session, Vector3f bedrockPosition, boolean onGround) {
|
||||
// We need to parse the float as a string since casting a float to a double causes us to
|
||||
// lose precision and thus, causes players to get stuck when walking near walls
|
||||
double javaY = bedrockPosition.getY() - EntityType.PLAYER.getOffset();
|
||||
|
||||
Vector3d position = Vector3d.from(Double.parseDouble(Float.toString(bedrockPosition.getX())), javaY,
|
||||
Double.parseDouble(Float.toString(bedrockPosition.getZ())));
|
||||
|
||||
if (session.getConnector().getConfig().isCacheChunks()) {
|
||||
// With chunk caching, we can do some proper collision checks
|
||||
CollisionManager collisionManager = session.getCollisionManager();
|
||||
collisionManager.updatePlayerBoundingBox(position);
|
||||
|
||||
// Correct player position
|
||||
if (!collisionManager.correctPlayerPosition()) {
|
||||
// Cancel the movement if it needs to be cancelled
|
||||
recalculatePosition(session);
|
||||
return null;
|
||||
}
|
||||
|
||||
position = Vector3d.from(collisionManager.getPlayerBoundingBox().getMiddleX(),
|
||||
collisionManager.getPlayerBoundingBox().getMiddleY() - (collisionManager.getPlayerBoundingBox().getSizeY() / 2),
|
||||
collisionManager.getPlayerBoundingBox().getMiddleZ());
|
||||
} else {
|
||||
// When chunk caching is off, we have to rely on this
|
||||
// It rounds the Y position up to the nearest 0.5
|
||||
// This snaps players to snap to the top of stairs and slabs like on Java Edition
|
||||
// However, it causes issues such as the player floating on carpets
|
||||
if (onGround) javaY = Math.ceil(javaY * 2) / 2;
|
||||
position = position.up(javaY - position.getY());
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
// TODO: This makes the player look upwards for some reason, rotation values must be wrong
|
||||
public void recalculatePosition(GeyserSession session) {
|
||||
PlayerEntity entity = session.getPlayerEntity();
|
||||
// Gravity might need to be reset...
|
||||
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
|
||||
entityDataPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
@ -166,7 +209,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
movePlayerPacket.setPosition(entity.getPosition());
|
||||
movePlayerPacket.setRotation(entity.getBedrockRotation());
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.RESPAWN);
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.NORMAL);
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
}
|
||||
|
||||
@ -174,11 +217,15 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||
if (session.isClosed()) return;
|
||||
PlayerEntity entity = session.getPlayerEntity();
|
||||
// Recalculate in case something else changed position
|
||||
Vector3d position = adjustBedrockPosition(entity.getPosition(), entity.isOnGround());
|
||||
ClientPlayerPositionPacket packet = new ClientPlayerPositionPacket(session.getPlayerEntity().isOnGround(),
|
||||
position.getX(), position.getY(), position.getZ());
|
||||
session.sendDownstreamPacket(packet);
|
||||
Vector3d position = adjustBedrockPosition(session, entity.getPosition(), entity.isOnGround());
|
||||
// A null return value cancels the packet
|
||||
if (position != null) {
|
||||
ClientPlayerPositionPacket packet = new ClientPlayerPositionPacket(session.getPlayerEntity().isOnGround(),
|
||||
position.getX(), position.getY(), position.getZ());
|
||||
session.sendDownstreamPacket(packet);
|
||||
}
|
||||
session.setMovementSendIfIdle(session.getConnector().getGeneralThreadPool().schedule(() -> sendPositionIfIdle(session),
|
||||
3, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,49 +25,51 @@
|
||||
|
||||
package org.geysermc.connector.network.translators.chat;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.DefaultComponentSerializer;
|
||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
|
||||
import com.github.steveice10.mc.protocol.data.message.style.ChatColor;
|
||||
import com.github.steveice10.mc.protocol.data.message.style.ChatFormat;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.kyori.adventure.text.renderer.TranslatableComponentRenderer;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.gson.legacyimpl.NBTLegacyHoverEventSerializer;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.kyori.adventure.translation.TranslationRegistry;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class MessageTranslator {
|
||||
|
||||
// These are used for handling the translations of the messages
|
||||
private static final TranslationRegistry REGISTRY = new MinecraftTranslationRegistry();
|
||||
private static final TranslatableComponentRenderer<Locale> RENDERER = TranslatableComponentRenderer.usingTranslationSource(REGISTRY);
|
||||
private static final TranslatableComponentRenderer<Locale> RENDERER = TranslatableComponentRenderer.usingTranslationSource(new MinecraftTranslationRegistry());
|
||||
|
||||
// Construct our own {@link GsonComponentSerializer} since we need to change a setting
|
||||
private static final GsonComponentSerializer GSON_SERIALIZER = GsonComponentSerializer.builder()
|
||||
// Specify that we may be expecting legacy hover events
|
||||
.legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get())
|
||||
.build();
|
||||
|
||||
// Store team colors for player names
|
||||
private static final Map<TeamColor, String> TEAM_COLORS = new HashMap<>();
|
||||
private static final Map<TeamColor, TextDecoration> TEAM_FORMATS = new HashMap<>();
|
||||
|
||||
// Legacy formatting character
|
||||
private static final String BASE = "\u00a7";
|
||||
|
||||
// Reset character
|
||||
private static final String RESET = BASE + "r";
|
||||
|
||||
static {
|
||||
TEAM_COLORS.put(TeamColor.BLACK, getColor(ChatColor.BLACK));
|
||||
TEAM_COLORS.put(TeamColor.DARK_BLUE, getColor(ChatColor.DARK_BLUE));
|
||||
TEAM_COLORS.put(TeamColor.DARK_GREEN, getColor(ChatColor.DARK_GREEN));
|
||||
TEAM_COLORS.put(TeamColor.DARK_AQUA, getColor(ChatColor.DARK_AQUA));
|
||||
TEAM_COLORS.put(TeamColor.DARK_RED, getColor(ChatColor.DARK_RED));
|
||||
TEAM_COLORS.put(TeamColor.DARK_PURPLE, getColor(ChatColor.DARK_PURPLE));
|
||||
TEAM_COLORS.put(TeamColor.GOLD, getColor(ChatColor.GOLD));
|
||||
TEAM_COLORS.put(TeamColor.GRAY, getColor(ChatColor.GRAY));
|
||||
TEAM_COLORS.put(TeamColor.DARK_GRAY, getColor(ChatColor.DARK_GRAY));
|
||||
TEAM_COLORS.put(TeamColor.BLUE, getColor(ChatColor.BLUE));
|
||||
TEAM_COLORS.put(TeamColor.GREEN, getColor(ChatColor.GREEN));
|
||||
TEAM_COLORS.put(TeamColor.AQUA, getColor(ChatColor.AQUA));
|
||||
TEAM_COLORS.put(TeamColor.RED, getColor(ChatColor.RED));
|
||||
TEAM_COLORS.put(TeamColor.LIGHT_PURPLE, getColor(ChatColor.LIGHT_PURPLE));
|
||||
TEAM_COLORS.put(TeamColor.YELLOW, getColor(ChatColor.YELLOW));
|
||||
TEAM_COLORS.put(TeamColor.WHITE, getColor(ChatColor.WHITE));
|
||||
TEAM_COLORS.put(TeamColor.OBFUSCATED, getFormat(ChatFormat.OBFUSCATED));
|
||||
TEAM_COLORS.put(TeamColor.BOLD, getFormat(ChatFormat.BOLD));
|
||||
TEAM_COLORS.put(TeamColor.STRIKETHROUGH, getFormat(ChatFormat.STRIKETHROUGH));
|
||||
TEAM_COLORS.put(TeamColor.ITALIC, getFormat(ChatFormat.ITALIC));
|
||||
TEAM_FORMATS.put(TeamColor.OBFUSCATED, TextDecoration.OBFUSCATED);
|
||||
TEAM_FORMATS.put(TeamColor.BOLD, TextDecoration.BOLD);
|
||||
TEAM_FORMATS.put(TeamColor.STRIKETHROUGH, TextDecoration.STRIKETHROUGH);
|
||||
TEAM_FORMATS.put(TeamColor.ITALIC, TextDecoration.ITALIC);
|
||||
|
||||
// Tell MCProtocolLib to use our serializer
|
||||
DefaultComponentSerializer.set(GSON_SERIALIZER);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,20 +79,43 @@ public class MessageTranslator {
|
||||
* @param locale Locale to use for translation strings
|
||||
* @return Parsed and formatted message for bedrock
|
||||
*/
|
||||
public static String convertMessage(Component message, String locale) {
|
||||
try {
|
||||
// Get a Locale from the given locale string
|
||||
Locale localeCode = Locale.forLanguageTag(locale.replace('_', '-'));
|
||||
message = RENDERER.render(message, localeCode);
|
||||
|
||||
String legacy = LegacyComponentSerializer.legacySection().serialize(message);
|
||||
|
||||
// Strip strikethrough and underline as they are not supported on bedrock
|
||||
legacy = legacy.replaceAll("\u00a7[mn]", "");
|
||||
|
||||
// Make color codes reset formatting like Java
|
||||
// See https://minecraft.gamepedia.com/Formatting_codes#Usage
|
||||
legacy = legacy.replaceAll("\u00a7([0-9a-f])", "\u00a7r\u00a7$1");
|
||||
legacy = legacy.replaceAll("\u00a7r\u00a7r", "\u00a7r");
|
||||
|
||||
return legacy;
|
||||
} catch (Exception e) {
|
||||
GeyserConnector.getInstance().getLogger().debug(GSON_SERIALIZER.serialize(message));
|
||||
GeyserConnector.getInstance().getLogger().error("Failed to parse message", e);
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static String convertMessage(String message, String locale) {
|
||||
Component component = GsonComponentSerializer.gson().deserialize(message);
|
||||
|
||||
// Get a Locale from the given locale string
|
||||
Locale localeCode = Locale.forLanguageTag(locale.replace('_', '-'));
|
||||
component = RENDERER.render(component, localeCode);
|
||||
|
||||
return LegacyComponentSerializer.legacySection().serialize(component);
|
||||
return convertMessage(GSON_SERIALIZER.deserialize(message), locale);
|
||||
}
|
||||
|
||||
public static String convertMessage(String message) {
|
||||
return convertMessage(message, LanguageUtils.getDefaultLocale());
|
||||
}
|
||||
|
||||
public static String convertMessage(Component message) {
|
||||
return convertMessage(message, LanguageUtils.getDefaultLocale());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the message is valid JSON in case it's plaintext. Works around GsonComponentSeraializer not using lenient mode.
|
||||
* See https://wiki.vg/Chat for messages sent in lenient mode, and for a description on leniency.
|
||||
@ -100,14 +125,18 @@ public class MessageTranslator {
|
||||
* @return Bedrock formatted message
|
||||
*/
|
||||
public static String convertMessageLenient(String message, String locale) {
|
||||
if (isMessage(message)) {
|
||||
if (message.trim().isEmpty()) {
|
||||
return message;
|
||||
}
|
||||
|
||||
try {
|
||||
return convertMessage(message, locale);
|
||||
} else {
|
||||
} catch (Exception ignored) {
|
||||
String convertedMessage = convertMessage(convertToJavaMessage(message), locale);
|
||||
|
||||
// We have to do this since Adventure strips the starting reset character
|
||||
if (message.startsWith(getColor(ChatColor.RESET))) {
|
||||
convertedMessage = getColor(ChatColor.RESET) + convertedMessage;
|
||||
if (message.startsWith(RESET) && !convertedMessage.startsWith(RESET)) {
|
||||
convertedMessage = RESET + convertedMessage;
|
||||
}
|
||||
|
||||
return convertedMessage;
|
||||
@ -126,138 +155,104 @@ public class MessageTranslator {
|
||||
*/
|
||||
public static String convertToJavaMessage(String message) {
|
||||
Component component = LegacyComponentSerializer.legacySection().deserialize(message);
|
||||
return GsonComponentSerializer.gson().serialize(component);
|
||||
return GSON_SERIALIZER.serialize(component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given text string is a JSON message
|
||||
* Convert a {@link NamedTextColor} into a string for inserting into messages
|
||||
*
|
||||
* @param text String to test
|
||||
* @return True if its a valid message JSON string, false if not
|
||||
*/
|
||||
public static boolean isMessage(String text) {
|
||||
if (text.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
GsonComponentSerializer.gson().deserialize(text);
|
||||
} catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a {@link ChatColor} into a string for inserting into messages
|
||||
*
|
||||
* @param color {@link ChatColor} to convert
|
||||
* @param color {@link NamedTextColor} to convert
|
||||
* @return The converted color string
|
||||
*/
|
||||
private static String getColor(String color) {
|
||||
String base = "\u00a7";
|
||||
switch (color) {
|
||||
case ChatColor.BLACK:
|
||||
base += "0";
|
||||
break;
|
||||
case ChatColor.DARK_BLUE:
|
||||
base += "1";
|
||||
break;
|
||||
case ChatColor.DARK_GREEN:
|
||||
base += "2";
|
||||
break;
|
||||
case ChatColor.DARK_AQUA:
|
||||
base += "3";
|
||||
break;
|
||||
case ChatColor.DARK_RED:
|
||||
base += "4";
|
||||
break;
|
||||
case ChatColor.DARK_PURPLE:
|
||||
base += "5";
|
||||
break;
|
||||
case ChatColor.GOLD:
|
||||
base += "6";
|
||||
break;
|
||||
case ChatColor.GRAY:
|
||||
base += "7";
|
||||
break;
|
||||
case ChatColor.DARK_GRAY:
|
||||
base += "8";
|
||||
break;
|
||||
case ChatColor.BLUE:
|
||||
base += "9";
|
||||
break;
|
||||
case ChatColor.GREEN:
|
||||
base += "a";
|
||||
break;
|
||||
case ChatColor.AQUA:
|
||||
base += "b";
|
||||
break;
|
||||
case ChatColor.RED:
|
||||
base += "c";
|
||||
break;
|
||||
case ChatColor.LIGHT_PURPLE:
|
||||
base += "d";
|
||||
break;
|
||||
case ChatColor.YELLOW:
|
||||
base += "e";
|
||||
break;
|
||||
case ChatColor.WHITE:
|
||||
base += "f";
|
||||
break;
|
||||
case ChatColor.RESET:
|
||||
base += "r";
|
||||
break;
|
||||
default:
|
||||
return "";
|
||||
private static String getColor(NamedTextColor color) {
|
||||
StringBuilder str = new StringBuilder(BASE);
|
||||
if (color.equals(NamedTextColor.BLACK)) {
|
||||
str.append("0");
|
||||
} else if (color.equals(NamedTextColor.DARK_BLUE)) {
|
||||
str.append("1");
|
||||
} else if (color.equals(NamedTextColor.DARK_GREEN)) {
|
||||
str.append("2");
|
||||
} else if (color.equals(NamedTextColor.DARK_AQUA)) {
|
||||
str.append("3");
|
||||
} else if (color.equals(NamedTextColor.DARK_RED)) {
|
||||
str.append("4");
|
||||
} else if (color.equals(NamedTextColor.DARK_PURPLE)) {
|
||||
str.append("5");
|
||||
} else if (color.equals(NamedTextColor.GOLD)) {
|
||||
str.append("6");
|
||||
} else if (color.equals(NamedTextColor.GRAY)) {
|
||||
str.append("7");
|
||||
} else if (color.equals(NamedTextColor.DARK_GRAY)) {
|
||||
str.append("8");
|
||||
} else if (color.equals(NamedTextColor.BLUE)) {
|
||||
str.append("9");
|
||||
} else if (color.equals(NamedTextColor.GREEN)) {
|
||||
str.append("a");
|
||||
} else if (color.equals(NamedTextColor.AQUA)) {
|
||||
str.append("b");
|
||||
} else if (color.equals(NamedTextColor.RED)) {
|
||||
str.append("c");
|
||||
} else if (color.equals(NamedTextColor.LIGHT_PURPLE)) {
|
||||
str.append("d");
|
||||
} else if (color.equals(NamedTextColor.YELLOW)) {
|
||||
str.append("e");
|
||||
} else if (color.equals(NamedTextColor.WHITE)) {
|
||||
str.append("f");
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
||||
return base;
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a {@link ChatFormat} into a string for inserting into messages
|
||||
* Convert a {@link TextDecoration} into a string for inserting into messages
|
||||
*
|
||||
* @param format {@link ChatFormat} to convert
|
||||
* @param format {@link TextDecoration} to convert
|
||||
* @return The converted chat formatting string
|
||||
*/
|
||||
private static String getFormat(ChatFormat format) {
|
||||
StringBuilder str = new StringBuilder();
|
||||
String base = "\u00a7";
|
||||
private static String getFormat(TextDecoration format) {
|
||||
StringBuilder str = new StringBuilder(BASE);
|
||||
switch (format) {
|
||||
case OBFUSCATED:
|
||||
base += "k";
|
||||
str.append("k");
|
||||
break;
|
||||
case BOLD:
|
||||
base += "l";
|
||||
str.append("l");
|
||||
break;
|
||||
case STRIKETHROUGH:
|
||||
base += "m";
|
||||
str.append("m");
|
||||
break;
|
||||
case UNDERLINED:
|
||||
base += "n";
|
||||
str.append("n");
|
||||
break;
|
||||
case ITALIC:
|
||||
base += "o";
|
||||
str.append("o");
|
||||
break;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
str.append(base);
|
||||
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a team color to a chat color
|
||||
*
|
||||
* @param teamColor
|
||||
* @param teamColor Color or format to convert
|
||||
* @return The chat color character
|
||||
*/
|
||||
public static String toChatColor(TeamColor teamColor) {
|
||||
return TEAM_COLORS.getOrDefault(teamColor, "");
|
||||
if (teamColor.equals(TeamColor.NONE)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
NamedTextColor textColor = NamedTextColor.NAMES.value(teamColor.name().toLowerCase());
|
||||
if (textColor != null) {
|
||||
return getColor(textColor);
|
||||
}
|
||||
|
||||
return getFormat(TEAM_FORMATS.get(teamColor));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,7 +26,7 @@
|
||||
package org.geysermc.connector.network.translators.chat;
|
||||
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.translation.TranslationRegistry;
|
||||
import net.kyori.adventure.translation.Translator;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
@ -40,10 +40,10 @@ import java.util.regex.Pattern;
|
||||
* This class is used for mapping a translation key with the already loaded Java locale data
|
||||
* Used in MessageTranslator.java as part of the KyoriPowered/Adventure library
|
||||
*/
|
||||
public class MinecraftTranslationRegistry implements TranslationRegistry {
|
||||
public class MinecraftTranslationRegistry implements Translator {
|
||||
@Override
|
||||
public @NonNull Key name() {
|
||||
return Key.key("", "");
|
||||
return Key.key("geyser", "minecraft_translations");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -61,21 +61,16 @@ public class MinecraftTranslationRegistry implements TranslationRegistry {
|
||||
}
|
||||
m.appendTail(sb);
|
||||
|
||||
// Replace the `%x$s` with numbered inserts `{x}`
|
||||
p = Pattern.compile("%([0-9]+)\\$s");
|
||||
m = p.matcher(sb.toString());
|
||||
sb = new StringBuffer();
|
||||
while (m.find()) {
|
||||
i = Integer.parseInt(m.group(1)) - 1;
|
||||
m.appendReplacement(sb, "{" + i + "}");
|
||||
}
|
||||
m.appendTail(sb);
|
||||
|
||||
return new MessageFormat(sb.toString(), locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defaultLocale(@NonNull Locale locale) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(@NonNull String key, @NonNull Locale locale, @NonNull MessageFormat format) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(@NonNull String key) {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.collision;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class BoundingBox {
|
||||
private double middleX;
|
||||
private double middleY;
|
||||
private double middleZ;
|
||||
|
||||
private double sizeX;
|
||||
private double sizeY;
|
||||
private double sizeZ;
|
||||
|
||||
public void translate(double x, double y, double z) {
|
||||
middleX += x;
|
||||
middleY += y;
|
||||
middleZ += z;
|
||||
}
|
||||
|
||||
public boolean checkIntersection(int offsetX, int offsetY, int offsetZ, BoundingBox otherBox) {
|
||||
return (Math.abs((middleX + offsetX) - otherBox.getMiddleX()) * 2 < (sizeX + otherBox.getSizeX())) &&
|
||||
(Math.abs((middleY + offsetY) - otherBox.getMiddleY()) * 2 < (sizeY + otherBox.getSizeY())) &&
|
||||
(Math.abs((middleZ + offsetZ) - otherBox.getMiddleZ()) * 2 < (sizeZ + otherBox.getSizeZ()));
|
||||
}
|
||||
}
|
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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.collision;
|
||||
|
||||
import com.nukkitx.math.vector.Vector3d;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.math.vector.Vector3i;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlags;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.collision.translators.BlockCollision;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CollisionManager {
|
||||
|
||||
private final GeyserSession session;
|
||||
|
||||
@Getter
|
||||
private BoundingBox playerBoundingBox;
|
||||
|
||||
/**
|
||||
* Whether the player is inside scaffolding
|
||||
*/
|
||||
@Setter
|
||||
private boolean touchingScaffolding;
|
||||
|
||||
/**
|
||||
* Whether the player is on top of scaffolding
|
||||
*/
|
||||
@Setter
|
||||
private boolean onScaffolding;
|
||||
|
||||
/**
|
||||
* Additional space where blocks are checked, which is helpful for fixing NoCheatPlus's Passable check.
|
||||
* This check doesn't allow players right up against the block, so they must be pushed slightly away.
|
||||
*/
|
||||
public static final double COLLISION_TOLERANCE = 0.00001;
|
||||
|
||||
public CollisionManager(GeyserSession session) {
|
||||
this.session = session;
|
||||
this.playerBoundingBox = new BoundingBox(0, 0, 0, 0.6, 1.8, 0.6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the stored bounding box
|
||||
* @param position The new position of the player
|
||||
*/
|
||||
public void updatePlayerBoundingBox(Vector3f position) {
|
||||
updatePlayerBoundingBox(position.toDouble());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the stored bounding box
|
||||
* @param position The new position of the player
|
||||
*/
|
||||
public void updatePlayerBoundingBox(Vector3d position) {
|
||||
updatePlayerBoundingBox();
|
||||
|
||||
playerBoundingBox.setMiddleX(position.getX());
|
||||
playerBoundingBox.setMiddleY(position.getY() + (playerBoundingBox.getSizeY() / 2));
|
||||
playerBoundingBox.setMiddleZ(position.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the stored bounding box without passing a position, which currently just changes the height depending on if the player is sneaking.
|
||||
*/
|
||||
public void updatePlayerBoundingBox() {
|
||||
if (playerBoundingBox == null) {
|
||||
Vector3f playerPosition;
|
||||
if (session.getPlayerEntity() == null) {
|
||||
// Temporary position to prevent NullPointerException
|
||||
playerPosition = Vector3f.ZERO;
|
||||
} else {
|
||||
playerPosition = session.getPlayerEntity().getPosition();
|
||||
}
|
||||
playerBoundingBox = new BoundingBox(playerPosition.getX(), playerPosition.getY() + 0.9, playerPosition.getZ(), 0.6, 1.8, 0.6);
|
||||
} else {
|
||||
// According to the Minecraft Wiki, when sneaking:
|
||||
// - In Bedrock Edition, the height becomes 1.65 blocks, allowing movement through spaces as small as 1.75 (2 - 1⁄4) blocks high.
|
||||
// - In Java Edition, the height becomes 1.5 blocks.
|
||||
if (session.isSneaking()) {
|
||||
playerBoundingBox.setSizeY(1.5);
|
||||
} else {
|
||||
playerBoundingBox.setSizeY(1.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Vector3i> getPlayerCollidableBlocks() {
|
||||
List<Vector3i> blocks = new ArrayList<>();
|
||||
|
||||
Vector3d position = Vector3d.from(playerBoundingBox.getMiddleX(),
|
||||
playerBoundingBox.getMiddleY() - (playerBoundingBox.getSizeY() / 2),
|
||||
playerBoundingBox.getMiddleZ());
|
||||
|
||||
// Loop through all blocks that could collide with the player
|
||||
int minCollisionX = (int) Math.floor(position.getX() - ((playerBoundingBox.getSizeX() / 2) + COLLISION_TOLERANCE));
|
||||
int maxCollisionX = (int) Math.floor(position.getX() + (playerBoundingBox.getSizeX() / 2) + COLLISION_TOLERANCE);
|
||||
|
||||
// Y extends 0.5 blocks down because of fence hitboxes
|
||||
int minCollisionY = (int) Math.floor(position.getY() - 0.5);
|
||||
|
||||
int maxCollisionY = (int) Math.floor(position.getY() + playerBoundingBox.getSizeY());
|
||||
|
||||
int minCollisionZ = (int) Math.floor(position.getZ() - ((playerBoundingBox.getSizeZ() / 2) + COLLISION_TOLERANCE));
|
||||
int maxCollisionZ = (int) Math.floor(position.getZ() + (playerBoundingBox.getSizeZ() / 2) + COLLISION_TOLERANCE);
|
||||
|
||||
for (int y = minCollisionY; y < maxCollisionY + 1; y++) {
|
||||
for (int x = minCollisionX; x < maxCollisionX + 1; x++) {
|
||||
for (int z = minCollisionZ; z < maxCollisionZ + 1; z++) {
|
||||
blocks.add(Vector3i.from(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if the movement is invalid, and in this case it shouldn't be sent to the server and should be
|
||||
* cancelled
|
||||
* See {@link BlockCollision#correctPosition(GeyserSession, BoundingBox)} for more info
|
||||
*/
|
||||
public boolean correctPlayerPosition() {
|
||||
|
||||
// These may be set to true by the correctPosition method in ScaffoldingCollision
|
||||
touchingScaffolding = false;
|
||||
onScaffolding = false;
|
||||
|
||||
List<Vector3i> collidableBlocks = getPlayerCollidableBlocks();
|
||||
|
||||
// Used when correction code needs to be run before the main correction
|
||||
for (Vector3i blockPos : collidableBlocks) {
|
||||
BlockCollision blockCollision = CollisionTranslator.getCollisionAt(
|
||||
session, blockPos.getX(), blockPos.getY(), blockPos.getZ()
|
||||
);
|
||||
if (blockCollision != null) {
|
||||
blockCollision.beforeCorrectPosition(playerBoundingBox);
|
||||
}
|
||||
}
|
||||
|
||||
// Main correction code
|
||||
for (Vector3i blockPos : collidableBlocks) {
|
||||
BlockCollision blockCollision = CollisionTranslator.getCollisionAt(
|
||||
session, blockPos.getX(), blockPos.getY(), blockPos.getZ()
|
||||
);
|
||||
if (blockCollision != null) {
|
||||
if (!blockCollision.correctPosition(session, playerBoundingBox)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateScaffoldingFlags();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates scaffolding entity flags
|
||||
* Scaffolding needs to be checked per-move since it's a flag in Bedrock but Java does it client-side
|
||||
*/
|
||||
public void updateScaffoldingFlags() {
|
||||
EntityFlags flags = session.getPlayerEntity().getMetadata().getFlags();
|
||||
boolean flagsChanged;
|
||||
boolean isSneakingWithScaffolding = (touchingScaffolding || onScaffolding) && session.isSneaking();
|
||||
|
||||
flagsChanged = flags.getFlag(EntityFlag.FALL_THROUGH_SCAFFOLDING) != isSneakingWithScaffolding;
|
||||
flagsChanged |= flags.getFlag(EntityFlag.OVER_SCAFFOLDING) != isSneakingWithScaffolding;
|
||||
|
||||
flags.setFlag(EntityFlag.FALL_THROUGH_SCAFFOLDING, isSneakingWithScaffolding);
|
||||
flags.setFlag(EntityFlag.OVER_SCAFFOLDING, isSneakingWithScaffolding);
|
||||
|
||||
flagsChanged |= flags.getFlag(EntityFlag.IN_SCAFFOLDING) != touchingScaffolding;
|
||||
flags.setFlag(EntityFlag.IN_SCAFFOLDING, touchingScaffolding);
|
||||
|
||||
if (flagsChanged) {
|
||||
session.getPlayerEntity().updateBedrockMetadata(session);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.collision;
|
||||
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(value = RetentionPolicy.RUNTIME)
|
||||
public @interface CollisionRemapper {
|
||||
|
||||
/**
|
||||
* Regex of block identifiers to apply this collision to
|
||||
* Matches against just the block ID name, not including the namespace or parameters
|
||||
*/
|
||||
String regex();
|
||||
|
||||
/**
|
||||
* Regex of block state parameters to apply this collision to
|
||||
* Defaults to matching any value
|
||||
*/
|
||||
String paramRegex() default ".*";
|
||||
|
||||
/**
|
||||
* Signals if a new instance needs to created for every block state
|
||||
*/
|
||||
boolean usesParams() default false;
|
||||
|
||||
/**
|
||||
* Signals if the default bounding boxes of this block as defined in collision.json should be passed to the
|
||||
* constructor
|
||||
*/
|
||||
boolean passDefaultBoxes() default false;
|
||||
}
|
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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.collision;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.google.common.collect.BiMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.collision.translators.BlockCollision;
|
||||
import org.geysermc.connector.network.translators.collision.translators.EmptyCollision;
|
||||
import org.geysermc.connector.network.translators.collision.translators.OtherCollision;
|
||||
import org.geysermc.connector.network.translators.collision.translators.SolidCollision;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.utils.FileUtils;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class CollisionTranslator {
|
||||
private static final Int2ObjectMap<BlockCollision> COLLISION_MAP = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public static void init() {
|
||||
// If chunk caching is off then don't initialize
|
||||
if (!GeyserConnector.getInstance().getConfig().isCacheChunks()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Class<?>> collisionTypes = new ArrayList<>();
|
||||
|
||||
Map<Class<?>, CollisionRemapper> annotationMap = new HashMap<>();
|
||||
|
||||
Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections("org.geysermc.connector.network.translators.collision.translators") : new Reflections("org.geysermc.connector.network.translators.collision.translators");
|
||||
for (Class<?> clazz : ref.getTypesAnnotatedWith(CollisionRemapper.class)) {
|
||||
GeyserConnector.getInstance().getLogger().debug("Found annotated collision translator: " + clazz.getCanonicalName());
|
||||
|
||||
collisionTypes.add(clazz);
|
||||
annotationMap.put(clazz, clazz.getAnnotation(CollisionRemapper.class));
|
||||
}
|
||||
|
||||
// Load collision mappings file
|
||||
InputStream stream = FileUtils.getResource("mappings/collision.json");
|
||||
|
||||
ArrayNode collisionList;
|
||||
try {
|
||||
collisionList = (ArrayNode) GeyserConnector.JSON_MAPPER.readTree(stream);
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Unable to load collision data", e);
|
||||
}
|
||||
|
||||
BiMap<String, Integer> javaIdBlockMap = BlockTranslator.getJavaIdBlockMap();
|
||||
|
||||
// Map of classes that don't change based on parameters that have already been created
|
||||
Map<Class<?>, BlockCollision> instantiatedCollision = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, Integer> entry : javaIdBlockMap.entrySet()) {
|
||||
BlockCollision newCollision = instantiateCollision(entry.getKey(), entry.getValue(), collisionTypes, annotationMap, instantiatedCollision, collisionList);
|
||||
if (newCollision != null) {
|
||||
instantiatedCollision.put(newCollision.getClass(), newCollision);
|
||||
}
|
||||
COLLISION_MAP.put(entry.getValue().intValue(), newCollision);
|
||||
}
|
||||
}
|
||||
|
||||
private static BlockCollision instantiateCollision(String blockID, int numericBlockID, List<Class<?>> collisionTypes, Map<Class<?>, CollisionRemapper> annotationMap, Map<Class<?>, BlockCollision> instantiatedCollision, ArrayNode collisionList) {
|
||||
|
||||
String blockName = blockID.split("\\[")[0].replace("minecraft:", "");
|
||||
String params = "";
|
||||
if (blockID.contains("[")) {
|
||||
params = "[" + blockID.split("\\[")[1];
|
||||
}
|
||||
int collisionIndex = BlockTranslator.JAVA_RUNTIME_ID_TO_COLLISION_INDEX.get(numericBlockID);
|
||||
|
||||
for (Class<?> type : collisionTypes) {
|
||||
CollisionRemapper annotation = annotationMap.get(type);
|
||||
|
||||
Pattern pattern = Pattern.compile(annotation.regex());
|
||||
Pattern paramsPattern = Pattern.compile(annotation.paramRegex());
|
||||
|
||||
if (pattern.matcher(blockName).find() && paramsPattern.matcher(params).find()) {
|
||||
try {
|
||||
if (!annotation.usesParams() && instantiatedCollision.containsKey(type)) {
|
||||
return instantiatedCollision.get(type);
|
||||
}
|
||||
|
||||
// Return null when empty to save unnecessary checks
|
||||
if (type == EmptyCollision.class) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BlockCollision collision;
|
||||
if (annotation.passDefaultBoxes()) {
|
||||
// Create an OtherCollision instance and get the bounding boxes
|
||||
BoundingBox[] defaultBoxes = new OtherCollision((ArrayNode) collisionList.get(collisionIndex)).getBoundingBoxes();
|
||||
collision = (BlockCollision) type.getDeclaredConstructor(String.class, BoundingBox[].class).newInstance(params, defaultBoxes);
|
||||
} else {
|
||||
collision = (BlockCollision) type.getDeclaredConstructor(String.class).newInstance(params);
|
||||
}
|
||||
|
||||
// If there's an existing instance equal to this one, use that instead
|
||||
for (Map.Entry<Class<?>, BlockCollision> entry : instantiatedCollision.entrySet()) {
|
||||
if (entry.getValue().equals(collision)) {
|
||||
collision = entry.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return collision;
|
||||
} catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unless some of the low IDs are changed, which is unlikely, the first item should always be empty collision
|
||||
if (collisionIndex == 0) {
|
||||
if (instantiatedCollision.containsKey(EmptyCollision.class)) {
|
||||
return instantiatedCollision.get(EmptyCollision.class);
|
||||
} else {
|
||||
return new EmptyCollision(params);
|
||||
}
|
||||
}
|
||||
|
||||
// Unless some of the low IDs are changed, which is unlikely, the second item should always be full collision
|
||||
if (collisionIndex == 1) {
|
||||
if (instantiatedCollision.containsKey(SolidCollision.class)) {
|
||||
return instantiatedCollision.get(SolidCollision.class);
|
||||
} else {
|
||||
return new SolidCollision(params);
|
||||
}
|
||||
}
|
||||
|
||||
BlockCollision collision = new OtherCollision((ArrayNode) collisionList.get(collisionIndex));
|
||||
// If there's an existing instance equal to this one, use that instead
|
||||
for (Map.Entry<Class<?>, BlockCollision> entry : instantiatedCollision.entrySet()) {
|
||||
if (entry.getValue().equals(collision)) {
|
||||
collision = entry.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return collision;
|
||||
}
|
||||
|
||||
// Note: these reuse classes, so don't try to store more than once instance or coordinates will get overwritten
|
||||
|
||||
public static BlockCollision getCollision(int blockID, int x, int y, int z) {
|
||||
BlockCollision collision = COLLISION_MAP.get(blockID);
|
||||
if (collision != null) {
|
||||
collision.setPosition(x, y, z);
|
||||
}
|
||||
return collision;
|
||||
}
|
||||
|
||||
|
||||
public static BlockCollision getCollisionAt(GeyserSession session, int x, int y, int z) {
|
||||
try {
|
||||
return getCollision(session.getConnector().getWorldManager().getBlockAt(session, x, y, z), x, y, z);
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
// Block out of world
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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.collision.translators;
|
||||
|
||||
import com.nukkitx.math.vector.Vector3d;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.collision.CollisionManager;
|
||||
import org.geysermc.connector.network.translators.collision.BoundingBox;
|
||||
|
||||
@EqualsAndHashCode
|
||||
public class BlockCollision {
|
||||
|
||||
@Getter
|
||||
protected BoundingBox[] boundingBoxes;
|
||||
|
||||
protected int x;
|
||||
protected int y;
|
||||
protected int z;
|
||||
|
||||
/**
|
||||
* This is used for the step up logic.
|
||||
* Usually, the player can only step up a block if they are on the same Y level as its bottom face or higher
|
||||
* For snow layers, due to its beforeCorrectPosition method the player can be slightly below (0.125 blocks) and
|
||||
* still need to step up
|
||||
* This used to be 0 but for now this has been set to 1 as it fixes bed collision
|
||||
* I didn't just set it for beds because other collision may also be slightly raised off the ground.
|
||||
* If this causes any problems, change this back to 0 and add an exception for beds.
|
||||
*/
|
||||
@EqualsAndHashCode.Exclude
|
||||
protected double pushUpTolerance = 1;
|
||||
|
||||
public void setPosition(int x, int y, int z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden in classes like SnowCollision and GrassPathCollision when correction code needs to be run before the
|
||||
* main correction
|
||||
*/
|
||||
public void beforeCorrectPosition(BoundingBox playerCollision) {}
|
||||
|
||||
/**
|
||||
* Returns false if the movement is invalid, and in this case it shouldn't be sent to the server and should be
|
||||
* cancelled
|
||||
* While the Java server should do this, it could result in false flags by anticheat
|
||||
* This functionality is currently only used in 6 or 7 layer snow
|
||||
*/
|
||||
public boolean correctPosition(GeyserSession session, BoundingBox playerCollision) {
|
||||
double playerMinY = playerCollision.getMiddleY() - (playerCollision.getSizeY() / 2);
|
||||
for (BoundingBox b : this.boundingBoxes) {
|
||||
double boxMinY = (b.getMiddleY() + y) - (b.getSizeY() / 2);
|
||||
double boxMaxY = (b.getMiddleY() + y) + (b.getSizeY() / 2);
|
||||
if (b.checkIntersection(x, y, z, playerCollision) && (playerMinY + pushUpTolerance) >= boxMinY) {
|
||||
// Max steppable distance in Minecraft as far as we know is 0.5625 blocks (for beds)
|
||||
if (boxMaxY - playerMinY <= 0.5625) {
|
||||
playerCollision.translate(0, boxMaxY - playerMinY, 0);
|
||||
// Update player Y for next collision box
|
||||
playerMinY = playerCollision.getMiddleY() - (playerCollision.getSizeY() / 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Make player collision slightly bigger to pick up on blocks that could cause problems with Passable
|
||||
playerCollision.setSizeX(playerCollision.getSizeX() + CollisionManager.COLLISION_TOLERANCE * 2);
|
||||
playerCollision.setSizeZ(playerCollision.getSizeZ() + CollisionManager.COLLISION_TOLERANCE * 2);
|
||||
|
||||
// If the player still intersects the block, then push them out
|
||||
// This fixes NoCheatPlus's Passable check
|
||||
// This check doesn't allow players right up against the block, so they must be pushed slightly away
|
||||
if (b.checkIntersection(x, y, z, playerCollision)) {
|
||||
Vector3d relativePlayerPosition = Vector3d.from(playerCollision.getMiddleX() - x,
|
||||
playerCollision.getMiddleY() - (playerCollision.getSizeY() / 2) - y,
|
||||
playerCollision.getMiddleZ() - z);
|
||||
|
||||
Vector3d northFacePos = Vector3d.from(b.getMiddleX(),
|
||||
b.getMiddleY(),
|
||||
b.getMiddleZ() - (b.getSizeZ() / 2));
|
||||
|
||||
Vector3d southFacePos = Vector3d.from(b.getMiddleX(),
|
||||
b.getMiddleY(),
|
||||
b.getMiddleZ() + (b.getSizeZ() / 2));
|
||||
|
||||
Vector3d eastFacePos = Vector3d.from(b.getMiddleX() + (b.getSizeX() / 2),
|
||||
b.getMiddleY(),
|
||||
b.getMiddleZ());
|
||||
|
||||
Vector3d westFacePos = Vector3d.from(b.getMiddleX() - (b.getSizeX() / 2),
|
||||
b.getMiddleY(),
|
||||
b.getMiddleZ());
|
||||
|
||||
double translateDistance = northFacePos.getZ() - relativePlayerPosition.getZ() - (playerCollision.getSizeZ() / 2);
|
||||
if (Math.abs(translateDistance) < CollisionManager.COLLISION_TOLERANCE * 1.1) {
|
||||
playerCollision.translate(0, 0, translateDistance);
|
||||
}
|
||||
|
||||
translateDistance = southFacePos.getZ() - relativePlayerPosition.getZ() + (playerCollision.getSizeZ() / 2);
|
||||
if (Math.abs(translateDistance) < CollisionManager.COLLISION_TOLERANCE * 1.1) {
|
||||
playerCollision.translate(0, 0, translateDistance);
|
||||
}
|
||||
|
||||
translateDistance = eastFacePos.getX() - relativePlayerPosition.getX() + (playerCollision.getSizeX() / 2);
|
||||
if (Math.abs(translateDistance) < CollisionManager.COLLISION_TOLERANCE * 1.1) {
|
||||
playerCollision.translate(translateDistance, 0, 0);
|
||||
}
|
||||
|
||||
translateDistance = westFacePos.getX() - relativePlayerPosition.getX() - (playerCollision.getSizeX() / 2);
|
||||
if (Math.abs(translateDistance) < CollisionManager.COLLISION_TOLERANCE * 1.1) {
|
||||
playerCollision.translate(translateDistance, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the collision size back to normal
|
||||
playerCollision.setSizeX(0.6);
|
||||
playerCollision.setSizeZ(0.6);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean checkIntersection(BoundingBox playerCollision) {
|
||||
for (BoundingBox b : boundingBoxes) {
|
||||
if (b.checkIntersection(x, y, z, playerCollision)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.collision.translators;
|
||||
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.collision.BoundingBox;
|
||||
import org.geysermc.connector.network.translators.collision.CollisionRemapper;
|
||||
|
||||
@CollisionRemapper(regex = "_door$", usesParams = true, passDefaultBoxes = true)
|
||||
public class DoorCollision extends BlockCollision {
|
||||
/**
|
||||
* 1 = north
|
||||
* 2 = east
|
||||
* 3 = south
|
||||
* 4 = west
|
||||
*/
|
||||
private int facing;
|
||||
|
||||
public DoorCollision(String params, BoundingBox[] defaultBoxes) {
|
||||
super();
|
||||
boundingBoxes = defaultBoxes;
|
||||
if (params.contains("facing=north")) {
|
||||
facing = 1;
|
||||
} else if (params.contains("facing=east")) {
|
||||
facing = 2;
|
||||
} else if (params.contains("facing=south")) {
|
||||
facing = 3;
|
||||
} else if (params.contains("facing=west")) {
|
||||
facing = 4;
|
||||
}
|
||||
|
||||
// If the door is open it changes direction
|
||||
if (params.contains("open=true")) {
|
||||
facing = facing % 2 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean correctPosition(GeyserSession session, BoundingBox playerCollision) {
|
||||
boolean result = super.correctPosition(session, playerCollision);
|
||||
// Hack to prevent false positives
|
||||
playerCollision.setSizeX(playerCollision.getSizeX() - 0.0001);
|
||||
playerCollision.setSizeY(playerCollision.getSizeY() - 0.0001);
|
||||
playerCollision.setSizeZ(playerCollision.getSizeZ() - 0.0001);
|
||||
|
||||
// Check for door bug (doors are 0.1875 blocks thick on Java but 0.1825 blocks thick on Bedrock)
|
||||
if (this.checkIntersection(playerCollision)) {
|
||||
switch (facing) {
|
||||
case 1: // North
|
||||
playerCollision.setMiddleZ(Math.floor(playerCollision.getMiddleZ()) + 0.5125);
|
||||
break;
|
||||
case 2: // East
|
||||
playerCollision.setMiddleX(Math.floor(playerCollision.getMiddleX()) + 0.5125);
|
||||
break;
|
||||
case 3: // South
|
||||
playerCollision.setMiddleZ(Math.floor(playerCollision.getMiddleZ()) + 0.4875);
|
||||
break;
|
||||
case 4: // West
|
||||
playerCollision.setMiddleX(Math.floor(playerCollision.getMiddleX()) + 0.4875);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
playerCollision.setSizeX(playerCollision.getSizeX() + 0.0001);
|
||||
playerCollision.setSizeY(playerCollision.getSizeY() + 0.0001);
|
||||
playerCollision.setSizeZ(playerCollision.getSizeZ() + 0.0001);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.collision.translators;
|
||||
|
||||
import org.geysermc.connector.network.translators.collision.BoundingBox;
|
||||
|
||||
public class EmptyCollision extends BlockCollision {
|
||||
public EmptyCollision(String params) {
|
||||
super();
|
||||
boundingBoxes = new BoundingBox[0];
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.collision.translators;
|
||||
|
||||
import org.geysermc.connector.network.translators.collision.BoundingBox;
|
||||
import org.geysermc.connector.network.translators.collision.CollisionRemapper;
|
||||
|
||||
@CollisionRemapper(regex = "^grass_path$", passDefaultBoxes = true)
|
||||
public class GrassPathCollision extends BlockCollision {
|
||||
public GrassPathCollision(String params, BoundingBox[] defaultBoxes) {
|
||||
super();
|
||||
boundingBoxes = defaultBoxes;
|
||||
}
|
||||
|
||||
// Needs to run before the main correction code or it can move the player into blocks
|
||||
// This is counteracted by the main collision code pushing them out
|
||||
@Override
|
||||
public void beforeCorrectPosition(BoundingBox playerCollision) {
|
||||
// In Bedrock, grass paths are small blocks so the player must be pushed down
|
||||
double playerMinY = playerCollision.getMiddleY() - (playerCollision.getSizeY() / 2);
|
||||
// If the player is in the buggy area, push them down
|
||||
if (playerMinY == y + 1) {
|
||||
playerCollision.translate(0, -0.0625, 0);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.collision.translators;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import org.geysermc.connector.network.translators.collision.BoundingBox;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class OtherCollision extends BlockCollision {
|
||||
|
||||
public OtherCollision(ArrayNode collisionList) {
|
||||
super();
|
||||
boundingBoxes = new BoundingBox[collisionList.size()];
|
||||
|
||||
for (int i = 0; i < collisionList.size(); i++) {
|
||||
ArrayNode collisionBoxArray = (ArrayNode) collisionList.get(i);
|
||||
boundingBoxes[i] = new BoundingBox(collisionBoxArray.get(0).asDouble(),
|
||||
collisionBoxArray.get(1).asDouble(),
|
||||
collisionBoxArray.get(2).asDouble(),
|
||||
collisionBoxArray.get(3).asDouble(),
|
||||
collisionBoxArray.get(4).asDouble(),
|
||||
collisionBoxArray.get(5).asDouble());
|
||||
}
|
||||
|
||||
// Sorting by lowest Y first fixes some bugs
|
||||
Arrays.sort(boundingBoxes, Comparator.comparingDouble(BoundingBox::getMiddleY));
|
||||
}
|
||||
}
|
@ -0,0 +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.collision.translators;
|
||||
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.collision.BoundingBox;
|
||||
import org.geysermc.connector.network.translators.collision.CollisionRemapper;
|
||||
|
||||
/**
|
||||
* In order for scaffolding to work on Bedrock, entity flags need to be sent to the player
|
||||
*/
|
||||
@CollisionRemapper(regex = "^scaffolding$", usesParams = true, passDefaultBoxes = true)
|
||||
public class ScaffoldingCollision extends BlockCollision {
|
||||
public ScaffoldingCollision(String params, BoundingBox[] defaultBoxes) {
|
||||
super();
|
||||
boundingBoxes = defaultBoxes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean correctPosition(GeyserSession session, BoundingBox playerCollision) {
|
||||
// Hack to not check below the player
|
||||
playerCollision.setSizeY(playerCollision.getSizeY() - 0.001);
|
||||
playerCollision.setMiddleY(playerCollision.getMiddleY() + 0.002);
|
||||
|
||||
boolean intersected = this.checkIntersection(playerCollision);
|
||||
|
||||
playerCollision.setSizeY(playerCollision.getSizeY() + 0.001);
|
||||
playerCollision.setMiddleY(playerCollision.getMiddleY() - 0.002);
|
||||
|
||||
if (intersected) {
|
||||
session.getCollisionManager().setTouchingScaffolding(true);
|
||||
session.getCollisionManager().setOnScaffolding(true);
|
||||
} else {
|
||||
// Hack to check slightly below the player
|
||||
playerCollision.setSizeY(playerCollision.getSizeY() + 0.001);
|
||||
playerCollision.setMiddleY(playerCollision.getMiddleY() - 0.002);
|
||||
|
||||
if (this.checkIntersection(playerCollision)) {
|
||||
session.getCollisionManager().setOnScaffolding(true);
|
||||
}
|
||||
|
||||
playerCollision.setSizeY(playerCollision.getSizeY() - 0.001);
|
||||
playerCollision.setMiddleY(playerCollision.getMiddleY() + 0.002);
|
||||
}
|
||||
|
||||
// Normal move correction isn't really needed for scaffolding
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.collision.translators;
|
||||
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.collision.BoundingBox;
|
||||
import org.geysermc.connector.network.translators.collision.CollisionRemapper;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
@CollisionRemapper(regex = "^snow$", usesParams = true)
|
||||
public class SnowCollision extends BlockCollision {
|
||||
private final int layers;
|
||||
|
||||
public SnowCollision(String params) {
|
||||
super();
|
||||
Pattern layersPattern = Pattern.compile("layers=([0-8])");
|
||||
Matcher matcher = layersPattern.matcher(params);
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
matcher.find();
|
||||
|
||||
// Hitbox is 1 layer less (you sink in 1 layer)
|
||||
layers = Integer.parseInt(matcher.group(1));
|
||||
|
||||
if (layers > 1) {
|
||||
boundingBoxes = new BoundingBox[] {
|
||||
// Take away 1 because you can go 1 layer into snow layers
|
||||
new BoundingBox(0.5, ((layers - 1) * 0.125) / 2, 0.5,
|
||||
1, (layers - 1) * 0.125, 1)
|
||||
};
|
||||
} else {
|
||||
// Single layers have no collision
|
||||
boundingBoxes = new BoundingBox[0];
|
||||
}
|
||||
|
||||
pushUpTolerance = 0.125;
|
||||
}
|
||||
|
||||
// Needs to run before the main correction code or it can move the player into blocks
|
||||
// This is counteracted by the main collision code pushing them out
|
||||
@Override
|
||||
public void beforeCorrectPosition(BoundingBox playerCollision) {
|
||||
// In Bedrock, snow layers round down to half blocks but you can't sink into them at all
|
||||
// This means the collision each half block reaches above where it should be on Java so the player has to be
|
||||
// pushed down
|
||||
if (layers == 4 || layers == 8) {
|
||||
double playerMinY = playerCollision.getMiddleY() - (playerCollision.getSizeY() / 2);
|
||||
double boxMaxY = (boundingBoxes[0].getMiddleY() + y) + (boundingBoxes[0].getSizeY() / 2);
|
||||
// If the player is in the buggy area, push them down
|
||||
if (playerMinY > boxMaxY &&
|
||||
playerMinY <= (boxMaxY + 0.125)) {
|
||||
playerCollision.translate(0, boxMaxY - playerMinY, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean correctPosition(GeyserSession session, BoundingBox playerCollision) {
|
||||
// Hack to prevent false positives
|
||||
playerCollision.setSizeX(playerCollision.getSizeX() - 0.0001);
|
||||
playerCollision.setSizeY(playerCollision.getSizeY() - 0.0001);
|
||||
playerCollision.setSizeZ(playerCollision.getSizeZ() - 0.0001);
|
||||
|
||||
if (this.checkIntersection(playerCollision)) {
|
||||
double playerMinY = playerCollision.getMiddleY() - (playerCollision.getSizeY() / 2);
|
||||
double boxMaxY = (boundingBoxes[0].getMiddleY() + y) + (boundingBoxes[0].getSizeY() / 2);
|
||||
// If the player actually can't step onto it (they can step onto it from other snow layers)
|
||||
if ((boxMaxY - playerMinY) > 0.5) {
|
||||
// Cancel the movement
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
playerCollision.setSizeX(playerCollision.getSizeX() + 0.0001);
|
||||
playerCollision.setSizeY(playerCollision.getSizeY() + 0.0001);
|
||||
playerCollision.setSizeZ(playerCollision.getSizeZ() + 0.0001);
|
||||
return super.correctPosition(session, playerCollision);
|
||||
}
|
||||
}
|
@ -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.network.translators.collision.translators;
|
||||
|
||||
import org.geysermc.connector.network.translators.collision.BoundingBox;
|
||||
import org.geysermc.connector.network.translators.collision.CollisionRemapper;
|
||||
|
||||
@CollisionRemapper(regex = "shulker_box$") // These have no collision in the mappings as it depends on the NBT data
|
||||
public class SolidCollision extends BlockCollision {
|
||||
public SolidCollision(String params) {
|
||||
super();
|
||||
boundingBoxes = new BoundingBox[]{
|
||||
new BoundingBox(0.5, 0.5, 0.5, 1, 1, 1)
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.collision.translators;
|
||||
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.collision.BoundingBox;
|
||||
import org.geysermc.connector.network.translators.collision.CollisionRemapper;
|
||||
|
||||
@CollisionRemapper(regex = "_trapdoor$", usesParams = true, passDefaultBoxes = true)
|
||||
public class TrapdoorCollision extends BlockCollision {
|
||||
/**
|
||||
* 1 = north
|
||||
* 2 = east
|
||||
* 3 = south
|
||||
* 4 = west
|
||||
* 5 = up
|
||||
* 6 = down
|
||||
*/
|
||||
private int facing;
|
||||
|
||||
public TrapdoorCollision(String params, BoundingBox[] defaultBoxes) {
|
||||
super();
|
||||
boundingBoxes = defaultBoxes;
|
||||
if (params.contains("open=true")) {
|
||||
if (params.contains("facing=north")) {
|
||||
facing = 1;
|
||||
} else if (params.contains("facing=east")) {
|
||||
facing = 2;
|
||||
} else if (params.contains("facing=south")) {
|
||||
facing = 3;
|
||||
} else if (params.contains("facing=west")) {
|
||||
facing = 4;
|
||||
}
|
||||
} else {
|
||||
if (params.contains("half=bottom")) {
|
||||
// Up
|
||||
facing = 5;
|
||||
} else {
|
||||
// Down
|
||||
facing = 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean correctPosition(GeyserSession session, BoundingBox playerCollision) {
|
||||
boolean result = super.correctPosition(session, playerCollision);
|
||||
// Hack to prevent false positives
|
||||
playerCollision.setSizeX(playerCollision.getSizeX() - 0.0001);
|
||||
playerCollision.setSizeY(playerCollision.getSizeY() - 0.0001);
|
||||
playerCollision.setSizeZ(playerCollision.getSizeZ() - 0.0001);
|
||||
|
||||
// Check for door bug (doors are 0.1875 blocks thick on Java but 0.1825 blocks thick on Bedrock)
|
||||
if (this.checkIntersection(playerCollision)) {
|
||||
switch (facing) {
|
||||
case 1: // North
|
||||
playerCollision.setMiddleZ(Math.floor(playerCollision.getMiddleZ()) + 0.5125);
|
||||
break;
|
||||
case 3: // South
|
||||
playerCollision.setMiddleZ(Math.floor(playerCollision.getMiddleZ()) + 0.4875);
|
||||
break;
|
||||
case 4: // West
|
||||
playerCollision.setMiddleX(Math.floor(playerCollision.getMiddleX()) + 0.4875);
|
||||
break;
|
||||
case 6: // Down
|
||||
playerCollision.setMiddleY(Math.floor(
|
||||
playerCollision.getMiddleY() - (playerCollision.getSizeY() / 2)
|
||||
) + 0.0125 + (playerCollision.getSizeY() / 2));
|
||||
break;
|
||||
case 2:
|
||||
case 5:
|
||||
// Up-facing and east-facing trapdoors work fine
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
playerCollision.setSizeX(playerCollision.getSizeX() + 0.0001);
|
||||
playerCollision.setSizeY(playerCollision.getSizeY() + 0.0001);
|
||||
playerCollision.setSizeZ(playerCollision.getSizeZ() + 0.0001);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -47,9 +47,7 @@ import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||
import org.geysermc.connector.utils.InventoryUtils;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A temporary reconstruction of the enchantment table UI until our inventory rewrite is complete.
|
||||
@ -100,11 +98,11 @@ public class EnchantmentInventoryTranslator extends BlockInventoryTranslator {
|
||||
@Override
|
||||
public void updateInventory(GeyserSession session, Inventory inventory) {
|
||||
super.updateInventory(session, inventory);
|
||||
ItemData[] items = new ItemData[5];
|
||||
items[0] = ItemTranslator.translateToBedrock(session, inventory.getItem(0));
|
||||
items[1] = ItemTranslator.translateToBedrock(session, inventory.getItem(1));
|
||||
List<ItemData> items = new ArrayList<>(5);
|
||||
items.add(ItemTranslator.translateToBedrock(session, inventory.getItem(0)));
|
||||
items.add(ItemTranslator.translateToBedrock(session, inventory.getItem(1)));
|
||||
for (int i = 0; i < 3; i++) {
|
||||
items[i + 2] = session.getEnchantmentSlotData()[i].getItem() != null ? session.getEnchantmentSlotData()[i].getItem() : createEnchantmentBook();
|
||||
items.add(session.getEnchantmentSlotData()[i].getItem() != null ? session.getEnchantmentSlotData()[i].getItem() : createEnchantmentBook());
|
||||
}
|
||||
|
||||
InventoryContentPacket contentPacket = new InventoryContentPacket();
|
||||
|
@ -27,7 +27,6 @@ package org.geysermc.connector.network.translators.inventory;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.InventorySource;
|
||||
import org.geysermc.connector.inventory.Inventory;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
|
||||
|
@ -41,6 +41,8 @@ import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||
import org.geysermc.connector.utils.InventoryUtils;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class PlayerInventoryTranslator extends InventoryTranslator {
|
||||
@ -65,7 +67,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
|
||||
for (int i = 36; i < 45; i++) {
|
||||
contents[i - 36] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
|
||||
}
|
||||
inventoryContentPacket.setContents(contents);
|
||||
inventoryContentPacket.setContents(Arrays.asList(contents));
|
||||
session.sendUpstreamPacket(inventoryContentPacket);
|
||||
|
||||
// Armor
|
||||
@ -75,13 +77,13 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
|
||||
for (int i = 5; i < 9; i++) {
|
||||
contents[i - 5] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
|
||||
}
|
||||
armorContentPacket.setContents(contents);
|
||||
armorContentPacket.setContents(Arrays.asList(contents));
|
||||
session.sendUpstreamPacket(armorContentPacket);
|
||||
|
||||
// Offhand
|
||||
InventoryContentPacket offhandPacket = new InventoryContentPacket();
|
||||
offhandPacket.setContainerId(ContainerId.OFFHAND);
|
||||
offhandPacket.setContents(new ItemData[]{ItemTranslator.translateToBedrock(session, inventory.getItem(45))});
|
||||
offhandPacket.setContents(Collections.singletonList(ItemTranslator.translateToBedrock(session, inventory.getItem(45))));
|
||||
session.sendUpstreamPacket(offhandPacket);
|
||||
}
|
||||
|
||||
@ -130,7 +132,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
|
||||
} else if (slot == 45) {
|
||||
InventoryContentPacket offhandPacket = new InventoryContentPacket();
|
||||
offhandPacket.setContainerId(ContainerId.OFFHAND);
|
||||
offhandPacket.setContents(new ItemData[]{ItemTranslator.translateToBedrock(session, inventory.getItem(slot))});
|
||||
offhandPacket.setContents(Collections.singletonList(ItemTranslator.translateToBedrock(session, inventory.getItem(slot))));
|
||||
session.sendUpstreamPacket(offhandPacket);
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,9 @@ import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||
import org.geysermc.connector.utils.InventoryUtils;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class ChestInventoryUpdater extends InventoryUpdater {
|
||||
private static final ItemData UNUSUABLE_SPACE_BLOCK = InventoryUtils.createUnusableSpaceBlock(LanguageUtils.getLocaleStringLog("geyser.inventory.unusable_item.slot"));
|
||||
@ -46,12 +49,12 @@ public class ChestInventoryUpdater extends InventoryUpdater {
|
||||
public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
||||
super.updateInventory(translator, session, inventory);
|
||||
|
||||
ItemData[] bedrockItems = new ItemData[paddedSize];
|
||||
for (int i = 0; i < bedrockItems.length; i++) {
|
||||
List<ItemData> bedrockItems = new ArrayList<>(paddedSize);
|
||||
for (int i = 0; i < paddedSize; i++) {
|
||||
if (i < translator.size) {
|
||||
bedrockItems[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
|
||||
bedrockItems.add(ItemTranslator.translateToBedrock(session, inventory.getItem(i)));
|
||||
} else {
|
||||
bedrockItems[i] = UNUSUABLE_SPACE_BLOCK;
|
||||
bedrockItems.add(UNUSUABLE_SPACE_BLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,8 @@ import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ContainerInventoryUpdater extends InventoryUpdater {
|
||||
@Override
|
||||
public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
||||
@ -45,7 +47,7 @@ public class ContainerInventoryUpdater extends InventoryUpdater {
|
||||
|
||||
InventoryContentPacket contentPacket = new InventoryContentPacket();
|
||||
contentPacket.setContainerId(inventory.getId());
|
||||
contentPacket.setContents(bedrockItems);
|
||||
contentPacket.setContents(Arrays.asList(bedrockItems));
|
||||
session.sendUpstreamPacket(contentPacket);
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,8 @@ import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public abstract class InventoryUpdater {
|
||||
public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
||||
ItemData[] bedrockItems = new ItemData[36];
|
||||
@ -43,7 +45,7 @@ public abstract class InventoryUpdater {
|
||||
}
|
||||
InventoryContentPacket contentPacket = new InventoryContentPacket();
|
||||
contentPacket.setContainerId(ContainerId.INVENTORY);
|
||||
contentPacket.setContents(bedrockItems);
|
||||
contentPacket.setContents(Arrays.asList(bedrockItems));
|
||||
session.sendUpstreamPacket(contentPacket);
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,12 @@ public class ItemRegistry {
|
||||
|
||||
private static final Map<String, ItemEntry> JAVA_IDENTIFIER_MAP = new HashMap<>();
|
||||
|
||||
/**
|
||||
* A list of all identifiers that only exist on Java. Used to prevent creative items from becoming these unintentionally.
|
||||
*/
|
||||
private static final List<String> JAVA_ONLY_ITEMS = Arrays.asList("minecraft:spectral_arrow", "minecraft:debug_stick",
|
||||
"minecraft:knowledge_book");
|
||||
|
||||
public static final ItemData[] CREATIVE_ITEMS;
|
||||
|
||||
public static final List<StartGamePacket.ItemEntry> ITEMS = new ArrayList<>();
|
||||
@ -245,7 +251,10 @@ public class ItemRegistry {
|
||||
if (itemEntry.getBedrockId() == data.getId() && (itemEntry.getBedrockData() == data.getDamage() ||
|
||||
// Make exceptions for potions and tipped arrows, whose damage values can vary
|
||||
(itemEntry.getJavaIdentifier().endsWith("potion") || itemEntry.getJavaIdentifier().equals("minecraft:arrow")))) {
|
||||
return itemEntry;
|
||||
if (!JAVA_ONLY_ITEMS.contains(itemEntry.getJavaIdentifier())) {
|
||||
// From a Bedrock item data, we aren't getting one of these items
|
||||
return itemEntry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,6 +111,10 @@ public abstract class ItemTranslator {
|
||||
translator.translateToJava(itemStack.getNbt(), javaItem);
|
||||
}
|
||||
}
|
||||
if (itemStack.getNbt().isEmpty()) {
|
||||
// Otherwise, seems to causes issues with villagers accepting books, and I don't see how this will break anything else. - Camotoy
|
||||
itemStack = new ItemStack(itemStack.getId(), itemStack.getAmount(), null);
|
||||
}
|
||||
}
|
||||
return itemStack;
|
||||
}
|
||||
@ -126,7 +130,7 @@ public abstract class ItemTranslator {
|
||||
|
||||
// This is a fallback for maps with no nbt
|
||||
if (nbt == null && bedrockItem.getJavaIdentifier().equals("minecraft:filled_map")) {
|
||||
nbt = new com.github.steveice10.opennbt.tag.builtin.CompoundTag("");
|
||||
nbt = new CompoundTag("");
|
||||
nbt.put(new IntTag("map", 0));
|
||||
}
|
||||
|
||||
@ -218,7 +222,7 @@ public abstract class ItemTranslator {
|
||||
NbtMapBuilder builder = NbtMap.builder();
|
||||
if (tag.getValue() != null && !tag.getValue().isEmpty()) {
|
||||
for (String str : tag.getValue().keySet()) {
|
||||
com.github.steveice10.opennbt.tag.builtin.Tag javaTag = tag.get(str);
|
||||
Tag javaTag = tag.get(str);
|
||||
Object translatedTag = translateToBedrockNBT(javaTag);
|
||||
if (translatedTag == null)
|
||||
continue;
|
||||
@ -287,21 +291,21 @@ public abstract class ItemTranslator {
|
||||
return new NbtList(type, 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;
|
||||
if (tag instanceof CompoundTag) {
|
||||
CompoundTag compoundTag = (CompoundTag) tag;
|
||||
return translateNbtToBedrock(compoundTag);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public com.github.steveice10.opennbt.tag.builtin.CompoundTag translateToJavaNBT(String name, NbtMap tag) {
|
||||
com.github.steveice10.opennbt.tag.builtin.CompoundTag javaTag = new com.github.steveice10.opennbt.tag.builtin.CompoundTag(name);
|
||||
Map<String, com.github.steveice10.opennbt.tag.builtin.Tag> javaValue = javaTag.getValue();
|
||||
public CompoundTag translateToJavaNBT(String name, NbtMap tag) {
|
||||
CompoundTag javaTag = new CompoundTag(name);
|
||||
Map<String, Tag> javaValue = javaTag.getValue();
|
||||
if (tag != null && !tag.isEmpty()) {
|
||||
for (String str : tag.keySet()) {
|
||||
Object bedrockTag = tag.get(str);
|
||||
com.github.steveice10.opennbt.tag.builtin.Tag translatedTag = translateToJavaNBT(str, bedrockTag);
|
||||
Tag translatedTag = translateToJavaNBT(str, bedrockTag);
|
||||
if (translatedTag == null)
|
||||
continue;
|
||||
|
||||
@ -313,7 +317,7 @@ public abstract class ItemTranslator {
|
||||
return javaTag;
|
||||
}
|
||||
|
||||
private com.github.steveice10.opennbt.tag.builtin.Tag translateToJavaNBT(String name, Object object) {
|
||||
private Tag translateToJavaNBT(String name, Object object) {
|
||||
if (object instanceof int[]) {
|
||||
return new IntArrayTag(name, (int[]) object);
|
||||
}
|
||||
@ -355,10 +359,10 @@ public abstract class ItemTranslator {
|
||||
}
|
||||
|
||||
if (object instanceof List) {
|
||||
List<com.github.steveice10.opennbt.tag.builtin.Tag> tags = new ArrayList<>();
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
|
||||
for (Object value : (List<?>) object) {
|
||||
com.github.steveice10.opennbt.tag.builtin.Tag javaTag = translateToJavaNBT("", value);
|
||||
Tag javaTag = translateToJavaNBT("", value);
|
||||
if (javaTag != null)
|
||||
tags.add(javaTag);
|
||||
}
|
||||
|
@ -41,6 +41,12 @@ import java.util.*;
|
||||
*/
|
||||
public class RecipeRegistry {
|
||||
|
||||
/**
|
||||
* Stores the last used recipe network ID. Since 1.16.200 (and for server-authoritative inventories),
|
||||
* each recipe needs a unique network ID (or else in .200 the client crashes).
|
||||
*/
|
||||
public static int LAST_RECIPE_NET_ID = 0;
|
||||
|
||||
/**
|
||||
* A list of all possible leather armor dyeing recipes.
|
||||
* Created manually.
|
||||
@ -72,20 +78,36 @@ public class RecipeRegistry {
|
||||
*/
|
||||
public static final List<CraftingData> TIPPED_ARROW_RECIPES = new ObjectArrayList<>();
|
||||
|
||||
// TODO: These are the other "multi" UUIDs that supposedly enable various recipes. Find out what each enables.
|
||||
// 442d85ed-8272-4543-a6f1-418f90ded05d 8b36268c-1829-483c-a0f1-993b7156a8f2 602234e4-cac1-4353-8bb7-b1ebff70024b 98c84b38-1085-46bd-b1ce-dd38c159e6cc
|
||||
// d81aaeaf-e172-4440-9225-868df030d27b b5c5d105-75a2-4076-af2b-923ea2bf4bf0 00000000-0000-0000-0000-000000000002 85939755-ba10-4d9d-a4cc-efb7a8e943c4
|
||||
// d392b075-4ba1-40ae-8789-af868d56f6ce aecd2294-4b94-434b-8667-4499bb2c9327
|
||||
/**
|
||||
* Recipe data that, when sent to the client, enables book cloning
|
||||
*/
|
||||
public static final CraftingData BOOK_CLONING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("d1ca6b84-338e-4f2f-9c6b-76cc8b4bd98d"));
|
||||
public static final CraftingData BOOK_CLONING_RECIPE_DATA;
|
||||
/**
|
||||
* Recipe data that, when sent to the client, enables tool repairing in a crafting table
|
||||
*/
|
||||
public static final CraftingData TOOL_REPAIRING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("00000000-0000-0000-0000-000000000001"));
|
||||
public static final CraftingData TOOL_REPAIRING_RECIPE_DATA;
|
||||
/**
|
||||
* Recipe data that, when sent to the client, enables map extending in a crafting table
|
||||
*/
|
||||
public static final CraftingData MAP_EXTENDING_RECIPE_DATA;
|
||||
/**
|
||||
* Recipe data that, when sent to the client, enables map cloning in a crafting table
|
||||
*/
|
||||
public static final CraftingData MAP_CLONING_RECIPE_DATA;
|
||||
/**
|
||||
* Recipe data that, when sent to the client, enables banner duplicating
|
||||
*/
|
||||
public static final CraftingData BANNER_DUPLICATING_RECIPE_DATA;
|
||||
|
||||
|
||||
static {
|
||||
BOOK_CLONING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("d1ca6b84-338e-4f2f-9c6b-76cc8b4bd98d"), LAST_RECIPE_NET_ID++);
|
||||
TOOL_REPAIRING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("00000000-0000-0000-0000-000000000001"), LAST_RECIPE_NET_ID++);
|
||||
MAP_EXTENDING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("d392b075-4ba1-40ae-8789-af868d56f6ce"), LAST_RECIPE_NET_ID++);
|
||||
MAP_CLONING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("85939755-ba10-4d9d-a4cc-efb7a8e943c4"), LAST_RECIPE_NET_ID++);
|
||||
BANNER_DUPLICATING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("b5c5d105-75a2-4076-af2b-923ea2bf4bf0"), LAST_RECIPE_NET_ID++);
|
||||
// https://github.com/pmmp/PocketMine-MP/blob/stable/src/pocketmine/inventory/MultiRecipe.php
|
||||
|
||||
// Get all recipes that are not directly sent from a Java server
|
||||
InputStream stream = FileUtils.getResource("mappings/recipes.json");
|
||||
|
||||
@ -142,19 +164,19 @@ public class RecipeRegistry {
|
||||
letterToRecipe.put(entry.getKey(), ItemRegistry.getBedrockItemFromJson(entry.getValue()));
|
||||
}
|
||||
|
||||
ItemData[] inputs = new ItemData[shape.size() * shape.get(0).length()];
|
||||
List<ItemData> inputs = new ArrayList<>(shape.size() * shape.get(0).length());
|
||||
int i = 0;
|
||||
// Create a linear array of items from the "cube" of the shape
|
||||
for (int j = 0; i < shape.size() * shape.get(0).length(); j++) {
|
||||
for (char c : shape.get(j).toCharArray()) {
|
||||
ItemData data = letterToRecipe.getOrDefault(String.valueOf(c), ItemData.AIR);
|
||||
inputs[i] = data;
|
||||
inputs.add(data);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return CraftingData.fromShaped(uuid.toString(), shape.get(0).length(), shape.size(),
|
||||
inputs, new ItemData[]{output}, uuid, "crafting_table", 0);
|
||||
inputs, Collections.singletonList(output), uuid, "crafting_table", 0, LAST_RECIPE_NET_ID++);
|
||||
}
|
||||
List<ItemData> inputs = new ObjectArrayList<>();
|
||||
for (JsonNode entry : node.get("input")) {
|
||||
@ -163,10 +185,10 @@ public class RecipeRegistry {
|
||||
if (node.get("type").asInt() == 5) {
|
||||
// Shulker box
|
||||
return CraftingData.fromShulkerBox(uuid.toString(),
|
||||
inputs.toArray(new ItemData[0]), new ItemData[]{output}, uuid, "crafting_table", 0);
|
||||
inputs, Collections.singletonList(output), uuid, "crafting_table", 0, LAST_RECIPE_NET_ID++);
|
||||
}
|
||||
return CraftingData.fromShapeless(uuid.toString(),
|
||||
inputs.toArray(new ItemData[0]), new ItemData[]{output}, uuid, "crafting_table", 0);
|
||||
inputs, Collections.singletonList(output), uuid, "crafting_table", 0, LAST_RECIPE_NET_ID++);
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
|
@ -0,0 +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.item.translators.nbt;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.ItemRemapper;
|
||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
|
||||
@ItemRemapper
|
||||
public class PlayerHeadTranslator extends NbtItemStackTranslator {
|
||||
|
||||
@Override
|
||||
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
if (!itemTag.contains("display") || !((CompoundTag) itemTag.get("display")).contains("Name")) {
|
||||
if (itemTag.contains("SkullOwner")) {
|
||||
StringTag name;
|
||||
Tag skullOwner = itemTag.get("SkullOwner");
|
||||
if (skullOwner instanceof StringTag) {
|
||||
name = (StringTag) skullOwner;
|
||||
} else {
|
||||
StringTag skullName;
|
||||
if (skullOwner instanceof CompoundTag && (skullName = ((CompoundTag) skullOwner).get("Name")) != null) {
|
||||
name = skullName;
|
||||
} else {
|
||||
session.getConnector().getLogger().debug("Not sure how to handle skull head item display. " + itemTag);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Add correct name of player skull
|
||||
// TODO: It's always yellow, even with a custom name. Handle?
|
||||
String displayName = "\u00a7r\u00a7e" + LocaleUtils.getLocaleString("block.minecraft.player_head.named", session.getLocale()).replace("%s", name.getValue());
|
||||
if (!itemTag.contains("display")) {
|
||||
itemTag.put(new CompoundTag("display"));
|
||||
}
|
||||
((CompoundTag) itemTag.get("display")).put(new StringTag("Name", displayName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(ItemEntry itemEntry) {
|
||||
return itemEntry.getJavaIdentifier().equals("minecraft:player_head");
|
||||
}
|
||||
}
|
@ -57,7 +57,7 @@ public class JavaChatTranslator extends PacketTranslator<ServerChatPacket> {
|
||||
}
|
||||
|
||||
textPacket.setNeedsTranslation(false);
|
||||
textPacket.setMessage(MessageTranslator.convertMessage(packet.getMessage().toString(), session.getLocale()));
|
||||
textPacket.setMessage(MessageTranslator.convertMessage(packet.getMessage(), session.getLocale()));
|
||||
|
||||
session.sendUpstreamPacket(textPacket);
|
||||
}
|
||||
|
@ -51,6 +51,8 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
|
||||
|
||||
@Override
|
||||
public void translate(ServerDeclareRecipesPacket packet, GeyserSession session) {
|
||||
// Get the last known network ID (first used for the pregenerated recipes) and increment from there.
|
||||
int networkId = RecipeRegistry.LAST_RECIPE_NET_ID;
|
||||
CraftingDataPacket craftingDataPacket = new CraftingDataPacket();
|
||||
craftingDataPacket.setCleanRecipes(true);
|
||||
for (Recipe recipe : packet.getRecipes()) {
|
||||
@ -63,7 +65,7 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
|
||||
for (ItemData[] inputs : inputCombinations) {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(),
|
||||
inputs, new ItemData[]{output}, uuid, "crafting_table", 0));
|
||||
Arrays.asList(inputs), Collections.singletonList(output), uuid, "crafting_table", 0, networkId++));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -75,8 +77,8 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
|
||||
for (ItemData[] inputs : inputCombinations) {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
craftingDataPacket.getCraftingData().add(CraftingData.fromShaped(uuid.toString(),
|
||||
shapedRecipeData.getWidth(), shapedRecipeData.getHeight(), inputs,
|
||||
new ItemData[]{output}, uuid, "crafting_table", 0));
|
||||
shapedRecipeData.getWidth(), shapedRecipeData.getHeight(), Arrays.asList(inputs),
|
||||
Collections.singletonList(output), uuid, "crafting_table", 0, networkId++));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -90,6 +92,18 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
|
||||
craftingDataPacket.getCraftingData().add(RecipeRegistry.TOOL_REPAIRING_RECIPE_DATA);
|
||||
break;
|
||||
}
|
||||
case CRAFTING_SPECIAL_MAPCLONING: {
|
||||
craftingDataPacket.getCraftingData().add(RecipeRegistry.MAP_CLONING_RECIPE_DATA);
|
||||
break;
|
||||
}
|
||||
case CRAFTING_SPECIAL_MAPEXTENDING: {
|
||||
craftingDataPacket.getCraftingData().add(RecipeRegistry.MAP_EXTENDING_RECIPE_DATA);
|
||||
break;
|
||||
}
|
||||
case CRAFTING_SPECIAL_BANNERDUPLICATE: {
|
||||
craftingDataPacket.getCraftingData().add(RecipeRegistry.BANNER_DUPLICATING_RECIPE_DATA);
|
||||
break;
|
||||
}
|
||||
|
||||
// Java doesn't actually tell us the recipes so we need to calculate this ahead of time.
|
||||
case CRAFTING_SPECIAL_FIREWORK_ROCKET: {
|
||||
|
@ -36,6 +36,6 @@ public class JavaDisconnectPacket extends PacketTranslator<ServerDisconnectPacke
|
||||
|
||||
@Override
|
||||
public void translate(ServerDisconnectPacket packet, GeyserSession session) {
|
||||
session.disconnect(MessageTranslator.convertMessage(packet.getReason().toString(), session.getLocale()));
|
||||
session.disconnect(MessageTranslator.convertMessage(packet.getReason(), session.getLocale()));
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ import com.nukkitx.protocol.bedrock.data.GameRuleData;
|
||||
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
|
||||
import com.nukkitx.protocol.bedrock.packet.*;
|
||||
import org.geysermc.connector.common.AuthType;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
@ -39,10 +39,9 @@ public class JavaKeepAliveTranslator extends PacketTranslator<ServerKeepAlivePac
|
||||
|
||||
@Override
|
||||
public void translate(ServerKeepAlivePacket packet, GeyserSession session) {
|
||||
session.setLastKeepAliveTimestamp(packet.getPingId());
|
||||
NetworkStackLatencyPacket latencyPacket = new NetworkStackLatencyPacket();
|
||||
latencyPacket.setFromServer(true);
|
||||
latencyPacket.setTimestamp(packet.getPingId());
|
||||
latencyPacket.setTimestamp(packet.getPingId() * 1000);
|
||||
session.sendUpstreamPacket(latencyPacket);
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,6 @@ public class JavaLoginDisconnectTranslator extends PacketTranslator<LoginDisconn
|
||||
@Override
|
||||
public void translate(LoginDisconnectPacket packet, GeyserSession session) {
|
||||
// The client doesn't manually get disconnected so we have to do it ourselves
|
||||
session.disconnect(MessageTranslator.convertMessage(packet.getReason().toString(), session.getLocale()));
|
||||
session.disconnect(MessageTranslator.convertMessage(packet.getReason(), session.getLocale()));
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ public class JavaTitleTranslator extends PacketTranslator<ServerTitlePacket> {
|
||||
if (packet.getTitle() == null) {
|
||||
text = " ";
|
||||
} else {
|
||||
text = MessageTranslator.convertMessage(packet.getTitle().toString(), locale);
|
||||
text = MessageTranslator.convertMessage(packet.getTitle(), locale);
|
||||
}
|
||||
|
||||
switch (packet.getAction()) {
|
||||
|
@ -25,16 +25,14 @@
|
||||
|
||||
package org.geysermc.connector.network.translators.java.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityEffectPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.MobEffectPacket;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.EntityUtils;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityEffectPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.MobEffectPacket;
|
||||
|
||||
@Translator(packet = ServerEntityEffectPacket.class)
|
||||
public class JavaEntityEffectTranslator extends PacketTranslator<ServerEntityEffectPacket> {
|
||||
|
||||
@ -43,7 +41,7 @@ public class JavaEntityEffectTranslator extends PacketTranslator<ServerEntityEff
|
||||
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
|
||||
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
|
||||
entity = session.getPlayerEntity();
|
||||
((PlayerEntity) entity).getEffectCache().addEffect(packet.getEffect(), packet.getAmplifier());
|
||||
session.getEffectCache().addEffect(packet.getEffect(), packet.getAmplifier());
|
||||
}
|
||||
if (entity == null)
|
||||
return;
|
||||
|
@ -25,16 +25,14 @@
|
||||
|
||||
package org.geysermc.connector.network.translators.java.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityRemoveEffectPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.MobEffectPacket;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.EntityUtils;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityRemoveEffectPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.MobEffectPacket;
|
||||
|
||||
@Translator(packet = ServerEntityRemoveEffectPacket.class)
|
||||
public class JavaEntityRemoveEffectTranslator extends PacketTranslator<ServerEntityRemoveEffectPacket> {
|
||||
|
||||
@ -43,7 +41,7 @@ public class JavaEntityRemoveEffectTranslator extends PacketTranslator<ServerEnt
|
||||
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
|
||||
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
|
||||
entity = session.getPlayerEntity();
|
||||
((PlayerEntity) entity).getEffectCache().removeEffect(packet.getEffect());
|
||||
session.getEffectCache().removeEffect(packet.getEffect());
|
||||
}
|
||||
if (entity == null)
|
||||
return;
|
||||
|
@ -61,6 +61,10 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator<ServerEn
|
||||
if (passengerId == session.getPlayerEntity().getEntityId()) {
|
||||
passenger = session.getPlayerEntity();
|
||||
session.setRidingVehicleEntity(entity);
|
||||
// We need to confirm teleports before entering a vehicle, or else we will likely exit right out
|
||||
if (session.getConnector().getConfig().isCacheChunks()) {
|
||||
session.confirmTeleport(passenger.getPosition().sub(0, EntityType.PLAYER.getOffset(), 0).toDouble());
|
||||
}
|
||||
}
|
||||
// Passenger hasn't loaded in and entity link needs to be set later
|
||||
if (passenger == null && passengerId != 0) {
|
||||
@ -82,9 +86,9 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator<ServerEn
|
||||
passenger.getMetadata().put(EntityData.RIDER_MAX_ROTATION, 90f);
|
||||
passenger.getMetadata().put(EntityData.RIDER_MIN_ROTATION, !passengers.isEmpty() ? -90f : 0f);
|
||||
} else {
|
||||
passenger.getMetadata().remove(EntityData.RIDER_ROTATION_LOCKED);
|
||||
passenger.getMetadata().remove(EntityData.RIDER_MAX_ROTATION);
|
||||
passenger.getMetadata().remove(EntityData.RIDER_MIN_ROTATION);
|
||||
passenger.getMetadata().put(EntityData.RIDER_ROTATION_LOCKED, (byte) 0);
|
||||
passenger.getMetadata().put(EntityData.RIDER_MAX_ROTATION, 0f);
|
||||
passenger.getMetadata().put(EntityData.RIDER_MIN_ROTATION, 0f);
|
||||
}
|
||||
|
||||
passenger.updateBedrockMetadata(session);
|
||||
@ -106,6 +110,9 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator<ServerEn
|
||||
linkPacket.setEntityLink(new EntityLinkData(entity.getGeyserId(), passenger.getGeyserId(), EntityLinkData.Type.REMOVE, false));
|
||||
session.sendUpstreamPacket(linkPacket);
|
||||
passengers.remove(passenger.getEntityId());
|
||||
passenger.getMetadata().put(EntityData.RIDER_ROTATION_LOCKED, (byte) 0);
|
||||
passenger.getMetadata().put(EntityData.RIDER_MAX_ROTATION, 0f);
|
||||
passenger.getMetadata().put(EntityData.RIDER_MIN_ROTATION, 0f);
|
||||
|
||||
this.updateOffset(passenger, entity, session, false, false, (packet.getPassengerIds().length > 1));
|
||||
} else {
|
||||
|
@ -26,19 +26,11 @@
|
||||
package org.geysermc.connector.network.translators.java.entity.player;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerAbilitiesPacket;
|
||||
import com.nukkitx.protocol.bedrock.data.AdventureSetting;
|
||||
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
|
||||
import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
||||
import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Translator(packet = ServerPlayerAbilitiesPacket.class)
|
||||
public class JavaPlayerAbilitiesTranslator extends PacketTranslator<ServerPlayerAbilitiesPacket> {
|
||||
|
||||
|
@ -26,11 +26,11 @@
|
||||
package org.geysermc.connector.network.translators.java.entity.player;
|
||||
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.SkinUtils;
|
||||
import org.geysermc.connector.skin.SkinManager;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.PlayerListEntry;
|
||||
import com.github.steveice10.mc.protocol.data.game.PlayerListEntryAction;
|
||||
@ -57,7 +57,8 @@ public class JavaPlayerListEntryTranslator extends PacketTranslator<ServerPlayer
|
||||
if (self) {
|
||||
// Entity is ourself
|
||||
playerEntity = session.getPlayerEntity();
|
||||
SkinUtils.requestAndHandleSkinAndCape(playerEntity, session, skinAndCape ->
|
||||
//TODO: playerEntity.setProfile(entry.getProfile()); seems to help with online mode skins but needs more testing to ensure Floodgate skins aren't overwritten
|
||||
SkinManager.requestAndHandleSkinAndCape(playerEntity, session, skinAndCape ->
|
||||
GeyserConnector.getInstance().getLogger().debug("Loaded Local Bedrock Java Skin Data"));
|
||||
} else {
|
||||
playerEntity = session.getEntityCache().getPlayerEntity(entry.getProfile().getId());
|
||||
@ -81,7 +82,7 @@ public class JavaPlayerListEntryTranslator extends PacketTranslator<ServerPlayer
|
||||
playerEntity.setPlayerList(true);
|
||||
playerEntity.setValid(true);
|
||||
|
||||
PlayerListPacket.Entry playerListEntry = SkinUtils.buildCachedEntry(session, entry.getProfile(), playerEntity.getGeyserId());
|
||||
PlayerListPacket.Entry playerListEntry = SkinManager.buildCachedEntry(session, playerEntity);
|
||||
|
||||
translate.getEntries().add(playerListEntry);
|
||||
break;
|
||||
|
@ -32,7 +32,7 @@ import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.RespawnPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.session.cache.TeleportCache;
|
||||
@ -91,7 +91,8 @@ public class JavaPlayerPositionRotationTranslator extends PacketTranslator<Serve
|
||||
|
||||
// Ignore certain move correction packets for smoother movement
|
||||
// These are never relative
|
||||
if (packet.getRelative().isEmpty()) {
|
||||
// When chunk caching is enabled this isn't needed as we shouldn't get these
|
||||
if (!session.getConnector().getConfig().isCacheChunks() && packet.getRelative().isEmpty()) {
|
||||
double xDis = Math.abs(entity.getPosition().getX() - packet.getX());
|
||||
double yDis = entity.getPosition().getY() - packet.getY();
|
||||
double zDis = Math.abs(entity.getPosition().getZ() - packet.getZ());
|
||||
@ -111,13 +112,16 @@ public class JavaPlayerPositionRotationTranslator extends PacketTranslator<Serve
|
||||
double newZ = packet.getZ() +
|
||||
(packet.getRelative().contains(PositionElement.Z) ? entity.getPosition().getZ() : 0);
|
||||
|
||||
double newPitch = packet.getPitch() +
|
||||
float newPitch = packet.getPitch() +
|
||||
(packet.getRelative().contains(PositionElement.PITCH) ? entity.getBedrockRotation().getX() : 0);
|
||||
double newYaw = packet.getYaw() +
|
||||
float newYaw = packet.getYaw() +
|
||||
(packet.getRelative().contains(PositionElement.YAW) ? entity.getBedrockRotation().getY() : 0);
|
||||
|
||||
session.getConnector().getLogger().debug("Teleport from " + entity.getPosition().getX() + " " + (entity.getPosition().getY() - EntityType.PLAYER.getOffset()) + " " + entity.getPosition().getZ());
|
||||
|
||||
session.setTeleportCache(new TeleportCache(newX, newY, newZ, packet.getTeleportId()));
|
||||
entity.moveAbsolute(session, Vector3f.from(newX, newY, newZ), (float) newYaw, (float) newPitch, true, true);
|
||||
session.addTeleport(new TeleportCache(newX, newY, newZ, newPitch, newYaw, packet.getTeleportId()));
|
||||
entity.moveAbsolute(session, Vector3f.from(newX, newY, newZ), newYaw, newPitch, true, true);
|
||||
|
||||
session.getConnector().getLogger().debug("to " + entity.getPosition().getX() + " " + (entity.getPosition().getY() - EntityType.PLAYER.getOffset()) + " " + entity.getPosition().getZ());
|
||||
}
|
||||
}
|
||||
|
@ -28,12 +28,12 @@ package org.geysermc.connector.network.translators.java.entity.spawn;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.ServerSpawnPlayerPacket;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import org.geysermc.connector.utils.SkinUtils;
|
||||
import org.geysermc.connector.skin.SkinManager;
|
||||
|
||||
@Translator(packet = ServerSpawnPlayerPacket.class)
|
||||
public class JavaSpawnPlayerTranslator extends PacketTranslator<ServerSpawnPlayerPacket> {
|
||||
@ -62,7 +62,7 @@ public class JavaSpawnPlayerTranslator extends PacketTranslator<ServerSpawnPlaye
|
||||
|
||||
if (session.getUpstream().isInitialized()) {
|
||||
entity.sendPlayer(session);
|
||||
SkinUtils.requestAndHandleSkinAndCape(entity, session, null);
|
||||
SkinManager.requestAndHandleSkinAndCape(entity, session, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ public class JavaScoreboardObjectiveTranslator extends PacketTranslator<ServerSc
|
||||
switch (packet.getAction()) {
|
||||
case ADD:
|
||||
case UPDATE:
|
||||
objective.setDisplayName(MessageTranslator.convertMessage(packet.getDisplayName().toString()))
|
||||
objective.setDisplayName(MessageTranslator.convertMessage(packet.getDisplayName()))
|
||||
.setType(packet.getType().ordinal());
|
||||
break;
|
||||
case REMOVE:
|
||||
|
@ -59,11 +59,11 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
|
||||
switch (packet.getAction()) {
|
||||
case CREATE:
|
||||
scoreboard.registerNewTeam(packet.getTeamName(), toPlayerSet(packet.getPlayers()))
|
||||
.setName(MessageTranslator.convertMessage(packet.getDisplayName().toString()))
|
||||
.setName(MessageTranslator.convertMessage(packet.getDisplayName()))
|
||||
.setColor(packet.getColor())
|
||||
.setNameTagVisibility(packet.getNameTagVisibility())
|
||||
.setPrefix(MessageTranslator.convertMessage(packet.getPrefix().toString(), session.getLocale()))
|
||||
.setSuffix(MessageTranslator.convertMessage(packet.getSuffix().toString(), session.getLocale()));
|
||||
.setPrefix(MessageTranslator.convertMessage(packet.getPrefix(), session.getLocale()))
|
||||
.setSuffix(MessageTranslator.convertMessage(packet.getSuffix(), session.getLocale()));
|
||||
break;
|
||||
case UPDATE:
|
||||
if (team == null) {
|
||||
@ -74,11 +74,11 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
|
||||
return;
|
||||
}
|
||||
|
||||
team.setName(MessageTranslator.convertMessage(packet.getDisplayName().toString()))
|
||||
team.setName(MessageTranslator.convertMessage(packet.getDisplayName()))
|
||||
.setColor(packet.getColor())
|
||||
.setNameTagVisibility(packet.getNameTagVisibility())
|
||||
.setPrefix(MessageTranslator.convertMessage(packet.getPrefix().toString(), session.getLocale()))
|
||||
.setSuffix(MessageTranslator.convertMessage(packet.getSuffix().toString(), session.getLocale()))
|
||||
.setPrefix(MessageTranslator.convertMessage(packet.getPrefix(), session.getLocale()))
|
||||
.setSuffix(MessageTranslator.convertMessage(packet.getSuffix(), session.getLocale()))
|
||||
.setUpdateType(UpdateType.UPDATE);
|
||||
break;
|
||||
case ADD_PLAYER:
|
||||
|
@ -29,12 +29,10 @@ import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSl
|
||||
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.inventory.InventoryTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||
import org.geysermc.connector.utils.InventoryUtils;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Translator(packet = ServerSetSlotPacket.class)
|
||||
public class JavaSetSlotTranslator extends PacketTranslator<ServerSetSlotPacket> {
|
||||
|
||||
|
@ -38,7 +38,7 @@ import com.nukkitx.protocol.bedrock.data.GameRuleData;
|
||||
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
|
||||
import com.nukkitx.protocol.bedrock.packet.*;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
@ -78,7 +78,7 @@ public class JavaSpawnParticleTranslator extends PacketTranslator<ServerSpawnPar
|
||||
int r = (int) (data.getRed()*255);
|
||||
int g = (int) (data.getGreen()*255);
|
||||
int b = (int) (data.getBlue()*255);
|
||||
particle.setType(LevelEventType.PARTICLE_REDSTONE);
|
||||
particle.setType(LevelEventType.PARTICLE_FALLING_DUST);
|
||||
particle.setData(((0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff));
|
||||
particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
|
||||
session.sendUpstreamPacket(particle);
|
||||
|
Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren