forked from GeyserMC/Geyser
Merge branch 'master' into inventory
This commit is contained in:
commit
605c9ef4db
41 changed files with 1072 additions and 105 deletions
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -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
1
.gitignore
vendored
|
@ -225,3 +225,4 @@ nbdist/
|
|||
config.yml
|
||||
logs/
|
||||
public-key.pem
|
||||
locales/
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -52,7 +52,7 @@ public class GeyserLogger extends SimpleTerminalConsole implements IGeyserLogger
|
|||
|
||||
@Override
|
||||
protected void shutdown() {
|
||||
GeyserConnector.getInstance().shutdown();
|
||||
GeyserConnector.getInstance().getBootstrap().onDisable();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -46,6 +46,8 @@ public interface IGeyserConfiguration {
|
|||
|
||||
boolean isAllowThirdPartyCapes();
|
||||
|
||||
String getDefaultLocale();
|
||||
|
||||
Path getFloodgateKeyFile();
|
||||
|
||||
IMetricsInfo getMetrics();
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 + ")");
|
||||
}
|
||||
|
||||
}
|
|
@ -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,12 +41,32 @@ 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
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
|
@ -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 + ")");
|
||||
}
|
||||
|
||||
}
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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) {
|
||||
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);
|
||||
} else if (packet.getActionType() == 2) {
|
||||
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;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
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(),
|
||||
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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
||||
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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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/.
|
||||
|
|
Loading…
Reference in a new issue