forked from GeyserMC/Geyser
Fix inconsistencies with movement and position (#699)
Movement is now significant better, especially on slabs, stairs, and other half-blocks. Co-authored-by: RednedEpic <redned235@gmail.com> Co-authored-by: DoctorMacc <toy.fighter1@gmail.com> Co-authored-by: Tim203 <mctim203@gmail.com> Co-authored-by: Camotoy <20743703+DoctorMacc@users.noreply.github.com>
This commit is contained in:
parent
4297215420
commit
a70d3e2150
53 changed files with 2117 additions and 248 deletions
|
@ -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>
|
||||
|
|
|
@ -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,9 +199,10 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (connector != null)
|
||||
if (connector != null) {
|
||||
connector.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeyserSpigotConfiguration getGeyserConfig() {
|
||||
|
@ -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);
|
||||
|
@ -220,8 +273,36 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,76 +99,20 @@ 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++) {
|
||||
|
@ -196,7 +123,6 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMoreBlockDataThanChunkCache() {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -257,6 +257,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,7 @@ import org.geysermc.connector.network.translators.sound.SoundRegistry;
|
|||
import org.geysermc.connector.network.translators.world.WorldManager;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
|
||||
import org.geysermc.connector.network.translators.collision.CollisionTranslator;
|
||||
import org.geysermc.connector.utils.DimensionUtils;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
|
@ -134,6 +135,7 @@ public class GeyserConnector {
|
|||
EntityIdentifierRegistry.init();
|
||||
ItemRegistry.init();
|
||||
ItemTranslator.init();
|
||||
CollisionTranslator.init();
|
||||
LocaleUtils.init();
|
||||
PotionMixRegistry.init();
|
||||
RecipeRegistry.init();
|
||||
|
|
|
@ -135,6 +135,8 @@ public interface GeyserConfiguration {
|
|||
|
||||
int getMtu();
|
||||
|
||||
boolean isUseAdapters();
|
||||
|
||||
int getConfigVersion();
|
||||
|
||||
static void checkGeyserConfiguration(GeyserConfiguration geyserConfig, GeyserLogger geyserLogger) {
|
||||
|
|
|
@ -170,6 +170,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;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ 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;
|
||||
|
@ -49,11 +48,11 @@ import lombok.Setter;
|
|||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
* @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;
|
||||
|
@ -43,12 +43,13 @@ import com.nukkitx.protocol.bedrock.packet.SetEntityLinkPacket;
|
|||
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
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;
|
||||
|
@ -66,7 +67,6 @@ public class PlayerEntity extends LivingEntity {
|
|||
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 +83,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 +157,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 +232,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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
@ -63,7 +67,7 @@ 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.inventory.PlayerInventory;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
import org.geysermc.connector.network.remote.RemoteServer;
|
||||
|
@ -73,6 +77,7 @@ import org.geysermc.connector.network.session.cache.*;
|
|||
import org.geysermc.connector.network.translators.BiomeTranslator;
|
||||
import org.geysermc.connector.network.translators.EntityIdentifierRegistry;
|
||||
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
||||
import org.geysermc.connector.network.translators.collision.CollisionManager;
|
||||
import org.geysermc.connector.network.translators.inventory.EnchantmentInventoryTranslator;
|
||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||
import org.geysermc.connector.utils.*;
|
||||
|
@ -100,16 +105,21 @@ 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 WindowCache windowCache;
|
||||
@Setter
|
||||
private TeleportCache teleportCache;
|
||||
private final Int2ObjectMap<TeleportCache> teleportMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
/**
|
||||
* Stores session collision
|
||||
*/
|
||||
private final CollisionManager collisionManager;
|
||||
|
||||
@Getter
|
||||
private final Long2ObjectMap<ClientboundMapItemDataPacket> storedMaps = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
|
||||
|
@ -136,7 +146,6 @@ public class GeyserSession implements CommandSender {
|
|||
|
||||
private final AtomicInteger pendingDimSwitches = new AtomicInteger(0);
|
||||
|
||||
@Setter
|
||||
private boolean sneaking;
|
||||
|
||||
@Setter
|
||||
|
@ -313,11 +322,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.windowCache = new WindowCache(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;
|
||||
|
@ -561,6 +573,7 @@ public class GeyserSession implements CommandSender {
|
|||
|
||||
this.chunkCache = null;
|
||||
this.entityCache = null;
|
||||
this.effectCache = null;
|
||||
this.worldCache = null;
|
||||
this.inventoryCache = null;
|
||||
this.windowCache = null;
|
||||
|
@ -576,6 +589,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();
|
||||
|
@ -679,18 +698,75 @@ public class GeyserSession implements CommandSender {
|
|||
upstream.sendPacket(startGamePacket);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (teleportMap.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
int teleportId = teleportCache.getTeleportConfirmId();
|
||||
teleportCache = null;
|
||||
ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(teleportId);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,7 +25,7 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.bedrock;
|
||||
|
||||
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;
|
||||
|
|
|
@ -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(position)){
|
||||
return;
|
||||
}
|
||||
|
||||
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()
|
||||
);
|
||||
|
||||
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());
|
||||
entity.setPosition(packet.getPosition().sub(0, EntityType.PLAYER.getOffset(), 0));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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());
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.server.ServerJoinGamePack
|
|||
import com.nukkitx.protocol.bedrock.data.GameRuleData;
|
||||
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,7 +26,7 @@
|
|||
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;
|
||||
|
|
|
@ -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,7 +28,7 @@ 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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -71,6 +71,9 @@ public class BlockTranslator {
|
|||
public static final Int2BooleanMap JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND = new Int2BooleanOpenHashMap();
|
||||
public static final Int2ObjectMap<String> JAVA_RUNTIME_ID_TO_TOOL_TYPE = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
// The index of the collision data in collision.json
|
||||
public static final Int2IntMap JAVA_RUNTIME_ID_TO_COLLISION_INDEX = new Int2IntOpenHashMap();
|
||||
|
||||
/**
|
||||
* Java numeric ID to java unique identifier, used for block names in the statistics screen
|
||||
*/
|
||||
|
@ -165,6 +168,11 @@ public class BlockTranslator {
|
|||
JAVA_RUNTIME_ID_TO_TOOL_TYPE.put(javaRuntimeId, toolTypeNode.textValue());
|
||||
}
|
||||
|
||||
JsonNode collisionIndexNode = entry.getValue().get("collision_index");
|
||||
if (hardnessNode != null) {
|
||||
JAVA_RUNTIME_ID_TO_COLLISION_INDEX.put(javaRuntimeId, collisionIndexNode.intValue());
|
||||
}
|
||||
|
||||
JAVA_ID_BLOCK_MAP.put(javaId, javaRuntimeId);
|
||||
|
||||
// Used for adding all "special" Java block states to block state map
|
||||
|
|
|
@ -125,8 +125,8 @@ public class BlockUtils {
|
|||
return calculateBreakTime(blockHardness, toolTier, canHarvestWithHand, correctTool, toolType, isWoolBlock, isCobweb, toolEfficiencyLevel, hasteLevel, miningFatigueLevel, false, false, false);
|
||||
}
|
||||
|
||||
hasteLevel = session.getPlayerEntity().getEffectCache().getEffectLevel(Effect.FASTER_DIG);
|
||||
miningFatigueLevel = session.getPlayerEntity().getEffectCache().getEffectLevel(Effect.SLOWER_DIG);
|
||||
hasteLevel = session.getEffectCache().getEffectLevel(Effect.FASTER_DIG);
|
||||
miningFatigueLevel = session.getEffectCache().getEffectLevel(Effect.SLOWER_DIG);
|
||||
|
||||
boolean isInWater = session.getConnector().getConfig().isCacheChunks()
|
||||
&& BlockTranslator.getBedrockBlockId(session.getConnector().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt())) == BlockTranslator.BEDROCK_WATER_ID;
|
||||
|
|
|
@ -82,7 +82,7 @@ public class DimensionUtils {
|
|||
session.setSpawned(false);
|
||||
session.setLastChunkPosition(null);
|
||||
|
||||
for (Effect effect : session.getPlayerEntity().getEffectCache().getEntityEffects().keySet()) {
|
||||
for (Effect effect : session.getEffectCache().getEntityEffects().keySet()) {
|
||||
MobEffectPacket mobEffectPacket = new MobEffectPacket();
|
||||
mobEffectPacket.setEvent(MobEffectPacket.Event.REMOVE);
|
||||
mobEffectPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
|
||||
|
@ -90,7 +90,7 @@ public class DimensionUtils {
|
|||
session.sendUpstreamPacket(mobEffectPacket);
|
||||
}
|
||||
// Effects are re-sent from server
|
||||
session.getPlayerEntity().getEffectCache().getEntityEffects().clear();
|
||||
session.getEffectCache().getEntityEffects().clear();
|
||||
|
||||
//let java server handle portal travel sound
|
||||
StopSoundPacket stopSoundPacket = new StopSoundPacket();
|
||||
|
|
|
@ -35,7 +35,7 @@ import lombok.AllArgsConstructor;
|
|||
import lombok.Getter;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
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.session.auth.BedrockClientData;
|
||||
|
||||
|
|
|
@ -94,13 +94,16 @@ show-cooldown: true
|
|||
# default-locale: en_us
|
||||
|
||||
# Configures if chunk caching should be enabled or not. This keeps an individual
|
||||
# record of each block the client loads in. While this feature does allow for a few
|
||||
# things such as block break animations to show up in creative mode and among others,
|
||||
# it is HIGHLY recommended you disable this on a production environment as it can eat
|
||||
# up a lot of RAM. However, when using the Spigot version of Geyser, support for features
|
||||
# or implementations this allows is automatically enabled without the additional caching as
|
||||
# Geyser has direct access to the server itself.
|
||||
cache-chunks: false
|
||||
# record of each block the client loads in. This feature does allow for a few things
|
||||
# such as more accurate movement that causes less problems with anticheat (meaning
|
||||
# you're less likely to be banned) and allows block break animations to show up in
|
||||
# creative mode (and other features). Although this increases RAM usage, it likely
|
||||
# won't have much of an effect for the vast majority of people. However, if you're
|
||||
# running out of RAM or are in a RAM-sensitive environment, you may want to disable
|
||||
# this. When using the Spigot version of Geyser, support for features or
|
||||
# implementations this allows is automatically enabled without the additional caching
|
||||
# as Geyser has direct access to the server itself.
|
||||
cache-chunks: true
|
||||
|
||||
# Specify how many days images will be cached to disk to save downloading them from the internet.
|
||||
# A value of 0 is disabled. (Default: 0)
|
||||
|
@ -148,4 +151,8 @@ enable-proxy-connections: false
|
|||
# 1400 is the default.
|
||||
# mtu: 1400
|
||||
|
||||
# Whether to use direct server methods to retrieve information such as block states.
|
||||
# Turning this off for Spigot will stop NMS from being used but will have a performance impact.
|
||||
use-adapters: true
|
||||
|
||||
config-version: 4
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit bf4b0b7103193154dd0b06e0459dc375c753069a
|
||||
Subproject commit b7ef31bd9c45aa3a0735883764c231f30cb55bfa
|
|
@ -1 +1 @@
|
|||
Subproject commit 618a9b981398647125b1b63494cb49ad93433243
|
||||
Subproject commit 2d14c9dc3d75df7463fc7605a6cff63b5926a03e
|
Loading…
Reference in a new issue