From 6cf7610fadc50b91ab431246856588c0b0d49f11 Mon Sep 17 00:00:00 2001 From: Pablo Herrera Date: Wed, 21 Nov 2018 02:01:22 +0100 Subject: [PATCH] Make map context a soft reference, and keep a persistent context (#79) --- .../java/tc/oc/pgm/commands/MapCommands.java | 15 ++---- .../tc/oc/pgm/cycle/CycleMatchModule.java | 4 +- .../development/MapDevelopmentCommands.java | 8 ++- .../java/tc/oc/pgm/map/MapDefinition.java | 25 +++++++--- .../tc/oc/pgm/map/MapPersistentContext.java | 49 +++++++++++++++++++ PGM/src/main/java/tc/oc/pgm/map/PGMMap.java | 4 +- .../java/tc/oc/pgm/match/MatchLoader.java | 2 +- .../tc/oc/pgm/rotation/RotationState.java | 2 +- 8 files changed, 83 insertions(+), 26 deletions(-) create mode 100644 PGM/src/main/java/tc/oc/pgm/map/MapPersistentContext.java 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 aee1fcd..a20d547 100644 --- a/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java +++ b/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java @@ -174,7 +174,7 @@ public class MapCommands implements Commands { map = CommandUtils.getMatch(sender).getMap(); } - final InfoModule infoModule = map.getContext().needModule(InfoModule.class); + final InfoModule infoModule = map.getPersistentContext().getInfoModule(); final MapInfo mapInfo = infoModule.getMapInfo(); audience.sendMessage(mapInfo.getFormattedMapTitle()); @@ -243,16 +243,7 @@ public class MapCommands implements Commands { } } - int maxPlayers = map.getContext() - .features() - .all(TeamFactory.class) - .mapToInt(TeamFactory::getMaxPlayers) - .sum(); - - FreeForAllModule ffam = map.getContext().getModule(FreeForAllModule.class); - if(ffam != null) { - maxPlayers += ffam.getOptions().maxPlayers; - } + int maxPlayers = map.getPersistentContext().playerLimits().upperEndpoint(); audience.sendMessage(new Component( mapInfoLabel("command.map.mapInfo.playerLimit"), @@ -261,7 +252,7 @@ public class MapCommands implements Commands { if(sender.hasPermission(Permissions.MAPDEV)) { audience.sendMessage(new Component(mapInfoLabel("command.map.mapInfo.genre"), new Component(mapInfo.getLocalizedGenre(), ChatColor.GOLD))); - audience.sendMessage(new Component(mapInfoLabel("command.map.mapInfo.proto"), new Component(map.getContext().getProto().toString(), ChatColor.GOLD))); + audience.sendMessage(new Component(mapInfoLabel("command.map.mapInfo.proto"), new Component(map.getPersistentContext().getProto().toString(), ChatColor.GOLD))); audience.sendMessage(new Component(mapInfoLabel("command.map.mapInfo.folder"), new Component(map.getFolder().getRelativePath().toString(), ChatColor.GOLD))); audience.sendMessage(new Component(mapInfoLabel("command.map.mapInfo.source"), new Component(map.getSource().getPath().toString(), ChatColor.GOLD))); } diff --git a/PGM/src/main/java/tc/oc/pgm/cycle/CycleMatchModule.java b/PGM/src/main/java/tc/oc/pgm/cycle/CycleMatchModule.java index 02484ff..c992508 100644 --- a/PGM/src/main/java/tc/oc/pgm/cycle/CycleMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/cycle/CycleMatchModule.java @@ -120,11 +120,11 @@ public class CycleMatchModule extends MatchModule implements Listener { } @Override public int min_players() { - return nextMap.getContext().playerLimits().lowerEndpoint(); + return nextMap.getPersistentContext().playerLimits().lowerEndpoint(); } @Override public int max_players() { - return nextMap.getContext().playerLimits().upperEndpoint(); + return nextMap.getPersistentContext().playerLimits().upperEndpoint(); } } ), diff --git a/PGM/src/main/java/tc/oc/pgm/development/MapDevelopmentCommands.java b/PGM/src/main/java/tc/oc/pgm/development/MapDevelopmentCommands.java index cd45588..213d457 100644 --- a/PGM/src/main/java/tc/oc/pgm/development/MapDevelopmentCommands.java +++ b/PGM/src/main/java/tc/oc/pgm/development/MapDevelopmentCommands.java @@ -59,6 +59,7 @@ import tc.oc.pgm.map.MapConfiguration; import tc.oc.pgm.map.MapDefinition; import tc.oc.pgm.map.MapLibrary; import tc.oc.pgm.map.MapLogRecord; +import tc.oc.pgm.map.MapModuleContext; import tc.oc.pgm.map.MapNotFoundException; import tc.oc.pgm.map.PGMMap; import tc.oc.pgm.match.Match; @@ -205,7 +206,10 @@ public class MapDevelopmentCommands implements Commands { final Optional typeFilter = CommandUtils.flag(args, 't').map(String::toLowerCase); final Optional idFilter = CommandUtils.flag(args, 'i').map(String::toLowerCase); - Stream features = map.getContext().features().all(); + MapModuleContext context = map.getContext().orElseThrow(() -> + new IllegalStateException("The map modules are currently unloaded.")); + + Stream features = context.features().all(); if(typeFilter.isPresent()) { features = features.filter(f -> f.inspectType().toLowerCase().contains(typeFilter.get())); } @@ -222,7 +226,7 @@ public class MapDevelopmentCommands implements Commands { feature.inspectIdentity().ifPresent(id -> c.extra(" ").extra(new Component(id, ChatColor.YELLOW))); if(locate) { - final Element element = map.getContext().features().definitionNode(feature); + final Element element = context.features().definitionNode(feature); if(element != null) { c.extra(" ").extra(new Component(new Node(element).describeWithLocation(), ChatColor.DARK_AQUA)); } diff --git a/PGM/src/main/java/tc/oc/pgm/map/MapDefinition.java b/PGM/src/main/java/tc/oc/pgm/map/MapDefinition.java index 5086845..21c295c 100644 --- a/PGM/src/main/java/tc/oc/pgm/map/MapDefinition.java +++ b/PGM/src/main/java/tc/oc/pgm/map/MapDefinition.java @@ -1,6 +1,7 @@ package tc.oc.pgm.map; import java.io.IOException; +import java.lang.ref.SoftReference; import java.nio.file.Path; import java.util.List; import java.util.Map; @@ -40,6 +41,7 @@ public class MapDefinition { @Inject private MapConfiguration configuration; @Inject private Provider contextProvider; + @Inject private Provider persistentContextProvider; @Inject private ExceptionHandler exceptionHandler; @Inject private MapInjectionScope mapInjectionScope; @@ -50,7 +52,8 @@ public class MapDefinition { private final MapFolder folder; - protected @Nullable MapModuleContext context; + protected @Nullable SoftReference context; + private @Nullable MapPersistentContext persistentContext; protected MapDefinition(MapFolder folder) { this.folder = folder; @@ -81,20 +84,29 @@ public class MapDefinition { return context != null; } - public MapModuleContext getContext() { + public Optional getContext() { if(context == null) { throw new IllegalStateException("Map is not loaded: " + this); } - return context; + return Optional.ofNullable(context.get()); + } + + public MapPersistentContext getPersistentContext() { + if(persistentContext == null) { + throw new IllegalStateException("Map is not loaded: " + this); + } + return persistentContext; } public boolean shouldReload() { if(context == null) return true; if(!configuration.autoReload()) return false; - if(context.loadedFiles().isEmpty()) return configuration.reloadWhenError(); + MapModuleContext mapContext = context.get(); + if(mapContext == null) return true; + if(mapContext.loadedFiles().isEmpty()) return configuration.reloadWhenError(); try { - for(Map.Entry loaded : context.loadedFiles().entrySet()) { + for(Map.Entry loaded : mapContext.loadedFiles().entrySet()) { HashCode latest = Files.hash(loaded.getKey().toFile(), Hashing.sha256()); if(!latest.equals(loaded.getValue())) return true; } @@ -116,7 +128,8 @@ public class MapDefinition { }); if(!newContext.hasErrors()) { - this.context = newContext; + this.context = new SoftReference<>(newContext); + this.persistentContext = newContext.asCurrentScope(persistentContextProvider::get); return true; } diff --git a/PGM/src/main/java/tc/oc/pgm/map/MapPersistentContext.java b/PGM/src/main/java/tc/oc/pgm/map/MapPersistentContext.java new file mode 100644 index 0000000..148d573 --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/map/MapPersistentContext.java @@ -0,0 +1,49 @@ +package tc.oc.pgm.map; + +import com.google.common.collect.Range; +import tc.oc.api.docs.SemanticVersion; +import tc.oc.pgm.map.inject.MapScoped; +import tc.oc.pgm.modules.InfoModule; + +import javax.inject.Inject; + +/** + * This is a replacement for classes that need information from MapModuleContext to use. MapModuleContext will not be + * guaranteed to be available if a match isn't being played for the map, but MapPersistentContext will always contain + * basic information required to reference the map (like map name, authors, or player amount). + */ +@MapScoped +public class MapPersistentContext { + + @Inject private @MapProto SemanticVersion proto; + @Inject private InfoModule infoModule; + private Range playerLimits; + private MapDocument apiDocument; + + @Inject public MapPersistentContext(MapModuleContext context) { + this.playerLimits = context.playerLimits(); + this.apiDocument = context.apiDocument(); + } + + public MapDocument apiDocument() { + return apiDocument; + } + + public SemanticVersion getProto() { + return proto; + } + + public Range playerLimits() { + return this.playerLimits; + } + + public Integer playerLimitAverage() { + Range lims = playerLimits(); + return (lims.lowerEndpoint() + lims.upperEndpoint()) / 2; + } + + public InfoModule getInfoModule() { + return infoModule; + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/map/PGMMap.java b/PGM/src/main/java/tc/oc/pgm/map/PGMMap.java index abdd020..f0953ff 100644 --- a/PGM/src/main/java/tc/oc/pgm/map/PGMMap.java +++ b/PGM/src/main/java/tc/oc/pgm/map/PGMMap.java @@ -48,7 +48,7 @@ public class PGMMap extends MapDefinition { } public MapInfo getInfo() { - return getContext().needModule(InfoModule.class).getMapInfo(); + return getPersistentContext().getInfoModule().getMapInfo(); } public MapId getId() { @@ -56,7 +56,7 @@ public class PGMMap extends MapDefinition { } public MapDoc getDocument() { - return getContext().apiDocument(); + return getPersistentContext().apiDocument(); } @Override diff --git a/PGM/src/main/java/tc/oc/pgm/match/MatchLoader.java b/PGM/src/main/java/tc/oc/pgm/match/MatchLoader.java index 75379cf..5205517 100644 --- a/PGM/src/main/java/tc/oc/pgm/match/MatchLoader.java +++ b/PGM/src/main/java/tc/oc/pgm/match/MatchLoader.java @@ -165,7 +165,7 @@ public class MatchLoader implements MatchFinder { * @param map Map to load. */ private Match loadMatch(PGMMap map) throws Throwable { - return map.getContext().asCurrentScope(() -> { + return map.getContext().orElseThrow(MapNotFoundException::new).asCurrentScope(() -> { final WorldManager worldManager = this.worldManager.get(); final String worldName = Match.createSlug(matchCounter.get()); final World world = worldManager.createWorld(worldName); diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/RotationState.java b/PGM/src/main/java/tc/oc/pgm/rotation/RotationState.java index cbea67d..47dcccc 100644 --- a/PGM/src/main/java/tc/oc/pgm/rotation/RotationState.java +++ b/PGM/src/main/java/tc/oc/pgm/rotation/RotationState.java @@ -43,7 +43,7 @@ public final class RotationState { private Lazy averageNeededPlayers = Lazy.from(() -> - (int) getMaps().stream().mapToInt(map -> map.getContext().playerLimitAverage()).average().orElse(0)); + (int) getMaps().stream().mapToInt(map -> map.getPersistentContext().playerLimitAverage()).average().orElse(0)); /** * Gets the approximate number of players supposed to be playing the rotation maps.