Rise of the Jank II

- Girders can now connect to brackets vertically
- Fixed girders not updating connections when built with placement assist
- Tracks now actively update girder shapes placed beneath the ties
- Fixed bezier girder texture inconsistencies to the block models
- Fixed flywheel rendered tracks creating headless instances
- Fixed track not removing its tileentity properly when changing to basic state
- Fixed track rendering not using the correct normals for diffuse
- Fixed tracks not rendering in schematic previews
- Tracks now transform correctly in schematics
- Fixed tracks not validating for target track after placement via schematic
- Tracks can now merge TE data when placed into each other via schematic
- Fixed junctions not working correctly with graph building
This commit is contained in:
simibubi 2022-02-09 21:13:33 +01:00
parent 2b750c943a
commit 2ca099ce6b
27 changed files with 574 additions and 283 deletions

View file

@ -114,8 +114,9 @@ public class AllBlockPartials {
TRACK_SEGMENT_LEFT = block("track/segment_left"),
TRACK_SEGMENT_RIGHT = block("track/segment_right"),
TRACK_TIE = block("track/tie"),
GIRDER_SEGMENT = block("metal_girder/segment"),
GIRDER_SEGMENT_2 = block("metal_girder/segment2"),
GIRDER_SEGMENT_TOP = block("metal_girder/segment_top"),
GIRDER_SEGMENT_MIDDLE = block("metal_girder/segment_middle"),
GIRDER_SEGMENT_BOTTOM = block("metal_girder/segment_bottom"),
TRACK_STATION_OVERLAY = block("track/station_overlay"),
TRACK_STATION_OVERLAY_DIAGONAL = block("track/station_overlay_diagonal"),

View file

@ -40,11 +40,11 @@ import net.minecraft.world.phys.Vec3;
public class StructureTransform {
// Assuming structures cannot be rotated around multiple axes at once
Rotation rotation;
int angle;
Axis rotationAxis;
BlockPos offset;
Mirror mirror;
public Axis rotationAxis;
public BlockPos offset;
public int angle;
public Rotation rotation;
public Mirror mirror;
private StructureTransform(BlockPos offset, int angle, Axis axis, Rotation rotation, Mirror mirror) {
this.offset = offset;
@ -88,6 +88,15 @@ public class StructureTransform {
mirror = Mirror.NONE;
}
public Vec3 applyWithoutOffsetUncentered(Vec3 localVec) {
Vec3 vec = localVec;
if (mirror != null)
vec = VecHelper.mirror(vec, mirror);
if (rotationAxis != null)
vec = VecHelper.rotate(vec, angle, rotationAxis);
return vec;
}
public Vec3 applyWithoutOffset(Vec3 localVec) {
Vec3 vec = localVec;
if (mirror != null)

View file

@ -55,6 +55,11 @@ public class BracketedTileEntityBehaviour extends TileEntityBehaviour {
this.bracket = Optional.of(state);
reRender = true;
tileEntity.notifyUpdate();
Level world = getWorld();
if (world.isClientSide)
return;
tileEntity.getBlockState()
.updateNeighbourShapes(world, getPos(), 3);
}
public void transformBracket(StructureTransform transform) {
@ -71,10 +76,15 @@ public class BracketedTileEntityBehaviour extends TileEntityBehaviour {
world.levelEvent(2001, getPos(), Block.getId(getBracket()));
this.bracket = Optional.empty();
reRender = true;
if (inOnReplacedContext)
if (inOnReplacedContext) {
tileEntity.sendData();
else
tileEntity.notifyUpdate();
return;
}
tileEntity.notifyUpdate();
if (world.isClientSide)
return;
tileEntity.getBlockState()
.updateNeighbourShapes(world, getPos(), 3);
}
public boolean isBracketPresent() {

View file

@ -2,6 +2,8 @@ package com.simibubi.create.content.curiosities.girder;
import static net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED;
import java.util.Random;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllShapes;
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
@ -10,7 +12,7 @@ import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileE
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.content.logistics.block.chute.AbstractChuteBlock;
import com.simibubi.create.content.logistics.trains.track.TrackBlock;
import com.simibubi.create.content.logistics.trains.track.TrackBlock.TrackShape;
import com.simibubi.create.content.logistics.trains.track.TrackShape;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.placement.IPlacementHelper;
@ -19,6 +21,7 @@ import com.simibubi.create.foundation.utility.placement.PlacementHelpers;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
@ -107,6 +110,12 @@ public class GirderBlock extends Block implements SimpleWaterloggedBlock, IWrenc
return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : Fluids.EMPTY.defaultFluidState();
}
@Override
public void tick(BlockState p_60462_, ServerLevel p_60463_, BlockPos p_60464_, Random p_60465_) {
Block.updateOrDestroy(p_60462_, Block.updateFromNeighbourShapes(p_60462_, p_60463_, p_60464_), p_60463_,
p_60464_, 3);
}
@Override
public BlockState updateShape(BlockState state, Direction direction, BlockState neighbourState, LevelAccessor world,
BlockPos pos, BlockPos neighbourPos) {

View file

@ -17,6 +17,7 @@ import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraftforge.common.ForgeMod;
@ -94,7 +95,8 @@ public class GirderPlacementHelper implements IPlacementHelper {
.isReplaceable())
continue;
return PlacementOffset.success(newPos, bState -> withAxis(bState, dir.getAxis()));
return PlacementOffset.success(newPos,
bState -> Block.updateFromNeighbourShapes(withAxis(bState, dir.getAxis()), world, newPos));
}
return PlacementOffset.fail();

View file

@ -4,7 +4,7 @@ import java.util.Iterator;
import com.jozufozu.flywheel.repack.joml.Math;
import com.jozufozu.flywheel.util.transform.MatrixTransformStack;
import com.mojang.math.Matrix4f;
import com.mojang.blaze3d.vertex.PoseStack.Pose;
import com.simibubi.create.content.logistics.trains.track.TrackRenderer;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
@ -24,7 +24,6 @@ import net.minecraftforge.api.distmarker.OnlyIn;
public class BezierConnection implements Iterable<BezierConnection.Segment> {
public Couple<BlockPos> tePositions;
public Couple<Boolean> trackEnds;
public Couple<Vec3> starts;
public Couple<Vec3> axes;
public Couple<Vec3> normals;
@ -44,37 +43,37 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
private double handleLength;
public BezierConnection(Couple<BlockPos> positions, Couple<Vec3> starts, Couple<Vec3> axes, Couple<Vec3> normals,
Couple<Boolean> targets, boolean primary, boolean girder) {
boolean primary, boolean girder) {
tePositions = positions;
this.starts = starts;
this.axes = axes;
this.normals = normals;
this.trackEnds = targets;
this.primary = primary;
this.hasGirder = girder;
resolved = false;
}
public BezierConnection secondary() {
return new BezierConnection(tePositions.swap(), starts.swap(), axes.swap(), normals.swap(), trackEnds.swap(),
false, hasGirder);
return new BezierConnection(tePositions.swap(), starts.swap(), axes.swap(), normals.swap(), false, hasGirder);
}
public BezierConnection(CompoundTag compound) {
this(Couple.deserializeEach(compound.getList("Positions", Tag.TAG_COMPOUND), NbtUtils::readBlockPos),
Couple.deserializeEach(compound.getList("Starts", Tag.TAG_COMPOUND), VecHelper::readNBTCompound),
public BezierConnection(CompoundTag compound, BlockPos localTo) {
this(Couple.deserializeEach(compound.getList("Positions", Tag.TAG_COMPOUND), NbtUtils::readBlockPos)
.map(b -> b.offset(localTo)),
Couple.deserializeEach(compound.getList("Starts", Tag.TAG_COMPOUND), VecHelper::readNBTCompound)
.map(v -> v.add(Vec3.atLowerCornerOf(localTo))),
Couple.deserializeEach(compound.getList("Axes", Tag.TAG_COMPOUND), VecHelper::readNBTCompound),
Couple.deserializeEach(compound.getList("Normals", Tag.TAG_COMPOUND), VecHelper::readNBTCompound),
Couple.create(compound.getBoolean("TrackEnd1"), compound.getBoolean("TrackEnd2")),
compound.getBoolean("Primary"), compound.getBoolean("Girder"));
}
public CompoundTag write() {
public CompoundTag write(BlockPos localTo) {
Couple<BlockPos> tePositions = this.tePositions.map(b -> b.subtract(localTo));
Couple<Vec3> starts = this.starts.map(v -> v.subtract(Vec3.atLowerCornerOf(localTo)));
CompoundTag compound = new CompoundTag();
compound.putBoolean("Girder", hasGirder);
compound.putBoolean("Primary", primary);
compound.putBoolean("TrackEnd1", trackEnds.getFirst());
compound.putBoolean("TrackEnd2", trackEnds.getSecond());
compound.put("Positions", tePositions.serializeEach(NbtUtils::writeBlockPos));
compound.put("Starts", starts.serializeEach(VecHelper::writeNBTCompound));
compound.put("Axes", axes.serializeEach(VecHelper::writeNBTCompound));
@ -85,7 +84,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
public BezierConnection(FriendlyByteBuf buffer) {
this(Couple.create(buffer::readBlockPos), Couple.create(() -> VecHelper.read(buffer)),
Couple.create(() -> VecHelper.read(buffer)), Couple.create(() -> VecHelper.read(buffer)),
Couple.create(buffer::readBoolean), buffer.readBoolean(), buffer.readBoolean());
buffer.readBoolean(), buffer.readBoolean());
}
public void write(FriendlyByteBuf buffer) {
@ -93,7 +92,6 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
starts.forEach(v -> VecHelper.write(v, buffer));
axes.forEach(v -> VecHelper.write(v, buffer));
normals.forEach(v -> VecHelper.write(v, buffer));
trackEnds.forEach(buffer::writeBoolean);
buffer.writeBoolean(primary);
buffer.writeBoolean(hasGirder);
}
@ -344,8 +342,8 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
@OnlyIn(Dist.CLIENT)
public static class SegmentAngles {
public Matrix4f tieTransform;
public Couple<Matrix4f> railTransforms;
public Pose tieTransform;
public Couple<Pose> railTransforms;
public BlockPos lightPosition;
}
@ -353,8 +351,8 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
@OnlyIn(Dist.CLIENT)
public static class GirderAngles {
public Couple<Matrix4f> beams;
public Couple<Couple<Matrix4f>> beamCaps;
public Couple<Pose> beams;
public Couple<Couple<Pose>> beamCaps;
public BlockPos lightPosition;
}
@ -398,8 +396,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
.rotateZRadians(tieAngles.z)
.translate(-1 / 2f, -2 / 16f - 1 / 256f, 0);
angles.tieTransform = mts.unwrap()
.last()
.pose();
.last();
angles.railTransforms = Couple.create(null, null);
// Rails
@ -418,8 +415,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
.translate(0, -2 / 16f + (i % 2 == 0 ? 1 : -1) / 2048f - 1 / 256f, -1 / 32f)
.scale(1, 1, (float) diff.length() * scale);
angles.railTransforms.set(first, mts.unwrap()
.last()
.pose());
.last());
}
previousOffsets = railOffsets;
@ -494,8 +490,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
.translate(0, 2 / 16f + (segment.index % 2 == 0 ? 1 : -1) / 2048f - 1 / 1024f, -1 / 32f)
.scale(1, 1, (float) beamDiff.length() * scale);
angles.beams.set(first, mts.unwrap()
.last()
.pose());
.last());
// Caps
for (boolean top : Iterate.trueAndFalse) {
@ -516,8 +511,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
.scale(1, 1, (float) diff.length() * scale);
angles.beamCaps.get(top)
.set(first, mts.unwrap()
.last()
.pose());
.last());
}
}

View file

@ -2,6 +2,7 @@ package com.simibubi.create.content.logistics.trains;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.util.Mth;
@ -60,11 +61,11 @@ public class TrackEdge {
}
public CompoundTag write() {
return isTurn() ? turn.write() : new CompoundTag();
return isTurn() ? turn.write(BlockPos.ZERO) : new CompoundTag();
}
public static TrackEdge read(CompoundTag tag) {
return new TrackEdge(tag.contains("Positions") ? new BezierConnection(tag) : null);
return new TrackEdge(tag.contains("Positions") ? new BezierConnection(tag, BlockPos.ZERO) : null);
}
}

View file

@ -26,7 +26,7 @@ public class TrackGraphHelper {
double length = axis.length();
List<Pair<BlockPos, DiscoveredLocation>> ends =
TrackPropagator.getEnds(level, pos, trackBlockState, null, true);
TrackPropagator.getEnds(level, pos, trackBlockState, true, null, null);
TrackGraph graph = null;
TrackNode frontNode = null;
@ -46,7 +46,7 @@ public class TrackGraphHelper {
for (int i = 0; i < 32; i++) {
DiscoveredLocation loc = current;
List<Pair<BlockPos, DiscoveredLocation>> list =
TrackPropagator.getEnds(level, currentPos, level.getBlockState(currentPos), current, true);
TrackPropagator.getEnds(level, currentPos, level.getBlockState(currentPos), true, current, null);
if (!list.isEmpty()) {
currentPos = list.get(0)
.getFirst();

View file

@ -13,7 +13,7 @@ import javax.annotation.Nullable;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
import com.simibubi.create.content.logistics.trains.track.TrackBlock;
import com.simibubi.create.content.logistics.trains.track.TrackBlock.TrackShape;
import com.simibubi.create.content.logistics.trains.track.TrackShape;
import com.simibubi.create.content.logistics.trains.track.TrackTileEntity;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper;
@ -47,11 +47,10 @@ public class TrackPropagator {
currentPos = pos;
currentNode = location;
}
}
public static void onRailRemoved(LevelAccessor reader, BlockPos pos, BlockState state) {
List<Pair<BlockPos, DiscoveredLocation>> ends = getEnds(reader, pos, state, null, false);
List<Pair<BlockPos, DiscoveredLocation>> ends = getEnds(reader, pos, state, false, null, null);
TrackGraph foundGraph = null;
GlobalRailwayManager manager = Create.RAILWAYS;
TrackGraphSync sync = manager.sync;
@ -78,9 +77,16 @@ public class TrackPropagator {
for (Pair<BlockPos, DiscoveredLocation> removedEnd : ends) {
BlockPos adjPos = removedEnd.getFirst();
BlockState adjState = reader.getBlockState(adjPos);
if (!getEnds(reader, adjPos, adjState, removedEnd.getSecond(), true).isEmpty())
toUpdate.add(onRailAdded(reader, adjPos, adjState));
List<Pair<BlockPos, DiscoveredLocation>> adjEnds =
getEnds(reader, adjPos, adjState, true, removedEnd.getSecond(), null);
if (adjEnds.isEmpty())
continue;
Vec3 filter = adjEnds.get(0)
.getSecond()
.getLocation()
.subtract(removedEnd.getSecond()
.getLocation());
toUpdate.add(onRailAdded(reader, adjPos, adjState, filter.normalize()));
}
for (TrackGraph railGraph : toUpdate)
@ -89,7 +95,7 @@ public class TrackPropagator {
manager.markTracksDirty();
}
public static TrackGraph onRailAdded(LevelAccessor reader, BlockPos pos, BlockState state) {
public static TrackGraph onRailAdded(LevelAccessor reader, BlockPos pos, BlockState state, Vec3 axisFilter) {
// 1. Remove all immediately reachable node locations
GlobalRailwayManager manager = Create.RAILWAYS;
@ -97,7 +103,7 @@ public class TrackPropagator {
List<FrontierEntry> frontier = new ArrayList<>();
Set<DiscoveredLocation> visited = new HashSet<>();
Set<TrackGraph> connectedGraphs = new HashSet<>();
addInitialEndsOf(reader, pos, state, frontier, false);
addInitialEndsOf(reader, pos, state, frontier, false, axisFilter);
int emergencyExit = 1000;
while (!frontier.isEmpty()) {
@ -155,7 +161,7 @@ public class TrackPropagator {
// 2. Find the first graph node candidate nearby
addInitialEndsOf(reader, pos, state, frontier, true);
addInitialEndsOf(reader, pos, state, frontier, true, axisFilter);
emergencyExit = 1000;
while (!frontier.isEmpty()) {
@ -229,8 +235,8 @@ public class TrackPropagator {
}
private static void addInitialEndsOf(LevelAccessor reader, BlockPos pos, BlockState state,
List<FrontierEntry> frontier, boolean ignoreTurns) {
for (Pair<BlockPos, DiscoveredLocation> initial : getEnds(reader, pos, state, null, ignoreTurns))
List<FrontierEntry> frontier, boolean ignoreTurns, Vec3 axisFilter) {
for (Pair<BlockPos, DiscoveredLocation> initial : getEnds(reader, pos, state, ignoreTurns, null, axisFilter))
frontier.add(new FrontierEntry(initial.getFirst(), pos, initial.getSecond()));
}
@ -255,13 +261,13 @@ public class TrackPropagator {
}
for (Pair<BlockPos, DiscoveredLocation> pair : getEnds(reader, prevPos, reader.getBlockState(prevPos),
entry.currentNode, false))
false, entry.currentNode, null))
if (!pair.getSecond()
.equals(entry.prevNode))
ends.add(pair);
}
ends.addAll(getEnds(reader, entry.currentPos, currentState, entry.currentNode, false));
ends.addAll(getEnds(reader, entry.currentPos, currentState, false, entry.currentNode, null));
return ends;
}
@ -304,7 +310,7 @@ public class TrackPropagator {
// TODO ITrackBlock
public static List<Pair<BlockPos, DiscoveredLocation>> getEnds(LevelReader reader, BlockPos pos, BlockState state,
@Nullable DiscoveredLocation fromEnd, boolean ignoreTurns) {
boolean ignoreTurns, @Nullable DiscoveredLocation fromEnd, @Nullable Vec3 axisFilter) {
Vec3 center = VecHelper.getCenterOf(pos);
List<Pair<BlockPos, DiscoveredLocation>> list = new ArrayList<>();
@ -315,21 +321,36 @@ public class TrackPropagator {
if (state.getValue(TrackBlock.HAS_TURN) && blockEntity instanceof TrackTileEntity && !ignoreTurns) {
TrackTileEntity trackTileEntity = (TrackTileEntity) blockEntity;
trackTileEntity.getConnections()
.forEach(map -> map.forEach((connectedPos, bc) -> addToSet(fromEnd, list,
(d, b) -> d == 1 ? Vec3.atLowerCornerOf(bc.tePositions.get(b)) : bc.starts.get(b), bc.normals::get,
bc)));
.forEach((connectedPos, bc) -> {
Vec3 curveHandle = bc.axes.getFirst();
if (axisFilter != null && !testAxisFilter(curveHandle.normalize(), axisFilter))
return;
addToSet(fromEnd, list,
(d, b) -> d == 1 ? Vec3.atLowerCornerOf(bc.tePositions.get(b)) : bc.starts.get(b),
bc.normals::get, bc);
});
}
TrackShape shape = state.getValue(TrackBlock.SHAPE);
if (shape != TrackShape.NONE)
shape.getAxes()
.forEach(axis -> addToSet(fromEnd, list, (d, b) -> axis.scale(b ? d : -d)
if (shape == TrackShape.NONE)
return list;
shape.getAxes()
.forEach(axis -> {
if (axisFilter != null && !testAxisFilter(axis.normalize(), axisFilter))
return;
addToSet(fromEnd, list, (d, b) -> axis.scale(b ? d : -d)
.add(center)
.add(0, axis.y == 0 ? -.5 : 0, 0), b -> shape.getNormal(), null));
.add(0, axis.y == 0 ? -.5 : 0, 0), b -> shape.getNormal(), null);
});
return list;
}
private static boolean testAxisFilter(Vec3 axis, Vec3 filter) {
return Mth.equal(axis.distanceToSqr(filter), 0) || Mth.equal(axis.distanceToSqr(filter.scale(-1)), 0);
}
private static void addToSet(DiscoveredLocation fromEnd, List<Pair<BlockPos, DiscoveredLocation>> list,
BiFunction<Double, Boolean, Vec3> offsetFactory, Function<Boolean, Vec3> normalFactory,
BezierConnection viaTurn) {

View file

@ -326,7 +326,7 @@ public class StationTileEntity extends SmartTileEntity {
DiscoveredLocation location = null;
List<Pair<BlockPos, DiscoveredLocation>> ends =
TrackPropagator.getEnds(level, trackPosition, trackState, null, true);
TrackPropagator.getEnds(level, trackPosition, trackState, true, null, null);
for (Pair<BlockPos, DiscoveredLocation> pair : ends)
if (trackPosition.relative(assemblyDirection)
.equals(pair.getFirst()))

View file

@ -4,7 +4,6 @@ import java.util.List;
import java.util.Map.Entry;
import java.util.Random;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.util.transform.MatrixTransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
@ -15,12 +14,12 @@ import com.simibubi.create.AllTileEntities;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.content.curiosities.girder.GirderBlock;
import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.content.logistics.trains.TrackPropagator;
import com.simibubi.create.content.logistics.trains.management.StationTileEntity;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;
@ -29,18 +28,20 @@ import net.minecraft.core.Direction.Axis;
import net.minecraft.core.Direction.AxisDirection;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition.Builder;
@ -62,74 +63,6 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac
public static final EnumProperty<TrackShape> SHAPE = EnumProperty.create("shape", TrackShape.class);
public static final BooleanProperty HAS_TURN = BooleanProperty.create("turn");
public enum TrackShape implements StringRepresentable {
NONE("", Vec3.ZERO),
ZO("z_ortho", new Vec3(0, 0, 1)),
XO("x_ortho", new Vec3(1, 0, 0)),
PD("diag", new Vec3(1, 0, 1)),
ND("diag_2", new Vec3(-1, 0, 1)),
AN("ascending", 180, new Vec3(0, 1, -1), new Vec3(0, 1, 1)),
AS("ascending", 0, new Vec3(0, 1, 1), new Vec3(0, 1, -1)),
AE("ascending", 270, new Vec3(1, 1, 0), new Vec3(-1, 1, 0)),
AW("ascending", 90, new Vec3(-1, 1, 0), new Vec3(1, 1, 0)),
CR_O("cross_ortho", new Vec3(0, 0, 1), new Vec3(1, 0, 0)),
CR_D("cross_diag", new Vec3(1, 0, 1), new Vec3(-1, 0, 1)),
CR_PDX("cross_d1_xo", new Vec3(1, 0, 0), new Vec3(1, 0, 1)),
CR_PDZ("cross_d1_zo", new Vec3(0, 0, 1), new Vec3(1, 0, 1)),
CR_NDX("cross_d2_xo", new Vec3(1, 0, 0), new Vec3(-1, 0, 1)),
CR_NDZ("cross_d2_zo", new Vec3(0, 0, 1), new Vec3(-1, 0, 1));
private String model;
private List<Vec3> axes;
private int modelRotation;
private Vec3 normal;
private TrackShape(String model, Vec3 axis) {
this(model, 0, axis, new Vec3(0, 1, 0));
}
private TrackShape(String model, Vec3 axis, Vec3 secondAxis) {
this.model = model;
this.modelRotation = 0;
this.normal = new Vec3(0, 1, 0);
this.axes = ImmutableList.of(axis, secondAxis);
}
private TrackShape(String model, int modelRotation, Vec3 axis, Vec3 normal) {
this.model = model;
this.modelRotation = modelRotation;
this.normal = normal.normalize();
this.axes = ImmutableList.of(axis);
}
@Override
public String getSerializedName() {
return Lang.asId(name());
}
public String getModel() {
return model;
}
public List<Vec3> getAxes() {
return axes;
}
public boolean isJunction() {
return axes.size() > 1;
}
public Vec3 getNormal() {
return normal;
}
public int getModelRotation() {
return modelRotation;
}
}
public TrackBlock(Properties p_49795_) {
super(p_49795_);
registerDefaultState(defaultBlockState().setValue(SHAPE, TrackShape.ZO)
@ -201,14 +134,18 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac
public void onPlace(BlockState pState, Level pLevel, BlockPos pPos, BlockState pOldState, boolean pIsMoving) {
if (pOldState.getBlock() == this && pState.setValue(HAS_TURN, true) == pOldState.setValue(HAS_TURN, true))
return;
if (pLevel.isClientSide)
return;
LevelTickAccess<Block> blockTicks = pLevel.getBlockTicks();
if (!blockTicks.hasScheduledTick(pPos, this))
pLevel.scheduleTick(pPos, this, 1);
updateGirders(pState, pLevel, pPos, blockTicks);
}
@Override
public void tick(BlockState p_60462_, ServerLevel p_60463_, BlockPos p_60464_, Random p_60465_) {
TrackPropagator.onRailAdded(p_60463_, p_60464_, p_60462_);
for (Vec3 axis : getTrackAxes(p_60463_, p_60464_, p_60462_))
TrackPropagator.onRailAdded(p_60463_, p_60464_, p_60462_, axis.normalize());
}
@Override
@ -216,7 +153,7 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac
boolean removeTE = false;
if (pState.getValue(HAS_TURN) && (!pState.is(pNewState.getBlock()) || !pNewState.getValue(HAS_TURN))) {
BlockEntity blockEntity = pLevel.getBlockEntity(pPos);
if (blockEntity instanceof TrackTileEntity)
if (blockEntity instanceof TrackTileEntity && !pLevel.isClientSide)
((TrackTileEntity) blockEntity).removeInboundConnections();
removeTE = true;
}
@ -225,6 +162,8 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac
TrackPropagator.onRailRemoved(pLevel, pPos, pState);
if (removeTE)
pLevel.removeBlockEntity(pPos);
if (!pLevel.isClientSide)
updateGirders(pState, pLevel, pPos, pLevel.getBlockTicks());
}
@Override
@ -253,14 +192,24 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac
return InteractionResult.SUCCESS;
}
// if (asItem() == itemInHand.getItem()) {
// TrackConnectionPlacementHandler.select(world, pos, player.getLookAngle(), itemInHand);
// return InteractionResult.SUCCESS;
// }
return InteractionResult.PASS;
}
private void updateGirders(BlockState pState, Level pLevel, BlockPos pPos, LevelTickAccess<Block> blockTicks) {
for (Vec3 vec3 : getTrackAxes(pLevel, pPos, pState)) {
if (vec3.length() > 1 || vec3.y != 0)
continue;
for (int side : Iterate.positiveAndNegative) {
BlockPos girderPos = pPos.below()
.offset(vec3.z * side, 0, vec3.x * side);
BlockState girderState = pLevel.getBlockState(girderPos);
if (girderState.getBlock()instanceof GirderBlock girderBlock
&& !blockTicks.hasScheduledTick(girderPos, girderBlock))
pLevel.scheduleTick(girderPos, girderBlock, 1);
}
}
}
@Override
public boolean canSurvive(BlockState state, LevelReader reader, BlockPos pos) {
return reader.getBlockState(pos.below())
@ -298,16 +247,21 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac
.add(0, (vertical ? 0 : -.5f), 0)
.add(axis.scale(.5));
}
@Override
public InteractionResult onWrenched(BlockState state, UseOnContext context) {
return InteractionResult.SUCCESS;
}
@Override
public BlockState overlay(BlockGetter world, BlockPos pos, BlockState existing, BlockState placed) {
if (placed.getBlock() != this)
return existing;
TrackShape existingShape = existing.getValue(SHAPE);
TrackShape placedShape = placed.getValue(SHAPE);
TrackShape combinedShape = null;
for (boolean flip : Iterate.trueAndFalse) {
TrackShape s1 = flip ? existingShape : placedShape;
TrackShape s2 = flip ? placedShape : existingShape;
@ -324,26 +278,22 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac
if (s1 == TrackShape.ZO && s2 == TrackShape.ND)
combinedShape = TrackShape.CR_NDZ;
}
if (combinedShape != null)
existing = existing.setValue(SHAPE, combinedShape);
return existing;
}
@Override
public BlockState getRotatedBlockState(BlockState state, Direction targetedFace) {
switch (state.getValue(SHAPE)) {
case ND:
return state.setValue(SHAPE, TrackShape.XO);
case PD:
return state.setValue(SHAPE, TrackShape.ZO);
case XO:
return state.setValue(SHAPE, TrackShape.PD);
case ZO:
return state.setValue(SHAPE, TrackShape.ND);
default:
return state;
}
public BlockState rotate(BlockState state, Rotation pRotation) {
return state.setValue(SHAPE, state.getValue(SHAPE)
.rotate(pRotation));
}
@Override
public BlockState mirror(BlockState state, Mirror pMirror) {
return state.setValue(SHAPE, state.getValue(SHAPE)
.mirror(pMirror));
}
@Override

View file

@ -113,20 +113,17 @@ public class TrackBlockItem extends BlockItem {
ITrackBlock track = (ITrackBlock) block;
Pair<Vec3, AxisDirection> nearestTrackAxis = track.getNearestTrackAxis(world, pos, blockState, lookVec);
Vec3 axis = nearestTrackAxis.getFirst();
boolean front = nearestTrackAxis.getSecond() == AxisDirection.POSITIVE;
Vec3 axis = nearestTrackAxis.getFirst()
.scale(nearestTrackAxis.getSecond() == AxisDirection.POSITIVE ? -1 : 1);
Vec3 end = track.getCurveStart(world, pos, blockState, axis);
Vec3 normal = track.getUpNormal(world, pos, blockState)
.normalize();
axis = axis.scale(front ? -1 : 1);
Vec3 end = track.getCurveStart(world, pos, blockState, axis);
CompoundTag compoundTag = heldItem.getOrCreateTagElement("ConnectingFrom");
compoundTag.put("Pos", NbtUtils.writeBlockPos(pos));
compoundTag.put("Axis", VecHelper.writeNBT(axis));
compoundTag.put("Normal", VecHelper.writeNBT(normal));
compoundTag.put("End", VecHelper.writeNBT(end));
compoundTag.putBoolean("Front", front);
return true;
}

View file

@ -1,7 +1,6 @@
package com.simibubi.create.content.logistics.trains.track;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.track.TrackBlock.TrackShape;
import com.simibubi.create.foundation.data.SpecialBlockStateGen;
import com.tterrag.registrate.providers.DataGenContext;
import com.tterrag.registrate.providers.RegistrateBlockstateProvider;

View file

@ -2,7 +2,6 @@ package com.simibubi.create.content.logistics.trains.track;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nullable;
@ -12,11 +11,11 @@ import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.light.LightUpdater;
import com.jozufozu.flywheel.util.FlwUtil;
import com.jozufozu.flywheel.util.box.GridAlignedBB;
import com.jozufozu.flywheel.util.box.ImmutableBox;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.PoseStack.Pose;
import com.simibubi.create.AllBlockPartials;
import com.simibubi.create.content.logistics.trains.BezierConnection;
import com.simibubi.create.content.logistics.trains.BezierConnection.GirderAngles;
@ -39,13 +38,12 @@ public class TrackInstance extends BlockEntityInstance<TrackTileEntity> {
@Override
public void update() {
if (blockEntity.connections.stream()
.allMatch(Map::isEmpty)) {
if (blockEntity.connections.isEmpty())
return;
}
instances = blockEntity.connections.stream()
.flatMap(FlwUtil::mapValues)
remove();
instances = blockEntity.connections.values()
.stream()
.map(this::createInstance)
.filter(Objects::nonNull)
.toList();
@ -56,10 +54,8 @@ public class TrackInstance extends BlockEntityInstance<TrackTileEntity> {
@Override
public ImmutableBox getVolume() {
List<BlockPos> out = new ArrayList<>();
out.addAll(blockEntity.connections.getFirst()
.keySet());
out.addAll(blockEntity.connections.getSecond()
.keySet());
out.addAll(blockEntity.connections.keySet());
out.addAll(blockEntity.connections.keySet());
return GridAlignedBB.containingAll(out);
}
@ -129,12 +125,15 @@ public class TrackInstance extends BlockEntityInstance<TrackTileEntity> {
var modelIndex = i - 1;
ties[modelIndex].setTransform(pose)
.mulPose(segment.tieTransform);
.mulPose(segment.tieTransform.pose())
.mulNormal(segment.tieTransform.normal());
tiesLightPos[modelIndex] = segment.lightPosition.offset(tePosition);
for (boolean first : Iterate.trueAndFalse) {
Pose transform = segment.railTransforms.get(first);
(first ? this.left : this.right)[modelIndex].setTransform(pose)
.mulPose(segment.railTransforms.get(first));
.mulPose(transform.pose())
.mulNormal(transform.normal());
(first ? leftLightPos : rightLightPos)[modelIndex] = segment.lightPosition.offset(tePosition);
}
}
@ -185,8 +184,9 @@ public class TrackInstance extends BlockEntityInstance<TrackTileEntity> {
beams = Couple.create(() -> new ModelData[segCount]);
beamCaps = Couple.create(() -> Couple.create(() -> new ModelData[segCount]));
lightPos = new BlockPos[segCount];
beams.forEach(mat.getModel(AllBlockPartials.GIRDER_SEGMENT_2)::createInstances);
beamCaps.forEach(c -> c.forEach(mat.getModel(AllBlockPartials.GIRDER_SEGMENT)::createInstances));
beams.forEach(mat.getModel(AllBlockPartials.GIRDER_SEGMENT_MIDDLE)::createInstances);
beamCaps.forEachWithContext((c, top) -> c.forEach(mat.getModel(top ? AllBlockPartials.GIRDER_SEGMENT_TOP
: AllBlockPartials.GIRDER_SEGMENT_BOTTOM)::createInstances));
GirderAngles[] bakedGirders = bc.getBakedGirders();
for (int i = 1; i < bakedGirders.length; i++) {
@ -195,13 +195,18 @@ public class TrackInstance extends BlockEntityInstance<TrackTileEntity> {
lightPos[modelIndex] = segment.lightPosition.offset(tePosition);
for (boolean first : Iterate.trueAndFalse) {
Pose beamTransform = segment.beams.get(first);
beams.get(first)[modelIndex].setTransform(pose)
.mulPose(segment.beams.get(first));
for (boolean top : Iterate.trueAndFalse)
.mulPose(beamTransform.pose())
.mulNormal(beamTransform.normal());
for (boolean top : Iterate.trueAndFalse) {
Pose beamCapTransform = segment.beamCaps.get(top)
.get(first);
beamCaps.get(top)
.get(first)[modelIndex].setTransform(pose)
.mulPose(segment.beamCaps.get(top)
.get(first));
.mulPose(beamCapTransform.pose())
.mulNormal(beamCapTransform.normal());
}
}
}

View file

@ -84,11 +84,10 @@ public class TrackPlacement {
ITrackBlock track = (ITrackBlock) state2.getBlock();
Pair<Vec3, AxisDirection> nearestTrackAxis = track.getNearestTrackAxis(level, pos2, state2, lookVec);
Vec3 axis2 = nearestTrackAxis.getFirst();
boolean front2 = nearestTrackAxis.getSecond() == AxisDirection.POSITIVE;
Vec3 axis2 = nearestTrackAxis.getFirst()
.scale(nearestTrackAxis.getSecond() == AxisDirection.POSITIVE ? -1 : 1);
Vec3 normal2 = track.getUpNormal(level, pos2, state2)
.normalize();
axis2 = axis2.scale(front2 ? -1 : 1);
Vec3 normedAxis2 = axis2.normalize();
Vec3 end2 = track.getCurveStart(level, pos2, state2, axis2);
@ -137,7 +136,6 @@ public class TrackPlacement {
if ((parallel && normedAxis1.dot(normedAxis2) > 0) || (!parallel && (intersect[0] < 0 || intersect[1] < 0))) {
axis2 = axis2.scale(-1);
normedAxis2 = normedAxis2.scale(-1);
front2 = !front2;
end2 = track.getCurveStart(level, pos2, state2, axis2);
if (level.isClientSide) {
info.end2 = end2;
@ -161,7 +159,7 @@ public class TrackPlacement {
BlockPos targetPos2 = pos2.offset(offset2.x, offset2.y, offset2.z);
info.curve = new BezierConnection(Couple.create(targetPos1, targetPos2),
Couple.create(end1.add(offset1), end2.add(offset2)), Couple.create(normedAxis1, normedAxis2),
Couple.create(normal1, normal2), Couple.create(front1, front2), true, girder);
Couple.create(normal1, normal2), true, girder);
}
// S curve or Straight
@ -281,7 +279,7 @@ public class TrackPlacement {
if (dist2 > dist1)
ex2 = (float) ((dist2 - dist1) / axis2.length());
double turnSize = Math.min(dist1, dist2);
double turnSize = Math.min(dist1, dist2) - .1d;
boolean ninety = (absAngle + .25f) % 90 < 1;
if (intersect[0] < 0 || intersect[1] < 0)
@ -312,7 +310,7 @@ public class TrackPlacement {
info.curve = skipCurve ? null
: new BezierConnection(Couple.create(targetPos1, targetPos2),
Couple.create(end1.add(offset1), end2.add(offset2)), Couple.create(normedAxis1, normedAxis2),
Couple.create(normal1, normal2), Couple.create(front1, front2), true, girder);
Couple.create(normal1, normal2), true, girder);
info.valid = true;
@ -324,13 +322,15 @@ public class TrackPlacement {
Vec3 axis = first ? axis1 : axis2;
BlockPos pos = first ? pos1 : pos2;
BlockState state = first ? state1 : state2;
if (state.hasProperty(TrackBlock.HAS_TURN))
state = state.setValue(TrackBlock.HAS_TURN, false);
for (int i = 0; i < extent; i++) {
Vec3 offset = axis.scale(i);
BlockPos offsetPos = pos.offset(offset.x, offset.y, offset.z);
BlockState stateAtPos = level.getBlockState(offsetPos);
BlockState toPlace = state;
boolean canPlace = stateAtPos.getMaterial()
.isReplaceable();
if (stateAtPos.getBlock()instanceof ITrackBlock trackAtPos) {
@ -361,8 +361,8 @@ public class TrackPlacement {
TrackTileEntity tte1 = (TrackTileEntity) te1;
TrackTileEntity tte2 = (TrackTileEntity) te2;
tte1.addConnection(front1, info.curve);
tte2.addConnection(front2, info.curve.secondary());
tte1.addConnection(info.curve);
tte2.addConnection(info.curve.secondary());
return info;
}

View file

@ -1,7 +1,8 @@
package com.simibubi.create.content.logistics.trains.track;
import static com.simibubi.create.AllBlockPartials.GIRDER_SEGMENT;
import static com.simibubi.create.AllBlockPartials.GIRDER_SEGMENT_2;
import static com.simibubi.create.AllBlockPartials.GIRDER_SEGMENT_BOTTOM;
import static com.simibubi.create.AllBlockPartials.GIRDER_SEGMENT_MIDDLE;
import static com.simibubi.create.AllBlockPartials.GIRDER_SEGMENT_TOP;
import static com.simibubi.create.AllBlockPartials.TRACK_SEGMENT_LEFT;
import static com.simibubi.create.AllBlockPartials.TRACK_SEGMENT_RIGHT;
import static com.simibubi.create.AllBlockPartials.TRACK_TIE;
@ -10,6 +11,7 @@ import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.repack.joml.Math;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.PoseStack.Pose;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.content.logistics.trains.BezierConnection;
import com.simibubi.create.content.logistics.trains.BezierConnection.GirderAngles;
@ -29,6 +31,7 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction.Axis;
import net.minecraft.util.Mth;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
@ -40,22 +43,21 @@ public class TrackRenderer extends SafeTileEntityRenderer<TrackTileEntity> {
@Override
protected void renderSafe(TrackTileEntity te, float partialTicks, PoseStack ms, MultiBufferSource buffer, int light,
int overlay) {
if (Backend.isOn())
Level level = te.getLevel();
if (Backend.canUseInstancing(level))
return;
VertexConsumer vb = buffer.getBuffer(RenderType.cutoutMipped());
te.connections.forEach(map -> map.values()
.forEach(bc -> renderBezierTurn(bc, ms, vb)));
te.connections.values()
.forEach(bc -> renderBezierTurn(level, bc, ms, vb));
}
public static void renderBezierTurn(BezierConnection bc, PoseStack ms, VertexConsumer vb) {
public static void renderBezierTurn(Level level, BezierConnection bc, PoseStack ms, VertexConsumer vb) {
if (!bc.isPrimary())
return;
ms.pushPose();
BlockPos tePosition = bc.tePositions.getFirst();
BlockState air = Blocks.AIR.defaultBlockState();
ClientLevel level = Minecraft.getInstance().level;
SegmentAngles[] segments = bc.getBakedSegments();
TransformStack.cast(ms)
@ -68,17 +70,19 @@ public class TrackRenderer extends SafeTileEntityRenderer<TrackTileEntity> {
int light = LevelRenderer.getLightColor(level, segment.lightPosition.offset(tePosition));
CachedBufferer.partial(TRACK_TIE, air)
.mulPose(segment.tieTransform)
.disableDiffuseMult()
.mulPose(segment.tieTransform.pose())
.mulNormal(segment.tieTransform.normal())
.light(light)
.renderInto(ms, vb);
for (boolean first : Iterate.trueAndFalse)
for (boolean first : Iterate.trueAndFalse) {
Pose transform = segment.railTransforms.get(first);
CachedBufferer.partial(first ? TRACK_SEGMENT_LEFT : TRACK_SEGMENT_RIGHT, air)
.mulPose(segment.railTransforms.get(first))
.disableDiffuseMult()
.mulPose(transform.pose())
.mulNormal(transform.normal())
.light(light)
.renderInto(ms, vb);
}
}
ms.popPose();
@ -97,19 +101,22 @@ public class TrackRenderer extends SafeTileEntityRenderer<TrackTileEntity> {
int light = LevelRenderer.getLightColor(level, segment.lightPosition.offset(tePosition));
for (boolean first : Iterate.trueAndFalse) {
CachedBufferer.partial(GIRDER_SEGMENT_2, air)
.mulPose(segment.beams.get(first))
.disableDiffuseMult()
Pose beamTransform = segment.beams.get(first);
CachedBufferer.partial(GIRDER_SEGMENT_MIDDLE, air)
.mulPose(beamTransform.pose())
.mulNormal(beamTransform.normal())
.light(light)
.renderInto(ms, vb);
for (boolean top : Iterate.trueAndFalse)
CachedBufferer.partial(GIRDER_SEGMENT, air)
.mulPose(segment.beamCaps.get(top)
.get(first))
.disableDiffuseMult()
for (boolean top : Iterate.trueAndFalse) {
Pose beamCapTransform = segment.beamCaps.get(top)
.get(first);
CachedBufferer.partial(top ? GIRDER_SEGMENT_TOP : GIRDER_SEGMENT_BOTTOM, air)
.mulPose(beamCapTransform.pose())
.mulNormal(beamCapTransform.normal())
.light(light)
.renderInto(ms, vb);
}
}
}
}

View file

@ -0,0 +1,135 @@
package com.simibubi.create.content.logistics.trains.track;
import java.util.EnumMap;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.phys.Vec3;
public enum TrackShape implements StringRepresentable {
NONE("", Vec3.ZERO),
ZO("z_ortho", new Vec3(0, 0, 1)),
XO("x_ortho", new Vec3(1, 0, 0)),
PD("diag", new Vec3(1, 0, 1)),
ND("diag_2", new Vec3(-1, 0, 1)),
AN("ascending", 180, new Vec3(0, 1, -1), new Vec3(0, 1, 1)),
AS("ascending", 0, new Vec3(0, 1, 1), new Vec3(0, 1, -1)),
AE("ascending", 270, new Vec3(1, 1, 0), new Vec3(-1, 1, 0)),
AW("ascending", 90, new Vec3(-1, 1, 0), new Vec3(1, 1, 0)),
CR_O("cross_ortho", new Vec3(0, 0, 1), new Vec3(1, 0, 0)),
CR_D("cross_diag", new Vec3(1, 0, 1), new Vec3(-1, 0, 1)),
CR_PDX("cross_d1_xo", new Vec3(1, 0, 0), new Vec3(1, 0, 1)),
CR_PDZ("cross_d1_zo", new Vec3(0, 0, 1), new Vec3(1, 0, 1)),
CR_NDX("cross_d2_xo", new Vec3(1, 0, 0), new Vec3(-1, 0, 1)),
CR_NDZ("cross_d2_zo", new Vec3(0, 0, 1), new Vec3(-1, 0, 1));
private String model;
private List<Vec3> axes;
private int modelRotation;
private Vec3 normal;
static EnumMap<TrackShape, TrackShape> zMirror = new EnumMap<>(TrackShape.class),
xMirror = new EnumMap<>(TrackShape.class), clockwise = new EnumMap<>(TrackShape.class);
static {
zMirror.putAll(ImmutableMap.<TrackShape, TrackShape>builder()
.put(PD, ND)
.put(ND, PD)
.put(AN, AS)
.put(AS, AN)
.put(CR_PDX, CR_NDX)
.put(CR_NDX, CR_PDX)
.put(CR_PDZ, CR_NDZ)
.put(CR_NDZ, CR_PDZ)
.build());
xMirror.putAll(ImmutableMap.<TrackShape, TrackShape>builder()
.put(PD, ND)
.put(ND, PD)
.put(AE, AW)
.put(AW, AE)
.put(CR_PDX, CR_NDX)
.put(CR_NDX, CR_PDX)
.put(CR_PDZ, CR_NDZ)
.put(CR_NDZ, CR_PDZ)
.build());
clockwise.putAll(ImmutableMap.<TrackShape, TrackShape>builder()
.put(PD, ND)
.put(ND, PD)
.put(XO, ZO)
.put(ZO, XO)
.put(AE, AS)
.put(AS, AW)
.put(AW, AN)
.put(AN, AE)
.put(CR_PDX, CR_NDZ)
.put(CR_NDX, CR_PDZ)
.put(CR_PDZ, CR_NDX)
.put(CR_NDZ, CR_PDX)
.build());
}
private TrackShape(String model, Vec3 axis) {
this(model, 0, axis, new Vec3(0, 1, 0));
}
private TrackShape(String model, Vec3 axis, Vec3 secondAxis) {
this.model = model;
this.modelRotation = 0;
this.normal = new Vec3(0, 1, 0);
this.axes = ImmutableList.of(axis, secondAxis);
}
private TrackShape(String model, int modelRotation, Vec3 axis, Vec3 normal) {
this.model = model;
this.modelRotation = modelRotation;
this.normal = normal.normalize();
this.axes = ImmutableList.of(axis);
}
@Override
public String getSerializedName() {
return Lang.asId(name());
}
public String getModel() {
return model;
}
public List<Vec3> getAxes() {
return axes;
}
public boolean isJunction() {
return axes.size() > 1;
}
public Vec3 getNormal() {
return normal;
}
public int getModelRotation() {
return modelRotation;
}
public TrackShape mirror(Mirror mirror) {
return mirror == Mirror.NONE ? this
: mirror == Mirror.FRONT_BACK ? xMirror.getOrDefault(this, this) : zMirror.getOrDefault(this, this);
}
public TrackShape rotate(Rotation rotation) {
TrackShape shape = this;
for (int i = 0; i < rotation.ordinal(); i++)
shape = clockwise.getOrDefault(shape, shape);
return shape;
}
}

View file

@ -1,102 +1,118 @@
package com.simibubi.create.content.logistics.trains.track;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableTE;
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.content.logistics.trains.BezierConnection;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.tileEntity.IMergeableTE;
import com.simibubi.create.foundation.tileEntity.RemoveTileEntityPacket;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.Couple;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction.Axis;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
public class TrackTileEntity extends SmartTileEntity {
public class TrackTileEntity extends SmartTileEntity implements ITransformableTE, IMergeableTE {
Couple<Map<BlockPos, BezierConnection>> connections;
Map<BlockPos, BezierConnection> connections;
boolean connectionsValidated;
public TrackTileEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
connections = Couple.create(HashMap::new);
connections = new HashMap<>();
connectionsValidated = false;
}
public Couple<Map<BlockPos, BezierConnection>> getConnections() {
public Map<BlockPos, BezierConnection> getConnections() {
if (!level.isClientSide && !connectionsValidated)
validateConnections();
return connections;
}
public void addConnection(boolean front, BezierConnection connection) {
connections.get(front)
.put(connection.getKey(), connection);
notifyUpdate();
level.scheduleTick(worldPosition, getBlockState().getBlock(), 1);
private void validateConnections() {
Set<BlockPos> invalid = new HashSet<>();
for (Entry<BlockPos, BezierConnection> entry : connections.entrySet()) {
BlockPos key = entry.getKey();
BezierConnection bc = entry.getValue();
if (key.equals(bc.getKey()) && worldPosition.equals(bc.tePositions.getFirst())) {
BlockEntity blockEntity = level.getBlockEntity(key);
if (blockEntity instanceof TrackTileEntity trackTE && trackTE.connections.containsKey(worldPosition))
continue;
}
invalid.add(key);
}
connectionsValidated = true;
for (BlockPos blockPos : invalid)
removeConnection(blockPos);
}
public void removeConnection(boolean front, BlockPos target) {
connections.get(front)
.remove(target);
public void addConnection(BezierConnection connection) {
connections.put(connection.getKey(), connection);
level.scheduleTick(worldPosition, getBlockState().getBlock(), 1);
notifyUpdate();
}
// TODO remove TE when all connections removed. seems tricky without a packet
// because level.removeBlockEntity apparently no longer syncs
public void removeConnection(BlockPos target) {
connections.remove(target);
notifyUpdate();
if (!connections.isEmpty())
return;
// if (connections.getFirst()
// .isEmpty()
// && connections.getSecond()
// .isEmpty()) {
// BlockState blockState = getBlockState();
// if (!blockState.hasProperty(TrackBlock.HAS_TURN))
// return;
// }
BlockState blockState = getBlockState();
if (blockState.hasProperty(TrackBlock.HAS_TURN))
level.setBlockAndUpdate(worldPosition, blockState.setValue(TrackBlock.HAS_TURN, false));
AllPackets.channel.send(packetTarget(), new RemoveTileEntityPacket(worldPosition));
}
public void removeInboundConnections() {
connections.forEach(map -> map.values()
.forEach(bc -> {
BlockEntity blockEntity = level.getBlockEntity(bc.getKey());
if (!(blockEntity instanceof TrackTileEntity))
return;
TrackTileEntity other = (TrackTileEntity) blockEntity;
other.removeConnection(bc.trackEnds.getSecond(), bc.tePositions.getFirst());
}));
for (BezierConnection bezierConnection : connections.values()) {
BlockEntity blockEntity = level.getBlockEntity(bezierConnection.getKey());
if (!(blockEntity instanceof TrackTileEntity))
return;
TrackTileEntity other = (TrackTileEntity) blockEntity;
other.removeConnection(bezierConnection.tePositions.getFirst());
}
}
@Override
protected void write(CompoundTag tag, boolean clientPacket) {
super.write(tag, clientPacket);
CompoundTag connectionsTag = new CompoundTag();
connections.forEachWithContext((map, first) -> {
ListTag listTag = new ListTag();
map.values()
.forEach(e -> listTag.add(e.write()));
connectionsTag.put(first ? "Front" : "Back", listTag);
});
tag.put("Connections", connectionsTag);
ListTag listTag = new ListTag();
for (BezierConnection bezierConnection : connections.values())
listTag.add(bezierConnection.write(worldPosition));
tag.put("Connections", listTag);
}
@Override
protected void read(CompoundTag tag, boolean clientPacket) {
super.read(tag, clientPacket);
CompoundTag connectionsTag = tag.getCompound("Connections");
connections.forEach(Map::clear);
connections.forEachWithContext((map, first) -> connectionsTag.getList(first ? "Front" : "Back", 10)
.forEach(t -> {
if (!(t instanceof CompoundTag))
return;
BezierConnection connection = new BezierConnection((CompoundTag) t);
map.put(connection.getKey(), connection);
}));
connections.clear();
for (Tag t : tag.getList("Connections", Tag.TAG_COMPOUND)) {
if (!(t instanceof CompoundTag))
return;
BezierConnection connection = new BezierConnection((CompoundTag) t, worldPosition);
connections.put(connection.getKey(), connection);
}
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> InstancedRenderDispatcher.enqueueUpdate(this));
}
@ -109,4 +125,49 @@ public class TrackTileEntity extends SmartTileEntity {
@Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) {}
@Override
public void accept(BlockEntity other) {
if (other instanceof TrackTileEntity track)
connections.putAll(track.connections);
connectionsValidated = false;
level.scheduleTick(worldPosition, getBlockState().getBlock(), 1);
}
@Override
public void transform(StructureTransform transform) {
if (transform.rotationAxis != Axis.Y)
return;
Map<BlockPos, BezierConnection> transformedConnections = new HashMap<>();
for (Entry<BlockPos, BezierConnection> entry : connections.entrySet()) {
BezierConnection newConnection = entry.getValue();
newConnection.normals.replace(transform::applyWithoutOffsetUncentered);
newConnection.axes.replace(transform::applyWithoutOffsetUncentered);
BlockPos diff = newConnection.tePositions.getSecond()
.subtract(newConnection.tePositions.getFirst());
newConnection.tePositions.setSecond(new BlockPos(Vec3.atCenterOf(newConnection.tePositions.getFirst())
.add(transform.applyWithoutOffsetUncentered(Vec3.atLowerCornerOf(diff)))));
Vec3 teVec = Vec3.atLowerCornerOf(worldPosition);
Vec3 teCenterVec = teVec.add(0.5, 0.5, 0.5);
Vec3 start = newConnection.starts.getFirst();
Vec3 startToTE = start.subtract(teCenterVec);
Vec3 endToStart = newConnection.starts.getSecond()
.subtract(start);
startToTE = transform.applyWithoutOffsetUncentered(startToTE)
.add(teCenterVec);
endToStart = transform.applyWithoutOffsetUncentered(endToStart)
.add(startToTE);
newConnection.starts.setFirst(new TrackNodeLocation(startToTE).getLocation());
newConnection.starts.setSecond(new TrackNodeLocation(endToStart).getLocation());
BlockPos newTarget = newConnection.getKey();
transformedConnections.put(newTarget, newConnection);
}
connections = transformedConnections;
}
}

View file

@ -8,6 +8,7 @@ import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.components.structureMovement.BlockMovementChecks;
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.content.schematics.item.SchematicItem;
import com.simibubi.create.foundation.tileEntity.IMergeableTE;
import com.simibubi.create.foundation.utility.BlockHelper;
import net.minecraft.core.BlockPos;
@ -190,7 +191,9 @@ public class SchematicPrinter {
BlockEntity tileEntity = blockReader.getBlockEntity(pos);
BlockState toReplace = world.getBlockState(pos);
BlockEntity toReplaceTE = world.getBlockEntity(pos);
BlockState toReplaceOther = null;
if (state.hasProperty(BlockStateProperties.BED_PART) && state.hasProperty(BlockStateProperties.HORIZONTAL_FACING)
&& state.getValue(BlockStateProperties.BED_PART) == BedPart.FOOT)
toReplaceOther = world.getBlockState(pos.relative(state.getValue(BlockStateProperties.HORIZONTAL_FACING)));
@ -198,11 +201,14 @@ public class SchematicPrinter {
&& state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER)
toReplaceOther = world.getBlockState(pos.above());
boolean mergeTEs = tileEntity != null && toReplaceTE instanceof IMergeableTE mergeTE && toReplaceTE.getType()
.equals(tileEntity.getType());
if (!world.isLoaded(pos))
return false;
if (!world.getWorldBorder().isWithinBounds(pos))
return false;
if (toReplace == state)
if (toReplace == state && !mergeTEs)
return false;
if (toReplace.getDestroySpeed(world, pos) == -1
|| (toReplaceOther != null && toReplaceOther.getDestroySpeed(world, pos) == -1))

View file

@ -8,6 +8,7 @@ import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllKeys;
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.content.schematics.SchematicWorld;
import com.simibubi.create.content.schematics.client.tools.Tools;
import com.simibubi.create.content.schematics.filtering.SchematicInstances;
@ -23,6 +24,7 @@ import com.simibubi.create.foundation.utility.outliner.AABBOutline;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction.Axis;
import net.minecraft.core.Vec3i;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
@ -33,6 +35,8 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
@ -137,16 +141,27 @@ public class SchematicHandler {
SchematicWorld wMirroredFB = new SchematicWorld(clientWorld);
SchematicWorld wMirroredLR = new SchematicWorld(clientWorld);
StructurePlaceSettings placementSettings = new StructurePlaceSettings();
StructureTransform transform;
BlockPos pos;
pos = BlockPos.ZERO;
schematic.placeInWorld(w, pos, pos, placementSettings, w.getRandom(), Block.UPDATE_CLIENTS);
placementSettings.setMirror(Mirror.FRONT_BACK);
pos = BlockPos.ZERO.east(size.getX() - 1);
schematic.placeInWorld(wMirroredFB, pos, pos, placementSettings, wMirroredFB.getRandom(), Block.UPDATE_CLIENTS);
transform = new StructureTransform(placementSettings.getRotationPivot(), Axis.Y, Rotation.NONE,
placementSettings.getMirror());
for (BlockEntity te : wMirroredFB.getRenderedTileEntities())
transform.apply(te);
placementSettings.setMirror(Mirror.LEFT_RIGHT);
pos = BlockPos.ZERO.south(size.getZ() - 1);
schematic.placeInWorld(wMirroredLR, pos, pos, placementSettings, wMirroredFB.getRandom(), Block.UPDATE_CLIENTS);
transform = new StructureTransform(placementSettings.getRotationPivot(), Axis.Y, Rotation.NONE,
placementSettings.getMirror());
for (BlockEntity te : wMirroredLR.getRenderedTileEntities())
transform.apply(te);
renderers.get(0)
.display(w);
@ -187,11 +202,11 @@ public class SchematicHandler {
renderers.get(0)
.render(ms, buffer);
}
if (active)
currentTool.getTool()
.renderOnSchematic(ms, buffer);
.renderOnSchematic(ms, buffer);
ms.popPose();
}
@ -227,7 +242,7 @@ public class SchematicHandler {
return;
}
currentTool.getTool()
.handleRightClick();
.handleRightClick();
}
public void onKeyInput(int key, boolean pressed) {
@ -272,10 +287,12 @@ public class SchematicHandler {
private boolean itemLost(Player player) {
for (int i = 0; i < Inventory.getSelectionSize(); i++) {
if (!player.getInventory().getItem(i)
if (!player.getInventory()
.getItem(i)
.sameItem(activeSchematicItem))
continue;
if (!ItemStack.tagMatches(player.getInventory().getItem(i), activeSchematicItem))
if (!ItemStack.tagMatches(player.getInventory()
.getItem(i), activeSchematicItem))
continue;
return false;
}
@ -289,7 +306,8 @@ public class SchematicHandler {
public void sync() {
if (activeSchematicItem == null)
return;
AllPackets.channel.sendToServer(new SchematicSyncPacket(activeHotbarSlot, transformation.toSettings(), transformation.getAnchor(), deployed));
AllPackets.channel.sendToServer(new SchematicSyncPacket(activeHotbarSlot, transformation.toSettings(),
transformation.getAnchor(), deployed));
}
public void equip(Tools tool) {

View file

@ -57,6 +57,7 @@ import com.simibubi.create.foundation.command.SConfigureConfigPacket;
import com.simibubi.create.foundation.config.ui.CConfigureConfigPacket;
import com.simibubi.create.foundation.gui.container.ClearContainerPacket;
import com.simibubi.create.foundation.gui.container.GhostItemSubmitPacket;
import com.simibubi.create.foundation.tileEntity.RemoveTileEntityPacket;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringCountUpdatePacket;
import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollValueUpdatePacket;
import com.simibubi.create.foundation.utility.ServerSpeedProvider;
@ -132,6 +133,7 @@ public enum AllPackets {
PERSISTENT_DATA(ISyncPersistentData.PersistentDataPacket.class, ISyncPersistentData.PersistentDataPacket::new, PLAY_TO_CLIENT),
SYNC_POTATO_PROJECTILE_TYPES(PotatoProjectileTypeManager.SyncPacket.class, PotatoProjectileTypeManager.SyncPacket::new, PLAY_TO_CLIENT),
SYNC_RAIL_GRAPH(RailGraphSyncPacket.class, RailGraphSyncPacket::new, PLAY_TO_CLIENT),
REMOVE_TE(RemoveTileEntityPacket.class, RemoveTileEntityPacket::new, PLAY_TO_CLIENT),
;

View file

@ -0,0 +1,9 @@
package com.simibubi.create.foundation.tileEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
public interface IMergeableTE {
public void accept(BlockEntity other);
}

View file

@ -0,0 +1,26 @@
package com.simibubi.create.foundation.tileEntity;
import com.simibubi.create.foundation.networking.TileEntityDataPacket;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
public class RemoveTileEntityPacket extends TileEntityDataPacket<SyncedTileEntity> {
public RemoveTileEntityPacket(BlockPos pos) {
super(pos);
}
public RemoveTileEntityPacket(FriendlyByteBuf buffer) {
super(buffer);
}
@Override
protected void writeData(FriendlyByteBuf buffer) {}
@Override
protected void handlePacket(SyncedTileEntity tile) {
tile.setRemoved();
}
}

View file

@ -7,6 +7,7 @@ import javax.annotation.Nullable;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.contraptions.components.actors.SeatBlock;
import com.simibubi.create.foundation.tileEntity.IMergeableTE;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@ -200,15 +201,12 @@ public class BlockHelper {
int idx = chunk.getSectionIndex(target.getY());
LevelChunkSection chunksection = chunk.getSection(idx);
if (chunksection == null) {
chunksection = new LevelChunkSection(chunk.getSectionYFromSectionIndex(idx),
world.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY));
chunksection = new LevelChunkSection(chunk.getSectionYFromSectionIndex(idx), world.registryAccess()
.registryOrThrow(Registry.BIOME_REGISTRY));
chunk.getSections()[idx] = chunksection;
}
BlockState old = chunksection.setBlockState(
SectionPos.sectionRelative(target.getX()),
SectionPos.sectionRelative(target.getY()),
SectionPos.sectionRelative(target.getZ()),
state);
BlockState old = chunksection.setBlockState(SectionPos.sectionRelative(target.getX()),
SectionPos.sectionRelative(target.getY()), SectionPos.sectionRelative(target.getZ()), state);
chunk.setUnsaved(true);
world.markAndNotifyBlock(target, chunk, old, state, 82, 512);
@ -219,6 +217,8 @@ public class BlockHelper {
public static void placeSchematicBlock(Level world, BlockState state, BlockPos target, ItemStack stack,
@Nullable CompoundTag data) {
BlockEntity existingTile = world.getBlockEntity(target);
// Piston
if (state.hasProperty(BlockStateProperties.EXTENDED))
state = state.setValue(BlockStateProperties.EXTENDED, Boolean.FALSE);
@ -259,6 +259,14 @@ public class BlockHelper {
}
if (data != null) {
if (existingTile instanceof IMergeableTE mergeable) {
BlockEntity loaded = BlockEntity.loadStatic(target, state, data);
if (existingTile.getType()
.equals(loaded.getType())) {
mergeable.accept(loaded);
return;
}
}
BlockEntity tile = world.getBlockEntity(target);
if (tile != null) {
data.putInt("x", target.getX());

View file

@ -0,0 +1,21 @@
{
"credit": "Made with Blockbench",
"textures": {
"0": "create:block/girder"
},
"elements": [
{
"from": [-4, -1, 0],
"to": [4, 1, 8],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 13, 8]},
"faces": {
"north": {"uv": [12, 2, 16, 3], "texture": "#0"},
"east": {"uv": [1, 5, 5, 4], "texture": "#0"},
"south": {"uv": [12, 2, 16, 3], "texture": "#0"},
"west": {"uv": [3, 5, 7, 4], "texture": "#0"},
"up": {"uv": [12, 0, 16, 4], "rotation": 270, "texture": "#0"},
"down": {"uv": [2, 0, 6, 4], "rotation": 270, "texture": "#0"}
}
}
]
}