Update ReplayStudio and add connect replay functionality to replay editor
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user