Banter on the Lift

- Pulley contraptions will now make an effort to place remote players at y values sensible to the client
This commit is contained in:
simibubi 2023-04-20 19:19:51 +02:00
parent ffa85dc889
commit ce108ad786
3 changed files with 158 additions and 16 deletions

View File

@ -4,7 +4,10 @@ import static net.minecraft.world.entity.Entity.collideBoundingBox;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.commons.lang3.mutable.MutableFloat; import org.apache.commons.lang3.mutable.MutableFloat;
@ -17,6 +20,7 @@ import com.simibubi.create.AllMovementBehaviours;
import com.simibubi.create.content.contraptions.components.actors.BlockBreakingMovementBehaviour; import com.simibubi.create.content.contraptions.components.actors.BlockBreakingMovementBehaviour;
import com.simibubi.create.content.contraptions.components.actors.HarvesterMovementBehaviour; import com.simibubi.create.content.contraptions.components.actors.HarvesterMovementBehaviour;
import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity.ContraptionRotationState; import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity.ContraptionRotationState;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionColliderLockPacket.ContraptionColliderLockPacketRequest;
import com.simibubi.create.content.contraptions.components.structureMovement.sync.ClientMotionPacket; import com.simibubi.create.content.contraptions.components.structureMovement.sync.ClientMotionPacket;
import com.simibubi.create.content.logistics.trains.entity.CarriageContraptionEntity; import com.simibubi.create.content.logistics.trains.entity.CarriageContraptionEntity;
import com.simibubi.create.foundation.advancement.AllAdvancements; import com.simibubi.create.foundation.advancement.AllAdvancements;
@ -30,7 +34,10 @@ import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.player.RemotePlayer;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis; import net.minecraft.core.Direction.Axis;
@ -65,6 +72,7 @@ public class ContraptionCollider {
} }
private static MutablePair<WeakReference<AbstractContraptionEntity>, Double> safetyLock = new MutablePair<>(); private static MutablePair<WeakReference<AbstractContraptionEntity>, Double> safetyLock = new MutablePair<>();
private static Map<AbstractContraptionEntity, Map<Player, Double>> remoteSafetyLocks = new WeakHashMap<>();
static void collideEntities(AbstractContraptionEntity contraptionEntity) { static void collideEntities(AbstractContraptionEntity contraptionEntity) {
Level world = contraptionEntity.getCommandSenderWorld(); Level world = contraptionEntity.getCommandSenderWorld();
@ -95,8 +103,13 @@ public class ContraptionCollider {
continue; continue;
PlayerType playerType = getPlayerType(entity); PlayerType playerType = getPlayerType(entity);
if (playerType == PlayerType.REMOTE) if (playerType == PlayerType.REMOTE) {
if (!(contraption instanceof TranslatingContraption))
continue;
DistExecutor.unsafeRunWhenOn(Dist.CLIENT,
() -> () -> saveRemotePlayerFromClipping((Player) entity, contraptionEntity, contraptionMotion));
continue; continue;
}
entity.getSelfAndPassengers() entity.getSelfAndPassengers()
.forEach(e -> { .forEach(e -> {
@ -354,7 +367,8 @@ public class ContraptionCollider {
entity.fallDistance = 0; entity.fallDistance = 0;
for (Entity rider : entity.getIndirectPassengers()) for (Entity rider : entity.getIndirectPassengers())
if (getPlayerType(rider) == PlayerType.CLIENT) if (getPlayerType(rider) == PlayerType.CLIENT)
AllPackets.getChannel().sendToServer(new ClientMotionPacket(rider.getDeltaMovement(), true, 0)); AllPackets.getChannel()
.sendToServer(new ClientMotionPacket(rider.getDeltaMovement(), true, 0));
boolean canWalk = bounce != 0 || slide == 0; boolean canWalk = bounce != 0 || slide == 0;
if (canWalk || !rotation.hasVerticalRotation()) { if (canWalk || !rotation.hasVerticalRotation()) {
if (canWalk) if (canWalk)
@ -378,7 +392,8 @@ public class ContraptionCollider {
float limbSwing = Mth.sqrt((float) (d0 * d0 + d1 * d1)) * 4.0F; float limbSwing = Mth.sqrt((float) (d0 * d0 + d1 * d1)) * 4.0F;
if (limbSwing > 1.0F) if (limbSwing > 1.0F)
limbSwing = 1.0F; limbSwing = 1.0F;
AllPackets.getChannel().sendToServer(new ClientMotionPacket(entityMotion, true, limbSwing)); AllPackets.getChannel()
.sendToServer(new ClientMotionPacket(entityMotion, true, limbSwing));
if (entity.isOnGround() && contraption instanceof TranslatingContraption) { if (entity.isOnGround() && contraption instanceof TranslatingContraption) {
safetyLock.setLeft(new WeakReference<>(contraptionEntity)); safetyLock.setLeft(new WeakReference<>(contraptionEntity));
@ -388,19 +403,32 @@ public class ContraptionCollider {
} }
private static int packetCooldown = 0;
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
private static void saveClientPlayerFromClipping(AbstractContraptionEntity contraptionEntity, private static void saveClientPlayerFromClipping(AbstractContraptionEntity contraptionEntity,
Vec3 contraptionMotion) { Vec3 contraptionMotion) {
Player entity = Minecraft.getInstance().player; LocalPlayer entity = Minecraft.getInstance().player;
if (entity.isPassenger()) if (entity.isPassenger())
return; return;
double prevDiff = safetyLock.right; double prevDiff = safetyLock.right;
double currentDiff = entity.getY() - contraptionEntity.getY(); double currentDiff = entity.getY() - contraptionEntity.getY();
double motion = contraptionMotion.subtract(entity.getDeltaMovement()).y; double motion = contraptionMotion.subtract(entity.getDeltaMovement()).y;
double trend = Math.signum(currentDiff - prevDiff); double trend = Math.signum(currentDiff - prevDiff);
ClientPacketListener handler = entity.connection;
if (handler.getOnlinePlayers()
.size() > 1) {
if (packetCooldown > 0)
packetCooldown--;
if (packetCooldown == 0) {
AllPackets.getChannel()
.sendToServer(new ContraptionColliderLockPacketRequest(contraptionEntity.getId(), currentDiff));
packetCooldown = 3;
}
}
if (trend == 0) if (trend == 0)
return; return;
if (trend == Math.signum(motion)) if (trend == Math.signum(motion))
@ -412,10 +440,43 @@ public class ContraptionCollider {
return; return;
if (speed < 0.05) if (speed < 0.05)
return; return;
AABB bb = entity.getBoundingBox().deflate(1/4f, 0, 1/4f); if (!savePlayerFromClipping(entity, contraptionEntity, contraptionMotion, prevDiff))
safetyLock.setLeft(null);
}
@OnlyIn(Dist.CLIENT)
public static void lockPacketReceived(int contraptionId, int remotePlayerId, double suggestedOffset) {
ClientLevel level = Minecraft.getInstance().level;
if (!(level.getEntity(contraptionId) instanceof ControlledContraptionEntity contraptionEntity))
return;
if (!(level.getEntity(remotePlayerId) instanceof RemotePlayer player))
return;
remoteSafetyLocks.computeIfAbsent(contraptionEntity, $ -> new WeakHashMap<>())
.put(player, suggestedOffset);
}
@OnlyIn(Dist.CLIENT)
private static void saveRemotePlayerFromClipping(Player entity, AbstractContraptionEntity contraptionEntity,
Vec3 contraptionMotion) {
if (entity.isPassenger())
return;
Map<Player, Double> locksOnThisContraption =
remoteSafetyLocks.getOrDefault(contraptionEntity, Collections.emptyMap());
double prevDiff = locksOnThisContraption.getOrDefault(entity, entity.getY() - contraptionEntity.getY());
if (!savePlayerFromClipping(entity, contraptionEntity, contraptionMotion, prevDiff))
if (locksOnThisContraption.containsKey(entity))
locksOnThisContraption.remove(entity);
}
@OnlyIn(Dist.CLIENT)
private static boolean savePlayerFromClipping(Player entity, AbstractContraptionEntity contraptionEntity,
Vec3 contraptionMotion, double yStartOffset) {
AABB bb = entity.getBoundingBox()
.deflate(1 / 4f, 0, 1 / 4f);
double shortestDistance = Double.MAX_VALUE; double shortestDistance = Double.MAX_VALUE;
double yStart = entity.getStepHeight() + contraptionEntity.getY() + prevDiff; double yStart = entity.getStepHeight() + contraptionEntity.getY() + yStartOffset;
double rayLength = Math.max(5, Math.abs(entity.getY() - yStart)); double rayLength = Math.max(5, Math.abs(entity.getY() - yStart));
for (int rayIndex = 0; rayIndex < 4; rayIndex++) { for (int rayIndex = 0; rayIndex < 4; rayIndex++) {
@ -432,12 +493,10 @@ public class ContraptionCollider {
shortestDistance = hitDiff; shortestDistance = hitDiff;
} }
if (shortestDistance > rayLength) { if (shortestDistance > rayLength)
safetyLock.setLeft(null); return false;
return;
}
entity.setPos(entity.getX(), yStart - shortestDistance, entity.getZ()); entity.setPos(entity.getX(), yStart - shortestDistance, entity.getZ());
return true;
} }
private static Vec3 handleDamageFromTrain(Level world, AbstractContraptionEntity contraptionEntity, private static Vec3 handleDamageFromTrain(Level world, AbstractContraptionEntity contraptionEntity,
@ -477,7 +536,8 @@ public class ContraptionCollider {
return entityMotion; return entityMotion;
if (playerType == PlayerType.CLIENT) { if (playerType == PlayerType.CLIENT) {
AllPackets.getChannel().sendToServer(new TrainCollisionPacket((int) (damage * 16), contraptionEntity.getId())); AllPackets.getChannel()
.sendToServer(new TrainCollisionPacket((int) (damage * 16), contraptionEntity.getId()));
world.playSound((Player) entity, entity.blockPosition(), SoundEvents.PLAYER_ATTACK_CRIT, world.playSound((Player) entity, entity.blockPosition(), SoundEvents.PLAYER_ATTACK_CRIT,
SoundSource.NEUTRAL, 1, .75f); SoundSource.NEUTRAL, 1, .75f);
} else { } else {

View File

@ -0,0 +1,76 @@
package com.simibubi.create.content.contraptions.components.structureMovement;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent.Context;
import net.minecraftforge.network.PacketDistributor;
public class ContraptionColliderLockPacket extends SimplePacketBase {
protected int contraption;
protected double offset;
private int sender;
public ContraptionColliderLockPacket(int contraption, double offset, int sender) {
this.contraption = contraption;
this.offset = offset;
this.sender = sender;
}
public ContraptionColliderLockPacket(FriendlyByteBuf buffer) {
contraption = buffer.readVarInt();
offset = buffer.readDouble();
sender = buffer.readVarInt();
}
@Override
public void write(FriendlyByteBuf buffer) {
buffer.writeVarInt(contraption);
buffer.writeDouble(offset);
buffer.writeVarInt(sender);
}
@Override
public boolean handle(Context context) {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT,
() -> () -> ContraptionCollider.lockPacketReceived(contraption, sender, offset));
return true;
}
public static class ContraptionColliderLockPacketRequest extends SimplePacketBase {
protected int contraption;
protected double offset;
public ContraptionColliderLockPacketRequest(int contraption, double offset) {
this.contraption = contraption;
this.offset = offset;
}
public ContraptionColliderLockPacketRequest(FriendlyByteBuf buffer) {
contraption = buffer.readVarInt();
offset = buffer.readDouble();
}
@Override
public void write(FriendlyByteBuf buffer) {
buffer.writeVarInt(contraption);
buffer.writeDouble(offset);
}
@Override
public boolean handle(Context context) {
AllPackets.getChannel()
.send(PacketDistributor.TRACKING_ENTITY.with(context::getSender),
new ContraptionColliderLockPacket(contraption, offset, context.getSender()
.getId()));
return true;
}
}
}

View File

@ -10,6 +10,8 @@ import java.util.function.Supplier;
import com.simibubi.create.Create; import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.components.actors.controls.ContraptionDisableActorPacket; import com.simibubi.create.content.contraptions.components.actors.controls.ContraptionDisableActorPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionBlockChangedPacket; import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionBlockChangedPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionColliderLockPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionColliderLockPacket.ContraptionColliderLockPacketRequest;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionDisassemblyPacket; import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionDisassemblyPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionRelocationPacket; import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionRelocationPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionStallPacket; import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionStallPacket;
@ -157,6 +159,8 @@ public enum AllPackets {
ELEVATOR_SET_FLOOR(ElevatorTargetFloorPacket.class, ElevatorTargetFloorPacket::new, PLAY_TO_SERVER), ELEVATOR_SET_FLOOR(ElevatorTargetFloorPacket.class, ElevatorTargetFloorPacket::new, PLAY_TO_SERVER),
VALUE_SETTINGS(ValueSettingsPacket.class, ValueSettingsPacket::new, PLAY_TO_SERVER), VALUE_SETTINGS(ValueSettingsPacket.class, ValueSettingsPacket::new, PLAY_TO_SERVER),
CLIPBOARD_EDIT(ClipboardEditPacket.class, ClipboardEditPacket::new, PLAY_TO_SERVER), CLIPBOARD_EDIT(ClipboardEditPacket.class, ClipboardEditPacket::new, PLAY_TO_SERVER),
CONTRAPTION_COLLIDER_LOCK_REQUEST(ContraptionColliderLockPacketRequest.class,
ContraptionColliderLockPacketRequest::new, PLAY_TO_SERVER),
// Server to Client // Server to Client
SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new, PLAY_TO_CLIENT), SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new, PLAY_TO_CLIENT),
@ -195,7 +199,9 @@ public enum AllPackets {
TRACK_GRAPH_ROLL_CALL(TrackGraphRollCallPacket.class, TrackGraphRollCallPacket::new, PLAY_TO_CLIENT), TRACK_GRAPH_ROLL_CALL(TrackGraphRollCallPacket.class, TrackGraphRollCallPacket::new, PLAY_TO_CLIENT),
UPDATE_ELEVATOR_FLOORS(ElevatorFloorListPacket.class, ElevatorFloorListPacket::new, PLAY_TO_CLIENT), UPDATE_ELEVATOR_FLOORS(ElevatorFloorListPacket.class, ElevatorFloorListPacket::new, PLAY_TO_CLIENT),
CONTRAPTION_ACTOR_TOGGLE(ContraptionDisableActorPacket.class, ContraptionDisableActorPacket::new, PLAY_TO_CLIENT), CONTRAPTION_ACTOR_TOGGLE(ContraptionDisableActorPacket.class, ContraptionDisableActorPacket::new, PLAY_TO_CLIENT),
SET_FIRE_IMMUNE(NetheriteDivingHandler.SetFireImmunePacket.class, NetheriteDivingHandler.SetFireImmunePacket::new, PLAY_TO_CLIENT), SET_FIRE_IMMUNE(NetheriteDivingHandler.SetFireImmunePacket.class, NetheriteDivingHandler.SetFireImmunePacket::new,
PLAY_TO_CLIENT),
CONTRAPTION_COLLIDER_LOCK(ContraptionColliderLockPacket.class, ContraptionColliderLockPacket::new, PLAY_TO_CLIENT),
; ;