Fix #5584 by copying the zipped resource to a temporary directory if

paulscode is requesting it. This is so icky.

Signed-off-by: cpw <cpw+github@weeksfamily.ca>
This commit is contained in:
cpw 2019-03-01 22:02:25 -05:00
parent 45f5f0904a
commit 8f5a2be6fd
No known key found for this signature in database
GPG Key ID: 8EB3DF749553B1B7
3 changed files with 91 additions and 23 deletions

View File

@ -16,6 +16,7 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common.util;
import io.netty.buffer.ByteBuf;

View File

@ -0,0 +1,31 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2019.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.fml;
import java.util.Objects;
public final class StackTraceUtils {
private StackTraceUtils() {}
public static boolean threadClassNameEquals(final String className) {
final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
return Objects.equals(stackTrace[stackTrace.length-1].getClassName(), className);
}
}

View File

@ -23,18 +23,26 @@ import net.minecraft.resources.AbstractResourcePack;
import net.minecraft.resources.ResourcePackInfo;
import net.minecraft.resources.ResourcePackType;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.StackTraceUtils;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.*;
import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@ -49,6 +57,25 @@ public class ModFileResourcePack extends AbstractResourcePack
private static final Logger LOGGER = LogManager.getLogger();
private final ModFile modFile;
private ResourcePackInfo packInfo;
private static final ExecutorService STUPIDPAULSCODEISSTUPIDWORKAROUNDTHREAD = Executors.newSingleThreadExecutor();
private static final Path tempDir;
static {
try {
tempDir = Files.createTempDirectory("modpacktmp");
Runtime.getRuntime().addShutdownHook(new Thread(()-> {
try {
Files.walk(tempDir).forEach(f->{try {Files.deleteIfExists(f);}catch (IOException ioe) {}});
Files.delete(tempDir);
} catch (IOException ioe) {
LOGGER.fatal("Failed to clean up tempdir {}", tempDir);
}
}));
} catch (IOException e) {
LOGGER.fatal(CORE, "Failed to create temporary directory", e);
throw new RuntimeException(e);
}
}
public ModFileResourcePack(final ModFile modFile)
{
@ -77,31 +104,40 @@ public class ModFileResourcePack extends AbstractResourcePack
if (path.getFileSystem() == FileSystems.getDefault()) {
LOGGER.trace(CORE, "Request for resource {} returning FileInputStream for regular file {}", name, path);
return new FileInputStream(path.toFile());
} else if (Objects.equals(Thread.currentThread().getStackTrace()[0].getClassName(), "paulscode.sound.CommandThread")) {
final Path tempFile = Files.createTempFile("modpack", "soundresource");
Files.copy(Files.newInputStream(path, StandardOpenOption.READ), tempFile);
LOGGER.trace(CORE, "Request for resource {} returning DeletingTemporaryFileInputStream for packed file {} on paulscode thread", name, path);
return new DeletingTemporaryFileInputStream(tempFile);
// If the resource is in a zip file, and paulscode is the requester, we need to return a file input stream,
// but we can't just use path.tofile to do it. Instead, we copy the resource to a temporary file. As all operations
// with an nio channel are interruptible, we do this at arms length on another thread, while paulscode spams
// interrupts on the paulscode main thread, which we politely ignore.
} else if (StackTraceUtils.threadClassNameEquals("paulscode.sound.CommandThread")) {
final Path tempFile = Files.createTempFile(tempDir, "modpack", "soundresource");
Future<FileInputStream> fis = STUPIDPAULSCODEISSTUPIDWORKAROUNDTHREAD.submit(()->{
try (final SeekableByteChannel resourceChannel = Files.newByteChannel(path, StandardOpenOption.READ);
final FileChannel tempFileChannel = FileChannel.open(tempFile, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
long size = resourceChannel.size();
for (long written = 0; written < size; ) {
written += tempFileChannel.transferFrom(resourceChannel, written, size - written);
}
}
LOGGER.trace(CORE, "Request for resource {} returning DeletingTemporaryFileInputStream for packed file {} on paulscode thread", name, path);
return new FileInputStream(tempFile.toFile());
});
try {
while (true) {
try {
return fis.get();
} catch (InterruptedException ie) {
// no op
}
}
} catch (ExecutionException e) {
LOGGER.fatal(CORE, "Encountered fatal exception copying sound resource", e);
throw new RuntimeException(e);
}
} else {
return Files.newInputStream(path, StandardOpenOption.READ);
}
}
private final class DeletingTemporaryFileInputStream extends FileInputStream {
private final Path tempfile;
DeletingTemporaryFileInputStream(final Path tempfile) throws FileNotFoundException {
super(tempfile.toFile());
this.tempfile = tempfile;
}
@Override
public void close() throws IOException {
super.close();
Files.deleteIfExists(tempfile);
}
}
@Override
protected boolean resourceExists(String name)
{