Send nodes

- Fixed incomplete track graph network packets at >1000 nodes
- Fixed Metal girders having incorrect block properties
- Trains getting abnormally high coupling stress now derail and stop moving
- Fixed trains not always migrating graphs properly when track connections are severed
- Motion applied to players run over by trains now caps at 60m/s
- Fixed train relocation able to cause run-over damage
- Fixed minecart contraption relocation sending nearby players to the moon
- Fixed non-player entities not getting run over by trains
- Trains can now reverse out of being stalled
- Fixed train relocation not deactivating mounted actors
- Fixed drill trains occasionally getting stuck breaking blocks at very low speeds
This commit is contained in:
simibubi 2022-05-31 02:01:32 +02:00
parent 224c5d0426
commit 64bc8a3499
21 changed files with 215 additions and 54 deletions

View file

@ -740,6 +740,7 @@ public class AllBlocks {
.register();
public static final BlockEntry<GirderBlock> METAL_GIRDER = REGISTRATE.block("metal_girder", GirderBlock::new)
.initialProperties(SharedProperties::softMetal)
.blockstate(GirderBlockStateGenerator::blockState)
.properties(p -> p.color(MaterialColor.COLOR_GRAY))
.properties(p -> p.sound(SoundType.NETHERITE_BLOCK))
@ -751,6 +752,7 @@ public class AllBlocks {
public static final BlockEntry<GirderEncasedShaftBlock> METAL_GIRDER_ENCASED_SHAFT =
REGISTRATE.block("metal_girder_encased_shaft", GirderEncasedShaftBlock::new)
.initialProperties(SharedProperties::softMetal)
.blockstate(GirderBlockStateGenerator::blockStateWithShaft)
.properties(p -> p.color(MaterialColor.COLOR_GRAY))
.properties(p -> p.sound(SoundType.NETHERITE_BLOCK))

View file

@ -3,7 +3,9 @@ package com.simibubi.create.content.contraptions.components.actors;
import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
import com.simibubi.create.content.contraptions.components.structureMovement.OrientedContraptionEntity;
import com.simibubi.create.foundation.utility.BlockHelper;
import com.simibubi.create.foundation.utility.Debug;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
@ -47,6 +49,8 @@ public class BlockBreakingMovementBehaviour implements MovementBehaviour {
}
public void damageEntities(MovementContext context, BlockPos pos, Level world) {
if (context.contraption.entity instanceof OrientedContraptionEntity oce && oce.nonDamageTicks > 0)
return;
DamageSource damageSource = getDamageSource();
if (damageSource == null && !throwsEntities())
return;
@ -69,9 +73,11 @@ public class BlockBreakingMovementBehaviour implements MovementBehaviour {
Vec3 motionBoost = context.motion.add(0, context.motion.length() / 4f, 0);
int maxBoost = 4;
if (motionBoost.length() > maxBoost) {
motionBoost = motionBoost.subtract(motionBoost.normalize().scale(motionBoost.length() - maxBoost));
motionBoost = motionBoost.subtract(motionBoost.normalize()
.scale(motionBoost.length() - maxBoost));
}
entity.setDeltaMovement(entity.getDeltaMovement().add(motionBoost));
entity.setDeltaMovement(entity.getDeltaMovement()
.add(motionBoost));
entity.hurtMarked = true;
}
}
@ -86,7 +92,7 @@ public class BlockBreakingMovementBehaviour implements MovementBehaviour {
}
@Override
public void stopMoving(MovementContext context) {
public void cancelStall(MovementContext context) {
CompoundTag data = context.data;
if (context.world.isClientSide)
return;
@ -101,10 +107,15 @@ public class BlockBreakingMovementBehaviour implements MovementBehaviour {
data.remove("TicksUntilNextProgress");
data.remove("BreakingPos");
context.stall = false;
MovementBehaviour.super.cancelStall(context);
world.destroyBlockProgress(id, breakingPos, -1);
}
@Override
public void stopMoving(MovementContext context) {
cancelStall(context);
}
@Override
public void tick(MovementContext context) {
tickBreaker(context);
@ -165,7 +176,8 @@ public class BlockBreakingMovementBehaviour implements MovementBehaviour {
float breakSpeed = Mth.clamp(Math.abs(context.getAnimationSpeed()) / 500f, 1 / 128f, 16f);
destroyProgress += Mth.clamp((int) (breakSpeed / blockHardness), 1, 10 - destroyProgress);
world.playSound(null, breakingPos, stateToBreak.getSoundType().getHitSound(), SoundSource.NEUTRAL, .25f, 1);
world.playSound(null, breakingPos, stateToBreak.getSoundType()
.getHitSound(), SoundSource.NEUTRAL, .25f, 1);
if (destroyProgress >= 10) {
world.destroyBlockProgress(id, breakingPos, -1);

View file

@ -2,6 +2,7 @@ package com.simibubi.create.content.contraptions.components.actors;
import com.simibubi.create.content.contraptions.components.actors.PloughBlock.PloughFakePlayer;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
import com.simibubi.create.content.contraptions.components.structureMovement.OrientedContraptionEntity;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;

View file

@ -153,6 +153,11 @@ public class PortableStorageInterfaceMovement implements MovementBehaviour {
// reset(context);
}
@Override
public void cancelStall(MovementContext context) {
reset(context);
}
public void reset(MovementContext context) {
context.data.remove(_clientPrevPos_);
context.data.remove(_workingPos_);

View file

@ -186,6 +186,21 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
context.stall = player.blockBreakingProgress != null;
}
@Override
public void cancelStall(MovementContext context) {
if (context.world.isClientSide)
return;
MovementBehaviour.super.cancelStall(context);
DeployerFakePlayer player = getPlayer(context);
if (player == null)
return;
if (player.blockBreakingProgress == null)
return;
context.world.destroyBlockProgress(player.getId(), player.blockBreakingProgress.getKey(), -1);
player.blockBreakingProgress = null;
}
@Override
public void stopMoving(MovementContext context) {
if (context.world.isClientSide)
@ -195,6 +210,7 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
if (player == null)
return;
cancelStall(context);
context.tileData.put("Inventory", player.getInventory()
.save(new ListTag()));
player.discard();

View file

@ -29,6 +29,8 @@ import com.simibubi.create.content.contraptions.components.structureMovement.int
import com.simibubi.create.content.contraptions.components.structureMovement.mounted.MountedContraption;
import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionSeatMappingPacket;
import com.simibubi.create.content.logistics.trains.entity.CarriageContraption;
import com.simibubi.create.content.logistics.trains.entity.CarriageContraptionEntity;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.foundation.collision.Matrix3d;
import com.simibubi.create.foundation.mixin.accessor.ServerLevelAccessor;
import com.simibubi.create.foundation.networking.AllPackets;
@ -440,6 +442,14 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
return false;
context.motion = actorPosition.subtract(previousPosition);
if (!level.isClientSide() && context.contraption.entity instanceof CarriageContraptionEntity cce
&& cce.getCarriage() != null) {
Train train = cce.getCarriage().train;
double actualSpeed = train.speedBeforeStall != null ? train.speedBeforeStall : train.speed;
context.motion = context.motion.normalize()
.scale(actualSpeed);
}
Vec3 relativeMotion = context.motion;
relativeMotion = reverseRotation(relativeMotion, 1);
context.relativeMotion = relativeMotion;

View file

@ -78,6 +78,8 @@ public class ContraptionCollider {
List<Entity> entitiesWithinAABB = world.getEntitiesOfClass(Entity.class, bounds.inflate(2)
.expandTowards(0, 32, 0), contraptionEntity::canCollideWith);
for (Entity entity : entitiesWithinAABB) {
if (!entity.isAlive())
continue;
PlayerType playerType = getPlayerType(entity);
if (playerType == PlayerType.REMOTE)
@ -116,7 +118,8 @@ public class ContraptionCollider {
// Use simplified bbs when present
final Vec3 motionCopy = motion;
List<AABB> collidableBBs = contraption.getSimplifiedEntityColliders().orElseGet(() -> {
List<AABB> collidableBBs = contraption.getSimplifiedEntityColliders()
.orElseGet(() -> {
// Else find 'nearby' individual block shapes to collide with
List<AABB> bbs = new ArrayList<>();
@ -308,25 +311,36 @@ public class ContraptionCollider {
entityPosition.z + allowedMovement.z);
entityPosition = entity.position();
if (contraptionEntity instanceof CarriageContraptionEntity cce && entity.isOnGround()) {
if (AllConfigs.SERVER.trains.trainsCauseDamage.get()) {
if (contraptionEntity instanceof CarriageContraptionEntity cce && entity.isOnGround()
&& !(entity instanceof ItemEntity) && cce.nonDamageTicks == 0
&& AllConfigs.SERVER.trains.trainsCauseDamage.get()) {
Vec3 diffMotion = contraptionMotion.subtract(entity.getDeltaMovement());
if (diffMotion.length() > 0.35f && contraptionMotion.length() > 0.35f) {
EntityDamageSource pSource = new EntityDamageSource("create.run_over", contraptionEntity);
double damage = diffMotion.length();
if (playerType == PlayerType.CLIENT)
if (!(entity instanceof Player p) || !p.isCreative() && !p.isSpectator()) {
if (playerType == PlayerType.CLIENT) {
AllPackets.channel
.sendToServer(new TrainCollisionPacket((int) (damage * 16), contraptionEntity.getId()));
else
world.playSound((Player) entity, entity.blockPosition(), SoundEvents.PLAYER_ATTACK_CRIT,
SoundSource.NEUTRAL, 1, .75f);
} else {
entity.hurt(pSource, (int) (damage * 16));
if (!(entity instanceof Player p) || !p.isCreative() && !p.isSpectator())
entityMotion = entityMotion.add(entity.position()
world.playSound(null, entity.blockPosition(), SoundEvents.PLAYER_ATTACK_CRIT,
SoundSource.NEUTRAL, 1, .75f);
}
Vec3 added = entityMotion.add(entity.position()
.subtract(contraptionPosition)
.multiply(1, 0, 1)
.normalize()
.add(0, .25, 0)
.scale(damage * 4))
.add(diffMotion);
entityMotion = VecHelper.clamp(added, 3);
}
}
}

View file

@ -53,6 +53,10 @@ public interface MovementBehaviour {
default void stopMoving(MovementContext context) {}
default void cancelStall(MovementContext context) {
context.stall = false;
}
default void writeExtraData(MovementContext context) {}
default boolean renderAsNormalTileEntity() {

View file

@ -75,11 +75,14 @@ public class OrientedContraptionEntity extends AbstractContraptionEntity {
public float prevPitch;
public float pitch;
public int nonDamageTicks;
public OrientedContraptionEntity(EntityType<?> type, Level world) {
super(type, world);
motionBeforeStall = Vec3.ZERO;
attachedExtraInventories = false;
isSerializingFurnaceCart = false;
nonDamageTicks = 10;
}
public static OrientedContraptionEntity create(Level world, Contraption contraption, Direction initialOrientation) {
@ -243,6 +246,8 @@ public class OrientedContraptionEntity extends AbstractContraptionEntity {
@Override
protected void tickContraption() {
if (nonDamageTicks > 0)
nonDamageTicks--;
Entity e = getVehicle();
if (e == null)
return;

View file

@ -7,6 +7,8 @@ import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.damagesource.EntityDamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
@ -45,6 +47,8 @@ public class TrainCollisionPacket extends SimplePacketBase {
return;
player.hurt(new EntityDamageSource("create.run_over", cce), (int) damage);
player.level.playSound(player, entity.blockPosition(), SoundEvents.PLAYER_ATTACK_CRIT, SoundSource.NEUTRAL,
1, .75f);
});
ctx.setPacketHandled(true);
}

View file

@ -50,6 +50,12 @@ public class ContactMovementBehaviour implements MovementBehaviour {
deactivateLastVisitedContact(context);
}
@Override
public void cancelStall(MovementContext context) {
MovementBehaviour.super.cancelStall(context);
deactivateLastVisitedContact(context);
}
public void deactivateLastVisitedContact(MovementContext context) {
if (context.data.contains("lastContact")) {
BlockPos last = NbtUtils.readBlockPos(context.data.getCompound("lastContact"));

View file

@ -294,9 +294,9 @@ public class TrackGraph {
frontier.add(connected.getLocation());
if (target != null) {
transfer(level, currentNode, target);
if (preAssignedIds != null && preAssignedIds.containsKey(currentNode.getNetId()))
target.setId(preAssignedIds.get(currentNode.getNetId()));
transfer(level, currentNode, target);
}
}
@ -331,6 +331,7 @@ public class TrackGraph {
}
}
if (level != null)
for (Iterator<UUID> iterator = trains.keySet()
.iterator(); iterator.hasNext();) {
UUID uuid = iterator.next();
@ -340,6 +341,7 @@ public class TrackGraph {
if (!train.isTravellingOn(node))
continue;
train.graph = target;
train.syncTrackGraphChanges();
}
nodes.remove(nodeLoc);

View file

@ -123,6 +123,15 @@ public class TrackGraphSync {
for (TrackNode node : graph.nodes.values()) {
TrackGraphSyncPacket currentPacket = packet;
currentPacket.addedNodes.put(node.getNetId(), Pair.of(node.getLocation(), node.getNormal()));
if (sent++ < 1000)
continue;
sent = 0;
packet = flushAndCreateNew(graph, player, packet);
}
for (TrackNode node : graph.nodes.values()) {
TrackGraphSyncPacket currentPacket = packet;
if (!graph.connectionsByNode.containsKey(node))
continue;
graph.connectionsByNode.get(node)
@ -130,7 +139,7 @@ public class TrackGraphSync {
Couple<Integer> key = Couple.create(node.getNetId(), node2.getNetId());
currentPacket.addedEdges.add(Pair.of(key, edge.getTurn()));
currentPacket.syncEdgeData(node, node2, edge);
});//FIXME these edges will have missing nodes
});
if (sent++ < 1000)
continue;

View file

@ -122,7 +122,7 @@ public class Carriage {
Function<TravellingPoint, ITrackSelector> forwardControl,
Function<TravellingPoint, ITrackSelector> backwardControl, int type) {
boolean onTwoBogeys = isOnTwoBogeys();
double stress = onTwoBogeys ? bogeySpacing - getAnchorDiff() : 0;
double stress = train.derailed ? 0 : onTwoBogeys ? bogeySpacing - getAnchorDiff() : 0;
blocked = false;
MutableDouble distanceMoved = new MutableDouble(distance);
@ -190,7 +190,7 @@ public class Carriage {
return distanceMoved.getValue();
}
private double getAnchorDiff() {
public double getAnchorDiff() {
double diff = 0;
int entries = 0;
@ -825,6 +825,9 @@ public class Carriage {
double diffY = positionVec.y - coupledVec.y;
double diffZ = positionVec.z - coupledVec.z;
if (!entity.level.isClientSide())
entity.setServerSidePrevPosition();
entity.setPos(positionAnchor);
entity.prevYaw = entity.yaw;
entity.prevPitch = entity.pitch;

View file

@ -109,6 +109,8 @@ public class CarriageBogey {
public double getStress() {
if (getDimension() == null)
return 0;
if (carriage.train.derailed)
return 0;
return type.getWheelPointSpacing() - leading().getPosition()
.distanceTo(trailing().getPosition());
}

View file

@ -76,6 +76,8 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
private boolean arrivalSoundReversed;
private int arrivalSoundTicks;
private Vec3 serverPrevPos;
public CarriageContraptionEntity(EntityType<?> type, Level world) {
super(type, world);
validForRender = false;
@ -133,6 +135,17 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
return entityData.get(SCHEDULED);
}
public void setServerSidePrevPosition() {
serverPrevPos = position();
}
@Override
public Vec3 getPrevPositionVec() {
if (!level.isClientSide() && serverPrevPos != null)
return serverPrevPos;
return super.getPrevPositionVec();
}
public boolean isLocalCoordWithin(BlockPos localPos, int min, int max) {
if (!(getContraption() instanceof CarriageContraption cc))
return false;
@ -173,6 +186,8 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
@Override
protected void tickContraption() {
if (nonDamageTicks > 0)
nonDamageTicks--;
if (!(contraption instanceof CarriageContraption cc))
return;
if (carriage == null) {
@ -511,6 +526,11 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
return true;
}
if (carriage.train.speedBeforeStall != null && targetSpeed != 0
&& Math.signum(carriage.train.speedBeforeStall) != Math.signum(targetSpeed)) {
carriage.train.cancelStall();
}
if (currentStation != null && targetSpeed != 0) {
stationMessage = false;
player.displayClientMessage(new TextComponent("<i> Departing from ").withStyle(ChatFormatting.YELLOW)

View file

@ -19,7 +19,9 @@ import javax.annotation.Nullable;
import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.commons.lang3.mutable.MutableObject;
import com.simibubi.create.AllMovementBehaviours;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour;
import com.simibubi.create.content.logistics.trains.DimensionPalette;
import com.simibubi.create.content.logistics.trains.GraphLocation;
import com.simibubi.create.content.logistics.trains.TrackEdge;
@ -186,6 +188,7 @@ public class Train {
Carriage previousCarriage = null;
int carriageCount = carriages.size();
boolean stalled = false;
double maxStress = 0;
for (int i = 0; i < carriageCount; i++) {
Carriage carriage = carriages.get(i);
@ -226,6 +229,7 @@ public class Train {
actual = total / entries;
stress[i - 1] = target - actual;
maxStress = Math.max(maxStress, Math.abs(target - actual));
}
previousCarriage = carriage;
@ -275,7 +279,7 @@ public class Train {
Function<TravellingPoint, ITrackSelector> backwardControl =
toFollowBackward == null ? navigation::control : mp -> mp.follow(toFollowBackward);
double totalStress = leadingStress + trailingStress;
double totalStress = derailed ? 0 : leadingStress + trailingStress;
boolean first = i == 0;
boolean last = i == carriageCount - 1;
int carriageType = first ? last ? Carriage.BOTH : Carriage.FIRST : last ? Carriage.LAST : Carriage.MIDDLE;
@ -283,6 +287,14 @@ public class Train {
carriage.travel(level, graph, distance + totalStress, forwardControl, backwardControl, carriageType);
blocked |= carriage.blocked;
boolean onTwoBogeys = carriage.isOnTwoBogeys();
maxStress = Math.max(maxStress, onTwoBogeys ? carriage.bogeySpacing - carriage.getAnchorDiff() : 0);
maxStress = Math.max(maxStress, carriage.leadingBogey()
.getStress());
if (onTwoBogeys)
maxStress = Math.max(maxStress, carriage.trailingBogey()
.getStress());
if (index == 0) {
distance = actualDistance;
collideWithOtherTrains(level, carriage);
@ -296,6 +308,15 @@ public class Train {
navigation.cancelNavigation();
runtime.tick(level);
status.endOfTrack();
} else if (maxStress > 2) {
speed = 0;
navigation.cancelNavigation();
runtime.tick(level);
derailed = true;
syncTrackGraphChanges();
status.highStress();
} else if (speed != 0)
status.trackOK();
@ -338,6 +359,20 @@ public class Train {
};
}
public void cancelStall() {
speedBeforeStall = null;
carriages.forEach(c -> {
c.stalled = false;
c.forEachPresentEntity(cce -> cce.getContraption()
.getActors()
.forEach(pair -> {
MovementBehaviour behaviour = AllMovementBehaviours.of(pair.getKey().state);
if (behaviour != null)
behaviour.cancelStall(pair.getValue());
}));
});
}
private boolean occupy(UUID groupId, @Nullable UUID boundaryId) {
reservedSignalBlocks.remove(groupId);
if (boundaryId != null && occupiedSignalBlocks.containsKey(groupId))

View file

@ -96,6 +96,7 @@ public class TrainRelocationPacket extends SimplePacketBase {
sender.displayClientMessage(Lang.translate("train.relocate.success")
.withStyle(ChatFormatting.GREEN), true);
train.syncTrackGraphChanges();
train.carriages.forEach(c -> c.forEachPresentEntity(e -> e.nonDamageTicks = 10));
return;
}

View file

@ -147,9 +147,11 @@ public class TrainRelocator {
Vec3 lookAngle = mc.player.getLookAngle();
boolean direction = bezierSelection != null && lookAngle.dot(bezierSelection.direction()) < 0;
boolean result = relocate(relocating, mc.level, blockPos, hoveredBezier, direction, lookAngle, true);
if (!simulate && result)
if (!simulate && result) {
relocating.carriages.forEach(c -> c.forEachPresentEntity(e -> e.nonDamageTicks = 10));
AllPackets.channel.sendToServer(new TrainRelocationPacket(relocatingTrain, blockPos, hoveredBezier,
direction, lookAngle, relocatingEntityId));
}
return lastHoveredResult = result;
}
@ -238,6 +240,7 @@ public class TrainRelocator {
train.graph = graph;
train.speed = 0;
train.migratingPoints.clear();
train.cancelStall();
if (train.navigation.destination != null)
train.navigation.cancelNavigation();

View file

@ -70,6 +70,13 @@ public class TrainStatus {
track = true;
}
public void highStress() {
if (track)
return;
displayInformation("Forced stop due to Stress on Couplings", false);
track = true;
}
public void doublePortal() {
if (track)
return;

View file

@ -64,7 +64,7 @@ public class CurvedTrackInteraction {
breakTicks++;
breakTimeout = 2;
breakProgress += creative ? 0.25f : blockState.getDestroyProgress(player, level, breakPos);
breakProgress += creative ? 0.125f : blockState.getDestroyProgress(player, level, breakPos) / 8f;
Vec3 vec = VecHelper.offsetRandomly(result.vec(), level.random, 0.25f);
level.addParticle(new BlockParticleOption(ParticleTypes.BLOCK, blockState), vec.x, vec.y, vec.z, 0, 0, 0);