mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 23:18:44 -07:00
launcher: Photon AppId setup walkthrough
The two empty "REALTIME APPID / VOICE APPID" textboxes assumed the user already knew what Photon was, where to sign up, and which app type to create. New 5-step wizard fills that gap: 1. What's Photon? — one-paragraph explanation tying back to DorkNet 2. Make an account — Open Dashboard button launches browser 3. Create a Realtime app — numbered click-by-click instructions 4. Create a Voice app — same, with a Skip button (voice is optional) 5. Paste your AppIds — text fields with live regex validation, region picker, Save commits to AppState Hyphens optional on input; we normalise to canonical 8-4-4-4-12 form before writing back. The existing Photon section keeps its three fields for advanced users, with a "Setup walkthrough" button at the top opening the wizard prefilled with current values.
This commit is contained in:
@@ -240,6 +240,15 @@
|
||||
<Setter Property="FontFamily" Value="Segoe UI Variable, Segoe UI" />
|
||||
</Style>
|
||||
|
||||
<!-- Numbered-step bullet used in walkthrough text. -->
|
||||
<Style x:Key="StepBullet" TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{StaticResource Ink}" />
|
||||
<Setter Property="FontSize" Value="13" />
|
||||
<Setter Property="LineHeight" Value="22" />
|
||||
<Setter Property="Margin" Value="0,0,0,6" />
|
||||
<Setter Property="TextWrapping" Value="Wrap" />
|
||||
</Style>
|
||||
|
||||
<!-- Pill-style "status chip" used in section headers. -->
|
||||
<Style x:Key="StatusChip" TargetType="Border">
|
||||
<Setter Property="Background" Value="{StaticResource BgElev2}" />
|
||||
|
||||
@@ -174,14 +174,20 @@
|
||||
<!-- Photon panel -->
|
||||
<Border Style="{StaticResource Panel}" Margin="0,0,0,16">
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource SectionTitle}" Text="Photon AppIds" />
|
||||
<Grid Margin="0,0,0,12">
|
||||
<TextBlock Style="{StaticResource SectionTitle}"
|
||||
Text="Photon AppIds" Margin="0" />
|
||||
<Button HorizontalAlignment="Right" Style="{StaticResource Btn}"
|
||||
Content="Setup walkthrough" Click="OnOpenPhotonWizard"
|
||||
Padding="14,6" FontSize="12" />
|
||||
</Grid>
|
||||
<TextBlock Foreground="{StaticResource InkSoft}" FontSize="12"
|
||||
Margin="0,-8,0,12" TextWrapping="Wrap">
|
||||
Margin="0,0,0,12" TextWrapping="Wrap">
|
||||
Photon Cloud powers in-room realtime sync. Free to register at
|
||||
<Hyperlink NavigateUri="https://dashboard.photonengine.com"
|
||||
Foreground="{StaticResource Brand}"
|
||||
RequestNavigate="OnNavigate">dashboard.photonengine.com</Hyperlink>;
|
||||
DorkNet never bundles one.
|
||||
DorkNet never bundles one. First time? Click "Setup walkthrough" above.
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Style="{StaticResource FieldLabel}" Text="REALTIME APPID" />
|
||||
|
||||
@@ -231,6 +231,22 @@ public partial class MainWindow : Window
|
||||
_state.Save();
|
||||
}
|
||||
|
||||
private void OnOpenPhotonWizard(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var wiz = new PhotonWizard(_state.PhotonAppId, _state.PhotonVoiceAppId, _state.PhotonRegion)
|
||||
{
|
||||
Owner = this,
|
||||
};
|
||||
if (wiz.ShowDialog() == true && wiz.Result is not null)
|
||||
{
|
||||
_state.PhotonAppId = wiz.Result.RealtimeAppId;
|
||||
_state.PhotonVoiceAppId = wiz.Result.VoiceAppId;
|
||||
_state.PhotonRegion = wiz.Result.Region;
|
||||
_state.Save();
|
||||
ReflectStateInUi();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHostingModeChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// RadioButton.Checked fires twice on selection-change (once for
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
<Window x:Class="DorkNet.Launcher.PhotonWizard"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Photon setup"
|
||||
Width="640" Height="560"
|
||||
MinWidth="540" MinHeight="460"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Background="{StaticResource Bg}"
|
||||
TextElement.Foreground="{StaticResource Ink}"
|
||||
FontFamily="Segoe UI Variable, Segoe UI"
|
||||
TextOptions.TextFormattingMode="Ideal"
|
||||
TextOptions.TextRenderingMode="ClearType"
|
||||
ResizeMode="CanResize"
|
||||
ShowInTaskbar="False">
|
||||
|
||||
<Grid>
|
||||
<Grid.Background>
|
||||
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
|
||||
<GradientStop Color="#2A1E48" Offset="0" />
|
||||
<GradientStop Color="#16102A" Offset="0.6" />
|
||||
<GradientStop Color="#16102A" Offset="1" />
|
||||
</LinearGradientBrush>
|
||||
</Grid.Background>
|
||||
|
||||
<Grid Margin="28,22">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Header -->
|
||||
<Grid Grid.Row="0" Margin="0,0,0,16">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<TextBlock Text="Photon setup" FontSize="20" FontWeight="Bold"
|
||||
Foreground="{StaticResource WordmarkBrush}" />
|
||||
<Border Style="{StaticResource StatusChip}" Margin="14,0,0,0"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock x:Name="StepIndicator" FontSize="10" FontWeight="Bold"
|
||||
Foreground="{StaticResource Brand}"
|
||||
Text="STEP 1 OF 5" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
<Button HorizontalAlignment="Right" Style="{StaticResource GhostBtn}"
|
||||
Content="✕" Click="OnClose" FontSize="14" Padding="10,4" />
|
||||
</Grid>
|
||||
|
||||
<!-- Step card — only one Visible at a time -->
|
||||
<Border Grid.Row="1" Style="{StaticResource Panel}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<Grid>
|
||||
<!-- Step 1: What is Photon? -->
|
||||
<StackPanel x:Name="Step1" Visibility="Visible">
|
||||
<TextBlock Text="What's Photon?" FontSize="22" FontWeight="Bold"
|
||||
Margin="0,0,0,16" />
|
||||
<TextBlock Foreground="{StaticResource Ink}" FontSize="14"
|
||||
TextWrapping="Wrap" LineHeight="22" Margin="0,0,0,12"
|
||||
Text="Rec Room rooms run on Photon — a real-time service that keeps players in sync. DorkNet doesn't include one because Photon's free tier is plenty for friend groups and each host should use their own account." />
|
||||
<TextBlock Foreground="{StaticResource Ink}" FontSize="14"
|
||||
TextWrapping="Wrap" LineHeight="22" Margin="0,0,0,12"
|
||||
Text="This wizard walks you through making a free Photon account, creating one (or two) apps, and pasting the keys back here. Takes about two minutes." />
|
||||
<Border CornerRadius="10" Padding="14,12"
|
||||
Background="{StaticResource BgDeeper}"
|
||||
BorderBrush="{StaticResource Border}" BorderThickness="1"
|
||||
Margin="0,8,0,0">
|
||||
<TextBlock Foreground="{StaticResource InkSoft}" FontSize="12"
|
||||
TextWrapping="Wrap"
|
||||
Text="What you'll end up with: a 32-character "AppId" for realtime, and optionally a second one for voice chat. You'll paste both into the form at the end." />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Step 2: Make an account -->
|
||||
<StackPanel x:Name="Step2" Visibility="Collapsed">
|
||||
<TextBlock Text="Make a Photon account" FontSize="22" FontWeight="Bold"
|
||||
Margin="0,0,0,16" />
|
||||
<TextBlock Foreground="{StaticResource Ink}" FontSize="14"
|
||||
TextWrapping="Wrap" LineHeight="22" Margin="0,0,0,16"
|
||||
Text="Click the button below to open the Photon dashboard. If you already have an account, sign in; otherwise hit "Sign up" and use any email + password." />
|
||||
<Button Style="{StaticResource PrimaryBtn}"
|
||||
Content="Open dashboard.photonengine.com"
|
||||
Click="OnOpenPhotonDashboard"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock Foreground="{StaticResource InkSoft}" FontSize="13"
|
||||
TextWrapping="Wrap" LineHeight="20" Margin="0,20,0,0"
|
||||
Text="Photon won't ask for a credit card. The free plan covers 20 simultaneous players in a room, which is more than enough for most servers." />
|
||||
<TextBlock Foreground="{StaticResource InkFaint}" FontSize="12"
|
||||
TextWrapping="Wrap" LineHeight="18" Margin="0,12,0,0"
|
||||
Text="Already signed in? Just click Next." />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Step 3: Create Realtime app -->
|
||||
<StackPanel x:Name="Step3" Visibility="Collapsed">
|
||||
<TextBlock Text="Create a Realtime app" FontSize="22" FontWeight="Bold"
|
||||
Margin="0,0,0,16" />
|
||||
<TextBlock Foreground="{StaticResource Ink}" FontSize="14"
|
||||
TextWrapping="Wrap" LineHeight="22" Margin="0,0,0,14"
|
||||
Text="On the Photon dashboard:" />
|
||||
<StackPanel Margin="6,0,0,12">
|
||||
<TextBlock Style="{StaticResource StepBullet}" Text="1. Click the orange "Create a new app" button (top right)." />
|
||||
<TextBlock Style="{StaticResource StepBullet}" Text="2. Under "Photon Type" choose Photon Realtime." />
|
||||
<TextBlock Style="{StaticResource StepBullet}" Text="3. Name it anything (e.g. "DorkNet Realtime")." />
|
||||
<TextBlock Style="{StaticResource StepBullet}" Text="4. Click Create." />
|
||||
<TextBlock Style="{StaticResource StepBullet}" Text="5. Back on the dashboard, click the new app's tile — the AppId is the long string near the top, looking like 12ab34cd-56ef-..." />
|
||||
<TextBlock Style="{StaticResource StepBullet}" Text="6. Copy that AppId. You'll paste it on the last step." />
|
||||
</StackPanel>
|
||||
<Border CornerRadius="10" Padding="14,12"
|
||||
Background="{StaticResource CardBgHost}"
|
||||
BorderBrush="{StaticResource Brand}" BorderThickness="1">
|
||||
<TextBlock Foreground="{StaticResource Ink}" FontSize="13"
|
||||
TextWrapping="Wrap"
|
||||
Text="💡 You only need to do this once. The same AppId works every time you host." />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Step 4: Voice app -->
|
||||
<StackPanel x:Name="Step4" Visibility="Collapsed">
|
||||
<TextBlock Text="Create a Voice app (optional)" FontSize="22" FontWeight="Bold"
|
||||
Margin="0,0,0,16" />
|
||||
<TextBlock Foreground="{StaticResource Ink}" FontSize="14"
|
||||
TextWrapping="Wrap" LineHeight="22" Margin="0,0,0,14"
|
||||
Text="Voice chat needs a separate Photon app. Skip this step if you don't care about voice." />
|
||||
<StackPanel Margin="6,0,0,12">
|
||||
<TextBlock Style="{StaticResource StepBullet}" Text="1. Back on the Photon dashboard, click "Create a new app" again." />
|
||||
<TextBlock Style="{StaticResource StepBullet}" Text="2. This time pick Photon Voice." />
|
||||
<TextBlock Style="{StaticResource StepBullet}" Text="3. Name it (e.g. "DorkNet Voice") and Create." />
|
||||
<TextBlock Style="{StaticResource StepBullet}" Text="4. Copy the Voice app's AppId — also a long 32-character string." />
|
||||
</StackPanel>
|
||||
<TextBlock Foreground="{StaticResource InkSoft}" FontSize="13"
|
||||
TextWrapping="Wrap" LineHeight="20" Margin="0,8,0,0"
|
||||
Text="Without voice, players can still chat via Discord or any other voice call. The room sync (movement, ink, gizmos) all runs through the Realtime app." />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Step 5: Paste -->
|
||||
<StackPanel x:Name="Step5" Visibility="Collapsed">
|
||||
<TextBlock Text="Paste your AppIds" FontSize="22" FontWeight="Bold"
|
||||
Margin="0,0,0,16" />
|
||||
|
||||
<TextBlock Style="{StaticResource FieldLabel}" Text="REALTIME APPID" />
|
||||
<TextBox x:Name="WizRealtime" Margin="0,0,0,4"
|
||||
FontFamily="Cascadia Mono, Consolas" FontSize="12"
|
||||
TextChanged="OnAppIdChanged" />
|
||||
<TextBlock x:Name="WizRealtimeStatus" FontSize="11"
|
||||
Foreground="{StaticResource InkFaint}"
|
||||
Margin="0,0,0,12"
|
||||
Text="32-character ID with hyphens, like 12ab34cd-56ef-78ab-90cd-12ef34ab56cd" />
|
||||
|
||||
<TextBlock Style="{StaticResource FieldLabel}" Text="VOICE APPID (LEAVE BLANK TO SKIP)" />
|
||||
<TextBox x:Name="WizVoice" Margin="0,0,0,4"
|
||||
FontFamily="Cascadia Mono, Consolas" FontSize="12"
|
||||
TextChanged="OnAppIdChanged" />
|
||||
<TextBlock x:Name="WizVoiceStatus" FontSize="11"
|
||||
Foreground="{StaticResource InkFaint}"
|
||||
Margin="0,0,0,12"
|
||||
Text="Optional — leave empty if you skipped step 4." />
|
||||
|
||||
<TextBlock Style="{StaticResource FieldLabel}" Text="CLOUD REGION" />
|
||||
<ComboBox x:Name="WizRegion">
|
||||
<ComboBoxItem Content="US" Tag="us" />
|
||||
<ComboBoxItem Content="EU" Tag="eu" />
|
||||
<ComboBoxItem Content="Asia" Tag="asia" />
|
||||
<ComboBoxItem Content="Japan" Tag="jp" />
|
||||
<ComboBoxItem Content="South America" Tag="sa" />
|
||||
<ComboBoxItem Content="Korea" Tag="kr" />
|
||||
<ComboBoxItem Content="India" Tag="in" />
|
||||
<ComboBoxItem Content="Australia" Tag="au" />
|
||||
</ComboBox>
|
||||
<TextBlock Foreground="{StaticResource InkFaint}" FontSize="11"
|
||||
Margin="0,4,0,0" TextWrapping="Wrap"
|
||||
Text="Pick the closest region to where most of your players live." />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
<!-- Footer with nav buttons -->
|
||||
<Grid Grid.Row="2" Margin="0,16,0,0">
|
||||
<Button x:Name="PrevBtn" HorizontalAlignment="Left"
|
||||
Style="{StaticResource GhostBtn}"
|
||||
Content="← Back" Click="OnPrev" Visibility="Collapsed" />
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button x:Name="SkipBtn" Style="{StaticResource GhostBtn}"
|
||||
Content="Skip" Click="OnNext" Visibility="Collapsed"
|
||||
Margin="0,0,10,0" />
|
||||
<Button x:Name="NextBtn" Style="{StaticResource PrimaryBtn}"
|
||||
Content="Next →" Click="OnNext" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,189 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using DorkNet.Launcher.Backend;
|
||||
|
||||
namespace DorkNet.Launcher;
|
||||
|
||||
/// <summary>Five-step walkthrough for first-time Photon setup. Opens
|
||||
/// dashboard.photonengine.com on step 2, walks the user through app
|
||||
/// creation in steps 3–4, then collects + validates AppIds on step 5.
|
||||
///
|
||||
/// <para>Result lives in <see cref="Result"/> when <see cref="DialogResult"/>
|
||||
/// is true. Caller (MainWindow) writes it back into AppState.</para></summary>
|
||||
public partial class PhotonWizard : Window
|
||||
{
|
||||
public PhotonResult? Result { get; private set; }
|
||||
|
||||
// Photon AppIds are GUIDs — accept with or without hyphens; user
|
||||
// gets confused if we're strict about formatting. We render the
|
||||
// canonical form back into AppState.
|
||||
private static readonly Regex AppIdRegex =
|
||||
new("^[0-9a-fA-F]{8}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{12}$",
|
||||
RegexOptions.Compiled);
|
||||
|
||||
private readonly StackPanel[] _steps;
|
||||
private int _stepIndex;
|
||||
|
||||
public PhotonWizard(string? existingRealtime, string? existingVoice, string? existingRegion)
|
||||
{
|
||||
InitializeComponent();
|
||||
_steps = new[] { Step1, Step2, Step3, Step4, Step5 };
|
||||
|
||||
// Pre-fill so a user re-opening the wizard sees their existing
|
||||
// values — they might just want to fix the region or swap apps.
|
||||
WizRealtime.Text = existingRealtime ?? "";
|
||||
WizVoice.Text = existingVoice ?? "";
|
||||
SelectRegion(existingRegion ?? "us");
|
||||
UpdateAppIdStatuses();
|
||||
ShowStep(0);
|
||||
}
|
||||
|
||||
private void ShowStep(int index)
|
||||
{
|
||||
_stepIndex = Math.Clamp(index, 0, _steps.Length - 1);
|
||||
for (var i = 0; i < _steps.Length; i++)
|
||||
_steps[i].Visibility = i == _stepIndex ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
StepIndicator.Text = $"STEP {_stepIndex + 1} OF {_steps.Length}";
|
||||
PrevBtn.Visibility = _stepIndex == 0 ? Visibility.Collapsed : Visibility.Visible;
|
||||
|
||||
// Step 4 (Voice) is optional — surface a Skip button so the user
|
||||
// doesn't feel stuck if they don't want voice chat. Final step
|
||||
// swaps Next for a Save action.
|
||||
SkipBtn.Visibility = _stepIndex == 3 ? Visibility.Visible : Visibility.Collapsed;
|
||||
NextBtn.Content = _stepIndex == _steps.Length - 1 ? "Save" : "Next →";
|
||||
}
|
||||
|
||||
private void OnPrev(object sender, RoutedEventArgs e) => ShowStep(_stepIndex - 1);
|
||||
|
||||
private void OnNext(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_stepIndex < _steps.Length - 1)
|
||||
{
|
||||
ShowStep(_stepIndex + 1);
|
||||
return;
|
||||
}
|
||||
TrySave();
|
||||
}
|
||||
|
||||
private void OnClose(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DialogResult = false;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void OnOpenPhotonDashboard(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo("https://dashboard.photonengine.com")
|
||||
{ UseShellExecute = true });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(this, "Could not open the dashboard: " + ex.Message,
|
||||
"Photon setup", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAppIdChanged(object sender, TextChangedEventArgs e) => UpdateAppIdStatuses();
|
||||
|
||||
private void UpdateAppIdStatuses()
|
||||
{
|
||||
var realtime = WizRealtime.Text.Trim();
|
||||
if (string.IsNullOrEmpty(realtime))
|
||||
{
|
||||
WizRealtimeStatus.Text = "32-character ID with hyphens, like 12ab34cd-56ef-78ab-90cd-12ef34ab56cd";
|
||||
WizRealtimeStatus.Foreground = (System.Windows.Media.Brush)FindResource("InkFaint");
|
||||
}
|
||||
else if (AppIdRegex.IsMatch(realtime))
|
||||
{
|
||||
WizRealtimeStatus.Text = "✓ Looks like a valid AppId.";
|
||||
WizRealtimeStatus.Foreground = (System.Windows.Media.Brush)FindResource("Ok");
|
||||
}
|
||||
else
|
||||
{
|
||||
WizRealtimeStatus.Text = "⚠ That doesn't look like a Photon AppId. Should be 32 hex characters (with or without hyphens).";
|
||||
WizRealtimeStatus.Foreground = (System.Windows.Media.Brush)FindResource("Danger");
|
||||
}
|
||||
|
||||
var voice = WizVoice.Text.Trim();
|
||||
if (string.IsNullOrEmpty(voice))
|
||||
{
|
||||
WizVoiceStatus.Text = "Optional — leave empty if you skipped step 4.";
|
||||
WizVoiceStatus.Foreground = (System.Windows.Media.Brush)FindResource("InkFaint");
|
||||
}
|
||||
else if (AppIdRegex.IsMatch(voice))
|
||||
{
|
||||
WizVoiceStatus.Text = "✓ Looks like a valid AppId.";
|
||||
WizVoiceStatus.Foreground = (System.Windows.Media.Brush)FindResource("Ok");
|
||||
}
|
||||
else
|
||||
{
|
||||
WizVoiceStatus.Text = "⚠ That doesn't look like a Photon AppId.";
|
||||
WizVoiceStatus.Foreground = (System.Windows.Media.Brush)FindResource("Danger");
|
||||
}
|
||||
}
|
||||
|
||||
private void TrySave()
|
||||
{
|
||||
var realtime = WizRealtime.Text.Trim();
|
||||
var voice = WizVoice.Text.Trim();
|
||||
if (string.IsNullOrEmpty(realtime))
|
||||
{
|
||||
MessageBox.Show(this,
|
||||
"A Realtime AppId is required — without it nobody can connect to your server. Voice is optional.",
|
||||
"Photon setup", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
if (!AppIdRegex.IsMatch(realtime))
|
||||
{
|
||||
MessageBox.Show(this,
|
||||
"Your Realtime AppId doesn't look right. It should be 32 hex characters (with or without hyphens). Double-check that you copied the entire string from the Photon dashboard.",
|
||||
"Photon setup", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(voice) && !AppIdRegex.IsMatch(voice))
|
||||
{
|
||||
MessageBox.Show(this,
|
||||
"Your Voice AppId doesn't look right. Either fix it or clear the field to skip voice.",
|
||||
"Photon setup", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var region = WizRegion.SelectedItem is ComboBoxItem item && item.Tag is not null
|
||||
? item.Tag.ToString() ?? "us"
|
||||
: "us";
|
||||
|
||||
Result = new PhotonResult(Normalise(realtime), Normalise(voice), region);
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void SelectRegion(string tag)
|
||||
{
|
||||
foreach (var item in WizRegion.Items.OfType<ComboBoxItem>())
|
||||
{
|
||||
if (string.Equals(item.Tag?.ToString(), tag, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
item.IsSelected = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Render the AppId in canonical 8-4-4-4-12 form regardless
|
||||
/// of how the user pasted it — the server's Photon SDK accepts both
|
||||
/// but normalising keeps the rest of the launcher tidy.</summary>
|
||||
private static string Normalise(string appId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(appId)) return "";
|
||||
var stripped = appId.Replace("-", "").Trim();
|
||||
if (stripped.Length != 32) return appId;
|
||||
return $"{stripped[..8]}-{stripped[8..12]}-{stripped[12..16]}-{stripped[16..20]}-{stripped[20..]}";
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record PhotonResult(string RealtimeAppId, string VoiceAppId, string Region);
|
||||
Reference in New Issue
Block a user