feat: update command line options for Terasology (#646)
* feat: update command line options for Terasology to use POSIX-style syntax for https://github.com/MovingBlocks/Terasology/pull/4157 * refactor(GameStarter): put platform-specific launch parameters here * fix(VersionHistory): picocli begins with 5.2.0-SNAPSHOTmaster
parent
e1e530f3fa
commit
4327450c1e
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020 The Terasology Foundation
|
||||
// Copyright 2021 The Terasology Foundation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.terasology.launcher.game;
|
||||
|
@ -9,6 +9,7 @@ import javafx.concurrent.Service;
|
|||
import javafx.concurrent.Worker;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.terasology.launcher.model.GameRelease;
|
||||
import org.terasology.launcher.settings.LauncherSettings;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
@ -20,7 +21,7 @@ import static com.google.common.base.Verify.verifyNotNull;
|
|||
/**
|
||||
* This service starts and monitors the game process.
|
||||
* <p>
|
||||
* Its {@linkplain #GameService() constructor} requires no arguments. Use {@link #start(Path, LauncherSettings, List, List)} to
|
||||
* Its {@linkplain #GameService() constructor} requires no arguments. Use {@link #start(GameRelease, Path, LauncherSettings)} to
|
||||
* start the game process; the zero-argument form of {@code start()} will not have enough information.
|
||||
* <p>
|
||||
* The Boolean value of this service is true when it believes the game process has started <em>successfully.</em>
|
||||
|
@ -48,8 +49,7 @@ public class GameService extends Service<Boolean> {
|
|||
|
||||
private Path gamePath;
|
||||
private LauncherSettings settings;
|
||||
private List<String> additionalJavaParameters;
|
||||
private List<String> additionalGameParameters;
|
||||
private GameRelease release;
|
||||
|
||||
public GameService() {
|
||||
setExecutor(Executors.newSingleThreadExecutor(
|
||||
|
@ -63,24 +63,22 @@ public class GameService extends Service<Boolean> {
|
|||
|
||||
/**
|
||||
* Start a new game process with these settings.
|
||||
* @param release the version of the game being run
|
||||
* @param gamePath the directory under which we will find libs/Terasology.jar, also used as the process's
|
||||
* working directory
|
||||
* @param settings supplies other settings relevant to configuring a process
|
||||
* @param additionalJavaParameters
|
||||
* @param additionalGameParameters
|
||||
*/
|
||||
@SuppressWarnings("checkstyle:HiddenField")
|
||||
public void start(Path gamePath, LauncherSettings settings, List<String> additionalJavaParameters, List<String> additionalGameParameters) {
|
||||
public void start(GameRelease release, Path gamePath, LauncherSettings settings) {
|
||||
this.release = release;
|
||||
this.gamePath = gamePath;
|
||||
this.settings = settings;
|
||||
this.additionalJavaParameters = additionalJavaParameters;
|
||||
this.additionalGameParameters = additionalGameParameters;
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use {@link #start(Path, LauncherSettings, List, List)} instead.
|
||||
* Use {@link #start(GameRelease, Path, LauncherSettings)} instead.
|
||||
* <p>
|
||||
* It is an error to call this method before providing the configuration.
|
||||
*/
|
||||
|
@ -130,13 +128,11 @@ public class GameService extends Service<Boolean> {
|
|||
|
||||
final List<String> javaParameters = Lists.newArrayList();
|
||||
javaParameters.addAll(settings.getJavaParameterList());
|
||||
javaParameters.addAll(additionalJavaParameters);
|
||||
|
||||
final List<String> gameParameters = Lists.newArrayList();
|
||||
gameParameters.addAll(settings.getUserGameParameterList());
|
||||
gameParameters.addAll(additionalGameParameters);
|
||||
|
||||
var starter = new GameStarter(verifyNotNull(gamePath), settings.getGameDataDirectory(),
|
||||
var starter = new GameStarter(release, verifyNotNull(gamePath), settings.getGameDataDirectory(),
|
||||
settings.getMaxHeapSize(), settings.getInitialHeapSize(),
|
||||
javaParameters, gameParameters,
|
||||
settings.getLogLevel());
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020 The Terasology Foundation
|
||||
// Copyright 2021 The Terasology Foundation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.terasology.launcher.game;
|
||||
|
@ -6,7 +6,9 @@ package org.terasology.launcher.game;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.event.Level;
|
||||
import org.terasology.launcher.model.GameRelease;
|
||||
import org.terasology.launcher.util.JavaHeapSize;
|
||||
import org.terasology.launcher.util.Platform;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
@ -24,8 +26,10 @@ class GameStarter implements Callable<Process> {
|
|||
private static final Logger logger = LoggerFactory.getLogger(GameStarter.class);
|
||||
|
||||
final ProcessBuilder processBuilder;
|
||||
final GameRelease release;
|
||||
|
||||
/**
|
||||
* @param release the version of the game being run
|
||||
* @param gamePath the directory under which we will find {@code libs/Terasology.jar}, also used as the process's
|
||||
* working directory
|
||||
* @param gameDataDirectory {@code -homedir}, the directory where Terasology's data files (saves & etc) are kept
|
||||
|
@ -35,9 +39,13 @@ class GameStarter implements Callable<Process> {
|
|||
* @param gameParams additional arguments for the Terasology command line
|
||||
* @param logLevel the minimum level of log events Terasology will include on its output stream to us
|
||||
*/
|
||||
GameStarter(Path gamePath, Path gameDataDirectory, JavaHeapSize heapMin, JavaHeapSize heapMax, List<String> javaParams, List<String> gameParams,
|
||||
GameStarter(GameRelease release, Path gamePath, Path gameDataDirectory, JavaHeapSize heapMin, JavaHeapSize heapMax, List<String> javaParams, List<String> gameParams,
|
||||
Level logLevel) {
|
||||
this.release = release;
|
||||
|
||||
final boolean isMac = Platform.getPlatform().isMac();
|
||||
final List<String> processParameters = new ArrayList<>();
|
||||
|
||||
processParameters.add(getRuntimePath().toString());
|
||||
|
||||
if (heapMin.isUsed()) {
|
||||
|
@ -47,13 +55,28 @@ class GameStarter implements Callable<Process> {
|
|||
processParameters.add("-Xmx" + heapMax.getSizeParameter());
|
||||
}
|
||||
processParameters.add("-DlogOverrideLevel=" + logLevel.name());
|
||||
|
||||
if (isMac && VersionHistory.LWJGL3.isProvidedBy(release.getId())) {
|
||||
processParameters.add("-XstartOnFirstThread"); // lwjgl3 requires this on OS X
|
||||
// awt didn't work either, but maybe fixed on newer versions?
|
||||
// https://github.com/LWJGLX/lwjgl3-awt/issues/1
|
||||
processParameters.add("-Djava.awt.headless=true");
|
||||
}
|
||||
|
||||
processParameters.addAll(javaParams);
|
||||
|
||||
processParameters.add("-jar");
|
||||
processParameters.add(gamePath.resolve(Path.of("libs", "Terasology.jar")).toString());
|
||||
processParameters.add("-homedir=" + gameDataDirectory.toAbsolutePath().toString());
|
||||
|
||||
// Parameters after this are for the game facade, not the java runtime.
|
||||
processParameters.add(homeDirParameter(gameDataDirectory));
|
||||
processParameters.addAll(gameParams);
|
||||
|
||||
if (isMac) {
|
||||
// splash screen uses awt, so no awt => no splash
|
||||
processParameters.add(noSplashParameter());
|
||||
}
|
||||
|
||||
processBuilder = new ProcessBuilder(processParameters)
|
||||
.directory(gamePath.toFile())
|
||||
.redirectErrorStream(true);
|
||||
|
@ -77,4 +100,20 @@ class GameStarter implements Callable<Process> {
|
|||
Path getRuntimePath() {
|
||||
return Paths.get(System.getProperty("java.home"), "bin", "java");
|
||||
}
|
||||
|
||||
String homeDirParameter(Path gameDataDirectory) {
|
||||
if (terasologyUsesPosixOptions()) {
|
||||
return "--homedir=" + gameDataDirectory.toAbsolutePath();
|
||||
} else {
|
||||
return "-homedir=" + gameDataDirectory.toAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
String noSplashParameter() {
|
||||
return terasologyUsesPosixOptions() ? "--no-splash" : "-noSplash";
|
||||
}
|
||||
|
||||
boolean terasologyUsesPosixOptions() {
|
||||
return VersionHistory.PICOCLI.isProvidedBy(release.getId());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2021 The Terasology Foundation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.terasology.launcher.game;
|
||||
|
||||
import com.vdurmont.semver4j.Semver;
|
||||
import org.terasology.launcher.model.GameIdentifier;
|
||||
|
||||
/**
|
||||
* Terasology versions which introduce specific features.
|
||||
* <p>
|
||||
* These features change the way Launcher must interact with Terasology.
|
||||
*/
|
||||
public enum VersionHistory {
|
||||
/**
|
||||
* The preview release of v4.1.0-rc.1 is the first release with LWJGL v3.
|
||||
* See https://github.com/MovingBlocks/Terasology/releases/tag/v4.1.0-rc.1
|
||||
*/
|
||||
LWJGL3("4.1.0-rc.1"),
|
||||
PICOCLI("5.2.0-SNAPSHOT");
|
||||
|
||||
public final Semver engineVersion;
|
||||
|
||||
VersionHistory(String s) {
|
||||
engineVersion = new Semver(s, Semver.SemverType.IVY);
|
||||
}
|
||||
|
||||
boolean isProvidedBy(Semver version) {
|
||||
return version.isGreaterThanOrEqualTo(engineVersion);
|
||||
}
|
||||
|
||||
boolean isProvidedBy(GameIdentifier version) {
|
||||
return isProvidedBy(version.getEngineVersion());
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020 The Terasology Foundation
|
||||
// Copyright 2021 The Terasology Foundation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.terasology.launcher.model;
|
||||
|
@ -45,8 +45,4 @@ public class GameRelease {
|
|||
public Date getTimestamp() {
|
||||
return releaseMetadata.getTimestamp();
|
||||
}
|
||||
|
||||
public boolean isLwjgl3() {
|
||||
return releaseMetadata.isLwjgl3();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,18 +8,16 @@ import java.util.Date;
|
|||
/**
|
||||
* Data container for metadata associated with a game release.
|
||||
*
|
||||
* The metadata in this class is either relevant for displaying more information to the user (e.g., {@code changelog},
|
||||
* {@code timestamp}) or for managing and starting the game itself (e.g., {@code isLwjgl3}).
|
||||
* The metadata in this class is relevant for displaying more information to the user, e.g., {@code changelog},
|
||||
* {@code timestamp}.
|
||||
*/
|
||||
public class ReleaseMetadata {
|
||||
private final String changelog;
|
||||
private final Date timestamp;
|
||||
private final boolean isLwjgl3;
|
||||
|
||||
public ReleaseMetadata(String changelog, Date timestamp, boolean isLwjgl3) {
|
||||
public ReleaseMetadata(String changelog, Date timestamp) {
|
||||
this.changelog = changelog;
|
||||
this.timestamp = timestamp;
|
||||
this.isLwjgl3 = isLwjgl3;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -35,11 +33,4 @@ public class ReleaseMetadata {
|
|||
public Date getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this release uses LWJGL v3 or not.
|
||||
*/
|
||||
public boolean isLwjgl3() {
|
||||
return isLwjgl3;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,12 +29,6 @@ public class GithubRepositoryAdapter implements ReleaseRepository {
|
|||
|
||||
private static final Logger logger = LoggerFactory.getLogger(GithubRepositoryAdapter.class);
|
||||
|
||||
/**
|
||||
* The preview release of v4.1.0-rc.1 is the first release with LWJGL v3.
|
||||
* See https://github.com/MovingBlocks/Terasology/releases/tag/v4.1.0-rc.1
|
||||
*/
|
||||
private static final Semver FIRST_LWJGL3_RELEASE = new Semver("4.1.0-rc.1");
|
||||
|
||||
private GitHub github;
|
||||
|
||||
public GithubRepositoryAdapter() {
|
||||
|
@ -64,8 +58,7 @@ public class GithubRepositoryAdapter implements ReleaseRepository {
|
|||
final String changelog = ghRelease.getBody();
|
||||
GameIdentifier id = new GameIdentifier(engineVersion.toString(), engineVersion, build, profile);
|
||||
|
||||
boolean isLwjgl3 = engineVersion.isGreaterThanOrEqualTo(FIRST_LWJGL3_RELEASE);
|
||||
ReleaseMetadata metadata = new ReleaseMetadata(changelog, ghRelease.getPublished_at(), isLwjgl3);
|
||||
ReleaseMetadata metadata = new ReleaseMetadata(changelog, ghRelease.getPublished_at());
|
||||
return new GameRelease(id, url, metadata);
|
||||
} catch (SemverException | IOException e) {
|
||||
logger.info("Could not create game release from Github release {}: {}", ghRelease.getHtmlUrl(), e.getMessage());
|
||||
|
|
|
@ -103,7 +103,7 @@ class JenkinsRepositoryAdapter implements ReleaseRepository {
|
|||
String changelog = computeChangelogFrom(jenkinsBuildInfo.changeSet);
|
||||
final Date timestamp = new Date(jenkinsBuildInfo.timestamp);
|
||||
// all builds from this Jenkins are using LWJGL v3
|
||||
return new ReleaseMetadata(changelog, timestamp, true);
|
||||
return new ReleaseMetadata(changelog, timestamp);
|
||||
}
|
||||
|
||||
private String computeChangelogFrom(Jenkins.ChangeSet changeSet) {
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
// Copyright 2020 The Terasology Foundation
|
||||
// Copyright 2021 The Terasology Foundation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.terasology.launcher.ui;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import javafx.animation.Transition;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.ObjectBinding;
|
||||
|
@ -49,7 +48,6 @@ import org.terasology.launcher.tasks.DownloadTask;
|
|||
import org.terasology.launcher.util.BundleUtils;
|
||||
import org.terasology.launcher.util.HostServices;
|
||||
import org.terasology.launcher.util.Languages;
|
||||
import org.terasology.launcher.util.Platform;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
@ -345,15 +343,7 @@ public class ApplicationController {
|
|||
final GameRelease release = selectedRelease.getValue();
|
||||
final GameIdentifier id = release.getId();
|
||||
final Path gamePath = gameManager.getInstallDirectory(id);
|
||||
List<String> additionalJavaParameters = Lists.newArrayList();
|
||||
List<String> additionalGameParameters = Lists.newArrayList();
|
||||
if (release.isLwjgl3() && Platform.getPlatform().isMac()) {
|
||||
additionalJavaParameters.add("-XstartOnFirstThread");
|
||||
additionalJavaParameters.add("-Djava.awt.headless=true");
|
||||
|
||||
additionalGameParameters.add("-noSplash");
|
||||
}
|
||||
gameService.start(gamePath, launcherSettings, additionalJavaParameters, additionalGameParameters);
|
||||
gameService.start(release, gamePath, launcherSettings);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,23 @@
|
|||
// Copyright 2020 The Terasology Foundation
|
||||
// Copyright 2021 The Terasology Foundation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package org.terasology.launcher.game;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.event.Level;
|
||||
import org.terasology.launcher.model.Build;
|
||||
import org.terasology.launcher.model.GameIdentifier;
|
||||
import org.terasology.launcher.model.GameRelease;
|
||||
import org.terasology.launcher.model.Profile;
|
||||
import org.terasology.launcher.model.ReleaseMetadata;
|
||||
import org.terasology.launcher.util.JavaHeapSize;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
@ -50,7 +58,20 @@ public class TestGameStarter {
|
|||
}
|
||||
|
||||
private GameStarter newStarter() {
|
||||
return new GameStarter(gamePath, gameDataPath, HEAP_MIN, HEAP_MAX, javaParams, gameParams, LOG_LEVEL);
|
||||
GameRelease release;
|
||||
try {
|
||||
release = new GameRelease(
|
||||
new GameIdentifier("alpha-20", "5.1.0", Build.STABLE, Profile.OMEGA),
|
||||
new URL("https://repository.example"),
|
||||
new ReleaseMetadata(
|
||||
"# CHANGES",
|
||||
(new Calendar.Builder()).setDate(2021, 1, 1).build().getTime()
|
||||
)
|
||||
);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new GameStarter(release, gamePath, gameDataPath, HEAP_MIN, HEAP_MAX, javaParams, gameParams, LOG_LEVEL);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2021 The Terasology Foundation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.terasology.launcher.game;
|
||||
|
||||
import com.vdurmont.semver4j.Semver;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/** test edge cases in Semver comparison */
|
||||
class VersionHistoryTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"5.2.0-SNAPSHOT", "5.2.0", "5.2.1", "6.0.0"})
|
||||
void hasPicocli(String version) {
|
||||
assertTrue(VersionHistory.PICOCLI.isProvidedBy(new Semver(version, Semver.SemverType.IVY)));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"5.1.0", "5.1.0-SNAPSHOT", "5.1.1"})
|
||||
void lacksPicocli(String version) {
|
||||
assertFalse(VersionHistory.PICOCLI.isProvidedBy(new Semver(version, Semver.SemverType.IVY)));
|
||||
}
|
||||
|
||||
}
|
|
@ -90,7 +90,7 @@ class JenkinsRepositoryAdapterTest {
|
|||
// 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 ReleaseMetadata releaseMetadata = new ReleaseMetadata("", new Date(1604285977306L), true);
|
||||
final ReleaseMetadata releaseMetadata = new ReleaseMetadata("", new Date(1604285977306L));
|
||||
final GameRelease expected = new GameRelease(id, expectedArtifactUrl, releaseMetadata);
|
||||
|
||||
final JenkinsRepositoryAdapter adapter = new JenkinsRepositoryAdapter(Profile.OMEGA, Build.STABLE, stubClient);
|
||||
|
@ -99,9 +99,7 @@ class JenkinsRepositoryAdapterTest {
|
|||
assertAll(
|
||||
() -> assertEquals(expected.getId(), adapter.fetchReleases().get(0).getId()),
|
||||
() -> assertEquals(expected.getUrl(), adapter.fetchReleases().get(0).getUrl()),
|
||||
() -> assertEquals(expected.getTimestamp(), adapter.fetchReleases().get(0).getTimestamp()),
|
||||
() -> assertEquals(expected.isLwjgl3(), adapter.fetchReleases().get(0).isLwjgl3(),
|
||||
"Jenkins adapter should assume only builds for LWJGL v3 releases")
|
||||
() -> assertEquals(expected.getTimestamp(), adapter.fetchReleases().get(0).getTimestamp())
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue