chore(GameIdentifier): remove engineVersion; rename String version to displayVersion (#655)
parent
4327450c1e
commit
d40284dd64
|
@ -18,18 +18,14 @@ import org.terasology.launcher.util.DownloadUtils;
|
|||
import org.terasology.launcher.util.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class GameManager {
|
||||
|
@ -55,7 +51,7 @@ public class GameManager {
|
|||
private String getFileNameFor(GameRelease release) {
|
||||
GameIdentifier id = release.getId();
|
||||
String profileString = id.getProfile().toString().toLowerCase();
|
||||
String versionString = id.getVersion();
|
||||
String versionString = id.getDisplayVersion();
|
||||
String buildString = id.getBuild().toString().toLowerCase();
|
||||
return "terasology-" + profileString + "-" + versionString + "-" + buildString + ".zip";
|
||||
}
|
||||
|
@ -129,7 +125,7 @@ public class GameManager {
|
|||
}
|
||||
|
||||
public Path getInstallDirectory(GameIdentifier id) {
|
||||
return installDirectory.resolve(id.getProfile().name()).resolve(id.getBuild().name()).resolve(id.getVersion());
|
||||
return installDirectory.resolve(id.getProfile().name()).resolve(id.getBuild().name()).resolve(id.getDisplayVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -164,85 +160,6 @@ public class GameManager {
|
|||
logger.debug("Directory does not match expected profile/build names: {}", versionDirectory, e);
|
||||
return null;
|
||||
}
|
||||
return getInstalledVersion(profile, build, versionDirectory);
|
||||
}
|
||||
|
||||
private static GameIdentifier getInstalledVersion(Profile profile, Build build, Path versionDirectory) {
|
||||
Path engineJar;
|
||||
|
||||
try (var jarMatches = Files.find(versionDirectory, 3, GameManager::matchEngineJar)) {
|
||||
var matches = jarMatches.collect(Collectors.toUnmodifiableSet());
|
||||
if (matches.isEmpty()) {
|
||||
logger.warn("Could not find engine jar in {}", versionDirectory);
|
||||
return null;
|
||||
} else if (matches.size() > 1) {
|
||||
logger.warn("Ambiguous results while looking for engine jar in {}: {}", versionDirectory, matches);
|
||||
return null;
|
||||
}
|
||||
engineJar = matches.iterator().next();
|
||||
} catch (IOException e) {
|
||||
logger.error("Error while looking for engine jar in {}", versionDirectory, e);
|
||||
return null;
|
||||
}
|
||||
|
||||
Properties versionInfo;
|
||||
try {
|
||||
versionInfo = getVersionPropertiesFromJar(engineJar);
|
||||
} catch (IOException e) {
|
||||
logger.error("Error while looking for version in {}.", engineJar, e);
|
||||
return null;
|
||||
}
|
||||
|
||||
return GameIdentifier.fromVersionInfo(versionInfo, build, profile).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link java.util.function.BiPredicate BiPredicate} matcher function for the Terasology Engine JAR file.
|
||||
* <p>
|
||||
* The Terasology Engine JAR file needs to be located within a {@code lib} or {@code libs} directory.
|
||||
* Its file name must be of the form {@code engine*.jar}.
|
||||
*
|
||||
* @param path the path of the file to match
|
||||
* @param basicFileAttributes the file attributes of the file to match
|
||||
* @return true iff the file matches the expected pattern for Terasology's engine JAR
|
||||
*/
|
||||
private static boolean matchEngineJar(Path path, BasicFileAttributes basicFileAttributes) {
|
||||
// Are there path-matching utilities to simplify this?
|
||||
final var libPaths = Set.of(Path.of("lib"), Path.of("libs"));
|
||||
|
||||
var parent = path.getParent();
|
||||
var file = path.getFileName().toString();
|
||||
return Files.isDirectory(parent)
|
||||
&& libPaths.contains(parent.getFileName())
|
||||
&& file.endsWith(".jar")
|
||||
&& file.startsWith("engine");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve any {@code versionInfo.properties} from the given JAR file if it exists.
|
||||
* <p>
|
||||
* This method assumes that there is exactly one file named {@code versionInfo.properties} in the JAR.
|
||||
* Note, that this will try to parse <b>any</b> file matching the naming pattern into a {@link Properties} object
|
||||
* and return it.
|
||||
*
|
||||
* @param jarLocation the path to the JAR file containing a version info file
|
||||
* @return the version info properties object
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws FileNotFoundException if the version info file could not be found in the JAR file
|
||||
*/
|
||||
private static Properties getVersionPropertiesFromJar(Path jarLocation) throws IOException {
|
||||
try (var jar = new JarFile(jarLocation.toFile())) {
|
||||
var versionEntry = jar.stream().filter(entry ->
|
||||
entry.getName().endsWith("versionInfo.properties") // FIXME: use const
|
||||
).findAny();
|
||||
if (versionEntry.isEmpty()) {
|
||||
throw new FileNotFoundException("Found no versionInfo.properties in " + jarLocation);
|
||||
}
|
||||
var properties = new Properties();
|
||||
try (var input = jar.getInputStream(versionEntry.get())) {
|
||||
properties.load(input);
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
return new GameIdentifier(versionDirectory.getFileName().toString(), build, profile);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,6 @@ public enum VersionHistory {
|
|||
}
|
||||
|
||||
boolean isProvidedBy(GameIdentifier version) {
|
||||
return isProvidedBy(version.getEngineVersion());
|
||||
return isProvidedBy(version.getVersion()); // FIXME ASAP: obsoleted by PR#654
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,11 @@ package org.terasology.launcher.model;
|
|||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.vdurmont.semver4j.Semver;
|
||||
import com.vdurmont.semver4j.SemverException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Uniquely identify a game release.
|
||||
|
@ -28,70 +27,27 @@ public class GameIdentifier {
|
|||
|
||||
private static final Logger logger = LoggerFactory.getLogger(GameIdentifier.class);
|
||||
|
||||
final String version;
|
||||
final Semver engineVersion;
|
||||
final String displayVersion;
|
||||
final Build build;
|
||||
final Profile profile;
|
||||
|
||||
public GameIdentifier(String version, Semver engineVersion, Build build, Profile profile) {
|
||||
this.version = version;
|
||||
this.engineVersion = engineVersion;
|
||||
public GameIdentifier(String displayVersion, Build build, Profile profile) {
|
||||
this.displayVersion = displayVersion;
|
||||
this.build = build;
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
public GameIdentifier(String version, String engineVersion, Build build, Profile profile) {
|
||||
this(version, new Semver(engineVersion, Semver.SemverType.IVY), build, profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to create a game identifier for a specific {@link Build} and {@link Profile} from a version info file.
|
||||
* <p>
|
||||
* The version info properties
|
||||
* <ul>
|
||||
* <li> MUST contain a {@code displayVersion} entry (any string)
|
||||
* <li> MUST contain a {@code engineVersion} entry holding a valid SemVer
|
||||
* <li> MAY contain a {@code buildNumber}
|
||||
* </ul>
|
||||
* Other properties are ignored.
|
||||
* <p>
|
||||
* If the version info properties contain a non-empty {@code buildNumber} it is appended to the
|
||||
* {@code displayVersion} to form the {@link GameIdentifier#getVersion()}.
|
||||
*
|
||||
* @param versionInfo the Terasology distribution version info; must contain a {@code displayVersion} (any string)
|
||||
* and a valid SemVer in {@code engineVersion}
|
||||
* @param build the build variant of the game release
|
||||
* @param profile the build profile of the game release
|
||||
* @return Some identifier if the version info meets the requirements; empty otherwise
|
||||
*/
|
||||
public static Optional<GameIdentifier> fromVersionInfo(Properties versionInfo, Build build, Profile profile) {
|
||||
var displayVersion = versionInfo.getProperty("displayVersion");
|
||||
if (displayVersion == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
// Append the build number to the display version to ensure that the version string is unique for two different
|
||||
// builds. This is necessary because the display version generated by the CI build is the same of subsequent
|
||||
// builds...
|
||||
var buildNumber = versionInfo.getProperty("buildNumber");
|
||||
if (buildNumber != null && !buildNumber.isEmpty()) {
|
||||
displayVersion += "+" + buildNumber;
|
||||
}
|
||||
public Semver getVersion() {
|
||||
// compatibility shim until we have GameIdentifier.version
|
||||
try {
|
||||
Semver engineVersion = new Semver(versionInfo.getProperty("engineVersion"), Semver.SemverType.IVY);
|
||||
return Optional.of(new GameIdentifier(displayVersion, engineVersion, build, profile));
|
||||
} catch (RuntimeException e) {
|
||||
logger.warn("versionInfo.properties displayVersion=\"{}\" engineVersion=\"{}\" " +
|
||||
"is not a valid version.", displayVersion, versionInfo.getProperty("engineVersion"), e);
|
||||
return Optional.empty();
|
||||
return new Semver(displayVersion, Semver.SemverType.LOOSE);
|
||||
} catch (SemverException e) {
|
||||
return new Semver("0.0.1-" + displayVersion, Semver.SemverType.LOOSE); // FIXME ASAP
|
||||
}
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public Semver getEngineVersion() {
|
||||
return engineVersion;
|
||||
public String getDisplayVersion() {
|
||||
return displayVersion;
|
||||
}
|
||||
|
||||
public Build getBuild() {
|
||||
|
@ -111,22 +67,20 @@ public class GameIdentifier {
|
|||
return false;
|
||||
}
|
||||
GameIdentifier that = (GameIdentifier) o;
|
||||
return version.equals(that.version)
|
||||
&& engineVersion.equals(that.engineVersion)
|
||||
return displayVersion.equals(that.displayVersion)
|
||||
&& build == that.build
|
||||
&& profile == that.profile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(version, build, profile, engineVersion);
|
||||
return Objects.hash(displayVersion, build, profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("version", version)
|
||||
.add("engineVersion", engineVersion)
|
||||
.add("version", displayVersion)
|
||||
.add("build", build)
|
||||
.add("profile", profile)
|
||||
.toString();
|
||||
|
|
|
@ -56,7 +56,7 @@ public class GithubRepositoryAdapter implements ReleaseRepository {
|
|||
final URL url = new URL(gameAsset.map(GHAsset::getBrowserDownloadUrl).orElseThrow(() -> new IOException("Missing game asset.")));
|
||||
|
||||
final String changelog = ghRelease.getBody();
|
||||
GameIdentifier id = new GameIdentifier(engineVersion.toString(), engineVersion, build, profile);
|
||||
GameIdentifier id = new GameIdentifier(engineVersion.toString(), build, profile);
|
||||
|
||||
ReleaseMetadata metadata = new ReleaseMetadata(changelog, ghRelease.getPublished_at());
|
||||
return new GameRelease(id, url, metadata);
|
||||
|
|
|
@ -96,7 +96,18 @@ class JenkinsRepositoryAdapter implements ReleaseRepository {
|
|||
private Optional<GameIdentifier> computeIdentifierFrom(Jenkins.Build jenkinsBuildInfo) {
|
||||
return Optional.ofNullable(client.getArtifactUrl(jenkinsBuildInfo, "versionInfo.properties"))
|
||||
.map(client::requestProperties)
|
||||
.flatMap(versionInfo -> GameIdentifier.fromVersionInfo(versionInfo, buildProfile, profile));
|
||||
.map(versionInfo -> versionInfo.getProperty("displayVersion"))
|
||||
.map(displayVersion -> {
|
||||
// versionInfo.properties is created during the Engine build.
|
||||
// jenkinsBuildInfo is the build of a Distribution.
|
||||
//
|
||||
// There may be multiple Distribution builds that come from the same Engine build.
|
||||
//
|
||||
// We can use the Engine's displayVersion, but we use the Distribution build number
|
||||
// to ensure uniqueness.
|
||||
String versionString = displayVersion + "+" + jenkinsBuildInfo.number;
|
||||
return new GameIdentifier(versionString, buildProfile, profile);
|
||||
});
|
||||
}
|
||||
|
||||
private ReleaseMetadata computeReleaseMetadataFrom(Jenkins.Build jenkinsBuildInfo) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020 The Terasology Foundation
|
||||
// Copyright 2021 The Terasology Foundation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.terasology.launcher.ui;
|
||||
|
@ -50,10 +50,10 @@ final class GameReleaseCell extends ListCell<GameRelease> {
|
|||
String displayVersion;
|
||||
if (id.getBuild().equals(Build.NIGHTLY)) {
|
||||
setStyle("-fx-font-weight: normal");
|
||||
displayVersion = "preview " + id.getVersion() + " (" + DATE_FORMAT.format(item.getTimestamp()) + ")";
|
||||
displayVersion = "preview " + id.getDisplayVersion() + " (" + DATE_FORMAT.format(item.getTimestamp()) + ")";
|
||||
} else {
|
||||
setStyle("-fx-font-weight: bold");
|
||||
displayVersion = "release " + id.getVersion();
|
||||
displayVersion = "release " + id.getDisplayVersion();
|
||||
}
|
||||
|
||||
setText(displayVersion);
|
||||
|
|
|
@ -61,7 +61,7 @@ public class TestGameStarter {
|
|||
GameRelease release;
|
||||
try {
|
||||
release = new GameRelease(
|
||||
new GameIdentifier("alpha-20", "5.1.0", Build.STABLE, Profile.OMEGA),
|
||||
new GameIdentifier("alpha-20", Build.STABLE, Profile.OMEGA),
|
||||
new URL("https://repository.example"),
|
||||
new ReleaseMetadata(
|
||||
"# CHANGES",
|
||||
|
|
|
@ -89,7 +89,7 @@ class JenkinsRepositoryAdapterTest {
|
|||
// is unique for two different builds. This is necessary because the display version generated by the CI build
|
||||
// is the same of subsequent builds...
|
||||
final String expectedVersion = displayVersion + "+" + validResult.builds[0].number;
|
||||
final GameIdentifier id = new GameIdentifier(expectedVersion, engineVersion, Build.STABLE, Profile.OMEGA);
|
||||
final GameIdentifier id = new GameIdentifier(expectedVersion, Build.STABLE, Profile.OMEGA);
|
||||
final ReleaseMetadata releaseMetadata = new ReleaseMetadata("", new Date(1604285977306L));
|
||||
final GameRelease expected = new GameRelease(id, expectedArtifactUrl, releaseMetadata);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020 The Terasology Foundation
|
||||
// Copyright 2021 The Terasology Foundation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.terasology.launcher.settings;
|
||||
|
@ -157,11 +157,9 @@ class TestLauncherSettings {
|
|||
@Test
|
||||
void canRecognizeLastGamePlayed() {
|
||||
final var displayVersion = "alpha-20";
|
||||
final var engineVersion = "5.0.1-SNAPSHOT";
|
||||
GameIdentifier id = new GameIdentifier(displayVersion, engineVersion, Build.NIGHTLY, Profile.OMEGA);
|
||||
GameIdentifier id = new GameIdentifier(displayVersion, Build.NIGHTLY, Profile.OMEGA);
|
||||
// Second object so as not to rely on instance identity.
|
||||
GameIdentifier expectedId = new GameIdentifier(displayVersion, engineVersion,
|
||||
Build.NIGHTLY, Profile.OMEGA);
|
||||
GameIdentifier expectedId = new GameIdentifier(displayVersion, Build.NIGHTLY, Profile.OMEGA);
|
||||
|
||||
baseLauncherSettings.setLastPlayedGameVersion(id);
|
||||
assertEquals(Optional.of(expectedId), baseLauncherSettings.getLastPlayedGameVersion());
|
||||
|
|
Loading…
Reference in New Issue