Update ReplayStudio and add connect replay functionality to replay editor

This commit is contained in:
johni0702
2015-06-02 19:42:38 +02:00
parent a69fad6663
commit d96ba1f135
4 changed files with 216 additions and 3 deletions

View File

@@ -5,6 +5,7 @@ import eu.crushedpixel.replaymod.gui.elements.GuiArrowButton;
import eu.crushedpixel.replaymod.gui.elements.GuiDropdown;
import eu.crushedpixel.replaymod.gui.elements.GuiEntryList;
import eu.crushedpixel.replaymod.gui.elements.listeners.SelectionListener;
import eu.crushedpixel.replaymod.studio.StudioImplementation;
import eu.crushedpixel.replaymod.utils.ReplayFileIO;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
@@ -42,7 +43,22 @@ public class GuiConnectPart extends GuiStudioPart {
@Override
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

View File

@@ -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);
}
}
}
}
}

View File

@@ -3,6 +3,7 @@ package eu.crushedpixel.replaymod.studio;
import com.google.gson.JsonObject;
import de.johni0702.replaystudio.PacketData;
import de.johni0702.replaystudio.filter.ChangeTimestampFilter;
import de.johni0702.replaystudio.filter.NeutralizerFilter;
import de.johni0702.replaystudio.filter.RemoveFilter;
import de.johni0702.replaystudio.filter.SquashFilter;
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.stream.PacketStream;
import de.johni0702.replaystudio.studio.ReplayStudio;
import org.apache.commons.lang3.Validate;
import java.io.File;
import java.io.IOException;
@@ -54,8 +56,48 @@ public class StudioImplementation {
replayFile.saveTo(outputFile);
}
//TODO Work with Johni to connect multiple Replay Files
public static void connectReplayFiles(List<File> filesToConnect, File outputFile) {
public static void connectReplayFiles(List<File> filesToConnect, File outputFile) throws IOException {
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);
}
}