Skip to content

Commit af069f5

Browse files
committed
Simplify console guidance for client and Cloudflare wizard
1 parent 6738e34 commit af069f5

8 files changed

Lines changed: 111 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ unless `ROAD_VERSION` is set during build.
1919
before release.
2020
- Fixed Plugin Studio output so generated plugin menus point at the generated
2121
per-plugin server/client config files instead of generic examples.
22+
- Simplified first-run console output: client mode now shows the exact in-game
23+
target address, and TryCloudflare mode hides noisy cloudflared logs while
24+
surfacing the created tunnel URL.
2225

2326
## v0.1.2 - 2026-05-14
2427

cmd/road/flow_client.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bufio"
55
"context"
66
"fmt"
7+
"net"
78
"net/url"
89
"os/signal"
910
"strings"
@@ -55,6 +56,8 @@ func startClientFlow(reader *bufio.Reader) error {
5556
return err
5657
}
5758

59+
printClientReadyInstructions(cfg)
60+
5861
tunnel := client.New(cfg, logging.New(cfg.Logging.Format, "road-proxy-client"))
5962
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
6063
defer cancel()
@@ -189,3 +192,38 @@ func applyClientProfileFromServer(cfg *config.ClientConfig, requireProfile bool)
189192
}
190193
return nil
191194
}
195+
196+
func printClientReadyInstructions(cfg *config.ClientConfig) {
197+
target, host, port := splitDisplayTarget(cfg.ListenAddr)
198+
199+
fmt.Println(msg("client.ready"))
200+
fmt.Printf(msg("client.game_target_line"), target)
201+
if host != "" && port != "" {
202+
fmt.Printf(msg("client.game_host_port_line"), host, port)
203+
}
204+
fmt.Printf(msg("client.remote_line"), cfg.ServerWSURL)
205+
if strings.TrimSpace(cfg.PluginName) != "" {
206+
fmt.Printf(msg("client.active_plugin_line"), cfg.PluginName)
207+
}
208+
fmt.Println(msg("client.stop_hint"))
209+
fmt.Println()
210+
fmt.Println(msg("client.logs_follow"))
211+
}
212+
213+
func splitDisplayTarget(addr string) (target string, host string, port string) {
214+
target = strings.TrimSpace(addr)
215+
if target == "" {
216+
return "", "", ""
217+
}
218+
219+
host, port, err := net.SplitHostPort(target)
220+
if err != nil {
221+
return target, "", ""
222+
}
223+
host = strings.Trim(host, "[]")
224+
switch host {
225+
case "", "0.0.0.0", "::":
226+
host = "127.0.0.1"
227+
}
228+
return net.JoinHostPort(host, port), host, port
229+
}

cmd/road/main_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,20 @@ func TestApplyClientConnectionInputPromptsForPublicAuthToken(t *testing.T) {
287287
}
288288
}
289289

290+
func TestSplitDisplayTargetNormalizesWildcardHost(t *testing.T) {
291+
target, host, port := splitDisplayTarget("0.0.0.0:25568")
292+
if target != "127.0.0.1:25568" || host != "127.0.0.1" || port != "25568" {
293+
t.Fatalf("unexpected display target: target=%q host=%q port=%q", target, host, port)
294+
}
295+
}
296+
297+
func TestSplitDisplayTargetKeepsConcreteHost(t *testing.T) {
298+
target, host, port := splitDisplayTarget("192.168.1.50:8303")
299+
if target != "192.168.1.50:8303" || host != "192.168.1.50" || port != "8303" {
300+
t.Fatalf("unexpected display target: target=%q host=%q port=%q", target, host, port)
301+
}
302+
}
303+
290304
func TestApplyClientConnectionInputBlankPublicAuthKeepsExistingToken(t *testing.T) {
291305
cfg := config.DefaultClient()
292306
cfg.AuthToken = "existing-token"
@@ -520,6 +534,21 @@ func TestTunnelURLCaptureFindsTryCloudflareURL(t *testing.T) {
520534
}
521535
}
522536

537+
func TestTunnelURLCaptureWorksWithoutOutputWriter(t *testing.T) {
538+
capture := newTunnelURLCapture(nil)
539+
if _, err := capture.Write([]byte("INF noisy cloudflared line\nhttps://quiet.trycloudflare.com\n")); err != nil {
540+
t.Fatalf("write failed: %v", err)
541+
}
542+
select {
543+
case got := <-capture.found:
544+
if got != "https://quiet.trycloudflare.com" {
545+
t.Fatalf("unexpected URL: %s", got)
546+
}
547+
default:
548+
t.Fatal("expected captured URL")
549+
}
550+
}
551+
523552
func TestPublicEndpointFromHTTPS(t *testing.T) {
524553
endpoint, host, err := publicEndpointFromHTTPS("https://abc-def.trycloudflare.com")
525554
if err != nil {

cmd/road/tunnel_trycloudflare.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func startTryCloudflareTunnel(ctx context.Context, bin string, originURL string)
6060
return nil, nil, err
6161
}
6262

63-
capture := newTunnelURLCapture(os.Stdout)
63+
capture := newTunnelURLCapture(nil)
6464
args := []string{
6565
"tunnel",
6666
"--config", isolatedConfig,
@@ -97,6 +97,7 @@ func waitForTunnelURL(ctx context.Context, urlCh <-chan string, proc *cloudflare
9797
select {
9898
case url := <-urlCh:
9999
fmt.Println()
100+
fmt.Printf(msg("cloudflared.tunnel_url_ready"), url)
100101
return url, nil
101102
case err := <-proc.done:
102103
fmt.Println()

cmd/road/wizard.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -308,14 +308,19 @@ func readRequiredLine(reader *bufio.Reader, prompt string) (string, error) {
308308
func printPublicServerReady(info publicServerRuntime) {
309309
fmt.Println()
310310
fmt.Println(msg("public.ready"))
311-
fmt.Printf(msg("public.endpoint_line"), info.Endpoint)
311+
fmt.Println(msg("public.share_header"))
312+
fmt.Printf(msg("public.client_endpoint_line"), info.Endpoint)
313+
fmt.Printf(msg("public.client_token_line"), info.Token)
314+
fmt.Printf(msg("public.client_plugin_line"), info.PluginName)
315+
fmt.Println()
316+
fmt.Println(msg("public.host_header"))
317+
fmt.Printf(msg("public.dashboard_line"), info.DashboardURL)
318+
fmt.Println()
319+
fmt.Println(msg("public.advanced_header"))
312320
fmt.Printf(msg("public.auth_header_line"), "X-ROAD-Token")
313-
fmt.Printf(msg("public.auth_token_line"), info.Token)
314-
fmt.Printf(msg("public.plugin_line"), info.PluginName)
315321
fmt.Printf(msg("public.local_origin_line"), info.LocalOriginURL)
316322
fmt.Printf(msg("public.server_config_line"), info.ConfigPath)
317323
fmt.Printf(msg("public.client_config_line"), info.ClientConfigPath)
318-
fmt.Printf(msg("public.dashboard_line"), info.DashboardURL)
319324
fmt.Println()
320325
fmt.Println(msg("public.stop_hint"))
321326
}

internal/client/tunnel.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func (t *Tunnel) startTCP(ctx context.Context) error {
104104
return err
105105
}
106106

107-
t.logger.Printf("client tunnel started: local=%s remote=%s", t.cfg.ListenAddr, targetWS)
107+
t.logger.Printf("client listener active: game_target=%s road_server=%s plugin=%s network=tcp", t.cfg.ListenAddr, targetWS, t.cfg.PluginName)
108108

109109
for {
110110
localConn, err := listener.Accept()
@@ -142,7 +142,7 @@ func (t *Tunnel) startUDP(ctx context.Context) error {
142142
return err
143143
}
144144

145-
t.logger.Printf("client udp tunnel started: local=%s remote=%s", t.cfg.ListenAddr, targetWS)
145+
t.logger.Printf("client listener active: game_target=%s road_server=%s plugin=%s network=udp", t.cfg.ListenAddr, targetWS, t.cfg.PluginName)
146146

147147
sessions := map[string]*udpSession{}
148148
var sessionsMu sync.Mutex

locales/en.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
"client.server_ws_url_repair_failed": "Invalid server address: %v\n",
1313
"client.server_ws_url_repair_prompt": "Enter ROAD server address to repair server_ws_url (default: %s): ",
1414
"client.starting": "Starting client...",
15+
"client.ready": "ROAD Client is ready.",
16+
"client.game_target_line": "Use this as the game/server address: %s\n",
17+
"client.game_host_port_line": "If the game asks separately, enter Host/IP=%s Port=%s.\n",
18+
"client.remote_line": "Connected ROAD server: %s\n",
19+
"client.active_plugin_line": "Active plugin: %s\n",
20+
"client.stop_hint": "Press Ctrl+C to stop.",
21+
"client.logs_follow": "Technical logs follow; normal users do not need to watch this section.",
1522
"common.error_line": "Error: %v\n\n",
1623
"common.runtime_line": "Runtime: %s\n",
1724
"config.template_load_failed": "template config could not be loaded (%s): %w",
@@ -72,6 +79,12 @@
7279
"protocol.unsupported": "unsupported protocol: %s",
7380
"public.auth_header_line": "Auth header: %s\n",
7481
"public.auth_token_line": "Auth token: %s\n",
82+
"public.share_header": "Give this to the client/player:",
83+
"public.client_endpoint_line": " ROAD address: %s\n",
84+
"public.client_token_line": " Auth token: %s\n",
85+
"public.client_plugin_line": " Plugin: %s\n",
86+
"public.host_header": "Host side:",
87+
"public.advanced_header": "Technical details:",
7588
"public.choice_1_4_default_1": "Choice (1-4, default: 1): ",
7689
"public.client_config_line": "Client config: %s\n",
7790
"public.cloudflare_mode": "Cloudflare mode:",
@@ -127,6 +140,7 @@
127140
"cloudflared.tunnel_reuse_prompt": "Reuse the existing tunnel?",
128141
"cloudflared.tunnel_reusing_uuid": "Using existing tunnel: %s\n",
129142
"cloudflared.tunnel_url_wait": "Waiting for tunnel URL",
143+
"cloudflared.tunnel_url_ready": "Tunnel started: %s\n",
130144
"cloudflared.tunnel_uuid_missing": "Warning: tunnel UUID was not found; continuing by tunnel name.",
131145
"runtime.generated_config_dir_failed": "generated config dir create failed: %w",
132146
"runtime.marshal_failed": "%s could not be marshaled: %w",

locales/tr.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
"client.server_ws_url_repair_failed": "Geçersiz server adresi: %v\n",
1313
"client.server_ws_url_repair_prompt": "server_ws_url düzeltmek için ROAD server adresini girin (varsayılan: %s): ",
1414
"client.starting": "Client başlatılıyor...",
15+
"client.ready": "ROAD Client hazır.",
16+
"client.game_target_line": "Oyunda/server adresi olarak bunu yazın: %s\n",
17+
"client.game_host_port_line": "Oyun ayrı alan istiyorsa Host/IP=%s Port=%s girin.\n",
18+
"client.remote_line": "Bağlı ROAD server: %s\n",
19+
"client.active_plugin_line": "Aktif plugin: %s\n",
20+
"client.stop_hint": "Durdurmak için Ctrl+C.",
21+
"client.logs_follow": "Teknik loglar aşağıda; normal kullanımda bu kısmı izlemeniz gerekmez.",
1522
"common.error_line": "Hata: %v\n\n",
1623
"common.runtime_line": "Çalışma dizini: %s\n",
1724
"config.template_load_failed": "template config okunamadı (%s): %w",
@@ -72,6 +79,12 @@
7279
"protocol.unsupported": "desteklenmeyen protokol: %s",
7380
"public.auth_header_line": "Auth header: %s\n",
7481
"public.auth_token_line": "Auth token: %s\n",
82+
"public.share_header": "Arkadaşına/client tarafına şu bilgileri verin:",
83+
"public.client_endpoint_line": " ROAD adresi: %s\n",
84+
"public.client_token_line": " Auth token: %s\n",
85+
"public.client_plugin_line": " Plugin: %s\n",
86+
"public.host_header": "Host tarafı:",
87+
"public.advanced_header": "Teknik bilgiler:",
7588
"public.choice_1_4_default_1": "Seçim (1-4, varsayılan: 1): ",
7689
"public.client_config_line": "Client config: %s\n",
7790
"public.cloudflare_mode": "Cloudflare modu:",
@@ -127,6 +140,7 @@
127140
"cloudflared.tunnel_reuse_prompt": "Mevcut tunnel yeniden kullanılsın mı?",
128141
"cloudflared.tunnel_reusing_uuid": "Mevcut tunnel kullanılıyor: %s\n",
129142
"cloudflared.tunnel_url_wait": "Tunnel URL bekleniyor",
143+
"cloudflared.tunnel_url_ready": "Tunnel başladı: %s\n",
130144
"cloudflared.tunnel_uuid_missing": "Uyarı: tunnel UUID bulunamadı, isim ile devam edilecek.",
131145
"runtime.generated_config_dir_failed": "generated config klasörü oluşturulamadı: %w",
132146
"runtime.marshal_failed": "%s yazılamadı: %w",

0 commit comments

Comments
 (0)