World and datapacks management

This commit is contained in:
huanghongxun
2018-09-16 12:38:15 +08:00
parent 1753b4d27e
commit 07febc18d4
23 changed files with 670 additions and 90 deletions

View File

@@ -55,7 +55,10 @@ public class DefaultCacheRepository extends CacheRepository {
lock.writeLock().lock();
try {
index = Constants.GSON.fromJson(FileUtils.readText(indexFile.toFile()), Index.class);
if (Files.isRegularFile(indexFile))
index = Constants.GSON.fromJson(FileUtils.readText(indexFile.toFile()), Index.class);
else
index = new Index();
} catch (IOException e) {
Logging.LOG.log(Level.WARNING, "Unable to read index file", e);
index = new Index();

View File

@@ -1,4 +1,162 @@
package org.jackhuang.hmcl.game;
import com.github.steveice10.opennbt.NBTIO;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.LongTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import org.jackhuang.hmcl.util.CompressingUtils;
import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.Unzipper;
import org.jackhuang.hmcl.util.Zipper;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
public class World {
private final Path file;
private String fileName;
private String worldName;
private String gameVersion;
private long lastPlayed, seed;
public World(Path file) throws IOException {
this.file = file;
if (Files.isDirectory(file))
loadFromDirectory();
else if (Files.isRegularFile(file))
loadFromZip();
else
throw new IOException("Path " + file + " cannot be recognized as a Minecraft world");
}
private void loadFromDirectory() throws IOException {
fileName = FileUtils.getName(file);
Path levelDat = file.resolve("level.dat");
getWorldName(levelDat);
}
public Path getFile() {
return file;
}
public String getFileName() {
return fileName;
}
public String getWorldName() {
return worldName;
}
public long getLastPlayed() {
return lastPlayed;
}
public long getSeed() {
return seed;
}
public String getGameVersion() {
return gameVersion;
}
private void loadFromZip() throws IOException {
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(file)) {
Path root = Files.list(fs.getPath("/")).filter(Files::isDirectory).findAny()
.orElseThrow(() -> new IOException("Not a valid world zip file"));
Path levelDat = root.resolve("level.dat");
if (!Files.exists(levelDat))
throw new IllegalArgumentException("Not a valid world zip file since level.dat cannot be found.");
fileName = FileUtils.getName(root);
getWorldName(levelDat);
}
}
private void getWorldName(Path levelDat) throws IOException {
CompoundTag nbt = parseLevelDat(levelDat);
CompoundTag data = nbt.get("Data");
String name = data.<StringTag>get("LevelName").getValue();
lastPlayed = data.<LongTag>get("LastPlayed").getValue();
seed = data.<LongTag>get("RandomSeed").getValue();
CompoundTag version = data.get("Version");
gameVersion = version.<StringTag>get("Name").getValue();
worldName = name;
}
public void rename(String newName) throws IOException {
if (!Files.isDirectory(file))
throw new IOException("Not a valid world directory");
// Change the name recorded in level.dat
Path levelDat = file.resolve("level.dat");
CompoundTag nbt = parseLevelDat(levelDat);
CompoundTag data = nbt.get("Data");
data.put(new StringTag("LevelName", newName));
NBTIO.writeTag(new GZIPOutputStream(Files.newOutputStream(levelDat)), nbt);
// then change the folder's name
Files.move(file, file.resolveSibling(newName));
}
public void install(Path savesDir, String name) throws IOException {
Path worldDir = savesDir.resolve(name);
if (Files.isDirectory(worldDir)) {
throw new FileAlreadyExistsException("World already exists");
}
if (Files.isRegularFile(file)) {
String subDirectoryName;
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(file)) {
List<Path> subDirs = Files.list(fs.getPath("/")).collect(Collectors.toList());
if (subDirs.size() != 1) {
throw new IOException("World zip malformed");
}
subDirectoryName = FileUtils.getName(subDirs.get(0));
}
new Unzipper(file, savesDir)
.setSubDirectory("/" + subDirectoryName + "/")
.unzip();
} else if (Files.isDirectory(file)) {
FileUtils.copyDirectory(file, worldDir);
}
}
public void export(Path zip, String worldName) throws IOException {
if (!Files.isDirectory(file))
throw new IOException();
try (Zipper zipper = new Zipper(zip)) {
zipper.putDirectory(file, "/" + worldName + "/");
}
}
private static CompoundTag parseLevelDat(Path path) throws IOException {
Tag nbt = NBTIO.readTag(new GZIPInputStream(Files.newInputStream(path)));
if (nbt instanceof CompoundTag)
return (CompoundTag) nbt;
else
throw new IOException("level.dat malformed");
}
public static List<World> getWorlds(Path worldDir) throws IOException {
List<World> worlds = new ArrayList<>();
for (Path world : Files.newDirectoryStream(worldDir)) {
worlds.add(new World(world));
}
return worlds;
}
}

View File

@@ -2,8 +2,11 @@ package org.jackhuang.hmcl.mod;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.jackhuang.hmcl.util.*;
import java.io.IOException;
@@ -11,25 +14,21 @@ import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.logging.Level;
public class Datapack {
private final Path path;
private final List<Pack> info;
private final ObservableList<Pack> info = FXCollections.observableArrayList();
private Datapack(Path path, List<Pack> info) {
public Datapack(Path path) {
this.path = path;
this.info = Collections.unmodifiableList(info);
for (Pack pack : info) {
pack.datapack = this;
}
}
public Path getPath() {
return path;
}
public List<Pack> getInfo() {
public ObservableList<Pack> getInfo() {
return info;
}
@@ -39,40 +38,53 @@ public class Datapack {
Set<String> packs = new HashSet<>();
for (Pack pack : info) packs.add(pack.getId());
for (Path datapack : Files.newDirectoryStream(datapacks)) {
if (packs.contains(FileUtils.getName(datapack)))
FileUtils.deleteDirectory(datapack.toFile());
}
if (Files.isDirectory(datapacks))
for (Path datapack : Files.newDirectoryStream(datapacks)) {
if (packs.contains(FileUtils.getName(datapack)))
FileUtils.deleteDirectory(datapack.toFile());
}
new Unzipper(path, worldPath).setReplaceExistentFile(true).unzip();
}
public static Datapack fromZip(Path path) throws IOException {
public void deletePack(String pack) throws IOException {
FileUtils.deleteDirectory(path.resolve(pack).toFile());
Platform.runLater(() -> info.removeIf(p -> p.getId().equals(pack)));
}
public void loadFromZip() throws IOException {
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(path)) {
Datapack datapack = fromDir(fs.getPath("/datapacks/"));
return new Datapack(path, datapack.info);
loadFromDir(fs.getPath("/datapacks/"));
}
}
/**
*
* @param dir
* @return
* @throws IOException
*/
public static Datapack fromDir(Path dir) throws IOException {
List<Pack> info = new LinkedList<>();
for (Path subDir : Files.newDirectoryStream(dir)) {
Path mcmeta = subDir.resolve("pack.mcmeta");
if (!Files.exists(mcmeta))
continue;
PackMcMeta pack = JsonUtils.fromNonNullJson(FileUtils.readText(mcmeta), PackMcMeta.class);
info.add(new Pack(mcmeta, FileUtils.getName(subDir), pack.getPackInfo().getDescription()));
public void loadFromDir() {
try {
loadFromDir(path);
} catch (IOException e) {
Logging.LOG.log(Level.WARNING, "Failed to read datapacks " + path, e);
}
return new Datapack(dir, info);
}
private void loadFromDir(Path dir) throws IOException {
List<Pack> info = new ArrayList<>();
if (Files.isDirectory(dir))
for (Path subDir : Files.newDirectoryStream(dir)) {
Path mcmeta = subDir.resolve("pack.mcmeta");
if (!Files.exists(mcmeta))
continue;
try {
PackMcMeta pack = JsonUtils.fromNonNullJson(FileUtils.readText(mcmeta), PackMcMeta.class);
info.add(new Pack(mcmeta, FileUtils.getName(subDir), pack.getPackInfo().getDescription(), this));
} catch (IOException e) {
Logging.LOG.log(Level.WARNING, "Failed to read datapack " + subDir, e);
}
}
this.info.setAll(info);
}
public static class Pack {
@@ -80,12 +92,13 @@ public class Datapack {
private final BooleanProperty active;
private final String id;
private final String description;
private Datapack datapack;
private final Datapack datapack;
public Pack(Path packMcMeta, String id, String description) {
public Pack(Path packMcMeta, String id, String description, Datapack datapack) {
this.packMcMeta = packMcMeta;
this.id = id;
this.description = description;
this.datapack = datapack;
active = new SimpleBooleanProperty(this, "active", !DISABLED_EXT.equals(FileUtils.getExtension(packMcMeta))) {
@Override