Skip to content

Commit

Permalink
Fix item pipe smoothing (#105)
Browse files Browse the repository at this point in the history
- Change smoothing updates to be based on client ticks instead of BER invocations.
- Make sure that traveling items only get ticked once per tick, both on the client and on the server side. This fixes jitters when an item moves from one pipe to the other, caused by the item being moved twice in the same tick.
  • Loading branch information
Technici4n committed Mar 8, 2024
1 parent 6fd5e69 commit ec64c46
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public void render(PipeBlockEntity pipe, float tickDelta, PoseStack matrices, Mu
Vec3 from, to;
double ratio;

var distance = Mth.frac(item.traveledDistance()) + ClientTravelingItemSmoothing.getAndUpdateDistanceDelta(item)
var distance = Mth.frac(item.traveledDistance()) + ClientTravelingItemSmoothing.getDistanceDelta(item, tickDelta)
+ item.speed() * tickDelta;
if (distance <= 0.5) {
from = findFaceMiddle(item.in().getOpposite());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,11 +299,21 @@ public void tickMovingItems() {
return;
}

long curTick = getLevel().getGameTime();

// List of items that moved out of this pipe.
List<TravelingItem> movedOut = new ArrayList<>();

for (var iterator = travelingItems.iterator(); iterator.hasNext();) {
var travelingItem = iterator.next();

// Make sure we only update items once per tick.
// This makes sure that we don't update items twice if they get moved to another pipe.
if (travelingItem.lastTick == curTick) {
continue;
}
travelingItem.lastTick = curTick;

// Calculate in which path segment the item is now, and in which segment it is after moving it
int currentIndex = (int) travelingItem.traveledDistance;
travelingItem.traveledDistance += travelingItem.getSpeed();
Expand Down Expand Up @@ -538,9 +548,20 @@ public void readClientNbt(CompoundTag tag) {

@Override
public void clientTick() {
long curTick = getLevel().getGameTime();

for (var it = clientTravelingItems.iterator(); it.hasNext();) {
ClientTravelingItem travelingItem = it.next();

// Make sure we only update items once per tick.
// This makes sure that we don't update items twice if they get moved to another pipe.
if (travelingItem.lastTick == curTick) {
continue;
}
travelingItem.lastTick = curTick;

travelingItem.traveledDistance += travelingItem.speed();
ClientTravelingItemSmoothing.onTickItem(travelingItem);

if (Mth.frac(travelingItem.traveledDistance) < travelingItem.speed()) {
// Goes out of this pipe!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class TravelingItem {
public final FailedInsertStrategy strategy;
public final double speedMultiplier;
public double traveledDistance;
public long lastTick;

public TravelingItem(ItemVariant variant, int amount, ItemPath path, FailedInsertStrategy strategy, double speedMultiplier,
double traveledDistance) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public final class ClientTravelingItem {
public Direction in;
public Direction out;
final double speed;
public long lastTick;

public ClientTravelingItem(int id, ItemVariant variant, int amount, double totalPathDistance, double traveledDistance, Direction in,
Direction out, double speed) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@
* Also keeps track of a client tick counter.
*/
public class ClientTravelingItemSmoothing {

public static void onUnpausedTick() {
// Cleanup items that are not used anymore
for (var it = INFOS.values().iterator(); it.hasNext();) {
SmoothingInfo info = it.next();

// Apply 1 tick worth of smoothing
info.wrongOffset *= smooth(1);

// Cleanup items that are not used anymore
info.deadTimer--;
if (info.deadTimer <= 0) {
it.remove();
Expand All @@ -55,16 +58,30 @@ public static void onReceiveItem(ClientTravelingItem item) {
info.resetTimer();
}

public static double getAndUpdateDistanceDelta(ClientTravelingItem item) {
public static void onTickItem(ClientTravelingItem item) {
var info = INFOS.get(item.id);
if (info == null) {
return 0;
return;
}
info.resetTimer();
info.lastTraveledDistance = item.traveledDistance;
double offset = info.wrongOffset;
info.wrongOffset *= 0.95; // might want to adjust based on FPS or similar
return offset;
}

public static double getDistanceDelta(ClientTravelingItem item, float partialTick) {
var info = INFOS.get(item.id);
if (info == null) {
return 0;
}
return info.wrongOffset * smooth(partialTick);
}

/**
* Smoothing constant. It takes ~1 second to smooth out 95% of the offset.
*/
private static final double SMOOTHING_EXP = 0.15;

private static double smooth(double ticks) {
return Math.exp(-SMOOTHING_EXP * ticks);
}

public static long getClientTick() {
Expand Down

0 comments on commit ec64c46

Please sign in to comment.