Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resource loader cleanup #8561

Merged
merged 8 commits into from
Jan 2, 2021
121 changes: 74 additions & 47 deletions game-core/src/main/java/games/strategy/triplea/ResourceLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.annotation.Nullable;
import javax.imageio.ImageIO;
import lombok.Getter;
Expand All @@ -37,35 +39,78 @@
@Slf4j
public class ResourceLoader implements Closeable {
public static final String ASSETS_FOLDER = "assets";
// All maps must have at least a "baseTiles" folder.
private static final String REQUIRED_ASSET_EXAMPLE_FOLDER = "baseTiles/";

private final URLClassLoader loader;
private final String mapPrefix;
@Getter private final String mapName;

private ResourceLoader(final String mapName, final String[] paths) {
final URL[] urls = new URL[paths.length];
for (int i = 0; i < paths.length; i++) {
final File f = new File(paths[i]);
if (!f.exists()) {
log.error(f + " does not exist");
}
if (!f.isDirectory() && !f.getName().endsWith(".zip")) {
log.error(f + " is not a directory or a zip file");
}
try {
urls[i] = f.toURI().toURL();
} catch (final MalformedURLException e) {
throw new IllegalStateException(e);
}
}
mapPrefix = ResourceLocationTracker.getMapPrefix(urls);
public ResourceLoader(final String mapName) {
Preconditions.checkNotNull(mapName);

final File mapLocation =
getPath(mapName)
.orElseThrow(
() -> {
SwingComponents.promptUser(
"Download Map?",
"Map missing: "
+ mapName
+ ", could not join game.\nWould you like to download the map now?"
+ "\nOnce the download completes, you may reconnect to this game.",
() -> DownloadMapsWindow.showDownloadMapsWindowAndDownload(mapName));

return new MapNotFoundException(mapName, getCandidatePaths(mapName));
});
mapPrefix = getMapPrefix(mapLocation);

// Add the assets folder from the game installation path. This assets folder supplements
// any map resources.
final File gameAssetsDirectory =
findDirectory(ClientFileSystemHelper.getRootFolder(), ASSETS_FOLDER)
.orElseThrow(GameAssetsNotFoundException::new);

// Note: URLClassLoader does not always respect the ordering of the search URLs
// To solve this we will get all matching paths and then filter by what matched
// the assets folder.
loader = new URLClassLoader(urls);
try {
loader =
new URLClassLoader(
new URL[] {mapLocation.toURI().toURL(), gameAssetsDirectory.toURI().toURL()});
} catch (final MalformedURLException e) {
throw new IllegalArgumentException(
"Error creating file system paths with map: "
+ mapName
+ ", engine assets path: "
+ gameAssetsDirectory.getAbsolutePath()
+ ", and path to map: "
+ mapLocation.getAbsolutePath(),
e);
}
this.mapName = mapName;
}

/**
* Will return an empty string unless a special prefix is needed, in which case that prefix is
* constructed based on where the {@code baseTiles} folder is located within the zip.
*/
private static String getMapPrefix(final File mapZip) {
try (ZipFile zip = new ZipFile(mapZip)) {
final Optional<? extends ZipEntry> baseTilesEntry =
zip.stream()
.filter(entry -> entry.getName().endsWith(REQUIRED_ASSET_EXAMPLE_FOLDER))
.findAny();
if (baseTilesEntry.isPresent()) {
final String path = baseTilesEntry.get().getName();
return path.substring(0, path.length() - REQUIRED_ASSET_EXAMPLE_FOLDER.length());
}
} catch (final IOException e) {
// File is not a zip or can't be opened
}
return "";
}

/**
* Loads an image from the 'assets' folder. Images downloaded as part of the build to be included
* with the game are downloaded to this location. Check the gradle build file download images task
Expand All @@ -76,34 +121,19 @@ public static Image loadImageAssert(final Path path) {
}

public static ResourceLoader getGameEngineAssetLoader() {
return getMapResourceLoader("");
return new ResourceLoader("");
}

/** Returns a resource loader that will find assets in a map directory. */
public static ResourceLoader getMapResourceLoader(final String mapName) {
Preconditions.checkNotNull(mapName);
private static class GameAssetsNotFoundException extends RuntimeException {
private static final long serialVersionUID = -8274500540886412040L;

final Optional<String> dir = getPath(mapName);
if (dir.isEmpty()) {
SwingComponents.promptUser(
"Download Map?",
"Map missing: "
+ mapName
+ ", could not join game.\nWould you like to download the map now?"
+ "\nOnce the download completes, you may reconnect to this game.",
() -> DownloadMapsWindow.showDownloadMapsWindowAndDownload(mapName));

throw new MapNotFoundException(mapName, getCandidatePaths(mapName));
GameAssetsNotFoundException() {
super(
"Unable to find game assets folder starting from location: "
+ ClientFileSystemHelper.getRootFolder().getAbsolutePath()
+ "\nThere is a problem with the installation, please report this to TripleA "
+ "and the path where TripleA is installed.");
}

final List<String> dirs = new ArrayList<>();
dirs.add(dir.get());

findDirectory(ClientFileSystemHelper.getRootFolder(), ASSETS_FOLDER)
.map(File::getAbsolutePath)
.ifPresent(dirs::add);

return new ResourceLoader(mapName, dirs.toArray(new String[0]));
}

@VisibleForTesting
Expand Down Expand Up @@ -180,11 +210,8 @@ static String normalizeMapName(final String zipName) {
return sb.toString();
}

private static Optional<String> getPath(final String mapName) {
return getCandidatePaths(mapName).stream()
.filter(File::exists)
.findAny()
.map(File::getAbsolutePath);
private static Optional<File> getPath(final String mapName) {
return getCandidatePaths(mapName).stream().filter(File::exists).findAny();
}

private static List<File> getCandidatePaths(final String mapName) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,14 @@ public class UiContext {
UiContext() {}

public static void setResourceLoader(final GameState gameData) {
resourceLoader = ResourceLoader.getMapResourceLoader(getDefaultMapDir(gameData));
resourceLoader = new ResourceLoader(getDefaultMapDir(gameData));
}

protected void internalSetMapDir(final String dir, final GameData data) {
if (resourceLoader != null) {
resourceLoader.close();
}
resourceLoader = ResourceLoader.getMapResourceLoader(dir);
resourceLoader = new ResourceLoader(dir);
mapData = new MapData(dir);
// DiceImageFactory needs loader and game data
diceImageFactory = new DiceImageFactory(resourceLoader, data.getDiceSides());
Expand Down Expand Up @@ -267,7 +267,7 @@ private static String getDefaultMapDir(final GameState data) {
final String mapDir = prefs.get(MAP_SKIN_PREF, mapName);
// check for existence
try {
ResourceLoader.getMapResourceLoader(mapDir).close();
new ResourceLoader(mapDir).close();
} catch (final RuntimeException re) {
// an error, clear the skin
prefs.remove(MAP_SKIN_PREF);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public class MapData {

public MapData(final String mapNameDir) {
this.mapNameDir = mapNameDir;
try (ResourceLoader loader = ResourceLoader.getMapResourceLoader(mapNameDir)) {
try (ResourceLoader loader = new ResourceLoader(mapNameDir)) {
try {
if (loader.getResource(POLYGON_FILE) == null) {
throw new IllegalStateException(
Expand Down Expand Up @@ -769,7 +769,7 @@ public List<Point> getTerritoryEffectPoints(final Territory territory) {
}

public Optional<Image> getTerritoryEffectImage(final String effectName) {
try (ResourceLoader loader = ResourceLoader.getMapResourceLoader(mapNameDir)) {
try (ResourceLoader loader = new ResourceLoader(mapNameDir)) {
// TODO: what does this cache buy us? should we still keep it?
if (effectImages.get(effectName) != null) {
return Optional.of(effectImages.get(effectName));
Expand Down
2 changes: 1 addition & 1 deletion game-core/src/main/java/org/triplea/util/LocalizeHtml.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public static String localizeImgLinksInHtml(final String htmlText) {
public static String localizeImgLinksInHtml(final String htmlText, final String mapNameDir) {
return htmlText == null || mapNameDir == null || mapNameDir.isBlank()
? htmlText
: localizeImgLinksInHtml(htmlText, ResourceLoader.getMapResourceLoader(mapNameDir));
: localizeImgLinksInHtml(htmlText, new ResourceLoader(mapNameDir));
}

@VisibleForTesting
Expand Down