Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on: [push]
jobs:
checks:
runs-on: ubuntu-latest
container: eclipse-temurin:17-jdk
container: eclipse-temurin:21-jdk
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
release:

runs-on: ubuntu-latest
container: eclipse-temurin:17-jdk
container: eclipse-temurin:21-jdk

steps:
- uses: actions/checkout@v3
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
A P2P connection proxy for Supreme Commander: Forged Alliance using [ICE](https://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment).

## Building
Build the project using gradle from the provided wrapper (Java 17 required).
Build the project using gradle from the provided wrapper (Java 21 required).

## JSONRPC Protocol
The `faf-ice-adapter` is controlled using a bi-directional [JSON-RPC](http://www.jsonrpc.org/specification) interface over TCP.
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
group 'com.faforever'
version '1.0-SNAPSHOT'

sourceCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_21

repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion client/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ apply plugin: 'com.github.johnrengelman.shadow'
group 'com.faforever'
version '1.0-SNAPSHOT'

sourceCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_21

repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion ice-adapter/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ apply plugin: 'com.diffplug.spotless'
group 'com.faforever'


sourceCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_21

repositories {
mavenCentral()
Expand Down
69 changes: 45 additions & 24 deletions ice-adapter/src/main/java/com/faforever/iceadapter/IceAdapter.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
package com.faforever.iceadapter;

import static com.faforever.iceadapter.debug.Debug.debug;

import com.faforever.iceadapter.debug.Debug;
import com.faforever.iceadapter.gpgnet.GPGNetServer;
import com.faforever.iceadapter.gpgnet.GameState;
import com.faforever.iceadapter.ice.GameSession;
import com.faforever.iceadapter.ice.PeerIceModule;
import com.faforever.iceadapter.rpc.RPCService;
import com.faforever.iceadapter.util.Executor;
import com.faforever.iceadapter.util.ExecutorHolder;
import com.faforever.iceadapter.util.LockUtil;
import com.faforever.iceadapter.util.TrayIcon;
import java.util.concurrent.Callable;
import lombok.extern.slf4j.Slf4j;
import picocli.CommandLine;

import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static com.faforever.iceadapter.debug.Debug.debug;

@CommandLine.Command(
name = "faf-ice-adapter",
mixinStandardHelpOptions = true,
usageHelpAutoWidth = true,
description = "An ice (RFC 5245) based network bridge between FAF client and ForgedAlliance.exe")
@Slf4j
public class IceAdapter implements Callable<Integer> {
public class IceAdapter implements Callable<Integer>, AutoCloseable {
private static IceAdapter INSTANCE;
private static String VERSION = "SNAPSHOT";
private static volatile GameSession GAME_SESSION;
Expand All @@ -29,6 +33,8 @@ public class IceAdapter implements Callable<Integer> {
private IceOptions iceOptions;

private volatile boolean running = true;
private final ExecutorService executor = ExecutorHolder.getExecutor();
private static final Lock lockGameSession = new ReentrantLock();

public static void main(String[] args) {
new CommandLine(new IceAdapter()).setUnmatchedArgumentsAllowed(true).execute(args);
Expand Down Expand Up @@ -60,6 +66,13 @@ public void start() {
debug().startupComplete();
}

@Override
public void close() {
executor.shutdown();
CompletableFuture.runAsync(executor::shutdownNow,
CompletableFuture.delayedExecutor(250, TimeUnit.MILLISECONDS));
}

public static void onHostGame(String mapName) {
log.info("onHostGame");
createGameSession();
Expand Down Expand Up @@ -113,42 +126,46 @@ public static void onDisconnectFromPeer(int remotePlayerId) {
});
}

private static synchronized void createGameSession() {
if (GAME_SESSION != null) {
GAME_SESSION.close();
GAME_SESSION = null;
}
private static void createGameSession() {
LockUtil.executeWithLock(lockGameSession, () -> {
if (GAME_SESSION != null) {
GAME_SESSION.close();
GAME_SESSION = null;
}

GAME_SESSION = new GameSession();
GAME_SESSION = new GameSession();
});
}

/**
* Triggered by losing gpgnet connection to FA.
* Closes the active Game/ICE session
*/
public static synchronized void onFAShutdown() {
if (GAME_SESSION != null) {
log.info("FA SHUTDOWN, closing everything");
GAME_SESSION.close();
GAME_SESSION = null;
// Do not put code outside of this if clause, else it will be executed multiple times
}
public static void onFAShutdown() {
LockUtil.executeWithLock(lockGameSession, () -> {
if (GAME_SESSION != null) {
log.info("FA SHUTDOWN, closing everything");
GAME_SESSION.close();
GAME_SESSION = null;
// Do not put code outside of this if clause, else it will be executed multiple times
}
});
}

/**
* Stop the ICE adapter
*/
public static void close() {
log.info("close() - stopping the adapter");

Executor.executeDelayed(500, () -> System.exit(0));
public static void close(int status) {
log.info("close() - stopping the adapter. Status: {}", status);

onFAShutdown(); // will close gameSession aswell
GPGNetServer.close();
RPCService.close();
Debug.close();
TrayIcon.close();

System.exit(0);
INSTANCE.close();
Comment thread
Brutus5000 marked this conversation as resolved.
CompletableFuture.runAsync(() -> System.exit(status),
CompletableFuture.delayedExecutor(500, TimeUnit.MILLISECONDS));
}

public static int getId() {
Expand Down Expand Up @@ -183,6 +200,10 @@ public static boolean isRunning() {
return INSTANCE.running;
}

public static Executor getExecutor() {
return INSTANCE.executor;
}

public static GameSession getGameSession() {
return GAME_SESSION;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.faforever.iceadapter.debug;

import com.faforever.iceadapter.IceAdapter;
import java.lang.reflect.InvocationTargetException;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.CompletableFuture;

@Slf4j
public class Debug {
// TODO
Expand All @@ -17,6 +19,8 @@ public class Debug {

public static int RPC_PORT;

private static TelemetryDebugger telemetryDebugger;

private static final DebugFacade debugFacade = new DebugFacade();

public static void register(Debugger debugger) {
Expand All @@ -28,7 +32,8 @@ public static void remove(Debugger debugger) {
}

public static void init() {
new TelemetryDebugger(IceAdapter.getTelemetryServer(), IceAdapter.getGameId(), IceAdapter.getId());
telemetryDebugger = new TelemetryDebugger(
IceAdapter.getTelemetryServer(), IceAdapter.getGameId(), IceAdapter.getId());

// Debugger window is started and set to debugFuture when either window is requested as the info window can be
// used to open the debug window
Expand All @@ -38,25 +43,33 @@ public static void init() {
}

if (isJavaFxSupported()) {
new Thread(() -> {
CompletableFuture.runAsync(
() -> {
try {
Class.forName("com.faforever.iceadapter.debug.DebugWindow")
.getMethod("launchApplication")
.invoke(null);
} catch (InvocationTargetException e) {
log.info("DebugWindows stopped");
} catch (IllegalAccessException
| ClassNotFoundException
| NoSuchMethodException
| InvocationTargetException e) {
e.printStackTrace();
log.error("Could not create DebugWindow. Running without debug window.");
| NoSuchMethodException e) {
log.error("Could not create DebugWindow. Running without debug window.", e);
}
})
.start(); // Completes future once application started
},
IceAdapter.getExecutor());
} else {
log.info("No JavaFX support detected. Running without debug window.");
}
}

public static void close() {
if (telemetryDebugger != null) {
telemetryDebugger.close();
telemetryDebugger = null;
}
}

public static Debugger debug() {
return debugFacade;
}
Expand Down
Loading