introduced new facade class SaveGame

master
Stefan Dollase 2017-06-01 13:37:56 +02:00
parent 4c1a9b1048
commit cf3f7c7bf1
15 changed files with 349 additions and 280 deletions

View File

@ -12,7 +12,6 @@ import amidst.mojangapi.file.MojangApiParsingException;
import amidst.mojangapi.file.facade.LauncherProfile;
import amidst.mojangapi.file.facade.MinecraftInstallation;
import amidst.mojangapi.file.json.versionlist.VersionListJson;
import amidst.mojangapi.file.service.SaveDirectoryService;
import amidst.mojangapi.file.service.VersionListService;
import amidst.mojangapi.minecraftinterface.MinecraftInterface;
import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException;
@ -128,7 +127,7 @@ public class MojangApi {
MojangApiParsingException {
MinecraftInterface minecraftInterface = this.minecraftInterface;
if (minecraftInterface != null) {
return worldBuilder.fromSaveGame(minecraftInterface, new SaveDirectoryService().newSaveDirectory(file));
return worldBuilder.fromSaveGame(minecraftInterface, minecraftInstallation.newSaveGame(file));
} else {
throw new IllegalStateException("cannot create a world without a minecraft interface");
}

View File

@ -10,9 +10,11 @@ import amidst.logging.AmidstLogger;
import amidst.mojangapi.file.DotMinecraftDirectoryNotFoundException;
import amidst.mojangapi.file.MojangApiParsingException;
import amidst.mojangapi.file.directory.DotMinecraftDirectory;
import amidst.mojangapi.file.directory.SaveDirectory;
import amidst.mojangapi.file.directory.VersionDirectory;
import amidst.mojangapi.file.json.version.VersionJson;
import amidst.mojangapi.file.service.DotMinecraftDirectoryService;
import amidst.mojangapi.file.service.SaveDirectoryService;
import amidst.parsing.FormatException;
import amidst.parsing.json.JsonReader;
@ -83,4 +85,10 @@ public class MinecraftInstallation {
throw new MojangApiParsingException(e);
}
}
public SaveGame newSaveGame(File location) throws IOException, MojangApiParsingException {
SaveDirectoryService saveDirectoryService = new SaveDirectoryService();
SaveDirectory saveDirectory = saveDirectoryService.newSaveDirectory(location);
return new SaveGame(saveDirectory, saveDirectoryService.createLevelDat(saveDirectory));
}
}

View File

@ -0,0 +1,71 @@
package amidst.mojangapi.file.facade;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import amidst.documentation.Immutable;
import amidst.mojangapi.file.directory.SaveDirectory;
import amidst.mojangapi.file.nbt.LevelDatNbt;
import amidst.mojangapi.file.service.SaveDirectoryService;
import amidst.mojangapi.world.WorldType;
import amidst.mojangapi.world.coordinates.CoordinatesInWorld;
@Immutable
public class SaveGame {
private final SaveDirectoryService saveDirectoryService = new SaveDirectoryService();
private final SaveDirectory saveDirectory;
private final LevelDatNbt levelDatNbt;
public SaveGame(SaveDirectory saveDirectory, LevelDatNbt levelDatNbt) {
this.saveDirectory = saveDirectory;
this.levelDatNbt = levelDatNbt;
}
public long getSeed() {
return levelDatNbt.getSeed();
}
public CoordinatesInWorld getWorldSpawn() {
return levelDatNbt.getWorldSpawn();
}
public WorldType getWorldType() {
return levelDatNbt.getWorldType();
}
public String getGeneratorOptions() {
return levelDatNbt.getGeneratorOptions();
}
public boolean hasSingleplayerPlayer() {
return levelDatNbt.hasPlayer();
}
public boolean hasMultiplayerPlayers() {
return saveDirectory.hasMultiplayerPlayers();
}
public Optional<SaveGamePlayer> tryReadSingleplayerPlayer() {
return saveDirectoryService
.tryReadSingleplayerPlayerNbt(saveDirectory)
.map(p -> new SaveGamePlayer(saveDirectory, p));
}
public List<SaveGamePlayer> tryReadMultiplayerPlayers() {
return saveDirectoryService
.tryReadMultiplayerPlayerNbts(saveDirectory)
.stream()
.map(p -> new SaveGamePlayer(saveDirectory, p))
.collect(Collectors.toList());
}
public List<SaveGamePlayer> tryReadAllPlayers() {
List<SaveGamePlayer> result = new LinkedList<>();
result.addAll(tryReadSingleplayerPlayer().map(Collections::singletonList).orElseGet(Collections::emptyList));
result.addAll(tryReadMultiplayerPlayers());
return result;
}
}

View File

@ -0,0 +1,35 @@
package amidst.mojangapi.file.facade;
import java.util.function.Function;
import java.util.function.Supplier;
import amidst.documentation.Immutable;
import amidst.mojangapi.file.directory.SaveDirectory;
import amidst.mojangapi.file.nbt.player.PlayerNbt;
import amidst.mojangapi.file.service.SaveDirectoryService;
import amidst.mojangapi.world.player.PlayerCoordinates;
@Immutable
public class SaveGamePlayer {
private final SaveDirectoryService saveDirectoryService = new SaveDirectoryService();
private final SaveDirectory saveDirectory;
private final PlayerNbt playerNbt;
public SaveGamePlayer(SaveDirectory saveDirectory, PlayerNbt playerNbt) {
this.saveDirectory = saveDirectory;
this.playerNbt = playerNbt;
}
public PlayerCoordinates getPlayerCoordinates() {
return playerNbt.getPlayerCoordinates();
}
public boolean tryBackupAndWritePlayerCoordinates(PlayerCoordinates coordinates) {
return saveDirectoryService.tryBackup(saveDirectory, playerNbt)
&& saveDirectoryService.tryWriteCoordinates(saveDirectory, playerNbt, coordinates);
}
public <R> R map(Supplier<R> ifIsLevelDat, Function<String, R> ifIsPlayerdata, Function<String, R> ifIsPlayers) {
return playerNbt.map(ifIsLevelDat, ifIsPlayerdata, ifIsPlayers);
}
}

View File

@ -1,41 +0,0 @@
package amidst.mojangapi.file.nbt.player;
import java.io.IOException;
import java.util.function.Function;
import java.util.function.Supplier;
import amidst.documentation.Immutable;
import amidst.mojangapi.file.MojangApiParsingException;
import amidst.mojangapi.file.directory.SaveDirectory;
import amidst.mojangapi.file.nbt.NBTUtils;
import amidst.mojangapi.file.service.AmidstBackupService;
import amidst.mojangapi.world.player.PlayerCoordinates;
@Immutable
public class LevelDatPlayerNbt extends PlayerNbt {
private final SaveDirectory saveDirectory;
public LevelDatPlayerNbt(SaveDirectory saveDirectory) {
this.saveDirectory = saveDirectory;
}
@Override
protected boolean tryBackup() {
return new AmidstBackupService().tryBackupLevelDat(saveDirectory);
}
@Override
protected void doWriteCoordinates(PlayerCoordinates coordinates) throws MojangApiParsingException {
PlayerLocationSaver.writeToLevelDat(coordinates, saveDirectory.getLevelDat());
}
@Override
public PlayerCoordinates readCoordinates() throws IOException, MojangApiParsingException {
return PlayerLocationLoader.readFromLevelDat(NBTUtils.readTagFromFile(saveDirectory.getLevelDat()));
}
@Override
public <R> R map(Supplier<R> ifIsLevelDat, Function<String, R> ifIsPlayerdata, Function<String, R> ifIsPlayers) {
return ifIsLevelDat.get();
}
}

View File

@ -1,6 +1,9 @@
package amidst.mojangapi.file.nbt.player;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import org.jnbt.CompoundTag;
import org.jnbt.IntTag;
@ -8,27 +11,28 @@ import org.jnbt.ListTag;
import org.jnbt.Tag;
import amidst.documentation.Immutable;
import amidst.mojangapi.file.MojangApiParsingException;
import amidst.mojangapi.file.nbt.NBTTagKeys;
import amidst.mojangapi.file.nbt.NBTUtils;
import amidst.mojangapi.world.player.PlayerCoordinates;
@Immutable
public enum PlayerLocationLoader {
;
public static PlayerCoordinates readFromPlayerFile(CompoundTag file) throws MojangApiParsingException {
public static Optional<PlayerCoordinates> tryReadFromPlayerFile(File file) throws IOException {
try {
return readPlayerCoordinates(file);
return Optional.of(readPlayerCoordinates(NBTUtils.readTagFromFile(file)));
} catch (NullPointerException e) {
throw new MojangApiParsingException("cannot read player coordinates", e);
return Optional.empty();
}
}
public static PlayerCoordinates readFromLevelDat(CompoundTag file) throws MojangApiParsingException {
public static Optional<PlayerCoordinates> tryReadFromLevelDat(File file) throws IOException {
try {
return readPlayerCoordinates(getSinglePlayerPlayerTag(getTagRootTag(file)));
return Optional
.of(readPlayerCoordinates(getSinglePlayerPlayerTag(getTagRootTag(NBTUtils.readTagFromFile(file)))));
} catch (NullPointerException e) {
throw new MojangApiParsingException("cannot read player coordinates", e);
return Optional.empty();
}
}

View File

@ -13,7 +13,6 @@ import org.jnbt.ListTag;
import org.jnbt.Tag;
import amidst.documentation.Immutable;
import amidst.mojangapi.file.MojangApiParsingException;
import amidst.mojangapi.file.nbt.NBTTagKeys;
import amidst.mojangapi.file.nbt.NBTUtils;
import amidst.mojangapi.world.player.PlayerCoordinates;
@ -22,23 +21,25 @@ import amidst.mojangapi.world.player.PlayerCoordinates;
public enum PlayerLocationSaver {
;
public static void writeToPlayerFile(PlayerCoordinates coordinates, File file) throws MojangApiParsingException {
public static boolean tryWriteToPlayerFile(PlayerCoordinates coordinates, File file) throws IOException {
try {
CompoundTag dataTag = NBTUtils.readTagFromFile(file);
CompoundTag modifiedDataTag = modifyPositionInDataTagMultiPlayer(dataTag, coordinates);
NBTUtils.writeTagToFile(file, modifiedDataTag);
} catch (IOException | NullPointerException e) {
throw new MojangApiParsingException("cannot write player coordinates", e);
return true;
} catch (NullPointerException e) {
return false;
}
}
public static void writeToLevelDat(PlayerCoordinates coordinates, File file) throws MojangApiParsingException {
public static boolean tryWriteToLevelDat(PlayerCoordinates coordinates, File file) throws IOException {
try {
CompoundTag baseTag = NBTUtils.readTagFromFile(file);
CompoundTag modifiedBaseTag = modifyPositionInBaseTagSinglePlayer(baseTag, coordinates);
NBTUtils.writeTagToFile(file, modifiedBaseTag);
} catch (IOException | NullPointerException e) {
throw new MojangApiParsingException("cannot write player coordinates", e);
return true;
} catch (NullPointerException e) {
return false;
}
}

View File

@ -1,29 +1,22 @@
package amidst.mojangapi.file.nbt.player;
import java.io.IOException;
import java.util.function.Function;
import java.util.function.Supplier;
import amidst.documentation.Immutable;
import amidst.mojangapi.file.MojangApiParsingException;
import amidst.mojangapi.world.player.PlayerCoordinates;
@Immutable
public abstract class PlayerNbt {
public boolean tryWriteCoordinates(PlayerCoordinates coordinates) throws MojangApiParsingException {
if (tryBackup()) {
doWriteCoordinates(coordinates);
return true;
} else {
return false;
}
private final PlayerCoordinates playerCoordinates;
public PlayerNbt(PlayerCoordinates playerCoordinates) {
this.playerCoordinates = playerCoordinates;
}
protected abstract boolean tryBackup();
protected abstract void doWriteCoordinates(PlayerCoordinates coordinates) throws MojangApiParsingException;
public abstract PlayerCoordinates readCoordinates() throws IOException, MojangApiParsingException;
public PlayerCoordinates getPlayerCoordinates() {
return playerCoordinates;
}
public abstract <R> R map(
Supplier<R> ifIsLevelDat,

View File

@ -1,44 +0,0 @@
package amidst.mojangapi.file.nbt.player;
import java.io.IOException;
import java.util.function.Function;
import java.util.function.Supplier;
import amidst.documentation.Immutable;
import amidst.mojangapi.file.MojangApiParsingException;
import amidst.mojangapi.file.directory.SaveDirectory;
import amidst.mojangapi.file.nbt.NBTUtils;
import amidst.mojangapi.file.service.AmidstBackupService;
import amidst.mojangapi.world.player.PlayerCoordinates;
@Immutable
public class PlayerdataPlayerNbt extends PlayerNbt {
private final SaveDirectory saveDirectory;
private final String playerUUID;
public PlayerdataPlayerNbt(SaveDirectory saveDirectory, String playerUUID) {
this.saveDirectory = saveDirectory;
this.playerUUID = playerUUID;
}
@Override
protected boolean tryBackup() {
return new AmidstBackupService().tryBackupPlayerdataFile(saveDirectory, playerUUID);
}
@Override
protected void doWriteCoordinates(PlayerCoordinates coordinates) throws MojangApiParsingException {
PlayerLocationSaver.writeToPlayerFile(coordinates, saveDirectory.getPlayerdataFile(playerUUID));
}
@Override
public PlayerCoordinates readCoordinates() throws IOException, MojangApiParsingException {
return PlayerLocationLoader
.readFromPlayerFile(NBTUtils.readTagFromFile(saveDirectory.getPlayerdataFile(playerUUID)));
}
@Override
public <R> R map(Supplier<R> ifIsLevelDat, Function<String, R> ifIsPlayerdata, Function<String, R> ifIsPlayers) {
return ifIsPlayerdata.apply(playerUUID);
}
}

View File

@ -1,44 +0,0 @@
package amidst.mojangapi.file.nbt.player;
import java.io.IOException;
import java.util.function.Function;
import java.util.function.Supplier;
import amidst.documentation.Immutable;
import amidst.mojangapi.file.MojangApiParsingException;
import amidst.mojangapi.file.directory.SaveDirectory;
import amidst.mojangapi.file.nbt.NBTUtils;
import amidst.mojangapi.file.service.AmidstBackupService;
import amidst.mojangapi.world.player.PlayerCoordinates;
@Immutable
public class PlayersPlayerNbt extends PlayerNbt {
private final SaveDirectory saveDirectory;
private final String playerName;
public PlayersPlayerNbt(SaveDirectory saveDirectory, String playerName) {
this.saveDirectory = saveDirectory;
this.playerName = playerName;
}
@Override
protected boolean tryBackup() {
return new AmidstBackupService().tryBackupPlayersFile(saveDirectory, playerName);
}
@Override
protected void doWriteCoordinates(PlayerCoordinates coordinates) throws MojangApiParsingException {
PlayerLocationSaver.writeToPlayerFile(coordinates, saveDirectory.getPlayersFile(playerName));
}
@Override
public PlayerCoordinates readCoordinates() throws IOException, MojangApiParsingException {
return PlayerLocationLoader
.readFromPlayerFile(NBTUtils.readTagFromFile(saveDirectory.getPlayersFile(playerName)));
}
@Override
public <R> R map(Supplier<R> ifIsLevelDat, Function<String, R> ifIsPlayerdata, Function<String, R> ifIsPlayers) {
return ifIsPlayers.apply(playerName);
}
}

View File

@ -3,9 +3,13 @@ package amidst.mojangapi.file.service;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import amidst.documentation.Immutable;
import amidst.documentation.NotNull;
@ -14,13 +18,15 @@ import amidst.mojangapi.file.MojangApiParsingException;
import amidst.mojangapi.file.directory.SaveDirectory;
import amidst.mojangapi.file.nbt.LevelDatNbt;
import amidst.mojangapi.file.nbt.NBTUtils;
import amidst.mojangapi.file.nbt.player.LevelDatPlayerNbt;
import amidst.mojangapi.file.nbt.player.PlayerLocationLoader;
import amidst.mojangapi.file.nbt.player.PlayerLocationSaver;
import amidst.mojangapi.file.nbt.player.PlayerNbt;
import amidst.mojangapi.file.nbt.player.PlayerdataPlayerNbt;
import amidst.mojangapi.file.nbt.player.PlayersPlayerNbt;
import amidst.mojangapi.world.player.PlayerCoordinates;
@Immutable
public class SaveDirectoryService {
private final AmidstBackupService amidstBackupService = new AmidstBackupService();
/**
* Returns a new valid instance of the class SaveDirectory. It tries to use
* the given file. If that is not valid it tires to use its parent file. If
@ -78,9 +84,9 @@ public class SaveDirectoryService {
* the map is used as singleplayer map.
*/
@NotNull
public List<PlayerNbt> createSingleplayerPlayerNbts(SaveDirectory saveDirectory) {
public Optional<PlayerNbt> tryReadSingleplayerPlayerNbt(SaveDirectory saveDirectory) {
AmidstLogger.info("using player from level.dat");
return Arrays.asList(new LevelDatPlayerNbt(saveDirectory));
return tryReadCoordinatesFromLevelDat(saveDirectory).map(this::createLevelDatPlayerNbt);
}
/**
@ -88,53 +94,175 @@ public class SaveDirectoryService {
* and uses the player uuid as filename.
*/
@NotNull
public List<PlayerNbt> createMultiplayerPlayerNbts(SaveDirectory saveDirectory) {
List<PlayerNbt> result = new ArrayList<>();
for (File playerdataFile : getPlayerdataFiles(saveDirectory)) {
if (playerdataFile.isFile()) {
result.add(new PlayerdataPlayerNbt(saveDirectory, getPlayerUUIDFromPlayerdataFile(playerdataFile)));
}
}
if (!result.isEmpty()) {
public List<PlayerNbt> tryReadMultiplayerPlayerNbts(SaveDirectory saveDirectory) {
List<PlayerNbt> playerdataPlayers = listFiles(saveDirectory.getPlayerdata())
.stream()
.filter(File::isFile)
.map(f -> createPlayerdataPlayerNbt(saveDirectory, f))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
if (!playerdataPlayers.isEmpty()) {
AmidstLogger.info("using players from the playerdata directory");
return result;
}
for (File playersFile : getPlayersFiles(saveDirectory)) {
if (playersFile.isFile()) {
result.add(new PlayersPlayerNbt(saveDirectory, getPlayerNameFromPlayersFile(playersFile)));
return playerdataPlayers;
} else {
List<PlayerNbt> playersPlayers = listFiles(saveDirectory.getPlayers())
.stream()
.filter(File::isFile)
.map(f -> createPlayersPlayerNbt(saveDirectory, f))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
if (!playersPlayers.isEmpty()) {
AmidstLogger.info("using players from the players directory");
return playersPlayers;
} else {
AmidstLogger.info("no multiplayer players found");
return Collections.emptyList();
}
}
if (!result.isEmpty()) {
AmidstLogger.info("using players from the players directory");
return result;
}
AmidstLogger.info("no multiplayer players found");
return result;
}
private File[] getPlayerdataFiles(SaveDirectory saveDirectory) {
File[] files = saveDirectory.getPlayerdata().listFiles();
if (files == null) {
return new File[0];
private List<File> listFiles(File file) {
File[] files = file.listFiles();
if (files != null) {
return Arrays.asList(files);
} else {
return files;
return Collections.emptyList();
}
}
private File[] getPlayersFiles(SaveDirectory saveDirectory) {
File[] files = saveDirectory.getPlayers().listFiles();
if (files == null) {
return new File[0];
} else {
return files;
}
private Optional<PlayerNbt> createPlayerdataPlayerNbt(SaveDirectory saveDirectory, File playerdataFile) {
String playerUUID = getPlayerUUIDFromPlayerdataFile(playerdataFile);
return tryReadCoordinatesFromPlayerdata(saveDirectory, playerUUID)
.map(c -> createPlayerdataPlayerNbt(playerUUID, c));
}
private String getPlayerUUIDFromPlayerdataFile(File playerdataFile) {
return playerdataFile.getName().split("\\.")[0];
}
private Optional<PlayerNbt> createPlayersPlayerNbt(SaveDirectory saveDirectory, File playersFile) {
String playerName = getPlayerNameFromPlayersFile(playersFile);
return tryReadCoordinatesFromPlayers(saveDirectory, playerName).map(c -> createPlayersPlayerNbt(playerName, c));
}
private String getPlayerNameFromPlayersFile(File playersFile) {
return playersFile.getName().split("\\.")[0];
}
private PlayerNbt createLevelDatPlayerNbt(PlayerCoordinates playerCoordinates) {
return new PlayerNbt(playerCoordinates) {
@Override
public <R> R map(
Supplier<R> ifIsLevelDat,
Function<String, R> ifIsPlayerdata,
Function<String, R> ifIsPlayers) {
return ifIsLevelDat.get();
}
};
}
private PlayerNbt createPlayerdataPlayerNbt(String playerUUID, PlayerCoordinates playerCoordinates) {
return new PlayerNbt(playerCoordinates) {
@Override
public <R> R map(
Supplier<R> ifIsLevelDat,
Function<String, R> ifIsPlayerdata,
Function<String, R> ifIsPlayers) {
return ifIsPlayerdata.apply(playerUUID);
}
};
}
private PlayerNbt createPlayersPlayerNbt(String playerName, PlayerCoordinates playerCoordinates) {
return new PlayerNbt(playerCoordinates) {
@Override
public <R> R map(
Supplier<R> ifIsLevelDat,
Function<String, R> ifIsPlayerdata,
Function<String, R> ifIsPlayers) {
return ifIsPlayers.apply(playerName);
}
};
}
private Optional<PlayerCoordinates> tryReadCoordinatesFromLevelDat(SaveDirectory saveDirectory) {
try {
return PlayerLocationLoader.tryReadFromLevelDat(saveDirectory.getLevelDat());
} catch (IOException e) {
AmidstLogger.warn(e, "error while reading player coordinates from level.dat");
return Optional.empty();
}
}
private Optional<PlayerCoordinates> tryReadCoordinatesFromPlayerdata(
SaveDirectory saveDirectory,
String playerUUID) {
try {
return PlayerLocationLoader.tryReadFromPlayerFile(saveDirectory.getPlayerdataFile(playerUUID));
} catch (IOException e) {
AmidstLogger.warn(e, "error while reading player coordinates for player " + playerUUID);
return Optional.empty();
}
}
private Optional<PlayerCoordinates> tryReadCoordinatesFromPlayers(SaveDirectory saveDirectory, String playerName) {
try {
return PlayerLocationLoader.tryReadFromPlayerFile(saveDirectory.getPlayersFile(playerName));
} catch (IOException e) {
AmidstLogger.warn(e, "error while reading player coordinates for player " + playerName);
return Optional.empty();
}
}
public boolean tryBackup(SaveDirectory saveDirectory, PlayerNbt playerNbt) {
return playerNbt.map(
() -> amidstBackupService.tryBackupLevelDat(saveDirectory),
playerUUID -> amidstBackupService.tryBackupPlayerdataFile(saveDirectory, playerUUID),
playerName -> amidstBackupService.tryBackupPlayersFile(saveDirectory, playerName));
}
public boolean tryWriteCoordinates(
SaveDirectory saveDirectory,
PlayerNbt playerNbt,
PlayerCoordinates coordinates) {
return playerNbt.map(
() -> tryWriteCoordinatesToLevelDat(saveDirectory, coordinates),
playerUUID -> tryWriteCoordinatesToPlayerdata(saveDirectory, playerUUID, coordinates),
playerName -> tryWriteCoordinatesToPlayers(saveDirectory, playerName, coordinates));
}
private boolean tryWriteCoordinatesToLevelDat(SaveDirectory saveDirectory, PlayerCoordinates coordinates) {
try {
return PlayerLocationSaver.tryWriteToLevelDat(coordinates, saveDirectory.getLevelDat());
} catch (IOException e) {
AmidstLogger.warn(e, "error while writing player coordinates to level.dat");
return false;
}
}
private boolean tryWriteCoordinatesToPlayerdata(
SaveDirectory saveDirectory,
String playerUUID,
PlayerCoordinates coordinates) {
try {
return PlayerLocationSaver.tryWriteToPlayerFile(coordinates, saveDirectory.getPlayerdataFile(playerUUID));
} catch (IOException e) {
AmidstLogger.warn(e, "error while writing player coordinates for player " + playerUUID);
return false;
}
}
private boolean tryWriteCoordinatesToPlayers(
SaveDirectory saveDirectory,
String playerName,
PlayerCoordinates coordinates) {
try {
return PlayerLocationSaver.tryWriteToPlayerFile(coordinates, saveDirectory.getPlayersFile(playerName));
} catch (IOException e) {
AmidstLogger.warn(e, "error while writing player coordinates for player " + playerName);
return false;
}
}
}

View File

@ -4,9 +4,7 @@ import java.io.IOException;
import amidst.documentation.Immutable;
import amidst.mojangapi.file.MojangApiParsingException;
import amidst.mojangapi.file.directory.SaveDirectory;
import amidst.mojangapi.file.nbt.LevelDatNbt;
import amidst.mojangapi.file.service.SaveDirectoryService;
import amidst.mojangapi.file.facade.SaveGame;
import amidst.mojangapi.minecraftinterface.MinecraftInterface;
import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException;
import amidst.mojangapi.minecraftinterface.RecognisedVersion;
@ -74,26 +72,25 @@ public class WorldBuilder {
versionFeatures.getValidBiomesForStructure_Spawn()));
}
public World fromSaveGame(MinecraftInterface minecraftInterface, SaveDirectory saveDirectory)
public World fromSaveGame(MinecraftInterface minecraftInterface, SaveGame saveGame)
throws IOException,
MinecraftInterfaceException,
MojangApiParsingException {
VersionFeatures versionFeatures = DefaultVersionFeatures.create(minecraftInterface.getRecognisedVersion());
LevelDatNbt levelDat = new SaveDirectoryService().createLevelDat(saveDirectory);
MovablePlayerList movablePlayerList = new MovablePlayerList(
playerInformationCache,
saveDirectory,
saveGame,
true,
WorldPlayerType.from(saveDirectory, levelDat));
WorldPlayerType.from(saveGame));
return create(
minecraftInterface,
WorldSeed.fromSaveGame(levelDat.getSeed()),
levelDat.getWorldType(),
levelDat.getGeneratorOptions(),
WorldSeed.fromSaveGame(saveGame.getSeed()),
saveGame.getWorldType(),
saveGame.getGeneratorOptions(),
movablePlayerList,
versionFeatures,
new BiomeDataOracle(minecraftInterface),
new ImmutableWorldSpawnOracle(levelDat.getWorldSpawn()));
new ImmutableWorldSpawnOracle(saveGame.getWorldSpawn()));
}
private World create(

View File

@ -7,8 +7,8 @@ import amidst.documentation.AmidstThread;
import amidst.documentation.CalledOnlyBy;
import amidst.documentation.ThreadSafe;
import amidst.logging.AmidstLogger;
import amidst.mojangapi.file.directory.SaveDirectory;
import amidst.mojangapi.file.nbt.player.PlayerNbt;
import amidst.mojangapi.file.facade.SaveGame;
import amidst.mojangapi.file.facade.SaveGamePlayer;
import amidst.threading.WorkerExecutor;
@ThreadSafe
@ -20,7 +20,7 @@ public class MovablePlayerList implements Iterable<Player> {
}
private final PlayerInformationCache playerInformationCache;
private final SaveDirectory saveDirectory;
private final SaveGame saveGame;
private final boolean isSaveEnabled;
private volatile WorldPlayerType worldPlayerType;
@ -28,11 +28,11 @@ public class MovablePlayerList implements Iterable<Player> {
public MovablePlayerList(
PlayerInformationCache playerInformationCache,
SaveDirectory saveDirectory,
SaveGame saveGame,
boolean isSaveEnabled,
WorldPlayerType worldPlayerType) {
this.playerInformationCache = playerInformationCache;
this.saveDirectory = saveDirectory;
this.saveGame = saveGame;
this.isSaveEnabled = isSaveEnabled;
this.worldPlayerType = worldPlayerType;
}
@ -46,11 +46,11 @@ public class MovablePlayerList implements Iterable<Player> {
}
public boolean canLoad() {
return saveDirectory != null;
return saveGame != null;
}
public void load(WorkerExecutor workerExecutor, Runnable onPlayerFinishedLoading) {
if (saveDirectory != null) {
if (saveGame != null) {
AmidstLogger.info("loading player locations");
ConcurrentLinkedQueue<Player> players = new ConcurrentLinkedQueue<>();
this.players = players;
@ -70,20 +70,17 @@ public class MovablePlayerList implements Iterable<Player> {
WorkerExecutor workerExecutor,
ConcurrentLinkedQueue<Player> players,
Runnable onPlayerFinishedLoading) {
for (PlayerNbt playerNbt : worldPlayerType.createPlayerNbts(saveDirectory)) {
workerExecutor.run(() -> loadPlayer(players, playerNbt), onPlayerFinishedLoading);
for (SaveGamePlayer saveGamePlayer : worldPlayerType.tryReadPlayers(saveGame)) {
workerExecutor.run(() -> loadPlayer(players, saveGamePlayer), onPlayerFinishedLoading);
}
}
@CalledOnlyBy(AmidstThread.WORKER)
private void loadPlayer(ConcurrentLinkedQueue<Player> players, PlayerNbt playerNbt) {
Player player = playerNbt.map(
() -> new Player(PlayerInformation.theSingleplayerPlayer(), playerNbt),
playerUUID -> new Player(playerInformationCache.getByUUID(playerUUID), playerNbt),
playerName -> new Player(playerInformationCache.getByName(playerName), playerNbt));
if (player.tryLoadLocation()) {
players.offer(player);
}
private void loadPlayer(ConcurrentLinkedQueue<Player> players, SaveGamePlayer saveGamePlayer) {
players.offer(saveGamePlayer.map(
() -> new Player(PlayerInformation.theSingleplayerPlayer(), saveGamePlayer),
playerUUID -> new Player(playerInformationCache.getByUUID(playerUUID), saveGamePlayer),
playerName -> new Player(playerInformationCache.getByName(playerName), saveGamePlayer)));
}
public boolean canSave() {

View File

@ -1,11 +1,8 @@
package amidst.mojangapi.world.player;
import java.io.IOException;
import amidst.documentation.ThreadSafe;
import amidst.logging.AmidstLogger;
import amidst.mojangapi.file.MojangApiParsingException;
import amidst.mojangapi.file.nbt.player.PlayerNbt;
import amidst.mojangapi.file.facade.SaveGamePlayer;
import amidst.mojangapi.world.Dimension;
import amidst.mojangapi.world.coordinates.CoordinatesInWorld;
import amidst.mojangapi.world.icon.WorldIconImage;
@ -13,13 +10,15 @@ import amidst.mojangapi.world.icon.WorldIconImage;
@ThreadSafe
public class Player {
private final PlayerInformation playerInformation;
private final PlayerNbt playerNbt;
private final SaveGamePlayer saveGamePlayer;
private volatile PlayerCoordinates savedCoordinates;
private volatile PlayerCoordinates currentCoordinates;
public Player(PlayerInformation playerInformation, PlayerNbt playerNbt) {
public Player(PlayerInformation playerInformation, SaveGamePlayer saveGamePlayer) {
this.playerInformation = playerInformation;
this.playerNbt = playerNbt;
this.saveGamePlayer = saveGamePlayer;
this.savedCoordinates = saveGamePlayer.getPlayerCoordinates();
this.currentCoordinates = savedCoordinates;
}
public String getPlayerName() {
@ -38,52 +37,22 @@ public class Player {
this.currentCoordinates = new PlayerCoordinates(coordinates, height, dimension);
}
public boolean trySaveLocation() {
try {
if (saveLocation()) {
return true;
} else {
AmidstLogger.warn(
"skipping to save player location, because the backup file cannot be created for player: "
+ getPlayerName());
return false;
}
} catch (MojangApiParsingException e) {
AmidstLogger.warn(e, "error while writing player location for player: " + getPlayerName());
return false;
}
}
/**
* Returns true if the player was not moved or the new location was
* successfully saved.
*/
public synchronized boolean saveLocation() throws MojangApiParsingException {
public synchronized boolean trySaveLocation() {
PlayerCoordinates currentCoordinates = this.currentCoordinates;
if (savedCoordinates != currentCoordinates) {
if (playerNbt.tryWriteCoordinates(currentCoordinates)) {
if (saveGamePlayer.tryBackupAndWritePlayerCoordinates(currentCoordinates)) {
savedCoordinates = currentCoordinates;
return true;
} else {
AmidstLogger.warn("error while writing player location for player: " + getPlayerName());
return false;
}
} else {
return true;
}
}
public boolean tryLoadLocation() {
try {
loadLocation();
return true;
} catch (IOException | MojangApiParsingException e) {
AmidstLogger.warn(e, "error while reading player location for player: " + getPlayerName());
return false;
}
}
public synchronized void loadLocation() throws IOException, MojangApiParsingException {
this.savedCoordinates = playerNbt.readCoordinates();
this.currentCoordinates = savedCoordinates;
}
}

View File

@ -1,16 +1,13 @@
package amidst.mojangapi.world.player;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import amidst.documentation.Immutable;
import amidst.documentation.NotNull;
import amidst.mojangapi.file.directory.SaveDirectory;
import amidst.mojangapi.file.nbt.LevelDatNbt;
import amidst.mojangapi.file.nbt.player.PlayerNbt;
import amidst.mojangapi.file.service.SaveDirectoryService;
import amidst.mojangapi.file.facade.SaveGame;
import amidst.mojangapi.file.facade.SaveGamePlayer;
@Immutable
public enum WorldPlayerType {
@ -27,9 +24,9 @@ public enum WorldPlayerType {
return SELECTABLE;
}
public static WorldPlayerType from(SaveDirectory saveDirectory, LevelDatNbt levelDat) {
boolean hasSingleplayerPlayer = levelDat.hasPlayer();
boolean hasMultiplayerPlayers = saveDirectory.hasMultiplayerPlayers();
public static WorldPlayerType from(SaveGame saveGame) {
boolean hasSingleplayerPlayer = saveGame.hasSingleplayerPlayer();
boolean hasMultiplayerPlayers = saveGame.hasMultiplayerPlayers();
if (hasSingleplayerPlayer && hasMultiplayerPlayers) {
return BOTH;
} else if (hasSingleplayerPlayer) {
@ -52,17 +49,16 @@ public enum WorldPlayerType {
}
@NotNull
public List<PlayerNbt> createPlayerNbts(SaveDirectory saveDirectory) {
SaveDirectoryService saveDirectoryService = new SaveDirectoryService();
public List<SaveGamePlayer> tryReadPlayers(SaveGame saveGame) {
if (this == BOTH) {
List<PlayerNbt> result = new ArrayList<>();
result.addAll(saveDirectoryService.createSingleplayerPlayerNbts(saveDirectory));
result.addAll(saveDirectoryService.createMultiplayerPlayerNbts(saveDirectory));
return result;
return saveGame.tryReadAllPlayers();
} else if (this == SINGLEPLAYER) {
return saveDirectoryService.createSingleplayerPlayerNbts(saveDirectory);
return saveGame
.tryReadSingleplayerPlayer()
.map(Collections::singletonList)
.orElseGet(Collections::emptyList);
} else if (this == MULTIPLAYER) {
return saveDirectoryService.createMultiplayerPlayerNbts(saveDirectory);
return saveGame.tryReadMultiplayerPlayers();
} else {
return Collections.emptyList();
}