forked from GeyserMC/Geyser
Merge branch 'master' into sound-master
This commit is contained in:
commit
1fbb755d26
25 changed files with 498 additions and 89 deletions
|
@ -25,6 +25,7 @@
|
|||
|
||||
package org.geysermc.platform.bukkit;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
|
@ -56,6 +57,15 @@ public class GeyserBukkitPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
saveConfig();
|
||||
}
|
||||
|
||||
// Don't change the ip if its listening on all interfaces
|
||||
// By default this should be 127.0.0.1 but may need to be changed in some circumstances
|
||||
if (!Bukkit.getIp().equals("0.0.0.0")) {
|
||||
getConfig().set("remote.address", Bukkit.getIp());
|
||||
}
|
||||
|
||||
getConfig().set("remote.port", Bukkit.getPort());
|
||||
saveConfig();
|
||||
|
||||
this.geyserLogger = new GeyserBukkitLogger(getLogger(), geyserConfig.isDebugMode());
|
||||
this.connector = GeyserConnector.start(PlatformType.BUKKIT, this);
|
||||
|
||||
|
|
|
@ -25,11 +25,11 @@
|
|||
|
||||
package org.geysermc.platform.bungeecord;
|
||||
|
||||
import net.md_5.bungee.api.config.ListenerInfo;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import net.md_5.bungee.config.ConfigurationProvider;
|
||||
import net.md_5.bungee.config.YamlConfiguration;
|
||||
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
||||
|
@ -40,6 +40,7 @@ import org.geysermc.platform.bungeecord.command.GeyserBungeeCommandManager;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.file.Files;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
@ -81,8 +82,31 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||
|
||||
this.geyserConfig = new GeyserBungeeConfiguration(getDataFolder(), configuration);
|
||||
|
||||
boolean configHasChanged = false;
|
||||
|
||||
if (getProxy().getConfig().getListeners().size() == 1) {
|
||||
ListenerInfo listener = getProxy().getConfig().getListeners().toArray(new ListenerInfo[0])[0];
|
||||
|
||||
InetSocketAddress javaAddr = listener.getHost();
|
||||
|
||||
// Don't change the ip if its listening on all interfaces
|
||||
// By default this should be 127.0.0.1 but may need to be changed in some circumstances
|
||||
if (!javaAddr.getHostString().equals("0.0.0.0")) {
|
||||
configuration.set("remote.address", javaAddr.getHostString());
|
||||
}
|
||||
|
||||
configuration.set("remote.port", javaAddr.getPort());
|
||||
|
||||
configHasChanged = true;
|
||||
}
|
||||
|
||||
if (geyserConfig.getMetrics().getUniqueId().equals("generateduuid")) {
|
||||
configuration.set("metrics.uuid", UUID.randomUUID().toString());
|
||||
|
||||
configHasChanged = true;
|
||||
}
|
||||
|
||||
if (configHasChanged) {
|
||||
try {
|
||||
ConfigurationProvider.getProvider(YamlConfiguration.class).save(configuration, new File(getDataFolder(), "config.yml"));
|
||||
} catch (IOException ex) {
|
||||
|
|
|
@ -34,9 +34,7 @@ import org.geysermc.connector.GeyserConfiguration;
|
|||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
public class GeyserSpongeConfiguration implements GeyserConfiguration {
|
||||
|
||||
|
@ -60,7 +58,8 @@ public class GeyserSpongeConfiguration implements GeyserConfiguration {
|
|||
if (node.getNode("userAuths").getValue() == null)
|
||||
return;
|
||||
|
||||
for (String key : (List<String>) node.getNode("userAuths").getValue()) {
|
||||
List<String> userAuths = new ArrayList<String>(((LinkedHashMap)node.getNode("userAuths").getValue()).keySet());
|
||||
for (String key : userAuths) {
|
||||
userAuthInfo.put(key, new SpongeUserAuthenticationInfo(key));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,13 +26,12 @@
|
|||
package org.geysermc.platform.sponge;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.loader.ConfigurationLoader;
|
||||
import ninja.leaping.configurate.yaml.YAMLConfigurationLoader;
|
||||
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
||||
import org.geysermc.connector.command.CommandManager;
|
||||
import org.geysermc.connector.utils.FileUtils;
|
||||
import org.geysermc.platform.sponge.command.GeyserSpongeCommandExecutor;
|
||||
|
@ -47,6 +46,7 @@ import org.spongepowered.api.plugin.Plugin;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
@Plugin(id = "geyser", name = GeyserConnector.NAME + "-Sponge", version = GeyserConnector.VERSION, url = "https://geysermc.org", authors = "GeyserMC")
|
||||
|
@ -79,14 +79,31 @@ public class GeyserSpongePlugin implements GeyserBootstrap {
|
|||
}
|
||||
|
||||
ConfigurationLoader loader = YAMLConfigurationLoader.builder().setPath(configFile.toPath()).build();
|
||||
ConfigurationNode config;
|
||||
try {
|
||||
this.geyserConfig = new GeyserSpongeConfiguration(configDir, loader.load());
|
||||
config = loader.load();
|
||||
this.geyserConfig = new GeyserSpongeConfiguration(configDir, config);
|
||||
} catch (IOException ex) {
|
||||
logger.warn("Failed to load config.yml!");
|
||||
ex.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigurationNode serverIP = config.getNode("remote").getNode("address");
|
||||
ConfigurationNode serverPort = config.getNode("remote").getNode("port");
|
||||
|
||||
if (Sponge.getServer().getBoundAddress().isPresent()) {
|
||||
InetSocketAddress javaAddr = Sponge.getServer().getBoundAddress().get();
|
||||
|
||||
// Don't change the ip if its listening on all interfaces
|
||||
// By default this should be 127.0.0.1 but may need to be changed in some circumstances
|
||||
if (!javaAddr.getHostString().equals("0.0.0.0")) {
|
||||
serverIP.setValue("127.0.0.1");
|
||||
}
|
||||
|
||||
serverPort.setValue(javaAddr.getPort());
|
||||
}
|
||||
|
||||
this.geyserLogger = new GeyserSpongeLogger(logger, geyserConfig.isDebugMode());
|
||||
this.connector = GeyserConnector.start(PlatformType.SPONGE, this);
|
||||
this.geyserCommandManager = new GeyserSpongeCommandManager(Sponge.getCommandManager(), connector);
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.geysermc.connector.GeyserConfiguration;
|
||||
|
||||
|
@ -89,7 +90,10 @@ public class GeyserVelocityConfiguration implements GeyserConfiguration {
|
|||
@Getter
|
||||
public static class RemoteConfiguration implements IRemoteConfiguration {
|
||||
|
||||
@Setter
|
||||
private String address;
|
||||
|
||||
@Setter
|
||||
private int port;
|
||||
|
||||
private String motd1;
|
||||
|
|
|
@ -33,6 +33,7 @@ import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
|||
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
||||
import com.velocitypowered.api.plugin.Plugin;
|
||||
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
||||
|
@ -43,6 +44,7 @@ import org.slf4j.Logger;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
@Plugin(id = "geyser", name = GeyserConnector.NAME + "-Velocity", version = GeyserConnector.VERSION, url = "https://geysermc.org", authors = "GeyserMC")
|
||||
|
@ -51,6 +53,9 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
|||
@Inject
|
||||
private Logger logger;
|
||||
|
||||
@Inject
|
||||
private ProxyServer server;
|
||||
|
||||
@Inject
|
||||
private CommandManager commandManager;
|
||||
|
||||
|
@ -73,6 +78,16 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
|||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
InetSocketAddress javaAddr = server.getBoundAddress();
|
||||
|
||||
// Don't change the ip if its listening on all interfaces
|
||||
// By default this should be 127.0.0.1 but may need to be changed in some circumstances
|
||||
if (!javaAddr.getHostString().equals("0.0.0.0")) {
|
||||
geyserConfig.getRemote().setAddress(javaAddr.getHostString());
|
||||
}
|
||||
|
||||
geyserConfig.getRemote().setPort(javaAddr.getPort());
|
||||
|
||||
this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode());
|
||||
this.connector = GeyserConnector.start(PlatformType.VELOCITY, this);
|
||||
|
||||
|
|
|
@ -158,11 +158,11 @@ public class Entity {
|
|||
session.getUpstream().sendPacket(moveEntityPacket);
|
||||
}
|
||||
|
||||
public void moveAbsolute(GeyserSession session, Vector3f position, float yaw, float pitch, boolean isOnGround) {
|
||||
moveAbsolute(session, position, Vector3f.from(yaw, pitch, yaw), isOnGround);
|
||||
public void moveAbsolute(GeyserSession session, Vector3f position, float yaw, float pitch, boolean isOnGround, boolean teleported) {
|
||||
moveAbsolute(session, position, Vector3f.from(yaw, pitch, yaw), isOnGround, teleported);
|
||||
}
|
||||
|
||||
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround) {
|
||||
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
|
||||
setPosition(position);
|
||||
setRotation(rotation);
|
||||
|
||||
|
@ -171,7 +171,7 @@ public class Entity {
|
|||
moveEntityPacket.setPosition(position);
|
||||
moveEntityPacket.setRotation(getBedrockRotation());
|
||||
moveEntityPacket.setOnGround(isOnGround);
|
||||
moveEntityPacket.setTeleported(false);
|
||||
moveEntityPacket.setTeleported(teleported);
|
||||
|
||||
session.getUpstream().sendPacket(moveEntityPacket);
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ public class PlayerEntity extends LivingEntity {
|
|||
addPlayerPacket.setUsername(username);
|
||||
addPlayerPacket.setRuntimeEntityId(geyserId);
|
||||
addPlayerPacket.setUniqueEntityId(geyserId);
|
||||
addPlayerPacket.setPosition(position);
|
||||
addPlayerPacket.setPosition(position.clone().sub(0, EntityType.PLAYER.getOffset(), 0));
|
||||
addPlayerPacket.setRotation(getBedrockRotation());
|
||||
addPlayerPacket.setMotion(motion);
|
||||
addPlayerPacket.setHand(hand);
|
||||
|
@ -101,6 +101,8 @@ public class PlayerEntity extends LivingEntity {
|
|||
}
|
||||
|
||||
public void sendPlayer(GeyserSession session) {
|
||||
if(session.getEntityCache().getPlayerEntity(uuid) == null)
|
||||
return;
|
||||
if (getLastSkinUpdate() == -1) {
|
||||
if (playerList) {
|
||||
PlayerListPacket playerList = new PlayerListPacket();
|
||||
|
@ -128,7 +130,7 @@ public class PlayerEntity extends LivingEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround) {
|
||||
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
|
||||
setPosition(position);
|
||||
setRotation(rotation);
|
||||
|
||||
|
@ -137,7 +139,11 @@ public class PlayerEntity extends LivingEntity {
|
|||
movePlayerPacket.setPosition(this.position);
|
||||
movePlayerPacket.setRotation(getBedrockRotation());
|
||||
movePlayerPacket.setOnGround(isOnGround);
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.NORMAL);
|
||||
movePlayerPacket.setMode(teleported ? MovePlayerPacket.Mode.TELEPORT : MovePlayerPacket.Mode.NORMAL);
|
||||
|
||||
if (teleported) {
|
||||
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.UNKNOWN);
|
||||
}
|
||||
|
||||
session.getUpstream().sendPacket(movePlayerPacket);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import com.github.steveice10.mc.auth.exception.request.InvalidCredentialsExcepti
|
|||
import com.github.steveice10.mc.auth.exception.request.RequestException;
|
||||
import com.github.steveice10.mc.protocol.MinecraftProtocol;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket;
|
||||
|
@ -84,8 +85,10 @@ public class GeyserSession implements CommandSender {
|
|||
private final UpstreamSession upstream;
|
||||
private RemoteServer remoteServer;
|
||||
private Client downstream;
|
||||
@Setter private AuthData authData;
|
||||
@Setter private BedrockClientData clientData;
|
||||
@Setter
|
||||
private AuthData authData;
|
||||
@Setter
|
||||
private BedrockClientData clientData;
|
||||
|
||||
private PlayerEntity playerEntity;
|
||||
private PlayerInventory inventory;
|
||||
|
@ -95,6 +98,8 @@ public class GeyserSession implements CommandSender {
|
|||
private InventoryCache inventoryCache;
|
||||
private ScoreboardCache scoreboardCache;
|
||||
private WindowCache windowCache;
|
||||
@Setter
|
||||
private TeleportCache teleportCache;
|
||||
|
||||
private DataCache<Packet> javaPacketCache;
|
||||
|
||||
|
@ -450,4 +455,19 @@ public class GeyserSession implements CommandSender {
|
|||
// startGamePacket.setMovementServerAuthoritative(true);
|
||||
upstream.sendPacket(startGamePacket);
|
||||
}
|
||||
|
||||
public boolean confirmTeleport(Vector3f position) {
|
||||
if (teleportCache != null) {
|
||||
if (!teleportCache.canConfirm(position)) {
|
||||
GeyserConnector.getInstance().getLogger().debug("Unconfirmed Teleport " + teleportCache.getTeleportConfirmId()
|
||||
+ " Ignore movement " + position + " expected " + teleportCache);
|
||||
return false;
|
||||
}
|
||||
int teleportId = teleportCache.getTeleportConfirmId();
|
||||
teleportCache = null;
|
||||
ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(teleportId);
|
||||
getDownstream().getSession().send(teleportConfirmPacket);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
47
connector/src/main/java/org/geysermc/connector/network/session/cache/TeleportCache.java
vendored
Normal file
47
connector/src/main/java/org/geysermc/connector/network/session/cache/TeleportCache.java
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.session.cache;
|
||||
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Data
|
||||
public class TeleportCache {
|
||||
|
||||
private static final double ERROR = 0.2;
|
||||
private static final double ERROR_Y = 0.5;
|
||||
|
||||
private double x, y, z;
|
||||
private int teleportConfirmId;
|
||||
|
||||
public boolean canConfirm(Vector3f position) {
|
||||
return (Math.abs(this.x - position.getX()) < ERROR &&
|
||||
Math.abs(this.y - position.getY()) < ERROR_Y &&
|
||||
Math.abs(this.z - position.getZ()) < ERROR);
|
||||
}
|
||||
}
|
|
@ -126,7 +126,7 @@ public abstract class ItemStackTranslator {
|
|||
|
||||
if (tag instanceof StringTag) {
|
||||
StringTag stringTag = (StringTag) tag;
|
||||
return new com.nukkitx.nbt.tag.StringTag(stringTag.getName(), MessageUtils.getBedrockMessage(Message.fromString(stringTag.getValue())));
|
||||
return new com.nukkitx.nbt.tag.StringTag(stringTag.getName(), stringTag.getValue());
|
||||
}
|
||||
|
||||
if (tag instanceof ListTag) {
|
||||
|
|
|
@ -34,7 +34,6 @@ import org.geysermc.connector.network.translators.PacketTranslator;
|
|||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket;
|
||||
import com.nukkitx.math.GenericMath;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
||||
|
@ -59,23 +58,31 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
|||
return;
|
||||
}
|
||||
|
||||
// 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 = packet.getPosition().getY() - EntityType.PLAYER.getOffset();
|
||||
if (packet.isOnGround()) javaY = Math.ceil(javaY * 2) / 2;
|
||||
|
||||
Vector3f position = Vector3f.from(Double.parseDouble(Float.toString(packet.getPosition().getX())), javaY,
|
||||
Double.parseDouble(Float.toString(packet.getPosition().getZ())));
|
||||
|
||||
if(!session.confirmTeleport(position)){
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isValidMove(session, packet.getMode(), entity.getPosition(), packet.getPosition())) {
|
||||
session.getConnector().getLogger().debug("Recalculating position...");
|
||||
recalculatePosition(session, entity, entity.getPosition());
|
||||
return;
|
||||
}
|
||||
|
||||
double javaY = packet.getPosition().getY() - EntityType.PLAYER.getOffset();
|
||||
if (packet.isOnGround()) javaY = Math.ceil(javaY * 2) / 2;
|
||||
// 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
|
||||
ClientPlayerPositionRotationPacket playerPositionRotationPacket = new ClientPlayerPositionRotationPacket(
|
||||
packet.isOnGround(), Double.parseDouble(Float.toString(packet.getPosition().getX())), javaY, Double.parseDouble(Float.toString(packet.getPosition().getZ())), packet.getRotation().getY(), packet.getRotation().getX()
|
||||
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.setPosition(position);
|
||||
entity.setRotation(rotation);
|
||||
|
||||
/*
|
||||
|
|
|
@ -46,8 +46,6 @@ import java.util.List;
|
|||
|
||||
public class PlayerInventoryTranslator extends InventoryTranslator {
|
||||
|
||||
private static final LongArraySet HAS_RECEIVED_MESSAGE = new LongArraySet();
|
||||
|
||||
public PlayerInventoryTranslator() {
|
||||
super(46);
|
||||
}
|
||||
|
@ -187,11 +185,6 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
|
|||
//crafting grid is not visible in creative mode in java edition
|
||||
for (InventoryActionData action : actions) {
|
||||
if (action.getSource().getContainerId() == ContainerId.CURSOR && (action.getSlot() >= 28 && 31 >= action.getSlot())) {
|
||||
if (!HAS_RECEIVED_MESSAGE.contains(session.getPlayerEntity().getEntityId())) {
|
||||
// TODO: Allow the crafting table to be used with non-standalone versions
|
||||
session.sendMessage("The creative crafting table cannot be used as it's incompatible with Minecraft: Java Edition.");
|
||||
HAS_RECEIVED_MESSAGE.add(session.getPlayerEntity().getEntityId());
|
||||
}
|
||||
updateInventory(session, inventory);
|
||||
InventoryUtils.updateCursor(session);
|
||||
return;
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.geysermc.connector.inventory.Inventory;
|
|||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class BlockInventoryHolder extends InventoryHolder {
|
||||
|
@ -60,7 +61,7 @@ public class BlockInventoryHolder extends InventoryHolder {
|
|||
.intTag("x", position.getX())
|
||||
.intTag("y", position.getY())
|
||||
.intTag("z", position.getZ())
|
||||
.stringTag("CustomName", inventory.getTitle()).buildRootTag();
|
||||
.stringTag("CustomName", LocaleUtils.getLocaleString(inventory.getTitle(), session.getClientData().getLanguageCode())).buildRootTag();
|
||||
BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
|
||||
dataPacket.setData(tag);
|
||||
dataPacket.setBlockPosition(position);
|
||||
|
|
|
@ -91,7 +91,6 @@ public class ItemTranslator {
|
|||
ItemEntry javaItem = getItem(data);
|
||||
|
||||
ItemStack itemStack;
|
||||
|
||||
ItemStackTranslator itemStackTranslator = itemTranslators.get(javaItem.getJavaId());
|
||||
if (itemStackTranslator != null) {
|
||||
itemStack = itemStackTranslator.translateToJava(data, javaItem);
|
||||
|
@ -116,19 +115,21 @@ public class ItemTranslator {
|
|||
|
||||
ItemEntry bedrockItem = getItem(stack);
|
||||
|
||||
if (stack != null && stack.getNbt() != null) {
|
||||
ItemStack itemStack = new ItemStack(stack.getId(), stack.getAmount(), stack.getNbt() != null ? stack.getNbt().clone() : null);
|
||||
|
||||
if (itemStack.getNbt() != null) {
|
||||
for (NbtItemStackTranslator translator : nbtItemTranslators) {
|
||||
if (translator.acceptItem(bedrockItem)) {
|
||||
translator.translateToBedrock(stack.getNbt(), bedrockItem);
|
||||
translator.translateToBedrock(itemStack.getNbt(), bedrockItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ItemStackTranslator itemStackTranslator = itemTranslators.get(bedrockItem.getJavaId());
|
||||
if (itemStackTranslator != null) {
|
||||
return itemStackTranslator.translateToBedrock(stack, bedrockItem);
|
||||
return itemStackTranslator.translateToBedrock(itemStack, bedrockItem);
|
||||
} else {
|
||||
return DEFAULT_TRANSLATOR.translateToBedrock(stack, bedrockItem);
|
||||
return DEFAULT_TRANSLATOR.translateToBedrock(itemStack, bedrockItem);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.item.translators.nbt;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import net.kyori.text.Component;
|
||||
import net.kyori.text.TextComponent;
|
||||
import net.kyori.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.geysermc.connector.network.translators.ItemRemapper;
|
||||
import org.geysermc.connector.network.translators.NbtItemStackTranslator;
|
||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ItemRemapper(priority = -1)
|
||||
public class BasicItemTranslator extends NbtItemStackTranslator {
|
||||
|
||||
@Override
|
||||
public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
if (itemTag.contains("display")) {
|
||||
CompoundTag displayTag = itemTag.get("display");
|
||||
if (displayTag.contains("Name")) {
|
||||
StringTag nameTag = displayTag.get("Name");
|
||||
try {
|
||||
displayTag.put(new StringTag("Name", toBedrockMessage(nameTag)));
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
}
|
||||
|
||||
if (displayTag.contains("Lore")) {
|
||||
ListTag loreTag = displayTag.get("Lore");
|
||||
List<Tag> lore = new ArrayList<>();
|
||||
for (Tag tag : loreTag.getValue()) {
|
||||
if (!(tag instanceof StringTag)) return;
|
||||
try {
|
||||
lore.add(new StringTag("", toBedrockMessage((StringTag) tag)));
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
}
|
||||
displayTag.put(new ListTag("Lore", lore));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
if (itemTag.contains("display")) {
|
||||
CompoundTag displayTag = itemTag.get("display");
|
||||
if (displayTag.contains("Name")) {
|
||||
StringTag nameTag = displayTag.get("Name");
|
||||
displayTag.put(new StringTag("Name", toJavaMessage(nameTag)));
|
||||
}
|
||||
|
||||
if (displayTag.contains("Lore")) {
|
||||
ListTag loreTag = displayTag.get("Lore");
|
||||
List<Tag> lore = new ArrayList<>();
|
||||
for (Tag tag : loreTag.getValue()) {
|
||||
if (!(tag instanceof StringTag)) return;
|
||||
lore.add(new StringTag("", "§r" + toJavaMessage((StringTag) tag)));
|
||||
}
|
||||
displayTag.put(new ListTag("Lore", lore));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String toJavaMessage(StringTag tag) {
|
||||
String message = tag.getValue();
|
||||
if (message == null) return null;
|
||||
if (message.startsWith("§r")) {
|
||||
message = message.replaceFirst("§r", "");
|
||||
}
|
||||
Component component = TextComponent.of(message);
|
||||
return GsonComponentSerializer.INSTANCE.serialize(component);
|
||||
}
|
||||
|
||||
private String toBedrockMessage(StringTag tag) {
|
||||
String message = tag.getValue();
|
||||
if (message == null) return null;
|
||||
TextComponent component = (TextComponent) MessageUtils.phraseJavaMessage(message);
|
||||
String legacy = LegacyComponentSerializer.legacy().serialize(component);
|
||||
if (hasFormatting(LegacyComponentSerializer.legacy().deserialize(legacy))) {
|
||||
return "§r" + legacy;
|
||||
}
|
||||
return legacy;
|
||||
}
|
||||
|
||||
private boolean hasFormatting(Component component) {
|
||||
if (component.hasStyling()) return true;
|
||||
for (Component child : component.children()) {
|
||||
if (hasFormatting(child)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -44,6 +44,6 @@ public class JavaEntityTeleportTranslator extends PacketTranslator<ServerEntityT
|
|||
}
|
||||
if (entity == null) return;
|
||||
|
||||
entity.moveAbsolute(session, Vector3f.from(packet.getX(), packet.getY(), packet.getZ()), packet.getYaw(), packet.getPitch(), packet.isOnGround());
|
||||
entity.moveAbsolute(session, Vector3f.from(packet.getX(), packet.getY(), packet.getZ()), packet.getYaw(), packet.getPitch(), packet.isOnGround(), false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,13 +25,6 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.java.entity.player;
|
||||
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.ChunkUtils;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerPositionRotationPacket;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
|
@ -40,13 +33,20 @@ import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
|||
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.RespawnPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.session.cache.TeleportCache;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.ChunkUtils;
|
||||
|
||||
@Translator(packet = ServerPlayerPositionRotationPacket.class)
|
||||
public class JavaPlayerPositionRotationTranslator extends PacketTranslator<ServerPlayerPositionRotationPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(ServerPlayerPositionRotationPacket packet, GeyserSession session) {
|
||||
Entity entity = session.getPlayerEntity();
|
||||
PlayerEntity entity = session.getPlayerEntity();
|
||||
if (entity == null)
|
||||
return;
|
||||
|
||||
|
@ -94,18 +94,21 @@ public class JavaPlayerPositionRotationTranslator extends PacketTranslator<Serve
|
|||
}
|
||||
|
||||
session.setSpawned(true);
|
||||
|
||||
if (!packet.getRelative().isEmpty()) {
|
||||
session.setTeleportCache(new TeleportCache(packet.getX(), packet.getY(), packet.getZ(), packet.getTeleportId()));
|
||||
entity.moveRelative(session, packet.getX(), packet.getY() + EntityType.PLAYER.getOffset() + 0.1f, packet.getZ(), packet.getYaw(), packet.getPitch(), true);
|
||||
} else {
|
||||
double xDis = Math.abs(entity.getPosition().getX() - packet.getX());
|
||||
double yDis = entity.getPosition().getY() - packet.getY();
|
||||
double zDis = Math.abs(entity.getPosition().getZ() - packet.getZ());
|
||||
if (xDis > 1.5 || (yDis < 1.45 || yDis > (session.isJumping() ? 4.3 : (session.isSprinting() ? 2.5 : 1.9))) || zDis > 1.5) {
|
||||
entity.moveAbsolute(session, Vector3f.from(packet.getX(), packet.getY(), packet.getZ()), packet.getYaw(), packet.getPitch(), true);
|
||||
}
|
||||
}
|
||||
|
||||
session.setTeleportCache(new TeleportCache(packet.getX(), packet.getY(), packet.getZ(), packet.getTeleportId()));
|
||||
entity.moveAbsolute(session, Vector3f.from(packet.getX(), packet.getY(), packet.getZ()), packet.getYaw(), packet.getPitch(), true, true);
|
||||
} else {
|
||||
ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(packet.getTeleportId());
|
||||
session.getDownstream().getSession().send(teleportConfirmPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,23 +25,21 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.java.entity.spawn;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.ServerSpawnPlayerPacket;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.SkinUtils;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.ServerSpawnPlayerPacket;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
|
||||
@Translator(packet = ServerSpawnPlayerPacket.class)
|
||||
public class JavaSpawnPlayerTranslator extends PacketTranslator<ServerSpawnPlayerPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(ServerSpawnPlayerPacket packet, GeyserSession session) {
|
||||
Vector3f position = Vector3f.from(packet.getX(), packet.getY() - EntityType.PLAYER.getOffset(), packet.getZ());
|
||||
Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
|
||||
Vector3f rotation = Vector3f.from(packet.getYaw(), packet.getPitch(), packet.getYaw());
|
||||
|
||||
PlayerEntity entity = session.getEntityCache().getPlayerEntity(packet.getUuid());
|
||||
|
|
|
@ -43,6 +43,7 @@ public class BlockStateValues {
|
|||
private static final Object2ByteMap<BlockState> BED_COLORS = new Object2ByteOpenHashMap<>();
|
||||
private static final Object2ByteMap<BlockState> SKULL_VARIANTS = new Object2ByteOpenHashMap<>();
|
||||
private static final Object2ByteMap<BlockState> SKULL_ROTATIONS = new Object2ByteOpenHashMap<>();
|
||||
private static final Object2ByteMap<BlockState> SHULKERBOX_DIRECTIONS = new Object2ByteOpenHashMap<>();
|
||||
|
||||
/**
|
||||
* Determines if the block state contains Bedrock block information
|
||||
|
@ -71,13 +72,19 @@ public class BlockStateValues {
|
|||
if (skullRotation != null) {
|
||||
BlockStateValues.SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue());
|
||||
}
|
||||
|
||||
JsonNode shulkerDirection = entry.getValue().get("shulker_direction");
|
||||
if (shulkerDirection != null) {
|
||||
BlockStateValues.SHULKERBOX_DIRECTIONS.put(javaBlockState, (byte) shulkerDirection.intValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Banner colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
||||
* This gives an integer color that Bedrock can use.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return banner color integer or -1 if no color
|
||||
* @return Banner color integer or -1 if no color
|
||||
*/
|
||||
public static int getBannerColor(BlockState state) {
|
||||
if (BANNER_COLORS.containsKey(state)) {
|
||||
|
@ -89,8 +96,9 @@ public class BlockStateValues {
|
|||
/**
|
||||
* Bed colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
||||
* This gives a byte color that Bedrock can use - Bedrock needs a byte in the final tag.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return bed color byte or -1 if no color
|
||||
* @return Bed color byte or -1 if no color
|
||||
*/
|
||||
public static byte getBedColor(BlockState state) {
|
||||
if (BED_COLORS.containsKey(state)) {
|
||||
|
@ -102,8 +110,9 @@ public class BlockStateValues {
|
|||
/**
|
||||
* Skull variations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
||||
* This gives a byte variant ID that Bedrock can use.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return skull variant byte or -1 if no variant
|
||||
* @return Skull variant byte or -1 if no variant
|
||||
*/
|
||||
public static byte getSkullVariant(BlockState state) {
|
||||
if (SKULL_VARIANTS.containsKey(state)) {
|
||||
|
@ -113,9 +122,11 @@ public class BlockStateValues {
|
|||
}
|
||||
|
||||
/**
|
||||
* Skull rotations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
||||
* This gives a byte rotation that Bedrock can use.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return skull rotation value or -1 if no value
|
||||
* @return Skull rotation value or -1 if no value
|
||||
*/
|
||||
public static byte getSkullRotation(BlockState state) {
|
||||
if (SKULL_ROTATIONS.containsKey(state)) {
|
||||
|
@ -124,4 +135,18 @@ public class BlockStateValues {
|
|||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shulker box directions are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
||||
* This gives a byte direction that Bedrock can use.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return Shulker direction value or -1 if no value
|
||||
*/
|
||||
public static byte getShulkerBoxDirection(BlockState state) {
|
||||
if (SHULKERBOX_DIRECTIONS.containsKey(state)) {
|
||||
return SHULKERBOX_DIRECTIONS.getByte(state);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,10 @@ public class BannerBlockEntityTranslator extends BlockEntityTranslator implement
|
|||
List<com.nukkitx.nbt.tag.CompoundTag> tagsList = new ArrayList<>();
|
||||
if (tag.contains("Patterns")) {
|
||||
for (com.github.steveice10.opennbt.tag.builtin.Tag patternTag : patterns.getValue()) {
|
||||
tagsList.add(getPattern((CompoundTag) patternTag));
|
||||
com.nukkitx.nbt.tag.CompoundTag newPatternTag = getPattern((CompoundTag) patternTag);
|
||||
if (newPatternTag != null) {
|
||||
tagsList.add(newPatternTag);
|
||||
}
|
||||
}
|
||||
com.nukkitx.nbt.tag.ListTag<com.nukkitx.nbt.tag.CompoundTag> bedrockPatterns =
|
||||
new com.nukkitx.nbt.tag.ListTag<>("Patterns", com.nukkitx.nbt.tag.CompoundTag.class, tagsList);
|
||||
|
@ -82,10 +85,24 @@ public class BannerBlockEntityTranslator extends BlockEntityTranslator implement
|
|||
return tagBuilder.buildRootTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the Java edition pattern nbt to Bedrock edition, null if the pattern doesn't exist
|
||||
*
|
||||
* @param pattern Java edition pattern nbt
|
||||
* @return The Bedrock edition format pattern nbt
|
||||
*/
|
||||
protected com.nukkitx.nbt.tag.CompoundTag getPattern(CompoundTag pattern) {
|
||||
String patternName = (String) pattern.get("Pattern").getValue();
|
||||
|
||||
// Return null if its the globe pattern as it doesn't exist on bedrock
|
||||
if (patternName.equals("glb")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return CompoundTagBuilder.builder()
|
||||
.intTag("Color", 15 - (int) pattern.get("Color").getValue())
|
||||
.stringTag("Pattern", (String) pattern.get("Pattern").getValue())
|
||||
.stringTag("Pattern", patternName)
|
||||
.buildRootTag();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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.world.block.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.nukkitx.nbt.CompoundTagBuilder;
|
||||
import com.nukkitx.nbt.tag.ByteTag;
|
||||
import com.nukkitx.nbt.tag.Tag;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@BlockEntity(name = "ShulkerBox", delay = false, regex = "shulker_box")
|
||||
public class ShulkerBoxBlockEntityTranslator extends BlockEntityTranslator {
|
||||
|
||||
@Override
|
||||
public List<Tag<?>> translateTag(CompoundTag tag, BlockState blockState) {
|
||||
List<Tag<?>> tags = new ArrayList<>();
|
||||
|
||||
byte direction = BlockStateValues.getShulkerBoxDirection(blockState);
|
||||
// Just in case...
|
||||
if (direction == -1) direction = 1;
|
||||
tags.add(new ByteTag("facing", direction));
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
|
||||
CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder();
|
||||
tagBuilder.byteTag("facing", (byte)1);
|
||||
return tagBuilder.buildRootTag();
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ package org.geysermc.connector.utils;
|
|||
|
||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
|
||||
import com.github.steveice10.mc.protocol.data.message.*;
|
||||
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
@ -121,20 +122,22 @@ public class MessageUtils {
|
|||
}
|
||||
|
||||
public static String getBedrockMessage(Message message) {
|
||||
Component component;
|
||||
if (isMessage(message.getText())) {
|
||||
component = GsonComponentSerializer.INSTANCE.deserialize(message.getText());
|
||||
return getBedrockMessage(message.getText());
|
||||
} else {
|
||||
component = GsonComponentSerializer.INSTANCE.deserialize(message.toJsonString());
|
||||
return getBedrockMessage(message.toJsonString());
|
||||
}
|
||||
return LegacyComponentSerializer.legacy().serialize(component);
|
||||
}
|
||||
|
||||
public static String getBedrockMessage(String message) {
|
||||
Component component = GsonComponentSerializer.INSTANCE.deserialize(message);
|
||||
Component component = phraseJavaMessage(message);
|
||||
return LegacyComponentSerializer.legacy().serialize(component);
|
||||
}
|
||||
|
||||
public static Component phraseJavaMessage(String message) {
|
||||
return GsonComponentSerializer.INSTANCE.deserialize(message);
|
||||
}
|
||||
|
||||
public static String getJavaMessage(String message) {
|
||||
Component component = LegacyComponentSerializer.legacy().deserialize(message);
|
||||
return GsonComponentSerializer.INSTANCE.serialize(component);
|
||||
|
|
|
@ -25,16 +25,22 @@
|
|||
|
||||
package org.geysermc.connector.utils;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.*;
|
||||
|
@ -52,6 +58,7 @@ public class SkinProvider {
|
|||
private static Map<String, Cape> cachedCapes = new ConcurrentHashMap<>();
|
||||
private static Map<String, CompletableFuture<Cape>> requestedCapes = new ConcurrentHashMap<>();
|
||||
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
private static final int CACHE_INTERVAL = 8 * 60 * 1000; // 8 minutes
|
||||
|
||||
public static boolean hasSkinCached(UUID uuid) {
|
||||
|
@ -74,9 +81,10 @@ public class SkinProvider {
|
|||
return CompletableFuture.supplyAsync(() -> {
|
||||
long time = System.currentTimeMillis();
|
||||
|
||||
CapeProvider provider = capeUrl != null ? CapeProvider.MINECRAFT : null;
|
||||
SkinAndCape skinAndCape = new SkinAndCape(
|
||||
getOrDefault(requestSkin(playerId, skinUrl, false), EMPTY_SKIN, 5),
|
||||
getOrDefault(requestCape(capeUrl, false), EMPTY_CAPE, 5)
|
||||
getOrDefault(requestCape(capeUrl, provider, false), EMPTY_CAPE, 5)
|
||||
);
|
||||
|
||||
GeyserConnector.getInstance().getLogger().debug("Took " + (System.currentTimeMillis() - time) + "ms for " + playerId);
|
||||
|
@ -112,11 +120,11 @@ public class SkinProvider {
|
|||
return future;
|
||||
}
|
||||
|
||||
public static CompletableFuture<Cape> requestCape(String capeUrl, boolean newThread) {
|
||||
public static CompletableFuture<Cape> requestCape(String capeUrl, CapeProvider provider, boolean newThread) {
|
||||
if (capeUrl == null || capeUrl.isEmpty()) return CompletableFuture.completedFuture(EMPTY_CAPE);
|
||||
if (requestedCapes.containsKey(capeUrl)) return requestedCapes.get(capeUrl); // already requested
|
||||
|
||||
boolean officialCape = capeUrl.startsWith("https://textures.minecraft.net");
|
||||
boolean officialCape = provider == CapeProvider.MINECRAFT;
|
||||
boolean validCache = (System.currentTimeMillis() - CACHE_INTERVAL) < cachedCapes.getOrDefault(capeUrl, EMPTY_CAPE).getRequestedOn();
|
||||
|
||||
if ((cachedCapes.containsKey(capeUrl) && officialCape) || validCache) {
|
||||
|
@ -126,14 +134,14 @@ public class SkinProvider {
|
|||
|
||||
CompletableFuture<Cape> future;
|
||||
if (newThread) {
|
||||
future = CompletableFuture.supplyAsync(() -> supplyCape(capeUrl), EXECUTOR_SERVICE)
|
||||
future = CompletableFuture.supplyAsync(() -> supplyCape(capeUrl, provider), EXECUTOR_SERVICE)
|
||||
.whenCompleteAsync((cape, throwable) -> {
|
||||
cachedCapes.put(capeUrl, cape);
|
||||
requestedCapes.remove(capeUrl);
|
||||
});
|
||||
requestedCapes.put(capeUrl, future);
|
||||
} else {
|
||||
Cape cape = supplyCape(capeUrl); // blocking
|
||||
Cape cape = supplyCape(capeUrl, provider); // blocking
|
||||
future = CompletableFuture.completedFuture(cape);
|
||||
cachedCapes.put(capeUrl, cape);
|
||||
}
|
||||
|
@ -143,9 +151,9 @@ public class SkinProvider {
|
|||
public static CompletableFuture<Cape> requestUnofficialCape(Cape officialCape, UUID playerId,
|
||||
String username, boolean newThread) {
|
||||
if (officialCape.isFailed() && ALLOW_THIRD_PARTY_CAPES) {
|
||||
for (UnofficalCape cape : UnofficalCape.VALUES) {
|
||||
for (CapeProvider provider : CapeProvider.VALUES) {
|
||||
Cape cape1 = getOrDefault(
|
||||
requestCape(cape.getUrlFor(playerId, username), newThread),
|
||||
requestCape(provider.getUrlFor(playerId, username), provider, newThread),
|
||||
EMPTY_CAPE, 4
|
||||
);
|
||||
if (!cape1.isFailed()) {
|
||||
|
@ -159,15 +167,15 @@ public class SkinProvider {
|
|||
private static Skin supplySkin(UUID uuid, String textureUrl) {
|
||||
byte[] skin = EMPTY_SKIN.getSkinData();
|
||||
try {
|
||||
skin = requestImage(textureUrl, false);
|
||||
skin = requestImage(textureUrl, null);
|
||||
} catch (Exception ignored) {} // just ignore I guess
|
||||
return new Skin(uuid, textureUrl, skin, System.currentTimeMillis(), false);
|
||||
}
|
||||
|
||||
private static Cape supplyCape(String capeUrl) {
|
||||
private static Cape supplyCape(String capeUrl, CapeProvider provider) {
|
||||
byte[] cape = new byte[0];
|
||||
try {
|
||||
cape = requestImage(capeUrl, true);
|
||||
cape = requestImage(capeUrl, provider);
|
||||
} catch (Exception ignored) {} // just ignore I guess
|
||||
|
||||
String[] urlSection = capeUrl.split("/"); // A real url is expected at this stage
|
||||
|
@ -181,11 +189,12 @@ public class SkinProvider {
|
|||
);
|
||||
}
|
||||
|
||||
private static byte[] requestImage(String imageUrl, boolean cape) throws Exception {
|
||||
BufferedImage image = ImageIO.read(new URL(imageUrl));
|
||||
private static byte[] requestImage(String imageUrl, CapeProvider provider) throws Exception {
|
||||
BufferedImage image = downloadImage(imageUrl, provider);
|
||||
GeyserConnector.getInstance().getLogger().debug("Downloaded " + imageUrl);
|
||||
|
||||
if (cape) {
|
||||
// if the requested image is an cape
|
||||
if (provider != null) {
|
||||
image = image.getWidth() > 64 ? scale(image) : image;
|
||||
BufferedImage newImage = new BufferedImage(64, 32, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics g = newImage.createGraphics();
|
||||
|
@ -209,7 +218,25 @@ public class SkinProvider {
|
|||
}
|
||||
}
|
||||
|
||||
private static BufferedImage scale (BufferedImage bufferedImage) {
|
||||
private static BufferedImage downloadImage(String imageUrl, CapeProvider provider) throws IOException {
|
||||
if (provider == CapeProvider.FIVEZIG)
|
||||
return readFiveZigCape(imageUrl);
|
||||
BufferedImage image = ImageIO.read(new URL(imageUrl));
|
||||
if (image == null) throw new NullPointerException();
|
||||
return image;
|
||||
}
|
||||
|
||||
private static BufferedImage readFiveZigCape(String url) throws IOException {
|
||||
JsonNode element = OBJECT_MAPPER.readTree(WebUtils.getBody(url));
|
||||
if (element != null && element.isObject()) {
|
||||
JsonNode capeElement = element.get("d");
|
||||
if (capeElement == null || capeElement.isNull()) return null;
|
||||
return ImageIO.read(new ByteArrayInputStream(Base64.getDecoder().decode(capeElement.textValue())));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static BufferedImage scale(BufferedImage bufferedImage) {
|
||||
BufferedImage resized = new BufferedImage(bufferedImage.getWidth() / 2, bufferedImage.getHeight() / 2, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g2 = resized.createGraphics();
|
||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
|
@ -262,14 +289,16 @@ public class SkinProvider {
|
|||
* Sorted by 'priority'
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
public enum UnofficalCape {
|
||||
public enum CapeProvider {
|
||||
MINECRAFT,
|
||||
OPTIFINE("http://s.optifine.net/capes/%s.png", CapeUrlType.USERNAME),
|
||||
LABYMOD("http://capes.labymod.net/capes/%s.png", CapeUrlType.UUID_DASHED),
|
||||
FIVEZIG("http://textures.5zig.net/2/%s", CapeUrlType.UUID),
|
||||
LABYMOD("https://www.labymod.net/page/php/getCapeTexture.php?uuid=%s", CapeUrlType.UUID_DASHED),
|
||||
FIVEZIG("https://textures.5zigreborn.eu/profile/%s", CapeUrlType.UUID_DASHED),
|
||||
MINECRAFTCAPES("https://www.minecraftcapes.co.uk/getCape/%s", CapeUrlType.UUID);
|
||||
|
||||
public static final UnofficalCape[] VALUES = values();
|
||||
public static final CapeProvider[] VALUES = Arrays.copyOfRange(values(), 1, 5);
|
||||
private String url;
|
||||
private CapeUrlType type;
|
||||
|
||||
|
|
|
@ -155,7 +155,7 @@ public class SkinUtils {
|
|||
cape = SkinProvider.getOrDefault(SkinProvider.requestUnofficialCape(
|
||||
cape, entity.getUuid(),
|
||||
entity.getUsername(), false
|
||||
), SkinProvider.EMPTY_CAPE, SkinProvider.UnofficalCape.VALUES.length * 3);
|
||||
), SkinProvider.EMPTY_CAPE, SkinProvider.CapeProvider.VALUES.length * 3);
|
||||
}
|
||||
|
||||
if (entity.getLastSkinUpdate() < skin.getRequestedOn()) {
|
||||
|
|
Loading…
Reference in a new issue