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();
}
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)));
}

View File

@ -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();
}
}
),

View File

@ -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<String> typeFilter = CommandUtils.flag(args, 't').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()) {
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));
}

View File

@ -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<MapModuleContext> contextProvider;
@Inject private Provider<MapPersistentContext> 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<MapModuleContext> 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<MapModuleContext> 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<Path, HashCode> loaded : context.loadedFiles().entrySet()) {
for(Map.Entry<Path, HashCode> 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;
}

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() {
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

View File

@ -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);

View File

@ -43,7 +43,7 @@ public final class RotationState {
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.