From 36c973d13a0a35197edf924f6bf71128d17a664e Mon Sep 17 00:00:00 2001 From: johni0702 Date: Mon, 1 Aug 2016 22:50:48 +0200 Subject: [PATCH] Add first person spectator keyframes to simplepathing --- ReplayStudio | 2 +- .../pathing/properties/CameraProperties.java | 8 +- .../pathing/properties/SpectatorProperty.java | 51 +++++ .../simplepathing/ReplayModSimplePathing.java | 6 + .../gui/GuiKeyframeTimeline.java | 26 +++ .../simplepathing/gui/GuiPathing.java | 157 ++++++++++++-- .../preparation/EntityPositionTracker.java | 200 ------------------ .../assets/replaymod/lang/en_US.lang | 2 + 8 files changed, 233 insertions(+), 219 deletions(-) create mode 100644 src/main/java/com/replaymod/pathing/properties/SpectatorProperty.java delete mode 100644 src/main/java/eu/crushedpixel/replaymod/preparation/EntityPositionTracker.java diff --git a/ReplayStudio b/ReplayStudio index ef307b46..4cb8cad6 160000 --- a/ReplayStudio +++ b/ReplayStudio @@ -1 +1 @@ -Subproject commit ef307b467895098c54345279485bde96579265db +Subproject commit 4cb8cad6c8ff238cf5f750ae05319a6a95c1e941 diff --git a/src/main/java/com/replaymod/pathing/properties/CameraProperties.java b/src/main/java/com/replaymod/pathing/properties/CameraProperties.java index 33f53f7e..af13d428 100644 --- a/src/main/java/com/replaymod/pathing/properties/CameraProperties.java +++ b/src/main/java/com/replaymod/pathing/properties/CameraProperties.java @@ -50,7 +50,9 @@ public class CameraProperties extends AbstractPropertyGroup { @Override public void applyToGame(Triple value, @NonNull Object replayHandler) { - ((ReplayHandler) replayHandler).getCameraEntity().setCameraPosition(value.getLeft(), value.getMiddle(), value.getRight()); + ReplayHandler handler = ((ReplayHandler) replayHandler); + handler.spectateCamera(); + handler.getCameraEntity().setCameraPosition(value.getLeft(), value.getMiddle(), value.getRight()); } @Override @@ -86,7 +88,9 @@ public class CameraProperties extends AbstractPropertyGroup { @Override public void applyToGame(Triple value, @NonNull Object replayHandler) { - ((ReplayHandler) replayHandler).getCameraEntity().setCameraRotation(value.getLeft(), value.getMiddle(), value.getRight()); + ReplayHandler handler = ((ReplayHandler) replayHandler); + handler.spectateCamera(); + handler.getCameraEntity().setCameraRotation(value.getLeft(), value.getMiddle(), value.getRight()); } @Override diff --git a/src/main/java/com/replaymod/pathing/properties/SpectatorProperty.java b/src/main/java/com/replaymod/pathing/properties/SpectatorProperty.java new file mode 100644 index 00000000..8bd6ee2e --- /dev/null +++ b/src/main/java/com/replaymod/pathing/properties/SpectatorProperty.java @@ -0,0 +1,51 @@ +package com.replaymod.pathing.properties; + +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.replaymod.replay.ReplayHandler; +import com.replaymod.replaystudio.pathing.property.AbstractProperty; +import com.replaymod.replaystudio.pathing.property.PropertyPart; +import com.replaymod.replaystudio.pathing.property.PropertyParts; +import lombok.NonNull; +import net.minecraft.entity.Entity; +import net.minecraft.world.World; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; + +/** + * Property for the camera spectating an entity. + */ +public class SpectatorProperty extends AbstractProperty { + public static final SpectatorProperty PROPERTY = new SpectatorProperty(); + public final PropertyPart ENTITY_ID = new PropertyParts.ForInteger(this, false); + private SpectatorProperty() { + super("spectate", "replaymod.gui.playeroverview.spectate", null, -1); + } + + @Override + public Collection> getParts() { + return Collections.singletonList(ENTITY_ID); + } + + @Override + public void applyToGame(Integer value, @NonNull Object replayHandler) { + ReplayHandler handler = ((ReplayHandler) replayHandler); + World world = handler.getCameraEntity().getEntityWorld(); + // Lookup entity by id, returns null if an entity with the id does not exists + Entity target = world.getEntityByID(value); + // Spectate entity, when called with null, returns to camera + handler.spectateEntity(target); + } + + @Override + public void toJson(JsonWriter writer, Integer value) throws IOException { + writer.value(value); + } + + @Override + public Integer fromJson(JsonReader reader) throws IOException { + return reader.nextInt(); + } +} diff --git a/src/main/java/com/replaymod/simplepathing/ReplayModSimplePathing.java b/src/main/java/com/replaymod/simplepathing/ReplayModSimplePathing.java index c5fd59ab..65f0896a 100644 --- a/src/main/java/com/replaymod/simplepathing/ReplayModSimplePathing.java +++ b/src/main/java/com/replaymod/simplepathing/ReplayModSimplePathing.java @@ -4,6 +4,7 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import com.replaymod.core.ReplayMod; import com.replaymod.pathing.properties.CameraProperties; +import com.replaymod.pathing.properties.SpectatorProperty; import com.replaymod.pathing.properties.TimestampProperty; import com.replaymod.replay.events.ReplayOpenEvent; import com.replaymod.replaystudio.pathing.PathingRegistry; @@ -79,6 +80,7 @@ public class ReplayModSimplePathing implements PathingRegistry { timeline.registerProperty(TimestampProperty.PROPERTY); timeline.registerProperty(CameraProperties.POSITION); timeline.registerProperty(CameraProperties.ROTATION); + timeline.registerProperty(SpectatorProperty.PROPERTY); return timeline; } @@ -107,4 +109,8 @@ public class ReplayModSimplePathing implements PathingRegistry { } } + + public ReplayMod getCore() { + return core; + } } diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiKeyframeTimeline.java b/src/main/java/com/replaymod/simplepathing/gui/GuiKeyframeTimeline.java index 40280836..b26b966b 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiKeyframeTimeline.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiKeyframeTimeline.java @@ -4,9 +4,11 @@ import com.google.common.base.Function; import com.google.common.collect.Iterables; import com.replaymod.core.ReplayMod; import com.replaymod.pathing.properties.CameraProperties; +import com.replaymod.pathing.properties.SpectatorProperty; import com.replaymod.pathing.properties.TimestampProperty; import com.replaymod.replaystudio.pathing.path.Keyframe; import com.replaymod.replaystudio.pathing.path.Path; +import com.replaymod.replaystudio.pathing.path.PathSegment; import com.replaymod.simplepathing.ReplayModSimplePathing; import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.element.advanced.AbstractGuiTimeline; @@ -53,6 +55,9 @@ public class GuiKeyframeTimeline extends AbstractGuiTimeline= endTime || endFrameTime <= startTime) { + continue; // Segment out of display range + } + + double relativeStart = startFrameTime - startTime; + double relativeEnd = endFrameTime - startTime; + int startX = BORDER_LEFT + Math.max(0, (int) (relativeStart / visibleTime * visibleWidth) + KEYFRAME_SIZE / 2 + 1); + int endX = BORDER_LEFT + Math.min(visibleWidth, (int) (relativeEnd / visibleTime * visibleWidth) - KEYFRAME_SIZE / 2); + if (startX < endX) { + renderer.drawRect(startX + 1, BORDER_TOP + 1, endX - startX - 2, KEYFRAME_SIZE - 2, 0xFF0088FF); + } + } + super.drawTimelineCursor(renderer, size); } diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java index 4d881166..688bc21a 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java @@ -6,6 +6,7 @@ import com.replaymod.core.ReplayMod; import com.replaymod.pathing.gui.GuiKeyframeRepository; import com.replaymod.pathing.player.RealtimeTimelinePlayer; import com.replaymod.pathing.properties.CameraProperties; +import com.replaymod.pathing.properties.SpectatorProperty; import com.replaymod.pathing.properties.TimestampProperty; import com.replaymod.render.gui.GuiRenderSettings; import com.replaymod.replay.ReplayHandler; @@ -19,18 +20,26 @@ import com.replaymod.replaystudio.pathing.path.Keyframe; import com.replaymod.replaystudio.pathing.path.Path; import com.replaymod.replaystudio.pathing.path.PathSegment; import com.replaymod.replaystudio.pathing.path.Timeline; +import com.replaymod.replaystudio.util.EntityPositionTracker; +import com.replaymod.replaystudio.util.Location; import com.replaymod.simplepathing.ReplayModSimplePathing; import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.RenderInfo; +import de.johni0702.minecraft.gui.container.GuiContainer; import de.johni0702.minecraft.gui.container.GuiPanel; import de.johni0702.minecraft.gui.element.*; +import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar; import de.johni0702.minecraft.gui.element.advanced.GuiTimelineTime; import de.johni0702.minecraft.gui.element.advanced.IGuiTimeline; import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.layout.VerticalLayout; +import de.johni0702.minecraft.gui.popup.AbstractGuiPopup; +import net.minecraft.entity.Entity; import net.minecraftforge.fml.common.Loader; import org.apache.commons.lang3.tuple.Triple; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.lwjgl.input.Keyboard; import org.lwjgl.util.Dimension; import org.lwjgl.util.ReadableDimension; @@ -38,6 +47,9 @@ import org.lwjgl.util.ReadablePoint; import org.lwjgl.util.WritablePoint; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; /** @@ -47,6 +59,8 @@ public class GuiPathing { public static final int TIME_PATH = 0; public static final int POSITION_PATH = 1; + private static final Logger logger = LogManager.getLogger(); + public final GuiTexturedButton playPauseButton = new GuiTexturedButton().setSize(20, 20) .setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE); @@ -174,6 +188,8 @@ public class GuiPathing { private final ReplayHandler replayHandler; private final RealtimeTimelinePlayer player; + private EntityPositionTracker entityTracker; + public GuiPathing(final ReplayMod core, final ReplayModSimplePathing mod, final ReplayHandler replayHandler) { this.mod = mod; this.replayHandler = replayHandler; @@ -315,6 +331,11 @@ public class GuiPathing { * @param isTime {@code true} for the time property button, {@code false} for the place property button */ private void updateKeyframe(final boolean isTime) { + if (entityTracker == null) { + loadEntityTracker(() -> updateKeyframe(isTime)); + return; + } + int time = timeline.getCursorPosition(); Timeline timeline = mod.getCurrentTimeline(); Path path = timeline.getPaths().get(isTime ? TIME_PATH : POSITION_PATH); @@ -339,31 +360,64 @@ public class GuiPathing { CameraEntity camera = replayHandler.getCameraEntity(); builder.setValue(CameraProperties.POSITION, Triple.of(camera.posX, camera.posY, camera.posZ)); builder.setValue(CameraProperties.ROTATION, Triple.of(camera.rotationYaw, camera.rotationPitch, camera.roll)); + if (!replayHandler.isCameraView()) { + Entity spectated = replayHandler.getOverlay().getMinecraft().getRenderViewEntity(); + builder.setValue(SpectatorProperty.PROPERTY, spectated.getEntityId()); + } } UpdateKeyframeProperties updateChange = builder.done(); updateChange.apply(timeline); - // If this new keyframe formed the first segment, then create and set the appropriate interpolator - if (path.getSegments().size() == 1) { + change = CombinedChange.createFromApplied(change, updateChange); + + // If this new keyframe formed the first segment of the time path + if (isTime && path.getSegments().size() == 1) { PathSegment segment = path.getSegments().iterator().next(); - Interpolator interpolator; - if (isTime) { - interpolator = new LinearInterpolator(); - interpolator.registerProperty(TimestampProperty.PROPERTY); - } else { - interpolator = new CubicSplineInterpolator(); - interpolator.registerProperty(CameraProperties.POSITION); - interpolator.registerProperty(CameraProperties.ROTATION); - } + Interpolator interpolator = new LinearInterpolator(); + interpolator.registerProperty(TimestampProperty.PROPERTY); SetInterpolator setInterpolator = SetInterpolator.create(segment, interpolator); setInterpolator.apply(timeline); - timeline.pushChange(CombinedChange.createFromApplied(change, updateChange, setInterpolator)); - } else { - timeline.pushChange(CombinedChange.createFromApplied(change, updateChange)); + change = CombinedChange.createFromApplied(change, setInterpolator); } - } else { - timeline.pushChange(change); } + // Update interpolators for spectator keyframes + // while this is overkill, it is far simpler than updating differently for every possible case + if (!isTime) { + Interpolator interpolator = null; + boolean isSpectatorInterpolator = false; + for (PathSegment segment : path.getSegments()) { + if (segment.getStartKeyframe().getValue(SpectatorProperty.PROPERTY).isPresent() + && segment.getEndKeyframe().getValue(SpectatorProperty.PROPERTY).isPresent()) { + // Spectator segment + if (!isSpectatorInterpolator) { + isSpectatorInterpolator = true; + interpolator = new LinearInterpolator(); + interpolator.registerProperty(SpectatorProperty.PROPERTY); + } + SetInterpolator setInterpolator = SetInterpolator.create(segment, interpolator); + setInterpolator.apply(timeline); + change = CombinedChange.createFromApplied(change, setInterpolator); + } else { + // Normal segment + if (isSpectatorInterpolator || interpolator == null) { + isSpectatorInterpolator = false; + interpolator = new CubicSplineInterpolator(); + interpolator.registerProperty(CameraProperties.POSITION); + interpolator.registerProperty(CameraProperties.ROTATION); + } + SetInterpolator setInterpolator = SetInterpolator.create(segment, interpolator); + setInterpolator.apply(timeline); + change = CombinedChange.createFromApplied(change, setInterpolator); + } + } + } + + Change specPosUpdate = updateSpectatorPositions(); + specPosUpdate.apply(timeline); + change = CombinedChange.createFromApplied(change, specPosUpdate); + + timeline.pushChange(change); + if (isTime) { mod.setSelectedTimeKeyframe(keyframe); } else { @@ -371,7 +425,78 @@ public class GuiPathing { } } + private Change updateSpectatorPositions() { + List changes = new ArrayList<>(); + Path positionPath = mod.getCurrentTimeline().getPaths().get(POSITION_PATH); + Path timePath = mod.getCurrentTimeline().getPaths().get(TIME_PATH); + timePath.updateAll(); + for (Keyframe keyframe : positionPath.getKeyframes()) { + Optional spectator = keyframe.getValue(SpectatorProperty.PROPERTY); + if (spectator.isPresent()) { + Optional time = timePath.getValue(TimestampProperty.PROPERTY, keyframe.getTime()); + if (!time.isPresent()) { + continue; // No time keyframes set at this video time, cannot determine replay time + } + Location expected = entityTracker.getEntityPositionAtTimestamp(spectator.get(), time.get()); + if (expected == null) { + continue; // We don't have any data on this entity for some reason + } + Triple pos = keyframe.getValue(CameraProperties.POSITION).orElse(Triple.of(0D, 0D, 0D)); + Triple rot = keyframe.getValue(CameraProperties.ROTATION).orElse(Triple.of(0F, 0F, 0F)); + Location actual = new Location(pos.getLeft(), pos.getMiddle(), pos.getRight(), rot.getLeft(), rot.getRight()); + if (!expected.equals(actual)) { + changes.add(UpdateKeyframeProperties.create(positionPath, keyframe) + .setValue(CameraProperties.POSITION, Triple.of(expected.getX(), expected.getY(), expected.getZ())) + .setValue(CameraProperties.ROTATION, Triple.of(expected.getYaw(), expected.getPitch(), 0f)).done() + ); + } + } + } + return CombinedChange.create(changes.toArray(new Change[changes.size()])); + } + + private void loadEntityTracker(Runnable runnable) { + LoadEntityTrackerPopup popup = new LoadEntityTrackerPopup(replayHandler.getOverlay()); + new Thread(() -> { + EntityPositionTracker tracker = new EntityPositionTracker(replayHandler.getReplayFile()); + try { + tracker.load(c -> popup.progressBar.setProgress((float)(double) c)); + } catch (IOException e) { + logger.error("Loading entity tracker:", e); + mod.getCore().runLater(() -> { + mod.getCore().printWarningToChat("Error loading entity tracker: %s", e.getLocalizedMessage()); + popup.close(); + }); + } + entityTracker = tracker; + mod.getCore().runLater(() -> { + popup.close(); + runnable.run(); + }); + }).start(); + } + public ReplayModSimplePathing getMod() { return mod; } + + private class LoadEntityTrackerPopup extends AbstractGuiPopup { + private final GuiProgressBar progressBar = new GuiProgressBar(popup).setSize(300, 20) + .setI18nLabel("replaymod.gui.loadentitytracker"); + + public LoadEntityTrackerPopup(GuiContainer container) { + super(container); + open(); + } + + @Override + public void close() { + super.close(); + } + + @Override + protected LoadEntityTrackerPopup getThis() { + return this; + } + } } diff --git a/src/main/java/eu/crushedpixel/replaymod/preparation/EntityPositionTracker.java b/src/main/java/eu/crushedpixel/replaymod/preparation/EntityPositionTracker.java deleted file mode 100644 index ec29a8ca..00000000 --- a/src/main/java/eu/crushedpixel/replaymod/preparation/EntityPositionTracker.java +++ /dev/null @@ -1,200 +0,0 @@ -package eu.crushedpixel.replaymod.preparation; - -import com.replaymod.replaystudio.PacketData; -import com.replaymod.replaystudio.io.ReplayInputStream; -import com.replaymod.replaystudio.replay.ZipReplayFile; -import com.replaymod.replaystudio.studio.ReplayStudio; -import com.replaymod.replaystudio.util.PacketUtils; -import eu.crushedpixel.replaymod.holders.AdvancedPosition; -import org.apache.commons.lang3.tuple.Pair; -import org.spacehq.mc.protocol.data.game.values.entity.player.PositionElement; -import org.spacehq.mc.protocol.packet.ingame.server.entity.ServerEntityMovementPacket; -import org.spacehq.mc.protocol.packet.ingame.server.entity.ServerEntityPositionPacket; -import org.spacehq.mc.protocol.packet.ingame.server.entity.ServerEntityPositionRotationPacket; -import org.spacehq.mc.protocol.packet.ingame.server.entity.ServerEntityTeleportPacket; -import org.spacehq.mc.protocol.packet.ingame.server.entity.player.ServerPlayerPositionRotationPacket; -import org.spacehq.mc.protocol.packet.ingame.server.entity.spawn.*; -import org.spacehq.packetlib.packet.Packet; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * An EntityPositionTracker knows every Entity's position at any timestamp for a single Replay File. - * To do so, it once reads the whole Replay File and stores all Packets that set or change an Entity's position into Memory.
- *
- * While this significantly increases the Replay loading time, it's the only way to:
- * 1) Properly preview the Camera Path when an Entity is spectated
- * 2) Calculate a smooth Path from an Entity's Shoulder Cam perspective - */ -public class EntityPositionTracker { - - private static final List MOVEMENT_PACKETS = new ArrayList() { - { - add(ServerPlayerPositionRotationPacket.class); - add(ServerSpawnPlayerPacket.class); - add(ServerSpawnObjectPacket.class); - add(ServerSpawnMobPacket.class); - add(ServerSpawnPaintingPacket.class); - add(ServerSpawnExpOrbPacket.class); - add(ServerEntityMovementPacket.class); - add(ServerEntityPositionRotationPacket.class); - add(ServerEntityPositionPacket.class); - add(ServerEntityTeleportPacket.class); - } - }; - - private final File replayFile; - private boolean loaded = false; - - private Map>> entityPositions; - - public EntityPositionTracker(File replayFile) { - this.replayFile = replayFile; - this.entityPositions = new HashMap>>(); - } - - public void load() throws IOException { - ReplayStudio studio = new ReplayStudio(); - studio.setWrappingEnabled(false); - - ZipReplayFile zrf = new ZipReplayFile(studio, replayFile); - ReplayInputStream rin = zrf.getPacketData(); - - PacketData packetData; - while((packetData = rin.readPacket()) != null) { - Packet packet = packetData.getPacket(); - - if(!MOVEMENT_PACKETS.contains(packet.getClass())) continue; - - Integer entityID = PacketUtils.getEntityId(packet); - if(entityID == null) continue; - - AdvancedPosition oldPosition = new AdvancedPosition(0, 0, 0, 0, 0); - - List> positions = entityPositions.get(entityID); - - if(positions == null) positions = new ArrayList>(); - if(!positions.isEmpty()) { - oldPosition = positions.get(positions.size() - 1).getRight(); - } - - AdvancedPosition newPosition = getNewPosition(oldPosition, packet); - - if(newPosition != null) positions.add(Pair.of((int) packetData.getTime(), newPosition)); - entityPositions.put(entityID, positions); - } - - loaded = true; - } - - /** - * @param entityID The Entity's ID - * @param timestamp The timestamp - * @return The specified Entity's position at the given timestamp, or null if the entity doesn't exist at that timestamp - * @throws IllegalStateException if the EntityPositionTracker's load() method wasn't called yet - */ - public AdvancedPosition getEntityPositionAtTimestamp(int entityID, int timestamp) { - if(!loaded) throw new IllegalStateException("The EntityPositionTracker hasn't loaded the Replay File yet"); - - List> positions = entityPositions.get(entityID); - if(positions == null || positions.isEmpty()) return null; - - boolean exists = false; - - for(Pair pair : positions) { - if(pair.getKey() <= timestamp) exists = true; - if(pair.getKey() >= timestamp) { - if(exists) return pair.getValue().copy(); - return null; - } - } - - return null; - } - - //@johni0702 - you may consider adding a similar method to the ReplayStudio PacketUtils - private AdvancedPosition getNewPosition(AdvancedPosition previous, Packet packet) { - if(packet instanceof ServerPlayerPositionRotationPacket) { - ServerPlayerPositionRotationPacket ppl = (ServerPlayerPositionRotationPacket)packet; - - double x = previous.getX() + ppl.getX(); - double y = previous.getY() + ppl.getY(); - double z = previous.getZ() + ppl.getZ(); - double yaw = previous.getYaw() + ppl.getYaw(); - double pitch = previous.getPitch() + ppl.getPitch(); - - for(PositionElement relative : ppl.getRelativeElements()) { - if(relative == PositionElement.X) { - x -= previous.getX(); - } else if(relative == PositionElement.Y) { - y -= previous.getY(); - } else if(relative == PositionElement.Z) { - z -= previous.getZ(); - } else if(relative == PositionElement.YAW) { - yaw -= previous.getYaw(); - } else if(relative == PositionElement.PITCH) { - pitch -= previous.getPitch(); - } - } - - return new AdvancedPosition(x, y, z, pitch, yaw); - } - - if(packet instanceof ServerSpawnPlayerPacket) { - ServerSpawnPlayerPacket spp = (ServerSpawnPlayerPacket)packet; - return new AdvancedPosition(spp.getX(), spp.getY(), spp.getZ(), spp.getPitch(), spp.getYaw()); - } - - if(packet instanceof ServerSpawnObjectPacket) { - ServerSpawnObjectPacket spp = (ServerSpawnObjectPacket)packet; - return new AdvancedPosition(spp.getX(), spp.getY(), spp.getZ(), spp.getPitch(), spp.getYaw()); - } - - if(packet instanceof ServerSpawnExpOrbPacket) { - ServerSpawnExpOrbPacket spp = (ServerSpawnExpOrbPacket)packet; - return new AdvancedPosition(spp.getX(), spp.getY(), spp.getZ(), 0, 0); - } - - if(packet instanceof ServerSpawnMobPacket) { - ServerSpawnMobPacket spp = (ServerSpawnMobPacket)packet; - return new AdvancedPosition(spp.getX(), spp.getY(), spp.getZ(), spp.getPitch(), spp.getYaw()); - } - - if(packet instanceof ServerSpawnPaintingPacket) { - ServerSpawnPaintingPacket spp = (ServerSpawnPaintingPacket)packet; - return new AdvancedPosition(spp.getPosition().getX(), spp.getPosition().getY(), spp.getPosition().getZ(), 0, 0); - } - - if(packet instanceof ServerEntityMovementPacket) { - ServerEntityMovementPacket ppl = (ServerEntityMovementPacket)packet; - //why is the rot/pos variable of ServerEntityMovementPacket not accessible? - boolean rot = ppl instanceof ServerEntityPositionRotationPacket; - - double x = previous.getX() + ppl.getMovementX(); - double y = previous.getY() + ppl.getMovementY(); - double z = previous.getZ() + ppl.getMovementZ(); - - double pitch = previous.getPitch(); - double yaw = previous.getYaw(); - - if(rot) { - pitch = ppl.getPitch(); - yaw = ppl.getYaw(); - } - - return new AdvancedPosition(x, y, z, pitch, yaw); - } - - if(packet instanceof ServerEntityTeleportPacket) { - ServerEntityTeleportPacket spp = (ServerEntityTeleportPacket)packet; - return new AdvancedPosition(spp.getX(), spp.getY(), spp.getZ(), spp.getPitch(), spp.getYaw()); - } - - return null; - } -} \ No newline at end of file diff --git a/src/main/resources/assets/replaymod/lang/en_US.lang b/src/main/resources/assets/replaymod/lang/en_US.lang index 5a5ad57e..e9688cdb 100644 --- a/src/main/resources/assets/replaymod/lang/en_US.lang +++ b/src/main/resources/assets/replaymod/lang/en_US.lang @@ -125,6 +125,8 @@ replaymod.gui.restorereplay1=It seems like Minecraft has not quit normally. replaymod.gui.restorereplay2=The Replay "%1$s" was not saved correctly. replaymod.gui.restorereplay3=Do you wish to recover it? +replaymod.gui.loadentitytracker=Loading entity positions: %%d%%%% + #Only change these if it's neccessary replaymod.gui.replayviewer=Replay Viewer replaymod.gui.replaycenter=Replay Center