Lobbies & Discovery
Instead of manually sharing private keys, you can let NGE’s LobbyManager handle key distribution and peer discovery for you. This is the recommended way to start a connection.
First, initialize a LobbyManager
:
LobbyManager lobbyManager = new LobbyManager(
localNostrSigner,
"My Awesome Game Name",
"v1.0",
List.of("wss://relay.ngengine.org"),
null
);
- localNostrSigner: The local player’s
NostrSigner
(from Authentication). - "My Awesome Game Name": Your game’s name.
- "v1.0": Your game’s version.
- List.of("wss://relay.ngengine.org"): A list of one or more Nostr relays for peer discovery and signaling. At least one relay is required; you can supply additional relays for redundancy.
- null: A
NostrRelay
to use as a TURN server (passnull
to disable TURN, which is acceptable for most modern unrestricted networks).
Once the LobbyManager
is created, you can perform the following operations:
Create a Lobby¶
String passphrase = "";
Map<String, String> data = Map.of("map", "ancient ruins");
Duration duration = Duration.ofDays(1);
lobbyManager.createLobby(
passphrase,
data,
duration,
(lobby, error) -> {
if (error != null) {
System.err.println("Failed to create lobby: " + error.getMessage());
} else {
System.out.println("Lobby created successfully: " + lobby);
}
}
);
- passphrase: A string to protect a private lobby. Leave empty (
""
) for a public lobby. - data: A
Map<String, String>
containing custom metadata (e.g.,"map": "ancient ruins"
). You can use these key‐value pairs to filter lobbies later and to store additional public information about the game session. - duration: A
Duration
after which the lobby will be automatically deleted. - Callback: Receives either a
LocalLobby
object (if successful) or anException
(if failed).
The returned LocalLobby
object can be updated in (near) real‐time by calling:
See LocalLobby Javadoc and Lobby Javadoc
lobby.setData(key, value);
This change may take a moment to propagate.
Tip
You can retrieve the lobby’s sharedPrivateKey
via:
NostrPrivateKey key = lobby.getKey(passphrase);
LobbyManager
will handle it for you.
Warning
Creating a lobby does not automatically connect you to it. To connect, see Connect to a Lobby below.
Find Lobbies¶
Query the relay(s) for all available lobbies matching your criteria:
String search = "";
int limit = 100;
Map<String, String> filters = Map.of("map", "ancient ruins");
lobbyManager.findLobbies(
search,
limit,
filters,
(lobbies, error) -> {
if (error != null) {
System.err.println("Failed to find lobbies: " + error.getMessage());
} else {
System.out.println("Found lobbies: " + lobbies);
}
}
);
- search: A string to search for in lobby names or metadata. Leave empty (
""
) to match all lobbies. The search is performed on‐relay if the relay supports it; otherwise, it happens client‐side. - limit: Maximum number of lobbies to return.
- filters: A
Map<String, String>
to filter lobbies by metadata keys/values. 1 letter keys will be filtered on‐relay (per the Nostr protocol); longer keys will be filtered client‐side. - Callback: Returns a
List<Lobby>
(if successful) or anException
(if failed).
Connect to a Lobby¶
After creating or discovering a Lobby
, connect to it with:
P2PChannel chan = lobbyManager.connectToLobby(lobby, passphrase);
- lobby: The
Lobby
orLocalLobby
object you created or discovered. - passphrase: The passphrase used when creating the lobby (use
""
if it was public).
This method returns a P2PChannel
immediately. Internally, it handles key retrieval, signaling, and ICE negotiation. Once the channel is connected, you can send and receive packets as usual.
For jME3 Developers
From this point onward, the flow is very similar to a SpiderMonkey server
Listening for Peers¶
Once you have a P2PChannel
, you can register listeners to detect incoming connections.
Note
Peer connections may take a few seconds to complete, due to NAT traversal and signaling delays.
chan.addConnectionListener(new com.jme3.network.ConnectionListener() {
@Override
public void connectionAdded(Server server, HostedConnection conn) {
// A peer has connected. You can now send packets to 'conn'.
}
@Override
public void connectionRemoved(Server server, HostedConnection conn) {
// A peer has disconnected. Clean up any resources here.
}
});
Optionally, you can listen for peers that have been discovered but have not yet fully connected:
chan.addDiscoveryListener(
new org.ngengine.nostr4j.rtc.listeners.NostrRTCRoomPeerDiscoveredListener() {
@Override
public void onRoomPeerDiscovered(
NostrPublicKey peerKey,
NostrRTCAnnounce announce,
NostrRTCRoomPeerDiscoveredState state
) {
// Handle a newly discovered peer (before ICE is complete)
}
}
);