Make map context a soft reference, and keep a persistent context (#79)

This commit is contained in:
Pablo Herrera 2018-11-21 02:01:22 +01:00 committed by Austin Mayes
parent 1450849c34
commit 6cf7610fad
8 changed files with 83 additions and 26 deletions

View File

@ -174,7 +174,7 @@ public class MapCommands implements Commands {
map = CommandUtils.getMatch(sender).getMap(); map = CommandUtils.getMatch(sender).getMap();
} }
final InfoModule infoModule = map.getContext().needModule(InfoModule.class); final InfoModule infoModule = map.getPersistentContext().getInfoModule();
final MapInfo mapInfo = infoModule.getMapInfo(); final MapInfo mapInfo = infoModule.getMapInfo();
audience.sendMessage(mapInfo.getFormattedMapTitle()); audience.sendMessage(mapInfo.getFormattedMapTitle());
@ -243,16 +243,7 @@ public class MapCommands implements Commands {
} }
} }
int maxPlayers = map.getContext() int maxPlayers = map.getPersistentContext().playerLimits().upperEndpoint();
.features()
.all(TeamFactory.class)
.mapToInt(TeamFactory::getMaxPlayers)
.sum();
FreeForAllModule ffam = map.getContext().getModule(FreeForAllModule.class);
if(ffam != null) {
maxPlayers += ffam.getOptions().maxPlayers;
}
audience.sendMessage(new Component( audience.sendMessage(new Component(
mapInfoLabel("command.map.mapInfo.playerLimit"), mapInfoLabel("command.map.mapInfo.playerLimit"),
@ -261,7 +252,7 @@ public class MapCommands implements Commands {
if(sender.hasPermission(Permissions.MAPDEV)) { 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.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.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))); audience.sendMessage(new Component(mapInfoLabel("command.map.mapInfo.source"), new Component(map.getSource().getPath().toString(), ChatColor.GOLD)));
} }

View File

@ -120,11 +120,11 @@ public class CycleMatchModule extends MatchModule implements Listener {
} }
@Override public int min_players() { @Override public int min_players() {
return nextMap.getContext().playerLimits().lowerEndpoint(); return nextMap.getPersistentContext().playerLimits().lowerEndpoint();
} }
@Override public int max_players() { @Override public int max_players() {
return nextMap.getContext().playerLimits().upperEndpoint(); return nextMap.getPersistentContext().playerLimits().upperEndpoint();
} }
} }
), ),

View File

@ -59,6 +59,7 @@ import tc.oc.pgm.map.MapConfiguration;
import tc.oc.pgm.map.MapDefinition; import tc.oc.pgm.map.MapDefinition;
import tc.oc.pgm.map.MapLibrary; import tc.oc.pgm.map.MapLibrary;
import tc.oc.pgm.map.MapLogRecord; import tc.oc.pgm.map.MapLogRecord;
import tc.oc.pgm.map.MapModuleContext;
import tc.oc.pgm.map.MapNotFoundException; import tc.oc.pgm.map.MapNotFoundException;
import tc.oc.pgm.map.PGMMap; import tc.oc.pgm.map.PGMMap;
import tc.oc.pgm.match.Match; import tc.oc.pgm.match.Match;
@ -205,7 +206,10 @@ public class MapDevelopmentCommands implements Commands {
final Optional<String> typeFilter = CommandUtils.flag(args, 't').map(String::toLowerCase); final Optional<String> typeFilter = CommandUtils.flag(args, 't').map(String::toLowerCase);
final Optional<String> idFilter = CommandUtils.flag(args, 'i').map(String::toLowerCase); final Optional<String> idFilter = CommandUtils.flag(args, 'i').map(String::toLowerCase);
Stream<? extends FeatureDefinition> features = map.getContext().features().all(); MapModuleContext context = map.getContext().orElseThrow(() ->
new IllegalStateException("The map modules are currently unloaded."));
Stream<? extends FeatureDefinition> features = context.features().all();
if(typeFilter.isPresent()) { if(typeFilter.isPresent()) {
features = features.filter(f -> f.inspectType().toLowerCase().contains(typeFilter.get())); 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))); feature.inspectIdentity().ifPresent(id -> c.extra(" ").extra(new Component(id, ChatColor.YELLOW)));
if(locate) { if(locate) {
final Element element = map.getContext().features().definitionNode(feature); final Element element = context.features().definitionNode(feature);
if(element != null) { if(element != null) {
c.extra(" ").extra(new Component(new Node(element).describeWithLocation(), ChatColor.DARK_AQUA)); c.extra(" ").extra(new Component(new Node(element).describeWithLocation(), ChatColor.DARK_AQUA));
} }

View File

@ -1,6 +1,7 @@
package tc.oc.pgm.map; package tc.oc.pgm.map;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.SoftReference;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -40,6 +41,7 @@ public class MapDefinition {
@Inject private MapConfiguration configuration; @Inject private MapConfiguration configuration;
@Inject private Provider<MapModuleContext> contextProvider; @Inject private Provider<MapModuleContext> contextProvider;
@Inject private Provider<MapPersistentContext> persistentContextProvider;
@Inject private ExceptionHandler exceptionHandler; @Inject private ExceptionHandler exceptionHandler;
@Inject private MapInjectionScope mapInjectionScope; @Inject private MapInjectionScope mapInjectionScope;
@ -50,7 +52,8 @@ public class MapDefinition {
private final MapFolder folder; private final MapFolder folder;
protected @Nullable MapModuleContext context; protected @Nullable SoftReference<MapModuleContext> context;
private @Nullable MapPersistentContext persistentContext;
protected MapDefinition(MapFolder folder) { protected MapDefinition(MapFolder folder) {
this.folder = folder; this.folder = folder;
@ -81,20 +84,29 @@ public class MapDefinition {
return context != null; return context != null;
} }
public MapModuleContext getContext() { public Optional<MapModuleContext> getContext() {
if(context == null) { if(context == null) {
throw new IllegalStateException("Map is not loaded: " + this); 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() { public boolean shouldReload() {
if(context == null) return true; if(context == null) return true;
if(!configuration.autoReload()) return false; 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 { try {
for(Map.Entry<Path, HashCode> loaded : context.loadedFiles().entrySet()) { for(Map.Entry<Path, HashCode> loaded : mapContext.loadedFiles().entrySet()) {
HashCode latest = Files.hash(loaded.getKey().toFile(), Hashing.sha256()); HashCode latest = Files.hash(loaded.getKey().toFile(), Hashing.sha256());
if(!latest.equals(loaded.getValue())) return true; if(!latest.equals(loaded.getValue())) return true;
} }
@ -116,7 +128,8 @@ public class MapDefinition {
}); });
if(!newContext.hasErrors()) { if(!newContext.hasErrors()) {
this.context = newContext; this.context = new SoftReference<>(newContext);
this.persistentContext = newContext.asCurrentScope(persistentContextProvider::get);
return true; return true;
} }

View File

@ -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<Integer> 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<Integer> playerLimits() {
return this.playerLimits;
}
public Integer playerLimitAverage() {
Range<Integer> lims = playerLimits();
return (lims.lowerEndpoint() + lims.upperEndpoint()) / 2;
}
public InfoModule getInfoModule() {
return infoModule;
}
}

View File

@ -48,7 +48,7 @@ public class PGMMap extends MapDefinition {
} }
public MapInfo getInfo() { public MapInfo getInfo() {
return getContext().needModule(InfoModule.class).getMapInfo(); return getPersistentContext().getInfoModule().getMapInfo();
} }
public MapId getId() { public MapId getId() {
@ -56,7 +56,7 @@ public class PGMMap extends MapDefinition {
} }
public MapDoc getDocument() { public MapDoc getDocument() {
return getContext().apiDocument(); return getPersistentContext().apiDocument();
} }
@Override @Override

View File

@ -165,7 +165,7 @@ public class MatchLoader implements MatchFinder {
* @param map Map to load. * @param map Map to load.
*/ */
private Match loadMatch(PGMMap map) throws Throwable { 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 WorldManager worldManager = this.worldManager.get();
final String worldName = Match.createSlug(matchCounter.get()); final String worldName = Match.createSlug(matchCounter.get());
final World world = worldManager.createWorld(worldName); final World world = worldManager.createWorld(worldName);

View File

@ -43,7 +43,7 @@ public final class RotationState {
private Lazy<Integer> averageNeededPlayers = Lazy.from(() -> private Lazy<Integer> 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. * Gets the approximate number of players supposed to be playing the rotation maps.