World border improvements (#488)

This commit is contained in:
Camotoy 2020-06-04 16:56:16 -04:00 committed by GitHub
parent 50c4c0b2d8
commit 498b058aba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
243 changed files with 7471 additions and 1898 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.world.particle.Particle;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.effect.EffectRegistry;
public class AreaEffectCloudEntity extends Entity {
public AreaEffectCloudEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
// Without this the cloud doesn't appear,
metadata.put(EntityData.AREA_EFFECT_CLOUD_DURATION, 600);
// This disabled client side shrink of the cloud
metadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS_PER_TICK, 0.0f);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7) {
metadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS, (float) entityMetadata.getValue());
metadata.put(EntityData.BOUNDING_BOX_WIDTH, 2.0f * (float) entityMetadata.getValue());
} else if (entityMetadata.getId() == 10) {
Particle particle = (Particle) entityMetadata.getValue();
metadata.put(EntityData.AREA_EFFECT_CLOUD_PARTICLE_ID, EffectRegistry.getParticleString(particle.getType()));
} else if (entityMetadata.getId() == 8) {
metadata.put(EntityData.POTION_COLOR, entityMetadata.getValue());
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View file

@ -0,0 +1,138 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import java.util.concurrent.TimeUnit;
public class BoatEntity extends Entity {
private boolean isPaddlingLeft;
private float paddleTimeLeft;
private boolean isPaddlingRight;
private float paddleTimeRight;
// Looks too fast and too choppy with 0.1f, which is how I believe the Microsoftian client handles it
private final float ROWING_SPEED = 0.05f;
public BoatEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position.add(0d, entityType.getOffset(), 0d), motion, rotation.add(0, 0, 90));
}
@Override
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
// Rotation is basically only called when entering/exiting a boat.
// We don't include the rotation (y) as it causes the boat to appear sideways
super.moveAbsolute(session, position.add(0d, this.entityType.getOffset(), 0d), Vector3f.from(0, 0, rotation.getZ() + 90), isOnGround, teleported);
}
@Override
public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) {
super.moveRelative(session, relX, relY, relZ, Vector3f.from(0, 0, rotation.getZ()), isOnGround);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
// Time since last hit
if (entityMetadata.getId() == 7) {
metadata.put(EntityData.HURT_TIME, entityMetadata.getValue());
}
// Rocking direction
if (entityMetadata.getId() == 8) {
metadata.put(EntityData.HURT_DIRECTION, entityMetadata.getValue());
}
// 'Health' in Bedrock, damage taken in Java
if (entityMetadata.getId() == 9) {
// Not exactly health but it makes motion in Bedrock
metadata.put(EntityData.HEALTH, 40 - ((int) (float) entityMetadata.getValue()));
}
if (entityMetadata.getId() == 10) {
metadata.put(EntityData.VARIANT, entityMetadata.getValue());
} else if (entityMetadata.getId() == 11) {
isPaddlingLeft = (boolean) entityMetadata.getValue();
if (!isPaddlingLeft) {
metadata.put(EntityData.PADDLE_TIME_LEFT, 0f);
}
else {
// Java sends simply "true" and "false" (is_paddling_left), Bedrock keeps sending packets as you're rowing
// This is an asynchronous method that emulates Bedrock rowing until "false" is sent.
paddleTimeLeft = 0f;
session.getConnector().getGeneralThreadPool().execute(() ->
updateLeftPaddle(session, entityMetadata)
);
}
}
else if (entityMetadata.getId() == 12) {
isPaddlingRight = (boolean) entityMetadata.getValue();
if (!isPaddlingRight) {
metadata.put(EntityData.PADDLE_TIME_RIGHT, 0f);
} else {
paddleTimeRight = 0f;
session.getConnector().getGeneralThreadPool().execute(() ->
updateRightPaddle(session, entityMetadata)
);
}
} else if (entityMetadata.getId() == 13) {
// Possibly - I don't think this does anything?
metadata.put(EntityData.BOAT_BUBBLE_TIME, entityMetadata.getValue());
}
super.updateBedrockMetadata(entityMetadata, session);
}
public void updateLeftPaddle(GeyserSession session, EntityMetadata entityMetadata) {
if (isPaddlingLeft) {
paddleTimeLeft += ROWING_SPEED;
metadata.put(EntityData.PADDLE_TIME_LEFT, paddleTimeLeft);
super.updateBedrockMetadata(entityMetadata, session);
session.getConnector().getGeneralThreadPool().schedule(() ->
updateLeftPaddle(session, entityMetadata),
100,
TimeUnit.MILLISECONDS
);
}}
public void updateRightPaddle(GeyserSession session, EntityMetadata entityMetadata) {
if (isPaddlingRight) {
paddleTimeRight += ROWING_SPEED;
metadata.put(EntityData.PADDLE_TIME_RIGHT, paddleTimeRight);
super.updateBedrockMetadata(entityMetadata, session);
session.getConnector().getGeneralThreadPool().schedule(() ->
updateRightPaddle(session, entityMetadata),
100,
TimeUnit.MILLISECONDS
);
}}
}

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
/**
* This class is used as a base for minecarts with a default block to display like furnaces and spawners
*/
public class DefaultBlockMinecartEntity extends MinecartEntity {
public int customBlock = 0;
public int customBlockOffset = 0;
public boolean showCustomBlock = false;
public DefaultBlockMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
updateDefaultBlockMetadata();
metadata.put(EntityData.HAS_DISPLAY, (byte) 1);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
// Custom block
if (entityMetadata.getId() == 10) {
customBlock = (int) entityMetadata.getValue();
if (showCustomBlock) {
metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(customBlock));
}
}
// Custom block offset
if (entityMetadata.getId() == 11) {
customBlockOffset = (int) entityMetadata.getValue();
if (showCustomBlock) {
metadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset);
}
}
// If the custom block should be enabled
if (entityMetadata.getId() == 12) {
if ((boolean) entityMetadata.getValue()) {
showCustomBlock = true;
metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.getBedrockBlockId(customBlock));
metadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset);
} else {
showCustomBlock = false;
updateDefaultBlockMetadata();
}
}
super.updateBedrockMetadata(entityMetadata, session);
}
public void updateDefaultBlockMetadata() { }
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,139 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.nbt.CompoundTagBuilder;
import com.nukkitx.protocol.bedrock.data.EntityData;
import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.FireworkColor;
import org.geysermc.connector.utils.MathUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.OptionalInt;
public class FireworkEntity extends Entity {
public FireworkEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7) {
ItemStack item = (ItemStack) entityMetadata.getValue();
CompoundTag tag = item.getNbt();
if (tag == null) {
return;
}
CompoundTag fireworks = tag.get("Fireworks");
CompoundTagBuilder fireworksBuilder = CompoundTagBuilder.builder();
if (fireworks.get("Flight") != null) {
fireworksBuilder.byteTag("Flight", MathUtils.convertByte(fireworks.get("Flight").getValue()));
}
List<com.nukkitx.nbt.tag.CompoundTag> explosions = new ArrayList<>();
if (fireworks.get("Explosions") != null) {
for (Tag effect : ((ListTag) fireworks.get("Explosions")).getValue()) {
CompoundTag effectData = (CompoundTag) effect;
CompoundTagBuilder effectBuilder = CompoundTagBuilder.builder();
if (effectData.get("Type") != null) {
effectBuilder.byteTag("FireworkType", MathUtils.convertByte(effectData.get("Type").getValue()));
}
if (effectData.get("Colors") != null) {
int[] oldColors = (int[]) effectData.get("Colors").getValue();
byte[] colors = new byte[oldColors.length];
int i = 0;
for (int color : oldColors) {
colors[i++] = FireworkColor.fromJavaID(color).getBedrockID();
}
effectBuilder.byteArrayTag("FireworkColor", colors);
}
if (effectData.get("FadeColors") != null) {
int[] oldColors = (int[]) effectData.get("FadeColors").getValue();
byte[] colors = new byte[oldColors.length];
int i = 0;
for (int color : oldColors) {
colors[i++] = FireworkColor.fromJavaID(color).getBedrockID();
}
effectBuilder.byteArrayTag("FireworkFade", colors);
}
if (effectData.get("Trail") != null) {
effectBuilder.byteTag("FireworkTrail", MathUtils.convertByte(effectData.get("Trail").getValue()));
}
if (effectData.get("Flicker") != null) {
effectBuilder.byteTag("FireworkFlicker", MathUtils.convertByte(effectData.get("Flicker").getValue()));
}
explosions.add(effectBuilder.buildRootTag());
}
}
fireworksBuilder.tag(new com.nukkitx.nbt.tag.ListTag<>("Explosions", com.nukkitx.nbt.tag.CompoundTag.class, explosions));
metadata.put(EntityData.DISPLAY_ITEM, CompoundTagBuilder.builder().tag(fireworksBuilder.build("Fireworks")).buildRootTag());
} else if (entityMetadata.getId() == 8 && !entityMetadata.getValue().equals(OptionalInt.empty()) && ((OptionalInt) entityMetadata.getValue()).getAsInt() == session.getPlayerEntity().getEntityId()) {
//Checks if the firework has an entity ID (used when a player is gliding) and checks to make sure the player that is gliding is the one getting sent the packet or else every player near the gliding player will boost too.
PlayerEntity entity = session.getPlayerEntity();
float yaw = entity.getRotation().getX();
float pitch = entity.getRotation().getY();
//Uses math from NukkitX
entity.setMotion(Vector3f.from(
-Math.sin(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2,
-Math.sin(Math.toRadians(pitch)) * 2,
Math.cos(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2));
//Need to update the EntityMotionPacket or else the player won't boost
SetEntityMotionPacket entityMotionPacket = new SetEntityMotionPacket();
entityMotionPacket.setRuntimeEntityId(entity.getGeyserId());
entityMotionPacket.setMotion(entity.getMotion());
session.sendUpstreamPacket(entityMotionPacket);
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*
*/
package org.geysermc.connector.entity;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.type.EntityType;
public class LeashKnotEntity extends Entity {
public LeashKnotEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
// Position is incorrect by default
super(entityId, geyserId, entityType, position.add(0.5f, 0.25f, 0.5f), motion, rotation);
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*
*/
package org.geysermc.connector.entity.living.monster;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityFlag;
import org.geysermc.connector.entity.type.EntityType;
public class ElderGuardianEntity extends GuardianEntity {
public ElderGuardianEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
// Otherwise it just looks like a normal guardian but bigger
metadata.getFlags().setFlag(EntityFlag.ELDER, true);
}
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living.monster;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
public class WitherEntity extends MonsterEntity {
public WitherEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
metadata.put(EntityData.WITHER_AERIAL_ATTACK, (short) 1);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
long targetID = 0;
if (entityMetadata.getId() >= 15 && entityMetadata.getId() <= 17) {
Entity entity = session.getEntityCache().getEntityByJavaId((int) entityMetadata.getValue());
if (entity == null && session.getPlayerEntity().getEntityId() == (Integer) entityMetadata.getValue()) {
entity = session.getPlayerEntity();
}
if (entity != null) {
targetID = entity.getGeyserId();
}
}
if (entityMetadata.getId() == 15) {
metadata.put(EntityData.WITHER_TARGET_1, targetID);
} else if (entityMetadata.getId() == 16) {
metadata.put(EntityData.WITHER_TARGET_2, targetID);
} else if (entityMetadata.getId() == 17) {
metadata.put(EntityData.WITHER_TARGET_3, targetID);
} else if (entityMetadata.getId() == 18) {
metadata.put(EntityData.WITHER_INVULNERABLE_TICKS, (int) entityMetadata.getValue());
// Show the shield for the first few seconds of spawning (like Java)
if ((int) entityMetadata.getValue() >= 165) {
metadata.put(EntityData.WITHER_AERIAL_ATTACK, (short) 0);
} else {
metadata.put(EntityData.WITHER_AERIAL_ATTACK, (short) 1);
}
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,49 +1,50 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 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 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.
* 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
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.utils;
package org.geysermc.connector.network.translators.effect;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.steveice10.mc.protocol.data.game.world.particle.ParticleType;
import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.data.SoundEvent;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.NonNull;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.translators.effect.Effect;
import org.geysermc.connector.utils.FileUtils;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class EffectUtils {
/**
* Registry for particles and effects.
*/
public class EffectRegistry {
public static final Map<String, Effect> EFFECTS = new HashMap<>();
public static final Int2ObjectMap<SoundEvent> RECORDS = new Int2ObjectOpenHashMap<>();
@ -57,10 +58,10 @@ public class EffectUtils {
static {
/* Load particles */
InputStream particleStream = Toolbox.getResource("mappings/particles.json");
InputStream particleStream = FileUtils.getResource("mappings/particles.json");
JsonNode particleEntries;
try {
particleEntries = Toolbox.JSON_MAPPER.readTree(particleStream);
particleEntries = GeyserConnector.JSON_MAPPER.readTree(particleStream);
} catch (Exception e) {
throw new AssertionError("Unable to load particle map", e);
}
@ -69,10 +70,10 @@ public class EffectUtils {
while (particlesIterator.hasNext()) {
Map.Entry<String, JsonNode> entry = particlesIterator.next();
try {
setIdentifier(ParticleType.valueOf(entry.getKey().toUpperCase()), LevelEventType.valueOf(entry.getValue().asText().toUpperCase()));
particleTypeMap.put(ParticleType.valueOf(entry.getKey().toUpperCase()), LevelEventType.valueOf(entry.getValue().asText().toUpperCase()));
} catch (IllegalArgumentException e1) {
try {
setIdentifier(ParticleType.valueOf(entry.getKey().toUpperCase()), entry.getValue().asText());
particleStringMap.put(ParticleType.valueOf(entry.getKey().toUpperCase()), entry.getValue().asText());
GeyserConnector.getInstance().getLogger().debug("Force to map particle "
+ entry.getKey()
+ "=>"
@ -85,10 +86,10 @@ public class EffectUtils {
}
/* Load effects */
InputStream effectsStream = Toolbox.getResource("mappings/effects.json");
InputStream effectsStream = FileUtils.getResource("mappings/effects.json");
JsonNode effects;
try {
effects = Toolbox.JSON_MAPPER.readTree(effectsStream);
effects = GeyserConnector.JSON_MAPPER.readTree(effectsStream);
} catch (Exception e) {
throw new AssertionError("Unable to load effects mappings", e);
}
@ -112,14 +113,6 @@ public class EffectUtils {
}
}
public static void setIdentifier(ParticleType type, LevelEventType identifier) {
particleTypeMap.put(type, identifier);
}
public static void setIdentifier(ParticleType type, String identifier) {
particleStringMap.put(type, identifier);
}
public static LevelEventType getParticleLevelEventType(@NonNull ParticleType type) {
return particleTypeMap.getOrDefault(type, null);
}
@ -127,5 +120,4 @@ public class EffectUtils {
public static String getParticleString(@NonNull ParticleType type){
return particleStringMap.getOrDefault(type, null);
}
}

View file

@ -108,7 +108,7 @@ public class AnvilInventoryTranslator extends BlockInventoryTranslator {
rename = "";
}
ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename);
session.getDownstream().getSession().send(renameItemPacket);
session.sendDownstreamPacket(renameItemPacket);
}
if (anvilResult != null) {
//client will send another packet to grab anvil output
@ -138,7 +138,7 @@ public class AnvilInventoryTranslator extends BlockInventoryTranslator {
rename = "";
}
ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename);
session.getDownstream().getSession().send(renameItemPacket);
session.sendDownstreamPacket(renameItemPacket);
}
}
super.updateSlot(session, inventory, slot);

View file

@ -44,7 +44,7 @@ public class BrewingInventoryTranslator extends BlockInventoryTranslator {
dataPacket.setWindowId((byte) inventory.getId());
dataPacket.setProperty(ContainerSetDataPacket.BREWING_STAND_FUEL_TOTAL);
dataPacket.setValue(20);
session.getUpstream().sendPacket(dataPacket);
session.sendUpstreamPacket(dataPacket);
}
@Override
@ -62,7 +62,7 @@ public class BrewingInventoryTranslator extends BlockInventoryTranslator {
return;
}
dataPacket.setValue(value);
session.getUpstream().sendPacket(dataPacket);
session.sendUpstreamPacket(dataPacket);
}
@Override

View file

@ -0,0 +1,69 @@
/*
* 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.inventory;
import com.nukkitx.protocol.bedrock.data.InventoryActionData;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.updater.ChestInventoryUpdater;
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
import org.geysermc.connector.utils.InventoryUtils;
import java.util.List;
public abstract class ChestInventoryTranslator extends BaseInventoryTranslator {
private final InventoryUpdater updater;
public ChestInventoryTranslator(int size, int paddedSize) {
super(size);
this.updater = new ChestInventoryUpdater(paddedSize);
}
@Override
public void updateInventory(GeyserSession session, Inventory inventory) {
updater.updateInventory(this, session, inventory);
}
@Override
public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
updater.updateSlot(this, session, inventory, slot);
}
@Override
public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
for (InventoryActionData action : actions) {
if (action.getSource().getContainerId() == inventory.getId()) {
if (action.getSlot() >= size) {
updateInventory(session, inventory);
InventoryUtils.updateCursor(session);
return;
}
}
}
super.translateActions(session, inventory, actions);
}
}

View file

@ -59,7 +59,7 @@ public class CraftingInventoryTranslator extends BaseInventoryTranslator {
containerOpenPacket.setType((byte) ContainerType.WORKBENCH.id());
containerOpenPacket.setBlockPosition(inventory.getHolderPosition());
containerOpenPacket.setUniqueEntityId(inventory.getHolderId());
session.getUpstream().sendPacket(containerOpenPacket);
session.sendUpstreamPacket(containerOpenPacket);
}
@Override
@ -92,7 +92,10 @@ public class CraftingInventoryTranslator extends BaseInventoryTranslator {
@Override
public int javaSlotToBedrock(int slot) {
return slot == 0 ? 50 : slot + 31;
if (slot < size) {
return slot == 0 ? 50 : slot + 31;
}
return super.javaSlotToBedrock(slot);
}
@Override

View file

@ -39,15 +39,13 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.network.translators.inventory.updater.ChestInventoryUpdater;
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
public class DoubleChestInventoryTranslator extends BaseInventoryTranslator {
public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
private final int blockId;
private final InventoryUpdater updater;
public DoubleChestInventoryTranslator(int size) {
super(size);
super(size, 54);
BlockState javaBlockState = BlockTranslator.getJavaBlockState("minecraft:chest[facing=north,type=single,waterlogged=false]");
this.blockId = BlockTranslator.getBedrockBlockId(javaBlockState);
this.updater = new ChestInventoryUpdater(54);
}
@Override
@ -60,7 +58,7 @@ public class DoubleChestInventoryTranslator extends BaseInventoryTranslator {
blockPacket.setBlockPosition(position);
blockPacket.setRuntimeId(blockId);
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
session.getUpstream().sendPacket(blockPacket);
session.sendUpstreamPacket(blockPacket);
CompoundTag tag = CompoundTag.builder()
.stringTag("id", "Chest")
@ -73,14 +71,14 @@ public class DoubleChestInventoryTranslator extends BaseInventoryTranslator {
BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
dataPacket.setData(tag);
dataPacket.setBlockPosition(position);
session.getUpstream().sendPacket(dataPacket);
session.sendUpstreamPacket(dataPacket);
blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(pairPosition);
blockPacket.setRuntimeId(blockId);
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
session.getUpstream().sendPacket(blockPacket);
session.sendUpstreamPacket(blockPacket);
tag = CompoundTag.builder()
.stringTag("id", "Chest")
@ -93,7 +91,7 @@ public class DoubleChestInventoryTranslator extends BaseInventoryTranslator {
dataPacket = new BlockEntityDataPacket();
dataPacket.setData(tag);
dataPacket.setBlockPosition(pairPosition);
session.getUpstream().sendPacket(dataPacket);
session.sendUpstreamPacket(dataPacket);
inventory.setHolderPosition(position);
}
@ -105,7 +103,7 @@ public class DoubleChestInventoryTranslator extends BaseInventoryTranslator {
containerOpenPacket.setType((byte) ContainerType.CONTAINER.id());
containerOpenPacket.setBlockPosition(inventory.getHolderPosition());
containerOpenPacket.setUniqueEntityId(inventory.getHolderId());
session.getUpstream().sendPacket(containerOpenPacket);
session.sendUpstreamPacket(containerOpenPacket);
}
@Override
@ -117,7 +115,7 @@ public class DoubleChestInventoryTranslator extends BaseInventoryTranslator {
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(holderPos);
blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock));
session.getUpstream().sendPacket(blockPacket);
session.sendUpstreamPacket(blockPacket);
holderPos = holderPos.add(Vector3i.UNIT_X);
pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ());
@ -126,16 +124,6 @@ public class DoubleChestInventoryTranslator extends BaseInventoryTranslator {
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(holderPos);
blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock));
session.getUpstream().sendPacket(blockPacket);
}
@Override
public void updateInventory(GeyserSession session, Inventory inventory) {
updater.updateInventory(this, session, inventory);
}
@Override
public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
updater.updateSlot(this, session, inventory, slot);
session.sendUpstreamPacket(blockPacket);
}
}

View file

@ -58,7 +58,7 @@ public class FurnaceInventoryTranslator extends BlockInventoryTranslator {
return;
}
dataPacket.setValue(value);
session.getUpstream().sendPacket(dataPacket);
session.sendUpstreamPacket(dataPacket);
}
@Override

View file

@ -25,15 +25,50 @@
package org.geysermc.connector.network.translators.inventory;
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
import com.nukkitx.protocol.bedrock.data.ContainerType;
import com.nukkitx.protocol.bedrock.data.InventoryActionData;
import lombok.AllArgsConstructor;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@AllArgsConstructor
public abstract class InventoryTranslator {
public static final Map<WindowType, InventoryTranslator> INVENTORY_TRANSLATORS = new HashMap<WindowType, InventoryTranslator>() {
{
put(null, new PlayerInventoryTranslator()); //player inventory
put(WindowType.GENERIC_9X1, new SingleChestInventoryTranslator(9));
put(WindowType.GENERIC_9X2, new SingleChestInventoryTranslator(18));
put(WindowType.GENERIC_9X3, new SingleChestInventoryTranslator(27));
put(WindowType.GENERIC_9X4, new DoubleChestInventoryTranslator(36));
put(WindowType.GENERIC_9X5, new DoubleChestInventoryTranslator(45));
put(WindowType.GENERIC_9X6, new DoubleChestInventoryTranslator(54));
put(WindowType.BREWING_STAND, new BrewingInventoryTranslator());
put(WindowType.ANVIL, new AnvilInventoryTranslator());
put(WindowType.CRAFTING, new CraftingInventoryTranslator());
put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator());
//put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO
InventoryTranslator furnace = new FurnaceInventoryTranslator();
put(WindowType.FURNACE, furnace);
put(WindowType.BLAST_FURNACE, furnace);
put(WindowType.SMOKER, furnace);
InventoryUpdater containerUpdater = new ContainerInventoryUpdater();
put(WindowType.GENERIC_3X3, new BlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER, containerUpdater));
put(WindowType.HOPPER, new BlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, containerUpdater));
put(WindowType.SHULKER_BOX, new BlockInventoryTranslator(27, "minecraft:shulker_box[facing=north]", ContainerType.CONTAINER, containerUpdater));
//put(WindowType.BEACON, new BlockInventoryTranslator(1, "minecraft:beacon", ContainerType.BEACON)); //TODO
}
};
public final int size;
public abstract void prepareInventory(GeyserSession session, Inventory inventory);

View file

@ -34,17 +34,17 @@ import com.nukkitx.protocol.bedrock.data.InventorySource;
import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.Translators;
import org.geysermc.connector.network.translators.inventory.action.InventoryActionDataTranslator;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.utils.InventoryUtils;
import org.geysermc.connector.utils.Toolbox;
import java.util.List;
public class PlayerInventoryTranslator extends InventoryTranslator {
private static final ItemData UNUSUABLE_CRAFTING_SPACE_BLOCK = InventoryUtils.createUnusableSpaceBlock(
"The creative crafting grid is\nunavailable in Java Edition");
public PlayerInventoryTranslator() {
super(46);
@ -59,30 +59,30 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
ItemData[] contents = new ItemData[36];
// Inventory
for (int i = 9; i < 36; i++) {
contents[i] = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i));
contents[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
}
// Hotbar
for (int i = 36; i < 45; i++) {
contents[i - 36] = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i));
contents[i - 36] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
}
inventoryContentPacket.setContents(contents);
session.getUpstream().sendPacket(inventoryContentPacket);
session.sendUpstreamPacket(inventoryContentPacket);
// Armor
InventoryContentPacket armorContentPacket = new InventoryContentPacket();
armorContentPacket.setContainerId(ContainerId.ARMOR);
contents = new ItemData[4];
for (int i = 5; i < 9; i++) {
contents[i - 5] = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i));
contents[i - 5] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
}
armorContentPacket.setContents(contents);
session.getUpstream().sendPacket(armorContentPacket);
session.sendUpstreamPacket(armorContentPacket);
// Offhand
InventoryContentPacket offhandPacket = new InventoryContentPacket();
offhandPacket.setContainerId(ContainerId.OFFHAND);
offhandPacket.setContents(new ItemData[]{Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(45))});
session.getUpstream().sendPacket(offhandPacket);
offhandPacket.setContents(new ItemData[]{ItemTranslator.translateToBedrock(session, inventory.getItem(45))});
session.sendUpstreamPacket(offhandPacket);
}
/**
@ -98,12 +98,12 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
slotPacket.setSlot(i + 27);
if (session.getGameMode() == GameMode.CREATIVE) {
slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, new ItemStack(Toolbox.BARRIER_INDEX)));
slotPacket.setItem(UNUSUABLE_CRAFTING_SPACE_BLOCK);
}else{
slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i)));
slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i)));
}
session.getUpstream().sendPacket(slotPacket);
session.sendUpstreamPacket(slotPacket);
}
}
@ -125,13 +125,13 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
slotPacket.setContainerId(ContainerId.CURSOR);
slotPacket.setSlot(slot + 27);
}
slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(slot)));
session.getUpstream().sendPacket(slotPacket);
slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(slot)));
session.sendUpstreamPacket(slotPacket);
} else if (slot == 45) {
InventoryContentPacket offhandPacket = new InventoryContentPacket();
offhandPacket.setContainerId(ContainerId.OFFHAND);
offhandPacket.setContents(new ItemData[]{Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(slot))});
session.getUpstream().sendPacket(offhandPacket);
offhandPacket.setContents(new ItemData[]{ItemTranslator.translateToBedrock(session, inventory.getItem(slot))});
session.sendUpstreamPacket(offhandPacket);
}
}
@ -201,23 +201,23 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
if (action.getToItem().getId() == 0) {
javaItem = new ItemStack(-1, 0, null);
} else {
javaItem = Translators.getItemTranslator().translateToJava(session, action.getToItem());
javaItem = ItemTranslator.translateToJava(action.getToItem());
}
ClientCreativeInventoryActionPacket creativePacket = new ClientCreativeInventoryActionPacket(javaSlot, javaItem);
session.getDownstream().getSession().send(creativePacket);
session.sendDownstreamPacket(creativePacket);
inventory.setItem(javaSlot, javaItem);
break;
case ContainerId.CURSOR:
if (action.getSlot() == 0) {
session.getInventory().setCursor(Translators.getItemTranslator().translateToJava(session, action.getToItem()));
session.getInventory().setCursor(ItemTranslator.translateToJava(action.getToItem()));
}
break;
case ContainerId.NONE:
if (action.getSource().getType() == InventorySource.Type.WORLD_INTERACTION
&& action.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
javaItem = Translators.getItemTranslator().translateToJava(session, action.getToItem());
javaItem = ItemTranslator.translateToJava(action.getToItem());
ClientCreativeInventoryActionPacket creativeDropPacket = new ClientCreativeInventoryActionPacket(-1, javaItem);
session.getDownstream().getSession().send(creativeDropPacket);
session.sendDownstreamPacket(creativeDropPacket);
}
break;
}

View file

@ -25,11 +25,35 @@
package org.geysermc.connector.network.translators.inventory;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.nukkitx.protocol.bedrock.data.ContainerType;
import org.geysermc.connector.network.translators.inventory.updater.ChestInventoryUpdater;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.holder.BlockInventoryHolder;
import org.geysermc.connector.network.translators.inventory.holder.InventoryHolder;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
public class SingleChestInventoryTranslator extends ChestInventoryTranslator {
private final InventoryHolder holder;
public class SingleChestInventoryTranslator extends BlockInventoryTranslator {
public SingleChestInventoryTranslator(int size) {
super(size, "minecraft:chest[facing=north,type=single,waterlogged=false]", ContainerType.CONTAINER, new ChestInventoryUpdater(27));
super(size, 27);
BlockState javaBlockState = BlockTranslator.getJavaBlockState("minecraft:chest[facing=north,type=single,waterlogged=false]");
this.holder = new BlockInventoryHolder(BlockTranslator.getBedrockBlockId(javaBlockState), ContainerType.CONTAINER);
}
@Override
public void prepareInventory(GeyserSession session, Inventory inventory) {
holder.prepareInventory(this, session, inventory);
}
@Override
public void openInventory(GeyserSession session, Inventory inventory) {
holder.openInventory(this, session, inventory);
}
@Override
public void closeInventory(GeyserSession session, Inventory inventory) {
holder.closeInventory(this, session, inventory);
}
}

View file

@ -104,8 +104,8 @@ class ClickPlan {
break;
}
}
session.getDownstream().getSession().send(clickPacket);
session.getDownstream().getSession().send(new ClientConfirmTransactionPacket(inventory.getId(), actionId, true));
session.sendDownstreamPacket(clickPacket);
session.sendDownstreamPacket(new ClientConfirmTransactionPacket(inventory.getId(), actionId, true));
}
/*if (refresh) {

View file

@ -38,9 +38,9 @@ import com.nukkitx.protocol.bedrock.data.InventorySource;
import com.nukkitx.protocol.bedrock.data.ItemData;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.Translators;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.network.translators.inventory.SlotType;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.utils.InventoryUtils;
import java.util.*;
@ -61,13 +61,13 @@ public class InventoryActionDataTranslator {
worldAction = action;
} else if (action.getSource().getContainerId() == ContainerId.CURSOR && action.getSlot() == 0) {
cursorAction = action;
ItemData translatedCursor = Translators.getItemTranslator().translateToBedrock(session, session.getInventory().getCursor());
ItemData translatedCursor = ItemTranslator.translateToBedrock(session, session.getInventory().getCursor());
if (!translatedCursor.equals(action.getFromItem())) {
refresh = true;
}
} else {
containerAction = action;
ItemData translatedItem = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(translator.bedrockSlotToJava(action)));
ItemData translatedItem = ItemTranslator.translateToBedrock(session, inventory.getItem(translator.bedrockSlotToJava(action)));
if (!translatedItem.equals(action.getFromItem())) {
refresh = true;
}
@ -94,7 +94,7 @@ public class InventoryActionDataTranslator {
ClientPlayerActionPacket actionPacket = new ClientPlayerActionPacket(
sourceAction.getToItem().getCount() == 0 ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM,
new Position(0, 0, 0), BlockFace.DOWN);
session.getDownstream().getSession().send(actionPacket);
session.sendDownstreamPacket(actionPacket);
ItemStack item = session.getInventory().getItem(heldSlot);
if (item != null) {
session.getInventory().setItem(heldSlot, new ItemStack(item.getId(), item.getAmount() - 1, item.getNbt()));
@ -110,14 +110,14 @@ public class InventoryActionDataTranslator {
inventory.getTransactionId().getAndIncrement(),
javaSlot, null, WindowAction.DROP_ITEM,
DropItemParam.DROP_SELECTED_STACK);
session.getDownstream().getSession().send(dropPacket);
session.sendDownstreamPacket(dropPacket);
} else {
for (int i = 0; i < dropAmount; i++) {
ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(),
inventory.getTransactionId().getAndIncrement(),
javaSlot, null, WindowAction.DROP_ITEM,
DropItemParam.DROP_FROM_SELECTED);
session.getDownstream().getSession().send(dropPacket);
session.sendDownstreamPacket(dropPacket);
}
}
ItemStack item = session.getInventory().getItem(javaSlot);
@ -129,7 +129,7 @@ public class InventoryActionDataTranslator {
ClientWindowActionPacket dropPacket = new ClientWindowActionPacket(inventory.getId(), inventory.getTransactionId().getAndIncrement(),
-999, null, WindowAction.CLICK_ITEM,
dropAmount > 1 ? ClickItemParam.LEFT_CLICK : ClickItemParam.RIGHT_CLICK);
session.getDownstream().getSession().send(dropPacket);
session.sendDownstreamPacket(dropPacket);
ItemStack cursor = session.getInventory().getCursor();
if (cursor != null) {
session.getInventory().setCursor(new ItemStack(cursor.getId(), dropAmount > 1 ? 0 : cursor.getAmount() - 1, cursor.getNbt()));
@ -180,18 +180,19 @@ public class InventoryActionDataTranslator {
inventory.getTransactionId().getAndIncrement(),
javaSlot, InventoryUtils.REFRESH_ITEM, WindowAction.SHIFT_CLICK_ITEM,
ShiftClickItemParam.LEFT_CLICK);
session.getDownstream().getSession().send(shiftClickPacket);
session.sendDownstreamPacket(shiftClickPacket);
translator.updateInventory(session, inventory);
return;
}
} else if (translator.getSlotType(javaSlot) == SlotType.OUTPUT) {
plan.add(Click.LEFT, javaSlot);
} else {
int cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Collections.singletonList(javaSlot));
int cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Collections.singletonList(javaSlot), false);
if (cursorSlot != -1) {
plan.add(Click.LEFT, cursorSlot);
} else {
translator.updateInventory(session, inventory);
InventoryUtils.updateCursor(session);
return;
}
plan.add(Click.LEFT, javaSlot);
@ -245,11 +246,15 @@ public class InventoryActionDataTranslator {
int cursorSlot = -1;
if (session.getInventory().getCursor() != null) { //move cursor contents to a temporary slot
cursorSlot = findTempSlot(inventory, session.getInventory().getCursor(), Arrays.asList(fromSlot, toSlot));
cursorSlot = findTempSlot(inventory,
session.getInventory().getCursor(),
Arrays.asList(fromSlot, toSlot),
translator.getSlotType(fromSlot) == SlotType.OUTPUT);
if (cursorSlot != -1) {
plan.add(Click.LEFT, cursorSlot);
} else {
translator.updateInventory(session, inventory);
InventoryUtils.updateCursor(session);
return;
}
}
@ -266,7 +271,7 @@ public class InventoryActionDataTranslator {
inventory.getTransactionId().getAndIncrement(),
fromSlot, InventoryUtils.REFRESH_ITEM, WindowAction.SHIFT_CLICK_ITEM,
ShiftClickItemParam.LEFT_CLICK);
session.getDownstream().getSession().send(shiftClickPacket);
session.sendDownstreamPacket(shiftClickPacket);
translator.updateInventory(session, inventory);
return;
} else if (translator.getSlotType(fromSlot) == SlotType.OUTPUT) {
@ -298,7 +303,7 @@ public class InventoryActionDataTranslator {
InventoryUtils.updateCursor(session);
}
private static int findTempSlot(Inventory inventory, ItemStack item, List<Integer> slotBlacklist) {
private static int findTempSlot(Inventory inventory, ItemStack item, List<Integer> slotBlacklist, boolean emptyOnly) {
/*try and find a slot that can temporarily store the given item
only look in the main inventory and hotbar
only slots that are empty or contain a different type of item are valid*/
@ -314,6 +319,9 @@ public class InventoryActionDataTranslator {
ItemStack testItem = inventory.getItem(i);
boolean acceptable = true;
if (testItem != null) {
if (emptyOnly) {
continue;
}
for (ItemStack blacklistItem : itemBlacklist) {
if (InventoryUtils.canStack(testItem, blacklistItem)) {
acceptable = false;

View file

@ -54,7 +54,7 @@ public class BlockInventoryHolder extends InventoryHolder {
blockPacket.setBlockPosition(position);
blockPacket.setRuntimeId(blockId);
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
session.getUpstream().sendPacket(blockPacket);
session.sendUpstreamPacket(blockPacket);
inventory.setHolderPosition(position);
CompoundTag tag = CompoundTag.builder()
@ -65,7 +65,7 @@ public class BlockInventoryHolder extends InventoryHolder {
BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
dataPacket.setData(tag);
dataPacket.setBlockPosition(position);
session.getUpstream().sendPacket(dataPacket);
session.sendUpstreamPacket(dataPacket);
}
@Override
@ -75,7 +75,7 @@ public class BlockInventoryHolder extends InventoryHolder {
containerOpenPacket.setType((byte) containerType.id());
containerOpenPacket.setBlockPosition(inventory.getHolderPosition());
containerOpenPacket.setUniqueEntityId(inventory.getHolderId());
session.getUpstream().sendPacket(containerOpenPacket);
session.sendUpstreamPacket(containerOpenPacket);
}
@Override
@ -87,6 +87,6 @@ public class BlockInventoryHolder extends InventoryHolder {
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(holderPos);
blockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(realBlock));
session.getUpstream().sendPacket(blockPacket);
session.sendUpstreamPacket(blockPacket);
}
}

View file

@ -31,11 +31,15 @@ import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import lombok.AllArgsConstructor;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.Translators;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.utils.InventoryUtils;
@AllArgsConstructor
public class ChestInventoryUpdater extends InventoryUpdater {
private static final ItemData UNUSUABLE_SPACE_BLOCK = InventoryUtils.createUnusableSpaceBlock(
"This slot does not exist in the inventory\non Java Edition, as there is less\nrows than possible in Bedrock");
private final int paddedSize;
@Override
@ -44,17 +48,17 @@ public class ChestInventoryUpdater extends InventoryUpdater {
ItemData[] bedrockItems = new ItemData[paddedSize];
for (int i = 0; i < bedrockItems.length; i++) {
if (i <= translator.size) {
bedrockItems[i] = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i));
if (i < translator.size) {
bedrockItems[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
} else {
bedrockItems[i] = ItemData.AIR;
bedrockItems[i] = UNUSUABLE_SPACE_BLOCK;
}
}
InventoryContentPacket contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(inventory.getId());
contentPacket.setContents(bedrockItems);
session.getUpstream().sendPacket(contentPacket);
session.sendUpstreamPacket(contentPacket);
}
@Override
@ -65,8 +69,8 @@ public class ChestInventoryUpdater extends InventoryUpdater {
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(inventory.getId());
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(javaSlot)));
session.getUpstream().sendPacket(slotPacket);
slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
session.sendUpstreamPacket(slotPacket);
return true;
}
}

View file

@ -30,8 +30,8 @@ import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.Translators;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.network.translators.item.ItemTranslator;
public class ContainerInventoryUpdater extends InventoryUpdater {
@Override
@ -40,13 +40,13 @@ public class ContainerInventoryUpdater extends InventoryUpdater {
ItemData[] bedrockItems = new ItemData[translator.size];
for (int i = 0; i < bedrockItems.length; i++) {
bedrockItems[translator.javaSlotToBedrock(i)] = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i));
bedrockItems[translator.javaSlotToBedrock(i)] = ItemTranslator.translateToBedrock(session, inventory.getItem(i));
}
InventoryContentPacket contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(inventory.getId());
contentPacket.setContents(bedrockItems);
session.getUpstream().sendPacket(contentPacket);
session.sendUpstreamPacket(contentPacket);
}
@Override
@ -57,8 +57,8 @@ public class ContainerInventoryUpdater extends InventoryUpdater {
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(inventory.getId());
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(javaSlot)));
session.getUpstream().sendPacket(slotPacket);
slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
session.sendUpstreamPacket(slotPacket);
return true;
}
}

View file

@ -29,8 +29,8 @@ import com.nukkitx.protocol.bedrock.data.ContainerId;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.Translators;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.network.translators.item.ItemTranslator;
public class CursorInventoryUpdater extends InventoryUpdater {
@Override
@ -44,8 +44,8 @@ public class CursorInventoryUpdater extends InventoryUpdater {
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.CURSOR);
slotPacket.setSlot(bedrockSlot);
slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(i)));
session.getUpstream().sendPacket(slotPacket);
slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i)));
session.sendUpstreamPacket(slotPacket);
}
}
@ -57,8 +57,8 @@ public class CursorInventoryUpdater extends InventoryUpdater {
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.CURSOR);
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(javaSlot)));
session.getUpstream().sendPacket(slotPacket);
slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
session.sendUpstreamPacket(slotPacket);
return true;
}
}

View file

@ -31,20 +31,20 @@ import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.Translators;
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
import org.geysermc.connector.network.translators.item.ItemTranslator;
public abstract class InventoryUpdater {
public void updateInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
ItemData[] bedrockItems = new ItemData[36];
for (int i = 0; i < 36; i++) {
final int offset = i < 9 ? 27 : -9;
bedrockItems[i] = Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(translator.size + i + offset));
bedrockItems[i] = ItemTranslator.translateToBedrock(session, inventory.getItem(translator.size + i + offset));
}
InventoryContentPacket contentPacket = new InventoryContentPacket();
contentPacket.setContainerId(ContainerId.INVENTORY);
contentPacket.setContents(bedrockItems);
session.getUpstream().sendPacket(contentPacket);
session.sendUpstreamPacket(contentPacket);
}
public boolean updateSlot(InventoryTranslator translator, GeyserSession session, Inventory inventory, int javaSlot) {
@ -52,8 +52,8 @@ public abstract class InventoryUpdater {
InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.INVENTORY);
slotPacket.setSlot(translator.javaSlotToBedrock(javaSlot));
slotPacket.setItem(Translators.getItemTranslator().translateToBedrock(session, inventory.getItem(javaSlot)));
session.getUpstream().sendPacket(slotPacket);
slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(javaSlot)));
session.sendUpstreamPacket(slotPacket);
return true;
}
return false;

View file

@ -1,91 +1,85 @@
/*
* 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:
* 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 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.
* 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
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.utils;
package org.geysermc.connector.network.translators.item;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.nbt.stream.NBTInputStream;
import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.item.ToolItemEntry;
import org.geysermc.connector.network.translators.sound.SoundHandlerRegistry;
import org.geysermc.connector.utils.FileUtils;
import java.io.*;
import java.util.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class Toolbox {
/**
* Registry for anything item related.
*/
public class ItemRegistry {
private static final Map<String, ItemEntry> JAVA_IDENTIFIER_MAP = new HashMap<>();
public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
public static final CompoundTag BIOMES;
public static final ItemData[] CREATIVE_ITEMS;
public static final List<StartGamePacket.ItemEntry> ITEMS = new ArrayList<>();
public static final Int2ObjectMap<ItemEntry> ITEM_ENTRIES = new Int2ObjectOpenHashMap<>();
public static CompoundTag ENTITY_IDENTIFIERS;
// Shield ID, used in Entity.java
public static final int SHIELD = 829;
// Boat ID, used in BedrockInventoryTransactionTranslator.java
public static final int BOAT = 333;
public static int BARRIER_INDEX = 0;
public static void init() {
// no-op
}
static {
/* Load biomes */
InputStream biomestream = GeyserConnector.class.getClassLoader().getResourceAsStream("bedrock/biome_definitions.dat");
if (biomestream == null) {
throw new AssertionError("Unable to find bedrock/biome_definitions.dat");
}
CompoundTag biomesTag;
try (NBTInputStream biomenbtInputStream = NbtUtils.createNetworkReader(biomestream)){
biomesTag = (CompoundTag) biomenbtInputStream.readTag();
BIOMES = biomesTag;
} catch (Exception ex) {
GeyserConnector.getInstance().getLogger().warning("Failed to get biomes from biome definitions, is there something wrong with the file?");
throw new AssertionError(ex);
}
/* Load item palette */
InputStream stream = getResource("bedrock/items.json");
InputStream stream = FileUtils.getResource("bedrock/items.json");
TypeReference<List<JsonNode>> itemEntriesType = new TypeReference<List<JsonNode>>() {
};
List<JsonNode> itemEntries;
try {
itemEntries = JSON_MAPPER.readValue(stream, itemEntriesType);
itemEntries = GeyserConnector.JSON_MAPPER.readValue(stream, itemEntriesType);
} catch (Exception e) {
throw new AssertionError("Unable to load Bedrock runtime item IDs", e);
}
@ -94,11 +88,11 @@ public class Toolbox {
ITEMS.add(new StartGamePacket.ItemEntry(entry.get("name").textValue(), (short) entry.get("id").intValue()));
}
stream = getResource("mappings/items.json");
stream = FileUtils.getResource("mappings/items.json");
JsonNode items;
try {
items = JSON_MAPPER.readTree(stream);
items = GeyserConnector.JSON_MAPPER.readTree(stream);
} catch (Exception e) {
throw new AssertionError("Unable to load Java runtime item IDs", e);
}
@ -139,22 +133,12 @@ public class Toolbox {
itemIndex++;
}
// Load particle/effect mappings
EffectUtils.init();
// Load sound mappings
SoundUtils.init();
// Load the locale data
LocaleUtils.init();
// Load sound handlers
SoundHandlerRegistry.init();
/* Load creative items */
stream = getResource("bedrock/creative_items.json");
stream = FileUtils.getResource("bedrock/creative_items.json");
JsonNode creativeItemEntries;
try {
creativeItemEntries = JSON_MAPPER.readTree(stream).get("items");
creativeItemEntries = GeyserConnector.JSON_MAPPER.readTree(stream).get("items");
} catch (Exception e) {
throw new AssertionError("Unable to load creative items", e);
}
@ -179,33 +163,51 @@ public class Toolbox {
}
}
CREATIVE_ITEMS = creativeItems.toArray(new ItemData[0]);
/* Load entity identifiers */
stream = Toolbox.getResource("bedrock/entity_identifiers.dat");
try (NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream)) {
ENTITY_IDENTIFIERS = (CompoundTag) nbtInputStream.readTag();
} catch (Exception e) {
throw new AssertionError("Unable to get entities from entity identifiers", e);
}
}
/**
* Get an InputStream for the given resource path, throws AssertionError if resource is not found
* Gets an {@link ItemEntry} from the given {@link ItemStack}.
*
* @param resource Resource to get
* @return InputStream of the given resource
* @param stack the item stack
* @return an item entry from the given item stack
*/
public static InputStream getResource(String resource) {
InputStream stream = Toolbox.class.getClassLoader().getResourceAsStream(resource);
if (stream == null) {
throw new AssertionError("Unable to find resource: " + resource);
}
return stream;
public static ItemEntry getItem(ItemStack stack) {
return ITEM_ENTRIES.get(stack.getId());
}
public static void init() {
// no-op
/**
* Gets an {@link ItemEntry} from the given {@link ItemData}.
*
* @param data the item data
* @return an item entry from the given item data
*/
public static ItemEntry getItem(ItemData data) {
for (ItemEntry itemEntry : ITEM_ENTRIES.values()) {
if (itemEntry.getBedrockId() == data.getId() && (itemEntry.getBedrockData() == data.getDamage() || itemEntry.getJavaIdentifier().endsWith("potion"))) {
return itemEntry;
}
}
// If item find was unsuccessful first time, we try again while ignoring damage
// Fixes piston, sticky pistons, dispensers and droppers turning into air from creative inventory
for (ItemEntry itemEntry : ITEM_ENTRIES.values()) {
if (itemEntry.getBedrockId() == data.getId()) {
return itemEntry;
}
}
GeyserConnector.getInstance().getLogger().debug("Missing mapping for bedrock item " + data.getId() + ":" + data.getDamage());
return ItemEntry.AIR;
}
}
/**
* Gets an {@link ItemEntry} from the given Minecraft: Java Edition
* block state identifier.
*
* @param javaIdentifier the block state identifier
* @return an item entry from the given java edition identifier
*/
public static ItemEntry getItemEntry(String javaIdentifier) {
return JAVA_IDENTIFIER_MAP.computeIfAbsent(javaIdentifier, key -> ITEM_ENTRIES.values()
.stream().filter(itemEntry -> itemEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null));
}
}

View file

@ -1,55 +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:
* 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 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.
* 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
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.network.translators.item;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.message.Message;
import com.nukkitx.nbt.CompoundTagBuilder;
import com.github.steveice10.opennbt.tag.builtin.*;
import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.nbt.tag.Tag;
import com.nukkitx.protocol.bedrock.data.ItemData;
import 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.*;
import org.geysermc.connector.utils.Toolbox;
import org.geysermc.connector.network.translators.ItemRemapper;
import org.geysermc.connector.utils.MessageUtils;
import org.reflections.Reflections;
import java.util.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class ItemTranslator {
public abstract class ItemTranslator {
private Int2ObjectMap<ItemStackTranslator> itemTranslators = new Int2ObjectOpenHashMap();
private List<NbtItemStackTranslator> nbtItemTranslators;
private Map<String, ItemEntry> javaIdentifierMap = new HashMap<>();
private static final Int2ObjectMap<ItemTranslator> ITEM_STACK_TRANSLATORS = new Int2ObjectOpenHashMap<>();
private static final List<NbtItemStackTranslator> NBT_TRANSLATORS;
// Shield ID, used in Entity.java
public static final int SHIELD = 829;
protected ItemTranslator() {
}
public void init() {
public static void init() {
// no-op
}
static {
/* Load item translators */
Reflections ref = new Reflections("org.geysermc.connector.network.translators.item");
Map<NbtItemStackTranslator, Integer> loadedNbtItemTranslators = new HashMap<>();
@ -64,34 +76,33 @@ public class ItemTranslator {
loadedNbtItemTranslators.put(nbtItemTranslator, priority);
continue;
}
ItemStackTranslator itemStackTranslator = (ItemStackTranslator) clazz.newInstance();
ItemTranslator itemStackTranslator = (ItemTranslator) clazz.newInstance();
List<ItemEntry> appliedItems = itemStackTranslator.getAppliedItems();
for (ItemEntry item : appliedItems) {
ItemStackTranslator registered = itemTranslators.get(item.getJavaId());
ItemTranslator registered = ITEM_STACK_TRANSLATORS.get(item.getJavaId());
if (registered != null) {
GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated item translator " + clazz.getCanonicalName() + "." +
" Item translator " + registered.getClass().getCanonicalName() + " is already registered for the item " + item.getJavaIdentifier());
continue;
}
itemTranslators.put(item.getJavaId(), itemStackTranslator);
ITEM_STACK_TRANSLATORS.put(item.getJavaId(), itemStackTranslator);
}
} catch (InstantiationException | IllegalAccessException e) {
GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated item translator " + clazz.getCanonicalName() + ".");
}
}
nbtItemTranslators = loadedNbtItemTranslators.keySet().stream()
.sorted(Comparator.comparingInt(value -> loadedNbtItemTranslators.get(value))).collect(Collectors.toList());
NBT_TRANSLATORS = loadedNbtItemTranslators.keySet().stream().sorted(Comparator.comparingInt(loadedNbtItemTranslators::get)).collect(Collectors.toList());
}
public ItemStack translateToJava(GeyserSession session, ItemData data) {
public static ItemStack translateToJava(ItemData data) {
if (data == null) {
return new ItemStack(0);
}
ItemEntry javaItem = getItem(data);
ItemEntry javaItem = ItemRegistry.getItem(data);
ItemStack itemStack;
ItemStackTranslator itemStackTranslator = itemTranslators.get(javaItem.getJavaId());
ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.get(javaItem.getJavaId());
if (itemStackTranslator != null) {
itemStack = itemStackTranslator.translateToJava(data, javaItem);
} else {
@ -99,7 +110,7 @@ public class ItemTranslator {
}
if (itemStack != null && itemStack.getNbt() != null) {
for (NbtItemStackTranslator translator : nbtItemTranslators) {
for (NbtItemStackTranslator translator : NBT_TRANSLATORS) {
if (translator.acceptItem(javaItem)) {
translator.translateToJava(itemStack.getNbt(), javaItem);
}
@ -108,62 +119,271 @@ public class ItemTranslator {
return itemStack;
}
public ItemData translateToBedrock(GeyserSession session, ItemStack stack) {
public static ItemData translateToBedrock(GeyserSession session, ItemStack stack) {
if (stack == null) {
return ItemData.AIR;
}
ItemEntry bedrockItem = getItem(stack);
ItemEntry bedrockItem = ItemRegistry.getItem(stack);
ItemStack itemStack = new ItemStack(stack.getId(), stack.getAmount(), stack.getNbt() != null ? stack.getNbt().clone() : null);
if (itemStack.getNbt() != null) {
for (NbtItemStackTranslator translator : nbtItemTranslators) {
for (NbtItemStackTranslator translator : NBT_TRANSLATORS) {
if (translator.acceptItem(bedrockItem)) {
translator.translateToBedrock(itemStack.getNbt(), bedrockItem);
}
}
}
ItemStackTranslator itemStackTranslator = itemTranslators.get(bedrockItem.getJavaId());
ItemData itemData;
ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.get(bedrockItem.getJavaId());
if (itemStackTranslator != null) {
return itemStackTranslator.translateToBedrock(itemStack, bedrockItem);
itemData = itemStackTranslator.translateToBedrock(itemStack, bedrockItem);
} else {
return DEFAULT_TRANSLATOR.translateToBedrock(itemStack, bedrockItem);
itemData = DEFAULT_TRANSLATOR.translateToBedrock(itemStack, bedrockItem);
}
}
public ItemEntry getItem(ItemStack stack) {
return Toolbox.ITEM_ENTRIES.get(stack.getId());
}
public ItemEntry getItem(ItemData data) {
for (ItemEntry itemEntry : Toolbox.ITEM_ENTRIES.values()) {
if (itemEntry.getBedrockId() == data.getId() && (itemEntry.getBedrockData() == data.getDamage() || itemEntry.getJavaIdentifier().endsWith("potion"))) {
return itemEntry;
}
}
// If item find was unsuccessful first time, we try again while ignoring damage
// Fixes piston, sticky pistons, dispensers and droppers turning into air from creative inventory
for (ItemEntry itemEntry : Toolbox.ITEM_ENTRIES.values()) {
if (itemEntry.getBedrockId() == data.getId()) {
return itemEntry;
// Get the display name of the item
CompoundTag tag = itemData.getTag();
if (tag != null) {
CompoundTag display = tag.getCompound("display");
if (display != null) {
String name = display.getString("Name");
// Check if its a message to translate
if (MessageUtils.isMessage(name)) {
// Get the translated name
name = MessageUtils.getTranslatedBedrockMessage(Message.fromString(name), session.getClientData().getLanguageCode());
// Build the new display tag
CompoundTagBuilder displayBuilder = display.toBuilder();
displayBuilder.stringTag("Name", name);
// Build the new root tag
CompoundTagBuilder builder = tag.toBuilder();
builder.tag(displayBuilder.build("display"));
// Create a new item with the original data + updated name
itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.buildRootTag());
}
}
}
GeyserConnector.getInstance().getLogger().debug("Missing mapping for bedrock item " + data.getId() + ":" + data.getDamage());
return ItemEntry.AIR;
return itemData;
}
public ItemEntry getItemEntry(String javaIdentifier) {
return javaIdentifierMap.computeIfAbsent(javaIdentifier, key -> Toolbox.ITEM_ENTRIES.values()
.stream().filter(itemEntry -> itemEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null));
}
private static final ItemStackTranslator DEFAULT_TRANSLATOR = new ItemStackTranslator() {
private static final ItemTranslator DEFAULT_TRANSLATOR = new ItemTranslator() {
@Override
public List<ItemEntry> getAppliedItems() {
return null;
}
};
public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) {
if (itemStack == null) {
return ItemData.AIR;
}
if (itemStack.getNbt() == null) {
return ItemData.of(itemEntry.getBedrockId(), (short) itemEntry.getBedrockData(), itemStack.getAmount());
}
return ItemData.of(itemEntry.getBedrockId(), (short) itemEntry.getBedrockData(), itemStack.getAmount(), this.translateNbtToBedrock(itemStack.getNbt()));
}
public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) {
if (itemData == null) return null;
if (itemData.getTag() == null) {
return new ItemStack(itemEntry.getJavaId(), itemData.getCount(), new com.github.steveice10.opennbt.tag.builtin.CompoundTag(""));
}
return new ItemStack(itemEntry.getJavaId(), itemData.getCount(), this.translateToJavaNBT(itemData.getTag()));
}
public abstract List<ItemEntry> getAppliedItems();
public CompoundTag translateNbtToBedrock(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag) {
Map<String, Tag<?>> javaValue = new HashMap<>();
if (tag.getValue() != null && !tag.getValue().isEmpty()) {
for (String str : tag.getValue().keySet()) {
com.github.steveice10.opennbt.tag.builtin.Tag javaTag = tag.get(str);
com.nukkitx.nbt.tag.Tag<?> translatedTag = translateToBedrockNBT(javaTag);
if (translatedTag == null)
continue;
javaValue.put(translatedTag.getName(), translatedTag);
}
}
return new CompoundTag(tag.getName(), javaValue);
}
private Tag<?> translateToBedrockNBT(com.github.steveice10.opennbt.tag.builtin.Tag tag) {
if (tag instanceof ByteArrayTag) {
ByteArrayTag byteArrayTag = (ByteArrayTag) tag;
return new com.nukkitx.nbt.tag.ByteArrayTag(byteArrayTag.getName(), byteArrayTag.getValue());
}
if (tag instanceof ByteTag) {
ByteTag byteTag = (ByteTag) tag;
return new com.nukkitx.nbt.tag.ByteTag(byteTag.getName(), byteTag.getValue());
}
if (tag instanceof DoubleTag) {
DoubleTag doubleTag = (DoubleTag) tag;
return new com.nukkitx.nbt.tag.DoubleTag(doubleTag.getName(), doubleTag.getValue());
}
if (tag instanceof FloatTag) {
FloatTag floatTag = (FloatTag) tag;
return new com.nukkitx.nbt.tag.FloatTag(floatTag.getName(), floatTag.getValue());
}
if (tag instanceof IntArrayTag) {
IntArrayTag intArrayTag = (IntArrayTag) tag;
return new com.nukkitx.nbt.tag.IntArrayTag(intArrayTag.getName(), intArrayTag.getValue());
}
if (tag instanceof IntTag) {
IntTag intTag = (IntTag) tag;
return new com.nukkitx.nbt.tag.IntTag(intTag.getName(), intTag.getValue());
}
if (tag instanceof LongArrayTag) {
LongArrayTag longArrayTag = (LongArrayTag) tag;
return new com.nukkitx.nbt.tag.LongArrayTag(longArrayTag.getName(), longArrayTag.getValue());
}
if (tag instanceof LongTag) {
LongTag longTag = (LongTag) tag;
return new com.nukkitx.nbt.tag.LongTag(longTag.getName(), longTag.getValue());
}
if (tag instanceof ShortTag) {
ShortTag shortTag = (ShortTag) tag;
return new com.nukkitx.nbt.tag.ShortTag(shortTag.getName(), shortTag.getValue());
}
if (tag instanceof StringTag) {
StringTag stringTag = (StringTag) tag;
return new com.nukkitx.nbt.tag.StringTag(stringTag.getName(), stringTag.getValue());
}
if (tag instanceof ListTag) {
ListTag listTag = (ListTag) tag;
List<Tag<?>> tagList = new ArrayList<>();
for (com.github.steveice10.opennbt.tag.builtin.Tag value : listTag) {
tagList.add(translateToBedrockNBT(value));
}
Class<?> clazz = CompoundTag.class;
if (!tagList.isEmpty()) {
clazz = tagList.get(0).getClass();
}
return new com.nukkitx.nbt.tag.ListTag(listTag.getName(), clazz, tagList);
}
if (tag instanceof com.github.steveice10.opennbt.tag.builtin.CompoundTag) {
com.github.steveice10.opennbt.tag.builtin.CompoundTag compoundTag = (com.github.steveice10.opennbt.tag.builtin.CompoundTag) tag;
return translateNbtToBedrock(compoundTag);
}
return null;
}
public com.github.steveice10.opennbt.tag.builtin.CompoundTag translateToJavaNBT(com.nukkitx.nbt.tag.CompoundTag tag) {
com.github.steveice10.opennbt.tag.builtin.CompoundTag javaTag = new com.github.steveice10.opennbt.tag.builtin.CompoundTag(tag.getName());
Map<String, com.github.steveice10.opennbt.tag.builtin.Tag> javaValue = javaTag.getValue();
if (tag.getValue() != null && !tag.getValue().isEmpty()) {
for (String str : tag.getValue().keySet()) {
Tag<?> bedrockTag = tag.get(str);
com.github.steveice10.opennbt.tag.builtin.Tag translatedTag = translateToJavaNBT(bedrockTag);
if (translatedTag == null)
continue;
javaValue.put(translatedTag.getName(), translatedTag);
}
}
javaTag.setValue(javaValue);
return javaTag;
}
private com.github.steveice10.opennbt.tag.builtin.Tag translateToJavaNBT(com.nukkitx.nbt.tag.Tag<?> tag) {
if (tag instanceof com.nukkitx.nbt.tag.ByteArrayTag) {
com.nukkitx.nbt.tag.ByteArrayTag byteArrayTag = (com.nukkitx.nbt.tag.ByteArrayTag) tag;
return new ByteArrayTag(byteArrayTag.getName(), byteArrayTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.ByteTag) {
com.nukkitx.nbt.tag.ByteTag byteTag = (com.nukkitx.nbt.tag.ByteTag) tag;
return new ByteTag(byteTag.getName(), byteTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.DoubleTag) {
com.nukkitx.nbt.tag.DoubleTag doubleTag = (com.nukkitx.nbt.tag.DoubleTag) tag;
return new DoubleTag(doubleTag.getName(), doubleTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.FloatTag) {
com.nukkitx.nbt.tag.FloatTag floatTag = (com.nukkitx.nbt.tag.FloatTag) tag;
return new FloatTag(floatTag.getName(), floatTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.IntArrayTag) {
com.nukkitx.nbt.tag.IntArrayTag intArrayTag = (com.nukkitx.nbt.tag.IntArrayTag) tag;
return new IntArrayTag(intArrayTag.getName(), intArrayTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.IntTag) {
com.nukkitx.nbt.tag.IntTag intTag = (com.nukkitx.nbt.tag.IntTag) tag;
return new IntTag(intTag.getName(), intTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.LongArrayTag) {
com.nukkitx.nbt.tag.LongArrayTag longArrayTag = (com.nukkitx.nbt.tag.LongArrayTag) tag;
return new LongArrayTag(longArrayTag.getName(), longArrayTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.LongTag) {
com.nukkitx.nbt.tag.LongTag longTag = (com.nukkitx.nbt.tag.LongTag) tag;
return new LongTag(longTag.getName(), longTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.ShortTag) {
com.nukkitx.nbt.tag.ShortTag shortTag = (com.nukkitx.nbt.tag.ShortTag) tag;
return new ShortTag(shortTag.getName(), shortTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.StringTag) {
com.nukkitx.nbt.tag.StringTag stringTag = (com.nukkitx.nbt.tag.StringTag) tag;
return new StringTag(stringTag.getName(), stringTag.getValue());
}
if (tag instanceof com.nukkitx.nbt.tag.ListTag) {
com.nukkitx.nbt.tag.ListTag<?> listTag = (com.nukkitx.nbt.tag.ListTag<?>) tag;
List<com.github.steveice10.opennbt.tag.builtin.Tag> tags = new ArrayList<>();
for (Object value : listTag.getValue()) {
if (!(value instanceof com.nukkitx.nbt.tag.Tag))
continue;
com.nukkitx.nbt.tag.Tag<?> tagValue = (com.nukkitx.nbt.tag.Tag<?>) value;
com.github.steveice10.opennbt.tag.builtin.Tag javaTag = translateToJavaNBT(tagValue);
if (javaTag != null)
tags.add(javaTag);
}
return new ListTag(listTag.getName(), tags);
}
if (tag instanceof com.nukkitx.nbt.tag.CompoundTag) {
com.nukkitx.nbt.tag.CompoundTag compoundTag = (com.nukkitx.nbt.tag.CompoundTag) tag;
return translateToJavaNBT(compoundTag);
}
return null;
}
}

View file

@ -0,0 +1,45 @@
/*
* 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;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
public class NbtItemStackTranslator {
public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
}
public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
}
public boolean acceptItem(ItemEntry itemEntry) {
return true;
}
}

View file

@ -0,0 +1,167 @@
/*
* 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;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
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 com.nukkitx.nbt.CompoundTagBuilder;
import com.nukkitx.protocol.bedrock.data.ItemData;
import org.geysermc.connector.network.translators.ItemRemapper;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.network.translators.item.ItemEntry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@ItemRemapper
public class BannerTranslator extends ItemTranslator {
private List<ItemEntry> appliedItems;
public BannerTranslator() {
appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("banner")).collect(Collectors.toList());
}
@Override
public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) {
if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, itemEntry);
ItemData itemData = super.translateToBedrock(itemStack, itemEntry);
CompoundTag blockEntityTag = itemStack.getNbt().get("BlockEntityTag");
if (blockEntityTag.contains("Patterns")) {
ListTag patterns = blockEntityTag.get("Patterns");
CompoundTagBuilder builder = itemData.getTag().toBuilder();
builder.tag(convertBannerPattern(patterns));
itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.buildRootTag());
}
return itemData;
}
@Override
public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) {
if (itemData.getTag() == null) return super.translateToJava(itemData, itemEntry);
ItemStack itemStack = super.translateToJava(itemData, itemEntry);
com.nukkitx.nbt.tag.CompoundTag nbtTag = itemData.getTag();
if (nbtTag.contains("Patterns")) {
com.nukkitx.nbt.tag.ListTag<?> patterns = nbtTag.get("Patterns");
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
blockEntityTag.put(convertBannerPattern(patterns));
itemStack.getNbt().put(blockEntityTag);
}
return itemStack;
}
@Override
public List<ItemEntry> getAppliedItems() {
return appliedItems;
}
/**
* Convert a list of patterns from Java nbt to Bedrock nbt
*
* @param patterns The patterns to convert
* @return The new converted patterns
*/
public static com.nukkitx.nbt.tag.ListTag convertBannerPattern(ListTag patterns) {
List<com.nukkitx.nbt.tag.CompoundTag> tagsList = new ArrayList<>();
for (com.github.steveice10.opennbt.tag.builtin.Tag patternTag : patterns.getValue()) {
com.nukkitx.nbt.tag.CompoundTag newPatternTag = getBedrockBannerPattern((CompoundTag) patternTag);
if (newPatternTag != null) {
tagsList.add(newPatternTag);
}
}
return new com.nukkitx.nbt.tag.ListTag<>("Patterns", com.nukkitx.nbt.tag.CompoundTag.class, tagsList);
}
/**
* Convert the Java edition banner 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
*/
public static com.nukkitx.nbt.tag.CompoundTag getBedrockBannerPattern(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();
}
/**
* Convert a list of patterns from Bedrock nbt to Java nbt
*
* @param patterns The patterns to convert
* @return The new converted patterns
*/
public static ListTag convertBannerPattern(com.nukkitx.nbt.tag.ListTag<?> patterns) {
List<Tag> tagsList = new ArrayList<>();
for (Object patternTag : patterns.getValue()) {
CompoundTag newPatternTag = getJavaBannerPattern((com.nukkitx.nbt.tag.CompoundTag) patternTag);
tagsList.add(newPatternTag);
}
return new ListTag("Patterns", tagsList);
}
/**
* Convert the Bedrock edition banner pattern nbt to Java edition
*
* @param pattern Bedorck edition pattern nbt
* @return The Java edition format pattern nbt
*/
public static CompoundTag getJavaBannerPattern(com.nukkitx.nbt.tag.CompoundTag pattern) {
Map<String, Tag> tags = new HashMap<>();
tags.put("Color", new IntTag("Color", 15 - pattern.getInt("Color")));
tags.put("Pattern", new StringTag("Pattern", pattern.getString("Pattern")));
return new CompoundTag("", tags);
}
}

View file

@ -30,22 +30,22 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.protocol.bedrock.data.ItemData;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.translators.ItemStackTranslator;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.network.translators.ItemRemapper;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.item.Potion;
import org.geysermc.connector.utils.Toolbox;
import java.util.List;
import java.util.stream.Collectors;
@ItemRemapper
public class PotionTranslator extends ItemStackTranslator {
public class PotionTranslator extends ItemTranslator {
private List<ItemEntry> appliedItems;
public PotionTranslator() {
appliedItems = Toolbox.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("potion")).collect(Collectors.toList());
appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("potion")).collect(Collectors.toList());
}
@Override

View file

@ -34,7 +34,7 @@ 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.NbtItemStackTranslator;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.utils.MessageUtils;
@ -46,49 +46,51 @@ 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");
if (!itemTag.contains("display")) {
return;
}
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 {
displayTag.put(new StringTag("Name", toBedrockMessage(nameTag)));
lore.add(new StringTag("", toBedrockMessage((StringTag) tag)));
} 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));
}
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 (!itemTag.contains("display")) {
return;
}
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));
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));
}
}

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