3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-11-19 14:30:17 +01:00

Merge remote-tracking branch 'origin/master' into feature/extensions-gradle

Dieser Commit ist enthalten in:
RednedEpic 2022-03-19 20:48:14 -05:00
Commit 02cda38259
21 geänderte Dateien mit 284 neuen und 131 gelöschten Zeilen

Datei anzeigen

@ -20,13 +20,13 @@ Special thanks to the DragonProxy project for being a trailblazer in protocol tr
### Currently supporting Minecraft Bedrock 1.17.41 + 1.18.0 - 1.18.10 and Minecraft Java 1.18.2. ### Currently supporting Minecraft Bedrock 1.17.41 + 1.18.0 - 1.18.10 and Minecraft Java 1.18.2.
## Setting Up ## Setting Up
Take a look [here](https://github.com/GeyserMC/Geyser/wiki/Setup) for how to set up Geyser. Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
[![YouTube Video](https://img.youtube.com/vi/U7dZZ8w7Gi4/0.jpg)](https://www.youtube.com/watch?v=U7dZZ8w7Gi4) [![YouTube Video](https://img.youtube.com/vi/U7dZZ8w7Gi4/0.jpg)](https://www.youtube.com/watch?v=U7dZZ8w7Gi4)
## Links: ## Links:
- Website: https://geysermc.org - Website: https://geysermc.org
- Docs: https://github.com/GeyserMC/Geyser/wiki - Docs: https://wiki.geysermc.org/geyser/
- Download: https://ci.geysermc.org - Download: https://ci.geysermc.org
- Discord: https://discord.gg/geysermc - Discord: https://discord.gg/geysermc
- Donate: https://opencollective.com/geysermc - Donate: https://opencollective.com/geysermc
@ -39,7 +39,7 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki/Setup) for how to set
- Structure block UI - Structure block UI
## What can't be fixed ## What can't be fixed
There are a few things Geyser is unable to support due to various differences between Minecraft Bedrock and Java. For a list of these limitations, see the [Current Limitations](https://github.com/GeyserMC/Geyser/wiki/Current-Limitations) page. There are a few things Geyser is unable to support due to various differences between Minecraft Bedrock and Java. For a list of these limitations, see the [Current Limitations](https://wiki.geysermc.org/geyser/current-limitations/) page.
## Compiling ## Compiling
1. Clone the repo to your computer 1. Clone the repo to your computer

Datei anzeigen

@ -26,6 +26,7 @@
package org.geysermc.api.connection; package org.geysermc.api.connection;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.common.value.qual.IntRange;
import java.util.UUID; import java.util.UUID;
@ -55,5 +56,13 @@ public interface Connection {
*/ */
String xuid(); String xuid();
/**
* Transfer the connection to a server. A Bedrock player can successfully transfer to the same server they are
* currently playing on.
*
* @param address The address of the server
* @param port The port of the server
* @return true if the transfer was a success
*/
boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port);
} }

Datei anzeigen

@ -23,31 +23,23 @@
* @link https://github.com/GeyserMC/Geyser * @link https://github.com/GeyserMC/Geyser
*/ */
package org.geysermc.geyser.translator.protocol.java; package org.geysermc.floodgate.pluginmessage;
import com.github.steveice10.mc.protocol.data.game.UnlockRecipesAction; import com.google.common.base.Charsets;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundRecipePacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import java.util.Collections; public final class PluginMessageChannels {
public static final String SKIN = "floodgate:skin";
public static final String FORM = "floodgate:form";
public static final String TRANSFER = "floodgate:transfer";
private static final byte[] FLOODGATE_REGISTER_DATA = String.join("\0", SKIN, FORM, TRANSFER).getBytes(Charsets.UTF_8);
/** /**
* Used to list recipes that we can definitely use the recipe book for (and therefore save on packet usage) * Get the prebuilt register data as a byte array
*
* @return the register data of the Floodgate channels
*/ */
@Translator(packet = ClientboundRecipePacket.class) public static byte[] getFloodgateRegisterData() {
public class JavaRecipeTranslator extends PacketTranslator<ClientboundRecipePacket> { return FLOODGATE_REGISTER_DATA;
@Override
public void translate(GeyserSession session, ClientboundRecipePacket packet) {
if (packet.getAction() == UnlockRecipesAction.REMOVE) {
for (String identifier : packet.getRecipes()) {
session.getUnlockedRecipes().remove(identifier);
}
} else {
Collections.addAll(session.getUnlockedRecipes(), packet.getRecipes());
} }
} }
}

Datei anzeigen

@ -100,7 +100,7 @@ public class GeyserSession {
} }
public void login() { public void login() {
this.handle.login(); throw new UnsupportedOperationException();
} }
public void authenticate(String username) { public void authenticate(String username) {
@ -120,7 +120,7 @@ public class GeyserSession {
} }
public void close() { public void close() {
this.handle.close(); throw new UnsupportedOperationException();
} }
public void executeInEventLoop(Runnable runnable) { public void executeInEventLoop(Runnable runnable) {

Datei anzeigen

@ -99,13 +99,15 @@ public class LivingEntity extends Entity {
public void setLivingEntityFlags(ByteEntityMetadata entityMetadata) { public void setLivingEntityFlags(ByteEntityMetadata entityMetadata) {
byte xd = entityMetadata.getPrimitiveValue(); byte xd = entityMetadata.getPrimitiveValue();
// Blocking gets triggered when using a bow, but if we set USING_ITEM for all items, it may look like boolean isUsingItem = (xd & 0x01) == 0x01;
// you're "mining" with ex. a shield. boolean isUsingOffhand = (xd & 0x02) == 0x02;
ItemMapping shield = session.getItemMappings().getStoredItems().shield(); ItemMapping shield = session.getItemMappings().getStoredItems().shield();
boolean isUsingShield = (getHand().getId() == shield.getBedrockId() || boolean isUsingShield = hasShield(isUsingOffhand, shield);
getHand().equals(ItemData.AIR) && getOffHand().getId() == shield.getBedrockId());
setFlag(EntityFlag.USING_ITEM, (xd & 0x01) == 0x01 && !isUsingShield); setFlag(EntityFlag.USING_ITEM, isUsingItem && !isUsingShield);
setFlag(EntityFlag.BLOCKING, (xd & 0x01) == 0x01); // Override the blocking
setFlag(EntityFlag.BLOCKING, isUsingItem && isUsingShield);
// Riptide spin attack // Riptide spin attack
setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, (xd & 0x04) == 0x04); setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, (xd & 0x04) == 0x04);
@ -142,6 +144,14 @@ public class LivingEntity extends Entity {
} }
} }
protected boolean hasShield(boolean offhand, ItemMapping shieldMapping) {
if (offhand) {
return offHand.getId() == shieldMapping.getBedrockId();
} else {
return hand.getId() == shieldMapping.getBedrockId();
}
}
@Override @Override
protected boolean isShaking() { protected boolean isShaking() {
return isMaxFrozenState; return isMaxFrozenState;

Datei anzeigen

@ -61,7 +61,13 @@ public class TameableEntity extends AnimalEntity {
// Note: Must be set for wolf collar color to work // Note: Must be set for wolf collar color to work
if (entityMetadata.getValue().isPresent()) { if (entityMetadata.getValue().isPresent()) {
// Owner UUID of entity // Owner UUID of entity
Entity entity = session.getEntityCache().getPlayerEntity(entityMetadata.getValue().get()); UUID uuid = entityMetadata.getValue().get();
Entity entity;
if (uuid.equals(session.getPlayerEntity().getUuid())) {
entity = session.getPlayerEntity();
} else {
entity = session.getEntityCache().getPlayerEntity(uuid);
}
// Used as both a check since the player isn't in the entity cache and a normal fallback // Used as both a check since the player isn't in the entity cache and a normal fallback
if (entity == null) { if (entity == null) {
// Set to tame, but indicate that we are not the player that owns this // Set to tame, but indicate that we are not the player that owns this

Datei anzeigen

@ -38,6 +38,7 @@ import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.Getter; import lombok.Getter;
import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.AttributeUtils; import org.geysermc.geyser.util.AttributeUtils;
@ -167,6 +168,16 @@ public class SessionPlayerEntity extends PlayerEntity {
return super.createHealthAttribute(); return super.createHealthAttribute();
} }
@Override
protected boolean hasShield(boolean offhand, ItemMapping shieldMapping) {
// Must be overridden to point to the player's inventory cache
if (offhand) {
return session.getPlayerInventory().getOffhand().getJavaId() == shieldMapping.getJavaId();
} else {
return session.getPlayerInventory().getItemInHand().getJavaId() == shieldMapping.getJavaId();
}
}
@Override @Override
public void updateBedrockMetadata() { public void updateBedrockMetadata() {
super.updateBedrockMetadata(); super.updateBedrockMetadata();

Datei anzeigen

@ -36,8 +36,11 @@ import com.github.steveice10.mc.protocol.MinecraftProtocol;
import com.github.steveice10.mc.protocol.data.ProtocolState; import com.github.steveice10.mc.protocol.data.ProtocolState;
import com.github.steveice10.mc.protocol.data.UnexpectedEncryptionException; import com.github.steveice10.mc.protocol.data.UnexpectedEncryptionException;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
import com.github.steveice10.mc.protocol.data.game.entity.object.Direction;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import com.github.steveice10.mc.protocol.data.game.entity.player.HandPreference; import com.github.steveice10.mc.protocol.data.game.entity.player.HandPreference;
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
import com.github.steveice10.mc.protocol.data.game.setting.ChatVisibility; import com.github.steveice10.mc.protocol.data.game.setting.ChatVisibility;
import com.github.steveice10.mc.protocol.data.game.setting.SkinPart; import com.github.steveice10.mc.protocol.data.game.setting.SkinPart;
import com.github.steveice10.mc.protocol.data.game.statistic.CustomStatistic; import com.github.steveice10.mc.protocol.data.game.statistic.CustomStatistic;
@ -46,6 +49,8 @@ import com.github.steveice10.mc.protocol.packet.handshake.serverbound.ClientInte
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundClientInformationPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundClientInformationPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundUseItemPacket;
import com.github.steveice10.mc.protocol.packet.login.serverbound.ServerboundCustomQueryPacket; import com.github.steveice10.mc.protocol.packet.login.serverbound.ServerboundCustomQueryPacket;
import com.github.steveice10.packetlib.BuiltinFlags; import com.github.steveice10.packetlib.BuiltinFlags;
import com.github.steveice10.packetlib.Session; import com.github.steveice10.packetlib.Session;
@ -73,6 +78,7 @@ import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.Setter; import lombok.Setter;
import org.checkerframework.common.value.qual.IntRange;
import org.geysermc.common.PlatformType; import org.geysermc.common.PlatformType;
import org.geysermc.cumulus.Form; import org.geysermc.cumulus.Form;
import org.geysermc.cumulus.util.FormBuilder; import org.geysermc.cumulus.util.FormBuilder;
@ -97,6 +103,7 @@ import org.geysermc.geyser.level.physics.CollisionManager;
import org.geysermc.geyser.network.netty.LocalSession; import org.geysermc.geyser.network.netty.LocalSession;
import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.BlockMappings; import org.geysermc.geyser.registry.type.BlockMappings;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.session.auth.AuthData; import org.geysermc.geyser.session.auth.AuthData;
import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.session.auth.AuthType;
@ -107,10 +114,7 @@ import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.text.MinecraftLocale;
import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.ChunkUtils; import org.geysermc.geyser.util.*;
import org.geysermc.geyser.util.DimensionUtils;
import org.geysermc.geyser.util.LoginEncryptionUtils;
import org.geysermc.geyser.util.MathUtils;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.net.ConnectException; import java.net.ConnectException;
@ -349,7 +353,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Setter @Setter
private Int2ObjectMap<GeyserRecipe> craftingRecipes; private Int2ObjectMap<GeyserRecipe> craftingRecipes;
private final Set<String> unlockedRecipes;
private final AtomicInteger lastRecipeNetId; private final AtomicInteger lastRecipeNetId;
/** /**
@ -423,6 +426,13 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Setter @Setter
private long lastVehicleMoveTimestamp = System.currentTimeMillis(); private long lastVehicleMoveTimestamp = System.currentTimeMillis();
/**
* Counts how many ticks have occurred since an arm animation started.
* -1 means there is no active arm swing.
*/
@Getter(AccessLevel.NONE)
private int armAnimationTicks = -1;
/** /**
* Controls whether the daylight cycle gamerule has been sent to the client, so the sun/moon remain motionless. * Controls whether the daylight cycle gamerule has been sent to the client, so the sun/moon remain motionless.
*/ */
@ -527,7 +537,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
this.playerInventory = new PlayerInventory(); this.playerInventory = new PlayerInventory();
this.openInventory = null; this.openInventory = null;
this.craftingRecipes = new Int2ObjectOpenHashMap<>(); this.craftingRecipes = new Int2ObjectOpenHashMap<>();
this.unlockedRecipes = new ObjectOpenHashSet<>();
this.lastRecipeNetId = new AtomicInteger(1); this.lastRecipeNetId = new AtomicInteger(1);
this.spawned = false; this.spawned = false;
@ -617,17 +626,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
upstream.sendPacket(gamerulePacket); upstream.sendPacket(gamerulePacket);
} }
public void login() {
if (this.remoteAuthType != AuthType.ONLINE) {
if (this.remoteAuthType == AuthType.OFFLINE) {
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.auth.login.offline"));
} else {
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.auth.login.floodgate"));
}
authenticate(authData.name());
}
}
public void authenticate(String username) { public void authenticate(String username) {
authenticate(username, ""); authenticate(username, "");
} }
@ -1038,10 +1036,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
closed = true; closed = true;
} }
public void close() {
disconnect(GeyserLocale.getPlayerLocaleString("geyser.network.close", getClientData().getLanguageCode()));
}
/** /**
* Executes a task and prints a stack trace if an error occurs. * Executes a task and prints a stack trace if an error occurs.
*/ */
@ -1109,6 +1103,34 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
for (Tickable entity : entityCache.getTickableEntities()) { for (Tickable entity : entityCache.getTickableEntities()) {
entity.tick(); entity.tick();
} }
if (armAnimationTicks != -1) {
// As of 1.18.2 Java Edition, it appears that the swing time is dynamically updated depending on the
// player's effect status, but the animation can cut short if the duration suddenly decreases
// (from suddenly no longer having mining fatigue, for example)
// This math is referenced from Java Edition 1.18.2
int swingTotalDuration;
int hasteLevel = Math.max(effectCache.getHaste(), effectCache.getConduitPower());
if (hasteLevel > 0) {
swingTotalDuration = 6 - hasteLevel;
} else {
int miningFatigueLevel = effectCache.getMiningFatigue();
if (miningFatigueLevel > 0) {
swingTotalDuration = 6 + miningFatigueLevel * 2;
} else {
swingTotalDuration = 6;
}
}
if (++armAnimationTicks >= swingTotalDuration) {
if (sneaking) {
// Attempt to re-activate blocking as our swing animation is up
if (attemptToBlock()) {
playerEntity.updateBedrockMetadata();
}
}
armAnimationTicks = -1;
}
}
} catch (Throwable throwable) { } catch (Throwable throwable) {
throwable.printStackTrace(); throwable.printStackTrace();
} }
@ -1118,7 +1140,23 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
this.authData = authData; this.authData = authData;
} }
public void setSneaking(boolean sneaking) { public void startSneaking() {
// Toggle the shield, if there is no ongoing arm animation
// This matches Bedrock Edition behavior as of 1.18.12
if (armAnimationTicks == -1) {
attemptToBlock();
}
setSneaking(true);
}
public void stopSneaking() {
disableBlocking();
setSneaking(false);
}
private void setSneaking(boolean sneaking) {
this.sneaking = sneaking; this.sneaking = sneaking;
// Update pose and bounding box on our end // Update pose and bounding box on our end
@ -1203,6 +1241,54 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
return null; return null;
} }
/**
* Checks to see if a shield is in either hand to activate blocking. If so, it sets the Bedrock client to display
* blocking and sends a packet to the Java server.
*/
private boolean attemptToBlock() {
ItemMapping shield = itemMappings.getStoredItems().shield();
ServerboundUseItemPacket useItemPacket;
if (playerInventory.getItemInHand().getJavaId() == shield.getJavaId()) {
useItemPacket = new ServerboundUseItemPacket(Hand.MAIN_HAND);
} else if (playerInventory.getOffhand().getJavaId() == shield.getJavaId()) {
useItemPacket = new ServerboundUseItemPacket(Hand.OFF_HAND);
} else {
// No blocking
return false;
}
sendDownstreamPacket(useItemPacket);
playerEntity.setFlag(EntityFlag.BLOCKING, true);
// Metadata should be updated later
return true;
}
/**
* Starts ticking the amount of time that the Bedrock client has been swinging their arm, and disables blocking if
* blocking.
*/
public void activateArmAnimationTicking() {
armAnimationTicks = 0;
if (disableBlocking()) {
playerEntity.updateBedrockMetadata();
}
}
/**
* Indicates to the client to stop blocking and tells the Java server the same.
*/
private boolean disableBlocking() {
if (playerEntity.getFlag(EntityFlag.BLOCKING)) {
ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM,
BlockUtils.POSITION_ZERO, Direction.DOWN);
sendDownstreamPacket(releaseItemPacket);
playerEntity.setFlag(EntityFlag.BLOCKING, false);
return true;
}
return false;
}
/** /**
* Will be overwritten for GeyserConnect. * Will be overwritten for GeyserConnect.
*/ */
@ -1225,6 +1311,21 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
return authData.xuid(); return authData.xuid();
} }
@SuppressWarnings("ConstantConditions") // Need to enforce the parameter annotations
@Override
public boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port) {
if (address == null || address.isBlank()) {
throw new IllegalArgumentException("Server address cannot be null or blank");
} else if (port < 0 || port > 65535) {
throw new IllegalArgumentException("Server port must be between 0 and 65535, was " + port);
}
TransferPacket transferPacket = new TransferPacket();
transferPacket.setAddress(address);
transferPacket.setPort(port);
sendUpstreamPacket(transferPacket);
return true;
}
@Override @Override
public void sendMessage(String message) { public void sendMessage(String message) {
TextPacket textPacket = new TextPacket(); TextPacket textPacket = new TextPacket();

Datei anzeigen

@ -30,6 +30,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Getter; import lombok.Getter;
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
@ -48,8 +49,6 @@ import java.util.List;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.geysermc.geyser.util.PluginMessageUtils.getSkinChannel;
public final class FloodgateSkinUploader { public final class FloodgateSkinUploader {
private final ObjectMapper JACKSON = new ObjectMapper(); private final ObjectMapper JACKSON = new ObjectMapper();
private final List<String> skinQueue = new ArrayList<>(); private final List<String> skinQueue = new ArrayList<>();
@ -126,7 +125,7 @@ public final class FloodgateSkinUploader {
byte[] bytes = (value + '\0' + signature) byte[] bytes = (value + '\0' + signature)
.getBytes(StandardCharsets.UTF_8); .getBytes(StandardCharsets.UTF_8);
PluginMessageUtils.sendMessage(session, getSkinChannel(), bytes); PluginMessageUtils.sendMessage(session, PluginMessageChannels.SKIN, bytes);
} }
break; break;
case LOG_MESSAGE: case LOG_MESSAGE:

Datei anzeigen

@ -601,6 +601,8 @@ public class SkinProvider {
HttpURLConnection con = (HttpURLConnection) new URL(imageUrl).openConnection(); HttpURLConnection con = (HttpURLConnection) new URL(imageUrl).openConnection();
con.setRequestProperty("User-Agent", "Geyser-" + GeyserImpl.getInstance().getPlatformType().toString() + "/" + GeyserImpl.VERSION); con.setRequestProperty("User-Agent", "Geyser-" + GeyserImpl.getInstance().getPlatformType().toString() + "/" + GeyserImpl.VERSION);
con.setConnectTimeout(10000);
con.setReadTimeout(10000);
BufferedImage image = ImageIO.read(con.getInputStream()); BufferedImage image = ImageIO.read(con.getInputStream());
if (image == null) throw new NullPointerException(); if (image == null) throw new NullPointerException();

Datei anzeigen

@ -48,8 +48,10 @@ public class BedrockAnimateTranslator extends PacketTranslator<AnimatePacket> {
switch (packet.getAction()) { switch (packet.getAction()) {
case SWING_ARM -> case SWING_ARM ->
// Delay so entity damage can be processed first // Delay so entity damage can be processed first
session.scheduleInEventLoop(() -> session.scheduleInEventLoop(() -> {
session.sendDownstreamPacket(new ServerboundSwingPacket(Hand.MAIN_HAND)), session.sendDownstreamPacket(new ServerboundSwingPacket(Hand.MAIN_HAND));
session.activateArmAnimationTicking();
},
25, 25,
TimeUnit.MILLISECONDS TimeUnit.MILLISECONDS
); );

Datei anzeigen

@ -411,22 +411,20 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
//https://wiki.vg/Protocol#Interact_Entity //https://wiki.vg/Protocol#Interact_Entity
switch (packet.getActionType()) { switch (packet.getActionType()) {
case 0: //Interact case 0 -> processEntityInteraction(session, packet, entity); // Interact
processEntityInteraction(session, packet, entity); case 1 -> { // Attack
break; int entityId;
case 1: //Attack
if (entity.getDefinition() == EntityDefinitions.ENDER_DRAGON) { if (entity.getDefinition() == EntityDefinitions.ENDER_DRAGON) {
// Redirects the attack to its body entity, this only happens when // Redirects the attack to its body entity, this only happens when
// attacking the underbelly of the ender dragon // attacking the underbelly of the ender dragon
ServerboundInteractPacket attackPacket = new ServerboundInteractPacket(entity.getEntityId() + 3, entityId = entity.getEntityId() + 3;
InteractAction.ATTACK, session.isSneaking());
session.sendDownstreamPacket(attackPacket);
} else { } else {
ServerboundInteractPacket attackPacket = new ServerboundInteractPacket(entity.getEntityId(), entityId = entity.getEntityId();
}
ServerboundInteractPacket attackPacket = new ServerboundInteractPacket(entityId,
InteractAction.ATTACK, session.isSneaking()); InteractAction.ATTACK, session.isSneaking());
session.sendDownstreamPacket(attackPacket); session.sendDownstreamPacket(attackPacket);
} }
break;
} }
break; break;
} }

Datei anzeigen

@ -28,7 +28,10 @@ package org.geysermc.geyser.translator.protocol.bedrock.entity.player;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.entity.object.Direction; import com.github.steveice10.mc.protocol.data.game.entity.object.Direction;
import com.github.steveice10.mc.protocol.data.game.entity.player.*; import com.github.steveice10.mc.protocol.data.game.entity.player.*;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.*; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerCommandPacket;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i; import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.LevelEventType;
@ -39,10 +42,8 @@ import com.nukkitx.protocol.bedrock.packet.*;
import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.ItemFrameEntity; import org.geysermc.geyser.entity.type.ItemFrameEntity;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.inventory.PlayerInventory;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.protocol.Translator;
@ -105,38 +106,13 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
ServerboundPlayerCommandPacket startSneakPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SNEAKING); ServerboundPlayerCommandPacket startSneakPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SNEAKING);
session.sendDownstreamPacket(startSneakPacket); session.sendDownstreamPacket(startSneakPacket);
// Toggle the shield, if relevant session.startSneaking();
PlayerInventory playerInv = session.getPlayerInventory();
ItemMapping shield = session.getItemMappings().getMapping("minecraft:shield");
if ((playerInv.getItemInHand().getJavaId() == shield.getJavaId()) ||
(playerInv.getOffhand().getJavaId() == shield.getJavaId())) {
ServerboundUseItemPacket useItemPacket;
if (playerInv.getItemInHand().getJavaId() == shield.getJavaId()) {
useItemPacket = new ServerboundUseItemPacket(Hand.MAIN_HAND);
} else {
// Else we just assume it's the offhand, to simplify logic and to assure the packet gets sent
useItemPacket = new ServerboundUseItemPacket(Hand.OFF_HAND);
}
session.sendDownstreamPacket(useItemPacket);
session.getPlayerEntity().setFlag(EntityFlag.BLOCKING, true);
// metadata will be updated when sneaking
}
session.setSneaking(true);
break; break;
case STOP_SNEAK: case STOP_SNEAK:
ServerboundPlayerCommandPacket stopSneakPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SNEAKING); ServerboundPlayerCommandPacket stopSneakPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SNEAKING);
session.sendDownstreamPacket(stopSneakPacket); session.sendDownstreamPacket(stopSneakPacket);
// Stop shield, if necessary session.stopSneaking();
if (session.getPlayerEntity().getFlag(EntityFlag.BLOCKING)) {
ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, BlockUtils.POSITION_ZERO, Direction.DOWN);
session.sendDownstreamPacket(releaseItemPacket);
session.getPlayerEntity().setFlag(EntityFlag.BLOCKING, false);
// metadata will be updated when sneaking
}
session.setSneaking(false);
break; break;
case START_SPRINT: case START_SPRINT:
if (!entity.getFlag(EntityFlag.SWIMMING)) { if (!entity.getFlag(EntityFlag.SWIMMING)) {

Datei anzeigen

@ -27,6 +27,8 @@ package org.geysermc.geyser.translator.protocol.java;
import com.github.steveice10.mc.protocol.data.game.command.CommandNode; import com.github.steveice10.mc.protocol.data.game.command.CommandNode;
import com.github.steveice10.mc.protocol.data.game.command.CommandParser; import com.github.steveice10.mc.protocol.data.game.command.CommandParser;
import com.github.steveice10.mc.protocol.data.game.command.properties.ResourceProperties;
import com.github.steveice10.mc.protocol.data.game.entity.attribute.AttributeType;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundCommandsPacket; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundCommandsPacket;
import com.nukkitx.protocol.bedrock.data.command.CommandData; import com.nukkitx.protocol.bedrock.data.command.CommandData;
import com.nukkitx.protocol.bedrock.data.command.CommandEnumData; import com.nukkitx.protocol.bedrock.data.command.CommandEnumData;
@ -59,6 +61,7 @@ import java.util.*;
public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommandsPacket> { public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommandsPacket> {
private static final String[] ALL_EFFECT_IDENTIFIERS = EntityUtils.getAllEffectIdentifiers(); private static final String[] ALL_EFFECT_IDENTIFIERS = EntityUtils.getAllEffectIdentifiers();
private static final String[] ATTRIBUTES = AttributeType.Builtin.BUILTIN.keySet().toArray(new String[0]);
private static final String[] ENUM_BOOLEAN = {"true", "false"}; private static final String[] ENUM_BOOLEAN = {"true", "false"};
private static final String[] VALID_COLORS; private static final String[] VALID_COLORS;
private static final String[] VALID_SCOREBOARD_SLOTS; private static final String[] VALID_SCOREBOARD_SLOTS;
@ -209,10 +212,11 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
* Convert Java edition command types to Bedrock edition * Convert Java edition command types to Bedrock edition
* *
* @param session the session * @param session the session
* @param parser Command type to convert * @param node Command type to convert
* @return Bedrock parameter data type * @return Bedrock parameter data type
*/ */
private static Object mapCommandType(GeyserSession session, CommandParser parser) { private static Object mapCommandType(GeyserSession session, CommandNode node) {
CommandParser parser = node.getParser();
if (parser == null) { if (parser == null) {
return CommandParam.STRING; return CommandParam.STRING;
} }
@ -235,6 +239,14 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
case COLOR -> VALID_COLORS; case COLOR -> VALID_COLORS;
case SCOREBOARD_SLOT -> VALID_SCOREBOARD_SLOTS; case SCOREBOARD_SLOT -> VALID_SCOREBOARD_SLOTS;
case MOB_EFFECT -> ALL_EFFECT_IDENTIFIERS; case MOB_EFFECT -> ALL_EFFECT_IDENTIFIERS;
case RESOURCE, RESOURCE_OR_TAG -> {
String resource = ((ResourceProperties) node.getProperties()).getRegistryKey();
if (resource.equals("minecraft:attribute")) {
yield ATTRIBUTES;
} else {
yield CommandParam.STRING;
}
}
default -> CommandParam.STRING; default -> CommandParam.STRING;
}; };
} }
@ -308,7 +320,7 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
} }
} else { } else {
// Put the non-enum param into the list // Put the non-enum param into the list
Object mappedType = mapCommandType(session, paramNode.getParser()); Object mappedType = mapCommandType(session, paramNode);
CommandEnumData enumData = null; CommandEnumData enumData = null;
CommandParam type = null; CommandParam type = null;
if (mappedType instanceof String[]) { if (mappedType instanceof String[]) {

Datei anzeigen

@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundCu
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundCustomPayloadPacket; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundCustomPayloadPacket;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.nukkitx.protocol.bedrock.packet.TransferPacket; import com.nukkitx.protocol.bedrock.packet.TransferPacket;
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.session.auth.AuthType;
@ -54,7 +55,7 @@ public class JavaCustomPayloadTranslator extends PacketTranslator<ClientboundCus
String channel = packet.getChannel(); String channel = packet.getChannel();
if (channel.equals("floodgate:form")) { if (channel.equals(PluginMessageChannels.FORM)) {
byte[] data = packet.getData(); byte[] data = packet.getData();
// receive: first byte is form type, second and third are the id, remaining is the form data // receive: first byte is form type, second and third are the id, remaining is the form data
@ -81,7 +82,7 @@ public class JavaCustomPayloadTranslator extends PacketTranslator<ClientboundCus
}); });
session.sendForm(form); session.sendForm(form);
} else if (channel.equals("floodgate:transfer")) { } else if (channel.equals(PluginMessageChannels.TRANSFER)) {
byte[] data = packet.getData(); byte[] data = packet.getData();
// port, 4 bytes. remaining data, address. // port, 4 bytes. remaining data, address.

Datei anzeigen

@ -26,18 +26,67 @@
package org.geysermc.geyser.translator.protocol.java; package org.geysermc.geyser.translator.protocol.java;
import com.github.steveice10.mc.protocol.packet.login.clientbound.ClientboundLoginDisconnectPacket; import com.github.steveice10.mc.protocol.packet.login.clientbound.ClientboundLoginDisconnectPacket;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.TranslatableComponent;
import org.geysermc.common.PlatformType;
import org.geysermc.geyser.network.MinecraftProtocol;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.translator.text.MessageTranslator;
import java.util.List;
@Translator(packet = ClientboundLoginDisconnectPacket.class) @Translator(packet = ClientboundLoginDisconnectPacket.class)
public class JavaLoginDisconnectTranslator extends PacketTranslator<ClientboundLoginDisconnectPacket> { public class JavaLoginDisconnectTranslator extends PacketTranslator<ClientboundLoginDisconnectPacket> {
@Override @Override
public void translate(GeyserSession session, ClientboundLoginDisconnectPacket packet) { public void translate(GeyserSession session, ClientboundLoginDisconnectPacket packet) {
Component disconnectReason = packet.getReason();
boolean isOutdatedMessage = false;
if (disconnectReason instanceof TranslatableComponent component) {
String key = component.key();
isOutdatedMessage = "multiplayer.disconnect.incompatible".equals(key) ||
// Legacy string (starting from at least 1.15.2)
"multiplayer.disconnect.outdated_server".equals(key)
// Reproduced on 1.15.2 server with ViaVersion 4.0.0-21w20a with 1.18.2 Java client
|| key.startsWith("Outdated server!");
} else {
if (disconnectReason instanceof TextComponent component) {
if (component.content().startsWith("Outdated server!")) {
// Reproduced with vanilla 1.8.8 server and 1.18.2 Java client
isOutdatedMessage = true;
} else {
List<Component> children = component.children();
for (int i = 0; i < children.size(); i++) {
if (children.get(i) instanceof TextComponent child && child.content().startsWith("Outdated server!")) {
// Reproduced on Paper 1.17.1
isOutdatedMessage = true;
break;
}
}
}
}
}
String serverDisconnectMessage = MessageTranslator.convertMessage(disconnectReason, session.locale());
String disconnectMessage;
if (isOutdatedMessage) {
String locale = session.locale();
PlatformType platform = session.getGeyser().getPlatformType();
String outdatedType = (platform == PlatformType.BUNGEECORD || platform == PlatformType.VELOCITY) ?
"geyser.network.remote.outdated.proxy" : "geyser.network.remote.outdated.server";
disconnectMessage = GeyserLocale.getPlayerLocaleString(outdatedType, locale, MinecraftProtocol.getJavaVersions().get(0)) + '\n'
+ GeyserLocale.getPlayerLocaleString("geyser.network.remote.original_disconnect_message", locale, serverDisconnectMessage);
} else {
disconnectMessage = serverDisconnectMessage;
}
// The client doesn't manually get disconnected so we have to do it ourselves // The client doesn't manually get disconnected so we have to do it ourselves
session.disconnect(MessageTranslator.convertMessage(packet.getReason(), session.locale())); session.disconnect(disconnectMessage);
} }
@Override @Override

Datei anzeigen

@ -32,6 +32,7 @@ import com.nukkitx.protocol.bedrock.data.PlayerPermission;
import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket; import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket;
import com.nukkitx.protocol.bedrock.packet.GameRulesChangedPacket; import com.nukkitx.protocol.bedrock.packet.GameRulesChangedPacket;
import com.nukkitx.protocol.bedrock.packet.SetPlayerGameTypePacket; import com.nukkitx.protocol.bedrock.packet.SetPlayerGameTypePacket;
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.session.auth.AuthType;
@ -102,7 +103,7 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
// register the plugin messaging channels used in Floodgate // register the plugin messaging channels used in Floodgate
if (session.getRemoteAuthType() == AuthType.FLOODGATE) { if (session.getRemoteAuthType() == AuthType.FLOODGATE) {
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket("minecraft:register", PluginMessageUtils.getFloodgateRegisterData())); session.sendDownstreamPacket(new ServerboundCustomPayloadPacket("minecraft:register", PluginMessageChannels.getFloodgateRegisterData()));
} }
if (!newDimension.equals(session.getDimension())) { if (!newDimension.equals(session.getDimension())) {

Datei anzeigen

@ -201,7 +201,6 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
session.sendUpstreamPacket(craftingDataPacket); session.sendUpstreamPacket(craftingDataPacket);
session.setCraftingRecipes(recipeMap); session.setCraftingRecipes(recipeMap);
session.getUnlockedRecipes().clear();
session.setStonecutterRecipes(stonecutterRecipeMap); session.setStonecutterRecipes(stonecutterRecipeMap);
session.getLastRecipeNetId().set(netId); session.getLastRecipeNetId().set(netId);
} }

Datei anzeigen

@ -50,6 +50,9 @@ public class JavaAnimateTranslator extends PacketTranslator<ClientboundAnimatePa
switch (packet.getAnimation()) { switch (packet.getAnimation()) {
case SWING_ARM: case SWING_ARM:
animatePacket.setAction(AnimatePacket.Action.SWING_ARM); animatePacket.setAction(AnimatePacket.Action.SWING_ARM);
if (entity.getEntityId() == session.getPlayerEntity().getEntityId()) {
session.activateArmAnimationTicking();
}
break; break;
case SWING_OFFHAND: case SWING_OFFHAND:
// Use the OptionalPack to trigger the animation // Use the OptionalPack to trigger the animation

Datei anzeigen

@ -33,9 +33,7 @@ import org.geysermc.geyser.session.GeyserSession;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
public class PluginMessageUtils { public class PluginMessageUtils {
private static final String SKIN_CHANNEL = "floodgate:skin";
private static final byte[] GEYSER_BRAND_DATA; private static final byte[] GEYSER_BRAND_DATA;
private static final byte[] FLOODGATE_REGISTER_DATA;
static { static {
byte[] data = GeyserImpl.NAME.getBytes(Charsets.UTF_8); byte[] data = GeyserImpl.NAME.getBytes(Charsets.UTF_8);
@ -44,8 +42,6 @@ public class PluginMessageUtils {
.put(writeVarInt(data.length)) .put(writeVarInt(data.length))
.put(data) .put(data)
.array(); .array();
FLOODGATE_REGISTER_DATA = (SKIN_CHANNEL + "\0floodgate:form").getBytes(Charsets.UTF_8);
} }
/** /**
@ -57,22 +53,6 @@ public class PluginMessageUtils {
return GEYSER_BRAND_DATA; return GEYSER_BRAND_DATA;
} }
/**
* Get the prebuilt register data as a byte array
*
* @return the register data of the Floodgate channels
*/
public static byte[] getFloodgateRegisterData() {
return FLOODGATE_REGISTER_DATA;
}
/**
* Returns the skin channel used in Floodgate
*/
public static String getSkinChannel() {
return SKIN_CHANNEL;
}
public static void sendMessage(GeyserSession session, String channel, byte[] data) { public static void sendMessage(GeyserSession session, String channel, byte[] data) {
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(channel, data)); session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(channel, data));
} }

Datei anzeigen

@ -52,6 +52,8 @@ public class WebUtils {
HttpURLConnection con = (HttpURLConnection) url.openConnection(); HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET"); con.setRequestMethod("GET");
con.setRequestProperty("User-Agent", "Geyser-" + GeyserImpl.getInstance().getPlatformType().toString() + "/" + GeyserImpl.VERSION); // Otherwise Java 8 fails on checking updates con.setRequestProperty("User-Agent", "Geyser-" + GeyserImpl.getInstance().getPlatformType().toString() + "/" + GeyserImpl.VERSION); // Otherwise Java 8 fails on checking updates
con.setConnectTimeout(10000);
con.setReadTimeout(10000);
return connectionToString(con); return connectionToString(con);
} catch (Exception e) { } catch (Exception e) {