Update Handcrafted v4.0.9

This commit is contained in:
Darto KLoning
2026-10-03 10:02:46 +07:00
parent 5ce6ef7b21
commit 98e9869daa
6 changed files with 50 additions and 24 deletions
+7
View File
@@ -1,3 +1,10 @@
## 4.0.9 — Seat synchronization fix
- Compared the Fabric 26.2 port directly against the original 1.21.x seating implementation.
- The current debug log proves server-side mounting already succeeds: the client reports `Received passengers for unknown entity`. This means the failure is no longer the chair/bench/couch interaction callback; the invisible `Seat` vehicle is not known client-side when the passenger update arrives.
- Removed the legacy custom `ClientboundAddEntityPacket`/`recreateFromPacket` override from `Seat` so Minecraft 26.2 uses its native entity spawn/tracking packet path.
- Kept one-passenger acceptance and the 26.2 passenger attachment implementation.
- `compile.bat` and `compile.sh` now read both `mod_version` and `archives_base_name` from `gradle.properties` when detecting an already-built JAR. Existing-JAR Compile/Git-only behavior and first-time Git guidance are preserved.
# Handcrafted Fabric 26.2 port
Target: Minecraft 26.2 / Fabric only / Java 25.
+3 -1
View File
@@ -31,13 +31,15 @@ for /f "usebackq tokens=1,* delims==" %%A in ("%GRADLE_PROPERTIES%") do (
if "%%A"=="mod_version" set "MOD_VERSION=%%B"
if "%%A"=="minecraft_version" set "MINECRAFT_VERSION=%%B"
if "%%A"=="maven_group" set "MAVEN_GROUP=%%B"
if "%%A"=="archives_base_name" set "ARCHIVES_BASE_NAME=%%B"
)
if not defined MOD_VERSION set "MOD_VERSION=unknown"
if not defined MINECRAFT_VERSION set "MINECRAFT_VERSION=26.2"
if not defined MAVEN_GROUP set "MAVEN_GROUP=id.dartokloning"
set "JAR_PATH=%CD%\build\libs\handcrafted-fabric-26.2-%MOD_VERSION%.jar"
if not defined ARCHIVES_BASE_NAME set "ARCHIVES_BASE_NAME=handcrafted"
set "JAR_PATH=%CD%\build\libs\%ARCHIVES_BASE_NAME%-%MOD_VERSION%.jar"
echo ============================================
echo Handcrafted - Fabric %MINECRAFT_VERSION% Build
+2 -1
View File
@@ -28,7 +28,8 @@ get_prop() { sed -n "s/^$1=//p" "$GRADLE_PROPERTIES" | tail -n 1 | tr -d '\r'; }
MOD_VERSION="$(get_prop mod_version)"; MOD_VERSION="${MOD_VERSION:-unknown}"
MINECRAFT_VERSION="$(get_prop minecraft_version)"; MINECRAFT_VERSION="${MINECRAFT_VERSION:-26.2}"
MAVEN_GROUP="$(get_prop maven_group)"; MAVEN_GROUP="${MAVEN_GROUP:-id.dartokloning}"
JAR_PATH="$PWD/build/libs/handcrafted-fabric-26.2-$MOD_VERSION.jar"
ARCHIVES_BASE_NAME="$(get_prop archives_base_name)"; ARCHIVES_BASE_NAME="${ARCHIVES_BASE_NAME:-handcrafted}"
JAR_PATH="$PWD/build/libs/$ARCHIVES_BASE_NAME-$MOD_VERSION.jar"
echo "============================================"
echo " Handcrafted - Fabric $MINECRAFT_VERSION Build"
+1 -1
View File
@@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx4G
org.gradle.parallel=true
org.gradle.caching=true
mod_version=4.0.6-fabric-26.2
mod_version=4.0.9
maven_group=earth.terrarium.handcrafted
archives_base_name=handcrafted
@@ -19,11 +19,15 @@ public interface SittableBlock {
if (!Seat.SITTING_POSITIONS.get(level.dimension()).contains(pos)) {
Seat entity = Seat.of(level, pos, dir);
if (level.addFreshEntity(entity)) {
if (player.startRiding(entity)) {
// Do not mount in the same server tick in which the invisible
// Seat is spawned. In 26.2 the passenger packet can otherwise
// reach the client before the entity-spawn packet, producing
// "Received passengers for unknown entity" and an immediate
// client/server mount desync. Queue the rider and let Seat.tick()
// mount it after the entity has entered normal tracking.
Seat.SITTING_POSITIONS.put(level.dimension(), pos.immutable());
entity.queueRider(player);
return true;
}
entity.removeSeat();
} else {
entity.removeSeat();
}
@@ -8,12 +8,8 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.util.Mth;
@@ -35,6 +31,8 @@ public class Seat extends Entity {
private AABB shape;
private boolean remove;
private boolean canRotate;
private Entity pendingRider;
private int mountDelayTicks;
public Seat(EntityType<? extends Entity> entityType, Level level) {
super(entityType, level);
@@ -66,16 +64,9 @@ public class Seat extends Entity {
return new AABB(aabb.minX, aabb.minY, aabb.minZ, aabb.maxX, aabb.maxY, aabb.maxZ);
}
@Override
public Packet<ClientGamePacketListener> getAddEntityPacket(ServerEntity serverEntity) {
return new ClientboundAddEntityPacket(this, serverEntity, canRotate ? 1 : 0);
}
@Override
public void recreateFromPacket(ClientboundAddEntityPacket packet) {
super.recreateFromPacket(packet);
this.canRotate = packet.getData() == 1;
}
// Use Minecraft's native entity spawn packet on 26.2.
// The old custom ClientboundAddEntityPacket path from 1.21.x can race the
// passenger packet, leaving the client with an unknown vehicle entity.
@Override
public boolean hurtServer(ServerLevel level, DamageSource source, float damage) {
@@ -122,11 +113,32 @@ public class Seat extends Entity {
return super.getDismountLocationForPassenger(passenger);
}
public void queueRider(Entity rider) {
this.pendingRider = rider;
// One complete entity tick is enough for ServerEntity tracking to send
// the add-entity packet before we generate a passengers packet.
this.mountDelayTicks = 1;
}
@Override
public void tick() {
super.tick();
if (!level().isClientSide() && (!(this.level().getBlockState(this.blockPosition()).getBlock() instanceof SittableBlock) || remove)) {
if (!level().isClientSide()) {
if (!(this.level().getBlockState(this.blockPosition()).getBlock() instanceof SittableBlock) || remove) {
removeSeat();
return;
}
if (pendingRider != null) {
if (mountDelayTicks-- <= 0) {
Entity rider = pendingRider;
pendingRider = null;
if (!rider.isRemoved() && rider.level() == this.level() && rider.startRiding(this)) {
return;
}
removeSeat();
}
}
}
}