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:
Commit
02cda38259
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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)
|
|
||||||
*/
|
|
||||||
@Translator(packet = ClientboundRecipePacket.class)
|
|
||||||
public class JavaRecipeTranslator extends PacketTranslator<ClientboundRecipePacket> {
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void translate(GeyserSession session, ClientboundRecipePacket packet) {
|
* Get the prebuilt register data as a byte array
|
||||||
if (packet.getAction() == UnlockRecipesAction.REMOVE) {
|
*
|
||||||
for (String identifier : packet.getRecipes()) {
|
* @return the register data of the Floodgate channels
|
||||||
session.getUnlockedRecipes().remove(identifier);
|
*/
|
||||||
}
|
public static byte[] getFloodgateRegisterData() {
|
||||||
} else {
|
return FLOODGATE_REGISTER_DATA;
|
||||||
Collections.addAll(session.getUnlockedRecipes(), packet.getRecipes());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
|
@ -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:
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
);
|
);
|
||||||
|
@ -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();
|
||||||
InteractAction.ATTACK, session.isSneaking());
|
|
||||||
session.sendDownstreamPacket(attackPacket);
|
|
||||||
}
|
}
|
||||||
break;
|
ServerboundInteractPacket attackPacket = new ServerboundInteractPacket(entityId,
|
||||||
|
InteractAction.ATTACK, session.isSneaking());
|
||||||
|
session.sendDownstreamPacket(attackPacket);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -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)) {
|
||||||
|
@ -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[]) {
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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())) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren