Merge pull request #11221 from AlfaRojo/fix/player-boarding-cooldown

A player boards a vehicle again right after leaving it
This commit is contained in:
rdbt authored and GitHub committed 2026-11-06 21:20:24 +00:00
commit 02641599e8
13 files changed
+13 -98

No files matched your search

-6
View File
@@ -3038,10 +3038,6 @@ struct Game<'a> {
/// Vehicles that gained a rider this tick whose `START_RIDING_TRIGGER` was
/// not fired yet (`game/boarding.rs`), for a guest's turn.
started_riding: Vec<u32>,
/// `Entity.boardingCooldown` of the acting player, and its vehicle at the
/// last tick (`game/boarding.rs`).
boarding_cooldown: i32,
riding_at_tick: Option<u32>,
/// A rotation-only write (`/rotate`, `Entity.forceSetRotation`) to the player of this
/// turn: `leave_guest` sends it as `ClientboundPlayerRotationPacket`, not a teleport.
rotation_forced: bool,
@@ -20300,8 +20296,6 @@ impl<'a> Game<'a> {
cart_models: prepared.cart_models,
riding: None,
started_riding: Vec::new(),
boarding_cooldown: 0,
riding_at_tick: None,
rotation_forced: false,
posed_rider: None,
pending_vehicle: None,
+6 -56
View File
@@ -1,23 +1,9 @@
//! The player's `Entity.boardingCooldown`: 60 ticks after leaving a vehicle in
//! which a non-forced `startRiding` (`Entity.canRide`) is refused.
//! Mobs keep theirs in `extra.boarding_cooldown`; a server's guests keep theirs
//! in `Guest::boarding_cooldown`, swapped in for their turn like `riding`.
use super::{Game, boat_passengers::BOARDING_COOLDOWN};
/// `Entity.baseTick`: the cooldown counts down while positive.
pub(super) fn tick_down(cooldown: i32) -> i32 {
(cooldown - 1).max(0)
}
/// `Entity.removePassenger`: the passenger that left sets its cooldown to 60.
/// `was` and `now` are its vehicle before and after.
pub(super) fn after_leaving(cooldown: i32, was: Option<u32>, now: Option<u32>) -> i32 {
if was.is_some() && now.is_none() {
BOARDING_COOLDOWN
} else {
cooldown
}
}
//! A player's `Entity.boardingCooldown` is always 0: `Player.removeVehicle`
//! zeroes the 60 ticks `Entity.removePassenger` just set (#11158), so
//! `Entity.canRide` only asks that the player is not sneaking, which each
//! vehicle's interaction checks. Mobs keep theirs in `extra.boarding_cooldown`
//! (`boat_passengers::BOARDING_COOLDOWN`).
use super::Game;
/// `Entity.getIndirectPassengersStream` for `vehicle`: the vehicle and every
/// vehicle riding it, directly or through another rider. `mounts` holds
@@ -63,48 +49,12 @@ impl Game<'_> {
self.trigger_started_riding(vehicle);
}
}
/// `Entity.canRide` for the player acting now (the host's, or the guest
/// whose turn it is): the cooldown has elapsed, and the player did not
/// leave a vehicle since the tick began (the tick that starts the
/// cooldown has not run yet).
pub(super) fn may_board(&self) -> bool {
self.boarding_cooldown <= 0 && !(self.riding_at_tick.is_some() && self.riding.is_none())
}
/// The host's own cooldown, once per tick.
pub(super) fn tick_boarding(&mut self) {
self.boarding_cooldown = tick_down(self.boarding_cooldown);
self.boarding_cooldown =
after_leaving(self.boarding_cooldown, self.riding_at_tick, self.riding);
self.riding_at_tick = self.riding;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn leaving_sets_sixty_and_boarding_waits_it_out() {
let mut cd = after_leaving(0, Some(7), None);
assert_eq!(cd, 60);
for _ in 0..59 {
cd = tick_down(cd);
assert!(cd > 0, "still refused");
}
cd = tick_down(cd);
assert_eq!(cd, 0, "allowed on the sixtieth tick");
}
#[test]
fn staying_or_boarding_does_not_set_it() {
assert_eq!(after_leaving(0, Some(7), Some(7)), 0);
assert_eq!(after_leaving(0, None, Some(7)), 0);
assert_eq!(after_leaving(0, None, None), 0);
}
#[test]
fn never_negative() {
assert_eq!(tick_down(0), 0);
}
#[test]
fn started_riding_reaches_every_rider_of_the_vehicle() {
// Entity.startRiding: START_RIDING_TRIGGER for every ServerPlayer in
// entityToRide.getIndirectPassengersStream(). A mob between two
-1
View File
@@ -1376,7 +1376,6 @@ impl Game<'_> {
if sneaking
|| self.boats[i].out_of_control >= 60.0
|| self.riding.is_some()
|| !self.may_board()
|| self.ridden_by_guest(self.boats[i].id)
|| !self.boat_has_room(i)
{
@@ -1316,7 +1316,6 @@ impl Game<'_> {
}
}
laps.lap("creature_retain");
self.tick_boarding();
self.sync_riders();
self.tick_skeleton_traps();
self.tick_apparition();
+1 -1
View File
@@ -339,7 +339,7 @@ impl Game<'_> {
}
// `Entity.startRiding`: a passenger first gets off what it rides
// (`stopRiding`), so a seated player moves to this cushion.
if !self.may_board() || !self.native_may_board(id) {
if !self.native_may_board(id) {
return true;
}
self.stop_riding();
+1 -1
View File
@@ -91,7 +91,7 @@ impl Game<'_> {
if harnessed && !sneaking {
// doPlayerRide.
let id = self.mobs[index].id;
if !self.may_board() || !self.native_may_board(id) {
if !self.native_may_board(id) {
return true;
}
self.break_leash(id, true, false);
@@ -197,7 +197,6 @@ pub(super) struct GuestSwap {
left: bool,
right: bool,
riding: Option<u32>,
host_boarding: (i32, Option<u32>),
host_station: Option<super::stations::Station>,
sleeping: Option<[i32; 3]>,
sleep_timer: u32,
@@ -572,11 +571,6 @@ impl Game<'_> {
self.right = play.using.is_some() || self.using.is_some();
// The guest's vehicle (Entity.vehicle) for its turn.
let riding = std::mem::replace(&mut self.riding, g.vehicle);
// The guest's boarding cooldown for its turn; `guest_server_tick`
// counts it down once per tick (Entity.baseTick), not per turn.
let host_boarding = (self.boarding_cooldown, self.riding_at_tick);
self.boarding_cooldown = g.boarding_cooldown;
self.riding_at_tick = g.vehicle;
// `Entity.isPassenger()` for that turn, from the same vehicle: the
// guest's body keeps no swim (`Entity.updateSwimming` needs it false).
// Taken from `g.vehicle` each turn, so boarding and getting out land.
@@ -692,7 +686,6 @@ impl Game<'_> {
left,
right,
riding,
host_boarding,
host_station,
sleeping,
sleep_timer,
@@ -794,7 +787,6 @@ impl Game<'_> {
left,
right,
riding,
host_boarding,
host_station,
sleeping,
sleep_timer,
@@ -975,11 +967,6 @@ impl Game<'_> {
self.guest_attachments.entry(g.uuid).or_default(),
);
let left_vehicle = g.vehicle.filter(|_| guest_vehicle.is_none());
// Entity.removePassenger: 60 ticks before it may board again.
let guest_boarding =
super::boarding::after_leaving(self.boarding_cooldown, g.vehicle, guest_vehicle);
(self.boarding_cooldown, self.riding_at_tick) = host_boarding;
g.boarding_cooldown = guest_boarding;
if guest_vehicle.is_none() {
g.vehicle_good = None;
}
@@ -4683,10 +4670,6 @@ impl Game<'_> {
/// regeneration and the food tick (ServerPlayer.doTick). Their hurt
/// sounds are heard at the guest.
fn guest_server_tick(&mut self, host: &mut LanHost, conn: u32) {
// Entity.baseTick: the boarding cooldown counts down once a tick.
if let Some(g) = host.hub.guest_mut(conn) {
g.boarding_cooldown = super::boarding::tick_down(g.boarding_cooldown);
}
self.guest_replay(host, conn);
self.guest_vehicle_tick(host, conn);
// The last move's fall stands in for the server's delta movement.
@@ -234,8 +234,6 @@ impl Game<'_> {
if dead || (boat.is_none() && !cart && !mount && !cushion) {
let g = &mut host.hub.guests[gi];
(g.vehicle, g.vehicle_good) = (None, None);
// Entity.removePassenger: the rider waits 60 ticks to board again.
g.boarding_cooldown = super::boat_passengers::BOARDING_COOLDOWN;
return;
}
let mut ejected = false;
+1 -1
View File
@@ -134,7 +134,7 @@ impl Game<'_> {
}
// doPlayerRide; mounting drops a lead.
let id = self.mobs[index].id;
if !self.may_board() || !self.native_may_board(id) {
if !self.native_may_board(id) {
return true;
}
self.break_leash(id, true, false);
+1 -6
View File
@@ -2166,12 +2166,7 @@ impl Game<'_> {
let id = self.carts[i].id;
// isVehicle: a mob already sits in it.
let has_mob = self.mobs.iter().any(|m| m.extra.mount == Some(id));
if sneaking
|| self.riding.is_some()
|| !self.may_board()
|| self.ridden_by_guest(id)
|| has_mob
{
if sneaking || self.riding.is_some() || self.ridden_by_guest(id) || has_mob {
return false;
}
if !self.native_may_board(id) {
+2 -2
View File
@@ -203,7 +203,7 @@ impl Game<'_> {
return false;
}
let id = m.id;
if !self.may_board() || !self.native_may_board(id) {
if !self.native_may_board(id) {
return true;
}
self.start_riding(id);
@@ -370,7 +370,7 @@ impl Game<'_> {
}
// doPlayerRide
let id = self.mobs[index].id;
if !self.may_board() || !self.native_may_board(id) {
if !self.native_may_board(id) {
return true;
}
let m = &mut self.mobs[index];
+1 -1
View File
@@ -129,7 +129,7 @@ impl Game<'_> {
if tame && !food {
// doPlayerRide.
let id = self.mobs[index].id;
if !self.may_board() || !self.native_may_board(id) {
if !self.native_may_board(id) {
return true;
}
self.break_leash(id, true, false);
-3
View File
@@ -133,8 +133,6 @@ pub struct Guest {
pub input: u8,
/// The boat or minecart the player rides (`Entity.getVehicle`).
pub vehicle: Option<u32>,
/// `Entity.boardingCooldown`: ticks before it may board a vehicle again.
pub boarding_cooldown: i32,
/// When the player last got into its vehicle, from `Hub.seat_order`:
/// `Entity.addPassenger` seats a rider behind the players already in the
/// vehicle, so the smallest order on a boat is its controlling rider
@@ -447,7 +445,6 @@ impl Guest {
sneaking: false,
input: 0,
vehicle: None,
boarding_cooldown: 0,
vehicle_order: 0,
vehicle_good: None,
sprinting: false,