diff --git a/API/api/src/main/java/tc/oc/api/docs/virtual/ServerDoc.java b/API/api/src/main/java/tc/oc/api/docs/virtual/ServerDoc.java index ea604d8..fd6adcb 100644 --- a/API/api/src/main/java/tc/oc/api/docs/virtual/ServerDoc.java +++ b/API/api/src/main/java/tc/oc/api/docs/virtual/ServerDoc.java @@ -106,7 +106,7 @@ public interface ServerDoc { * Startup info received from the API */ @Serialize - interface Configuration extends Partial { + interface Configuration extends Rotations { String settings_profile(); Map operators(); @Nullable Team team(); @@ -117,7 +117,6 @@ public interface ServerDoc { Visibility startup_visibility(); boolean whitelist_enabled(); boolean waiting_room(); - @Nullable String resource_pack_url(); @Nullable String resource_pack_sha1(); boolean resource_pack_fast_update(); @@ -142,6 +141,17 @@ public interface ServerDoc { Set queued_mutations(); } + @Serialize + interface Rotations extends Partial { + List rotations(); + } + + @Serialize + interface Rotation extends Document { + String name(); + String next_map_id(); + } + /** * Status sent to the API from Lobby */ diff --git a/API/minecraft/src/main/java/tc/oc/api/minecraft/servers/LocalServerDocument.java b/API/minecraft/src/main/java/tc/oc/api/minecraft/servers/LocalServerDocument.java index 64498ad..7006448 100644 --- a/API/minecraft/src/main/java/tc/oc/api/minecraft/servers/LocalServerDocument.java +++ b/API/minecraft/src/main/java/tc/oc/api/minecraft/servers/LocalServerDocument.java @@ -282,4 +282,9 @@ public class LocalServerDocument extends StartupServerDocument implements Server public Set queued_mutations() { return mutations != null ? mutations.queued_mutations() : Collections.emptySet(); } + + @Override + public List rotations() { + return Collections.emptyList(); + } } diff --git a/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java b/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java index 3fc0510..bf718ad 100644 --- a/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java +++ b/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java @@ -209,7 +209,13 @@ public class MapCommands { final RotationState rotation = CommandUtils.getRotation(args.getFlag('n'), sender); int page = args.getInteger(0, 1); - new PrettyPaginatedResult(PGMTranslations.get().t("command.map.currentRotation.title", sender) + "(" + ChatColor.DARK_AQUA + PGM.getMatchManager().getRotationManager().getCurrentRotationName() + ChatColor.RESET + ")") { + String header = PGMTranslations.get().t("command.map.currentRotation.title", sender); + String name = PGM.getMatchManager().getRotationManager().getCurrentRotationName(); + if(!name.equalsIgnoreCase("default")) { + header += " (" + ChatColor.DARK_AQUA + name + ChatColor.RESET + ")"; + } + + new PrettyPaginatedResult(header) { @Override public String format(PGMMap map, int index) { ChatColor color = index == rotation.getNextId() ? ChatColor.DARK_AQUA : ChatColor.WHITE; return color.toString() + (index + 1) + ". " + map.getInfo().getShortDescription(sender); diff --git a/PGM/src/main/java/tc/oc/pgm/match/MatchManager.java b/PGM/src/main/java/tc/oc/pgm/match/MatchManager.java index 3013f94..ff95d2f 100644 --- a/PGM/src/main/java/tc/oc/pgm/match/MatchManager.java +++ b/PGM/src/main/java/tc/oc/pgm/match/MatchManager.java @@ -20,6 +20,7 @@ import org.bukkit.ChatColor; import org.bukkit.World; import org.bukkit.configuration.Configuration; import org.bukkit.event.EventBus; +import tc.oc.api.minecraft.MinecraftService; import tc.oc.api.util.Permissions; import tc.oc.commons.core.logging.Loggers; import tc.oc.pgm.PGM; @@ -48,6 +49,7 @@ public class MatchManager implements MatchFinder { private final FileRotationProviderFactory fileRotationProviderFactory; private final EventBus eventBus; private final MatchLoader matchLoader; + private final MinecraftService minecraftService; private @Nullable RotationManager rotationManager; /** Custom set next map. */ @@ -64,7 +66,8 @@ public class MatchManager implements MatchFinder { MapErrorTracker mapErrorTracker, FileRotationProviderFactory fileRotationProviderFactory, EventBus eventBus, - MatchLoader matchLoader) throws MapNotFoundException { + MatchLoader matchLoader, + MinecraftService minecraftService) throws MapNotFoundException { this.pluginDataFolder = pluginDataFolder; this.mapErrorTracker = mapErrorTracker; @@ -75,6 +78,7 @@ public class MatchManager implements MatchFinder { this.mapLoader = mapLoader; this.eventBus = eventBus; this.matchLoader = matchLoader; + this.minecraftService = minecraftService; } @Override @@ -126,6 +130,7 @@ public class MatchManager implements MatchFinder { if(rotationManager == null) { rotationManager = new RotationManager( log, + minecraftService, config.get(), mapLibrary.getMaps().iterator().next(), fileRotationProviderFactory.parse( diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/FileRotationProvider.java b/PGM/src/main/java/tc/oc/pgm/rotation/FileRotationProvider.java index 54b0aee..87cb27b 100644 --- a/PGM/src/main/java/tc/oc/pgm/rotation/FileRotationProvider.java +++ b/PGM/src/main/java/tc/oc/pgm/rotation/FileRotationProvider.java @@ -16,6 +16,8 @@ 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.api.docs.virtual.ServerDoc; import tc.oc.commons.core.logging.ClassLogger; import tc.oc.commons.core.util.Joiners; import tc.oc.pgm.PGM; @@ -32,8 +34,9 @@ public class FileRotationProvider extends AbstractRotationProvider { private final String name; private final Path rotationFile; private final Path dataPath; + private final Optional rotationApi; - public FileRotationProvider(MapLibrary mapLibrary, String name, Path rotationFile, Path dataPath) { + public FileRotationProvider(MapLibrary mapLibrary, String name, Path rotationFile, Path dataPath, Optional rotationApi) { Preconditions.checkNotNull(mapLibrary, "map manager"); Preconditions.checkNotNull(rotationFile, "rotation path"); Preconditions.checkNotNull(dataPath, "data path"); @@ -46,6 +49,7 @@ public class FileRotationProvider extends AbstractRotationProvider { this.name = name; this.rotationFile = rotationFile; this.dataPath = dataPath; + this.rotationApi = rotationApi; } Path nextIdFile() { @@ -54,7 +58,7 @@ public class FileRotationProvider extends AbstractRotationProvider { @Override public @Nonnull Future loadRotations() { - return getExecutorService().submit((Runnable) () -> { + return getExecutorService().submit(() -> { try { setRotation(name, loadRotationFromDisk(), Instant.now()); } catch(IOException e) { @@ -64,8 +68,8 @@ public class FileRotationProvider extends AbstractRotationProvider { } private RotationState loadRotationFromDisk() throws IOException { - int nextId = this.parseNextId(); List maps = this.parseRotationNames(); + int nextId = this.parseNextId(maps); if(maps.isEmpty()) { throw new IOException(String.format("Rotation '%s' was empty!", name)); @@ -80,13 +84,17 @@ public class FileRotationProvider extends AbstractRotationProvider { return new RotationState(maps, nextId); } - private int parseNextId() { + private int parseNextId(List maps) { List 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; + return rotationApi.map(rot -> maps.indexOf(mapLibrary.getMapByNameOrId(rot.next_map_id()).get())) + .flatMap(index -> Optional.ofNullable(index >= 0 ? index : null)) + .orElseGet(() -> { + this.logger.warning("Failed to read next id from " + nextIdFile().toString()); + return DEFAULT_NEXTID; + }); } for(String line : lines) { @@ -125,11 +133,7 @@ public class FileRotationProvider extends AbstractRotationProvider { @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); - } - }); + return getExecutorService().submit(() -> saveRotationToDisk(name, rotation)); } private void saveRotationToDisk(@Nonnull String name, @Nonnull RotationState rotation) { diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/FileRotationProviderFactory.java b/PGM/src/main/java/tc/oc/pgm/rotation/FileRotationProviderFactory.java index 09dfe09..5f27a9e 100644 --- a/PGM/src/main/java/tc/oc/pgm/rotation/FileRotationProviderFactory.java +++ b/PGM/src/main/java/tc/oc/pgm/rotation/FileRotationProviderFactory.java @@ -5,6 +5,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; import java.util.HashSet; +import java.util.Optional; import java.util.Set; import javax.inject.Inject; @@ -35,9 +36,14 @@ public class FileRotationProviderFactory { int priority = provider.getInt("priority", 0); int count = provider.getInt("count", 0); + Optional next = minecraftService.getLocalServer() + .rotations() + .stream() + .filter(rot -> rot.name().equals(name) && mapLibrary.getMapByNameOrId(rot.next_map_id()).isPresent()) + .findFirst(); if(Files.isRegularFile(rotationFile)) { - providers.add(new RotationProviderInfo(new FileRotationProvider(mapLibrary, name, rotationFile, dataPath), name, priority, count)); + providers.add(new RotationProviderInfo(new FileRotationProvider(mapLibrary, name, rotationFile, dataPath, next), name, priority, count)); } else if(minecraftService.getLocalServer().startup_visibility() == ServerDoc.Visibility.PUBLIC) { // This is not a perfect way to decide whether or not to throw an error, but it's the best we can do right now mapLibrary.getLogger().severe("Missing rotation file: " + rotationFile); diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/RotationManager.java b/PGM/src/main/java/tc/oc/pgm/rotation/RotationManager.java index 1ed6ec6..f63b985 100644 --- a/PGM/src/main/java/tc/oc/pgm/rotation/RotationManager.java +++ b/PGM/src/main/java/tc/oc/pgm/rotation/RotationManager.java @@ -4,6 +4,7 @@ import java.time.Duration; import java.time.Instant; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.SortedSet; @@ -13,6 +14,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -22,6 +24,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.bukkit.configuration.Configuration; +import tc.oc.api.docs.virtual.ServerDoc; +import tc.oc.api.minecraft.MinecraftService; import tc.oc.commons.core.logging.ClassLogger; import tc.oc.pgm.map.PGMMap; @@ -30,13 +34,15 @@ import static com.google.common.base.Preconditions.checkNotNull; public class RotationManager { private final Logger logger; + private final MinecraftService minecraftService; private final Configuration config; private final SortedSet providers; private String currentRotationName; private RotationState defaultRotation; - public RotationManager(Logger logger, Configuration config, PGMMap defaultMap, Collection providers) { + public RotationManager(Logger logger, MinecraftService minecraftService, Configuration config, PGMMap defaultMap, Collection providers) { this.logger = ClassLogger.get(checkNotNull(logger, "logger"), getClass()); + this.minecraftService = minecraftService; this.config = config; this.providers = Collections.synchronizedSortedSet(Sets.newTreeSet(providers)); @@ -85,6 +91,16 @@ public class RotationManager { for(RotationProviderInfo info : this.providers) { info.provider.saveRotation(name, rotation); } + + minecraftService.updateLocalServer((ServerDoc.Rotations) () -> + getRotations().entrySet() + .stream() + .map(entry -> new ServerDoc.Rotation() { + public String name() { return entry.getKey(); } + public String next_map_id() { return entry.getValue().getNext().getId().slug(); } + }) + .sorted(Comparator.comparing(ServerDoc.Rotation::name, (r1, r2) -> r1.equals(name) ? -1 : 1)) + .collect(Collectors.toList())); } public @Nonnull String getCurrentRotationName() { @@ -136,7 +152,10 @@ public class RotationManager { */ public boolean load(PGMMap defaultMap) { this.defaultRotation = new RotationState(Collections.singletonList(defaultMap), 0); - this.currentRotationName = config.getString("rotation.default-name", "default"); + + List rotations = minecraftService.getLocalServer().rotations(); + this.currentRotationName = rotations.isEmpty() ? config.getString("rotation.default-name", "default") : rotations.get(0).name(); + logger.info("Loading rotations from " + providers.size() + " providers. Fallback map is '" + defaultRotation.getMaps().get(0).getName() +