{
if (MAP.containsKey(clazz)) {
((PacketTranslator) 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);
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java
index ebc45ff08..206f42d1f 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java
@@ -33,12 +33,10 @@ import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
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.entity.player.PlayerState;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket;
-import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPlaceBlockPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerStatePacket;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket;
@@ -101,10 +99,7 @@ public class BedrockActionTranslator extends PacketTranslator {
@@ -40,8 +42,12 @@ public class BedrockAnimateTranslator extends PacketTranslator {
public void translate(AnimatePacket packet, GeyserSession session) {
switch (packet.getAction()) {
case SWING_ARM:
- ClientPlayerSwingArmPacket swingArmPacket = new ClientPlayerSwingArmPacket(Hand.MAIN_HAND);
- session.getDownstream().getSession().send(swingArmPacket);
+ // Delay so entity damage can be processed first
+ session.getConnector().getGeneralThreadPool().schedule(() ->
+ session.getDownstream().getSession().send(new ClientPlayerSwingArmPacket(Hand.MAIN_HAND)),
+ 25,
+ TimeUnit.MILLISECONDS
+ );
break;
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
index cc399f107..94903246b 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java
@@ -25,6 +25,7 @@
package org.geysermc.connector.network.translators.bedrock;
+import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPlaceBlockPacket;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
@@ -49,19 +50,33 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator {
+public class BedrockSetLocalPlayerAsInitializedTranslator extends PacketTranslator {
@Override
public void translate(SetLocalPlayerAsInitializedPacket packet, GeyserSession session) {
if (session.getPlayerEntity().getGeyserId() == packet.getRuntimeEntityId()) {
if (!session.getUpstream().isInitialized()) {
session.getUpstream().setInitialized(true);
+ session.login();
for (PlayerEntity entity : session.getEntityCache().getEntitiesByType(PlayerEntity.class)) {
if (!entity.isValid()) {
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java
index 92be5c562..1ac9a9fe1 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java
@@ -57,6 +57,9 @@ public class BlockTranslator {
private static final Int2ObjectMap BEDROCK_TO_JAVA_BLOCK_MAP = new Int2ObjectOpenHashMap<>();
private static final IntSet WATERLOGGED = new IntOpenHashSet();
+ // Bedrock carpet ID, used in LlamaEntity.java for decoration
+ public static final int CARPET = 171;
+
private static final int BLOCK_STATE_VERSION = 17760256;
static {
@@ -103,7 +106,8 @@ public class BlockTranslator {
if ("minecraft:water[level=0]".equals(javaId)) {
waterRuntimeId = bedrockRuntimeId;
}
- boolean waterlogged = entry.getValue().has("waterlogged") && entry.getValue().get("waterlogged").booleanValue();
+ boolean waterlogged = entry.getKey().contains("waterlogged=true")
+ || javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass");
if (waterlogged) {
BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId | 1 << 31, new BlockState(javaRuntimeId));
@@ -180,6 +184,10 @@ public class BlockTranslator {
return JAVA_TO_BEDROCK_BLOCK_MAP.get(state.getId());
}
+ public static int getBedrockBlockId(int javaId) {
+ return JAVA_TO_BEDROCK_BLOCK_MAP.get(javaId);
+ }
+
public static BlockState getJavaBlockState(int bedrockId) {
return BEDROCK_TO_JAVA_BLOCK_MAP.get(bedrockId);
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java
index 99d416bba..68f88bb21 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java
@@ -72,7 +72,18 @@ public class ItemTranslator {
if (stack.getNbt() == null) {
return ItemData.of(bedrockItem.getBedrockId(), (short) bedrockItem.getBedrockData(), stack.getAmount());
}
- return ItemData.of(bedrockItem.getBedrockId(), (short) bedrockItem.getBedrockData(), stack.getAmount(), translateToBedrockNBT(stack.getNbt()));
+
+ // TODO: Create proper transformers instead of shoving everything here
+ CompoundTag tag = stack.getNbt();
+ IntTag mapId = tag.get("map");
+
+ if (mapId != null) {
+ tag.put(new StringTag("map_uuid", mapId.getValue().toString()));
+ tag.put(new IntTag("map_name_index", mapId.getValue()));
+ }
+
+
+ return ItemData.of(bedrockItem.getBedrockId(), (short) bedrockItem.getBedrockData(), stack.getAmount(), translateToBedrockNBT(tag));
}
public ItemEntry getItem(ItemStack stack) {
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java
index 3c1452a53..582f0b5aa 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java
@@ -51,7 +51,7 @@ public class JavaBossBarTranslator extends PacketTranslator
bossEventPacket.setAction(BossEventPacket.Action.SHOW);
bossEventPacket.setBossUniqueEntityId(entityId);
- bossEventPacket.setTitle(MessageUtils.getBedrockMessage(packet.getTitle()));
+ bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), session.getClientData().getLanguageCode()));
bossEventPacket.setHealthPercentage(packet.getHealth());
bossEventPacket.setColor(0); //ignored by client
bossEventPacket.setOverlay(1);
@@ -59,7 +59,7 @@ public class JavaBossBarTranslator extends PacketTranslator
break;
case UPDATE_TITLE:
bossEventPacket.setAction(BossEventPacket.Action.TITLE);
- bossEventPacket.setTitle(MessageUtils.getBedrockMessage(packet.getTitle()));
+ bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), session.getClientData().getLanguageCode()));
break;
case UPDATE_HEALTH:
bossEventPacket.setAction(BossEventPacket.Action.HEALTH_PERCENTAGE);
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaChatTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaChatTranslator.java
index 226bd9714..a527866c9 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaChatTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaChatTranslator.java
@@ -34,6 +34,8 @@ import com.github.steveice10.mc.protocol.data.message.TranslationMessage;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerChatPacket;
import com.nukkitx.protocol.bedrock.packet.TextPacket;
+import java.util.List;
+
@Translator(packet = ServerChatPacket.class)
public class JavaChatTranslator extends PacketTranslator {
@@ -58,14 +60,20 @@ public class JavaChatTranslator extends PacketTranslator {
break;
}
+ String locale = session.getClientData().getLanguageCode();
+
if (packet.getMessage() instanceof TranslationMessage) {
textPacket.setType(TextPacket.Type.TRANSLATION);
textPacket.setNeedsTranslation(true);
- textPacket.setParameters(MessageUtils.getTranslationParams(((TranslationMessage) packet.getMessage()).getTranslationParams()));
- textPacket.setMessage(MessageUtils.getBedrockMessage(packet.getMessage()));
+
+ List paramsTranslated = MessageUtils.getTranslationParams(((TranslationMessage) packet.getMessage()).getTranslationParams(), locale);
+ textPacket.setParameters(paramsTranslated);
+
+ textPacket.setMessage(MessageUtils.insertParams(MessageUtils.getTranslatedBedrockMessage(packet.getMessage(), locale, true), paramsTranslated));
} else {
textPacket.setNeedsTranslation(false);
- textPacket.setMessage(MessageUtils.getBedrockMessage(packet.getMessage()));
+
+ textPacket.setMessage(MessageUtils.getTranslatedBedrockMessage(packet.getMessage(), locale, false));
}
session.getUpstream().sendPacket(textPacket);
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java
index 1abe1940c..34fe2271d 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java
@@ -29,7 +29,6 @@ import org.geysermc.connector.entity.PlayerEntity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
-import org.geysermc.connector.utils.ChunkUtils;
import org.geysermc.connector.utils.DimensionUtils;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerJoinGamePacket;
@@ -49,7 +48,7 @@ public class JavaJoinGameTranslator extends PacketTranslator entityClass = type.getEntityClass();
try {
- Constructor extends Entity> entityConstructor = entityClass.getConstructor(long.class, long.class, EntityType.class,
- Vector3f.class, Vector3f.class, Vector3f.class);
+ Entity entity;
+ if (packet.getType() == ObjectType.FALLING_BLOCK) {
+ entity = new FallingBlockEntity(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
+ type, position, motion, rotation, ((FallingBlockData) packet.getData()).getId());
+ } else {
+ Constructor extends Entity> entityConstructor = entityClass.getConstructor(long.class, long.class, EntityType.class,
+ Vector3f.class, Vector3f.class, Vector3f.class);
- Entity entity = entityConstructor.newInstance(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
- type, position, motion, rotation
- );
+ entity = entityConstructor.newInstance(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
+ type, position, motion, rotation
+ );
+ }
session.getEntityCache().spawnEntity(entity);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException ex) {
ex.printStackTrace();
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaTeamTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaTeamTranslator.java
index a832f3d71..c9d1ccfe2 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaTeamTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaTeamTranslator.java
@@ -52,12 +52,14 @@ public class JavaTeamTranslator extends PacketTranslator {
case CREATE:
scoreboard.registerNewTeam(packet.getTeamName(), toPlayerSet(packet.getPlayers()))
.setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
+ .setColor(packet.getColor())
.setPrefix(MessageUtils.getBedrockMessage(packet.getPrefix()))
.setSuffix(MessageUtils.getBedrockMessage(packet.getSuffix()));
break;
case UPDATE:
scoreboard.getTeam(packet.getTeamName())
.setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
+ .setColor(packet.getColor())
.setPrefix(MessageUtils.getBedrockMessage(packet.getPrefix()))
.setSuffix(MessageUtils.getBedrockMessage(packet.getSuffix()))
.setUpdateType(UpdateType.UPDATE);
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java
index 4687dfbbf..bb73c5f02 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java
@@ -36,7 +36,6 @@ import org.geysermc.connector.world.chunk.ChunkSection;
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerChunkDataPacket;
-import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.network.VarInts;
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
@@ -48,63 +47,47 @@ public class JavaChunkDataTranslator extends PacketTranslator {
try {
- if (packet.getColumn().getBiomeData() != null) { //Full chunk
- ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(packet.getColumn());
- ByteBuf byteBuf = Unpooled.buffer(32);
- ChunkSection[] sections = chunkData.sections;
+ ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(packet.getColumn());
+ ByteBuf byteBuf = Unpooled.buffer(32);
+ ChunkSection[] sections = chunkData.sections;
- int sectionCount = sections.length - 1;
- while (sectionCount >= 0 && sections[sectionCount].isEmpty()) {
- sectionCount--;
- }
- sectionCount++;
-
- for (int i = 0; i < sectionCount; i++) {
- ChunkSection section = chunkData.sections[i];
- section.writeToNetwork(byteBuf);
- }
-
- byte[] bedrockBiome = BiomeTranslator.toBedrockBiome(packet.getColumn().getBiomeData());
-
- byteBuf.writeBytes(bedrockBiome); // Biomes - 256 bytes
- byteBuf.writeByte(0); // Border blocks - Edu edition only
- VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now
-
- byte[] payload = new byte[byteBuf.writerIndex()];
- byteBuf.readBytes(payload);
-
- LevelChunkPacket levelChunkPacket = new LevelChunkPacket();
- levelChunkPacket.setSubChunksLength(sectionCount);
- levelChunkPacket.setCachingEnabled(false);
- levelChunkPacket.setChunkX(packet.getColumn().getX());
- levelChunkPacket.setChunkZ(packet.getColumn().getZ());
- levelChunkPacket.setData(payload);
- session.getUpstream().sendPacket(levelChunkPacket);
- } else {
- final int xOffset = packet.getColumn().getX() << 4;
- final int zOffset = packet.getColumn().getZ() << 4;
- Chunk[] chunks = packet.getColumn().getChunks();
- for (int i = 0; i < chunks.length; i++) {
- Chunk chunk = chunks[i];
- if (chunk == null) continue;
- final int yOffset = i * 16;
- for (int x = 0; x < 16; x++) {
- for (int y = 0; y < 16; y++) {
- for (int z = 0; z < 16; z++) {
- BlockState blockState = chunk.get(x, y, z);
- Vector3i pos = Vector3i.from(
- x + xOffset,
- y + yOffset,
- z + zOffset);
- ChunkUtils.updateBlock(session, blockState, pos);
- }
- }
- }
- }
+ int sectionCount = sections.length - 1;
+ while (sectionCount >= 0 && sections[sectionCount].isEmpty()) {
+ sectionCount--;
}
+ sectionCount++;
+
+ for (int i = 0; i < sectionCount; i++) {
+ ChunkSection section = chunkData.sections[i];
+ section.writeToNetwork(byteBuf);
+ }
+
+ byte[] bedrockBiome = BiomeTranslator.toBedrockBiome(packet.getColumn().getBiomeData());
+
+ byteBuf.writeBytes(bedrockBiome); // Biomes - 256 bytes
+ byteBuf.writeByte(0); // Border blocks - Edu edition only
+ VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now
+
+ byte[] payload = new byte[byteBuf.writerIndex()];
+ byteBuf.readBytes(payload);
+
+ LevelChunkPacket levelChunkPacket = new LevelChunkPacket();
+ levelChunkPacket.setSubChunksLength(sectionCount);
+ levelChunkPacket.setCachingEnabled(false);
+ levelChunkPacket.setChunkX(packet.getColumn().getX());
+ levelChunkPacket.setChunkZ(packet.getColumn().getZ());
+ levelChunkPacket.setData(payload);
+ session.getUpstream().sendPacket(levelChunkPacket);
} catch (Exception ex) {
ex.printStackTrace();
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java
new file mode 100644
index 000000000..b4287b081
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java
@@ -0,0 +1,55 @@
+/*
+ * 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.java.world;
+
+import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityCollectItemPacket;
+import com.nukkitx.protocol.bedrock.packet.TakeItemEntityPacket;
+import org.geysermc.connector.entity.Entity;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.Translator;
+
+@Translator(packet = ServerEntityCollectItemPacket.class)
+public class JavaCollectItemTranslator extends PacketTranslator {
+
+ @Override
+ public void translate(ServerEntityCollectItemPacket packet, GeyserSession session) {
+ // This is the definition of translating - both packets take the same values
+ TakeItemEntityPacket takeItemEntityPacket = new TakeItemEntityPacket();
+ // Collected entity is the item
+ Entity collectedEntity = session.getEntityCache().getEntityByJavaId(packet.getCollectedEntityId());
+ // Collector is the entity picking up the item
+ Entity collectorEntity;
+ if (packet.getCollectorEntityId() == session.getPlayerEntity().getEntityId()) {
+ collectorEntity = session.getPlayerEntity();
+ } else {
+ collectorEntity = session.getEntityCache().getEntityByJavaId(packet.getCollectorEntityId());
+ }
+ takeItemEntityPacket.setRuntimeEntityId(collectorEntity.getGeyserId());
+ takeItemEntityPacket.setItemRuntimeEntityId(collectedEntity.getGeyserId());
+ session.getUpstream().sendPacket(takeItemEntityPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaMapDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaMapDataTranslator.java
new file mode 100644
index 000000000..28022c16d
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaMapDataTranslator.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.java.world;
+
+import com.github.steveice10.mc.protocol.data.game.world.map.MapData;
+import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerMapDataPacket;
+import com.nukkitx.protocol.bedrock.packet.ClientboundMapItemDataPacket;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.Translator;
+import org.geysermc.connector.utils.MapColor;
+
+@Translator(packet = ServerMapDataPacket.class)
+public class JavaMapDataTranslator extends PacketTranslator {
+ @Override
+ public void translate(ServerMapDataPacket packet, GeyserSession session) {
+ ClientboundMapItemDataPacket mapItemDataPacket = new ClientboundMapItemDataPacket();
+
+ mapItemDataPacket.setUniqueMapId(packet.getMapId());
+ mapItemDataPacket.setDimensionId(session.getPlayerEntity().getDimension());
+ mapItemDataPacket.setLocked(packet.isLocked());
+ mapItemDataPacket.setScale(packet.getScale());
+
+ MapData data = packet.getData();
+ if (data != null) {
+ mapItemDataPacket.setXOffset(data.getX());
+ mapItemDataPacket.setYOffset(data.getY());
+ mapItemDataPacket.setWidth(data.getColumns());
+ mapItemDataPacket.setHeight(data.getRows());
+
+ // Every int entry is an ARGB color
+ int[] colors = new int[data.getData().length];
+
+ int idx = 0;
+ for (byte colorId : data.getData()) {
+ colors[idx] = MapColor.fromId(colorId).toARGB();
+ idx++;
+ }
+
+ mapItemDataPacket.setColors(colors);
+ }
+
+ session.getUpstream().getSession().sendPacket(mapItemDataPacket);
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java
index 3e1904d48..6c7eeaf92 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java
@@ -98,7 +98,7 @@ public class JavaNotifyClientTranslator extends PacketTranslator entities = new ObjectOpenHashSet<>();
-
public Team(Scoreboard scoreboard, String id) {
this.scoreboard = scoreboard;
this.id = id;
diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
index 2acda2c2b..a35b2cc58 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
@@ -29,8 +29,10 @@ import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
import com.github.steveice10.mc.protocol.data.game.chunk.Column;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.nukkitx.math.vector.Vector2i;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
+import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket;
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.Translators;
@@ -74,6 +76,20 @@ public class ChunkUtils {
return chunkData;
}
+ public static void updateChunkPosition(GeyserSession session, Vector3i position) {
+ Vector2i chunkPos = session.getLastChunkPosition();
+ Vector2i newChunkPos = Vector2i.from(position.getX() >> 4, position.getZ() >> 4);
+
+ if (chunkPos == null || !chunkPos.equals(newChunkPos)) {
+ NetworkChunkPublisherUpdatePacket chunkPublisherUpdatePacket = new NetworkChunkPublisherUpdatePacket();
+ chunkPublisherUpdatePacket.setPosition(position);
+ chunkPublisherUpdatePacket.setRadius(session.getRenderDistance() << 4);
+ session.getUpstream().sendPacket(chunkPublisherUpdatePacket);
+
+ session.setLastChunkPosition(newChunkPos);
+ }
+ }
+
public static void updateBlock(GeyserSession session, BlockState blockState, Position position) {
Vector3i pos = Vector3i.from(position.getX(), position.getY(), position.getZ());
updateBlock(session, blockState, pos);
diff --git a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java
index c7ecaafb4..199c5a5c6 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java
@@ -52,6 +52,7 @@ public class DimensionUtils {
player.setDimension(bedrockDimension);
player.setPosition(pos.toFloat());
session.setSpawned(false);
+ session.setLastChunkPosition(null);
//let java server handle portal travel sound
StopSoundPacket stopSoundPacket = new StopSoundPacket();
diff --git a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java
index 22248db38..eac2fc3f4 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java
@@ -72,4 +72,23 @@ public class FileUtils {
return file;
}
+
+ public static void writeFile(File file, char[] data) throws IOException {
+ if (!file.exists()) {
+ file.createNewFile();
+ }
+
+ FileOutputStream fos = new FileOutputStream(file);
+
+ for (char c : data) {
+ fos.write(c);
+ }
+
+ fos.flush();
+ fos.close();
+ }
+
+ public static void writeFile(String name, char[] data) throws IOException {
+ writeFile(new File(name), data);
+ }
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java
new file mode 100644
index 000000000..e8555eb02
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java
@@ -0,0 +1,316 @@
+/*
+ * 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.utils;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.Getter;
+import org.geysermc.connector.GeyserConnector;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.zip.ZipFile;
+
+public class LocaleUtils {
+
+ public static final Map> LOCALE_MAPPINGS = new HashMap<>();
+
+ private static final Map ASSET_MAP = new HashMap<>();
+
+ private static final String DEFAULT_LOCALE = (GeyserConnector.getInstance().getConfig().getDefaultLocale() != null ? GeyserConnector.getInstance().getConfig().getDefaultLocale() : "en_us");
+
+ private static String smallestURL = "";
+
+ static {
+ // Create the locales folder
+ File localesFolder = new File("locales/");
+ localesFolder.mkdir();
+
+ // Download the latest asset list and cache it
+ generateAssetCache();
+ downloadAndLoadLocale(DEFAULT_LOCALE);
+ }
+
+ private static void generateAssetCache() {
+ try {
+ VersionManifest versionManifest = Toolbox.JSON_MAPPER.readValue(WebUtils.getBody("https://launchermeta.mojang.com/mc/game/version_manifest.json"), VersionManifest.class);
+ String latestInfoURL = "";
+ for (Version version : versionManifest.getVersions()) {
+ if (version.getId().equals(versionManifest.getLatestVersion().getRelease())) {
+ latestInfoURL = version.getUrl();
+ break;
+ }
+ }
+
+ if (latestInfoURL.isEmpty()) {
+ throw new Exception("Unable to get latest Minecraft version");
+ }
+
+ VersionInfo versionInfo = Toolbox.JSON_MAPPER.readValue(WebUtils.getBody(latestInfoURL), VersionInfo.class);
+
+ int currentSize = Integer.MAX_VALUE;
+ for (VersionDownload download : versionInfo.getDownloads().values()) {
+ if (download.getUrl().endsWith(".jar") && download.getSize() < currentSize) {
+ smallestURL = download.getUrl();
+ currentSize = download.getSize();
+ }
+ }
+
+ JsonNode assets = Toolbox.JSON_MAPPER.readTree(WebUtils.getBody(versionInfo.getAssetIndex().getUrl())).get("objects");
+
+ Iterator> assetIterator = assets.fields();
+ while (assetIterator.hasNext()) {
+ Map.Entry entry = assetIterator.next();
+ Asset asset = Toolbox.JSON_MAPPER.treeToValue(entry.getValue(), Asset.class);
+ ASSET_MAP.put(entry.getKey(), asset);
+ }
+ } catch (Exception e) {
+ GeyserConnector.getInstance().getLogger().info("Failed to load locale asset cache: " + (!e.getMessage().isEmpty() ? e.getMessage() : e.getStackTrace()));
+ }
+ }
+
+ public static void downloadAndLoadLocale(String locale) {
+ locale = locale.toLowerCase();
+ if (!ASSET_MAP.containsKey("minecraft/lang/" + locale + ".json") && !locale.equals("en_us")) {
+ GeyserConnector.getInstance().getLogger().warning("Invalid locale requested to download and load: " + locale);
+ return;
+ }
+
+ GeyserConnector.getInstance().getLogger().debug("Downloading and loading locale: " + locale);
+
+ downloadLocale(locale);
+ loadLocale(locale);
+ }
+
+ private static void downloadLocale(String locale) {
+ File localeFile = new File("locales/" + locale + ".json");
+
+ if (localeFile.exists()) {
+ GeyserConnector.getInstance().getLogger().debug("Locale already downloaded: " + locale);
+ return;
+ }
+
+ // Create the en_us locale
+ if (locale.equals("en_us")) {
+ downloadEN_US(localeFile);
+
+ return;
+ }
+
+ // Get the hash and download the locale
+ String hash = ASSET_MAP.get("minecraft/lang/" + locale + ".json").getHash();
+ WebUtils.downloadFile("http://resources.download.minecraft.net/" + hash.substring(0, 2) + "/" + hash, "locales/" + locale + ".json");
+ }
+
+ private static void loadLocale(String locale) {
+ File localeFile = new File("locales/" + locale + ".json");
+
+ // Load the locale
+ if (localeFile.exists()) {
+ // Read the localefile
+ InputStream localeStream;
+ try {
+ localeStream = new FileInputStream(localeFile);
+ } catch (FileNotFoundException e) {
+ throw new AssertionError("Unable to load locale: " + locale + " (" + e.getMessage() + ")");
+ }
+
+ // Parse the file as json
+ JsonNode localeObj;
+ try {
+ localeObj = Toolbox.JSON_MAPPER.readTree(localeStream);
+ } catch (Exception e) {
+ throw new AssertionError("Unable to load Java lang map for " + locale, e);
+ }
+
+ // Parse all the locale fields
+ Iterator> localeIterator = localeObj.fields();
+ Map langMap = new HashMap<>();
+ while (localeIterator.hasNext()) {
+ Map.Entry entry = localeIterator.next();
+ langMap.put(entry.getKey(), entry.getValue().asText());
+ }
+
+ // Insert the locale into the mappings
+ LOCALE_MAPPINGS.put(locale.toLowerCase(), langMap);
+ } else {
+ GeyserConnector.getInstance().getLogger().warning("Missing locale file: " + locale);
+ }
+ }
+
+ private static void downloadEN_US(File localeFile) {
+ try {
+ // Let the user know we are downloading the JAR
+ GeyserConnector.getInstance().getLogger().info("Downloading Minecraft JAR to extract en_us locale, please wait... (this may take some time depending on the speed of your internet connection)");
+ GeyserConnector.getInstance().getLogger().debug("Download URL: " + smallestURL);
+
+ // Download the smallest JAR (client or server)
+ WebUtils.downloadFile(smallestURL, "tmp_locale.jar");
+
+ // Load in the JAR as a zip and extract the file
+ ZipFile localeJar = new ZipFile("tmp_locale.jar");
+ InputStream inputStream = localeJar.getInputStream(localeJar.getEntry("assets/minecraft/lang/en_us.json"));
+ FileOutputStream outputStream = new FileOutputStream(localeFile);
+
+ // Write the file to the locale dir
+ int data = inputStream.read();
+ while(data != -1){
+ outputStream.write(data);
+ data = inputStream.read();
+ }
+
+ // Flush all changes to disk and cleanup
+ outputStream.flush();
+ outputStream.close();
+
+ inputStream.close();
+ localeJar.close();
+
+ // Delete the nolonger needed client/server jar
+ Files.delete(Paths.get("tmp_locale.jar"));
+ } catch (Exception e) {
+ throw new AssertionError("Unable to download and extract en_us locale!", e);
+ }
+ }
+
+ public static String getLocaleString(String messageText, String locale) {
+ Map localeStrings = LocaleUtils.LOCALE_MAPPINGS.get(locale.toLowerCase());
+ if (localeStrings == null)
+ localeStrings = LocaleUtils.LOCALE_MAPPINGS.get(DEFAULT_LOCALE);
+
+ return localeStrings.getOrDefault(messageText, messageText);
+ }
+
+ public static void init() {
+ // no-op
+ }
+}
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Getter
+class VersionManifest {
+ @JsonProperty("latest")
+ private LatestVersion latestVersion;
+
+ @JsonProperty("versions")
+ private List versions;
+}
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Getter
+class LatestVersion {
+ @JsonProperty("release")
+ private String release;
+
+ @JsonProperty("snapshot")
+ private String snapshot;
+}
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Getter
+class Version {
+ @JsonProperty("id")
+ private String id;
+
+ @JsonProperty("type")
+ private String type;
+
+ @JsonProperty("url")
+ private String url;
+
+ @JsonProperty("time")
+ private String time;
+
+ @JsonProperty("releaseTime")
+ private String releaseTime;
+}
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Getter
+class VersionInfo {
+ @JsonProperty("id")
+ private String id;
+
+ @JsonProperty("type")
+ private String type;
+
+ @JsonProperty("time")
+ private String time;
+
+ @JsonProperty("releaseTime")
+ private String releaseTime;
+
+ @JsonProperty("assetIndex")
+ private AssetIndex assetIndex;
+
+ @JsonProperty("downloads")
+ private Map downloads;
+}
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Getter
+class VersionDownload {
+ @JsonProperty("sha1")
+ private String sha1;
+
+ @JsonProperty("size")
+ private int size;
+
+ @JsonProperty("url")
+ private String url;
+}
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Getter
+class AssetIndex {
+ @JsonProperty("id")
+ private String id;
+
+ @JsonProperty("sha1")
+ private String sha1;
+
+ @JsonProperty("size")
+ private int size;
+
+ @JsonProperty("totalSize")
+ private int totalSize;
+
+ @JsonProperty("url")
+ private String url;
+}
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Getter
+class Asset {
+ @JsonProperty("hash")
+ private String hash;
+
+ @JsonProperty("size")
+ private int size;
+}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/MapColor.java b/connector/src/main/java/org/geysermc/connector/utils/MapColor.java
new file mode 100644
index 000000000..2c4a13b9f
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/utils/MapColor.java
@@ -0,0 +1,244 @@
+package org.geysermc.connector.utils;
+
+import java.util.Arrays;
+
+public enum MapColor {
+ COLOR_0(-1, -1, -1),
+ COLOR_1(-1, -1, -1),
+ COLOR_2(-1, -1, -1),
+ COLOR_3(-1, -1, -1),
+ COLOR_4(89, 125, 39),
+ COLOR_5(109, 153, 48),
+ COLOR_6(127, 178, 56),
+ COLOR_7(67, 94, 29),
+ COLOR_8(174, 164, 115),
+ COLOR_9(213, 201, 140),
+ COLOR_10(247, 233, 163),
+ COLOR_11(130, 123, 86),
+ COLOR_12(140, 140, 140),
+ COLOR_13(171, 171, 171),
+ COLOR_14(199, 199, 199),
+ COLOR_15(105, 105, 105),
+ COLOR_16(180, 0, 0),
+ COLOR_17(220, 0, 0),
+ COLOR_18(255, 0, 0),
+ COLOR_19(135, 0, 0),
+ COLOR_20(112, 112, 180),
+ COLOR_21(138, 138, 220),
+ COLOR_22(160, 160, 255),
+ COLOR_23(84, 84, 135),
+ COLOR_24(117, 117, 117),
+ COLOR_25(144, 144, 144),
+ COLOR_26(167, 167, 167),
+ COLOR_27(88, 88, 88),
+ COLOR_28(0, 87, 0),
+ COLOR_29(0, 106, 0),
+ COLOR_30(0, 124, 0),
+ COLOR_31(0, 65, 0),
+ COLOR_32(180, 180, 180),
+ COLOR_33(220, 220, 220),
+ COLOR_34(255, 255, 255),
+ COLOR_35(135, 135, 135),
+ COLOR_36(115, 118, 129),
+ COLOR_37(141, 144, 158),
+ COLOR_38(164, 168, 184),
+ COLOR_39(86, 88, 97),
+ COLOR_40(106, 76, 54),
+ COLOR_41(130, 94, 66),
+ COLOR_42(151, 109, 77),
+ COLOR_43(79, 57, 40),
+ COLOR_44(79, 79, 79),
+ COLOR_45(96, 96, 96),
+ COLOR_46(112, 112, 112),
+ COLOR_47(59, 59, 59),
+ COLOR_48(45, 45, 180),
+ COLOR_49(55, 55, 220),
+ COLOR_50(64, 64, 255),
+ COLOR_51(33, 33, 135),
+ COLOR_52(100, 84, 50),
+ COLOR_53(123, 102, 62),
+ COLOR_54(143, 119, 72),
+ COLOR_55(75, 63, 38),
+ COLOR_56(180, 177, 172),
+ COLOR_57(220, 217, 211),
+ COLOR_58(255, 252, 245),
+ COLOR_59(135, 133, 129),
+ COLOR_60(152, 89, 36),
+ COLOR_61(186, 109, 44),
+ COLOR_62(216, 127, 51),
+ COLOR_63(114, 67, 27),
+ COLOR_64(125, 53, 152),
+ COLOR_65(153, 65, 186),
+ COLOR_66(178, 76, 216),
+ COLOR_67(94, 40, 114),
+ COLOR_68(72, 108, 152),
+ COLOR_69(88, 132, 186),
+ COLOR_70(102, 153, 216),
+ COLOR_71(54, 81, 114),
+ COLOR_72(161, 161, 36),
+ COLOR_73(197, 197, 44),
+ COLOR_74(229, 229, 51),
+ COLOR_75(121, 121, 27),
+ COLOR_76(89, 144, 17),
+ COLOR_77(109, 176, 21),
+ COLOR_78(127, 204, 25),
+ COLOR_79(67, 108, 13),
+ COLOR_80(170, 89, 116),
+ COLOR_81(208, 109, 142),
+ COLOR_82(242, 127, 165),
+ COLOR_83(128, 67, 87),
+ COLOR_84(53, 53, 53),
+ COLOR_85(65, 65, 65),
+ COLOR_86(76, 76, 76),
+ COLOR_87(40, 40, 40),
+ COLOR_88(108, 108, 108),
+ COLOR_89(132, 132, 132),
+ COLOR_90(153, 153, 153),
+ COLOR_91(81, 81, 81),
+ COLOR_92(53, 89, 108),
+ COLOR_93(65, 109, 132),
+ COLOR_94(76, 127, 153),
+ COLOR_95(40, 67, 81),
+ COLOR_96(89, 44, 125),
+ COLOR_97(109, 54, 153),
+ COLOR_98(127, 63, 178),
+ COLOR_99(67, 33, 94),
+ COLOR_100(36, 53, 125),
+ COLOR_101(44, 65, 153),
+ COLOR_102(51, 76, 178),
+ COLOR_103(27, 40, 94),
+ COLOR_104(72, 53, 36),
+ COLOR_105(88, 65, 44),
+ COLOR_106(102, 76, 51),
+ COLOR_107(54, 40, 27),
+ COLOR_108(72, 89, 36),
+ COLOR_109(88, 109, 44),
+ COLOR_110(102, 127, 51),
+ COLOR_111(54, 67, 27),
+ COLOR_112(108, 36, 36),
+ COLOR_113(132, 44, 44),
+ COLOR_114(153, 51, 51),
+ COLOR_115(81, 27, 27),
+ COLOR_116(17, 17, 17),
+ COLOR_117(21, 21, 21),
+ COLOR_118(25, 25, 25),
+ COLOR_119(13, 13, 13),
+ COLOR_120(176, 168, 54),
+ COLOR_121(215, 205, 66),
+ COLOR_122(250, 238, 77),
+ COLOR_123(132, 126, 40),
+ COLOR_124(64, 154, 150),
+ COLOR_125(79, 188, 183),
+ COLOR_126(92, 219, 213),
+ COLOR_127(48, 115, 112),
+ COLOR_128(52, 90, 180),
+ COLOR_129(63, 110, 220),
+ COLOR_130(74, 128, 255),
+ COLOR_131(39, 67, 135),
+ COLOR_132(0, 153, 40),
+ COLOR_133(0, 187, 50),
+ COLOR_134(0, 217, 58),
+ COLOR_135(0, 114, 30),
+ COLOR_136(91, 60, 34),
+ COLOR_137(111, 74, 42),
+ COLOR_138(129, 86, 49),
+ COLOR_139(68, 45, 25),
+ COLOR_140(79, 1, 0),
+ COLOR_141(96, 1, 0),
+ COLOR_142(112, 2, 0),
+ COLOR_143(59, 1, 0),
+ COLOR_144(147, 124, 113),
+ COLOR_145(180, 152, 138),
+ COLOR_146(209, 177, 161),
+ COLOR_147(110, 93, 85),
+ COLOR_148(112, 57, 25),
+ COLOR_149(137, 70, 31),
+ COLOR_150(159, 82, 36),
+ COLOR_151(84, 43, 19),
+ COLOR_152(105, 61, 76),
+ COLOR_153(128, 75, 93),
+ COLOR_154(149, 87, 108),
+ COLOR_155(78, 46, 57),
+ COLOR_156(79, 76, 97),
+ COLOR_157(96, 93, 119),
+ COLOR_158(112, 108, 138),
+ COLOR_159(59, 57, 73),
+ COLOR_160(131, 93, 25),
+ COLOR_161(160, 114, 31),
+ COLOR_162(186, 133, 36),
+ COLOR_163(98, 70, 19),
+ COLOR_164(72, 82, 37),
+ COLOR_165(88, 100, 45),
+ COLOR_166(103, 117, 53),
+ COLOR_167(54, 61, 28),
+ COLOR_168(112, 54, 55),
+ COLOR_169(138, 66, 67),
+ COLOR_170(160, 77, 78),
+ COLOR_171(84, 40, 41),
+ COLOR_172(40, 28, 24),
+ COLOR_173(49, 35, 30),
+ COLOR_174(57, 41, 35),
+ COLOR_175(30, 21, 18),
+ COLOR_176(95, 75, 69),
+ COLOR_177(116, 92, 84),
+ COLOR_178(135, 107, 98),
+ COLOR_179(71, 56, 51),
+ COLOR_180(61, 64, 64),
+ COLOR_181(75, 79, 79),
+ COLOR_182(87, 92, 92),
+ COLOR_183(46, 48, 48),
+ COLOR_184(86, 51, 62),
+ COLOR_185(105, 62, 75),
+ COLOR_186(122, 73, 88),
+ COLOR_187(64, 38, 46),
+ COLOR_188(53, 43, 64),
+ COLOR_189(65, 53, 79),
+ COLOR_190(76, 62, 92),
+ COLOR_191(40, 32, 48),
+ COLOR_192(53, 35, 24),
+ COLOR_193(65, 43, 30),
+ COLOR_194(76, 50, 35),
+ COLOR_195(40, 26, 18),
+ COLOR_196(53, 57, 29),
+ COLOR_197(65, 70, 36),
+ COLOR_198(76, 82, 42),
+ COLOR_199(40, 43, 22),
+ COLOR_200(100, 42, 32),
+ COLOR_201(122, 51, 39),
+ COLOR_202(142, 60, 46),
+ COLOR_203(75, 31, 24),
+ COLOR_204(26, 15, 11),
+ COLOR_205(31, 18, 13),
+ COLOR_206(37, 22, 16),
+ COLOR_207(19, 11, 8);
+
+ private final int red;
+ private final int green;
+ private final int blue;
+
+ MapColor(int red, int green, int blue) {
+ this.red = red;
+ this.green = green;
+ this.blue = blue;
+ }
+
+ int getId() {
+ return ordinal();
+ }
+
+ public static MapColor fromId(int id) {
+ return Arrays.stream(values()).filter(color -> color.getId() == id).findFirst().get();
+ }
+
+ public int toARGB() {
+ int alpha = 255;
+ if (red == -1 && green == -1 && blue == -1)
+ alpha = 0; // transparent
+
+ long result = red & 0xff;
+ result |= (green & 0xff) << 8;
+ result |= (blue & 0xff) << 16;
+ result |= (alpha & 0xff) << 24;
+ return (int) (result & 0xFFFFFFFFL);
+ }
+}
\ No newline at end of file
diff --git a/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java b/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java
index b5260ab75..a28d6a7a1 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java
@@ -25,31 +25,30 @@
package org.geysermc.connector.utils;
-import com.github.steveice10.mc.protocol.data.message.ChatColor;
-import com.github.steveice10.mc.protocol.data.message.ChatFormat;
-import com.github.steveice10.mc.protocol.data.message.Message;
-import com.github.steveice10.mc.protocol.data.message.TranslationMessage;
+import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
+import com.github.steveice10.mc.protocol.data.message.*;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class MessageUtils {
- public static List getTranslationParams(Message[] messages) {
- List strings = new ArrayList();
- for (int i = 0; i < messages.length; i++) {
- if (messages[i] instanceof TranslationMessage) {
- TranslationMessage translation = (TranslationMessage) messages[i];
+ public static List getTranslationParams(Message[] messages, String locale) {
+ List strings = new ArrayList<>();
+ for (Message message : messages) {
+ if (message instanceof TranslationMessage) {
+ TranslationMessage translation = (TranslationMessage) message;
- StringBuilder builder = new StringBuilder("");
- builder.append("%");
- builder.append(translation.getTranslationKey());
- strings.add(builder.toString());
+ if (locale == null) {
+ String builder = "%" + translation.getTranslationKey();
+ strings.add(builder);
+ }
if (translation.getTranslationKey().equals("commands.gamemode.success.other")) {
strings.add("");
@@ -59,47 +58,92 @@ public class MessageUtils {
strings.add(" - no permission or invalid command!");
}
- for (int j = 0; j < getTranslationParams(translation.getTranslationParams()).size(); j++) {
- strings.add(getTranslationParams(translation.getTranslationParams()).get(j));
+ List furtherParams = getTranslationParams(translation.getTranslationParams());
+ if (locale != null) {
+ strings.add(insertParams(LocaleUtils.getLocaleString(translation.getTranslationKey(), locale), furtherParams));
+ }else{
+ strings.addAll(furtherParams);
}
} else {
- StringBuilder builder = new StringBuilder("");
- builder.append(getFormat(messages[i].getStyle().getFormats()));
- builder.append(getColor(messages[i].getStyle().getColor()));
- builder.append(getBedrockMessage(messages[i]));
- strings.add(builder.toString());
+ String builder = getFormat(message.getStyle().getFormats()) +
+ getColorOrParent(message.getStyle());
+ builder += getTranslatedBedrockMessage(message, locale, false);
+ strings.add(builder);
}
}
return strings;
}
- public static String getTranslationText(TranslationMessage message) {
- StringBuilder builder = new StringBuilder("");
- builder.append(getFormat(message.getStyle().getFormats()));
- builder.append(getColor(message.getStyle().getColor()));
- builder.append("%");
- builder.append(message.getTranslationKey());
- return builder.toString();
+ public static List getTranslationParams(Message[] messages) {
+ return getTranslationParams(messages, null);
}
- public static String getBedrockMessage(Message message) {
+ public static String getTranslationText(TranslationMessage message) {
+ return getFormat(message.getStyle().getFormats()) + getColorOrParent(message.getStyle())
+ + "%" + message.getTranslationKey();
+ }
+
+ public static String getTranslatedBedrockMessage(Message message, String locale, boolean shouldTranslate) {
JsonParser parser = new JsonParser();
if (isMessage(message.getText())) {
JsonObject object = parser.parse(message.getText()).getAsJsonObject();
message = Message.fromJson(formatJson(object));
}
- StringBuilder builder = new StringBuilder(message.getText());
+ String messageText = message.getText();
+ if (locale != null && shouldTranslate) {
+ messageText = LocaleUtils.getLocaleString(messageText, locale);
+ }
+
+ StringBuilder builder = new StringBuilder(messageText);
for (Message msg : message.getExtra()) {
builder.append(getFormat(msg.getStyle().getFormats()));
- builder.append(getColor(msg.getStyle().getColor()));
+ builder.append(getColorOrParent(msg.getStyle()));
if (!(msg.getText() == null)) {
- builder.append(getBedrockMessage(msg));
+ boolean isTranslationMessage = (msg instanceof TranslationMessage);
+ builder.append(getTranslatedBedrockMessage(msg, locale, isTranslationMessage));
+ }
+ }
+ return builder.toString();
+ }
+
+ public static String getTranslatedBedrockMessage(Message message, String locale) {
+ return getTranslatedBedrockMessage(message, locale, true);
+ }
+
+ public static String getBedrockMessage(Message message) {
+ return getTranslatedBedrockMessage(message, null, false);
+ }
+
+ public static String insertParams(String message, List params) {
+ String newMessage = message;
+
+ Pattern p = Pattern.compile("%([1-9])\\$s");
+ Matcher m = p.matcher(message);
+ while (m.find()) {
+ try {
+ newMessage = newMessage.replaceFirst("%" + m.group(1) + "\\$s" , params.get(Integer.parseInt(m.group(1)) - 1));
+ } catch (Exception e) {
+ // Couldnt find the param to replace
}
}
- return builder.toString();
+ for (String text : params) {
+ newMessage = newMessage.replaceFirst("%s", text);
+ }
+
+ return newMessage;
+ }
+
+ private static String getColorOrParent(MessageStyle style) {
+ ChatColor chatColor = style.getColor();
+
+ if (chatColor == ChatColor.NONE) {
+ return getColor(style.getParent().getColor());
+ }
+
+ return getColor(chatColor);
}
private static String getColor(ChatColor color) {
@@ -206,7 +250,6 @@ public class MessageUtils {
} catch (Exception ex) {
return false;
}
-
return true;
}
@@ -231,7 +274,20 @@ public class MessageUtils {
formatJson((JsonObject) a.get(i));
}
}
-
return object;
}
+
+ public static String toChatColor(TeamColor teamColor) {
+ for (ChatColor color : ChatColor.values()) {
+ if (color.name().equals(teamColor.name())) {
+ return getColor(color);
+ }
+ }
+ for (ChatFormat format : ChatFormat.values()) {
+ if (format.name().equals(teamColor.name())) {
+ return getFormat(Collections.singletonList(format));
+ }
+ }
+ return "";
+ }
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java
index 562fb3c51..7815fd087 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java
@@ -40,7 +40,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.translators.item.ItemEntry;
-import java.io.InputStream;
+import java.io.*;
import java.util.*;
public class Toolbox {
@@ -52,6 +52,8 @@ public class Toolbox {
public static final Int2ObjectMap ITEM_ENTRIES = new Int2ObjectOpenHashMap<>();
+ public static final Map> LOCALE_MAPPINGS = new HashMap<>();
+
static {
/* Load biomes */
InputStream biomestream = GeyserConnector.class.getClassLoader().getResourceAsStream("bedrock/biome_definitions.dat");
@@ -104,6 +106,9 @@ public class Toolbox {
entry.getValue().get("bedrock_id").intValue(), entry.getValue().get("bedrock_data").intValue()));
itemIndex++;
}
+
+ // Load the locale data
+ LocaleUtils.init();
}
public static InputStream getResource(String resource) {
diff --git a/connector/src/main/java/org/geysermc/connector/utils/WebUtils.java b/connector/src/main/java/org/geysermc/connector/utils/WebUtils.java
new file mode 100644
index 000000000..065d683a0
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/utils/WebUtils.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.utils;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+
+public class WebUtils {
+
+ public static String getBody(String reqURL) {
+ URL url = null;
+ try {
+ url = new URL(reqURL);
+ HttpURLConnection con = (HttpURLConnection) url.openConnection();
+ con.setRequestMethod("GET");
+
+ BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
+ String inputLine;
+ StringBuffer content = new StringBuffer();
+
+ while ((inputLine = in.readLine()) != null) {
+ content.append(inputLine);
+ content.append("\n");
+ }
+
+ in.close();
+ con.disconnect();
+
+ return content.toString();
+ } catch (Exception e) {
+ return e.getMessage();
+ }
+ }
+
+ public static void downloadFile(String reqURL, String fileLocation) {
+ try {
+ InputStream in = new URL(reqURL).openStream();
+ Files.copy(in, Paths.get(fileLocation), StandardCopyOption.REPLACE_EXISTING);
+ } catch (Exception e) {
+ throw new AssertionError("Unable to download and save file: " + fileLocation + " (" + reqURL + ")", e);
+ }
+ }
+}
diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml
index ba5700e27..ae0cbed8d 100644
--- a/connector/src/main/resources/config.yml
+++ b/connector/src/main/resources/config.yml
@@ -57,6 +57,9 @@ general-thread-pool: 32
# OptiFine capes, LabyMod capes, 5Zig capes and MinecraftCapes
allow-third-party-capes: true
+# The default locale if we dont have the one the client requested
+default-locale: en_us
+
# bStats is a stat tracker that is entirely anonymous and tracks only basic information
# about Geyser, such as how many people are online, how many servers are using Geyser,
# what OS is being used, etc. You can learn more about bStats here: https://bstats.org/.
diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings
index 278c73449..efc9db6b7 160000
--- a/connector/src/main/resources/mappings
+++ b/connector/src/main/resources/mappings
@@ -1 +1 @@
-Subproject commit 278c73449aeeb4064c7513a68f98a49a5f463f0a
+Subproject commit efc9db6b7d51bdf145230933ac23b321ac1c132d