plane update and stuff

This commit is contained in:
2026-02-25 18:56:58 +01:00
parent 21f3acba86
commit 284cd1a547
12 changed files with 294 additions and 94 deletions

View File

@@ -6,7 +6,7 @@ minecraft_version=1.20.1
yarn_mappings=1.20.1+build.10
loader_version=0.18.3
# Mod Properties
mod_version=26.2.24.1
mod_version=26.2.25
maven_group=dev.tggamesyt
archives_base_name=szar
# Dependencies

View File

@@ -6,7 +6,9 @@ import net.minecraft.client.render.*;
import net.minecraft.client.render.entity.*;
import net.minecraft.client.render.entity.model.EntityModelLayer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.RotationAxis;
public class PlaneEntityRenderer extends EntityRenderer<PlaneEntity> {
@@ -35,26 +37,48 @@ public class PlaneEntityRenderer extends EntityRenderer<PlaneEntity> {
int light
) {
matrices.push();
// Smooth interpolation of rotation
float interpolatedYaw = entity.prevYaw + (entity.getYaw() - entity.prevYaw) * tickDelta;
float interpolatedPitch = entity.prevPitch + (entity.getPitch() - entity.prevPitch) * tickDelta;
// Scale
matrices.scale(4.0F, 4.0F, 4.0F);
// Move model to correct pivot point
matrices.translate(0.0, 1.5, 0.0);
// Rotate to match hitbox exactly
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-interpolatedYaw));
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(interpolatedPitch));
// Rotate 180° to fix backwards-facing
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180.0F));
// Flip model upright (Minecraft model fix)
matrices.scale(-1.0F, -1.0F, 1.0F);
// Set model angles
model.setAngles(
entity,
0,
0,
entity.age + tickDelta,
0,
0
interpolatedYaw,
interpolatedPitch
);
VertexConsumer consumer =
vertices.getBuffer(RenderLayer.getEntityCutout(getTexture(entity)));
model.render(matrices, consumer, light, OverlayTexture.DEFAULT_UV,
1.0F, 1.0F, 1.0F, 1.0F);
model.render(
matrices,
consumer,
light,
OverlayTexture.DEFAULT_UV,
1.0F, 1.0F, 1.0F, 1.0F
);
matrices.pop();
super.render(entity, yaw, tickDelta, matrices, vertices, light);
}
}

View File

@@ -11,6 +11,7 @@ import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.fabricmc.fabric.api.object.builder.v1.client.model.FabricModelPredicateProviderRegistry;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.KeyBinding;
@@ -26,6 +27,7 @@ import net.minecraft.client.render.*;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvent;
import net.minecraft.util.Identifier;
@@ -73,6 +75,18 @@ public class SzarClient implements ClientModInitializer {
int loopStart = startOffset + startLength;
@Override
public void onInitializeClient() {
ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (client.player == null) return;
boolean forward = client.options.attackKey.isPressed();
boolean backward = client.options.useKey.isPressed();
PacketByteBuf buf = PacketByteBufs.create();
buf.writeBoolean(forward);
buf.writeBoolean(backward);
ClientPlayNetworking.send(PlayerMovementManager.PACKET_ID, buf);
});
ClientPlayNetworking.registerGlobalReceiver(SYNC_PACKET, (client, handler, buf, responseSender) -> {
// First read the player UUID
UUID playerUuid = buf.readUuid();

View File

@@ -3,11 +3,13 @@ package dev.tggamesyt.szar;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.entity.*;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.math.MathHelper;
@@ -22,6 +24,9 @@ public class PlaneEntity extends Entity {
private PlaneAnimation currentServerAnimation = null;
private float enginePower = 0f;
private double lastY;
double stallSpeed = 1.0;
double explodeSpeed = 1.0;
private int brakeHoldTicks = 0;
@Environment(EnvType.CLIENT)
private PlaneAnimation currentAnimation;
@@ -34,6 +39,7 @@ public class PlaneEntity extends Entity {
public PlaneEntity(EntityType<? extends PlaneEntity> type, World world) {
super(type, world);
this.noClip = false;
this.setStepHeight(2.0f);
this.setNoGravity(false); // FORCE gravity ON
}
@@ -66,128 +72,142 @@ public class PlaneEntity extends Entity {
@Override
public void tick() {
super.tick();
PlayerEntity player = getControllingPassenger();
// -----------------------------
// No pilot: just apply basic gravity
// -----------------------------
if (player == null) {
Vec3d velocity = getVelocity();
// Apply gravity even without pilot
velocity = velocity.add(0, -0.04, 0);
velocity = velocity.multiply(0.98);
Vec3d velocity = getVelocity().add(0, -0.04, 0).multiply(0.95);
setVelocity(velocity);
move(MovementType.SELF, velocity);
return;
}
/* --------------------------------
CONTROLLER (AircraftEntity logic)
-------------------------------- */
// YAW
// -----------------------------
// Yaw & pitch control
// -----------------------------
setYaw(getYaw() - player.sidewaysSpeed * 4.0f);
if (!isOnGround() || getVelocity().length() > 0.9) setPitch(getPitch() - player.forwardSpeed * 1.5f);
setPitch(getPitch() * 0.98f); // auto leveling
player.setInvisible(true);
// PITCH (only in air)
if (!isOnGround()) {
setPitch(getPitch() - player.forwardSpeed * 2.5f);
// -----------------------------
// Engine target adjustments (server authoritative)
// -----------------------------
boolean forward = !getWorld().isClient && PlayerMovementManager.isForwardPressed((ServerPlayerEntity) player);
boolean braking = !getWorld().isClient && PlayerMovementManager.isBackwardPressed((ServerPlayerEntity) player);
if (forward) setEngineTarget(getEngineTarget() + 0.02f);
if (braking) {
brakeHoldTicks++;
float baseBrake = isOnGround() ? 0.04f : 0.015f;
float progressive = Math.min(brakeHoldTicks * 0.0035f, 0.15f);
float brakeStrength = baseBrake + progressive;
setEngineTarget(getEngineTarget() - brakeStrength);
// Apply actual braking locally
Vec3d vel = getVelocity();
if (vel.lengthSquared() > 0.0001) {
Vec3d brakeDir = vel.normalize().multiply(-brakeStrength * 0.6);
setVelocity(vel.add(brakeDir));
}
} else {
brakeHoldTicks = 0;
}
// Stabilizer (small auto leveling)
setPitch(getPitch() * 0.98f);
// -----------------------------
// Engine power smoothing
// -----------------------------
float lerpSpeed = braking ? 0.25f : 0.05f;
enginePower += (getEngineTarget() - enginePower) * lerpSpeed;
/* --------------------------------
THROTTLE (AirplaneEntity logic)
-------------------------------- */
if (player.jumping) {
setEngineTarget(getEngineTarget() + 0.02f);
}
if (player.isSneaking()) {
setEngineTarget(getEngineTarget() - 0.02f);
}
// Smooth engine reaction
enginePower += (getEngineTarget() - enginePower) * 0.05f;
/* --------------------------------
PHYSICS (STABLE VERSION)
-------------------------------- */
Vec3d forward = getRotationVector().normalize();
// -----------------------------
// PHYSICS (runs on both client & server)
// -----------------------------
Vec3d forwardVec = getRotationVector().normalize();
Vec3d velocity = getVelocity();
double horizontalSpeed = Math.sqrt(velocity.x * velocity.x + velocity.z * velocity.z);
/* ---------- REALIGN VELOCITY ---------- */
/* Prevents internal momentum stacking */
// Stall gravity in air
if (!isOnGround() && horizontalSpeed < stallSpeed) {
velocity = velocity.add(0, -0.2, 0);
}
// Forward locking (only if not braking)
double speed = velocity.length();
if (speed > 0.001) {
// Stronger forward locking
double alignment = 0.25; // was 0.08
Vec3d newDir = velocity.normalize().lerp(forward, alignment).normalize();
if (speed > 0.001 && enginePower > 0.01f && !braking) {
Vec3d newDir = velocity.normalize().lerp(forwardVec, 0.25).normalize();
velocity = newDir.multiply(speed);
}
/* ---------- THRUST ---------- */
// Apply thrust
double thrust = Math.pow(enginePower, 2.0) * 0.08;
velocity = velocity.add(forward.multiply(thrust));
/* ---------- GLIDE ---------- */
velocity = velocity.add(forwardVec.multiply(thrust));
// Glide (air only)
double diffY = lastY - getY();
if (lastY != 0.0 && diffY != 0.0) {
velocity = velocity.add(
forward.multiply(diffY * 0.04 * (1.0 - Math.abs(forward.y)))
);
if (!isOnGround() && lastY != 0.0 && diffY != 0.0) {
velocity = velocity.add(forwardVec.multiply(diffY * 0.04 * (1.0 - Math.abs(forwardVec.y))));
}
lastY = getY();
/* ---------- DYNAMIC GRAVITY ---------- */
double horizontalSpeed = velocity.length() * (1.0 - Math.abs(forward.y));
// Dynamic gravity
horizontalSpeed = velocity.length() * (1.0 - Math.abs(forwardVec.y));
double gravityFactor = Math.max(0.0, 1.0 - horizontalSpeed * 1.5);
// ALWAYS apply — do not check hasNoGravity()
velocity = velocity.add(0, -0.04 * gravityFactor, 0);
/* ---------- DRAG ---------- */
// Drag / friction
velocity = isOnGround() ? velocity.multiply(0.94) : velocity.multiply(0.98);
velocity = velocity.multiply(0.98);
// Max speed clamp
double maxSpeed = 2;
if (velocity.length() > maxSpeed) velocity = velocity.normalize().multiply(maxSpeed);
/* ---------- MAX SPEED CLAMP ---------- */
double maxSpeed = 1.5;
if (velocity.length() > maxSpeed) {
velocity = velocity.normalize().multiply(maxSpeed);
}
// Save vertical velocity before move for impact check
Vec3d preMoveVelocity = velocity;
// Move
setVelocity(velocity);
move(MovementType.SELF, velocity);
/* --------------------------------
ANIMATION STATE SYNC
-------------------------------- */
// -----------------------------
// Crash detection (server only)
// Explodes if hitting block with high horizontal or vertical velocity
// -----------------------------
if (!getWorld().isClient) {
double horizontalImpact = Math.sqrt(preMoveVelocity.x * preMoveVelocity.x + preMoveVelocity.z * preMoveVelocity.z);
double verticalImpact = Math.abs(preMoveVelocity.y);
boolean hasPassenger = getControllingPassenger() != null;
boolean crash = (horizontalImpact > 1.5 && horizontalCollision) || (verticalImpact > explodeSpeed && verticalCollision);
if (crash) {
getWorld().createExplosion(this, getX(), getY(), getZ(), 7.0f, World.ExplosionSourceType.TNT);
remove(RemovalReason.KILLED);
return;
}
}
if (!hasPassenger) {
playServerAnimation(null);
}
else if (enginePower < 0.05f) {
playServerAnimation(PlaneAnimation.START_ENGINE);
}
else if (isOnGround()) {
playServerAnimation(PlaneAnimation.LAND_STARTED);
}
else {
playServerAnimation(PlaneAnimation.FLYING);
}
// Stop tiny movements
if (velocity.lengthSquared() < 0.005) velocity = Vec3d.ZERO;
setVelocity(velocity);
// -----------------------------
// Animation sync
// -----------------------------
boolean hasPassenger = getControllingPassenger() != null;
if (!hasPassenger) playServerAnimation(null);
else if (enginePower < 0.05f) playServerAnimation(PlaneAnimation.START_ENGINE);
else if (isOnGround()) playServerAnimation(PlaneAnimation.LAND_STARTED);
else playServerAnimation(PlaneAnimation.FLYING);
}
@Override
protected void removePassenger(Entity passenger) {
super.removePassenger(passenger);
if (passenger instanceof PlayerEntity player) {
player.setInvisible(false);
}
}
@Override

View File

@@ -0,0 +1,53 @@
package dev.tggamesyt.szar;
import net.fabricmc.fabric.api.networking.v1.PacketSender;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import java.util.HashMap;
import java.util.Map;
public class PlayerMovementManager {
// Packet ID
public static final Identifier PACKET_ID = new Identifier("szar", "player_movement");
// Stores current key state per player
private static final Map<ServerPlayerEntity, MovementState> playerStates = new HashMap<>();
// Represents pressed keys
public static class MovementState {
public boolean forwardPressed = false;
public boolean backwardPressed = false;
}
// Server-side init (register packet receiver)
public static void init() {
ServerPlayNetworking.registerGlobalReceiver(PACKET_ID, (server, player, handler, buf, responseSender) -> {
boolean forward = buf.readBoolean();
boolean backward = buf.readBoolean();
server.execute(() -> {
MovementState state = playerStates.computeIfAbsent(player, k -> new MovementState());
state.forwardPressed = forward;
state.backwardPressed = backward;
});
});
}
// Helper to get player state
public static boolean isForwardPressed(ServerPlayerEntity player) {
return playerStates.getOrDefault(player, new MovementState()).forwardPressed;
}
public static boolean isBackwardPressed(ServerPlayerEntity player) {
return playerStates.getOrDefault(player, new MovementState()).backwardPressed;
}
// Optional: clear state when player disconnects
public static void removePlayer(ServerPlayerEntity player) {
playerStates.remove(player);
}
}

View File

@@ -293,6 +293,7 @@ public class Szar implements ModInitializer {
private final Map<UUID, BlockPos> sleepingPlayers = new HashMap<>();
@Override
public void onInitialize() {
PlayerMovementManager.init();
ServerCosmetics.init();
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
ServerPlayerEntity player = handler.getPlayer();

View File

@@ -0,0 +1,27 @@
package dev.tggamesyt.szar.mixin;
import dev.tggamesyt.szar.PlaneEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.util.math.MathHelper;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(LivingEntity.class)
public abstract class LivingEntityFallDamageMixin {
// This injects at the start of computeFallDamage
@Inject(method = "computeFallDamage", at = @At("HEAD"), cancellable = true)
private void preventFallDamageIfOnPlane(float fallDistance, float damageMultiplier, CallbackInfoReturnable<Integer> cir) {
LivingEntity self = (LivingEntity) (Object) this;
// Check if the entity is a player riding a PlaneEntity
if (self.hasVehicle() && self.getVehicle() instanceof PlaneEntity) {
cir.setReturnValue(0); // Cancel fall damage
}
}
}

View File

@@ -0,0 +1,34 @@
package dev.tggamesyt.szar.mixin;
import dev.tggamesyt.szar.PlaneEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.network.ServerPlayerInteractionManager;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ServerPlayerInteractionManager.class)
public class PlaneBlockInteractionMixin {
@Inject(method = "interactBlock", at = @At("HEAD"), cancellable = true)
private void interactBlock(ServerPlayerEntity player, World world, ItemStack stack, Hand hand, BlockHitResult hitResult, CallbackInfoReturnable<ActionResult> cir) {
if (player.getVehicle() instanceof PlaneEntity) {
cir.setReturnValue(ActionResult.FAIL);
}
}
@Inject(method = "tryBreakBlock", at = @At("HEAD"), cancellable = true)
private void preventBlockBreaking(BlockPos pos, CallbackInfoReturnable<Boolean> cir) {
ServerPlayerEntity player = ((ServerPlayerInteractionManager)(Object)this).player;
if (player.getVehicle() instanceof PlaneEntity) {
cir.setReturnValue(false);
}
}
}

View File

@@ -0,0 +1,23 @@
package dev.tggamesyt.szar.mixin;
import dev.tggamesyt.szar.PlaneEntity;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(PlayerEntity.class)
public class PlayerInteractionMixin {
@Inject(method = "interact", at = @At("HEAD"), cancellable = true)
private void preventPlaneInteraction(Entity entity, Hand hand, CallbackInfoReturnable<ActionResult> cir) {
PlayerEntity player = (PlayerEntity) (Object) this;
if (player.getVehicle() instanceof PlaneEntity) {
cir.setReturnValue(ActionResult.FAIL); // cancel interaction
}
}
}

View File

@@ -6,7 +6,7 @@
},
"display": {
"icon": { "item": "minecraft:tnt" },
"title": "Too Close",
"title": "Hmm, familiar...",
"description": "You were there when the towers fell",
"frame": "challenge",
"show_toast": true,

View File

@@ -2,4 +2,5 @@ accessWidener v2 named
accessible field net/minecraft/client/gui/hud/InGameHud spyglassScale F
accessible field net/minecraft/entity/LivingEntity jumping Z
accessible class net/minecraft/client/gui/hud/InGameHud$HeartType
accessible class net/minecraft/client/gui/hud/InGameHud$HeartType
accessible field net/minecraft/server/network/ServerPlayerInteractionManager player Lnet/minecraft/server/network/ServerPlayerEntity;

View File

@@ -4,9 +4,12 @@
"package": "dev.tggamesyt.szar.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"PlayerEntityMixin",
"CraftingScreenHandlerMixin",
"CraftingScreenHandlerMixin2",
"LivingEntityFallDamageMixin",
"PlaneBlockInteractionMixin",
"PlayerEntityMixin",
"PlayerInteractionMixin",
"RadiatedItemMixin"
],
"injectors": {