我正在与LibGDX进行游戏,现在我正试图通过重新运行jar重启游戏,因为我正在使用jar的路径,通过使用以下方式找到它:
String location = new File(DesktopLauncher.class
.getProtectionDomain().getCodeSource().getLocation()
.getPath()).toString().replace("%20", " ");
使用后,我尝试使用Process
和Runtime.getRuntime().exec("java -jar " + location + "\\Test.jar");
来重新启动
现在到目前为止它的工作原理,但问题是我创建新实例(我重新启动)的游戏的第一个实例仍保留在屏幕上并且不会关闭直到第二个实例关闭。 />
这是重新启动的代码:
public static void restart() {
Gdx.app.exit();
try {
String location = new File(DesktopLauncher.class
.getProtectionDomain().getCodeSource().getLocation()
.getPath()).toString().replace("%20", " ");
System.out.println(location);
Process pro = Runtime.getRuntime().exec(
"java -jar " + location + "\\Test.jar");
BufferedWriter writer = new BufferedWriter(new FileWriter(new File(
"reprot.txt")));
InputStream stream = pro.getErrorStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(
stream));
String line = "";
writer.write(location);
while ((line = reader.readLine()) != null) {
writer.write(line);
}
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
我做错了吗?如何在启动第二个实例后关闭游戏的第一个实例?
我尝试使用不同的线程,使用以下代码:
public static void main(String[] args) {
try {
String location = new File(DesktopLauncher.class
.getProtectionDomain().getCodeSource().getLocation()
.getPath()).toString();
System.out.println(location);
Process pro = Runtime.getRuntime().exec(
"java -jar " + location + "\\Test.jar");
BufferedWriter writer = new BufferedWriter(new FileWriter(new File(
"report.txt")));
InputStream stream = pro.getErrorStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(
stream));
String line = "";
writer.write(location);
while ((line = reader.readLine()) != null) {
writer.write(line);
}
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
但它仍然存在同样的问题。
编辑:我尝试使用System.exit(0);
,并尝试使用LwjglApplication关闭它,但它保持不变,但是我取得了一些进展:
当创建这个新进程时,游戏的第二个实例,第一个实例的用户界面冻结,导致游戏无法响应。我想,如果它没有回应,我应该找到一种方法来杀掉它并离开另一个实例,但由于关闭一次游戏实例的事实,这无法完成(通过强制关闭它,你关闭两个实例
我想我找到了一个令人讨厌的谜题:
让我们说我们的主要游戏实例叫做“Game_1”,而我们正在创建的实例是“Game_2'。”
在查看代码并思考发生了什么(测试小班而不是大型游戏)之后,我认为这就是“游戏_1”。不会因为' Game_2'而关闭没有关闭。
在更复杂的术语中,“Game_1'”的实例不会因为它以某种方式附加到Game_2'而不会被关闭。因此正在等待Game_2'在它自己关闭之前关闭
所以,如果这是正确的,那么关闭Game_1'会做出Game_2' Game_2'同时运行到Game_1'使其独立,从而允许Game_1'继续代码的当前进度,这将是Gdx.app.exit();
方法的实现
所以现在问题仍然存在,我如何制作“Game_2'独立于Game_1'?或者我将如何制作“Game_1'继续代码,或者不要等到从Game_2'收到退出值
EDIT2:大规模进展在重新启动课程中添加一行代码System.exit(0);
后,“Game_1'在终止' Game_1' Game_2' Game_2'后,继续不回复,但我没有被关掉,我会继续玩它,直到我弄清楚要做什么
编辑3:我继续尝试修复它以便它可以工作,但遇到了另一个问题。我发现如果我可以模拟游戏_2的过程的退出值。没有实际退出,我可以终止' Game_1'用户界面,同时保持游戏2仍然活着,如果有人有任何想法,请与我分享。
编辑4:我继续努力做到这一点,但我不能按照正在进行的操作,我试图通过写一下将PID传递给重启类
"java -cp " + location + "\\Test.jar Restart " + PID
但它似乎无法正常工作,或者我似乎没有从Restart类接收任何信息(例如syso)。最重要的是,我在游戏中发现了一个内存泄漏,一旦我完成了这个工作,我将解决这个问题
如果您有任何想法如何帮助我,即使只是一个理论,请分享
编辑5:我已经使用LINK确定了给定流程终止的效率
答案 0 :(得分:1)
有一种更“简单”的方法可以做你想要的。您当然必须适应您自己的应用程序,因为您尝试做的事情完全超出了libgdx的范围。它是一个跨平台的库,想法更新/重启与移动设备非常不同。
可以找到实际的桌面跨平台解决方案here,我强烈建议您不要使用您的方法,因为它不是一个可靠的解决方案,而且非常适合平台。
以下是如何在libgdx中执行此操作的示例。您需要两件事,即启动应用程序的代码和重新启动它的代码。
发射器:
public class TestLauncher {
public static void main(final String[] args) {
final LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
cfg.title = "Game";
cfg.width = 1280;
cfg.height = 720;
cfg.backgroundFPS = 12;
cfg.foregroundFPS = 60;
final Runnable rebootable = new Runnable() {
@Override public void run() {
if (Gdx.app != null) {
Gdx.app.exit();
}
TestLauncher.restart();
}
};
new LwjglApplication(new RebootTest(rebootable), cfg);
}
public static void restart() {
final StringBuilder cmd = new StringBuilder();
cmd.append(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java ");
for (final String jvmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
cmd.append(jvmArg + " ");
}
cmd.append("-cp ").append(ManagementFactory.getRuntimeMXBean().getClassPath()).append(" ");
cmd.append(TestLauncher.class.getName()).append(" ");
try {
Runtime.getRuntime().exec(cmd.toString());
} catch (final IOException e) {
e.printStackTrace();
}
}
}
游戏代码示例:
public class RebootTest implements ApplicationListener {
private final Runnable rebootHook;
private Stage stage;
private Skin skin;
public RebootTest(final Runnable rebootHook) {
this.rebootHook = rebootHook;
}
@Override public void create() {
this.stage = new Stage();
this.skin = new Skin(Gdx.files.internal("skin/uiskin.json"));
final Table table = new Table();
table.setFillParent(true);
final TextButton button = new TextButton("Reboot", this.skin);
button.addListener(new ClickListener() {
@Override public void clicked(final InputEvent event, final float x, final float y) {
Gdx.app.postRunnable(RebootTest.this.rebootHook);
}
});
table.add(button).expand().size(120, 40);
this.stage.addActor(table);
Gdx.input.setInputProcessor(this.stage);
}
@Override public void resize(final int width, final int height) {}
@Override public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
this.stage.act();
this.stage.draw();
}
@Override public void pause() {}
@Override public void resume() {}
@Override public void dispose() {
if (this.stage != null) {
this.stage.dispose();
}
if (this.skin != null) {
this.skin.dispose();
}
}
}
答案 1 :(得分:0)
这是解决方案,因为直到明天我才能回答我的问题:
好吧,最后,我完成了解决它,它有一些问题,我会提到其中只有两个,因为它涉及一般的代码而不是我如何使用它。 'Game_1'将是首先启动的游戏,'Game_2'将是重新启动的游戏的实例。就是这样:
首先,我获得了当前正在运行的当前进程的PID,'Game_1',我将从中创建'Game_2'。这个问题是Java应用程序都有相同的名称,'Java.exe',这导致了一堆同名的应用程序,所以现在我添加一条消息说游戏应该是唯一的java计算机上的实例,而不是日食,或类似的东西
PID检索的代码如下:
private static String getPID() {
try {
String line;
Process p = Runtime.getRuntime().exec(
System.getenv("windir") + "\\system32\\" + "tasklist.exe");
BufferedReader input = new BufferedReader(new InputStreamReader(
p.getInputStream()));
while ((line = input.readLine()) != null) {
System.out.println(line);
if (line.contains("java")) {
String data = line.subSequence(27, 35).toString();
data = data.trim();
return data;
}
}
input.close();
} catch (IOException e) {
e.printStackTrace();
}
return "-1";
}
现在,稍后,我将寻找一种方法来命名当前正在运行的进程,这样您就不必使用line.contains("java")
,因为它可能会提供多个实例,但现在它是尽可能好。
此代码使用Windows内部的exe文件,基本上提供计算机上运行的所有当前进程,因此您可以找到您的
返回的列表以这种格式给出:
Image Name PID Session Name Session# Mem Usage
========================= ======== ================ =========== ============
All the processes will be located here.
PID位于第27个字符到第35个字符之间,这就是我添加的原因
String data = line.subSequence(27, 35).toString();
这样它就会返回过程的PID
在这之后,我准备了一个带有执行命令的cmd,如下所示:
String jarLocation = new File(YourClass.class.getProtectedDomain().getCodeSource().getLocation().getPath()).toString();
String command = "java -cp " + jarLocation + " your.Package.here.Restart \""+PID+"\"";
Runtime.getRuntime().exec("cmd /C start cmd.exe /C \"" + command + "\"");
现在首先我获得了.jar文件的位置。它以下列格式返回:
C:\A%20Folder\To%20YourJar\YourJar.jar
因此需要对位置进行以下格式化
jarLocation = jarLocation.replace("%20", " ");
只需将所有%20转换为空格。
注意如果您的目录中没有空格,则不需要执行上一步格式化
之后我准备了实际的命令,如下所示(这对我来说,但你可以改变它以满足你的需要)。
java
- 在cmd中调用java程序。
-cp
- 执行一个位于jar文件中的类。
然后我添加了jar位置,然后添加了包并添加了一个参数(对于main方法中的String[] args
) PID终止。
现在,以下代码行表示操作系统依赖关系,因此如果您想添加多个操作系统支持,我建议在其他操作系统中找到与cmd等效的内容,并确定如何使用它。
最后一行代码是执行,我们获取运行时,启动cmd并在关闭cmd之前执行单个命令。
您可以在以下问题中找到有关它的详细信息:LINK
@Vincent Ramdhanie还提供了激活cmd时可以使用运行时运行的命令的链接
之后,我有了一个实际重启游戏本身的类,名为Restart
与最后一行代码一样,代码行表示操作系统依赖关系,因此如果要支持多个操作系统,请在其他操作系统中找到等效于taskkil
的代码。据@erhun所说,pkill
对于Linux或其他东西,抱歉我不记得了
这是该类的代码:
public static void main(String[] args) {
try {
String location = new File(DesktopLauncher.class
.getProtectionDomain().getCodeSource().getLocation()
.getPath()).toString();
location = "\"" + location.replaceAll("%20", " ");
Runtime.getRuntime().exec("taskkill /F /PID " + args[0]);
Runtime.getRuntime().exec("java -jar " + location);
} catch (IOException e) {
e.printStackTrace();
}
}
与上一行一样,此处的位置与以前的含义相同,如果目录中有空格,则必须格式化。
之后你需要终止前一个进程,即taskkill /F /PID + args[0]
进来的地方。如果你运行它,你将使用args [0]的id终止任务,这是'Game_1'的PID。
之后我就运行jar文件,你很高兴
我想注意一些事情,我尝试运行它,以便主类(DesktopLauncher)在运行时通过exec命令使用Restart类,但问题已经存在,我发现解决这个问题的唯一方法是工作围绕它,并使用cmd。 (这是在使用cmd调试位置字符串之后)
就是这样,我工作了整整一个星期,试图解决这个问题,而且就像这样粗糙,它是一个解决方案,同时也是。如果我在此代码的某个地方遇到问题,请告诉我。