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>
|
<version>3.2.0</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.geysermc.adapters</groupId>
|
||||||
|
<artifactId>spigot-all</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<finalName>${outputName}-Spigot</finalName>
|
<finalName>${outputName}-Spigot</finalName>
|
||||||
|
|
|
@ -25,8 +25,10 @@
|
||||||
|
|
||||||
package org.geysermc.platform.spigot;
|
package org.geysermc.platform.spigot;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import org.geysermc.adapters.spigot.SpigotAdapters;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.common.PlatformType;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
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.GeyserSpigotCommandManager;
|
||||||
import org.geysermc.platform.spigot.command.SpigotCommandSender;
|
import org.geysermc.platform.spigot.command.SpigotCommandSender;
|
||||||
import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener;
|
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.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.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
|
|
||||||
private GeyserSpigotCommandManager geyserCommandManager;
|
private GeyserSpigotCommandManager geyserCommandManager;
|
||||||
private GeyserSpigotConfiguration geyserConfig;
|
private GeyserSpigotConfiguration geyserConfig;
|
||||||
private GeyserSpigotLogger geyserLogger;
|
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
|
// Set if we need to use a different method for getting a player's locale
|
||||||
SpigotCommandSender.setUseLegacyLocaleMethod(!isCompatible(Bukkit.getServer().getVersion(), "1.12.0"));
|
SpigotCommandSender.setUseLegacyLocaleMethod(!isCompatible(Bukkit.getServer().getVersion(), "1.12.0"));
|
||||||
|
|
||||||
this.geyserWorldManager = new GeyserSpigotWorldManager(isLegacy, use3dBiomes, isViaVersion);
|
if (connector.getConfig().isUseAdapters()) {
|
||||||
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(connector, isLegacy, isViaVersion);
|
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);
|
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
|
||||||
|
|
||||||
|
@ -152,8 +199,9 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
if (connector != null)
|
if (connector != null) {
|
||||||
connector.shutdown();
|
connector.shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -186,6 +234,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
return getDataFolder().toPath();
|
return getDataFolder().toPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BootstrapDumpInfo getDumpInfo() {
|
||||||
|
return new GeyserSpigotDumpInfo();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isCompatible(String version, String whichVersion) {
|
public boolean isCompatible(String version, String whichVersion) {
|
||||||
int[] currentVersion = parseVersion(version);
|
int[] currentVersion = parseVersion(version);
|
||||||
int[] otherVersion = parseVersion(whichVersion);
|
int[] otherVersion = parseVersion(whichVersion);
|
||||||
|
@ -213,15 +266,43 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
String t = stringArray[index].replaceAll("\\D", "");
|
String t = stringArray[index].replaceAll("\\D", "");
|
||||||
try {
|
try {
|
||||||
temp[index] = Integer.parseInt(t);
|
temp[index] = Integer.parseInt(t);
|
||||||
} catch(NumberFormatException ex) {
|
} catch (NumberFormatException ex) {
|
||||||
temp[index] = 0;
|
temp[index] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public BootstrapDumpInfo getDumpInfo() {
|
* @return the server version before ViaVersion finishes initializing
|
||||||
return new GeyserSpigotDumpInfo();
|
*/
|
||||||
|
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.GeyserConnector;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
|
import org.geysermc.platform.spigot.world.manager.GeyserSpigotWorldManager;
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class GeyserSpigotBlockPlaceListener implements Listener {
|
public class GeyserSpigotBlockPlaceListener implements Listener {
|
||||||
|
|
||||||
private final GeyserConnector connector;
|
private final GeyserConnector connector;
|
||||||
private final boolean isLegacy;
|
private final GeyserSpigotWorldManager worldManager;
|
||||||
private final boolean isViaVersion;
|
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void place(final BlockPlaceEvent event) {
|
public void place(final BlockPlaceEvent event) {
|
||||||
|
@ -52,14 +52,13 @@ public class GeyserSpigotBlockPlaceListener implements Listener {
|
||||||
placeBlockSoundPacket.setSound(SoundEvent.PLACE);
|
placeBlockSoundPacket.setSound(SoundEvent.PLACE);
|
||||||
placeBlockSoundPacket.setPosition(Vector3f.from(event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ()));
|
placeBlockSoundPacket.setPosition(Vector3f.from(event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ()));
|
||||||
placeBlockSoundPacket.setBabySound(false);
|
placeBlockSoundPacket.setBabySound(false);
|
||||||
String javaBlockId;
|
if (worldManager.isLegacy()) {
|
||||||
if (isLegacy) {
|
placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(worldManager.getBlockAt(session,
|
||||||
javaBlockId = BlockTranslator.getJavaIdBlockMap().inverse().get(GeyserSpigotWorldManager.getLegacyBlock(session,
|
event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ())));
|
||||||
event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ(), isViaVersion));
|
|
||||||
} else {
|
} 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(":");
|
placeBlockSoundPacket.setIdentifier(":");
|
||||||
session.sendUpstreamPacket(placeBlockSoundPacket);
|
session.sendUpstreamPacket(placeBlockSoundPacket);
|
||||||
session.setLastBlockPlacePosition(null);
|
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
|
* @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.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
||||||
|
@ -34,6 +34,7 @@ import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
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.FileUtils;
|
||||||
import org.geysermc.connector.utils.GameRule;
|
import org.geysermc.connector.utils.GameRule;
|
||||||
import org.geysermc.connector.utils.LanguageUtils;
|
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.io.InputStream;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base world manager to use when there is no supported NMS revision
|
||||||
|
*/
|
||||||
public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
||||||
/**
|
/**
|
||||||
* The current client protocol version for ViaVersion usage.
|
* 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.
|
* Whether the server is pre-1.16 and therefore does not support 3D biomes on an API level guaranteed.
|
||||||
*/
|
*/
|
||||||
private final boolean use3dBiomes;
|
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.
|
* 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);
|
private final Int2IntMap biomeToIdMap = new Int2IntOpenHashMap(Biome.values().length);
|
||||||
|
|
||||||
public GeyserSpigotWorldManager(boolean isLegacy, boolean use3dBiomes, boolean isViaVersion) {
|
public GeyserSpigotWorldManager(boolean use3dBiomes) {
|
||||||
this.isLegacy = isLegacy;
|
|
||||||
this.use3dBiomes = use3dBiomes;
|
this.use3dBiomes = use3dBiomes;
|
||||||
this.isViaVersion = isViaVersion;
|
|
||||||
|
|
||||||
// Load the values into the biome-to-ID map
|
// Load the values into the biome-to-ID map
|
||||||
InputStream biomeStream = FileUtils.getResource("biomes.json");
|
InputStream biomeStream = FileUtils.getResource("biomes.json");
|
||||||
|
@ -116,83 +99,26 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
||||||
@Override
|
@Override
|
||||||
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
||||||
Player bukkitPlayer;
|
Player bukkitPlayer;
|
||||||
if ((this.isLegacy && !this.isViaVersion)
|
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
||||||
|| session.getPlayerEntity() == null
|
|
||||||
|| (bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
|
||||||
return BlockTranslator.JAVA_AIR_ID;
|
return BlockTranslator.JAVA_AIR_ID;
|
||||||
}
|
}
|
||||||
World world = bukkitPlayer.getWorld();
|
World world = bukkitPlayer.getWorld();
|
||||||
if (isLegacy) {
|
return BlockTranslator.getJavaIdBlockMap().getOrDefault(world.getBlockAt(x, y, z).getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
|
public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
|
||||||
Player bukkitPlayer;
|
Player bukkitPlayer;
|
||||||
if ((this.isLegacy && !this.isViaVersion)
|
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
||||||
|| session.getPlayerEntity() == null
|
|
||||||
|| (bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
World world = bukkitPlayer.getWorld();
|
World world = bukkitPlayer.getWorld();
|
||||||
if (this.isLegacy) {
|
for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
|
||||||
// Get block entity storage
|
for (int blockZ = 0; blockZ < 16; blockZ++) {
|
||||||
BlockStorage storage = Via.getManager().getConnection(bukkitPlayer.getUniqueId()).get(BlockStorage.class);
|
for (int blockX = 0; blockX < 16; blockX++) {
|
||||||
for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
|
Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ);
|
||||||
for (int blockZ = 0; blockZ < 16; blockZ++) {
|
int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID);
|
||||||
for (int blockX = 0; blockX < 16; blockX++) {
|
chunk.set(blockX, blockY, blockZ, id);
|
||||||
chunk.set(blockX, blockY, blockZ, getLegacyBlock(storage, world, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//TODO: see above TODO in getBlockAt
|
|
||||||
for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
|
|
||||||
for (int blockZ = 0; blockZ < 16; blockZ++) {
|
|
||||||
for (int blockX = 0; blockX < 16; blockX++) {
|
|
||||||
Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ);
|
|
||||||
int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID);
|
|
||||||
chunk.set(blockX, blockY, blockZ, id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -254,4 +180,16 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
||||||
public boolean hasPermission(GeyserSession session, String permission) {
|
public boolean hasPermission(GeyserSession session, String permission) {
|
||||||
return Bukkit.getPlayer(session.getPlayerEntity().getUsername()).hasPermission(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[
|
<script><![CDATA[
|
||||||
new org.reflections.Reflections("org.geysermc.connector.network.translators")
|
new org.reflections.Reflections("org.geysermc.connector.network.translators")
|
||||||
.save("${project.artifactId}/target/classes/META-INF/reflections/org.geysermc.connector.network.translators-reflections.xml")
|
.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")
|
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")
|
.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")
|
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.WorldManager;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
|
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
|
||||||
|
import org.geysermc.connector.network.translators.collision.CollisionTranslator;
|
||||||
import org.geysermc.connector.utils.DimensionUtils;
|
import org.geysermc.connector.utils.DimensionUtils;
|
||||||
import org.geysermc.connector.utils.LanguageUtils;
|
import org.geysermc.connector.utils.LanguageUtils;
|
||||||
import org.geysermc.connector.utils.LocaleUtils;
|
import org.geysermc.connector.utils.LocaleUtils;
|
||||||
|
@ -134,6 +135,7 @@ public class GeyserConnector {
|
||||||
EntityIdentifierRegistry.init();
|
EntityIdentifierRegistry.init();
|
||||||
ItemRegistry.init();
|
ItemRegistry.init();
|
||||||
ItemTranslator.init();
|
ItemTranslator.init();
|
||||||
|
CollisionTranslator.init();
|
||||||
LocaleUtils.init();
|
LocaleUtils.init();
|
||||||
PotionMixRegistry.init();
|
PotionMixRegistry.init();
|
||||||
RecipeRegistry.init();
|
RecipeRegistry.init();
|
||||||
|
|
|
@ -135,6 +135,8 @@ public interface GeyserConfiguration {
|
||||||
|
|
||||||
int getMtu();
|
int getMtu();
|
||||||
|
|
||||||
|
boolean isUseAdapters();
|
||||||
|
|
||||||
int getConfigVersion();
|
int getConfigVersion();
|
||||||
|
|
||||||
static void checkGeyserConfiguration(GeyserConfiguration geyserConfig, GeyserLogger geyserLogger) {
|
static void checkGeyserConfiguration(GeyserConfiguration geyserConfig, GeyserLogger geyserLogger) {
|
||||||
|
|
|
@ -170,6 +170,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
||||||
@JsonProperty("mtu")
|
@JsonProperty("mtu")
|
||||||
private int mtu = 1400;
|
private int mtu = 1400;
|
||||||
|
|
||||||
|
@JsonProperty("use-adapters")
|
||||||
|
private boolean useAdapters = true;
|
||||||
|
|
||||||
@JsonProperty("config-version")
|
@JsonProperty("config-version")
|
||||||
private int configVersion = 0;
|
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.ClientPlayerActionPacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket;
|
||||||
import com.nukkitx.math.vector.Vector3f;
|
import com.nukkitx.math.vector.Vector3f;
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
|
||||||
import com.nukkitx.protocol.bedrock.data.AttributeData;
|
import com.nukkitx.protocol.bedrock.data.AttributeData;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap;
|
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.Attribute;
|
||||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||||
import org.geysermc.connector.entity.living.ArmorStandEntity;
|
import org.geysermc.connector.entity.living.ArmorStandEntity;
|
||||||
|
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||||
import org.geysermc.connector.utils.AttributeUtils;
|
import org.geysermc.connector.utils.AttributeUtils;
|
||||||
import org.geysermc.connector.utils.ChunkUtils;
|
|
||||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
|
@ -36,6 +36,7 @@ import com.nukkitx.nbt.NbtMapBuilder;
|
||||||
import com.nukkitx.nbt.NbtType;
|
import com.nukkitx.nbt.NbtType;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket;
|
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.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.utils.FireworkColor;
|
import org.geysermc.connector.utils.FireworkColor;
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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.auth.data.GameProfile;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
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 com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
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.Attribute;
|
||||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||||
import org.geysermc.connector.entity.living.animal.tameable.ParrotEntity;
|
import org.geysermc.connector.entity.living.animal.tameable.ParrotEntity;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.session.cache.EntityEffectCache;
|
|
||||||
import org.geysermc.connector.scoreboard.Team;
|
import org.geysermc.connector.scoreboard.Team;
|
||||||
import org.geysermc.connector.utils.AttributeUtils;
|
import org.geysermc.connector.utils.AttributeUtils;
|
||||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||||
|
@ -66,7 +67,6 @@ public class PlayerEntity extends LivingEntity {
|
||||||
private String username;
|
private String username;
|
||||||
private long lastSkinUpdate = -1;
|
private long lastSkinUpdate = -1;
|
||||||
private boolean playerList = true; // Player is in the player list
|
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
|
* Saves the parrot currently on the player's left shoulder; otherwise null
|
||||||
|
@ -83,14 +83,10 @@ public class PlayerEntity extends LivingEntity {
|
||||||
profile = gameProfile;
|
profile = gameProfile;
|
||||||
uuid = gameProfile.getId();
|
uuid = gameProfile.getId();
|
||||||
username = gameProfile.getName();
|
username = gameProfile.getName();
|
||||||
effectCache = new EntityEffectCache();
|
|
||||||
if (geyserId == 1) valid = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void spawnEntity(GeyserSession session) {
|
public void spawnEntity(GeyserSession session) {
|
||||||
if (geyserId == 1) return;
|
|
||||||
|
|
||||||
AddPlayerPacket addPlayerPacket = new AddPlayerPacket();
|
AddPlayerPacket addPlayerPacket = new AddPlayerPacket();
|
||||||
addPlayerPacket.setUuid(uuid);
|
addPlayerPacket.setUuid(uuid);
|
||||||
addPlayerPacket.setUsername(username);
|
addPlayerPacket.setUsername(username);
|
||||||
|
@ -161,6 +157,10 @@ public class PlayerEntity extends LivingEntity {
|
||||||
setRotation(rotation);
|
setRotation(rotation);
|
||||||
this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ);
|
this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ);
|
||||||
|
|
||||||
|
// If this is the player logged in through this Geyser session
|
||||||
|
if (geyserId == 1) {
|
||||||
|
session.getCollisionManager().updatePlayerBoundingBox(position);
|
||||||
|
}
|
||||||
setOnGround(isOnGround);
|
setOnGround(isOnGround);
|
||||||
|
|
||||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||||
|
@ -232,7 +232,18 @@ public class PlayerEntity extends LivingEntity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPosition(Vector3f position) {
|
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
|
@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.PillagerEntity;
|
||||||
import org.geysermc.connector.entity.living.monster.raid.RaidParticipantEntity;
|
import org.geysermc.connector.entity.living.monster.raid.RaidParticipantEntity;
|
||||||
import org.geysermc.connector.entity.living.monster.raid.SpellcasterIllagerEntity;
|
import org.geysermc.connector.entity.living.monster.raid.SpellcasterIllagerEntity;
|
||||||
|
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public enum EntityType {
|
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.statistic.Statistic;
|
||||||
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
|
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.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.client.world.ClientTeleportConfirmPacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.login.server.LoginSuccessPacket;
|
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.*;
|
||||||
import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
||||||
import com.nukkitx.protocol.bedrock.packet.*;
|
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.Long2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
@ -63,7 +67,7 @@ import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.command.CommandSender;
|
import org.geysermc.connector.command.CommandSender;
|
||||||
import org.geysermc.connector.common.AuthType;
|
import org.geysermc.connector.common.AuthType;
|
||||||
import org.geysermc.connector.entity.Entity;
|
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.inventory.PlayerInventory;
|
||||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||||
import org.geysermc.connector.network.remote.RemoteServer;
|
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.BiomeTranslator;
|
||||||
import org.geysermc.connector.network.translators.EntityIdentifierRegistry;
|
import org.geysermc.connector.network.translators.EntityIdentifierRegistry;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
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.inventory.EnchantmentInventoryTranslator;
|
||||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||||
import org.geysermc.connector.utils.*;
|
import org.geysermc.connector.utils.*;
|
||||||
|
@ -100,16 +105,21 @@ public class GeyserSession implements CommandSender {
|
||||||
@Setter
|
@Setter
|
||||||
private BedrockClientData clientData;
|
private BedrockClientData clientData;
|
||||||
|
|
||||||
private PlayerEntity playerEntity;
|
private final SessionPlayerEntity playerEntity;
|
||||||
private PlayerInventory inventory;
|
private PlayerInventory inventory;
|
||||||
|
|
||||||
private ChunkCache chunkCache;
|
private ChunkCache chunkCache;
|
||||||
private EntityCache entityCache;
|
private EntityCache entityCache;
|
||||||
|
private EntityEffectCache effectCache;
|
||||||
private InventoryCache inventoryCache;
|
private InventoryCache inventoryCache;
|
||||||
private WorldCache worldCache;
|
private WorldCache worldCache;
|
||||||
private WindowCache windowCache;
|
private WindowCache windowCache;
|
||||||
@Setter
|
private final Int2ObjectMap<TeleportCache> teleportMap = new Int2ObjectOpenHashMap<>();
|
||||||
private TeleportCache teleportCache;
|
|
||||||
|
/**
|
||||||
|
* Stores session collision
|
||||||
|
*/
|
||||||
|
private final CollisionManager collisionManager;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final Long2ObjectMap<ClientboundMapItemDataPacket> storedMaps = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
|
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);
|
private final AtomicInteger pendingDimSwitches = new AtomicInteger(0);
|
||||||
|
|
||||||
@Setter
|
|
||||||
private boolean sneaking;
|
private boolean sneaking;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
|
@ -313,11 +322,14 @@ public class GeyserSession implements CommandSender {
|
||||||
|
|
||||||
this.chunkCache = new ChunkCache(this);
|
this.chunkCache = new ChunkCache(this);
|
||||||
this.entityCache = new EntityCache(this);
|
this.entityCache = new EntityCache(this);
|
||||||
|
this.effectCache = new EntityEffectCache();
|
||||||
this.inventoryCache = new InventoryCache(this);
|
this.inventoryCache = new InventoryCache(this);
|
||||||
this.worldCache = new WorldCache(this);
|
this.worldCache = new WorldCache(this);
|
||||||
this.windowCache = new WindowCache(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.inventory = new PlayerInventory();
|
||||||
|
|
||||||
this.spawned = false;
|
this.spawned = false;
|
||||||
|
@ -561,6 +573,7 @@ public class GeyserSession implements CommandSender {
|
||||||
|
|
||||||
this.chunkCache = null;
|
this.chunkCache = null;
|
||||||
this.entityCache = null;
|
this.entityCache = null;
|
||||||
|
this.effectCache = null;
|
||||||
this.worldCache = null;
|
this.worldCache = null;
|
||||||
this.inventoryCache = null;
|
this.inventoryCache = null;
|
||||||
this.windowCache = null;
|
this.windowCache = null;
|
||||||
|
@ -576,6 +589,12 @@ public class GeyserSession implements CommandSender {
|
||||||
this.authData = authData;
|
this.authData = authData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSneaking(boolean sneaking) {
|
||||||
|
this.sneaking = sneaking;
|
||||||
|
collisionManager.updatePlayerBoundingBox();
|
||||||
|
collisionManager.updateScaffoldingFlags();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return authData.getName();
|
return authData.getName();
|
||||||
|
@ -679,24 +698,81 @@ public class GeyserSession implements CommandSender {
|
||||||
upstream.sendPacket(startGamePacket);
|
upstream.sendPacket(startGamePacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean confirmTeleport(Vector3d position) {
|
public void addTeleport(TeleportCache teleportCache) {
|
||||||
if (teleportCache != null) {
|
teleportMap.put(teleportCache.getTeleportConfirmId(), teleportCache);
|
||||||
if (!teleportCache.canConfirm(position)) {
|
|
||||||
GeyserConnector.getInstance().getLogger().debug("Unconfirmed Teleport " + teleportCache.getTeleportConfirmId()
|
ObjectIterator<Int2ObjectMap.Entry<TeleportCache>> it = teleportMap.int2ObjectEntrySet().iterator();
|
||||||
+ " Ignore movement " + position + " expected " + teleportCache);
|
|
||||||
return false;
|
// Remove any teleports with a higher number - maybe this is a world change that reset the ID to 0?
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Int2ObjectMap.Entry<TeleportCache> entry = it.next();
|
||||||
|
int nextID = entry.getValue().getTeleportConfirmId();
|
||||||
|
if (nextID > teleportCache.getTeleportConfirmId()) {
|
||||||
|
it.remove();
|
||||||
}
|
}
|
||||||
int teleportId = teleportCache.getTeleportConfirmId();
|
|
||||||
teleportCache = null;
|
|
||||||
ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(teleportId);
|
|
||||||
sendDownstreamPacket(teleportConfirmPacket);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean confirmTeleport(Vector3d position) {
|
||||||
|
if (teleportMap.size() == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int teleportID = -1;
|
||||||
|
|
||||||
|
for (Int2ObjectMap.Entry<TeleportCache> entry : teleportMap.int2ObjectEntrySet()) {
|
||||||
|
if (entry.getValue().canConfirm(position)) {
|
||||||
|
if (entry.getValue().getTeleportConfirmId() > teleportID) {
|
||||||
|
teleportID = entry.getValue().getTeleportConfirmId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectIterator<Int2ObjectMap.Entry<TeleportCache>> it = teleportMap.int2ObjectEntrySet().iterator();
|
||||||
|
|
||||||
|
if (teleportID != -1) {
|
||||||
|
// Confirm the current teleport and any earlier ones
|
||||||
|
while (it.hasNext()) {
|
||||||
|
TeleportCache entry = it.next().getValue();
|
||||||
|
int nextID = entry.getTeleportConfirmId();
|
||||||
|
if (nextID <= teleportID) {
|
||||||
|
ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(nextID);
|
||||||
|
sendDownstreamPacket(teleportConfirmPacket);
|
||||||
|
// Servers (especially ones like Hypixel) expect exact coordinates given back to them.
|
||||||
|
ClientPlayerPositionRotationPacket positionPacket = new ClientPlayerPositionRotationPacket(playerEntity.isOnGround(),
|
||||||
|
entry.getX(), entry.getY(), entry.getZ(), entry.getYaw(), entry.getPitch());
|
||||||
|
sendDownstreamPacket(positionPacket);
|
||||||
|
it.remove();
|
||||||
|
connector.getLogger().debug("Confirmed teleport " + nextID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (teleportMap.size() > 0) {
|
||||||
|
int resendID = -1;
|
||||||
|
for (Int2ObjectMap.Entry<TeleportCache> entry : teleportMap.int2ObjectEntrySet()) {
|
||||||
|
TeleportCache teleport = entry.getValue();
|
||||||
|
teleport.incrementUnconfirmedFor();
|
||||||
|
if (teleport.shouldResend()) {
|
||||||
|
if (teleport.getTeleportConfirmId() >= resendID) {
|
||||||
|
resendID = teleport.getTeleportConfirmId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resendID != -1) {
|
||||||
|
connector.getLogger().debug("Resending teleport " + resendID);
|
||||||
|
TeleportCache teleport = teleportMap.get(resendID);
|
||||||
|
getPlayerEntity().moveAbsolute(this, Vector3f.from(teleport.getX(), teleport.getY(), teleport.getZ()),
|
||||||
|
teleport.getYaw(), teleport.getPitch(), playerEntity.isOnGround(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queue a packet to be sent to player.
|
* Queue a packet to be sent to player.
|
||||||
*
|
*
|
||||||
* @param packet the bedrock packet from the NukkitX protocol lib
|
* @param packet the bedrock packet from the NukkitX protocol lib
|
||||||
*/
|
*/
|
||||||
public void sendUpstreamPacket(BedrockPacket packet) {
|
public void sendUpstreamPacket(BedrockPacket packet) {
|
||||||
|
@ -709,7 +785,7 @@ public class GeyserSession implements CommandSender {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a packet immediately to the player.
|
* Send a packet immediately to the player.
|
||||||
*
|
*
|
||||||
* @param packet the bedrock packet from the NukkitX protocol lib
|
* @param packet the bedrock packet from the NukkitX protocol lib
|
||||||
*/
|
*/
|
||||||
public void sendUpstreamPacketImmediately(BedrockPacket packet) {
|
public void sendUpstreamPacketImmediately(BedrockPacket packet) {
|
||||||
|
|
|
@ -29,7 +29,7 @@ import it.unimi.dsi.fastutil.longs.*;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.geysermc.connector.entity.Entity;
|
import org.geysermc.connector.entity.Entity;
|
||||||
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.GeyserSession;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
|
@ -26,23 +26,47 @@
|
||||||
package org.geysermc.connector.network.session.cache;
|
package org.geysermc.connector.network.session.cache;
|
||||||
|
|
||||||
import com.nukkitx.math.vector.Vector3d;
|
import com.nukkitx.math.vector.Vector3d;
|
||||||
import com.nukkitx.math.vector.Vector3f;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
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
|
@Data
|
||||||
public class TeleportCache {
|
public class TeleportCache {
|
||||||
|
|
||||||
private static final double ERROR = 0.2;
|
private static final double ERROR_X_AND_Z = 0.1;
|
||||||
private static final double ERROR_Y = 0.5;
|
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) {
|
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.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.MovePlayerPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.RespawnPacket;
|
import com.nukkitx.protocol.bedrock.packet.RespawnPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
|
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.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
package org.geysermc.connector.network.translators.bedrock;
|
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.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
|
|
@ -43,6 +43,7 @@ import org.geysermc.connector.entity.Entity;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
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.network.translators.world.block.BlockTranslator;
|
||||||
import org.geysermc.connector.utils.BlockUtils;
|
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.ClientPlayerPositionPacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket;
|
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.Vector3d;
|
||||||
import com.nukkitx.math.vector.Vector3f;
|
import com.nukkitx.math.vector.Vector3f;
|
||||||
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
|
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
|
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
|
||||||
import org.geysermc.connector.common.ChatColor;
|
import org.geysermc.connector.common.ChatColor;
|
||||||
import org.geysermc.connector.entity.Entity;
|
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||||
import org.geysermc.connector.entity.PlayerEntity;
|
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
import org.geysermc.connector.network.translators.collision.CollisionManager;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@ -48,7 +50,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||||
@Override
|
@Override
|
||||||
public void translate(MovePlayerPacket packet, GeyserSession session) {
|
public void translate(MovePlayerPacket packet, GeyserSession session) {
|
||||||
PlayerEntity entity = session.getPlayerEntity();
|
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()) {
|
if (!session.getUpstream().isInitialized()) {
|
||||||
MoveEntityAbsolutePacket moveEntityBack = new MoveEntityAbsolutePacket();
|
MoveEntityAbsolutePacket moveEntityBack = new MoveEntityAbsolutePacket();
|
||||||
|
@ -65,27 +67,53 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||||
session.getMovementSendIfIdle().cancel(true);
|
session.getMovementSendIfIdle().cancel(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3d position = adjustBedrockPosition(packet.getPosition(), packet.isOnGround());
|
if (session.confirmTeleport(packet.getPosition().toDouble().sub(0, EntityType.PLAYER.getOffset(), 0))) {
|
||||||
|
// head yaw, pitch, head yaw
|
||||||
|
Vector3f rotation = Vector3f.from(packet.getRotation().getY(), packet.getRotation().getX(), packet.getRotation().getY());
|
||||||
|
|
||||||
if(!session.confirmTeleport(position)){
|
boolean positionChanged = !entity.getPosition().equals(packet.getPosition());
|
||||||
return;
|
boolean rotationChanged = !entity.getRotation().equals(rotation);
|
||||||
|
|
||||||
|
// If only the pitch and yaw changed
|
||||||
|
// This isn't needed, but it makes the packets closer to vanilla
|
||||||
|
// It also means you can't "lag back" while only looking, in theory
|
||||||
|
if (!positionChanged && rotationChanged) {
|
||||||
|
ClientPlayerRotationPacket playerRotationPacket = new ClientPlayerRotationPacket(
|
||||||
|
packet.isOnGround(), packet.getRotation().getY(), packet.getRotation().getX());
|
||||||
|
|
||||||
|
entity.setRotation(rotation);
|
||||||
|
entity.setOnGround(packet.isOnGround());
|
||||||
|
|
||||||
|
session.sendDownstreamPacket(playerRotationPacket);
|
||||||
|
} else {
|
||||||
|
Vector3d position = adjustBedrockPosition(session, packet.getPosition(), packet.isOnGround());
|
||||||
|
if (position != null) { // A null return value cancels the packet
|
||||||
|
if (isValidMove(session, packet.getMode(), entity.getPosition(), packet.getPosition())) {
|
||||||
|
Packet movePacket;
|
||||||
|
if (rotationChanged) {
|
||||||
|
// Send rotation updates as well
|
||||||
|
movePacket = new ClientPlayerPositionRotationPacket(packet.isOnGround(), position.getX(), position.getY(), position.getZ(),
|
||||||
|
packet.getRotation().getY(), packet.getRotation().getX());
|
||||||
|
entity.setRotation(rotation);
|
||||||
|
} else {
|
||||||
|
// Rotation did not change; don't send an update with rotation
|
||||||
|
movePacket = new ClientPlayerPositionPacket(packet.isOnGround(), position.getX(), position.getY(), position.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.setPosition(packet.getPosition(), false);
|
||||||
|
entity.setOnGround(packet.isOnGround());
|
||||||
|
|
||||||
|
// Send final movement changes
|
||||||
|
session.sendDownstreamPacket(movePacket);
|
||||||
|
} else {
|
||||||
|
// Not a valid move
|
||||||
|
session.getConnector().getLogger().debug("Recalculating position...");
|
||||||
|
recalculatePosition(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValidMove(session, packet.getMode(), entity.getPosition(), packet.getPosition())) {
|
|
||||||
session.getConnector().getLogger().debug("Recalculating position...");
|
|
||||||
recalculatePosition(session, entity, entity.getPosition());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientPlayerPositionRotationPacket playerPositionRotationPacket = new ClientPlayerPositionRotationPacket(
|
|
||||||
packet.isOnGround(), position.getX(), position.getY(), position.getZ(), packet.getRotation().getY(), packet.getRotation().getX()
|
|
||||||
);
|
|
||||||
|
|
||||||
// head yaw, pitch, head yaw
|
|
||||||
Vector3f rotation = Vector3f.from(packet.getRotation().getY(), packet.getRotation().getX(), packet.getRotation().getY());
|
|
||||||
entity.setPosition(packet.getPosition().sub(0, EntityType.PLAYER.getOffset(), 0));
|
|
||||||
entity.setRotation(rotation);
|
|
||||||
entity.setOnGround(packet.isOnGround());
|
|
||||||
// Move parrots to match if applicable
|
// Move parrots to match if applicable
|
||||||
if (entity.getLeftParrot() != null) {
|
if (entity.getLeftParrot() != null) {
|
||||||
entity.getLeftParrot().moveAbsolute(session, entity.getPosition(), entity.getRotation(), true, false);
|
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);
|
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
|
// Schedule a position send loop if the player is idle
|
||||||
session.setMovementSendIfIdle(session.getConnector().getGeneralThreadPool().schedule(() -> sendPositionIfIdle(session),
|
session.setMovementSendIfIdle(session.getConnector().getGeneralThreadPool().schedule(() -> sendPositionIfIdle(session),
|
||||||
3, TimeUnit.SECONDS));
|
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) {
|
public boolean isValidMove(GeyserSession session, MovePlayerPacket.Mode mode, Vector3f currentPosition, Vector3f newPosition) {
|
||||||
if (mode != MovePlayerPacket.Mode.NORMAL)
|
if (mode != MovePlayerPacket.Mode.NORMAL)
|
||||||
return true;
|
return true;
|
||||||
|
@ -155,7 +152,53 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||||
return true;
|
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...
|
// Gravity might need to be reset...
|
||||||
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
|
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
|
||||||
entityDataPacket.setRuntimeEntityId(entity.getGeyserId());
|
entityDataPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||||
|
@ -166,7 +209,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||||
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
|
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||||
movePlayerPacket.setPosition(entity.getPosition());
|
movePlayerPacket.setPosition(entity.getPosition());
|
||||||
movePlayerPacket.setRotation(entity.getBedrockRotation());
|
movePlayerPacket.setRotation(entity.getBedrockRotation());
|
||||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.RESPAWN);
|
movePlayerPacket.setMode(MovePlayerPacket.Mode.NORMAL);
|
||||||
session.sendUpstreamPacket(movePlayerPacket);
|
session.sendUpstreamPacket(movePlayerPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,11 +217,15 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||||
if (session.isClosed()) return;
|
if (session.isClosed()) return;
|
||||||
PlayerEntity entity = session.getPlayerEntity();
|
PlayerEntity entity = session.getPlayerEntity();
|
||||||
// Recalculate in case something else changed position
|
// Recalculate in case something else changed position
|
||||||
Vector3d position = adjustBedrockPosition(entity.getPosition(), entity.isOnGround());
|
Vector3d position = adjustBedrockPosition(session, entity.getPosition(), entity.isOnGround());
|
||||||
ClientPlayerPositionPacket packet = new ClientPlayerPositionPacket(session.getPlayerEntity().isOnGround(),
|
// A null return value cancels the packet
|
||||||
position.getX(), position.getY(), position.getZ());
|
if (position != null) {
|
||||||
session.sendDownstreamPacket(packet);
|
ClientPlayerPositionPacket packet = new ClientPlayerPositionPacket(session.getPlayerEntity().isOnGround(),
|
||||||
|
position.getX(), position.getY(), position.getZ());
|
||||||
|
session.sendDownstreamPacket(packet);
|
||||||
|
}
|
||||||
session.setMovementSendIfIdle(session.getConnector().getGeneralThreadPool().schedule(() -> sendPositionIfIdle(session),
|
session.setMovementSendIfIdle(session.getConnector().getGeneralThreadPool().schedule(() -> sendPositionIfIdle(session),
|
||||||
3, TimeUnit.SECONDS));
|
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.GameRuleData;
|
||||||
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
|
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
|
||||||
import com.nukkitx.protocol.bedrock.packet.*;
|
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.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
|
|
@ -25,16 +25,14 @@
|
||||||
|
|
||||||
package org.geysermc.connector.network.translators.java.entity;
|
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.Entity;
|
||||||
import org.geysermc.connector.entity.PlayerEntity;
|
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
import org.geysermc.connector.utils.EntityUtils;
|
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)
|
@Translator(packet = ServerEntityEffectPacket.class)
|
||||||
public class JavaEntityEffectTranslator extends PacketTranslator<ServerEntityEffectPacket> {
|
public class JavaEntityEffectTranslator extends PacketTranslator<ServerEntityEffectPacket> {
|
||||||
|
|
||||||
|
@ -43,7 +41,7 @@ public class JavaEntityEffectTranslator extends PacketTranslator<ServerEntityEff
|
||||||
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
|
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
|
||||||
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
|
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
|
||||||
entity = session.getPlayerEntity();
|
entity = session.getPlayerEntity();
|
||||||
((PlayerEntity) entity).getEffectCache().addEffect(packet.getEffect(), packet.getAmplifier());
|
session.getEffectCache().addEffect(packet.getEffect(), packet.getAmplifier());
|
||||||
}
|
}
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -25,16 +25,14 @@
|
||||||
|
|
||||||
package org.geysermc.connector.network.translators.java.entity;
|
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.Entity;
|
||||||
import org.geysermc.connector.entity.PlayerEntity;
|
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
import org.geysermc.connector.utils.EntityUtils;
|
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)
|
@Translator(packet = ServerEntityRemoveEffectPacket.class)
|
||||||
public class JavaEntityRemoveEffectTranslator extends PacketTranslator<ServerEntityRemoveEffectPacket> {
|
public class JavaEntityRemoveEffectTranslator extends PacketTranslator<ServerEntityRemoveEffectPacket> {
|
||||||
|
|
||||||
|
@ -43,7 +41,7 @@ public class JavaEntityRemoveEffectTranslator extends PacketTranslator<ServerEnt
|
||||||
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
|
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
|
||||||
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
|
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
|
||||||
entity = session.getPlayerEntity();
|
entity = session.getPlayerEntity();
|
||||||
((PlayerEntity) entity).getEffectCache().removeEffect(packet.getEffect());
|
session.getEffectCache().removeEffect(packet.getEffect());
|
||||||
}
|
}
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -26,19 +26,11 @@
|
||||||
package org.geysermc.connector.network.translators.java.entity.player;
|
package org.geysermc.connector.network.translators.java.entity.player;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerAbilitiesPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerAbilitiesPacket;
|
||||||
import com.nukkitx.protocol.bedrock.data.AdventureSetting;
|
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||||
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.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@Translator(packet = ServerPlayerAbilitiesPacket.class)
|
@Translator(packet = ServerPlayerAbilitiesPacket.class)
|
||||||
public class JavaPlayerAbilitiesTranslator extends PacketTranslator<ServerPlayerAbilitiesPacket> {
|
public class JavaPlayerAbilitiesTranslator extends PacketTranslator<ServerPlayerAbilitiesPacket> {
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
package org.geysermc.connector.network.translators.java.entity.player;
|
package org.geysermc.connector.network.translators.java.entity.player;
|
||||||
|
|
||||||
import org.geysermc.connector.GeyserConnector;
|
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.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
|
|
@ -32,7 +32,7 @@ import com.nukkitx.math.vector.Vector3f;
|
||||||
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.RespawnPacket;
|
import com.nukkitx.protocol.bedrock.packet.RespawnPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
|
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.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.session.cache.TeleportCache;
|
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
|
// Ignore certain move correction packets for smoother movement
|
||||||
// These are never relative
|
// 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 xDis = Math.abs(entity.getPosition().getX() - packet.getX());
|
||||||
double yDis = entity.getPosition().getY() - packet.getY();
|
double yDis = entity.getPosition().getY() - packet.getY();
|
||||||
double zDis = Math.abs(entity.getPosition().getZ() - packet.getZ());
|
double zDis = Math.abs(entity.getPosition().getZ() - packet.getZ());
|
||||||
|
@ -111,13 +112,16 @@ public class JavaPlayerPositionRotationTranslator extends PacketTranslator<Serve
|
||||||
double newZ = packet.getZ() +
|
double newZ = packet.getZ() +
|
||||||
(packet.getRelative().contains(PositionElement.Z) ? entity.getPosition().getZ() : 0);
|
(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);
|
(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);
|
(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()));
|
session.addTeleport(new TeleportCache(newX, newY, newZ, newPitch, newYaw, packet.getTeleportId()));
|
||||||
entity.moveAbsolute(session, Vector3f.from(newX, newY, newZ), (float) newYaw, (float) newPitch, true, true);
|
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.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.ServerSpawnPlayerPacket;
|
||||||
import com.nukkitx.math.vector.Vector3f;
|
import com.nukkitx.math.vector.Vector3f;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
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.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
|
|
@ -38,7 +38,7 @@ import com.nukkitx.protocol.bedrock.data.GameRuleData;
|
||||||
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
|
||||||
import com.nukkitx.protocol.bedrock.packet.*;
|
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.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
|
|
@ -71,6 +71,9 @@ public class BlockTranslator {
|
||||||
public static final Int2BooleanMap JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND = new Int2BooleanOpenHashMap();
|
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<>();
|
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
|
* 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());
|
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);
|
JAVA_ID_BLOCK_MAP.put(javaId, javaRuntimeId);
|
||||||
|
|
||||||
// Used for adding all "special" Java block states to block state map
|
// 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);
|
return calculateBreakTime(blockHardness, toolTier, canHarvestWithHand, correctTool, toolType, isWoolBlock, isCobweb, toolEfficiencyLevel, hasteLevel, miningFatigueLevel, false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasteLevel = session.getPlayerEntity().getEffectCache().getEffectLevel(Effect.FASTER_DIG);
|
hasteLevel = session.getEffectCache().getEffectLevel(Effect.FASTER_DIG);
|
||||||
miningFatigueLevel = session.getPlayerEntity().getEffectCache().getEffectLevel(Effect.SLOWER_DIG);
|
miningFatigueLevel = session.getEffectCache().getEffectLevel(Effect.SLOWER_DIG);
|
||||||
|
|
||||||
boolean isInWater = session.getConnector().getConfig().isCacheChunks()
|
boolean isInWater = session.getConnector().getConfig().isCacheChunks()
|
||||||
&& BlockTranslator.getBedrockBlockId(session.getConnector().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt())) == BlockTranslator.BEDROCK_WATER_ID;
|
&& 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.setSpawned(false);
|
||||||
session.setLastChunkPosition(null);
|
session.setLastChunkPosition(null);
|
||||||
|
|
||||||
for (Effect effect : session.getPlayerEntity().getEffectCache().getEntityEffects().keySet()) {
|
for (Effect effect : session.getEffectCache().getEntityEffects().keySet()) {
|
||||||
MobEffectPacket mobEffectPacket = new MobEffectPacket();
|
MobEffectPacket mobEffectPacket = new MobEffectPacket();
|
||||||
mobEffectPacket.setEvent(MobEffectPacket.Event.REMOVE);
|
mobEffectPacket.setEvent(MobEffectPacket.Event.REMOVE);
|
||||||
mobEffectPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
|
mobEffectPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
|
||||||
|
@ -90,7 +90,7 @@ public class DimensionUtils {
|
||||||
session.sendUpstreamPacket(mobEffectPacket);
|
session.sendUpstreamPacket(mobEffectPacket);
|
||||||
}
|
}
|
||||||
// Effects are re-sent from server
|
// Effects are re-sent from server
|
||||||
session.getPlayerEntity().getEffectCache().getEntityEffects().clear();
|
session.getEffectCache().getEntityEffects().clear();
|
||||||
|
|
||||||
//let java server handle portal travel sound
|
//let java server handle portal travel sound
|
||||||
StopSoundPacket stopSoundPacket = new StopSoundPacket();
|
StopSoundPacket stopSoundPacket = new StopSoundPacket();
|
||||||
|
|
|
@ -35,7 +35,7 @@ import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.common.AuthType;
|
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.GeyserSession;
|
||||||
import org.geysermc.connector.network.session.auth.BedrockClientData;
|
import org.geysermc.connector.network.session.auth.BedrockClientData;
|
||||||
|
|
||||||
|
|
|
@ -94,13 +94,16 @@ show-cooldown: true
|
||||||
# default-locale: en_us
|
# default-locale: en_us
|
||||||
|
|
||||||
# Configures if chunk caching should be enabled or not. This keeps an individual
|
# 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
|
# record of each block the client loads in. This feature does allow for a few things
|
||||||
# things such as block break animations to show up in creative mode and among others,
|
# such as more accurate movement that causes less problems with anticheat (meaning
|
||||||
# it is HIGHLY recommended you disable this on a production environment as it can eat
|
# you're less likely to be banned) and allows block break animations to show up in
|
||||||
# up a lot of RAM. However, when using the Spigot version of Geyser, support for features
|
# creative mode (and other features). Although this increases RAM usage, it likely
|
||||||
# or implementations this allows is automatically enabled without the additional caching as
|
# won't have much of an effect for the vast majority of people. However, if you're
|
||||||
# Geyser has direct access to the server itself.
|
# running out of RAM or are in a RAM-sensitive environment, you may want to disable
|
||||||
cache-chunks: false
|
# 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.
|
# 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)
|
# A value of 0 is disabled. (Default: 0)
|
||||||
|
@ -148,4 +151,8 @@ enable-proxy-connections: false
|
||||||
# 1400 is the default.
|
# 1400 is the default.
|
||||||
# mtu: 1400
|
# 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
|
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