Update ReplayStudio and add connect replay functionality to replay editor
This commit is contained in:
Binary file not shown.
@@ -5,6 +5,7 @@ import eu.crushedpixel.replaymod.gui.elements.GuiArrowButton;
|
|||||||
import eu.crushedpixel.replaymod.gui.elements.GuiDropdown;
|
import eu.crushedpixel.replaymod.gui.elements.GuiDropdown;
|
||||||
import eu.crushedpixel.replaymod.gui.elements.GuiEntryList;
|
import eu.crushedpixel.replaymod.gui.elements.GuiEntryList;
|
||||||
import eu.crushedpixel.replaymod.gui.elements.listeners.SelectionListener;
|
import eu.crushedpixel.replaymod.gui.elements.listeners.SelectionListener;
|
||||||
|
import eu.crushedpixel.replaymod.studio.StudioImplementation;
|
||||||
import eu.crushedpixel.replaymod.utils.ReplayFileIO;
|
import eu.crushedpixel.replaymod.utils.ReplayFileIO;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.GuiButton;
|
import net.minecraft.client.gui.GuiButton;
|
||||||
@@ -42,7 +43,22 @@ public class GuiConnectPart extends GuiStudioPart {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyFilters(File replayFile, File outputFile) {
|
public void applyFilters(File replayFile, File outputFile) {
|
||||||
//TODO
|
try {
|
||||||
|
List<File> inputFiles = new ArrayList<File>();
|
||||||
|
OUTER:
|
||||||
|
for (String fileName : filesToConcat) {
|
||||||
|
for (File file : replayFiles) {
|
||||||
|
if (fileName.equals(FilenameUtils.getBaseName(file.getAbsolutePath()))) {
|
||||||
|
inputFiles.add(file);
|
||||||
|
continue OUTER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RuntimeException(fileName);
|
||||||
|
}
|
||||||
|
StudioImplementation.connectReplayFiles(inputFiles, outputFile);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
package eu.crushedpixel.replaymod.studio;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import de.johni0702.replaystudio.PacketData;
|
||||||
|
import de.johni0702.replaystudio.Studio;
|
||||||
|
import de.johni0702.replaystudio.filter.StreamFilter;
|
||||||
|
import de.johni0702.replaystudio.replay.ReplayFile;
|
||||||
|
import de.johni0702.replaystudio.replay.ReplayMetaData;
|
||||||
|
import de.johni0702.replaystudio.stream.PacketStream;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.spacehq.mc.protocol.packet.ingame.server.ServerResourcePackSendPacket;
|
||||||
|
import org.spacehq.mc.protocol.packet.ingame.server.entity.spawn.ServerSpawnPlayerPacket;
|
||||||
|
import org.spacehq.packetlib.packet.Packet;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
// TODO Reset resourcepack when changing from a resourcepack replay to a normal replay
|
||||||
|
public class ConnectMetadataFilter implements StreamFilter {
|
||||||
|
private ReplayFile replayFile;
|
||||||
|
private Set<String> players = new HashSet<String>();
|
||||||
|
private Set<UUID> invisiblePlayers = new HashSet<UUID>();
|
||||||
|
private long duration;
|
||||||
|
private Map<String, File> resourcePacks = new HashMap<String, File>();
|
||||||
|
private Map<Integer, String> resourcePackIndex = new HashMap<Integer, String>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "connect_metadata";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInputReplay(ReplayFile replayFile) {
|
||||||
|
this.replayFile = replayFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Studio studio, JsonObject jsonObject) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart(PacketStream packetStream) {
|
||||||
|
try {
|
||||||
|
Optional<Set<UUID>> invisible = replayFile.getInvisiblePlayers();
|
||||||
|
if (invisible.isPresent()) {
|
||||||
|
invisiblePlayers.addAll(invisible.get());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPacket(PacketStream packetStream, PacketData packetData) {
|
||||||
|
Packet packet = packetData.getPacket();
|
||||||
|
if (packet instanceof ServerSpawnPlayerPacket) {
|
||||||
|
players.add(((ServerSpawnPlayerPacket) packet).getUUID().toString());
|
||||||
|
}
|
||||||
|
if (packet instanceof ServerResourcePackSendPacket) {
|
||||||
|
String url = ((ServerResourcePackSendPacket) packet).getUrl();
|
||||||
|
if (url.startsWith("replay://")) {
|
||||||
|
int id = Integer.parseInt(url.substring("replay://".length()));
|
||||||
|
try {
|
||||||
|
Map<Integer, String> oldIndex = replayFile.getResourcePackIndex();
|
||||||
|
if (oldIndex != null) {
|
||||||
|
String hash = oldIndex.get(id);
|
||||||
|
if (hash != null) {
|
||||||
|
// Make sure we have the resource pack
|
||||||
|
File file = resourcePacks.get(hash);
|
||||||
|
if (file == null) {
|
||||||
|
Optional<InputStream> in = replayFile.getResourcePack(hash);
|
||||||
|
if (in.isPresent()) {
|
||||||
|
try {
|
||||||
|
file = Files.createTempFile("replaymod", "resourcepack").toFile();
|
||||||
|
OutputStream out = new FileOutputStream(file);
|
||||||
|
try {
|
||||||
|
IOUtils.copy(in.get(), out);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(out);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(in.get());
|
||||||
|
}
|
||||||
|
resourcePacks.put(hash, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-Index the current packet
|
||||||
|
id = resourcePackIndex.size();
|
||||||
|
resourcePackIndex.put(id, hash);
|
||||||
|
url = "replay://" + id;
|
||||||
|
packetStream.insert(packetData.getTime(), new ServerResourcePackSendPacket(url, ""));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnd(PacketStream packetStream, long l) {
|
||||||
|
if (l > duration) {
|
||||||
|
duration = l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTo(ReplayFile output) throws IOException {
|
||||||
|
String mcVersion = Minecraft.getMinecraft().getVersion();
|
||||||
|
String[] split = mcVersion.split("-");
|
||||||
|
if(split.length > 0) {
|
||||||
|
mcVersion = split[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
ReplayMetaData metaData = new ReplayMetaData();
|
||||||
|
metaData.setDuration((int) duration);
|
||||||
|
metaData.setSingleplayer(false);
|
||||||
|
metaData.setServerName("Multiple worlds");
|
||||||
|
metaData.setMcVersion(mcVersion);
|
||||||
|
metaData.setDate(System.currentTimeMillis());
|
||||||
|
metaData.setPlayers(players.toArray(new String[players.size()]));
|
||||||
|
output.writeMetaData(metaData);
|
||||||
|
|
||||||
|
if (!invisiblePlayers.isEmpty()) {
|
||||||
|
output.writeInvisiblePlayers(invisiblePlayers);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resourcePackIndex.isEmpty()) {
|
||||||
|
output.writeResourcePackIndex(resourcePackIndex);
|
||||||
|
|
||||||
|
// Only store resource packs we really need
|
||||||
|
resourcePacks.keySet().retainAll(resourcePackIndex.values());
|
||||||
|
|
||||||
|
for (Map.Entry<String, File> e : resourcePacks.entrySet()) {
|
||||||
|
OutputStream out = output.writeResourcePack(e.getKey());
|
||||||
|
try {
|
||||||
|
InputStream in = new FileInputStream(e.getValue());
|
||||||
|
try {
|
||||||
|
IOUtils.copy(in, out);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(in);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package eu.crushedpixel.replaymod.studio;
|
|||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import de.johni0702.replaystudio.PacketData;
|
import de.johni0702.replaystudio.PacketData;
|
||||||
import de.johni0702.replaystudio.filter.ChangeTimestampFilter;
|
import de.johni0702.replaystudio.filter.ChangeTimestampFilter;
|
||||||
|
import de.johni0702.replaystudio.filter.NeutralizerFilter;
|
||||||
import de.johni0702.replaystudio.filter.RemoveFilter;
|
import de.johni0702.replaystudio.filter.RemoveFilter;
|
||||||
import de.johni0702.replaystudio.filter.SquashFilter;
|
import de.johni0702.replaystudio.filter.SquashFilter;
|
||||||
import de.johni0702.replaystudio.io.ReplayOutputStream;
|
import de.johni0702.replaystudio.io.ReplayOutputStream;
|
||||||
@@ -11,6 +12,7 @@ import de.johni0702.replaystudio.replay.ReplayMetaData;
|
|||||||
import de.johni0702.replaystudio.replay.ZipReplayFile;
|
import de.johni0702.replaystudio.replay.ZipReplayFile;
|
||||||
import de.johni0702.replaystudio.stream.PacketStream;
|
import de.johni0702.replaystudio.stream.PacketStream;
|
||||||
import de.johni0702.replaystudio.studio.ReplayStudio;
|
import de.johni0702.replaystudio.studio.ReplayStudio;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -54,8 +56,48 @@ public class StudioImplementation {
|
|||||||
replayFile.saveTo(outputFile);
|
replayFile.saveTo(outputFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO Work with Johni to connect multiple Replay Files
|
public static void connectReplayFiles(List<File> filesToConnect, File outputFile) throws IOException {
|
||||||
public static void connectReplayFiles(List<File> filesToConnect, File outputFile) {
|
Validate.notEmpty(filesToConnect);
|
||||||
|
ReplayStudio studio = new ReplayStudio();
|
||||||
|
studio.setWrappingEnabled(false);
|
||||||
|
|
||||||
|
ReplayFile outputReplayFile = new ZipReplayFile(studio, outputFile);
|
||||||
|
ReplayOutputStream out = outputReplayFile.writePacketData();
|
||||||
|
|
||||||
|
ConnectMetadataFilter metaDataFilter = new ConnectMetadataFilter();
|
||||||
|
int startTime = 0;
|
||||||
|
for (File file : filesToConnect) {
|
||||||
|
ReplayFile replayFile = new ZipReplayFile(studio, file);
|
||||||
|
PacketStream stream = studio.createReplayStream(replayFile.getPacketData(), true);
|
||||||
|
ReplayMetaData metaData = replayFile.getMetaData();
|
||||||
|
|
||||||
|
stream.addFilter(new NeutralizerFilter());
|
||||||
|
|
||||||
|
if (startTime > 0) {
|
||||||
|
ChangeTimestampFilter ctf = new ChangeTimestampFilter();
|
||||||
|
JsonObject cfg = new JsonObject();
|
||||||
|
cfg.addProperty("offset", startTime);
|
||||||
|
ctf.init(studio, cfg);
|
||||||
|
stream.addFilter(ctf);
|
||||||
|
}
|
||||||
|
|
||||||
|
metaDataFilter.setInputReplay(replayFile);
|
||||||
|
stream.addFilter(metaDataFilter);
|
||||||
|
|
||||||
|
PacketData packetData;
|
||||||
|
while ((packetData = stream.next()) != null) {
|
||||||
|
out.write(packetData);
|
||||||
|
}
|
||||||
|
for (PacketData data : stream.end()) {
|
||||||
|
out.write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
startTime += metaData.getDuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
metaDataFilter.writeTo(outputReplayFile);
|
||||||
|
outputReplayFile.saveTo(outputFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user