Skip to content

Commit

Permalink
Reintroduce include statements (#1028)
Browse files Browse the repository at this point in the history
Signed-off-by: applenick <[email protected]>
  • Loading branch information
applenick authored Sep 2, 2022
1 parent d667721 commit e65c23b
Show file tree
Hide file tree
Showing 13 changed files with 340 additions and 5 deletions.
12 changes: 12 additions & 0 deletions core/src/main/java/tc/oc/pgm/PGMConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.Map;
import java.util.TreeSet;
import java.util.logging.Level;
import javax.annotation.Nullable;
import net.kyori.adventure.text.Component;
import org.bukkit.ChatColor;
import org.bukkit.configuration.ConfigurationSection;
Expand Down Expand Up @@ -56,6 +57,7 @@ public final class PGMConfig implements Config {
// map.*
private final List<MapSourceFactory> mapSourceFactories;
private final String mapPoolFile;
private final String includesDirectory;

// countdown.*
private final Duration startTime;
Expand Down Expand Up @@ -159,6 +161,11 @@ public final class PGMConfig implements Config {
mapPoolFile == null || mapPoolFile.isEmpty()
? null
: new File(dataFolder, mapPoolFile).getAbsolutePath();
final String includesDirectory = config.getString("map.includes");
this.includesDirectory =
includesDirectory == null || includesDirectory.isEmpty()
? null
: new File(dataFolder, includesDirectory).getAbsolutePath();

this.startTime = parseDuration(config.getString("countdown.start", "30s"));
this.huddleTime = parseDuration(config.getString("countdown.huddle", "0s"));
Expand Down Expand Up @@ -466,6 +473,11 @@ public String getMapPoolFile() {
return mapPoolFile;
}

@Override
public @Nullable String getIncludesDirectory() {
return includesDirectory;
}

@Override
public Duration getStartTime() {
return startTime;
Expand Down
7 changes: 6 additions & 1 deletion core/src/main/java/tc/oc/pgm/PGMPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import tc.oc.pgm.api.map.MapOrder;
import tc.oc.pgm.api.map.exception.MapException;
import tc.oc.pgm.api.map.factory.MapSourceFactory;
import tc.oc.pgm.api.map.includes.MapIncludeProcessor;
import tc.oc.pgm.api.match.Match;
import tc.oc.pgm.api.match.MatchManager;
import tc.oc.pgm.api.module.Module;
Expand All @@ -57,6 +58,7 @@
import tc.oc.pgm.listeners.ServerPingDataListener;
import tc.oc.pgm.listeners.WorldProblemListener;
import tc.oc.pgm.map.MapLibraryImpl;
import tc.oc.pgm.map.includes.MapIncludeProcessorImpl;
import tc.oc.pgm.match.MatchManagerImpl;
import tc.oc.pgm.match.NoopVanishManager;
import tc.oc.pgm.namedecorations.ConfigDecorationProvider;
Expand Down Expand Up @@ -85,6 +87,7 @@ public class PGMPlugin extends JavaPlugin implements PGM, Listener {
private Logger gameLogger;
private Datastore datastore;
private MapLibrary mapLibrary;
private MapIncludeProcessor mapIncludeProcessor;
private List<MapSourceFactory> mapSourceFactories;
private MatchManager matchManager;
private MatchTabManager matchTabManager;
Expand Down Expand Up @@ -134,7 +137,8 @@ public void onEnable() {
asyncExecutorService = new BukkitExecutorService(this, true);

mapSourceFactories = new ArrayList<>();
mapLibrary = new MapLibraryImpl(gameLogger, mapSourceFactories);
mapIncludeProcessor = new MapIncludeProcessorImpl(gameLogger);
mapLibrary = new MapLibraryImpl(gameLogger, mapSourceFactories, mapIncludeProcessor);

saveDefaultConfig(); // Writes a config file, if one does not exist.
reloadConfig(); // Populates "this.config", if there is an error, will be null
Expand Down Expand Up @@ -261,6 +265,7 @@ public void reloadConfig() {

mapSourceFactories.clear();
mapSourceFactories.addAll(config.getMapSourceFactories());
mapIncludeProcessor.reload(config);

if (mapOrder != null) {
mapOrder.reload();
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/java/tc/oc/pgm/api/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ public interface Config {
@Nullable
String getMapPoolFile();

/**
* Gets a path to the includes directory.
*
* @return A path to the includes directory, or null for none.
*/
@Nullable
String getIncludesDirectory();

/**
* Gets a duration to wait before starting a match.
*
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/java/tc/oc/pgm/api/map/MapLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Iterator;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nullable;
import tc.oc.pgm.api.map.includes.MapIncludeProcessor;

/** A library of {@link MapInfo}s and {@link MapContext}s. */
public interface MapLibrary {
Expand Down Expand Up @@ -45,4 +46,11 @@ public interface MapLibrary {
* @return A {@link MapContext}.
*/
CompletableFuture<MapContext> loadExistingMap(String id);

/**
* Get the {@link MapIncludeProcessor}.
*
* @return A {@link MapIncludeProcessor}
*/
MapIncludeProcessor getIncludeProcessor();
}
11 changes: 11 additions & 0 deletions core/src/main/java/tc/oc/pgm/api/map/MapSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.File;
import java.io.InputStream;
import tc.oc.pgm.api.map.exception.MapMissingException;
import tc.oc.pgm.api.map.includes.MapInclude;

/** A source where {@link MapInfo} documents and files are downloaded. */
public interface MapSource {
Expand Down Expand Up @@ -38,4 +39,14 @@ public interface MapSource {
* @throws MapMissingException If the document can no longer be found.
*/
boolean checkForUpdates() throws MapMissingException;

/**
* Adds an associated {@link MapInclude}
*
* @param include The {@link MapInclude}
*/
void addMapInclude(MapInclude include);

/** Remove all associated {@link MapInclude}, used when reloading document. */
void clearIncludes();
}
37 changes: 37 additions & 0 deletions core/src/main/java/tc/oc/pgm/api/map/includes/MapInclude.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package tc.oc.pgm.api.map.includes;

import java.util.Collection;
import org.jdom2.Content;

/** Represents a snippet of XML that can be referenced for reuse * */
public interface MapInclude {

/**
* Get a unique id which identifies this MapInclude.
*
* @return A unique id
*/
String getId();

/**
* Get the system file time from when this MapInclude file was last modified.
*
* @return Time of last file modification
*/
long getLastModified();

/**
* Gets whether the associated {@link MapInclude} files have changed since last loading.
*
* @param time The current system time
* @return True if given time is newer than last modified time
*/
boolean hasBeenModified(long time);

/**
* Get a collection of {@link Content} which can be merged into an existing {@link Document}
*
* @return a collection of {@link Content}
*/
Collection<Content> getContent();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package tc.oc.pgm.api.map.includes;

import java.util.Collection;
import org.jdom2.Document;
import tc.oc.pgm.api.Config;
import tc.oc.pgm.util.xml.InvalidXMLException;

/** A processor to determine which {@link MapInclude}s should be included when loading a map * */
public interface MapIncludeProcessor {

/**
* Process the given {@link Document} and return a collection of {@link MapInclude}s.
*
* @param document A map document
* @return A collection of map includes, collection will be empty if none are found.
* @throws InvalidXMLException If the given document is not found or able to be parsed.
*/
Collection<MapInclude> getMapIncludes(Document document) throws InvalidXMLException;

/**
* Get a {@link MapInclude} by its id
*
* @param includeId ID of the map include
* @return A {@link MapInclude}
*/
MapInclude getMapIncludeById(String includeId);

/**
* Reload the processor to fetch new map includes or reload existing ones.
*
* @param config A configuration file.
*/
void reload(Config config);
}
20 changes: 19 additions & 1 deletion core/src/main/java/tc/oc/pgm/map/MapFactoryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.logging.Logger;
import org.jdom2.Document;
import org.jdom2.JDOMException;
Expand All @@ -19,6 +20,8 @@
import tc.oc.pgm.api.map.exception.MapMissingException;
import tc.oc.pgm.api.map.factory.MapFactory;
import tc.oc.pgm.api.map.factory.MapModuleFactory;
import tc.oc.pgm.api.map.includes.MapInclude;
import tc.oc.pgm.api.map.includes.MapIncludeProcessor;
import tc.oc.pgm.api.module.ModuleGraph;
import tc.oc.pgm.api.module.exception.ModuleLoadException;
import tc.oc.pgm.features.FeatureDefinitionContext;
Expand Down Expand Up @@ -49,17 +52,19 @@ public class MapFactoryImpl extends ModuleGraph<MapModule, MapModuleFactory<? ex

private final Logger logger;
private final MapSource source;
private final MapIncludeProcessor includes;
private Document document;
private MapInfo info;
private RegionParser regions;
private FilterParser filters;
private KitParser kits;
private FeatureDefinitionContext features;

public MapFactoryImpl(Logger logger, MapSource source) {
public MapFactoryImpl(Logger logger, MapSource source, MapIncludeProcessor includes) {
super(Modules.MAP); // Do not copy to avoid N copies of the factories
this.logger = ClassLogger.get(checkNotNull(logger), getClass(), checkNotNull(source).getId());
this.source = source;
this.includes = includes;
}

@Override
Expand All @@ -71,17 +76,30 @@ protected MapModule createModule(MapModuleFactory factory) throws ModuleLoadExce
}
}

private void storeInclude(MapInclude include) {
this.source.addMapInclude(include);
}

private void preLoad()
throws IOException, JDOMException, InvalidXMLException, MapMissingException {
if (document != null && !source.checkForUpdates()) {
return; // If a document is present and there are no updates, skip loading again
}

source.clearIncludes();

try (final InputStream stream = source.getDocument()) {
document = DOCUMENT_FACTORY.get().build(stream);
document.setBaseURI(source.getId());
}

// Check for any included map sources, appending them to the document if present
Collection<MapInclude> mapIncludes = includes.getMapIncludes(document);
for (MapInclude include : mapIncludes) {
document.getRootElement().addContent(0, include.getContent());
storeInclude(include);
}

info = new MapInfoImpl(document.getRootElement());
}

Expand Down
13 changes: 11 additions & 2 deletions core/src/main/java/tc/oc/pgm/map/MapLibraryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import tc.oc.pgm.api.map.exception.MapMissingException;
import tc.oc.pgm.api.map.factory.MapFactory;
import tc.oc.pgm.api.map.factory.MapSourceFactory;
import tc.oc.pgm.api.map.includes.MapIncludeProcessor;
import tc.oc.pgm.util.StringUtils;
import tc.oc.pgm.util.UsernameResolver;

Expand All @@ -37,6 +38,7 @@ public class MapLibraryImpl implements MapLibrary {
private final List<MapSourceFactory> factories;
private final SortedMap<String, MapEntry> maps;
private final Set<MapSource> failed;
private final MapIncludeProcessor includes;

private static class MapEntry {
private final MapSource source;
Expand All @@ -50,11 +52,13 @@ private MapEntry(MapSource source, MapInfo info, MapContext context) {
}
}

public MapLibraryImpl(Logger logger, List<MapSourceFactory> factories) {
public MapLibraryImpl(
Logger logger, List<MapSourceFactory> factories, MapIncludeProcessor includes) {
this.logger = checkNotNull(logger); // Logger should be visible in-game
this.factories = Collections.synchronizedList(checkNotNull(factories));
this.maps = Collections.synchronizedSortedMap(new ConcurrentSkipListMap<>());
this.failed = Collections.synchronizedSet(new HashSet<>());
this.includes = includes;
}

@Override
Expand All @@ -79,6 +83,11 @@ public long getSize() {
return maps.size();
}

@Override
public MapIncludeProcessor getIncludeProcessor() {
return includes;
}

private void logMapError(MapException err) {
logger.log(Level.WARNING, err.getMessage(), err);
}
Expand Down Expand Up @@ -187,7 +196,7 @@ public CompletableFuture<MapContext> loadExistingMap(String id) {

private MapContext loadMap(MapSource source, @Nullable String mapId) throws MapException {
final MapContext context;
try (final MapFactory factory = new MapFactoryImpl(logger, source)) {
try (final MapFactory factory = new MapFactoryImpl(logger, source, includes)) {
context = factory.load();
} catch (MapMissingException e) {
failed.remove(source);
Expand Down
59 changes: 59 additions & 0 deletions core/src/main/java/tc/oc/pgm/map/includes/MapIncludeImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package tc.oc.pgm.map.includes;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicLong;
import org.jdom2.Content;
import org.jdom2.Document;
import org.jdom2.JDOMException;
import tc.oc.pgm.api.map.exception.MapMissingException;
import tc.oc.pgm.api.map.includes.MapInclude;

public class MapIncludeImpl implements MapInclude {

private final String id;
private final Document source;
private final AtomicLong lastModified;

public MapIncludeImpl(File file) throws MapMissingException, JDOMException, IOException {
try {
InputStream fileStream = new FileInputStream(file);
this.id = file.getName().replace(".xml", "");
this.source = MapIncludeProcessorImpl.DOCUMENT_FACTORY.get().build(fileStream);
} catch (FileNotFoundException e) {
throw new MapMissingException(file.getPath(), "Unable to read map include document", e);
} finally {
lastModified = new AtomicLong(file.lastModified());
}
}

@Override
public String getId() {
return id;
}

@Override
public Collection<Content> getContent() {
return source.getRootElement().cloneContent();
}

@Override
public boolean equals(Object other) {
if (other == null || !(other instanceof MapInclude)) return false;
return ((MapInclude) other).getId().equalsIgnoreCase(getId());
}

@Override
public long getLastModified() {
return lastModified.get();
}

@Override
public boolean hasBeenModified(long time) {
return time > lastModified.get();
}
}
Loading

0 comments on commit e65c23b

Please sign in to comment.