152 lines
5.2 KiB
Java
152 lines
5.2 KiB
Java
|
package tc.oc.pgm.rotation;
|
||
|
|
||
|
import java.io.IOException;
|
||
|
import java.nio.file.Files;
|
||
|
import java.nio.file.Path;
|
||
|
import java.util.List;
|
||
|
import java.util.Optional;
|
||
|
import java.util.concurrent.ExecutorService;
|
||
|
import java.util.concurrent.Executors;
|
||
|
import java.util.concurrent.Future;
|
||
|
import java.util.logging.Level;
|
||
|
import java.util.logging.Logger;
|
||
|
import javax.annotation.Nonnull;
|
||
|
|
||
|
import com.google.common.base.Charsets;
|
||
|
import com.google.common.base.Preconditions;
|
||
|
import com.google.common.collect.ImmutableList;
|
||
|
import java.time.Instant;
|
||
|
import tc.oc.commons.core.logging.ClassLogger;
|
||
|
import tc.oc.commons.core.util.Joiners;
|
||
|
import tc.oc.pgm.PGM;
|
||
|
import tc.oc.pgm.map.MapLibrary;
|
||
|
import tc.oc.pgm.map.PGMMap;
|
||
|
|
||
|
public class FileRotationProvider extends AbstractRotationProvider {
|
||
|
public static final String FILE_NEXTID_SUFFIX = ".next";
|
||
|
public static final int DEFAULT_NEXTID = 0;
|
||
|
|
||
|
private final Logger logger;
|
||
|
private final Logger mapLogger;
|
||
|
private final MapLibrary mapLibrary;
|
||
|
private final String name;
|
||
|
private final Path rotationFile;
|
||
|
private final Path dataPath;
|
||
|
|
||
|
public FileRotationProvider(MapLibrary mapLibrary, String name, Path rotationFile, Path dataPath) {
|
||
|
Preconditions.checkNotNull(mapLibrary, "map manager");
|
||
|
Preconditions.checkNotNull(rotationFile, "rotation path");
|
||
|
Preconditions.checkNotNull(dataPath, "data path");
|
||
|
Preconditions.checkArgument(Files.isRegularFile(rotationFile), "rotation path must be a file");
|
||
|
Preconditions.checkArgument(Files.isDirectory(dataPath), "data path must be a directory");
|
||
|
|
||
|
this.logger = ClassLogger.get(PGM.get().getLogger(), getClass());
|
||
|
this.mapLogger = PGM.get().getRootMapLogger();
|
||
|
this.mapLibrary = mapLibrary;
|
||
|
this.name = name;
|
||
|
this.rotationFile = rotationFile;
|
||
|
this.dataPath = dataPath;
|
||
|
}
|
||
|
|
||
|
Path nextIdFile() {
|
||
|
return dataPath.resolve(name + FILE_NEXTID_SUFFIX);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public @Nonnull Future<?> loadRotations() {
|
||
|
return getExecutorService().submit((Runnable) () -> {
|
||
|
try {
|
||
|
setRotation(name, loadRotationFromDisk(), Instant.now());
|
||
|
} catch(IOException e) {
|
||
|
mapLogger.log(Level.SEVERE, "Failed to load file rotation", e);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
private RotationState loadRotationFromDisk() throws IOException {
|
||
|
int nextId = this.parseNextId();
|
||
|
List<PGMMap> maps = this.parseRotationNames();
|
||
|
|
||
|
if(maps.isEmpty()) {
|
||
|
throw new IOException(String.format("Rotation '%s' was empty!", name));
|
||
|
}
|
||
|
|
||
|
if(!RotationState.isNextIdValid(maps, nextId)) {
|
||
|
nextId = 0;
|
||
|
}
|
||
|
|
||
|
logger.info("Loaded rotation '" + name + "' with " + maps.size() + " maps: " + Joiners.onCommaSpace.join(maps));
|
||
|
|
||
|
return new RotationState(maps, nextId);
|
||
|
}
|
||
|
|
||
|
private int parseNextId() {
|
||
|
List<String> lines;
|
||
|
try {
|
||
|
lines = Files.readAllLines(nextIdFile(), Charsets.UTF_8);
|
||
|
} catch (IOException e) {
|
||
|
this.logger.warning("Failed to read next id from " + nextIdFile().toString());
|
||
|
return DEFAULT_NEXTID;
|
||
|
}
|
||
|
|
||
|
for(String line : lines) {
|
||
|
try {
|
||
|
return Integer.parseInt(line);
|
||
|
} catch (NumberFormatException e) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.logger.warning("Failed to parse next id from " + nextIdFile().toString());
|
||
|
return DEFAULT_NEXTID;
|
||
|
}
|
||
|
|
||
|
private List<PGMMap> parseRotationNames() throws IOException {
|
||
|
List<String> lines = Files.readAllLines(rotationFile, Charsets.UTF_8);
|
||
|
|
||
|
ImmutableList.Builder<PGMMap> maps = ImmutableList.builder();
|
||
|
for(String line : lines) {
|
||
|
line = line.trim();
|
||
|
if(line.isEmpty()) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
Optional<PGMMap> map = this.mapLibrary.getMapByNameOrId(line);
|
||
|
if(map.isPresent()) {
|
||
|
maps.add(map.get());
|
||
|
} else {
|
||
|
mapLogger.severe("Unknown map '" + line + "' when parsing " + rotationFile.toString());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return maps.build();
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public Future<?> saveRotation(@Nonnull final String name, @Nonnull final RotationState rotation) {
|
||
|
this.setRotation(name, rotation);
|
||
|
return getExecutorService().submit(new Runnable() {
|
||
|
@Override public void run() {
|
||
|
FileRotationProvider.this.saveRotationToDisk(name, rotation);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
private void saveRotationToDisk(@Nonnull String name, @Nonnull RotationState rotation) {
|
||
|
List<String> nextIdSerialized = ImmutableList.of(Integer.toString(rotation.getNextId()));
|
||
|
try {
|
||
|
Files.write(nextIdFile(), nextIdSerialized, Charsets.UTF_8);
|
||
|
} catch (IOException e) {
|
||
|
this.logger.log(Level.SEVERE, "Failed to save next id for rotation: " + name, e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static @Nonnull ExecutorService getExecutorService() {
|
||
|
if(fileExecutorService == null) {
|
||
|
fileExecutorService = Executors.newSingleThreadExecutor();
|
||
|
}
|
||
|
return fileExecutorService;
|
||
|
}
|
||
|
private static ExecutorService fileExecutorService;
|
||
|
}
|