Skip to content

Commit

Permalink
Re-implement steering indicators
Browse files Browse the repository at this point in the history
  • Loading branch information
Camotoy committed Oct 26, 2024
1 parent 774d367 commit 9d2d12b
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundPaddleBoatPacket;

import java.util.UUID;

Expand Down Expand Up @@ -182,6 +183,12 @@ public InteractionResult interact(Hand hand) {
@Override
public void tick() {
// Java sends simply "true" and "false" (is_paddling_left), Bedrock keeps sending packets as you're rowing
if (session.getPlayerEntity().getVehicle() == this) {
// For packet timing accuracy, we'll send the packets here, as that's what Java Edition 1.21.3 does.
ServerboundPaddleBoatPacket steerPacket = new ServerboundPaddleBoatPacket(session.isSteeringLeft(), session.isSteeringRight());
session.sendDownstreamGamePacket(steerPacket);
return;
}
doTick = !doTick; // Run every 100 ms
if (!doTick || passengers.isEmpty()) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,12 @@

package org.geysermc.geyser.translator.protocol.bedrock;

import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundPaddleBoatPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket;
import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket;

import java.util.concurrent.TimeUnit;

Expand All @@ -45,45 +44,32 @@ public void translate(GeyserSession session, AnimatePacket packet) {
return;
}

switch (packet.getAction()) {
case SWING_ARM -> {
session.armSwingPending();
// Delay so entity damage can be processed first
session.scheduleInEventLoop(() -> {
if (session.getArmAnimationTicks() != 0) {
// So, generally, a Java player can only do one *thing* at a time.
// If a player right-clicks, for example, then there's probably only one action associated with
// that right-click that will send a swing.
// The only exception I can think of to this, *maybe*, is a player dropping items
// Bedrock is a little funkier than this - it can send several arm animation packets in the
// same tick, notably with high levels of haste applied.
// Packet limiters do not like this and can crash the player.
// If arm animation ticks is 0, then we just sent an arm swing packet this tick.
// See https://github.com/GeyserMC/Geyser/issues/2875
// This behavior was last touched on with ViaVersion 4.5.1 (with its packet limiter), Java 1.16.5,
// and Bedrock 1.19.51.
// Note for the future: we should probably largely ignore this packet and instead replicate
// all actions on our end, and send swings where needed.
session.sendDownstreamGamePacket(new ServerboundSwingPacket(Hand.MAIN_HAND));
session.activateArmAnimationTicking();
}
},
25,
TimeUnit.MILLISECONDS
);
}
// These two might need to be flipped, but my recommendation is getting moving working first
case ROW_LEFT -> {
// Packet value is a float of how long one has been rowing, so we convert that into a boolean
session.setSteeringLeft(packet.getRowingTime() > 0.0);
ServerboundPaddleBoatPacket steerLeftPacket = new ServerboundPaddleBoatPacket(session.isSteeringLeft(), session.isSteeringRight());
session.sendDownstreamGamePacket(steerLeftPacket);
}
case ROW_RIGHT -> {
session.setSteeringRight(packet.getRowingTime() > 0.0);
ServerboundPaddleBoatPacket steerRightPacket = new ServerboundPaddleBoatPacket(session.isSteeringLeft(), session.isSteeringRight());
session.sendDownstreamGamePacket(steerRightPacket);
}
System.out.println("wewewewewewewewewewewe");
if (packet.getAction() == AnimatePacket.Action.SWING_ARM) {
session.armSwingPending();
// Delay so entity damage can be processed first
session.scheduleInEventLoop(() -> {
if (session.getArmAnimationTicks() != 0) {
// So, generally, a Java player can only do one *thing* at a time.
// If a player right-clicks, for example, then there's probably only one action associated with
// that right-click that will send a swing.
// The only exception I can think of to this, *maybe*, is a player dropping items
// Bedrock is a little funkier than this - it can send several arm animation packets in the
// same tick, notably with high levels of haste applied.
// Packet limiters do not like this and can crash the player.
// If arm animation ticks is 0, then we just sent an arm swing packet this tick.
// See https://github.com/GeyserMC/Geyser/issues/2875
// This behavior was last touched on with ViaVersion 4.5.1 (with its packet limiter), Java 1.16.5,
// and Bedrock 1.19.51.
// Note for the future: we should probably largely ignore this packet and instead replicate
// all actions on our end, and send swings where needed.
session.sendDownstreamGamePacket(new ServerboundSwingPacket(Hand.MAIN_HAND));
session.activateArmAnimationTicking();
}
},
25,
TimeUnit.MILLISECONDS
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,83 +78,86 @@ public void translate(GeyserSession session, PlayerAuthInputPacket packet) {
BedrockMovePlayerTranslator.translate(session, packet);

Set<PlayerAuthInputData> inputData = packet.getInputData();
if (!inputData.isEmpty()) {
for (PlayerAuthInputData input : inputData) {
switch (input) {
case PERFORM_ITEM_INTERACTION -> processItemUseTransaction(session, packet.getItemUseTransaction());
case PERFORM_BLOCK_ACTIONS -> BedrockBlockActions.translate(session, packet.getPlayerActions());
case START_SNEAKING -> {
ServerboundPlayerCommandPacket startSneakPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SNEAKING);
session.sendDownstreamGamePacket(startSneakPacket);
for (PlayerAuthInputData input : inputData) {
switch (input) {
case PERFORM_ITEM_INTERACTION -> processItemUseTransaction(session, packet.getItemUseTransaction());
case PERFORM_BLOCK_ACTIONS -> BedrockBlockActions.translate(session, packet.getPlayerActions());
case START_SNEAKING -> {
ServerboundPlayerCommandPacket startSneakPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SNEAKING);
session.sendDownstreamGamePacket(startSneakPacket);

session.startSneaking();
}
case STOP_SNEAKING -> {
ServerboundPlayerCommandPacket stopSneakPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SNEAKING);
session.sendDownstreamGamePacket(stopSneakPacket);
session.startSneaking();
}
case STOP_SNEAKING -> {
ServerboundPlayerCommandPacket stopSneakPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SNEAKING);
session.sendDownstreamGamePacket(stopSneakPacket);

session.stopSneaking();
session.stopSneaking();
}
case START_SPRINTING -> {
if (!entity.getFlag(EntityFlag.SWIMMING)) {
ServerboundPlayerCommandPacket startSprintPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SPRINTING);
session.sendDownstreamGamePacket(startSprintPacket);
session.setSprinting(true);
}
case START_SPRINTING -> {
if (!entity.getFlag(EntityFlag.SWIMMING)) {
ServerboundPlayerCommandPacket startSprintPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SPRINTING);
session.sendDownstreamGamePacket(startSprintPacket);
session.setSprinting(true);
}
}
case STOP_SPRINTING -> {
if (!entity.getFlag(EntityFlag.SWIMMING)) {
ServerboundPlayerCommandPacket stopSprintPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SPRINTING);
session.sendDownstreamGamePacket(stopSprintPacket);
}
case STOP_SPRINTING -> {
if (!entity.getFlag(EntityFlag.SWIMMING)) {
ServerboundPlayerCommandPacket stopSprintPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SPRINTING);
session.sendDownstreamGamePacket(stopSprintPacket);
session.setSprinting(false);
}
case START_SWIMMING -> session.setSwimming(true);
case STOP_SWIMMING -> session.setSwimming(false);
case START_FLYING -> { // Since 1.20.30
if (session.isCanFly()) {
if (session.getGameMode() == GameMode.SPECTATOR) {
// should already be flying
session.sendAdventureSettings();
break;
}
session.setSprinting(false);
}
case START_SWIMMING -> session.setSwimming(true);
case STOP_SWIMMING -> session.setSwimming(false);
case START_FLYING -> { // Since 1.20.30
if (session.isCanFly()) {
if (session.getGameMode() == GameMode.SPECTATOR) {
// should already be flying
session.sendAdventureSettings();
break;
}

if (session.getPlayerEntity().getFlag(EntityFlag.SWIMMING) && session.getCollisionManager().isPlayerInWater()) {
// As of 1.18.1, Java Edition cannot fly while in water, but it can fly while crawling
// If this isn't present, swimming on a 1.13.2 server and then attempting to fly will put you into a flying/swimming state that is invalid on JE
session.sendAdventureSettings();
break;
}

session.setFlying(true);
session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(true));
} else {
// update whether we can fly
if (session.getPlayerEntity().getFlag(EntityFlag.SWIMMING) && session.getCollisionManager().isPlayerInWater()) {
// As of 1.18.1, Java Edition cannot fly while in water, but it can fly while crawling
// If this isn't present, swimming on a 1.13.2 server and then attempting to fly will put you into a flying/swimming state that is invalid on JE
session.sendAdventureSettings();
// stop flying
PlayerActionPacket stopFlyingPacket = new PlayerActionPacket();
stopFlyingPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
stopFlyingPacket.setAction(PlayerActionType.STOP_FLYING);
stopFlyingPacket.setBlockPosition(Vector3i.ZERO);
stopFlyingPacket.setResultPosition(Vector3i.ZERO);
stopFlyingPacket.setFace(0);
session.sendUpstreamPacket(stopFlyingPacket);
break;
}

session.setFlying(true);
session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(true));
} else {
// update whether we can fly
session.sendAdventureSettings();
// stop flying
PlayerActionPacket stopFlyingPacket = new PlayerActionPacket();
stopFlyingPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
stopFlyingPacket.setAction(PlayerActionType.STOP_FLYING);
stopFlyingPacket.setBlockPosition(Vector3i.ZERO);
stopFlyingPacket.setResultPosition(Vector3i.ZERO);
stopFlyingPacket.setFace(0);
session.sendUpstreamPacket(stopFlyingPacket);
}
case STOP_FLYING -> {
session.setFlying(false);
session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(false));
}
case START_GLIDING -> {
// Otherwise gliding will not work in creative
ServerboundPlayerAbilitiesPacket playerAbilitiesPacket = new ServerboundPlayerAbilitiesPacket(false);
session.sendDownstreamGamePacket(playerAbilitiesPacket);
sendPlayerGlideToggle(session, entity);
}
case STOP_GLIDING -> sendPlayerGlideToggle(session, entity);
}
case STOP_FLYING -> {
session.setFlying(false);
session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(false));
}
case START_GLIDING -> {
// Otherwise gliding will not work in creative
ServerboundPlayerAbilitiesPacket playerAbilitiesPacket = new ServerboundPlayerAbilitiesPacket(false);
session.sendDownstreamGamePacket(playerAbilitiesPacket);
sendPlayerGlideToggle(session, entity);
}
case STOP_GLIDING -> sendPlayerGlideToggle(session, entity);
}
}
boolean up = inputData.contains(PlayerAuthInputData.UP);
// Yes. These are flipped. It's always been an issue with Geyser. That's what it's like working with this codebase.
// Hi random stranger. I am six days into updating for 1.21.3. How's it going?
session.setSteeringLeft(up || inputData.contains(PlayerAuthInputData.PADDLE_RIGHT));
session.setSteeringRight(up || inputData.contains(PlayerAuthInputData.PADDLE_LEFT));
}

private static void sendPlayerGlideToggle(GeyserSession session, Entity entity) {
Expand Down

0 comments on commit 9d2d12b

Please sign in to comment.