Skip to content

A P2P connection proxy for Supreme Commander: Forged Alliance using ICE

License

Notifications You must be signed in to change notification settings

FAForever/java-ice-adapter

Repository files navigation

❆ faf-ice-adapter ❆

A P2P connection proxy for Supreme Commander: Forged Alliance using ICE.

Building

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

JSONRPC Protocol

The faf-ice-adapter is controlled using a bi-directional JSON-RPC interface over TCP.

Methods (client ➠ faf-ice-adapter)

Name Parameters Returns Description
quit Gracefully shuts down the faf-ice-adapter.
hostGame mapName (string) Tell the game to create the lobby and host game on Lobby-State.
joinGame remotePlayerLogin (string), remotePlayerId (int) Tell the game to create the Lobby, create a PeerRelay in answer mode and join the remote game.
connectToPeer remotePlayerLogin (string), remotePlayerId (int), offer (bool) Create a PeerRelay and tell the game to connect to the remote peer with offer/answer mode.
disconnectFromPeer remotePlayerId (int) Destroy PeerRelay and tell the game to disconnect from the remote peer.
setLobbyInitMode lobbyInitMode (string): "normal" or "auto" Set the lobby mode the game will use. Supported values are "normal" for normal lobby and "auto" for automatch lobby (aka ladder).
iceMsg remotePlayerId (int), msg (object) Add the remote ICE message to the PeerRelay to establish a connection.
sendToGpgNet header (string), chunks (array) Send an arbitrary message to the game.
setIceServers iceServers (array) ICE server array for use in webrtc. Must be called before joinGame/connectToPeer. See https://developer.mozilla.org/en-US/docs/Web/API/RTCIceServer
status status structure Polls the current status of the faf-ice-adapter.

Notifications (faf-ice-adapter ➠ client )

Name Parameters Description
onConnectionStateChanged "Connected"/"Disconnected" (string) The game connected to the internal GPGNetServer.
onGpgNetMessageReceived header (string), chunks (array) The game sent a message to the faf-ice-adapter via the internal GPGNetServer.
onIceMsg localPlayerId (int), remotePlayerId (int), msg (object) The PeerRelays gathered a local ICE message for connecting to the remote player. This message must be forwarded to the remote peer and set using the iceMsg command.
onIceConnectionStateChanged localPlayerId (int), remotePlayerId (int), state (string) See https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceConnectionState
onConnected localPlayerId (int), remotePlayerId (int), connected (bool) Informs the client that ICE connectivity to the peer is established or unestablished.

Status structure

{
"version" : /* string: faf-ice-adapter version */
"ice_servers_size" : /* the number of ICE servers set using `setIceServers` */
"lobby_port" : /* the actual game lobby UDP port. Should match --lobby-port option if non-zero port is specified. */
"init_mode" : /* the current init mode. See setLobbyInitMode */
"options" : /* The specified commandline options */
"gpgnet" : { /* The GPGNet state */
  "local_port" : /* int: The port the game should connect to via /gpgnet 127.0.0.1:port */
  "connected" : /* boolean: Is the game connected? */
  "game_state" : /* string: The last received "GameState" */
  "task_string" : /* string: A string describing the task/role of the game (joining/hosting)*/
  }
"relays" : [/* An array of relay information for each peer */
  {
    "remote_player_id" : /* int: The ID of the remote player */
    "remote_player_login" : /* string: The name of the remote player */
    "local_game_udp_port" : /* int: The UDP port opened for the game to connect to */
    "ice": {/* ICE state information for this peer */
      "offerer": /* bool: one peer is always offerer, one answerer */
      "state": /* string: The connection state https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceConnectionState */
      "gathering_state": /* string: The gathering state https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceGatheringState */
      "datachannel_state": /* string: The state of the https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel */
      "connected": /* bool: Is the peer connected? Needs to be in sync with the remote peer. */
      "loc_cand_addr": /* string: The local address used for the connection */
      "rem_cand_addr": /* string: The remote address used for the connection */
      "loc_cand_type": /* string: The type of the local candidate 'local'/'stun'/'relay' */
      "rem_cand_type": /* string: The type of the remote candidate 'local'/'stun'/'relay' */
      "time_to_connected": /* double: The time it took to connect to the peer in seconds */
      }
    },
  ...
  ]
}

Commandline invocation

The first two commandline arguments --id and --login must be specified like this: faf-ice-adapter -i 3 -l "Rhiza" The full commandline help text is:

faf-ice-adapter usage:
--help                               produce help message
--id arg                             set the ID of the local player
--login arg                          set the login of the local player, e.g. "Rhiza"
--rpc-port arg (=7236)               set the port of internal JSON-RPC server
--gpgnet-port arg (=0)               set the port of internal GPGNet server
--lobby-port arg (=0)                set the port the game lobby should use for incoming UDP packets from the PeerRelay
--log-directory arg                  (DEPRECATED, use env variable LOG_DIR)set a log directory to write ice_adapter_0 log files
--force-relay                        force the usage of relay candidates only
--debug-window                       activate the debug window if JavaFX is available
--info-window                        activate the info window if JavaFX is available (allows access at the debug window)
--delay-ui arg                       delays the launch of the info and debug window by arg ms

Example usage sequence

Step Player 1 "Alice" Player 2 "Bob"
1 Start the client
2 The client starts faf-ice-adapter and connects to the JSONRPC server
3 The client starts the game and makes it connect to the GPGNet server of the faf-ice-adapter using /gpgnet 127.0.0.1:7237 commandline argument for ForgedAlliance.exe
3.1 The game sends GameState Idle
3.2 The client sends CreateLobby ...
3.3 The game sends GameState Lobby
4 The client sends hostGame('monument_valley.v0001')
5 The game should now wait in the lobby and the client receives a lot of onGpgNetMessageReceived notifications from faf-ice-adapter
6 Now Bob want to join Alices game, starts the client, the client starts faf-ice-adapter and the game like Alice did.
6.1 The game sends GameState Idle
6.2 The client sends CreateLobby ...
6.3 The game sends GameState Lobby
7 The client sends connectToPeer('Bob', 2, true) The client sends joinGame('Alice', 1)
8 The game should connect to the internal PeerRelay, and shows that it's connecting to the peer. The game should connect to the internal PeerRelay, and shows that it's connecting to the peer.
9 The client receives multiple onIceMsg(1, 2, someIceMsg)notifications and must now transfer these messages string ordered to the peer. The client receives multiple onIceMsg(1, 2, someIceMsg)notifications and must now transfer these messages string ordered to the peer.
10 The client must set the transferred ICE messages for the peer using iceMsg(2, someIceMsg). The client must set the transferred ICE messages for the peer using iceMsg(1, someIceMsg).
11 The client received multiple iceConnectionStateChanged(...) notifications which would finally show the 'Connected' or 'Complete' state, which should also let the game connect to the peer. Another indicator for a connection is the onDatachannelOpen notification. The client received multiple iceConnectionStateChanged(...) notifications which would finally show the 'Connected' or 'Complete' state, which should also let the game connect to the peer.