Merge branch 'master' into inventory

This commit is contained in:
RednedEpic 2020-04-11 22:46:25 -05:00
commit 605c9ef4db
41 changed files with 1072 additions and 105 deletions

View file

@ -7,30 +7,34 @@ assignees: ''
---
<!--- Please follow this format COMPLETELY and make sure the bug you are reporting has not been reported yet. Reports should contain as much information or context as possible to help us find the problem. Simply creating an issue on a vague topic will not help us at all, and if you are unsure if something should belong here, please contact us on [Discord](http://discord.geysermc.org).-->
<!--- Issues pertaining to connection problem, or anything of that covered on the [Common Issues](https://github.com/GeyserMC/Geyser/wiki/Common-Issues) do not belong here and only clutter this issue tracker. -->
**Describe the bug**
A clear and concise description of what the bug is.
<!--- A clear and concise description of what the bug is. -->
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
<!--- Steps to reproduce the behavior: -->
<!--- 1. Go to '...' -->
<!--- 2. Click on '....' -->
<!--- 3. Scroll down to '....' -->
<!--- 4. See error -->
**Expected behavior**
A clear and concise description of what you expected to happen.
<!--- A clear and concise description of what you expected to happen. -->
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Screenshots / Videos**
<!--- If applicable, add screenshots to help explain your problem. -->
**Server version**
run /version
**Server Version**
<!--- Give us the exact output from /version. Saying "latest" does not help us at all. -->
**Geyser version**
Jenkins
**Geyser Version**
<!--- Give us the exact build number as well as branch if applicable. Saying "latest" does not help us at all. -->
**Bedrock version**
The version of your Minecraft pe
**Minecraft: Bedrock Edition Version**
<!-- The version of your Minecraft: Bedrock Edition client you tested with. -->
**Additional context**
Add any other context about the problem here.
**Additional Context**
<!--- Add any other context about the problem here. --->

1
.gitignore vendored
View file

@ -225,3 +225,4 @@ nbdist/
config.yml
logs/
public-key.pem
locales/

View file

@ -101,6 +101,11 @@ public class GeyserBukkitConfiguration implements IGeyserConfiguration {
return config.getBoolean("allow-third-party-capes", true);
}
@Override
public String getDefaultLocale() {
return config.getString("default-locale", "en_us");
}
@Override
public Path getFloodgateKeyFile() {
return Paths.get(dataFolder.toString(), config.getString("floodgate-key-file", "public-key.pem"));

View file

@ -102,6 +102,11 @@ public class GeyserBungeeConfiguration implements IGeyserConfiguration {
return config.getBoolean("allow-third-party-capes", true);
}
@Override
public String getDefaultLocale() {
return config.getString("default-locale", "en_us");
}
@Override
public Path getFloodgateKeyFile() {
return Paths.get(dataFolder.toString(), config.getString("floodgate-key-file", "public-key.pem"));

View file

@ -105,6 +105,11 @@ public class GeyserSpongeConfiguration implements IGeyserConfiguration {
return node.getNode("allow-third-party-capes").getBoolean(true);
}
@Override
public String getDefaultLocale() {
return node.getNode("default-locale").getString("en_us");
}
@Override
public Path getFloodgateKeyFile() {
return Paths.get(dataFolder.toString(), node.getNode("floodgate-key-file").getString("public-key.pem"));

View file

@ -120,6 +120,9 @@
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.geysermc.platform.standalone.GeyserBootstrap</mainClass>
<manifestEntries>
<Multi-Release>true</Multi-Release>
</manifestEntries>
</transformer>
<transformer
implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer">
@ -130,4 +133,4 @@
</plugin>
</plugins>
</build>
</project>
</project>

View file

@ -63,6 +63,9 @@ public class GeyserConfiguration implements IGeyserConfiguration {
@JsonProperty("allow-third-party-capes")
private boolean allowThirdPartyCapes;
@JsonProperty("default-locale")
private String defaultLocale;
private MetricsInfo metrics;
@Override

View file

@ -52,7 +52,7 @@ public class GeyserLogger extends SimpleTerminalConsole implements IGeyserLogger
@Override
protected void shutdown() {
GeyserConnector.getInstance().shutdown();
GeyserConnector.getInstance().getBootstrap().onDisable();
}
@Override

View file

@ -63,6 +63,9 @@ public class GeyserVelocityConfiguration implements IGeyserConfiguration {
@JsonProperty("allow-third-party-capes")
private boolean allowThirdPartyCapes;
@JsonProperty("default-locale")
private String defaultLocale;
private MetricsInfo metrics;
@Override

View file

@ -46,6 +46,8 @@ public interface IGeyserConfiguration {
boolean isAllowThirdPartyCapes();
String getDefaultLocale();
Path getFloodgateKeyFile();
IMetricsInfo getMetrics();

View file

@ -49,6 +49,8 @@ import java.net.InetSocketAddress;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@ -61,7 +63,7 @@ public class GeyserConnector {
public static final String NAME = "Geyser";
public static final String VERSION = "1.0-SNAPSHOT";
private final Map<Object, GeyserSession> players = new HashMap<>();
private final Map<InetSocketAddress, GeyserSession> players = new HashMap<>();
private static GeyserConnector instance;
@ -141,6 +143,40 @@ public class GeyserConnector {
bootstrap.getGeyserLogger().info("Shutting down Geyser.");
shuttingDown = true;
if (players.size() >= 1) {
bootstrap.getGeyserLogger().info("Kicking " + players.size() + " player(s)");
for (GeyserSession playerSession : players.values()) {
playerSession.disconnect("Geyser Proxy shutting down.");
}
CompletableFuture<Void> future = CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
// Simulate a long-running Job
try {
while (true) {
if (players.size() == 0) {
return;
}
TimeUnit.MILLISECONDS.sleep(100);
}
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
});
// Block and wait for the future to complete
try {
future.get();
bootstrap.getGeyserLogger().info("Kicked all players");
} catch (Exception e) {
// Quietly fail
}
}
generalThreadPool.shutdown();
bedrockServer.close();
players.clear();
@ -148,17 +184,15 @@ public class GeyserConnector {
authType = null;
commandMap.getCommands().clear();
commandMap = null;
bootstrap.getGeyserLogger().info("Geyser shutdown successfully.");
}
public void addPlayer(GeyserSession player) {
players.put(player.getAuthData().getName(), player);
players.put(player.getAuthData().getUUID(), player);
players.put(player.getSocketAddress(), player);
}
public void removePlayer(GeyserSession player) {
players.remove(player.getAuthData().getName());
players.remove(player.getAuthData().getUUID());
players.remove(player.getSocketAddress());
}

View file

@ -48,6 +48,11 @@ public class StopCommand extends GeyserCommand {
if (!sender.isConsole() && connector.getPlatformType() == PlatformType.STANDALONE) {
return;
}
connector.shutdown();
if (connector.getPlatformType() == PlatformType.STANDALONE) {
System.exit(0);
}
}
}

View file

@ -0,0 +1,40 @@
/*
* 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.block.BlockTranslator;
public class FallingBlockEntity extends Entity {
public FallingBlockEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, int javaId) {
super(entityId, geyserId, entityType, position, motion, rotation);
this.metadata.put(EntityData.VARIANT, BlockTranslator.getBedrockBlockId(javaId));
}
}

View file

@ -42,6 +42,8 @@ public class BeeEntity extends AnimalEntity {
if (entityMetadata.getId() == 16) {
byte xd = (byte) entityMetadata.getValue();
metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 0x02) == 0x02);
// If the bee has nectar or not
metadata.getFlags().setFlag(EntityFlag.POWERED, (xd & 0x08) == 0x08);
}
super.updateBedrockMetadata(entityMetadata, session);
}

View file

@ -25,12 +25,52 @@
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.ItemData;
import com.nukkitx.protocol.bedrock.packet.MobArmorEquipmentPacket;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.block.BlockTranslator;
public class LlamaEntity extends ChestedHorseEntity {
public LlamaEntity(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) {
// Strength
if (entityMetadata.getId() == 19) {
metadata.put(EntityData.STRENGTH, entityMetadata.getValue());
}
// Color equipped on the llama
if (entityMetadata.getId() == 20) {
// Bedrock treats llama decoration as armor
MobArmorEquipmentPacket equipmentPacket = new MobArmorEquipmentPacket();
equipmentPacket.setRuntimeEntityId(getGeyserId());
// -1 means no armor
if ((int) entityMetadata.getValue() != -1) {
// The damage value is the dye color that Java sends us
// Always going to be a carpet so we can hardcode 171 in BlockTranslator
// The int then short conversion is required or we get a ClassCastException
equipmentPacket.setChestplate(ItemData.of(BlockTranslator.CARPET, (short)((int) entityMetadata.getValue()), 1));
} else {
equipmentPacket.setChestplate(ItemData.AIR);
}
// Required to fill out the rest of the equipment or Bedrock ignores it, including above else statement if removing armor
equipmentPacket.setBoots(ItemData.AIR);
equipmentPacket.setHelmet(ItemData.AIR);
equipmentPacket.setLeggings(ItemData.AIR);
session.getUpstream().sendPacket(equipmentPacket);
}
// Color of the llama
if (entityMetadata.getId() == 21) {
metadata.put(EntityData.VARIANT, entityMetadata.getValue());
}
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.living.animal.horse;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData;
import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
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) {
// The trader llama is a separate entity from the llama in Java but a normal llama with extra metadata in Bedrock.
AddEntityPacket addEntityPacket = new AddEntityPacket();
addEntityPacket.setIdentifier("minecraft:llama");
addEntityPacket.setRuntimeEntityId(geyserId);
addEntityPacket.setUniqueEntityId(geyserId);
addEntityPacket.setPosition(position);
addEntityPacket.setMotion(motion);
addEntityPacket.setRotation(getBedrockRotation());
addEntityPacket.setEntityType(entityType.getType());
addEntityPacket.getMetadata().putAll(metadata);
// Here's the difference
addEntityPacket.getMetadata().put(EntityData.MARK_VARIANT, 1);
valid = true;
session.getUpstream().sendPacket(addEntityPacket);
session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
}
}

View file

@ -28,6 +28,7 @@ package org.geysermc.connector.entity.living.animal.tameable;
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;
@ -40,11 +41,31 @@ public class CatEntity extends TameableEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 18) {
metadata.put(EntityData.VARIANT, (int) entityMetadata.getValue());
// Different colors in Java and Bedrock for some reason
int variantColor;
switch ((int) entityMetadata.getValue()) {
case 0:
variantColor = 8;
break;
case 8:
variantColor = 0;
break;
case 9:
variantColor = 10;
break;
case 10:
variantColor = 9;
break;
default:
variantColor = (int) entityMetadata.getValue();
}
metadata.put(EntityData.VARIANT, variantColor);
}
if (entityMetadata.getId() == 21) {
// FIXME: Colors the whole animal instead of just collar
metadata.put(EntityData.COLOR, (byte) (int) entityMetadata.getValue());
// Needed or else wild cats are a red color
if (metadata.getFlags().getFlag(EntityFlag.TAMED)) {
metadata.put(EntityData.COLOR, (byte) (int) entityMetadata.getValue());
}
}
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.animal.tameable;
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 ParrotEntity extends TameableEntity {
public ParrotEntity(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) {
// Parrot color
if (entityMetadata.getId() == 18) {
metadata.put(EntityData.VARIANT, entityMetadata.getValue());
}
super.updateBedrockMetadata(entityMetadata, session);
}
}

View file

@ -27,6 +27,7 @@ package org.geysermc.connector.entity.living.animal.tameable;
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.living.animal.AnimalEntity;
import org.geysermc.connector.entity.type.EntityType;
@ -45,6 +46,11 @@ public class TameableEntity extends AnimalEntity {
metadata.getFlags().setFlag(EntityFlag.SITTING, (xd & 0x01) == 0x01);
metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 0x02) == 0x02);
metadata.getFlags().setFlag(EntityFlag.TAMED, (xd & 0x04) == 0x04);
// Must be set for wolf collar color to work
// Extending it to all entities to prevent future bugs
if (metadata.getFlags().getFlag(EntityFlag.TAMED)) {
metadata.put(EntityData.OWNER_EID, session.getPlayerEntity().getGeyserId());
} // Can't de-tame an entity so no resetting the owner ID
}
super.updateBedrockMetadata(entityMetadata, session);
}

View file

@ -28,6 +28,7 @@ package org.geysermc.connector.entity.living.animal.tameable;
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;
@ -39,9 +40,14 @@ public class WolfEntity extends TameableEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
// "Begging" on wiki.vg, "Interested" in Nukkit - the tilt of the head
if (entityMetadata.getId() == 18) {
metadata.getFlags().setFlag(EntityFlag.INTERESTED, (boolean) entityMetadata.getValue());
}
// Wolf collar color
// Relies on EntityData.OWNER_EID being set in TameableEntity.java
if (entityMetadata.getId() == 19) {
// FIXME: Colors the whole animal instead of just collar
// metadata.put(EntityData.COLOR, (byte) (int) entityMetadata.getValue());
metadata.put(EntityData.COLOR, (byte) (int) entityMetadata.getValue());
}
super.updateBedrockMetadata(entityMetadata, session);
}

View file

@ -23,9 +23,10 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.entity.living;
package org.geysermc.connector.entity.living.merchant;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.living.AgeableEntity;
import org.geysermc.connector.entity.type.EntityType;
public class AbstractMerchantEntity extends AgeableEntity {

View file

@ -0,0 +1,108 @@
/*
* 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.merchant;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.EntityData;
import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
public class VillagerEntity extends AbstractMerchantEntity {
private static final Int2IntMap VILLAGER_VARIANTS = new Int2IntOpenHashMap();
private static final Int2IntMap VILLAGER_REGIONS = new Int2IntOpenHashMap();
static {
// Java villager profession IDs -> Bedrock
VILLAGER_VARIANTS.put(0, 0);
VILLAGER_VARIANTS.put(1, 8);
VILLAGER_VARIANTS.put(2, 11);
VILLAGER_VARIANTS.put(3, 6);
VILLAGER_VARIANTS.put(4, 7);
VILLAGER_VARIANTS.put(5, 1);
VILLAGER_VARIANTS.put(6, 2);
VILLAGER_VARIANTS.put(7, 4);
VILLAGER_VARIANTS.put(8, 12);
VILLAGER_VARIANTS.put(9, 5);
VILLAGER_VARIANTS.put(10, 13);
VILLAGER_VARIANTS.put(11, 14);
VILLAGER_VARIANTS.put(12, 3);
VILLAGER_VARIANTS.put(13, 10);
VILLAGER_VARIANTS.put(14, 9);
VILLAGER_REGIONS.put(0, 1);
VILLAGER_REGIONS.put(1, 2);
VILLAGER_REGIONS.put(2, 0);
VILLAGER_REGIONS.put(3, 3);
VILLAGER_REGIONS.put(4, 4);
VILLAGER_REGIONS.put(5, 5);
VILLAGER_REGIONS.put(6, 6);
}
public VillagerEntity(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() == 17) {
VillagerData villagerData = (VillagerData) entityMetadata.getValue();
// Profession
metadata.put(EntityData.VARIANT, VILLAGER_VARIANTS.get(villagerData.getProfession()));
//metadata.put(EntityData.SKIN_ID, villagerData.getType()); Looks like this is modified but for any reason?
// Region
metadata.put(EntityData.MARK_VARIANT, VILLAGER_REGIONS.get(villagerData.getType()));
// Trade tier - different indexing in Bedrock
metadata.put(EntityData.TRADE_TIER, villagerData.getLevel() - 1);
}
super.updateBedrockMetadata(entityMetadata, session);
}
@Override
public void spawnEntity(GeyserSession session) {
AddEntityPacket addEntityPacket = new AddEntityPacket();
// "v2" or else it's the legacy villager
addEntityPacket.setIdentifier("minecraft:villager_v2");
addEntityPacket.setRuntimeEntityId(geyserId);
addEntityPacket.setUniqueEntityId(geyserId);
addEntityPacket.setPosition(position);
addEntityPacket.setMotion(motion);
addEntityPacket.setRotation(getBedrockRotation());
addEntityPacket.setEntityType(entityType.getType());
addEntityPacket.getMetadata().putAll(metadata);
valid = true;
session.getUpstream().sendPacket(addEntityPacket);
session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
}
}

View file

@ -29,13 +29,9 @@ import lombok.Getter;
import org.geysermc.connector.entity.*;
import org.geysermc.connector.entity.living.*;
import org.geysermc.connector.entity.living.animal.*;
import org.geysermc.connector.entity.living.animal.tameable.CatEntity;
import org.geysermc.connector.entity.living.animal.tameable.TameableEntity;
import org.geysermc.connector.entity.living.animal.horse.AbstractHorseEntity;
import org.geysermc.connector.entity.living.animal.horse.ChestedHorseEntity;
import org.geysermc.connector.entity.living.animal.horse.HorseEntity;
import org.geysermc.connector.entity.living.animal.horse.LlamaEntity;
import org.geysermc.connector.entity.living.animal.tameable.WolfEntity;
import org.geysermc.connector.entity.living.animal.horse.*;
import org.geysermc.connector.entity.living.animal.tameable.*;
import org.geysermc.connector.entity.living.merchant.*;
import org.geysermc.connector.entity.living.monster.*;
import org.geysermc.connector.entity.living.monster.raid.AbstractIllagerEntity;
import org.geysermc.connector.entity.living.monster.raid.RaidParticipantEntity;
@ -49,7 +45,7 @@ public enum EntityType {
PIG(PigEntity.class, 12, 0.9f),
SHEEP(SheepEntity.class, 13, 1.3f, 0.9f),
WOLF(WolfEntity.class, 14, 0.85f, 0.6f),
VILLAGER(AbstractMerchantEntity.class, 15, 1.8f, 0.6f, 0.6f, 1.62f),
VILLAGER(VillagerEntity.class, 15, 1.8f, 0.6f, 0.6f, 1.62f),
MOOSHROOM(AnimalEntity.class, 16, 1.4f, 0.9f),
SQUID(WaterEntity.class, 17, 0.8f),
RABBIT(RabbitEntity.class, 18, 0.5f, 0.4f),
@ -64,8 +60,8 @@ public enum EntityType {
ZOMBIE_HORSE(AbstractHorseEntity.class, 27, 1.6f, 1.3965f),
POLAR_BEAR(PolarBearEntity.class, 28, 1.4f, 1.3f),
LLAMA(LlamaEntity.class, 29, 1.87f, 0.9f),
TRADER_LLAMA(LlamaEntity.class, 29, 1.187f, 0.9f),
PARROT(TameableEntity.class, 30, 0.9f, 0.5f),
TRADER_LLAMA(TraderLlamaEntity.class, 29, 1.187f, 0.9f),
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),
CREEPER(CreeperEntity.class, 33, 1.7f, 0.6f, 0.6f, 1.62f),
@ -103,7 +99,7 @@ public enum EntityType {
PLAYER(PlayerEntity.class, 63, 1.8f, 0.6f, 0.6f, 1.62f),
ITEM(ItemEntity.class, 64, 0.25f, 0.25f),
TNT(Entity.class, 65, 0.98f, 0.98f),
FALLING_BLOCK(Entity.class, 66, 0.98f, 0.98f),
FALLING_BLOCK(FallingBlockEntity.class, 66, 0.98f, 0.98f),
MOVING_BLOCK(Entity.class, 67, 0f),
EXPERIENCE_BOTTLE(ThrowableEntity.class, 68, 0.25f, 0.25f),
EXPERIENCE_ORB(ExpOrbEntity.class, 69, 0f),

View file

@ -27,6 +27,7 @@ package org.geysermc.connector.network;
import com.nukkitx.protocol.bedrock.BedrockPacket;
import com.nukkitx.protocol.bedrock.packet.*;
import org.geysermc.common.AuthType;
import org.geysermc.common.IGeyserConfiguration;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
@ -107,7 +108,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
@Override
public boolean handle(MovePlayerPacket packet) {
if (!session.isLoggedIn() && !session.isLoggingIn()) {
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);

View file

@ -26,6 +26,7 @@
package org.geysermc.connector.network.session;
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.game.entity.player.GameMode;
@ -70,6 +71,7 @@ import org.geysermc.connector.network.session.cache.*;
import org.geysermc.connector.network.translators.Registry;
import org.geysermc.connector.network.translators.block.BlockTranslator;
import org.geysermc.connector.utils.ChunkUtils;
import org.geysermc.connector.utils.LocaleUtils;
import org.geysermc.connector.utils.Toolbox;
import org.geysermc.floodgate.util.BedrockData;
import org.geysermc.floodgate.util.EncryptionUtil;
@ -156,15 +158,6 @@ public class GeyserSession implements CommandSender {
public void connect(RemoteServer remoteServer) {
startGame();
this.remoteServer = remoteServer;
if (connector.getAuthType() != AuthType.ONLINE) {
connector.getLogger().info(
"Attempting to login using " + connector.getAuthType().name().toLowerCase() + " mode... " +
(connector.getAuthType() == AuthType.OFFLINE ?
"authentication is disabled." : "authentication will be encrypted"
)
);
authenticate(authData.getName());
}
ChunkUtils.sendEmptyChunks(this, playerEntity.getPosition().toInt(), 0, false);
@ -186,6 +179,18 @@ public class GeyserSession implements CommandSender {
upstream.sendPacket(playStatusPacket);
}
public void login() {
if (connector.getAuthType() != AuthType.ONLINE) {
connector.getLogger().info(
"Attempting to login using " + connector.getAuthType().name().toLowerCase() + " mode... " +
(connector.getAuthType() == AuthType.OFFLINE ?
"authentication is disabled." : "authentication will be encrypted"
)
);
authenticate(authData.getName());
}
}
public void authenticate(String username) {
authenticate(username, "");
}
@ -196,7 +201,7 @@ public class GeyserSession implements CommandSender {
return;
}
loggedIn = true;
loggingIn = true;
// new thread so clients don't timeout
new Thread(() -> {
try {
@ -265,6 +270,17 @@ public class GeyserSession implements CommandSender {
connector.getLogger().info(authData.getName() + " (logged in as: " + protocol.getProfile().getName() + ")" + " has connected to remote java server on address " + remoteServer.getAddress());
playerEntity.setUuid(protocol.getProfile().getId());
playerEntity.setUsername(protocol.getProfile().getName());
String locale = clientData.getLanguageCode();
// Let the user know there locale may take some time to download
// as it has to be extracted from a JAR
if (locale.toLowerCase().equals("en_us") && !LocaleUtils.LOCALE_MAPPINGS.containsKey("en_us")) {
sendMessage("Downloading your locale (en_us) this may take some time");
}
// Download and load the language for the player
LocaleUtils.downloadAndLoadLocale(locale);
}
@Override
@ -295,6 +311,9 @@ public class GeyserSession implements CommandSender {
downstream.getSession().connect();
connector.addPlayer(this);
} catch (InvalidCredentialsException e) {
connector.getLogger().info("User '" + username + "' entered invalid login info, kicking.");
disconnect("Invalid/incorrect login info");
} catch (RequestException ex) {
ex.printStackTrace();
}

View file

@ -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<PlayerActionPacket
session.getDownstream().getSession().send(stopSleepingPacket);
break;
case BLOCK_INTERACT:
ClientPlayerPlaceBlockPacket blockPacket = new ClientPlayerPlaceBlockPacket(position,
BlockFace.values()[packet.getFace()],
Hand.MAIN_HAND, 0, 0, 0, false);
session.getDownstream().getSession().send(blockPacket);
// Handled in BedrockInventoryTransactionTranslator
break;
case START_BREAK:
ClientPlayerActionPacket startBreakingPacket = new ClientPlayerActionPacket(PlayerAction.START_DIGGING, new Position(packet.getBlockPosition().getX(),

View file

@ -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.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
@ -63,23 +64,33 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
InventoryUtils.updateCursor(session);
break;
case ITEM_USE:
if (packet.getActionType() == 1) {
ClientPlayerUseItemPacket useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
session.getDownstream().getSession().send(useItemPacket);
} else if (packet.getActionType() == 2) {
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);
switch (packet.getActionType()) {
case 0:
ClientPlayerPlaceBlockPacket blockPacket = new ClientPlayerPlaceBlockPacket(
new Position(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()),
BlockFace.values()[packet.getFace()],
Hand.MAIN_HAND,
packet.getClickPosition().getX(), packet.getClickPosition().getY(), packet.getClickPosition().getZ(),
false);
session.getDownstream().getSession().send(blockPacket);
break;
case 1:
ClientPlayerUseItemPacket useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
session.getDownstream().getSession().send(useItemPacket);
break;
case 2:
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);
break;
}
break;
case ITEM_RELEASE:
if (packet.getActionType() == 0) {
ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, new Position(
packet.getBlockPosition().getX(),
packet.getBlockPosition().getY(),
packet.getBlockPosition().getZ()
), BlockFace.values()[packet.getFace()]);
// 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);
}
break;

View file

@ -40,6 +40,7 @@ public class BedrockSetLocalPlayerAsInitializedTranslator extends PacketTranslat
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()) {

View file

@ -62,6 +62,9 @@ public class BlockTranslator {
private static final Map<String, BlockState> JAVA_ID_BLOCK_MAP = new HashMap<>();
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 Map<BlockState, String> JAVA_ID_TO_BLOCK_ENTITY_MAP = new HashMap<>();
private static final Object2ByteMap<BlockState> BED_COLORS = new Object2ByteOpenHashMap<>();
@ -238,6 +241,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);
}

View file

@ -51,7 +51,7 @@ public class JavaBossBarTranslator extends PacketTranslator<ServerBossBarPacket>
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<ServerBossBarPacket>
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);

View file

@ -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<ServerChatPacket> {
@ -58,14 +60,20 @@ public class JavaChatTranslator extends PacketTranslator<ServerChatPacket> {
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<String> 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);

View file

@ -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;
@ -69,7 +68,6 @@ public class JavaJoinGameTranslator extends PacketTranslator<ServerJoinGamePacke
session.setRenderDistance(packet.getViewDistance());
if (DimensionUtils.javaToBedrock(packet.getDimension()) != entity.getDimension()) {
ChunkUtils.sendEmptyChunks(session, entity.getPosition().toInt(), 3, true);
DimensionUtils.switchDimension(session, packet.getDimension());
}
}

View file

@ -28,7 +28,9 @@ package org.geysermc.connector.network.translators.java.entity.spawn;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import com.github.steveice10.mc.protocol.data.game.entity.type.object.FallingBlockData;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.FallingBlockEntity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
@ -59,12 +61,18 @@ public class JavaSpawnObjectTranslator extends PacketTranslator<ServerSpawnObjec
Class<? extends Entity> 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();

View file

@ -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<ServerEntityCollectItemPacket> {
@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);
}
}

View file

@ -40,7 +40,7 @@ public class JavaMapDataTranslator extends PacketTranslator<ServerMapDataPacket>
ClientboundMapItemDataPacket mapItemDataPacket = new ClientboundMapItemDataPacket();
mapItemDataPacket.setUniqueMapId(packet.getMapId());
mapItemDataPacket.setDimensionId(session.getLastDimPacket().getDimension());
mapItemDataPacket.setDimensionId(session.getPlayerEntity().getDimension());
mapItemDataPacket.setLocked(packet.isLocked());
mapItemDataPacket.setScale(packet.getScale());

View file

@ -65,4 +65,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);
}
}

View file

@ -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<String, Map<String, String>> LOCALE_MAPPINGS = new HashMap<>();
private static final Map<String, Asset> 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<Map.Entry<String, JsonNode>> assetIterator = assets.fields();
while (assetIterator.hasNext()) {
Map.Entry<String, JsonNode> 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<Map.Entry<String, JsonNode>> localeIterator = localeObj.fields();
Map<String, String> langMap = new HashMap<>();
while (localeIterator.hasNext()) {
Map.Entry<String, JsonNode> 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<String, String> 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<Version> 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<String, VersionDownload> 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;
}

View file

@ -26,30 +26,29 @@
package org.geysermc.connector.utils;
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
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.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.Collections;
import java.util.List;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MessageUtils {
public static List<String> getTranslationParams(Message[] messages) {
public static List<String> getTranslationParams(Message[] messages, String locale) {
List<String> strings = new ArrayList<>();
for (Message message : messages) {
if (message instanceof TranslationMessage) {
TranslationMessage translation = (TranslationMessage) message;
String builder = "%" + translation.getTranslationKey();
strings.add(builder);
if (locale == null) {
String builder = "%" + translation.getTranslationKey();
strings.add(builder);
}
if (translation.getTranslationKey().equals("commands.gamemode.success.other")) {
strings.add("");
@ -59,11 +58,16 @@ public class MessageUtils {
strings.add(" - no permission or invalid command!");
}
strings.addAll(getTranslationParams(translation.getTranslationParams()));
List<String> furtherParams = getTranslationParams(translation.getTranslationParams());
if (locale != null) {
strings.add(insertParams(LocaleUtils.getLocaleString(translation.getTranslationKey(), locale), furtherParams));
}else{
strings.addAll(furtherParams);
}
} else {
String builder = getFormat(message.getStyle().getFormats()) +
getColor(message.getStyle().getColor()) +
getBedrockMessage(message);
getColorOrParent(message.getStyle());
builder += getTranslatedBedrockMessage(message, locale, false);
strings.add(builder);
}
}
@ -71,29 +75,77 @@ public class MessageUtils {
return strings;
}
public static List<String> getTranslationParams(Message[] messages) {
return getTranslationParams(messages, null);
}
public static String getTranslationText(TranslationMessage message) {
return getFormat(message.getStyle().getFormats()) + getColor(message.getStyle().getColor())
return getFormat(message.getStyle().getFormats()) + getColorOrParent(message.getStyle())
+ "%" + message.getTranslationKey();
}
public static String getBedrockMessage(Message message) {
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<String> 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
}
}
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) {
String base = "\u00a7";
switch (color) {

View file

@ -42,9 +42,7 @@ import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.item.ToolItemEntry;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.util.*;
public class Toolbox {
@ -57,6 +55,8 @@ public class Toolbox {
public static final Int2ObjectMap<ItemEntry> ITEM_ENTRIES = new Int2ObjectOpenHashMap<>();
public static final Map<String, Map<String, String>> LOCALE_MAPPINGS = new HashMap<>();
static {
/* Load biomes */
InputStream biomestream = GeyserConnector.class.getClassLoader().getResourceAsStream("bedrock/biome_definitions.dat");
@ -129,6 +129,9 @@ public class Toolbox {
itemIndex++;
}
// Load the locale data
LocaleUtils.init();
stream = getResource("bedrock/creative_items.json");
JsonNode creativeItemEntries;

View file

@ -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);
}
}
}

View file

@ -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/.