Files
netris-nestri/packages/relay/internal/core/p2p.go
Kristian Ollikainen c62a22b552 feat: Controller support, performance enchancements, multi-stage images, fixes (#304)
## Description
Oops.. another massive PR 🥲 

This PR contains multiple improvements and changes.

Firstly, thanks gst-wayland-display's PR
[here](https://github.com/games-on-whales/gst-wayland-display/pull/20).
NVIDIA path is now way more efficient than before.

Secondly, adding controller support was a massive hurdle, requiring me
to start another project
[vimputti](https://github.com/DatCaptainHorse/vimputti) - which allows
simple virtual controller inputs in isolated containers. Well, it's not
simple, it includes LD_PRELOAD shims and other craziness, but the
library API is simple to use..

Thirdly, split runner image into 3 separate stages, base + build +
runtime, should help keep things in check in future, also added GitHub
Actions CI builds for v2 to v4 builds (hopefully they pass..).

Fourth, replaced the runner's runtime Steam patching with better and
simpler bubblewrap patch, massive thanks to `games-on-whales` to
figuring it out better!

Fifth, relay for once needed some changes, the new changes are still
mostly WIP, but I'll deal with them next time I have energy.. I'm spent
now. Needed to include these changes as relay needed a minor change to
allow rumble events to flow back to client peer.

Sixth.. tons of package updates, minor code improvements and the usual. 

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* End-to-end gamepad/controller support (attach/detach, buttons, sticks,
triggers, rumble) with client/server integration and virtual controller
plumbing.
  * Optional Prometheus metrics endpoint and WebTransport support.
  * Background vimputti manager process added for controller handling.

* **Improvements**
  * Multi-variant container image builds and streamlined runtime images.
  * Zero-copy video pipeline and encoder improvements for lower latency.
  * Updated Steam compat mapping and dependency/toolchain refreshes.

* **Bug Fixes**
* More robust GPU detection, input/fullscreen lifecycle,
startup/entrypoint, and container runtime fixes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
2025-10-20 11:20:05 +03:00

124 lines
3.7 KiB
Go

package core
import (
"context"
"errors"
"fmt"
"log/slog"
"time"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/multiformats/go-multiaddr"
)
// --- Structs ---
// networkNotifier logs connection events and updates relay state
type networkNotifier struct {
relay *Relay
}
// Connected is called when a connection is established
func (n *networkNotifier) Connected(net network.Network, conn network.Conn) {
if n.relay != nil {
n.relay.onPeerConnected(conn.RemotePeer())
}
}
// Disconnected is called when a connection is terminated
func (n *networkNotifier) Disconnected(net network.Network, conn network.Conn) {
// Update the status of the disconnected peer
if n.relay != nil {
n.relay.onPeerDisconnected(conn.RemotePeer())
}
}
// Listen is called when the node starts listening on an address
func (n *networkNotifier) Listen(net network.Network, addr multiaddr.Multiaddr) {}
// ListenClose is called when the node stops listening on an address
func (n *networkNotifier) ListenClose(net network.Network, addr multiaddr.Multiaddr) {}
// --- PubSub Setup ---
// setupPubSub initializes PubSub topics and subscriptions.
func (r *Relay) setupPubSub(ctx context.Context) error {
var err error
// Room State Topic
r.pubTopicState, err = r.PubSub.Join(roomStateTopicName)
if err != nil {
return fmt.Errorf("failed to join room state topic '%s': %w", roomStateTopicName, err)
}
stateSub, err := r.pubTopicState.Subscribe()
if err != nil {
return fmt.Errorf("failed to subscribe to room state topic '%s': %w", roomStateTopicName, err)
}
go r.handleRoomStateMessages(ctx, stateSub) // Handler in relay_state.go
// Relay Metrics Topic
r.pubTopicRelayMetrics, err = r.PubSub.Join(relayMetricsTopicName)
if err != nil {
return fmt.Errorf("failed to join relay metrics topic '%s': %w", relayMetricsTopicName, err)
}
metricsSub, err := r.pubTopicRelayMetrics.Subscribe()
if err != nil {
return fmt.Errorf("failed to subscribe to relay metrics topic '%s': %w", relayMetricsTopicName, err)
}
go r.handleRelayMetricsMessages(ctx, metricsSub) // Handler in relay_state.go
slog.Info("PubSub topics joined and subscriptions started")
return nil
}
// --- Connection Management ---
// connectToPeer is internal method to connect to a peer using multiaddresses
func (r *Relay) connectToPeer(ctx context.Context, peerInfo *peer.AddrInfo) error {
if peerInfo.ID == r.ID {
return errors.New("cannot connect to self")
}
// Use a timeout for the connection attempt
connectCtx, cancel := context.WithTimeout(ctx, 15*time.Second) // 15s timeout
defer cancel()
slog.Info("Attempting to connect to peer", "peer", peerInfo.ID, "addrs", peerInfo.Addrs)
if err := r.Host.Connect(connectCtx, *peerInfo); err != nil {
return fmt.Errorf("failed to connect to %s: %w", peerInfo.ID, err)
}
slog.Info("Successfully connected to peer", "peer", peerInfo.ID, "addrs", peerInfo.Addrs)
return nil
}
// ConnectToPeer connects to another peer by its multiaddress.
func (r *Relay) ConnectToPeer(ctx context.Context, addr multiaddr.Multiaddr) error {
peerInfo, err := peer.AddrInfoFromP2pAddr(addr)
if err != nil {
return fmt.Errorf("failed to extract peer info: %w", err)
}
return r.connectToPeer(ctx, peerInfo)
}
// printConnectInstructions logs the multiaddresses for connecting to this relay.
func printConnectInstructions(p2pHost host.Host) {
peerInfo := peer.AddrInfo{
ID: p2pHost.ID(),
Addrs: p2pHost.Addrs(),
}
addrs, err := peer.AddrInfoToP2pAddrs(&peerInfo)
if err != nil {
slog.Error("Failed to convert peer info to addresses", "err", err)
return
}
slog.Info("Mesh connection addresses:")
for _, addr := range addrs {
slog.Info(fmt.Sprintf("> %s", addr.String()))
}
}