This commit is contained in:
龙腾猫跃
2025-10-24 22:39:55 +08:00
parent e5dccf3a13
commit 8d2eb85aec
46 changed files with 1435 additions and 1584 deletions
@@ -1,19 +0,0 @@
<local:MyPageRight
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PCL" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" x:Class="PageLinkFeedback"
PanScroll="PanBack">
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
<StackPanel Name="PanMain" Margin="25,25,25,10" Grid.IsSharedSizeScope="True">
<local:MyCard Title="关于" Margin="0,0,0,15">
<StackPanel Margin="20,40,18,18">
</StackPanel>
</local:MyCard>
<local:MyCard Title="发送反馈" Margin="0,0,0,15">
<StackPanel Margin="20,40,18,18">
</StackPanel>
</local:MyCard>
</StackPanel>
</local:MyScrollViewer>
</local:MyPageRight>
@@ -1,2 +0,0 @@
Public Class PageLinkFeedback
End Class
@@ -1,234 +0,0 @@
<local:MyPageRight
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PCL" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" x:Class="PageLinkHiper"
d:DesignWidth="800" d:DesignHeight="800">
<Grid>
<Grid x:Name="PanLoad">
<local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" x:Name="CardLoad">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="25" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="18" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="1*" />
<RowDefinition Height="23" />
</Grid.RowDefinitions>
<local:MyLoading Grid.Column="1" Grid.Row="1" x:Name="Load" Grid.RowSpan="3" Margin="0,0,0,-30" HorizontalAlignment="Center"
Text="" TextError="" TextErrorInherit="False" AutoRun="False" IsHitTestVisible="False" />
<TextBlock x:Name="LabLoadTitle" Grid.Column="3" Grid.Row="1" Grid.ColumnSpan="2" VerticalAlignment="Center" Text="正在创建联机房间" FontSize="19"
Margin="0,0,25,1" Foreground="{Binding Foreground, ElementName=Load}" IsHitTestVisible="False" />
<TextBlock x:Name="LabLoadDesc" Grid.Column="3" Grid.ColumnSpan="3" Grid.Row="2" VerticalAlignment="Center" Margin="0,5,10,-5" Text="下载联机模块中……" FontSize="14"
LineHeight="19" MaxWidth="400" MaxHeight="220" TextWrapping="Wrap" Foreground="{StaticResource ColorBrushGray3}" IsHitTestVisible="False" />
<local:MyIconButton Grid.Column="5" Grid.Row="1" x:Name="BtnLoadCancel" Height="28" Width="28" VerticalAlignment="Top"
Theme="Black" Opacity="0.5"
ToolTip="取消" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="100"
LogoScale="0.72" Logo="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" />
<Grid Height="12" VerticalAlignment="Bottom" Grid.ColumnSpan="100" Grid.RowSpan="100" Margin="0,0,0,-0.6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0*" x:Name="ColumnProgressA" />
<ColumnDefinition Width="1*" x:Name="ColumnProgressB" />
</Grid.ColumnDefinitions>
<Grid.Clip>
<RectangleGeometry RadiusX="6" RadiusY="6" x:Name="RectProgressClip" />
</Grid.Clip>
<Rectangle Fill="{Binding Foreground, ElementName=Load}" VerticalAlignment="Bottom" Height="2" />
</Grid>
</Grid>
</local:MyCard>
</Grid>
<Grid x:Name="PanContent">
<Grid x:Name="PanCert">
<local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25" />
<ColumnDefinition Width="Auto" /> <!--Logo-->
<ColumnDefinition Width="10" />
<ColumnDefinition Width="1*" /> <!--文本区 ↓↓-->
<ColumnDefinition Width="Auto" /> <!--按钮 1-->
<ColumnDefinition Width="Auto" /> <!--按钮 2-->
<ColumnDefinition Width="25" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="21" />
<RowDefinition Height="Auto" /> <!--标题栏-->
<RowDefinition Height="Auto" /> <!--文本区-->
<RowDefinition Height="Auto" /> <!--文本框-->
<RowDefinition Height="Auto" /> <!--按钮-->
<RowDefinition Height="21" />
</Grid.RowDefinitions>
<Path Grid.Column="1" Grid.Row="1" Height="25" Width="25"
Stretch="Uniform" Fill="{DynamicResource ColorBrush3}"
Data="M856.3 291.1c-12-29.6-29.5-56-52.2-78.6-22.6-22.6-49.1-40.2-78.6-52.2-28.5-11.5-58.5-17.4-89.3-17.4s-60.8 5.9-89.3 17.4c-29.6 12-56 29.5-78.6 52.2-22.6 22.6-40.2 49.1-52.2 78.6-11.5 28.5-17.4 58.5-17.4 89.3 0 30.8 5.9 60.8 17.4 89.3 1.5 3.6 3 7.2 4.6 10.7l-238 238c-19 19-30.4 43.4-32.3 68.7-2 26.9 6.9 51.6 25 69.5C191.9 873 214 881 236.9 881c27 0 55.2-11.2 77.1-33l40.4-40.4c12.5-12.5 17.6-30.4 13.2-46.6-3.2-11.7 1.5-25.9 11.8-36.3 10.3-10.3 24.5-15 36.2-11.8 16.3 4.4 34.2-0.7 46.8-13.3l95.1-95.1c25.3 8.9 51.6 13.4 78.6 13.4 30.8 0 60.8-5.9 89.3-17.4 29.6-12 56-29.5 78.6-52.2 22.6-22.6 40.2-49.1 52.2-78.6 11.5-28.5 17.4-58.5 17.4-89.3 0.1-30.7-5.7-60.8-17.3-89.3zM427.2 664.2c-28.7-7.1-60.4 2.5-83 25.1-22.7 22.7-32.2 54.4-25.1 83l-40.3 40.3c-20.7 20.7-51.8 24.5-68 8.4-7.6-7.5-11.3-18.3-10.4-30.4 1-13.4 7.3-26.6 17.8-37l229.2-229.2c6.4 8.4 13.4 16.3 20.9 23.9 12.7 12.7 26.6 23.8 41.6 33.2l-82.7 82.7zM810 450.9c-9.5 23.3-23.3 44.2-41.2 62.1-17.9 17.9-38.7 31.7-62.1 41.2-22.5 9.1-46.2 13.7-70.5 13.7-24.3 0-48-4.6-70.5-13.7-2.5-1.7-5.3-2.8-8.1-3.5-20.1-9.3-38.2-21.9-54-37.7-17.9-17.9-31.7-38.7-41.2-62.1-9.1-22.5-13.7-46.2-13.7-70.5 0-24.3 4.6-48 13.7-70.5 9.5-23.3 23.3-44.2 41.2-62.1 17.9-17.9 38.7-31.7 62.1-41.2 22.5-9.1 46.2-13.7 70.5-13.7s48 4.6 70.5 13.7c23.3 9.5 44.2 23.3 62.1 41.2 17.9 17.9 31.7 38.7 41.2 62.1 9.1 22.5 13.7 46.2 13.7 70.5 0 24.3-4.6 48-13.7 70.5z M551.5 295.7c-22.6 22.6-35.1 52.7-35.1 84.7s12.5 62.1 35.1 84.7c23.4 23.4 54 35 84.7 35 30.7 0 61.4-11.7 84.7-35 22.6-22.6 35.1-52.7 35.1-84.7s-12.5-62.1-35.1-84.7c-46.7-46.7-122.7-46.7-169.4 0z m134.1 134.1c-27.2 27.2-71.5 27.2-98.7 0-13.2-13.2-20.4-30.7-20.4-49.4 0-18.6 7.3-36.2 20.4-49.4 13.6-13.6 31.5-20.4 49.4-20.4s35.7 6.8 49.4 20.4c13.2 13.2 20.4 30.7 20.4 49.4-0.1 18.7-7.3 36.2-20.5 49.4z" />
<TextBlock Grid.Column="3" Grid.ColumnSpan="3" Grid.Row="1" x:Name="LabCertTitle"
Text="输入索引码" FontSize="20" Foreground="{DynamicResource ColorBrush3}" />
<TextBlock Grid.Column="3" Grid.ColumnSpan="3" Grid.Row="2" Margin="0,10,0,6"
Text="你需要获取索引码才能使用 HiPer。&#xa;如果实在没有索引码,可以在左侧选择 IOI 方式联机。" x:Name="LabCertDesc"
FontSize="14" LineHeight="19" MaxWidth="400" TextWrapping="Wrap" Foreground="{StaticResource ColorBrushGray2}" />
<local:MyTextBox x:Name="TextCert" Grid.Column="3" Grid.ColumnSpan="3" Grid.Row="3"
Margin="0,0,0,12" Width="400" MaxLength="100" Height="28">
<local:MyTextBox.ValidateRules>
<local:ValidateNullOrWhiteSpace />
<local:ValidateRegex Regex="^[^\u4e00-\u9fa5]{6,}$" />
</local:MyTextBox.ValidateRules>
</local:MyTextBox>
<local:MyButton Grid.Column="4" Grid.Row="4" Margin="0,0,15,0" Text="获取索引码" Padding="13,8" local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://shimo.im/docs/KrkEVXz1K0frowAJ" />
<local:MyButton x:Name="BtnCertDone" Grid.Column="5" Grid.Row="4" Text="确定" ColorType="Highlight" Padding="13,8" IsEnabled="False" />
</Grid>
</local:MyCard>
</Grid>
<Grid x:Name="PanSelect" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="480" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="120" />
<RowDefinition Height="20" />
<RowDefinition Height="120" />
<RowDefinition Height="20" />
<RowDefinition Height="Auto" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<local:MyCard x:Name="BtnSelectCreate" Grid.Column="1" Grid.Row="1">
<Grid Margin="35,0,25,0" IsHitTestVisible="False">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="30" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Path Stretch="Uniform" Fill="{DynamicResource ColorBrush3}" Margin="0,25,0,12"
Data="M904.479163 416.014006c-41.499044-0.199995-78.198199 20.799521-99.797701 52.698786-4.499896 6.699846-13.299694 9.09979-20.499527 5.399876L398.590818 280.017139c-6.999839-3.499919-10.39976-11.69973-7.999816-19.199557 6.099859-19.199558 9.399783-39.599088 9.399783-60.7986C399.990785 88.721546 308.092902-1.676371 196.795466 0.023589 87.797977 1.72355 0 90.621502 0 200.018982c0 92.597867 62.898551 170.396075 148.196586 193.195549 6.999839 1.899956 11.799728 8.199811 11.799728 15.399646v253.994148c0 7.199834-4.799889 13.399691-11.599733 15.399646C81.198129 697.007533 31.999263 758.806109 31.999263 832.004423c0 88.597959 71.998341 160.296307 160.5963 159.996314 88.09797-0.299993 159.396328-71.798346 159.396328-159.996314v-1.399968c-0.099998-6.099859 3.399922-11.69973 8.799797-14.399668l425.990187-214.495059c6.999839-3.499919 15.499643-1.499965 20.099537 4.79989 21.799498 29.999309 57.198682 49.598857 97.197761 49.598857 66.898459 0 120.997213-54.69874 119.997235-121.797194-1.099975-65.198498-54.498745-118.097279-119.597245-118.297275zM103.797609 296.216766C78.198199 270.517358 63.998526 236.318146 63.998526 200.018982c0-36.299164 14.199673-70.498376 39.799083-96.197784C129.497017 78.12179 163.696229 64.022115 199.995393 64.022115s70.498376 14.099675 96.197784 39.799083c25.699408 25.699408 39.799083 59.798622 39.799083 96.197784 0 15.499643-2.59994 30.499297-7.499828 44.69897-6.599848 19.199558-17.599595 36.699155-32.399253 51.498814-0.899979 0.899979-1.799959 1.699961-2.59994 2.59994-19.499551 18.399576-43.498998 30.499297-69.498399 35.099192-7.79982 1.399968-15.899634 2.099952-23.999447 2.099951-13.799682 0-27.199373-1.999954-39.999079-5.999862-20.999516-6.399853-40.199074-17.899588-56.198705-33.799221z m181.595816 557.887148c-9.99977 42.299026-48.098892 73.898298-93.397848 73.898298-52.898781 0-95.997788-43.099007-95.997789-95.997789 0-41.699039 26.799383-77.298219 63.998526-90.497915 9.99977-3.599917 20.799521-5.499873 31.999263-5.499873s21.999493 1.899956 31.999263 5.499873c22.699477 7.999816 41.499044 24.399438 52.698786 45.398954 7.199834 13.499689 11.29974 28.799337 11.299739 45.098961 0 7.599825-0.899979 14.999654-2.59994 22.099491z m41.299049-108.597498c-20.799521-32.399254-52.998779-56.698694-90.897906-67.498445-6.899841-1.999954-11.799728-8.199811-11.799728-15.399646V412.314091c0-7.699823 5.499873-14.299671 12.9997-15.699638 42.299026-7.899818 79.898159-29.09933 108.297505-59.098638 4.799889-5.099883 12.499712-6.399853 18.69957-3.199927l377.291308 189.995623c11.69973 5.899864 11.69973 22.699477 0 28.599342l-393.990924 198.395429c-7.299832 3.699915-16.199627 1.199972-20.599525-5.799866zM903.979175 592.009952c-19.699546 0-37.099145-10.299763-47.098915-25.699408-5.599871-8.6998-8.899795-19.09956-8.899795-30.299302 0-9.499781 2.399945-18.499574 6.599848-26.399392C863.980096 492.012255 882.579668 480.012532 903.979175 480.012532c30.899288 0 55.99871 25.099422 55.99871 55.99871s-25.099422 55.99871-55.99871 55.99871z" />
<TextBlock Grid.Row="1" Text="创建房间" Margin="0,0,0,25" FontSize="20" Foreground="{DynamicResource ColorBrush3}" HorizontalAlignment="Center" />
<TextBlock Grid.RowSpan="2" Grid.Column="2" LineHeight="23" FontSize="14" TextWrapping="Wrap" VerticalAlignment="Center" Margin="0,7,0,0"
Text="1、打开游戏,在暂停画面选择【对局域网开放】&#xa;2、点击这里,并输入游戏显示的端口号&#xa;3、将联机码发送给你的朋友" />
</Grid>
</local:MyCard>
<local:MyCard x:Name="BtnSelectJoin" Grid.Column="1" Grid.Row="3">
<Grid Margin="35,0,25,0" IsHitTestVisible="False">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="30" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Path Stretch="Uniform" Fill="{DynamicResource ColorBrush3}" Margin="0,25,0,12"
Data="M896 64H544c-17.7 0-32 14.3-32 32s14.3 32 32 32h352c35.3 0 64 28.7 64 64v640c0 35.3-28.7 64-64 64H544c-17.7 0-32 14.3-32 32s14.3 32 32 32h352c70.7 0 128-57.3 128-128V192c0-70.7-57.3-128-128-128z M821.3 488.3L458.9 160.7c-41.2-37.2-106.9-8-106.9 47.5V339c0 4.4-3.6 8-8 8H32c-17.7 0-32 14.3-32 32v266c0 17.7 14.3 32 32 32h312c4.4 0 8 3.6 8 8v130.9c0 55.5 65.8 84.7 106.9 47.5l362.4-327.6c14.1-12.8 14.1-34.8 0-47.5zM64 597V427c0-8.8 7.2-16 16-16h304c17.7 0 32-14.3 32-32V244.9c0-13.9 16.4-21.2 26.7-11.9L746 506.1c3.5 3.2 3.5 8.7 0 11.9L442.7 791c-10.3 9.3-26.7 2-26.7-11.9V645c0-17.7-14.3-32-32-32H80c-8.8 0-16-7.2-16-16z" />
<TextBlock Grid.Row="1" Text="加入房间" Margin="0,0,0,25" FontSize="20" Foreground="{DynamicResource ColorBrush3}" HorizontalAlignment="Center" />
<TextBlock Grid.RowSpan="2" Grid.Column="2" LineHeight="23" FontSize="14" TextWrapping="Wrap" VerticalAlignment="Center" Margin="0,7,10,0" HorizontalAlignment="Center"
Text="点击这里,然后输入朋友发送给你的联机码" />
</Grid>
</local:MyCard>
<local:MyCard x:Name="BtnSelectReturn" Grid.Column="1" Grid.Row="5" HorizontalAlignment="Center">
<StackPanel Orientation="Horizontal" IsHitTestVisible="False" Margin="15,12">
<Path Stretch="Uniform" Fill="{DynamicResource ColorBrush3}" Height="13" Margin="0,0,8,0"
Data="M268.70922 566.468085h929.588652c36.312057 0 72.624113-29.049645 72.624114-72.624113 0-36.312057-29.049645-72.624113-72.624114-72.624114H305.021277l297.758865-297.758865c29.049645-29.049645 29.049645-72.624113 0-101.673759-29.049645-29.049645-72.624113-29.049645-101.673759 0L72.624113 450.269504c-14.524823 14.524823-21.787234 36.312057-21.787234 58.09929 0 21.787234 0 43.574468 21.787234 58.099291l428.48227 428.48227c29.049645 29.049645 72.624113 29.049645 101.673759 0 29.049645-29.049645 29.049645-72.624113 0-101.673759l-334.070922-326.808511z" />
<TextBlock Text="修改索引码" FontSize="14" Foreground="{DynamicResource ColorBrush3}" />
<TextBlock Text="(28F…)" x:Name="LabSelectCode" FontSize="12" Foreground="{DynamicResource ColorBrushGray4}" VerticalAlignment="Bottom" Margin="5,0,0,0.5" />
</StackPanel>
</local:MyCard>
</Grid>
<Grid x:Name="PanFinish" Visibility="Collapsed" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,-50,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,20">
<StackPanel Orientation="Horizontal" Margin="3,0">
<StackPanel x:Name="BtnFinishQuality" Orientation="Horizontal" Background="{StaticResource ColorBrushSemiTransparent}"
ToolTip="连接状况" ToolTipService.Placement="Center" ToolTipService.InitialShowDelay="100" ToolTipService.VerticalOffset="-35">
<Path IsHitTestVisible="False" Height="14" Width="14" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" Margin="12,1,7,0"
Data="M512 128c212.064 0 384 171.936 384 384s-173.632 384-384 384C299.936 896 128 724.064 128 512S299.936 128 512 128z m-126.4 575.968c-1.728 40.8 3.52 76.8 36.32 115.2 28.576 8.32 58.816 12.832 90.08 12.832 81.984 0 156.8-30.848 213.408-81.536-77.12 7.2-161.6-5.952-289.216-61.856-11.616 7.52-18.976 12.608-50.592 15.36z m-193.44-202.368c0 123.744 53.536 214.272 135.744 272.16-6.08-25.6-8.352-48.448-4.96-91.68-21.344-17.6-34.944-39.264-34.624-82.08-50.816-42.88-82.88-75.712-96.16-98.4zM544 448c-14.944 0-29.12-3.424-41.728-9.504-10.976 12.96-21.376 26.432-31.2 40.512-4.832 6.912-15.36 23.776-31.616 50.624a95.872 95.872 0 0 1 36.384 106.432c155.52 68.16 302.56 62.048 325.952 11.904 23.36-50.112-35.968-139.776-189.504-228.48A95.68 95.68 0 0 1 544 448z m-160 128a32 32 0 1 0 0 64 32 32 0 0 0 0-64z m-161.92-199.2c-16.576 35.52 16.96 99.68 94.08 163.2a95.936 95.936 0 0 1 59.616-27.648c19.936-35.008 30.72-52.736 42.88-70.08 10.944-15.648 22.528-30.656 38.432-49.44-5.696-12.128-8.96-25.6-9.088-39.872-118.176-30.4-206.88-16.96-225.92 23.84z m415.136-47.84c1.824 7.36 3.296 19.104 2.336 32.416 88.96 48.704 153.76 105.056 192.32 160.704 0-118.656-34.208-176.064-75.52-210.976-49.792-5.92-69.952-4.064-119.136 17.856zM544 320a32 32 0 1 0 0 64 32 32 0 0 0 0-64z m-32-128c-82.016 0-156.8 30.848-213.44 81.6 47.264-4.384 96-0.544 170.304 18.624A95.84 95.84 0 0 1 544 256c21.568 0 41.472 7.104 57.504 19.104 35.968-17.28 62.016-25.824 87.136-29.984A318.528 318.528 0 0 0 512 192z" />
<TextBlock x:Name="LabFinishQuality" IsHitTestVisible="False" Text="优秀" VerticalAlignment="Center" Foreground="{StaticResource ColorBrushGray1}" Margin="0,0,12,0" />
</StackPanel>
<TextBlock Text="|" Opacity="0.3" Margin="0,8.8,0,8" RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<ScaleTransform ScaleX="1" ScaleY="2.75" CenterX="0.5" CenterY="0.5" />
</TextBlock.RenderTransform>
</TextBlock>
<StackPanel x:Name="BtnFinishPing" Orientation="Horizontal" Background="{StaticResource ColorBrushSemiTransparent}"
ToolTip="与房主的延迟(点击刷新)" ToolTipService.Placement="Center" ToolTipService.InitialShowDelay="100" ToolTipService.VerticalOffset="-35">
<Path IsHitTestVisible="False" Height="14" Width="14" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" Margin="12,0,7,0"
Data="M512 409.6a102.4 102.4 0 0 0-51.2 190.464V1024h102.4v-423.936A102.4 102.4 0 0 0 512 409.6z M512 0a512 512 0 0 0-263.168 950.272l51.2-87.552a409.6 409.6 0 1 1 420.864 0l51.2 87.552A512 512 0 0 0 512 0z M819.2 512a307.2 307.2 0 1 0-464.896 263.168l51.2-88.064a204.8 204.8 0 1 1 209.92 0l51.2 88.064A307.2 307.2 0 0 0 819.2 512z" />
<TextBlock x:Name="LabFinishPing" IsHitTestVisible="False" Text="15ms" VerticalAlignment="Center" Foreground="{StaticResource ColorBrushGray1}" Margin="0,0,12,0" />
</StackPanel>
<TextBlock Text="|" x:Name="LineFinishPing" Opacity="0.3" Margin="0,8.8,0,8" RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<ScaleTransform ScaleX="1" ScaleY="2.75" CenterX="0.5" CenterY="0.5" />
</TextBlock.RenderTransform>
</TextBlock>
<StackPanel x:Name="BtnFinishIp" Orientation="Horizontal" Background="{StaticResource ColorBrushSemiTransparent}"
ToolTip="服务器 IP 地址(点击复制)" ToolTipService.Placement="Center" ToolTipService.InitialShowDelay="100" ToolTipService.VerticalOffset="-35">
<Path IsHitTestVisible="False" Height="11" Width="11" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" Margin="12,0,7,0"
Data="M277.333333 853.333333H192V170.666667h85.333333z m341.333334-597.333333h-170.666667v256h170.666667a128 128 0 0 0 0-256m0-85.333333a213.333333 213.333333 0 0 1 0 426.666666h-170.666667v256h-85.333333V170.666667z" />
<TextBlock x:Name="LabFinishIp" IsHitTestVisible="False" Text="6.0.0.1:12345" VerticalAlignment="Center" Foreground="{StaticResource ColorBrushGray1}" Margin="0,0,12,0" />
</StackPanel>
<TextBlock Text="|" Opacity="0.3" Margin="0,8.8,0,8" RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<ScaleTransform ScaleX="1" ScaleY="2.75" CenterX="0.5" CenterY="0.5" />
</TextBlock.RenderTransform>
</TextBlock>
<StackPanel Orientation="Horizontal" Background="{StaticResource ColorBrushSemiTransparent}"
ToolTip="索引码剩余时间" ToolTipService.Placement="Center" ToolTipService.InitialShowDelay="100" ToolTipService.VerticalOffset="-35">
<Path IsHitTestVisible="False" Height="13" Width="13" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" Margin="12,0,7,0"
Data="M832 64H192c-35.2 0-64 28.8-64 64v192c0 14.532 4.918 27.965 13.158 38.739a63.808 63.808 0 0 0 5.94 6.869L293.49 512 147.098 658.392a63.808 63.808 0 0 0-5.94 6.869C132.918 676.035 128 689.468 128 704v192c0 35.2 28.8 64 64 64h640c35.2 0 64-28.8 64-64V704c0-14.532-4.918-27.965-13.158-38.739a63.808 63.808 0 0 0-5.94-6.869L730.51 512l146.392-146.392a63.808 63.808 0 0 0 5.94-6.869C891.082 347.965 896 334.532 896 320V128c0-35.2-28.8-64-64-64z m-32 244.49L640.098 466.392c-12.534 12.534-18.749 29.086-18.659 45.608-0.09 16.522 6.125 33.074 18.659 45.608L800 718.51V864H224V718.51l158.902-160.902c12.534-12.534 18.749-29.086 18.659-45.608 0.09-16.522-6.125-33.074-18.659-45.608L224 302.49V160h576v148.49z M476.33 457.292c15.631 17.517 42.263 57.543 8.75 100.373L297.608 745.137a31.809 31.809 0 0 0-2.275 2.536C290.757 753.211 288 760.299 288 768c0 17.6 14.4 32 32 32h384.001c17.6 0 32-14.4 32-32 0-7.701-2.757-14.789-7.333-20.327a32.004 32.004 0 0 0-2.275-2.536L545.71 564.454c-16.012-18.851-39.722-58.366-5.07-100.241l58.388-57.487c12.445-12.445 12.445-32.81 0-45.255a31.711 31.711 0 0 0-13.806-8.102A31.712 31.712 0 0 0 576 352H449c-1.287 0-2.553 0.086-3.801 0.235-7.385 0.548-14.612 3.621-20.227 9.236-12.445 12.445-12.445 32.81 0 45.255l51.358 50.566z" />
<TextBlock x:Name="LabFinishTime" IsHitTestVisible="False" Text="2:21" VerticalAlignment="Center" Foreground="{StaticResource ColorBrushGray1}" Margin="0,0,12,0" />
</StackPanel>
</StackPanel>
</local:MyCard>
<local:MyCard Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25" />
<ColumnDefinition Width="Auto" /> <!--Logo-->
<ColumnDefinition Width="13" />
<ColumnDefinition Width="1*" /> <!--文本区 ↓↓-->
<ColumnDefinition Width="Auto" /> <!--按钮 1-->
<ColumnDefinition Width="Auto" /> <!--按钮 2-->
<ColumnDefinition Width="25" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="21" />
<RowDefinition Height="Auto" /> <!--标题栏-->
<RowDefinition Height="Auto" /> <!--文本区-->
<RowDefinition Height="Auto" /> <!--按钮-->
<RowDefinition Height="21" />
</Grid.RowDefinitions>
<Path Grid.Column="1" Grid.Row="1" Height="25" Width="25"
Stretch="Uniform" Fill="{DynamicResource ColorBrush3}"
Data="M845 547.45h65.54A400.6 400.6 0 0 0 712.7 201.77l-33.22 56.49C781.6 318.32 845 429.13 845 547.45zM347.72 253.92L316 196.58a401.21 401.21 0 0 0-207 350.87h65.54a335.66 335.66 0 0 1 173.18-293.53zM509.79 882.68a334 334 0 0 1-181.3-53.2L293 884.57a401.13 401.13 0 0 0 419.1 8.91L679 836.93a334.94 334.94 0 0 1-169.21 45.75z M821.51 625.1A108.64 108.64 0 0 0 752.2 650l-67.31-38.86a185.44 185.44 0 0 0 11.22-63.7c0-91.55-66.38-167.9-153.54-183.42V289a109.14 109.14 0 1 0-65.57 0v75c-87.16 15.53-153.54 91.87-153.54 183.42a185.46 185.46 0 0 0 11.22 63.7L267.39 650a109.17 109.17 0 1 0 35.81 55l64.47-37.23a186 186 0 0 0 284.23 0L716.38 705a109.13 109.13 0 1 0 105.13-79.9zM198.07 777.85a43.6 43.6 0 1 1 43.61-43.61 43.66 43.66 0 0 1-43.61 43.61z m311.72-636.53a43.61 43.61 0 1 1-43.61 43.61 43.65 43.65 0 0 1 43.61-43.61z m0 526.91a120.78 120.78 0 1 1 120.78-120.78 120.91 120.91 0 0 1-120.78 120.77z m311.72 109.62a43.6 43.6 0 1 1 43.61-43.61 43.65 43.65 0 0 1-43.61 43.61z" />
<TextBlock x:Name="LabFinishTitle" Grid.Column="3" Grid.ColumnSpan="3" Grid.Row="1"
Text="已加入 Herobrine 的房间" FontSize="20" Foreground="{DynamicResource ColorBrush3}" />
<TextBlock x:Name="LabFinishDesc" Grid.Column="3" Grid.ColumnSpan="3" Grid.Row="2" Margin="0,10,0,12"
Text="描述文本。"
FontSize="14" LineHeight="19" MaxWidth="400" TextWrapping="Wrap" Foreground="{StaticResource ColorBrushGray2}" />
<local:MyButton x:Name="BtnFinishCopy" Grid.Column="4" Grid.Row="3" Margin="0,0,15,0" Text="复制联机码" Padding="13,8" />
<local:MyButton x:Name="BtnFinishExit" Grid.Column="5" Grid.Row="3" Text="退出" Padding="13,8" />
</Grid>
</local:MyCard>
</Grid>
</Grid>
<Grid x:Name="PanAlways" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="15" Opacity="0.5">
</Grid>
</Grid>
</local:MyPageRight>
@@ -1,395 +0,0 @@
Public Class PageLinkHiper
Public Const RequestVersion As Char = "2"
'记录的启动情况
Public Shared IsServerSide As Boolean
Private Shared HostIp As String
Private Shared HostPort As Integer
#Region "初始化"
'加载器初始化
Private Sub LoaderInit() Handles Me.Initialized
PageLoaderInit(Load, PanLoad, PanContent, PanAlways, InitLoader, AutoRun:=False)
'注册自定义的 OnStateChanged
AddHandler InitLoader.OnStateChangedUi, AddressOf OnLoadStateChanged
End Sub
Private IsLoad As Boolean = False
Private Sub OnLoaded() Handles Me.Loaded
FormMain.EndProgramForce(ProcessReturnValues.Aborted)
If IsLoad Then Return
IsLoad = True
'启动监视线程
If Not IsWatcherStarted Then RunInNewThread(AddressOf WatcherThread, "Hiper Watcher")
'读取索引码
Try
Dim Time As String = Setup.Get("LinkHiperCertTime")
If Time = "" Then
Log("[HiPer] 没有缓存凭证")
ElseIf Date.Parse(Time) > Date.Now Then
TextCert.Text = Setup.Get("LinkHiperCertLast")
Log("[HiPer] 缓存凭证尚未过期:" & Time)
CurrentSubpage = Subpages.PanSelect
Else
Log("[HiPer] 缓存凭证已过期:" & Time)
LabCertTitle.Text = "输入索引码"
LabCertDesc.Text = "你的 HiPer 索引码已经过期,请输入新的索引码。" & vbCrLf & "如果实在没有索引码,可以在左侧选择 IOI 方式联机。"
End If
Catch ex As Exception
Log(ex, "读取缓存凭证失败")
Setup.Set("LinkHiperCertTime", "")
End Try
End Sub
#End Region
#Region "加载步骤"
Public Shared PathHiper As String = PathAppdata & "联机模块\"
Public Shared WithEvents InitLoader As New LoaderCombo(Of Integer)("HiPer 初始化", {
New LoaderTask(Of Integer, Integer)("网络环境:连通检测", AddressOf InitPingCheck) With {.Block = False, .ProgressWeight = 0.5},
New LoaderTask(Of Integer, Integer)("网络环境:IP 检测", AddressOf InitIpCheck) With {.Block = False, .ProgressWeight = 1},
New LoaderTask(Of Integer, Integer)("检查网络环境", AddressOf InitCheck) With {.ProgressWeight = 0.5},
New LoaderTask(Of Integer, List(Of NetFile))("获取所需文件", AddressOf InitGetFile) With {.ProgressWeight = 4},
New LoaderDownload("下载所需文件", New List(Of NetFile)) With {.ProgressWeight = 4},
New LoaderTask(Of Integer, Integer)("启动联机模块", AddressOf InitLaunch) With {.ProgressWeight = 7}
})
'检查网络状态
Private Shared Sub InitPingCheck(Task As LoaderTask(Of Integer, Integer))
End Sub
Private Shared Sub InitIpCheck()
End Sub
Private Shared PingTime As Integer, IpCheckStatus As LoadState = LoadState.Loading, IpIsInChina As Boolean
Private Shared Sub InitCheck(Task As LoaderTask(Of Integer, Integer))
End Sub
'获取所需文件
Private Shared Sub InitGetFile(Task As LoaderTask(Of Integer, List(Of NetFile)))
End Sub
Public Class CertOutdatedException
Inherits Exception
End Class
'启动联机模块
Private Shared Sub InitLaunch(Task As LoaderTask(Of Integer, Integer))
End Sub
Private Shared PingNodes As Integer, AllNodes As List(Of String)
#End Region
#Region "进程管理"
Private Shared _HiperState As LoadState = LoadState.Waiting
Public Shared Property HiperState As LoadState
Get
Return _HiperState
End Get
Set(value As LoadState)
_HiperState = value
RunInUi(Sub() If FrmLinkLeft IsNot Nothing Then CType(FrmLinkLeft.ItemHiper.Buttons(0), MyIconButton).Visibility = If(HiperState = LoadState.Finished OrElse HiperState = LoadState.Loading, Visibility.Visible, Visibility.Collapsed))
End Set
End Property
Public Shared Sub ModuleStopManually() '关闭联机模块按钮
HiperExit(False)
End Sub
Private Shared HiperIp As String = Nothing
Private Shared HiperProcessId As Integer = -1, McbProcessId As Integer = -1
Private Shared HiperCertTime As Date = Date.Now
''' <summary>
''' 若程序正在运行,则结束程序进程,同时初始化状态数据。返回是否关闭了相关进程。
''' </summary>
Public Shared Function HiperStop(SleepWhenKilled As Boolean) As Boolean
Return False
End Function
''' <summary>
''' 启动程序,并等待初始化完成后退出运行,同时更新 HiperIp。
''' 若启动失败,则会直接抛出异常。
''' 若程序正在运行,则会先停止其运行。
''' </summary>
Public Shared Sub HiperStart(Task As LoaderTask(Of Integer, Integer))
End Sub
'Hiper 日志
Private Shared Sub HiperLogLine(Content As String, Task As LoaderTask(Of Integer, Integer))
End Sub
Private Shared PossibleFailReason As String = Nothing
#End Region
#Region "监视线程"
'主 Timer 线程
Private IsWatcherStarted As Boolean = False
Private Sub WatcherThread()
Dim Sec15 As Integer = 0
Do While True
Try
For i = 1 To 5
Thread.Sleep(200)
If InitLoader.State = LoadState.Loading Then
RunInUi(AddressOf UpdateProgress)
End If
Next
Thread.Sleep(1000)
Sec15 += 1
WatcherTimer1()
If Sec15 = 15 Then
Sec15 = 0
WatcherTimer15()
End If
Catch ex As Exception
Log(ex, "联机模块主时钟出错", LogLevel.Feedback)
Thread.Sleep(20000)
End Try
Loop
End Sub
'每 1 秒执行的 Timer
Private Sub WatcherTimer1()
If HiperState <> LoadState.Finished Then Return
RunInUi(Sub()
'索引码剩余时间
Dim Span As TimeSpan = HiperCertTime - Date.Now
If Span.TotalDays >= 30 Then
LabFinishTime.Text = "> 30 天"
ElseIf Span.TotalDays >= 4 Then
LabFinishTime.Text = Span.Days & " 天"
ElseIf Span.TotalDays >= 1 Then
LabFinishTime.Text = Span.Days & " 天" & If(Span.Hours > 0, " " & Span.Hours & " 小时", "")
ElseIf Span.TotalMinutes >= 10 Then
LabFinishTime.Text = Span.Hours & ":" & Span.Minutes.ToString.PadLeft(2, "0") & "'"
Else
LabFinishTime.Text = Span.Minutes & "'" & Span.Seconds.ToString.PadLeft(2, "0") & """"
End If
'提示索引码即将到期
If Span.TotalSeconds <= 5 * 60 AndAlso Span.TotalSeconds > 5 * 60 - 1 AndAlso Setup.Get("LinkHiperCertWarn") Then
MyMsgBox("你的索引码还有不到 5 分钟就要过期了!" & vbCrLf & "你可以在设置中关闭这个提示……", "索引码即将过期", "我知道了……")
ShowWindowToTop(Handle)
Beep()
End If
'检查索引码到期
If Span.TotalSeconds < 2 Then
LabCertTitle.Text = "索引码已过期"
LabCertDesc.Text = "你的 HiPer 索引码已经过期,请输入新的索引码。" & vbCrLf & "如果实在没有索引码,可以在左侧选择 IOI 方式联机。"
TextCert.Text = ""
HiperExit(True)
ShowWindowToTop(Handle)
Beep()
Return
End If
'网络质量
Dim QualityScore As Integer = If(IpIsInChina, 0, -2)
QualityScore -= Math.Ceiling((Math.Min(PingTime, 600) + Math.Min(PingNodes, 600)) / 80)
Select Case QualityScore
Case Is >= -1
LabFinishQuality.Text = "优秀"
Case Is >= -2
LabFinishQuality.Text = "优良"
Case Is >= -3
LabFinishQuality.Text = "良好"
Case Is >= -5
LabFinishQuality.Text = "一般"
Case Is >= -7
LabFinishQuality.Text = "较差"
Case Else
LabFinishQuality.Text = "很差"
End Select
'Ping
If HostPing <> -1 Then
If FrmLinkHiper IsNot Nothing AndAlso FrmLinkHiper.LabFinishPing.IsLoaded Then
FrmLinkHiper.LabFinishPing.Text = HostPing & "ms"
End If
End If
End Sub)
End Sub
'每 15 秒执行的 Timer
Private Shared HostPing As Integer = -1
Private Sub WatcherTimer15()
End Sub
#End Region
#Region "PanCert | 索引码输入页面"
'检测输入
Private Sub TextCert_ValidateChanged(sender As Object, e As EventArgs) Handles TextCert.ValidateChanged
BtnCertDone.IsEnabled = TextCert.ValidateResult = ""
End Sub
Private Sub TextCert_KeyDown(sender As Object, e As KeyEventArgs) Handles TextCert.KeyDown
If e.Key = Key.Enter AndAlso BtnCertDone.IsEnabled Then BtnCertDone_Click() '允许回车确认
End Sub
'确认
Private Sub BtnCertDone_Click() Handles BtnCertDone.Click
CurrentSubpage = Subpages.PanSelect
End Sub
#End Region
#Region "PanSelect | 种类选择页面"
'返回
Private Sub BtnSelectReturn_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles BtnSelectReturn.MouseLeftButtonUp
LabCertTitle.Text = "输入索引码"
LabCertDesc.Text = "你需要获取索引码才能使用 HiPer。" & vbCrLf & "如果实在没有索引码,可以在左侧选择 IOI 方式联机。"
CurrentSubpage = Subpages.PanCert
End Sub
'创建房间
Private Sub BtnSelectCreate_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles BtnSelectCreate.MouseLeftButtonUp
End Sub
Private Sub RoomCreate(Port As Integer)
'记录信息
HostIp = Nothing : HostPort = Port
IsServerSide = True
'启动
InitLoader.Start(IsForceRestart:=True)
End Sub
'加入房间
Private Sub BtnSelectJoin_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles BtnSelectJoin.MouseLeftButtonUp
End Sub
Private Sub RoomJoin(Ip As String, Port As Integer)
'记录信息
HostIp = Ip : HostPort = Port
IsServerSide = False
'启动
InitLoader.Start(IsForceRestart:=True)
End Sub
#End Region
#Region "PanLoad | 加载中页面"
'承接状态切换的 UI 改变
Private Sub OnLoadStateChanged(Loader As LoaderBase, NewState As LoadState, OldState As LoadState)
End Sub
Private Shared LoadStep As String = "准备初始化"
Private Shared Sub SetLoadDesc(Intro As String, [Step] As String)
Log("[Hiper] 连接步骤:" & Intro)
LoadStep = [Step]
RunInUiWait(Sub()
If FrmLinkHiper Is Nothing OrElse Not FrmLinkHiper.LabLoadDesc.IsLoaded Then Return
FrmLinkHiper.LabLoadDesc.Text = Intro
FrmLinkHiper.UpdateProgress()
End Sub)
End Sub
'承接重试
Private Sub CardLoad_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles CardLoad.MouseLeftButtonUp
If Not InitLoader.State = LoadState.Failed Then Return
InitLoader.Start(IsForceRestart:=True)
End Sub
'取消加载
Private Sub CancelLoad() Handles BtnLoadCancel.Click
If InitLoader.State = LoadState.Loading Then
CurrentSubpage = Subpages.PanSelect
InitLoader.Abort()
Else
InitLoader.State = LoadState.Waiting
End If
HiperStop(False)
End Sub
'进度改变
Private Sub UpdateProgress(Optional Value As Double = -1)
If Value = -1 Then Value = InitLoader.Progress
Dim DisplayingProgress As Double = ColumnProgressA.Width.Value
If Math.Round(Value - DisplayingProgress, 3) = 0 Then Return
If DisplayingProgress > Value Then
ColumnProgressA.Width = New GridLength(Value, GridUnitType.Star)
ColumnProgressB.Width = New GridLength(1 - Value, GridUnitType.Star)
AniStop("Hiper Progress")
Else
Dim NewProgress As Double = If(Value = 1, 1, (Value - DisplayingProgress) * 0.2 + DisplayingProgress)
AniStart({
AaGridLengthWidth(ColumnProgressA, NewProgress - ColumnProgressA.Width.Value, 300, Ease:=New AniEaseOutFluent),
AaGridLengthWidth(ColumnProgressB, (1 - NewProgress) - ColumnProgressB.Width.Value, 300, Ease:=New AniEaseOutFluent)
}, "Hiper Progress")
End If
End Sub
Private Sub CardResized() Handles CardLoad.SizeChanged
RectProgressClip.Rect = New Rect(0, 0, CardLoad.ActualWidth, 12)
End Sub
#End Region
#Region "PanFinish | 加载完成页面"
'复制 IP
Private Sub BtnFinishIp_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles BtnFinishIp.MouseLeftButtonUp
ClipboardSet(LabFinishIp.Text)
End Sub
'退出
Private Sub BtnFinishExit_Click(sender As Object, e As EventArgs) Handles BtnFinishExit.Click
If IsServerSide AndAlso MyMsgBox("你确定要关闭联机房间吗?", "确认退出", "确定", "取消", IsWarn:=True) = 2 Then Return
HiperExit(False)
End Sub
'复制联机码
Private Sub BtnFinishCopy_Click(sender As Object, e As EventArgs) Handles BtnFinishCopy.Click
End Sub
'Ping 房主
Private Sub BtnFinishPing_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles BtnFinishPing.MouseLeftButtonUp
LabFinishPing.Text = "检测中"
If TaskPingHost.State = LoadState.Loading Then Return
TaskPingHost.Start(True, IsForceRestart:=True)
End Sub
Private Shared TaskPingHost As New LoaderTask(Of Boolean, Integer)("HiPer Ping Host",
Sub(Task As LoaderTask(Of Boolean, Integer))
HostPing = -1
HostPing = Ping(HostIp, 5000, Task.Input)
End Sub)
#End Region
#Region "子页面管理"
Public Enum Subpages
PanCert
PanSelect
PanFinish
End Enum
Private _CurrentSubpage As Subpages = Subpages.PanCert
Public Property CurrentSubpage As Subpages
Get
Return _CurrentSubpage
End Get
Set(value As Subpages)
If _CurrentSubpage = value Then Return
_CurrentSubpage = value
Log("[Hiper] 子页面更改为 " & GetStringFromEnum(value))
PageOnContentExit()
If value = Subpages.PanSelect Then
LabSelectCode.Text = "(" & TextCert.Text.Substring(0, Math.Min(TextCert.Text.Length, 3)) & "…)"
End If
End Set
End Property
Private Sub PageLinkHiper_OnPageEnter() Handles Me.PageEnter
FrmLinkHiper.PanCert.Visibility = If(CurrentSubpage = Subpages.PanCert, Visibility.Visible, Visibility.Collapsed)
FrmLinkHiper.PanSelect.Visibility = If(CurrentSubpage = Subpages.PanSelect, Visibility.Visible, Visibility.Collapsed)
FrmLinkHiper.PanFinish.Visibility = If(CurrentSubpage = Subpages.PanFinish, Visibility.Visible, Visibility.Collapsed)
End Sub
Private Shared Sub HiperExit(ExitToCertPage As Boolean)
Log("[Hiper] 要求退出 Hiper(当前加载器状态为 " & GetStringFromEnum(InitLoader.State) & ")")
HiperStop(False)
If InitLoader.State = LoadState.Loading Then InitLoader.Abort()
If InitLoader.State = LoadState.Failed Then InitLoader.State = LoadState.Waiting
RunInUi(Sub()
If FrmLinkHiper Is Nothing OrElse Not FrmLinkHiper.IsLoaded Then Return
FrmLinkHiper.CurrentSubpage = If(ExitToCertPage, Subpages.PanCert, Subpages.PanSelect)
FrmLinkHiper.PageOnContentExit()
End Sub)
End Sub
#End Region
End Class
@@ -1,50 +0,0 @@
<local:MyPageRight
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:PCL" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" x:Class="PageLinkIoi"
PanScroll="PanBack">
<Grid>
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
<StackPanel Orientation="Vertical" Margin="25,10">
<local:MyCard Margin="0,15,0,0" x:Name="CardUser" Title="已连接的玩家 (0)">
<StackPanel Margin="20,37,20,15">
<local:MyHint Margin="5,2,5,8" x:Name="LabHint" />
<StackPanel x:Name="PanUserList" />
<Grid Height="35" Margin="5,8,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
</Grid.ColumnDefinitions>
<local:MyButton x:Name="BtnLeftCreate" Grid.Column="0" MinWidth="140" Text="建立连接" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
<local:MyButton x:Name="BtnLeftCopy" Grid.Column="1" MinWidth="140" Text="复制联机码" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
</Grid>
</StackPanel>
</local:MyCard>
<local:MyCard Margin="0,15" Title="房间列表" x:Name="CardRoom">
<StackPanel Margin="20,37,20,15">
<StackPanel x:Name="PanRoom">
<!--<local:MyListItem Type="Clickable" PaddingRight="60" Height="40" Title="00ll00" Info="已连接,1100 ms" Logo="pack://application:,,,/images/Blocks/Grass.png">
<local:MyListItem.Buttons>
<x:Array Type="{x:Type local:MyIconButton}">
<local:MyIconButton ToolTip="刷新" ToolTipService.Placement="Bottom" ToolTipService.HorizontalOffset="-10" ToolTipService.VerticalOffset="5" ToolTipService.InitialShowDelay="200" LogoScale="0.85" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
<local:MyIconButton ToolTip="断开" ToolTipService.Placement="Bottom" ToolTipService.HorizontalOffset="-10" ToolTipService.VerticalOffset="5" ToolTipService.InitialShowDelay="200" LogoScale="0.85" Logo="F1 M 26.9166,22.1667L 37.9999,33.25L 49.0832,22.1668L 53.8332,26.9168L 42.7499,38L 53.8332,49.0834L 49.0833,53.8334L 37.9999,42.75L 26.9166,53.8334L 22.1666,49.0833L 33.25,38L 22.1667,26.9167L 26.9166,22.1667 Z" />
</x:Array>
</local:MyListItem.Buttons>
</local:MyListItem>-->
</StackPanel>
<local:MyListItem x:Name="BtnCreate" Type="Clickable" Title="创建房间" Height="34" PaddingLeft="0"
Logo="M512.277 954.412c-118.89 0-230.659-46.078-314.73-129.73S67.12 629.666 67.12 511.222s46.327-229.744 130.398-313.427 195.82-129.73 314.73-129.73 230.659 46.078 314.72 129.73S957.397 392.81 957.397 511.183 911.078 740.96 826.97 824.642s-195.8 129.77-314.692 129.77z m0-822.784c-101.972 0-197.809 39.494-269.865 111.222s-111.7 166.997-111.7 268.373 39.653 196.695 111.67 268.335S410.246 890.78 512.248 890.78s197.809-39.484 269.865-111.222 111.7-166.998 111.67-268.374c-0.03-101.375-39.654-196.665-111.67-268.303S614.22 131.628 512.277 131.628z m222.585 347.8H544.073V288.64c-0.76-17.561-15.613-31.18-33.173-30.419-16.495 0.714-29.704 13.924-30.419 30.419v190.787H289.703c-17.56 0.761-31.179 15.614-30.419 33.174 0.715 16.494 13.924 29.703 30.42 30.418H480.48v190.788c0.761 17.56 15.614 31.179 33.174 30.419 16.494-0.715 29.703-13.925 30.418-30.42V543.02h190.788c17.56 0.762 32.413-12.857 33.173-30.418 0.762-17.561-12.858-32.414-30.419-33.174a31.683 31.683 0 0 0-2.753 0z" />
</StackPanel>
</local:MyCard>
</StackPanel>
</local:MyScrollViewer>
<local:MyCard Opacity="0" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="40,0" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False">
<local:MyLoading Text="正在加载联机模块" Margin="20,20,20,17" x:Name="Load" ShowProgress="True" HorizontalAlignment="Center" VerticalAlignment="Center" />
</local:MyCard>
<Grid x:Name="PanAlways" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="15" Opacity="0.5">
<local:MyTextButton Text="联机服务由 速聚 授权提供" local:CustomEventService.EventType="打开网页" local:CustomEventService.EventData="https://www.the.bb/zh-CN.html" />
</Grid>
</Grid>
</local:MyPageRight>
@@ -1,411 +0,0 @@
Imports System.Net.Sockets
Public Class PageLinkIoi
Public Const RequestVersion As Integer = 4
Public Const IoiVersion As Integer = 10 '由于已关闭更新渠道,在提升 IoiVersion 时必须提升 RequestVersion
Public Shared PathIoi As String = PathAppdata & "联机模块\IOI 联机模块.exe"
#Region "进程管理"
Private Shared IoiId As String, IoiPassword As String
Private Shared IoiProcess As Process = Nothing
Private Shared IoiState As LoadState = LoadState.Waiting
''' <summary>
''' 若 Ioi 正在运行,则结束 Ioi 进程,同时初始化状态数据。返回是否关闭了对应进程。
''' </summary>
Public Shared Function IoiStop(SleepWhenKilled As Boolean) As Boolean
Return False
End Function
''' <summary>
''' 启动 Ioi,并等待初始化完成后退出运行,同时更新 IoiId 与 IoiPassword。
''' 正常初始化返回 True,需要更新返回 False,其余情况抛出异常。
''' 若 Ioi 正在运行,则会先停止其运行。
''' </summary>
Public Shared Function IoiStart() As Boolean
Return False
End Function
'Ioi 日志
Private Shared Sub IoiLogLine(Content As String)
End Sub
Private Shared LogLinesCount As Integer = 0
Private Shared LastPortsId As String = "" '上一个收到 portssub 的 ID,用于记录端口
#End Region
#Region "时钟"
'UI 线程刷新
Private Shared UserListIdentifyCache As String = ""
Private Shared RoomListIdentifyCache As String = ""
Public Sub RefreshUi()
End Sub
'工作线程刷新
Public Sub RefreshWorker()
End Sub
#End Region
#Region "发送请求"
''' <summary>
''' 发送 Portsub 请求并等待获取控制台端口。进度将从 0 变化至 80%。
''' </summary>
Private Shared Sub SendPortsubRequest(User As LinkUserIoi)
End Sub
''' <summary>
''' 向控制台发送 Connect 请求。
''' </summary>
Private Shared Sub SendConnectRequest(User As LinkUserIoi)
Dim RawJson As New JObject()
RawJson("version") = RequestVersion
RawJson("name") = GetPlayerName()
RawJson("id") = IoiId
RawJson("type") = "connect"
User.Send(RawJson)
End Sub
''' <summary>
''' 向控制台发送 Update 请求。
''' </summary>
Private Shared Sub SendUpdateRequest(User As LinkUserIoi, Stage As Integer, Optional Unique As Long = -1)
If Unique = -1 Then Unique = GetTimeTick()
Dim RawJson As New JObject
RawJson("name") = GetPlayerName()
RawJson("id") = IoiId
RawJson("type") = "update"
RawJson("stage") = Stage
RawJson("unique") = Unique
If Stage < 3 Then
Dim Rooms As New JArray
For Each Room In RoomListForMe
Dim RoomObject As New JObject
RoomObject("name") = Room.DisplayName
RoomObject("port") = Room.Port
Rooms.Add(RoomObject)
Next
RawJson("rooms") = Rooms
User.PingPending(Unique) = Date.Now
End If
User.Send(RawJson)
End Sub
''' <summary>
''' 尝试发送断开请求,并将其从用户列表中移除。
''' </summary>
Private Shared Sub SendDisconnectRequest(User As LinkUserIoi, Optional Message As String = Nothing, Optional IsError As Boolean = False)
End Sub
#End Region
#Region "左边栏操作"
'刷新连接
Private Shared Sub BtnListRefresh_Click(sender As MyIconButton, e As EventArgs)
End Sub
'断开连接
Private Shared Sub BtnListDisconnect_Click(sender As MyIconButton, e As EventArgs)
End Sub
'复制联机码
Public Shared Sub BtnLeftCopy_Click() Handles BtnLeftCopy.Click
End Sub
#End Region
#Region "玩家名"
''' <summary>
''' 获取当前的玩家名。
''' </summary>
Public Shared Function GetPlayerName() As String
Return ""
End Function
Private Shared AutogenPlayerName As String = Nothing '并非由玩家自定义,而是自动生成的玩家名
''' <summary>
''' 检查某个玩家名是否合法。
''' </summary>
Private Shared Function IsPlayerNameValid(Name As String) As Boolean
Return True
End Function
#End Region
#Region "请求核心"
''' <summary>
''' 启动 Socket 监听核心。
''' </summary>
Public Shared Sub StartSocketListener()
End Sub
#End Region
#Region "用户核心"
'用户基类
Public MustInherit Class LinkUserBase
Implements IDisposable
'基础数据
Public Uuid As Integer = GetUuid()
Public Id As String
Public DisplayName As String
'请求管理
Public Socket As Socket = Nothing
Public Sub Send(Request As JObject)
End Sub
Public ListenerThread As Thread = Nothing
Public Sub StartListener()
End Sub
Public Sub BindSocket(Socket As Socket)
If Me.Socket IsNot Nothing Then Throw New Exception("该用户已经绑定了 Socket")
Me.Socket = Socket
StartListener()
End Sub
'Ping
'0:与 Ping 计算无关,不回应
'1:A to B,2:B to A,3:A to B
Public PingPending As New Dictionary(Of Long, Date)
Public PingRecord As New Queue(Of Integer)
'心跳包
Public LastSend As Date = Date.Now
Public LastReceive As Date = Date.Now
'类型转换
Public Sub New(Id As String, DisplayName As String)
Me.Id = Id
Me.DisplayName = DisplayName
Log("[IOI] 无通信包的新用户对象:" & ToString())
End Sub
Public Sub New(Id As String, DisplayName As String, Socket As Socket)
Me.Id = Id
Me.DisplayName = DisplayName
Me.Socket = Socket
Log("[IOI] 新用户对象:" & ToString())
StartListener()
End Sub
Public Overrides Function ToString() As String
Return DisplayName & " @ " & Id & " #" & Uuid
End Function
Public Shared Widening Operator CType(User As LinkUserBase) As String
Return User.ToString
End Operator
'释放资源
Public IsDisposed As Boolean = False
Protected Overridable Sub Dispose(IsDisposing As Boolean)
If Socket IsNot Nothing Then Socket.Dispose()
If ListenerThread IsNot Nothing AndAlso ListenerThread.IsAlive Then ListenerThread.Interrupt()
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
If Not IsDisposed Then
IsDisposed = True
Dispose(True)
End If
GC.SuppressFinalize(Me)
End Sub
End Class
'用户对象
Public Shared UserList As New Dictionary(Of String, LinkUserIoi)
Public Class LinkUserIoi
Inherits LinkUserBase
Public Sub New(Id As String, DisplayName As String, Socket As Socket)
MyBase.New(Id, DisplayName, Socket)
End Sub
Public Sub New(Id As String, DisplayName As String)
MyBase.New(Id, DisplayName)
End Sub
'基础数据
Public Ports As New Dictionary(Of Integer, String)
Public Rooms As New List(Of RoomEntry)
'进度与 UI
Public Progress As Double = 0
Public RelativeThread As Thread = Nothing
Public Function GetDescription() As String
Return If(Progress < 1,
"正在连接," & Math.Round(Progress * 100) & "%",
"已连接," & If(Not PingRecord.Any(), "检查延迟中", Math.Round(PingRecord.Average) & "ms"))
End Function
Public Function ToListItem() As MyListItem
Dim Item As New MyListItem With {
.Title = DisplayName, .Height = 42, .Tag = Me, .Type = MyListItem.CheckType.None,
.Logo = "pack://application:,,,/images/Blocks/Grass.png"}
'绑定图标按钮
Dim BtnRefresh As New MyIconButton With {.Logo = Logo.IconButtonRefresh, .LogoScale = 0.85, .ToolTip = "刷新", .Tag = Me}
AddHandler BtnRefresh.Click, AddressOf BtnListRefresh_Click
ToolTipService.SetPlacement(BtnRefresh, Primitives.PlacementMode.Bottom)
ToolTipService.SetHorizontalOffset(BtnRefresh, -10)
ToolTipService.SetVerticalOffset(BtnRefresh, 5)
ToolTipService.SetInitialShowDelay(BtnRefresh, 200)
Dim BtnClose As New MyIconButton With {.Logo = Logo.IconButtonCross, .LogoScale = 0.85, .ToolTip = "断开", .Tag = Me}
AddHandler BtnClose.Click, AddressOf BtnListDisconnect_Click
ToolTipService.SetPlacement(BtnClose, Primitives.PlacementMode.Bottom)
ToolTipService.SetHorizontalOffset(BtnClose, -10)
ToolTipService.SetVerticalOffset(BtnClose, 5)
ToolTipService.SetInitialShowDelay(BtnClose, 200)
Item.Buttons = {BtnRefresh, BtnClose}
'刷新并返回
RefreshUi(Item)
Return Item
End Function
Public Sub RefreshUi(RelatedListItem As MyListItem)
RelatedListItem.Title = DisplayName
RelatedListItem.Info = GetDescription()
RelatedListItem.Buttons(0).Visibility = If(Progress = 1, Visibility.Visible, Visibility.Collapsed)
End Sub
'释放
Protected Overrides Sub Dispose(IsDisposing As Boolean)
Log("[IOI] 用户资源释放(IOI, " & DisplayName & ")")
If RelativeThread IsNot Nothing AndAlso RelativeThread.IsAlive Then RelativeThread.Interrupt()
UserList.Remove(Id)
MyBase.Dispose(IsDisposing)
End Sub
End Class
'房间对象
Private Shared RoomListForMe As New List(Of RoomEntry)
Private Function GetRoomList() As List(Of RoomEntry)
Dim RoomList As New List(Of RoomEntry)(RoomListForMe)
For i = 0 To UserList.Count - 1
If i > UserList.Count - 1 Then Exit For
RoomList.AddRange(UserList.Values(i).Rooms)
Next
Return RoomList
End Function
Public Class RoomEntry
'基础数据
Public Port As Integer
Public DisplayName As String
Public User As LinkUserIoi = Nothing '若 IsOwner = True,则此项为 Nothing
Public IsOwner As Boolean
Public ReadOnly Property Ip As String
Get
If IsOwner Then
Return "localhost:" & Port
Else
Return User.Ports(Port) & ":" & Port
End If
End Get
End Property
'类型转换
Public Sub New(Port As Integer, DisplayName As String, Optional User As LinkUserIoi = Nothing)
Me.IsOwner = User Is Nothing
Me.User = User
Me.DisplayName = DisplayName
Me.Port = Port
End Sub
Public Overrides Function ToString() As String
Return DisplayName & " - " & Port & " - " & IsOwner
End Function
Public Shared Widening Operator CType(Room As RoomEntry) As String
Return Room.ToString
End Operator
Public Shared Function SelectPort(Room As RoomEntry) As Integer
Return Room.Port
End Function
'UI
Public Function GetDescription() As String
If IsOwner Then
Return "由我创建,端口 " & Port
Else
Return "由 " & User.DisplayName & " 创建,端口 " & Port
End If
End Function
Public Function ToListItem() As MyListItem
Dim Item As New MyListItem With {
.Title = DisplayName, .Height = 42, .Info = GetDescription(), .Tag = Me,
.Type = If(IsOwner, MyListItem.CheckType.None, MyListItem.CheckType.Clickable),
.Logo = "pack://application:,,,/images/Blocks/" & If(IsOwner, "GrassPath", "Grass") & ".png"}
If IsOwner Then
'绑定图标按钮
Dim BtnEdit As New MyIconButton With {.Logo = Logo.IconButtonEdit, .LogoScale = 1, .ToolTip = "修改名称", .Tag = Me}
AddHandler BtnEdit.Click, AddressOf BtnRoomEdit_Click
ToolTipService.SetPlacement(BtnEdit, Primitives.PlacementMode.Bottom)
ToolTipService.SetHorizontalOffset(BtnEdit, -22)
ToolTipService.SetVerticalOffset(BtnEdit, 5)
ToolTipService.SetInitialShowDelay(BtnEdit, 200)
Dim BtnClose As New MyIconButton With {.Logo = Logo.IconButtonCross, .LogoScale = 0.85, .ToolTip = "关闭", .Tag = Me}
AddHandler BtnClose.Click, AddressOf BtnRoomClose_Click
ToolTipService.SetPlacement(BtnClose, Primitives.PlacementMode.Bottom)
ToolTipService.SetHorizontalOffset(BtnClose, -10)
ToolTipService.SetVerticalOffset(BtnClose, 5)
ToolTipService.SetInitialShowDelay(BtnClose, 200)
Item.Buttons = {BtnEdit, BtnClose}
Else
'绑定点击事件
AddHandler Item.Click, AddressOf BtnRoom_Click
End If
Return Item
End Function
Public Sub RefreshUi(RelatedListItem As MyListItem)
RelatedListItem.Title = DisplayName
RelatedListItem.Info = GetDescription()
End Sub
End Class
#End Region
'正向与反向连接
Public Shared Sub BtnLeftCreate_Click() Handles BtnLeftCreate.Click
End Sub
Private Shared Sub SendPortsubBack(User As LinkUserIoi, TargetVersion As Integer)
End Sub
'创建房间
Private Sub LinkCreate() Handles BtnCreate.Click
End Sub
Private Shared Sub SendUpdateRequestToAllUsers()
For i = 0 To UserList.Count - 1
If i > UserList.Count - 1 Then Exit For
Dim User = UserList.Values(i)
If User.Progress < 1 Then Continue For
Try
SendUpdateRequest(User, 1) '不需要使用多线程,发送实际会瞬间完成
Catch ex As Exception
Log(ex, "发送全局刷新请求失败(" & User.DisplayName & ")")
End Try
Next
End Sub
'修改房间名称
Private Shared Sub BtnRoomEdit_Click(sender As MyIconButton, e As EventArgs)
End Sub
'加入房间
Private Shared Sub BtnRoom_Click(sender As MyListItem, e As EventArgs)
Dim Room As RoomEntry = sender.Tag
If MyMsgBox("请在多人游戏页面点击直接连接,输入 " & Room.Ip & " 以进入服务器!", "加入房间", "复制地址", "确定") = 1 Then
ClipboardSet(Room.Ip)
End If
End Sub
'关闭房间
Private Shared Sub BtnRoomClose_Click(sender As MyIconButton, e As EventArgs)
End Sub
'获取数据包
Public Shared Sub ReceiveJson(JsonData As JObject, Optional NewSocket As Socket = Nothing)
End Sub
''' <summary>
''' 从用户列表中移除一位用户。提示信息视作该用户主动离开。
''' </summary>
Public Shared Sub UserRemove(User As LinkUserIoi, ShowLeaveMessage As Boolean)
End Sub
Public Shared Sub ModuleStopManually()
End Sub
End Class
@@ -1,44 +0,0 @@
<local:MyPageLeft x:Class="PageLinkLeft"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:PCL"
mc:Ignorable="d" AnimatedControl="PanItem" MinWidth="150">
<StackPanel Orientation="Vertical" x:Name="PanItem" Margin="0,12,0,0">
<TextBlock Text="联机方式" Margin="13,6,5,4" Opacity="0.6" FontSize="12" />
<local:MyListItem x:Name="ItemHiper" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="1" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="HiPer"
LogoScale="0.85" Logo="M554.496 170.496c141.312 0 256 114.688 256 256s-114.688 256-256 256H402.432c-22.016-1.536-39.424-19.968-39.424-42.496a42.391 42.391 0 0 1 42.496-42.496h149.504c94.208 0 170.496-76.288 170.496-170.496S648.704 256 554.496 256h-256C204.288 256 128 332.288 128 426.496c0 38.4 12.8 73.728 33.792 102.4 5.632 7.168 8.704 15.872 8.704 25.6A42.391 42.391 0 0 1 128 596.992c-11.264 0-20.992-4.096-28.672-11.264l-0.512 0.512c-35.328-44.032-56.32-99.328-56.32-159.744 0-141.312 114.688-256 256-256h256z m64 192h3.072c22.016 1.536 39.424 19.968 39.424 42.496a42.391 42.391 0 0 1-42.496 42.496H469.504c-94.208 0-170.496 76.288-170.496 170.496S375.296 788.48 469.504 788.48h256C819.712 788.48 896 712.192 896 617.984c0-38.4-12.8-73.728-33.792-102.4-5.632-7.168-8.704-15.872-8.704-25.6A42.391 42.391 0 0 1 896 447.488c11.264 0 20.992 4.096 28.672 11.264l0.512-0.512c35.328 44.032 56.32 99.328 56.32 159.744 0 141.312-114.688 256-256 256h-256c-141.312 0-256-114.688-256-256s114.688-256 256-256h148.992z">
<local:MyListItem.Buttons>
<x:Array Type="{x:Type local:MyIconButton}">
<local:MyIconButton ToolTip="关闭联机模块" Loaded="BtnHiperStop_Loaded" Click="BtnHiperStop_Click"
ToolTipService.Placement="Right" ToolTipService.HorizontalOffset="5" ToolTipService.VerticalOffset="-2" ToolTipService.InitialShowDelay="200" LogoScale="0.7"
Logo="M576 512l277.333333 277.333333-64 64-277.333333-277.333333L234.666667 853.333333 170.666667 789.333333l277.333333-277.333333L170.666667 234.666667 234.666667 170.666667l277.333333 277.333333L789.333333 170.666667 853.333333 234.666667 576 512z" />
</x:Array>
</local:MyListItem.Buttons>
</local:MyListItem>
<local:MyListItem x:Name="ItemIoi" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="2" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="IOI"
LogoScale="0.85" Logo="M554.496 170.496c141.312 0 256 114.688 256 256s-114.688 256-256 256H402.432c-22.016-1.536-39.424-19.968-39.424-42.496a42.391 42.391 0 0 1 42.496-42.496h149.504c94.208 0 170.496-76.288 170.496-170.496S648.704 256 554.496 256h-256C204.288 256 128 332.288 128 426.496c0 38.4 12.8 73.728 33.792 102.4 5.632 7.168 8.704 15.872 8.704 25.6A42.391 42.391 0 0 1 128 596.992c-11.264 0-20.992-4.096-28.672-11.264l-0.512 0.512c-35.328-44.032-56.32-99.328-56.32-159.744 0-141.312 114.688-256 256-256h256z m64 192h3.072c22.016 1.536 39.424 19.968 39.424 42.496a42.391 42.391 0 0 1-42.496 42.496H469.504c-94.208 0-170.496 76.288-170.496 170.496S375.296 788.48 469.504 788.48h256C819.712 788.48 896 712.192 896 617.984c0-38.4-12.8-73.728-33.792-102.4-5.632-7.168-8.704-15.872-8.704-25.6A42.391 42.391 0 0 1 896 447.488c11.264 0 20.992 4.096 28.672 11.264l0.512-0.512c35.328 44.032 56.32 99.328 56.32 159.744 0 141.312-114.688 256-256 256h-256c-141.312 0-256-114.688-256-256s114.688-256 256-256h148.992z">
<local:MyListItem.Buttons>
<x:Array Type="{x:Type local:MyIconButton}">
<local:MyIconButton ToolTip="关闭联机模块" Loaded="BtnIoiStop_Loaded" Click="BtnIoiStop_Click"
ToolTipService.Placement="Right" ToolTipService.HorizontalOffset="5" ToolTipService.VerticalOffset="-2" ToolTipService.InitialShowDelay="200" LogoScale="0.7"
Logo="M576 512l277.333333 277.333333-64 64-277.333333-277.333333L234.666667 853.333333 170.666667 789.333333l277.333333-277.333333L170.666667 234.666667 234.666667 170.666667l277.333333 277.333333L789.333333 170.666667 853.333333 234.666667 576 512z" />
</x:Array>
</local:MyListItem.Buttons>
</local:MyListItem>
<TextBlock Text="更多选项" Margin="13,18,5,4" Opacity="0.6" FontSize="12" />
<local:MyListItem x:Name="ItemSetup" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="4" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="设置"
LogoScale="0.85" Logo="M940.4 463.7L773.3 174.2c-17.3-30-49.2-48.4-83.8-48.4H340.2c-34.6 0-66.5 18.5-83.8 48.4L89.2 463.7c-17.3 30-17.3 66.9 0 96.8L256.4 850c17.3 30 49.2 48.4 83.8 48.4h349.2c34.6 0 66.5-18.5 83.8-48.4l167.2-289.5c17.3-29.9 17.3-66.8 0-96.8z m-94.6 96.8L725.9 768.1c-17.3 30-49.2 48.4-83.8 48.4H387.5c-34.6 0-66.5-18.5-83.8-48.4L183.9 560.5c-17.3-30-17.3-66.9 0-96.8l119.8-207.5c17.3-30 49.2-48.4 83.8-48.4h254.6c34.6 0 66.5 18.5 83.8 48.4l119.8 207.5c17.3 30 17.3 66.9 0.1 96.8z M522.3 321.2c-2.5-0.1-5-0.2-7.5-0.2-119.9 0-214 110.3-186.3 235 15.8 70.9 71.5 126.6 142.4 142.4 17.5 3.9 34.7 5.4 51.4 4.7 102.1-3.9 183.6-87.9 183.6-191 0.1-103-81.5-187-183.6-190.9z m68.6 269.1c-18.5 18-43 28.9-68.6 30.7l-6 0.3c-30.2 0.4-58.6-11.4-79.7-33-19.5-20.1-30.7-47-30.9-75-0.3-29.6 11.1-57.4 32-78.3 20.6-20.6 48-32 77.2-32 2.5 0 5 0.1 7.5 0.3 26.7 1.8 51.5 13.2 70.5 32.5 19.6 20 30.8 46.9 31.2 74.9 0.2 30.2-11.5 58.6-33.2 79.6z">
<local:MyListItem.Buttons>
<x:Array Type="{x:Type local:MyIconButton}">
<local:MyIconButton Tag="5" ToolTip="初始化设置" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" Click="Reset" Logo="M530 0c287 0 521 229 521 511s-233 511-521 511c-233 0-436-151-500-368a63 63 0 0 1 44-79 65 65 0 0 1 80 43c48 162 200 276 375 276 215 0 390-171 390-383s-174-383-390-383c-103 0-199 39-270 106l21-5a63 63 0 0 1 33 123l-157 42a65 65 0 0 1-90-42l-49-183a65 65 0 1 1 126-33l6 26A524 524 0 0 1 530 0z" LogoScale="0.9" />
</x:Array>
</local:MyListItem.Buttons>
</local:MyListItem>
<local:MyListItem x:Name="ItemHelp" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="5" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="常见问题"
LogoScale="0.97" Logo="M520.6 620.3c-11.3-0.6-20.1-4-26.9-10.5-6.6-6.3-8.1-11-10.1-20.8-1.9-9.5-1.5-16.7-1-24.9l0.3-5.3c0.5-9.9 3.5-19.6 8.8-28.1 5.7-9 12.5-17.2 20.8-25.1 8.6-8 17.7-15.7 27-22.9 9.4-7.2 18.7-14.8 27.7-22.7 9-7.9 16.2-15.8 22.1-24.3 6.2-9 9.3-18.8 9.3-29.4 1.2-20.3-6.5-37.7-22.8-51.3-16-13.3-37.4-20-63.5-20-13.7 0-26.3 3.3-37.4 10-10.9 6.5-20.2 14.5-27.7 23.8-7.5 9.2-13.5 19-17.9 29.2-4.5 10.6-6.7 19.4-6.7 26.8 0 9.2-3.3 16-10.1 20.8-6.9 4.8-14.6 7.3-22.8 7.3h-1.3c-9-0.3-17-3.3-24.6-9.2-7.3-5.6-11.1-14.6-11.6-27.5-0.6-11.5 1.9-26.1 7.2-43.5 5.4-17.4 14.8-34.4 28.1-50.5 13.4-16.3 31.2-30.4 52.8-41.8 21.6-11.4 49.2-17.2 81.9-17.2 26.4 0 49.6 3.9 69.1 11.7 19.4 7.7 35.4 18.2 47.5 31.1l1.6 1.7c11.8 12.6 20.3 21.7 29.4 44.4 11.6 28.9 7.1 52.1 5.5 58.5-5.8 22.6-12.6 37.7-22.7 50.4-11.7 14.6-24.9 28.2-39.2 40.4-14 11.7-39.2 33.3-39.2 33.3-13.3 11.4-19.4 22.1-18.6 32.9V587c0.5 8.7-2.4 16.1-8.8 22.8-6.2 6.4-14.8 10-26.2 10.5zM519 766.1c-13 0-23.6-4.2-32.2-12.9-8.7-8.7-13-19-13-31.4 0-12.9 4.2-23.5 12.9-32.2 8.7-8.7 19.2-12.9 32.2-12.9 13 0 23.5 4.2 32.2 12.9s12.9 19.2 12.9 32.2c0 12.4-4.2 22.7-12.9 31.4-8.6 8.6-19.2 12.9-32.1 12.9z M515 928.3c-228.1 0-413.7-185.6-413.7-413.7S286.9 100.9 515 100.9s413.7 185.6 413.7 413.7c0.1 228.1-185.5 413.7-413.7 413.7z m0-747.4c-184 0-333.7 149.7-333.7 333.7S331 848.3 515 848.3s333.7-149.7 333.7-333.7S699 180.9 515 180.9z" />
<local:MyListItem x:Name="ItemFeedback" IsScaleAnimationEnabled="False" Type="RadioBox" Tag="6" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="关于与反馈"
LogoScale="0.9" Logo="M613.717333 64.426667l3.413334 3.242666 331.861333 331.861334a85.333333 85.333333 0 0 1 3.2 117.269333l-3.2 3.413333L573.162667 896H960a21.333333 21.333333 0 0 1 21.333333 21.333333v42.666667a21.333333 21.333333 0 0 1-21.333333 21.333333H64a21.333333 21.333333 0 0 1-21.333333-21.333333v-42.666667a21.333333 21.333333 0 0 1 21.333333-21.333333h295.338667L74.069333 610.709333a85.333333 85.333333 0 0 1-3.221333-117.269333l3.2-3.413333 422.4-422.357334a85.333333 85.333333 0 0 1 117.269333-3.221333z m-20.629333 99.882666l-21.418667 321.493334a85.333333 85.333333 0 0 1-74.944 79.04l-4.522666 0.426666-321.493334 21.44 295.552 295.530667 422.4-422.378667L593.066667 164.309333z m-86.442667 13.824L184.533333 500.266667l301.994667-20.117334 20.117333-301.994666z" />
</StackPanel>
</local:MyPageLeft>
@@ -1,112 +0,0 @@
Public Class PageLinkLeft
Private IsLoad As Boolean = False
Private IsPageSwitched As Boolean = False '如果在 Loaded 前切换到其他页面,会导致触发 Loaded 时再次切换一次
Private Sub PageLinkLeft_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
If IsLoad Then Return
IsLoad = True
'切换默认页面
If IsPageSwitched Then Return
ItemHiper.SetChecked(True, False, False)
End Sub
Private Sub PageOtherLeft_Unloaded(sender As Object, e As RoutedEventArgs) Handles Me.Unloaded
IsPageSwitched = False
End Sub
#Region "页面切换"
''' <summary>
''' 当前页面的编号。
''' </summary>
Public PageID As FormMain.PageSubType = FormMain.PageSubType.LinkHiper
''' <summary>
''' 勾选事件改变页面。
''' </summary>
Private Sub PageCheck(sender As MyListItem, e As RouteEventArgs) Handles ItemHiper.Check, ItemIoi.Check, ItemSetup.Check, ItemHelp.Check, ItemFeedback.Check
'尚未初始化控件属性时,sender.Tag 为 Nothing,会导致切换到页面 0
'若使用 IsLoaded,则会导致模拟点击不被执行(模拟点击切换页面时,控件的 IsLoaded 为 False)
If sender.Tag IsNot Nothing Then PageChange(Val(sender.Tag))
End Sub
Public Function PageGet(Optional ID As FormMain.PageSubType = -1)
If ID = -1 Then ID = PageID
Select Case ID
Case 0, FormMain.PageSubType.LinkHiper
If FrmLinkHiper Is Nothing Then FrmLinkHiper = New PageLinkHiper
Return FrmLinkHiper
Case FormMain.PageSubType.LinkIoi
If FrmLinkIoi Is Nothing Then FrmLinkIoi = New PageLinkIoi
Return FrmLinkIoi
Case FormMain.PageSubType.LinkSetup
If FrmSetupLink Is Nothing Then FrmSetupLink = New PageSetupLink
Return FrmSetupLink
Case FormMain.PageSubType.LinkHelp
If FrmLinkHelp Is Nothing Then FrmLinkHelp = PageOtherHelp.GetHelpPage(PathTemp & "Help\启动器\联机.json")
Return FrmLinkHelp
Case FormMain.PageSubType.LinkFeedback
If FrmLinkFeedback Is Nothing Then FrmLinkFeedback = New PageLinkFeedback
Return FrmLinkFeedback
Case Else
Throw New Exception("未知的更多子页面种类:" & ID)
End Select
End Function
''' <summary>
''' 切换现有页面。
''' </summary>
Public Sub PageChange(ID As FormMain.PageSubType)
If PageID = ID Then Return
AniControlEnabled += 1
IsPageSwitched = True
Try
PageChangeRun(PageGet(ID))
PageID = ID
Catch ex As Exception
Log(ex, "切换分页面失败(ID " & ID & ")", LogLevel.Feedback)
Finally
AniControlEnabled -= 1
End Try
End Sub
Private Shared Sub PageChangeRun(Target As MyPageRight)
AniStop("FrmMain PageChangeRight") '停止主页面的右页面切换动画,防止它与本动画一起触发多次 PageOnEnter
If Target.Parent IsNot Nothing Then Target.SetValue(ContentPresenter.ContentProperty, Nothing)
FrmMain.PageRight = Target
CType(FrmMain.PanMainRight.Child, MyPageRight).PageOnExit()
AniStart({
AaCode(Sub()
CType(FrmMain.PanMainRight.Child, MyPageRight).PageOnForceExit()
FrmMain.PanMainRight.Child = FrmMain.PageRight
FrmMain.PageRight.Opacity = 0
End Sub, 130),
AaCode(Sub()
'延迟触发页面通用动画,以使得在 Loaded 事件中加载的控件得以处理
FrmMain.PageRight.Opacity = 1
FrmMain.PageRight.PageOnEnter()
End Sub, 30, True)
}, "PageLeft PageChange")
End Sub
#End Region
Public Sub Reset(sender As Object, e As EventArgs)
If MyMsgBox("是否要初始化联机页的所有设置?该操作不可撤销。", "初始化确认",, "取消", IsWarn:=True) = 1 Then
If IsNothing(FrmSetupLink) Then FrmSetupLink = New PageSetupLink
FrmSetupLink.Reset()
ItemSetup.Checked = True
End If
End Sub
Private Sub BtnHiperStop_Loaded(sender As Object, e As RoutedEventArgs)
sender.Visibility = If(PageLinkHiper.HiperState = LoadState.Finished OrElse PageLinkHiper.HiperState = LoadState.Loading, Visibility.Visible, Visibility.Collapsed)
End Sub
Private Sub BtnHiperStop_Click(sender As Object, e As EventArgs)
PageLinkHiper.ModuleStopManually()
End Sub
Private Sub BtnIoiStop_Loaded(sender As Object, e As RoutedEventArgs)
End Sub
Private Sub BtnIoiStop_Click(sender As Object, e As EventArgs)
PageLinkIoi.ModuleStopManually()
End Sub
End Class
@@ -0,0 +1,202 @@
<local:MyPageRight
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PCL" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" x:Class="PageLinkMain"
d:DesignWidth="800" d:DesignHeight="800">
<Grid>
<Grid x:Name="PanLoad">
<local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" x:Name="CardLoad" MinWidth="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="20" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="1*" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<local:MyLoading Grid.Column="1" Grid.Row="1" x:Name="Load" Grid.RowSpan="3" Margin="0,-4,0,-29" HorizontalAlignment="Center"
Text="" TextError="" TextErrorInherit="False" AutoRun="False" IsHitTestVisible="False" RenderTransformOrigin="0.5,0.5">
<local:MyLoading.RenderTransform>
<ScaleTransform ScaleX="0.9" ScaleY="0.9" />
</local:MyLoading.RenderTransform>
</local:MyLoading>
<TextBlock x:Name="LabLoadTitle" Grid.Column="3" Grid.Row="1" Grid.ColumnSpan="2" VerticalAlignment="Center" Text="正在创建联机房间" FontSize="19"
Margin="0,0,25,1" Foreground="{Binding Foreground, ElementName=Load}" IsHitTestVisible="False" />
<TextBlock x:Name="LabLoadDesc" Grid.Column="3" Grid.ColumnSpan="3" Grid.Row="2" VerticalAlignment="Center" Margin="0,5,10,-5" Text="下载联机模块中……" FontSize="14"
LineHeight="19" MaxWidth="450" MaxHeight="220" TextWrapping="Wrap" Foreground="{StaticResource ColorBrushGray3}" IsHitTestVisible="False" />
<local:MyIconButton Grid.Column="5" Grid.Row="1" x:Name="BtnLoadCancel" Height="25" Width="25"
VerticalAlignment="Top" Margin="0,-13,-13,0"
Theme="Black" Opacity="0.3"
ToolTip="取消" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="100"
ToolTipService.VerticalOffset="-2" ToolTipService.HorizontalOffset="0"
LogoScale="0.72" Logo="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" />
<Grid Height="12" VerticalAlignment="Bottom" Grid.ColumnSpan="100" Grid.RowSpan="100" Margin="0,0,0,-0.6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0*" x:Name="ColumnProgressA" />
<ColumnDefinition Width="1*" x:Name="ColumnProgressB" />
</Grid.ColumnDefinitions>
<Grid.Clip>
<RectangleGeometry RadiusX="6" RadiusY="6" x:Name="RectProgressClip" />
</Grid.Clip>
<Rectangle Fill="{Binding Foreground, ElementName=Load}" VerticalAlignment="Bottom" Height="2" />
</Grid>
</Grid>
</local:MyCard>
</Grid>
<Grid x:Name="PanContent">
<Grid x:Name="PanSelect">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="35" />
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<local:MyCard x:Name="PanSelectCreate" Grid.Column="1" Grid.Row="1" CornerRadius="13" VerticalAlignment="Center">
<Grid IsHitTestVisible="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="13" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="22" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="12" />
</Grid.RowDefinitions>
<Path Grid.Column="1" Grid.Row="1" Height="26" Width="26" Margin="0,-1,0,0"
Stretch="Uniform" Fill="{DynamicResource ColorBrush3}"
Data="M384 128v192h256a64 64 0 0 1 64 64v256h192V128H384zM320 320V128a64 64 0 0 1 64-64h512a64 64 0 0 1 64 64v512a64 64 0 0 1-64 64h-192v192a64 64 0 0 1-64 64H128a64 64 0 0 1-64-64V384a64 64 0 0 1 64-64h192z m320 384H384a64 64 0 0 1-64-64V384H128v512h512v-192z m0-64V384H384v256h256z" />
<TextBlock Grid.Column="3" Grid.ColumnSpan="3" Grid.Row="1"
Text="创建房间" FontSize="20" Foreground="{DynamicResource ColorBrush3}" />
<TextBlock Grid.Column="3" Grid.ColumnSpan="3" Grid.Row="2" Margin="1,10,30,10"
Text="邀请朋友加入你的单人游戏。"
FontSize="15" LineHeight="19" MaxWidth="400" TextWrapping="Wrap" Foreground="{StaticResource ColorBrushGray3}" />
</Grid>
</local:MyCard>
<local:MyCard x:Name="PanSelectJoin" Grid.Column="1" Grid.Row="3" CornerRadius="13" VerticalAlignment="Center">
<Grid IsHitTestVisible="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="13" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="22" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="12" />
</Grid.RowDefinitions>
<Path Grid.Column="1" Grid.Row="1" Height="26" Width="26" Margin="0,-1,0,0"
Stretch="Uniform" Fill="{DynamicResource ColorBrush3}"
Data="m399,355l-92.5,101.6c-13.7,13.7-10.6,36.4,4.6,47,13.7,9.1,30.3,7.6,41-4.5l127.4-138.1c16.7-18.2,18.2-47,0-66.8l-125.9-141.1c-12.1-13.7-34.9-15.2-47,0-12.1,12.1-12.1,31.9,0,44l92.5,98.6H28.8c-16.7,0-28.8,13.7-28.8,28.8s13.7,28.8,28.8,28.8h370.2v1.5ZM625.7,0H224.6c-44,0-78.9,34.9-78.9,78.9v101.6c0,15.2,12.1,27.3,27.3,27.3s27.3-12.1,27.3-27.3v-98.6c0-15.2,12.1-27.3,27.3-27.3h387.5c15.2,0,27.3,12.1,27.3,27.3v493c0,15.2-12.1,27.3-27.3,27.3H229.2c-15.2,0-27.3-12.1-27.3-27.3v-101.6c0-16.7-13.7-28.8-28.8-28.8s-28.8,13.7-28.8,28.8v98.6c0,44,34.9,78.9,78.9,78.9h407.2c44,0,67.9-34.9,67.9-78.9V78.9c-4.6-44-28.4-78.9-72.4-78.9Z" />
<TextBlock Grid.Column="3" Grid.ColumnSpan="3" Grid.Row="1"
Text="加入房间" FontSize="20" Foreground="{DynamicResource ColorBrush3}" />
<TextBlock Grid.Column="3" Grid.ColumnSpan="3" Grid.Row="2" Margin="1,10,30,10"
Text="输入你收到的邀请码,加入朋友的房间。"
FontSize="15" LineHeight="19" MaxWidth="400" TextWrapping="Wrap" Foreground="{StaticResource ColorBrushGray3}" />
</Grid>
</local:MyCard>
</Grid>
<Grid x:Name="PanFinish" Visibility="Collapsed" HorizontalAlignment="Center" VerticalAlignment="Center" MinWidth="300">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="25" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" CornerRadius="12">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25" />
<ColumnDefinition Width="Auto" /> <!--Logo-->
<ColumnDefinition Width="13" />
<ColumnDefinition Width="1*" /> <!--文本区 ↓↓-->
<ColumnDefinition Width="Auto" /> <!--按钮 1-->
<ColumnDefinition Width="Auto" /> <!--按钮 2-->
<ColumnDefinition Width="25" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="21" />
<RowDefinition Height="Auto" /> <!--标题栏-->
<RowDefinition Height="Auto" /> <!--文本区-->
<RowDefinition Height="Auto" /> <!--按钮-->
<RowDefinition Height="21" />
</Grid.RowDefinitions>
<Path Grid.Column="1" Grid.Row="1" Height="28" Width="28" x:Name="ImgFinishLogo"
Stretch="Uniform" Fill="{DynamicResource ColorBrush3}" RenderTransformOrigin="0.5,0.54"
Data="M845 547.45h65.54A400.6 400.6 0 0 0 712.7 201.77l-33.22 56.49C781.6 318.32 845 429.13 845 547.45zM347.72 253.92L316 196.58a401.21 401.21 0 0 0-207 350.87h65.54a335.66 335.66 0 0 1 173.18-293.53zM509.79 882.68a334 334 0 0 1-181.3-53.2L293 884.57a401.13 401.13 0 0 0 419.1 8.91L679 836.93a334.94 334.94 0 0 1-169.21 45.75z M821.51 625.1A108.64 108.64 0 0 0 752.2 650l-67.31-38.86a185.44 185.44 0 0 0 11.22-63.7c0-91.55-66.38-167.9-153.54-183.42V289a109.14 109.14 0 1 0-65.57 0v75c-87.16 15.53-153.54 91.87-153.54 183.42a185.46 185.46 0 0 0 11.22 63.7L267.39 650a109.17 109.17 0 1 0 35.81 55l64.47-37.23a186 186 0 0 0 284.23 0L716.38 705a109.13 109.13 0 1 0 105.13-79.9zM198.07 777.85a43.6 43.6 0 1 1 43.61-43.61 43.66 43.66 0 0 1-43.61 43.61z m311.72-636.53a43.61 43.61 0 1 1-43.61 43.61 43.65 43.65 0 0 1 43.61-43.61z m0 526.91a120.78 120.78 0 1 1 120.78-120.78 120.91 120.91 0 0 1-120.78 120.77z m311.72 109.62a43.6 43.6 0 1 1 43.61-43.61 43.65 43.65 0 0 1-43.61 43.61z" />
<TextBlock x:Name="LabFinishTitle" Grid.Column="3" Grid.ColumnSpan="3" Grid.Row="1" Margin="0,3,0,0"
Text="已加入房间" FontSize="20" Foreground="{DynamicResource ColorBrush3}" />
<TextBlock x:Name="LabFinishDesc" Grid.Column="3" Grid.ColumnSpan="3" Grid.Row="2" Margin="1,9,10,10"
Text="描述文本。"
FontSize="14" LineHeight="19" MaxWidth="400" TextWrapping="Wrap" Foreground="{StaticResource ColorBrushGray2}" />
<local:MyButton x:Name="BtnFinishCopy" Grid.Column="4" Grid.Row="3" Margin="0,0,15,0" Text="复制" Padding="13,8" ColorType="Highlight" />
<local:MyButton x:Name="BtnFinishExit" Grid.Column="5" Grid.Row="3" Text="退出" Padding="13,8" />
</Grid>
</local:MyCard>
<local:MyCard Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center" CornerRadius="8">
<StackPanel Orientation="Horizontal" Margin="3,0">
<StackPanel x:Name="BtnFinishPing" Orientation="Horizontal" Background="{StaticResource ColorBrushSemiTransparent}"
ToolTip="延迟" ToolTipService.Placement="Center" ToolTipService.InitialShowDelay="50" ToolTipService.VerticalOffset="40" ToolTipService.HorizontalOffset="4">
<Path IsHitTestVisible="False" Height="14" Width="14" Stretch="Uniform" Fill="#9C9C9C" Margin="13,0,8,0"
Data="M60.9 966.2A60.3 60.3 0 0 1 0.8 906.1v-90.1a60.3 60.3 0 0 1 60.1-60.1 60.3 60.3 0 0 1 60.1 60.1v90.1a60.2 60.2 0 0 1-60.1 60.1z m300.6 0a60.3 60.3 0 0 1-60.1-60.1V665.6a60.3 60.3 0 0 1 60.1-60.1 60.3 60.3 0 0 1 60.1 60.1v240.5a60.2 60.2 0 0 1-60.1 60.1z m300.6 0a60.3 60.3 0 0 1-60.1-60.1V425.1a60.3 60.3 0 0 1 60.1-60.1 60.3 60.3 0 0 1 60.1 60.1v481.0a60.3 60.3 0 0 1-60.1 60.1z m300.6 0a60.3 60.3 0 0 1-60.1-60.1V124.5a60.3 60.3 0 0 1 60.1-60.1 60.3 60.3 0 0 1 60.1 60.1v781.6a60.3 60.3 0 0 1-60.1 60.1z" />
<TextBlock x:Name="LabFinishPing" IsHitTestVisible="False" Text="15 ms" VerticalAlignment="Center" Foreground="{StaticResource ColorBrushGray1}" Margin="0,0.5,13,0" />
</StackPanel>
<TextBlock Text="|" Opacity="0.2" Margin="0,8.8,0,8" RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<ScaleTransform ScaleX="1.5" ScaleY="2.75" CenterX="0.5" CenterY="0.5" />
</TextBlock.RenderTransform>
</TextBlock>
<StackPanel x:Name="BtnFinishPlayer" Orientation="Horizontal" Background="{StaticResource ColorBrushSemiTransparent}"
ToolTip="房间内人数" ToolTipService.Placement="Center" ToolTipService.InitialShowDelay="50" ToolTipService.VerticalOffset="40" ToolTipService.HorizontalOffset="4">
<Path IsHitTestVisible="False" Height="18" Width="18" Stretch="Uniform" Fill="{StaticResource ColorBrushGray4}" Margin="13,0,8,0"
Data="m843.3,478.4c-.1-3.9-.2-7.7-.2-10.8,0-42.8-11.1-84.4-32-120.2-19.6-33.5-47.1-60.7-79.7-79,15-16.8,23.6-39.2,25-65.2,1.3-24.8-3.7-52.1-14.6-79-20.5-50.8-56-86.3-86.3-86.3s-25.1,4.1-37.5,23.5l-.4.7-.2.8c-4.1,16.6,3.9,33.4,20,41.7l.7.4h.8c34.9,5.6,50.6,38.7,53.5,65.3,3.3,30.7-7.8,67.2-40.1,80.1-7.4,1.1-9.9,7.7-11.5,12.1-.6,1.7-1.2,3.3-1.9,4.3l-.7,1v1.2c-3.4,27.8,19.6,39.8,46.3,53.6,41.4,21.4,93,48.1,93.1,144.9v.3c.3,4.3,0,9.9-.1,15.7-.6,17.9-1.4,40.2,10.3,52.4,5.5,5.7,13,8.6,22.5,8.7.7,0,1.4,0,2.1,0h0c8.1,0,14.7-2.6,19.8-7.9,12.7-13.1,11.9-39.4,11.4-58.6Z M204.7,295.9l.5-1.1v-30.1c0-3.9-2.1-6-3.3-7.1-.1-.1-.2-.2-.3-.3v-1.7l-8.9-9.2-.7-.4c-26.3-13.5-42.6-40.8-42.6-71.3s21.8-67.4,55.5-78.1c17-4.5,28.2-22.1,24.5-38.4v-.6c-.1,0-.4-.5-.4-.5-7.5-15.3-21.4-24.7-36.2-24.7s-5.1.3-7.7.8h-.2c-29.8,7.2-56.6,25.5-75.7,51.4-18.9,25.7-29.3,57.8-29.3,90.4s11.1,64.8,32.2,92.1C45.3,303.9,2.5,376.9,0,458.9v57.5s0,.2,0,.2c1.4,15.3,13.1,30.8,32.8,30.8s32.9-12.7,32.9-33.9v-43.3c0-76.2,45.9-139.2,114.3-157.1,10.5-.5,19.5-6.8,24.7-17.3Z M516,286.5c36.8-31.1,58.5-77.4,58.5-125.4C574.4,70.8,507.4,0,421.7,0s-152.7,70.8-152.7,161.2,20.4,95.1,56.4,125.6c-51.7,19.8-96.1,54.8-129,101.5-35.2,50.2-53.9,109.7-53.9,172v33.8c0,16.9,13.7,30.6,30.6,30.6h496.6c16.9,0,30.6-13.7,30.6-30.6v-33.8c0-61.4-19-120.6-54.9-171.3-32.9-46.4-78.6-82.5-129.5-102.4Zm-94.5-225.3c51.3,0,91.5,43.9,91.5,100s-41.9,100-91.5,100-91.5-43.9-91.5-100,40.2-100,91.5-100Zm218,502H203.8v-3.2c0-127,97.8-230.3,218-230.3s217.7,105.4,217.7,230.3v3.2Z" />
<TextBlock x:Name="LabFinishPlayer" IsHitTestVisible="False" Text="2  人" VerticalAlignment="Center" Foreground="{StaticResource ColorBrushGray1}" Margin="0,0.5,13,0" />
</StackPanel>
<TextBlock Text="|" x:Name="LineFinishPort" Opacity="0.2" Margin="0,8.8,0,8" RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<ScaleTransform ScaleX="1.5" ScaleY="2.75" CenterX="0.5" CenterY="0.5" />
</TextBlock.RenderTransform>
</TextBlock>
<StackPanel x:Name="BtnFinishPort" Orientation="Horizontal" Background="{StaticResource ColorBrushSemiTransparent}"
ToolTip="端口" ToolTipService.Placement="Center" ToolTipService.InitialShowDelay="50" ToolTipService.VerticalOffset="40" ToolTipService.HorizontalOffset="4">
<Path IsHitTestVisible="False" Height="16" Width="16" Stretch="Uniform" Fill="{StaticResource ColorBrushGray4}" Margin="13,0,7,0"
Data="m365,295.3c-15.1-3.6-36.5-1.8-39.6,17.5-1.2,12.4,0,107-.5,119.7.8,16.8-5.2,42.4,16.8,47.1,14.3,3.3,34.5,1.9,38.7-15.4,2.6-1.4.1-142.4,1-148.3-.7-10.9-6.2-17.8-16.4-20.6ZM234.1,311.6c-3.2-16.7-20-18.6-34.4-17.2-9.8.4-17.2,6.2-20,15.4-4.4,47.8-.2,97.3-1.6,144.8,0,16,9.8,26.6,24.6,26.1,14.4,1.4,29.1-2.9,31.7-19,2.2-8.8.8-63.9,1.1-74.1-1-25.3,2.1-50.6-1.5-75.9ZM521.4,300.6c-5.4-6.2-15.9-7.1-24.6-6.4-11.9-1-23,6.6-24.7,18.7-2.2,24.1-2,124-.2,148.1,2.6,19.7,24.1,22.4,40.1,18.4,11.9-2.4,16.6-15.2,16.3-27.1,0-33.6.1-69.3,0-103.1-1.2-15.1,4.2-37-6.9-48.5ZM708.8,194.2c.7-34.5-26.8-63.7-61.5-63.4-22.1-.1-52.4,0-75.6,0-13.4,1.1-8.7-12.1-9.6-20.9-1.6-30.6,7.5-67.2-17.5-90.9-11.7-11.9-27.4-18.4-44.3-18.4-64.8,0-129.7,0-194.5,0-37.7,1.4-76.5-3.1-113.8,2.2-48.1,14.7-46.7,55.3-45.4,97.3,0,8,.1,16.1,0,24.1.2,7-7.3,6.8-12.2,6.6-16,0-32,0-48,0-24.4-.7-51.3-1-68.6,18.4C5.8,161.2,0,176.1,0,194.7c0,0,0,350.9,0,350.9,0,22.9,5.5,41.7,16.8,57.5,19.2,27,45.9,40.7,79.5,40.7,37.4,0,77.4.1,129.9.1,42.5,0,85,0,127.5,0,11.2-1.3,273.6,2.4,278-1.8,46.1-9.1,77.1-46.8,77.1-93.8,0-117.8,0-237.9,0-354.1Zm-71-5.3c7.6.2,9.2,1.9,9.3,9.7,0,0,0,347.3,0,347.3,0,3.2.2,6.5-.3,9.9-2.8,19.3-18.9,32.3-38.5,31.9-4.5.4-508.9-.4-513.5,0-3.2,0-6.5.1-9.9-.4-19-2.9-31.8-17.8-31.8-37.1,0-117.9,0-233.9,0-350.9,0-9,2.3-10.3,10.9-10.3,0,0,131.2,0,131.2,0,1.6,0,3,0,4.5,0,0-1.5,0-2.7,0-4.3,0,0,0-115.7,0-115.7.1-8.3,1.9-10.1,10.3-10.1h280.3c8.3,0,10.1,1.8,10.1,10.3v114.1c0,1.9,0,3.7,0,5.6" />
<TextBlock x:Name="LabFinishPort" IsHitTestVisible="False" Text="12345" VerticalAlignment="Center" Foreground="{StaticResource ColorBrushGray1}" Margin="0,0.5,13,0" />
</StackPanel>
<!--<StackPanel x:Name="BtnFinishIp" Orientation="Horizontal" Background="{StaticResource ColorBrushSemiTransparent}"
ToolTip="服务器 IP ( 点击复制 )" ToolTipService.Placement="Center" ToolTipService.InitialShowDelay="50" ToolTipService.VerticalOffset="40" ToolTipService.HorizontalOffset="4">
<Path IsHitTestVisible="False" Height="17" Width="17" Stretch="Uniform" Fill="{StaticResource ColorBrushGray4}" Margin="11,0,7,0"
Data="m346.4,692.8c-92.5,0-179.5-36-244.9-101.5C36,525.9,0,438.9,0,346.4S36,166.9,101.5,101.5C166.9,36,253.9,0,346.4,0s179.5,36,244.9,101.5c65.4,65.4,101.5,152.4,101.5,244.9s-36,179.5-101.5,244.9c-65.4,65.4-152.4,101.5-244.9,101.5Zm0-645.1c-164.7,0-298.7,134-298.7,298.7s134,298.7,298.7,298.7,298.7-134,298.7-298.7S511.1,47.7,346.4,47.7Zm13.8,473.4c-14.5,0-28.2-9.8-28.2-24.2V207.9c0-14.5,11.8-26.2,26.2-26.2h78.9c57.9,0,105.1,47.1,105.1,105.1s-47.1,105.1-105.1,105.1h-52.6v105.1c0,14.5-9.8,24.2-24.2,24.2Zm24.2-181.7h52.6c29,0,52.6-23.6,52.6-52.6s-20-49.2-46.5-52.3h-.4s-5.3-.4-5.3-.4h-53v105.2Zm-158.8,181.7c-14.5,0-28.2-9.8-28.2-24.2V207.9c0-14.5,11.8-26.2,26.2-26.2s26.2,11.8,26.2,26.2v289c0,14.5-9.8,24.2-24.2,24.2Z" />
<TextBlock x:Name="LabFinishIp" IsHitTestVisible="False" Text="6.0.0.1:12345" VerticalAlignment="Center" Foreground="{StaticResource ColorBrushGray1}" Margin="0,0.5,13,0" />
</StackPanel>-->
</StackPanel>
</local:MyCard>
</Grid>
</Grid>
<Grid x:Name="PanAlways" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="15" Opacity="0.5">
</Grid>
</Grid>
</local:MyPageRight>
@@ -0,0 +1,844 @@
Public Class PageLinkMain
'===============================
' 状态机与前端页面
'===============================
#Region "状态管理"
Private Enum States
Waiting
Loading
Failed
Finished
End Enum
Private State As States = States.Waiting
''' <summary>
''' 切换到指定的状态。
''' </summary>
Private Sub ChangeState(NewState As States)
If State = NewState Then Return
Dim OldState = State
State = NewState
Log($"[Link] 主状态由 {GetStringFromEnum(OldState)} 变更为 {GetStringFromEnum(NewState)}")
'触发状态切换
RunInUi(
Sub()
Select Case NewState
Case States.Waiting
SwitchToWaiting(OldState)
Case States.Loading
SwitchToLoading(OldState)
Case States.Failed
SwitchToFailed(OldState)
Case States.Finished
SwitchToFinished(OldState)
End Select
End Sub)
End Sub
'让加载器的 成功/失败 事件触发状态切换
Private Sub LoaderInit() Handles Me.Initialized
PageLoaderInit(Load, PanLoad, PanContent, PanAlways, LinkLoader, AutoRun:=False)
AddHandler LinkLoader.OnStateChangedUi,
Sub(Loader As LoaderBase, NewState As LoadState, OldState As LoadState)
Select Case NewState
Case LoadState.Finished
ChangeState(States.Finished)
Case LoadState.Failed
Telemetry("联机失败", "Exception", FilterUserName(Loader.Error.GetDetail, "*"))
ChangeState(States.Failed)
End Select
End Sub
End Sub
'页面动画兼容
Private Sub UpdatePanelVisibility() Handles Me.PageEnter
FrmLinkMain.PanSelect.Visibility = If(State = States.Waiting, Visibility.Visible, Visibility.Collapsed)
FrmLinkMain.PanFinish.Visibility = If(State = States.Finished, Visibility.Visible, Visibility.Collapsed)
End Sub
#End Region
#Region "初始页面 (Waiting)"
'由任意状态切换到 Waiting
Private Sub SwitchToWaiting(OldState As States)
'加载器与进程状态
LinkLoader.Abort()
SyncLock LinkLoader.LockState
LinkLoader.State = LoadState.Waiting
End SyncLock
ProcessStop()
'页面切换
If OldState <> States.Finished Then
'从完成页面退出时,PageOnContentExit 结束会触发 PageEnter 事件,这会调用 UpdatePanelVisibility
'如果在现在提前调用了,会丢失完成页面的退出动画
UpdatePanelVisibility()
End If
PageOnContentExit()
End Sub
'===============================
' 页面 UI
'===============================
'创建
Private Sub Create_MouseLeftButtonUp() Handles PanSelectCreate.MouseLeftButtonUp
'输入端口号
Dim Port As String = MyMsgBoxInput("输入端口", $"在单人游戏的暂停菜单选择 {vbLQ}对局域网开放{vbRQ},然后输入端口数字。{vbCrLf}其实也可以输入其他游戏的端口……",
ValidateRules:=New ObjectModel.Collection(Of Validate) From {New ValidateInteger(1024, 65535)},
HintText:="端口号")
If Port Is Nothing Then Return
'基础信息
IsServerSide = True
ServerPort = Port
Dim GenerateRandomCode =
Function() As String
Dim Result As String = ""
For i = 1 To 5
Result &= RadixConvert(RandomInteger(0, 35), 10, 36)
Next
Return Result.Replace("O", "0").Replace("I", "1")
End Function
NetworkName = $"P{RadixConvert(ServerPort, 10, 16).PadLeft(4, "0"c)}-{GenerateRandomCode()}"
NetworkSecret = GenerateRandomCode()
Log($"[Link] 尝试创建房间,网络名 {NetworkName},网络密码 {NetworkSecret},端口 {ServerPort}")
'启动
ChangeState(States.Loading)
End Sub
'加入
Private LastCode As String = Nothing
Private Sub Join_MouseLeftButtonUp() Handles PanSelectJoin.MouseLeftButtonUp
'输入邀请码
Dim Code As String = MyMsgBoxInput("输入邀请码", "输入房主发给你的邀请码。",
HintText:=If(String.IsNullOrEmpty(LastCode), "", "使用上一次的邀请码:" & LastCode))
If Not String.IsNullOrEmpty(LastCode) AndAlso Code IsNot Nothing AndAlso Code = "" Then Code = LastCode
If Code Is Nothing Then Return
'基础格式校验
Code = Code.Between("【", "】").Between("[", "]") '从完整消息中提取
Code = Code.ToUpper.Replace("O", "0").Replace("I", "1") '输入修正
If Not (Code.Length >= 14 AndAlso Code(0) = "P"c AndAlso Code(5) = "-"c AndAlso Code(11) = "-"c) Then
If Code.StartsWithF("U/") Then 'HMCL
Hint("请让房主使用 PCL 创建房间!", HintType.Red)
ElseIf Code.Length = 10 Then 'PCL CE
Hint("请让房主使用非社区版的 PCL 创建房间!", HintType.Red)
ElseIf Code.StartsWithF("P") Then '远古版 PCL
Hint("你的 PCL 版本与房主的 PCL 版本不一致,或输入的邀请码有误!", HintType.Red)
Else
Hint("邀请码有误,请让房主使用 PCL 创建房间!", HintType.Red)
End If
Return
End If
'离线登录警告
If McLoginLoader.State = LoadState.Finished AndAlso McLoginLoader.Output.Type = "Legacy" AndAlso Not Setup.Get("LinkOfflineHint") Then
Dim Solution As String
If McVersionCurrent IsNot Nothing AndAlso McVersionCurrent.Version.McVersion >= New Version(1, 21, 5) Then
Solution = $"请换用正版登录,或是让房主安装 {vbLQ}更高级联机设置{vbRQ} Mod 并禁用正版验证。"
ElseIf McVersionCurrent IsNot Nothing AndAlso McVersionCurrent.Version.McVersion >= New Version(1, 12, 0) Then
Solution = $"请换用正版登录,或是让房主安装 {vbLQ}自定义局域网联机{vbRQ} Mod 并关闭在线模式。"
Else
Solution = $"请换用正版登录,或是让房主用服务端开服,关闭 online-mode,然后在创建房间时输入服务端的端口号。"
End If
Select Case MyMsgBox($"如果不用正版登录可能会进不了服,显示 {vbLQ}无效会话{vbRQ}。" & vbCrLf & Solution,
"离线登录警告", "继续", "继续且不再提示", "取消", IsWarn:=True)
Case 2
Setup.Set("LinkOfflineHint", True)
Case 3
Return
End Select
End If
'基础信息
IsServerSide = False
ServerPort = RadixConvert(Code.Substring(1, 4), 16, 10)
NetworkName = Code.Substring(0, 11)
NetworkSecret = Code.Substring(12, 5)
Log($"[Link] 尝试加入房间,网络名 {NetworkName},网络密码 {NetworkSecret},端口 {ServerPort}")
'启动
LastCode = Code
ChangeState(States.Loading)
End Sub
#End Region
#Region "加载页面 (Loading | Failed)"
'由 Waiting 或 Failed 状态切换到 Loading
Private Sub SwitchToLoading(OldState As States)
'清理变量
Peers = Nothing
FailCount = 0
'加载器与进程状态
LinkLoader.Start(IsForceRestart:=True)
'页面切换会由 Loader 调用 MyPageRight 来触发
'UI 更新
UpdateProgressBar(0)
LabLoadTitle.Text = If(IsServerSide, "创建房间中", "加入房间中")
UpdateLoadingPage("正在初始化……", "准备初始化")
End Sub
'由 Loading 状态切换到 Failed
Private Sub SwitchToFailed(OldState As States)
'加载器与进程状态
LinkLoader.Abort()
ProcessStop()
'UI 更新
UpdateProgressBar(1)
'显示错误信息
LabLoadTitle.Text = FailReason
Dim Brief As String = LinkLoader.Error.GetBrief
Dim InnerEx As Exception = LinkLoader.Error
If InnerEx.Message.StartsWithF("$") Then Brief = InnerEx.Message
Do Until InnerEx.InnerException Is Nothing
InnerEx = InnerEx.InnerException
If InnerEx.Message.StartsWithF("$") Then Brief = InnerEx.Message
Loop
LabLoadDesc.Text = If(Brief.StartsWithF("$"), Brief.TrimStart("$"), LinkLoader.Error.GetDetail)
Log(LinkLoader.Error, LabLoadTitle.Text)
End Sub
'===============================
' 页面 UI
'===============================
Private Sub UpdateLoadingPage(Title As String, FailBrief As String)
If FailReason = FailBrief & "失败" Then Return
FailReason = FailBrief & "失败"
Log("[Link] 开始步骤:" & Title)
RunInUiWait(
Sub()
If FrmLinkMain Is Nothing OrElse Not FrmLinkMain.LabLoadDesc.IsLoaded Then Return
FrmLinkMain.LabLoadDesc.Text = Title
End Sub)
End Sub
Private FailReason As String = "准备初始化"
'取消加载
Private Sub BtnLoadCancel_Click() Handles BtnLoadCancel.Click
ChangeState(States.Waiting)
End Sub
'点击重试
Private Sub CardLoad_MouseLeftButtonUp() Handles CardLoad.MouseLeftButtonUp
ChangeState(States.Loading)
End Sub
'进度条
Private Sub UpdateProgressBar(Optional Value As Double = -1)
If Value = -1 Then Value = LinkLoader.Progress
Dim DisplayingProgress As Double = ColumnProgressA.Width.Value
If Math.Round(Value - DisplayingProgress, 3) = 0 Then Return
If DisplayingProgress <= Value Then
Dim NewProgress As Double = If(Value = 1, 1, DisplayingProgress +
(Value - DisplayingProgress) ^ 2 * 0.5)
AniStart({
AaGridLengthWidth(ColumnProgressA, NewProgress - ColumnProgressA.Width.Value, 200),
AaGridLengthWidth(ColumnProgressB, (1 - NewProgress) - ColumnProgressB.Width.Value, 200)
}, "Link Progress")
Else
ColumnProgressA.Width = New GridLength(Value, GridUnitType.Star)
ColumnProgressB.Width = New GridLength(1 - Value, GridUnitType.Star)
AniStop("Link Progress")
End If
End Sub
Private Sub CardLoad_SizeChanged() Handles CardLoad.SizeChanged
RectProgressClip.Rect = New Rect(0, 0, CardLoad.ActualWidth, 12)
End Sub
#End Region
#Region "完成页面 (Finished)"
'由 Loading 状态切换到 Finished
Private Sub SwitchToFinished(OldState As States)
'由于只可能从加载器的完成事件触发,不需要管加载器
UpdatePanelVisibility() '页面的实际切换会由 Loader 调用 MyPageRight 来触发
'UI 更新
If IsServerSide Then
LabFinishTitle.Text = "已创建房间"
LabFinishDesc.Text = $"把邀请码发给朋友,让大家加入房间吧!{vbCrLf}邀请码:{NetworkName}-{NetworkSecret}"
BtnFinishCopy.Text = "复制邀请码"
BtnFinishExit.Text = "关闭"
'下边栏
BtnFinishPing.ToolTip = "网络延迟"
LineFinishPort.Visibility = Visibility.Visible
BtnFinishPort.Visibility = Visibility.Visible
LabFinishPort.Visibility = Visibility.Visible
LabFinishPort.Text = ServerPort
Else
LabFinishTitle.Text = "已加入房间"
LabFinishDesc.Text = $"在多人游戏中选择直接连接,粘贴地址即可加入游戏!{vbCrLf}服务器地址:{ClientAddress}" '"如果提示无效会话,不要退出联机房间,重新启动游戏即可!"
BtnFinishCopy.Text = "复制地址"
BtnFinishExit.Text = "离开"
'下边栏
BtnFinishPing.ToolTip = "与房主的延迟"
LineFinishPort.Visibility = Visibility.Collapsed
BtnFinishPort.Visibility = Visibility.Collapsed
LabFinishPort.Visibility = Visibility.Collapsed
End If
Copy() '复制信息
Update() '立即刷新
End Sub
'===============================
' 页面 UI
'===============================
'退出
Private Sub BtnFinishExit_Click() Handles BtnFinishExit.Click
TryExit(True)
End Sub
''' <summary>
''' 退出联机。
''' 返回是否弹出了警告窗口并且玩家选择了取消。
''' </summary>
Public Function TryExit(SendWarning As Boolean) As Boolean
If State = States.Waiting OrElse State = States.Failed Then Return False
If IsServerSide AndAlso PeopleCount > 1 Then
If MyMsgBox("你确定要关闭联机房间吗?" & vbCrLf & "所有玩家都需要重新输入邀请码才可加入游戏!", "关闭房间", "确定", "取消", IsWarn:=True) = 2 Then Return True
End If
ChangeState(States.Waiting)
Return False
End Function
'复制
Private Sub Copy() Handles BtnFinishCopy.Click
If IsServerSide Then
ClipboardSet($"在 PCL 启动器中输入邀请码【{NetworkName}-{NetworkSecret}】,即可加入联机房间!", False)
Hint("已复制邀请码!", HintType.Green)
Else
ClipboardSet(ClientAddress, False)
Hint("已复制服务器地址!", HintType.Green)
End If
End Sub
#End Region
'===============================
' 后端
'===============================
''' <summary>
''' EasyTier 所在的文件夹路径,以 \ 结尾。
''' </summary>
Private PathEasyTier As String = PathAppdata & "EasyTier\"
''' <summary>
''' 当前是服务端还是客户端。
''' </summary>
Private IsServerSide As Boolean
''' <summary>
''' 服务端映射前的端口号。
''' </summary>
Private ServerPort As Integer
''' <summary>
''' 在客户端映射后的地址。
''' 除非已作为客户端启动 EasyTier,否则一直为 Nothing。
''' </summary>
Private ClientAddress As String = Nothing
''' <summary>
''' 网络信息。
''' </summary>
Private NetworkName As String, NetworkSecret As String
#Region "加载"
Private WithEvents LinkLoader As New LoaderCombo(Of Integer)("联机", {
New LoaderTask(Of Integer, Integer)("获取配置", AddressOf InitConfig) With {.Block = False, .ProgressWeight = 8},
New LoaderTask(Of Integer, List(Of NetFile))("准备下载联机模块", AddressOf InitPrepareDownload) With {.ProgressWeight = 2},
New LoaderDownload("下载联机模块", New List(Of NetFile)) With {.ProgressWeight = 40},
New LoaderTask(Of Integer, Integer)("启动联机模块", AddressOf InitLaunch) With {.ProgressWeight = 50}
})
'1. 获取服务器配置
Private Sub InitConfig(Task As LoaderTask(Of Integer, Integer))
UpdateLoadingPage("正在联网获取配置……", "联网获取配置")
If VersionBranchMain <> "Official" Then
Throw New Exception($"$开源版无法联网获取配置。{vbCrLf}你可以在 PCL 官方版的缓存文件夹下查看 ServerConfig 的当前内容, 并在代码中进行相应修改。")
End If
ServerLoader.WaitForExit(LoaderToSyncProgress:=Task)
If ServerConfig Is Nothing Then Throw New Exception("无法从服务器获取配置")
If Not String.IsNullOrEmpty(ServerConfig("Link")("DisableReason")) Then '检查是否已禁用联机功能
Throw New Exception("$" & ServerConfig("Link")("DisableReason").ToString)
End If
End Sub
'2. 获取需要下载的文件
Private ServerVersion As Integer
Private Sub InitPrepareDownload(Task As LoaderTask(Of Integer, List(Of NetFile)))
'获取 CPU 架构
UpdateLoadingPage("正在获取 CPU 架构……", "获取 CPU 架构")
Dim Architecture As String = GetType(String).Assembly.GetName().ProcessorArchitecture
Select Case Architecture
Case Reflection.ProcessorArchitecture.X86
Architecture = "i686"
Case Reflection.ProcessorArchitecture.Amd64
Architecture = "x86_64"
Case Reflection.ProcessorArchitecture.Arm
Architecture = "arm64"
Case Else
Log($"[Link] CPU 是不支持的 {Architecture} 架构,这可能会导致联机模块无法启动!", LogLevel.Debug)
Architecture = "arm64"
End Select
Log("[Link] CPU 架构:" & Architecture)
Telemetry("联机开始")
'检查 EasyTier 版本
UpdateLoadingPage("正在检查联机模块版本……", "检查联机模块版本")
If Not (File.Exists(PathEasyTier & "联机模块 CLI.exe") AndAlso File.Exists(PathEasyTier & "联机模块.exe") AndAlso
File.Exists(PathEasyTier & "Packet.dll")) Then
Setup.Set("LinkEasyTierVersion", -1)
End If
Dim LocalVersion As Integer = Setup.Get("LinkEasyTierVersion")
ServerVersion = ServerConfig("Link")("EasyTierVersion")
Dim RequiredFiles As New List(Of NetFile)
Log($"[Link] EasyTier 本地版本:{LocalVersion},需求版本:{ServerVersion}")
If LocalVersion < ServerVersion Then
RequiredFiles.Add(New NetFile(
ServerConfig("Link")("Downloads").Select(Function(UrlEntry) UrlEntry.ToString.Replace("{arch}", Architecture)),
PathEasyTier & "EasyTier.zip",
New FileChecker(MinSize:=1024 * 1024 * 2)))
End If
'开始下载
UpdateLoadingPage("正在下载联机模块……", "下载联机模块")
Task.Output = RequiredFiles
End Sub
'3. 启动联机模块
Private Shared HostName As String
Private Sub InitLaunch(Task As LoaderTask(Of Integer, Integer))
'解压文件
UpdateLoadingPage("正在解压联机模块……", "解压联机模块")
If File.Exists(PathEasyTier & "EasyTier.zip") Then
'解压
Dim ExtractPath As String = RequestTaskTempFolder()
ExtractFile(PathEasyTier & "EasyTier.zip", ExtractPath,
ProgressIncrementHandler:=Sub(Progress) Task.Progress += Progress * 0.06)
Dim ExtractedPath As String = New DirectoryInfo(ExtractPath).EnumerateDirectories.FirstOrDefault?.FullName
Log("[Link] 联机模块解压时的临时路径:" & ExtractedPath)
CopyDirectory(ExtractedPath, PathEasyTier)
'重命名
File.Delete(PathEasyTier & "联机模块.exe")
Rename(PathEasyTier & "easytier-core.exe", PathEasyTier & "联机模块.exe")
File.Delete(PathEasyTier & "联机模块 CLI.exe")
Rename(PathEasyTier & "easytier-cli.exe", PathEasyTier & "联机模块 CLI.exe")
'清理
File.Delete(PathEasyTier & "EasyTier.zip")
Setup.Set("LinkEasyTierVersion", ServerVersion)
End If
Task.Progress = 0.1
'获取启动参数
UpdateLoadingPage("正在启动联机模块……", "启动联机模块")
Dim Arguments As String = ServerConfig("Link")("Argument")
Arguments += $" --network-name={NetworkName} --network-secret={NetworkSecret}"
HostName = If(IsServerSide, "Server-", "Client-") & RadixConvert(Math.Abs(Identify.GetHashCode), 10, 36)
If IsServerSide Then
Arguments += $" -i 10.114.114.114 --hostname={HostName} --tcp-whitelist={ServerPort} --udp-whitelist={ServerPort}"
Else
Arguments += $" -d --hostname={HostName} --tcp-whitelist=0 --udp-whitelist=0"
'端口转发
Dim Port As Integer = GetAvailablePort()
ClientAddress = $"localhost:{Port}"
Arguments += $" --port-forward tcp://[{IPAddress.IPv6Loopback}]:{Port}/10.114.114.114:{ServerPort}"
Arguments += $" --port-forward udp://[{IPAddress.IPv6Loopback}]:{Port}/10.114.114.114:{ServerPort}"
Arguments += $" --port-forward tcp://{IPAddress.Loopback}:{Port}/10.114.114.114:{ServerPort}"
Arguments += $" --port-forward udp://{IPAddress.Loopback}:{Port}/10.114.114.114:{ServerPort}"
End If
For Each Peer As String In ServerConfig("Link")("Peers")
Arguments += $" -p=""{Peer}"""
Next
If Not Setup.Get("LinkShareMode") Then Arguments += " --private-mode true"
'启动进程
ProcessStart(Arguments)
Task.Progress = 0.15
'等待连接
Dim TimeoutCounter As Integer = 0
Do
Thread.Sleep(500) '为避免还没启动好先跑 CLI 了,先等半秒
'刷新信息
CheckCrash()
RefreshPeerLoader.WaitForExit(IsForceRestart:=True)
'查找目标节点
Dim Ping = GetPeerPing()
If Ping <> 0 Then
Log($"[Link] 已与目标建立连接,当前 Ping 为 {Ping:0.0}ms")
Telemetry("联机成功", "Server", IsServerSide, "Ping", Ping)
Exit Do '退出循环
End If
'更新进度
Dim LastProgress = Task.Progress
Dim PeerCount As Integer = If(Peers Is Nothing, -1, Peers.Count)
Select Case PeerCount
Case -1 'CLI 无返回
Task.Progress = MathClamp(Task.Progress + 0.02, 0.15, 0.25)
Case 0 'CLI 有返回,但未连接到任何节点
UpdateLoadingPage("正在连接到节点……", "连接节点")
Task.Progress = MathClamp(Task.Progress + 0.02, If(IsServerSide, 0.5, 0.3), If(IsServerSide, 0.99, 0.5))
Case Else '已连接到节点,但未连接到房主
UpdateLoadingPage("正在连接到房主……", "连接房主")
Task.Progress = MathClamp(Task.Progress + 0.02, Math.Min(0.45 + PeerCount * 0.05, 0.65), 0.99)
End Select
'超时判定
If LastProgress <> Task.Progress Then
TimeoutCounter = 0
ElseIf TimeoutCounter < 20 Then
TimeoutCounter += 1
Else '进度停滞超过 10s
Select Case PeerCount
Case -1 'CLI 无返回
Panic("无法启动联机模块", "近期日志:" & vbCrLf & LogHistory.Join(vbCrLf))
Case 0 'CLI 有返回,但未连接到任何节点
Panic("无法连接到节点", $"请检查你的网络环境是否良好。")
Case Else '已连接到节点,但未连接到房主
Panic("无法连接到房主", $"可能的原因:{vbCrLf}- 你或者房主的网络环境不佳{vbCrLf}- 房主已关闭房间{vbCrLf}- 邀请码输错了")
End Select
End If
Loop Until Task.IsAborted
If Task.IsAborted Then Throw New ThreadInterruptedException
End Sub
#End Region
#Region "进程与日志"
''' <summary>
''' EasyTier Core 进程。
''' </summary>
Private Shared ProcessCore As Process
Private Shared ProcessOutputHandle As AutoResetEvent, ProcessErrorHandle As AutoResetEvent
''' <summary>
''' 若程序正在运行,则结束程序进程。
''' 即使失败也不会抛出异常。
''' </summary>
Public Shared Sub ProcessStop()
Try
'关闭未捕获的进程
For Each ProcessObject In Process.GetProcesses
If ProcessObject.Id = ProcessCore?.Id Then Continue For
If ProcessObject.ProcessName <> "联机模块" AndAlso ProcessObject.ProcessName <> "联机模块 CLI" Then Continue For
Try
Log("[Link] 停止残留的联机模块,PID:" & ProcessObject.Id & ",进程名:" & ProcessObject.ProcessName)
ProcessObject.Kill()
ProcessObject.Close()
Catch exx As Exception
Log(exx, $"结束进程失败({ProcessObject.ProcessName},PID {ProcessObject.Id})")
End Try
Next
'关闭由自身启动的进程
If ProcessCore IsNot Nothing AndAlso Not ProcessCore.HasExited Then
Log("[Link] 停止所启动的联机模块,PID:" & ProcessCore.Id)
ProcessCore.Kill()
ProcessCore.Close()
ProcessCore = Nothing
End If
'释放资源
ProcessOutputHandle?.Dispose()
ProcessErrorHandle?.Dispose()
Catch ex As Exception
Log(ex, "停止联机模块失败")
End Try
End Sub
''' <summary>
''' 出现意外错误,给出错误信息并结束联机。
''' </summary>
Private Sub Panic(Brief As String, Detail As String)
If State = States.Loading Then
FailReason = Brief
LinkLoader.Failed(New Exception(Detail))
Else
MyMsgBox(Detail, "联机出错:" & Brief, IsWarn:=True)
ChangeState(States.Waiting)
Telemetry("联机失败", "Exception", FilterUserName(Brief & ":" & Detail, "*"))
End If
'收集到的 CLI 错误:
'Error: Timeout : deadline has elapsed
'Caused by
' deadline has elapsed
'Error: failed to get peer manager client
'Caused by
' 0: rust tun error failed to connect to server: Url {scheme: "tcp", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("127.0.0.1")), port: Some(15888), path: "", query: None, fragment: None }
' 1: failed to connect to server: Url {scheme: "tcp", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("127.0.0.1")), port: Some(15888), path: "", query: None, fragment: None }
' 2: IO error
' 3: 由于目标计算机积极拒绝, 无法连接。 (os error 10061)
End Sub
''' <summary>
''' 启动进程。
''' </summary>
Private Sub ProcessStart(Arguments As String)
Dim Info = New ProcessStartInfo With {
.FileName = PathEasyTier & "联机模块.exe",
.Arguments = Arguments,
.UseShellExecute = False,
.CreateNoWindow = True,
.RedirectStandardError = True,
.RedirectStandardOutput = True,
.StandardOutputEncoding = Encoding.UTF8,
.StandardErrorEncoding = Encoding.UTF8
}
Log("[Link] 正在启动 EasyTier:" & Arguments)
ProcessCore = New Process With {.StartInfo = Info}
Dim LogLineHandler =
Function(sender As Object, e As DataReceivedEventArgs, Handle As AutoResetEvent)
Try
If e.Data Is Nothing Then
Handle.[Set]()
Return Nothing
End If
ProcessLogLine(e.Data)
Catch unused As ObjectDisposedException
Catch ex As Exception
Log(ex, "读取 EasyTier 信息失败")
End Try
Return Nothing
End Function
ProcessOutputHandle = New AutoResetEvent(False)
AddHandler ProcessCore.OutputDataReceived, Function(sender, e) LogLineHandler(sender, e, ProcessOutputHandle)
ProcessErrorHandle = New AutoResetEvent(False)
AddHandler ProcessCore.ErrorDataReceived, Function(sender, e) LogLineHandler(sender, e, ProcessErrorHandle)
ProcessCore.Start()
ProcessCore.BeginOutputReadLine()
ProcessCore.BeginErrorReadLine()
End Sub
Private Sub ProcessLogLine(Line As String)
'记录日志
Log("[EasyTier] " & Line)
LogHistory.Enqueue(Line)
If LogHistory.Count >= 10 Then LogHistory.Dequeue()
'检查日志内容
If Line.ContainsF("new peer connection added", True) Then
Log("[Link] 已建立连接:" & If(RegexSeek(Line, "(?<=remote_addr.+?"")[^""}]{3,}"), Line), LogLevel.Debug)
Update()
ElseIf Line.ContainsF("peer connection removed", True) Then
Log("[Link] 已断开连接:" & If(RegexSeek(Line, "(?<=remote_addr.+?"")[^""}]{3,}"), Line), LogLevel.Debug)
Update()
End If
End Sub
Private LogHistory As New Queue(Of String)(10)
#End Region
#Region "CLI 节点信息获取"
Private Class Peer
''' <summary>
''' 该节点的类型。
''' </summary>
Public ReadOnly Type As Types
Public Enum Types
''' <summary>
''' 客户端。
''' </summary>
Client
''' <summary>
''' 服务器。
''' </summary>
Server
''' <summary>
''' 公共节点。
''' </summary>
Misc
''' <summary>
''' 自己,或者自己的重复(它可能返回多个自己)。
''' </summary>
Self
End Enum
''' <summary>
''' 与该节点的延迟 (ms)。
''' 若是自己则为 0。
''' </summary>
Public ReadOnly Ping As Double
''' <summary>
''' 名称。
''' </summary>
Public ReadOnly Name As String
''' <summary>
''' 从 CLI 给出的信息行分析对应的数据。
''' </summary>
Public Sub New(Info As String)
'具体每个部分的索引见 GetPeerData()
Dim Parts = Info.Split("|").Select(Function(s) If(s.All(Function(c) c = " "c), s, s.Trim)).Where(Function(s) s <> "").ToList
If Parts.Count <> 10 Then Throw New Exception($"信息列数有误")
'延迟
Ping = If(Parts(3) = "-", 0, Parts(3))
'类别
Dim PeerName = Parts(1)
If PeerName = HostName Then
Type = Types.Self
ElseIf PeerName.StartsWithF("Client") Then
Type = Types.Client
ElseIf Parts(0).StartsWithF("10.114.114.114") Then '只允许这个 IP 作为房主,避免网络被多个房主搞炸的情况
Type = Types.Server
Else
Type = Types.Misc
End If
'主机名
Name = PeerName
End Sub
Public Overrides Function ToString() As String
Return $"{Type} - {Name} - Ping {Ping:0.0}ms"
End Function
End Class
''' <summary>
''' 当前的节点列表,使用 RefreshPeerLoader 来刷新。
''' 若尚未成功获取过则为 Nothing,但保证在加载完成后至少是一个列表。
''' </summary>
Private Peers As List(Of Peer) = Nothing
''' <summary>
''' 调用 EasyTier CLI 获取已连接节点信息。
''' 这会过滤掉尚未完成连接的其他节点以及自己。
''' 若在加载完成后连续获取失败 3 次,则会强制断开连接。
''' </summary>
Private RefreshPeerLoader As New LoaderTask(Of Integer, List(Of Peer))("EasyTier CLI", AddressOf RefreshPeer)
Private Sub RefreshPeer()
'| ipv4 | hostname | cost | lat(ms) | loss | rx | tx | tunnel | NAT | version |
'|-------------------|-----------------------|----------|---------|------|---------|---------|--------|----------------|----------------|
'| 10.114.114.1/24 | Client-RJ458A | Local | - | - | - | - | - | Unknown | 2.4.5-4c4d172e |
'| | PublicServer_公用服务器  | p2p | 48.40 | 0.0% | 875 B | 1.26 kB | tcp | NoPat | 2.4.5-4c4d172e |
'| 10.114.114.114/24 | Server-J6P6IW | p2p | 5.63 | 0.0% | 1.65 kB | 1.64 kB | udp | PortRestricted | 2.4.5-4c4d172e |
'| 10.114.114.114/24 | Server-J6PHIW (连接中) | relay(2) | 1000.00 | 0.0% | 0 B | 0 B | | PortRestricted | 2.4.5-4c4d172e |
Try
Dim CliResult = StartProcessAndGetOutput(PathEasyTier & "联机模块 CLI.exe", "peer", 2000, Encoding:=Encoding.UTF8, PrintLog:=False)
'解析
If Not CliResult.Contains("lat(ms)") Then Throw New Exception("CLI 调用失败:" & vbCrLf & CliResult)
If GetUuid() Mod If(ModeDebug, 4, 30) = 0 Then Log("[EasyTier] CLI 输出抽样:" & vbCrLf & CliResult)
Dim NewPeers As New List(Of Peer)
For Each Line In CliResult.Split(vbCrLf.ToCharArray, StringSplitOptions.RemoveEmptyEntries).Skip(2)
Try
Dim Peer = New Peer(Line)
If Peer.Type = Peer.Types.Self Then Continue For '自己
NewPeers.Add(Peer)
Catch exx As Exception
Log(exx, $"错误的信息行({Line})")
End Try
Next
'完成
Peers = NewPeers
FailCount = 0
Catch ex As Exception
Log(ex, "获取节点信息失败")
If State = States.Finished Then
FailCount += 1
If FailCount >= 4 Then Panic("获取节点信息失败", ex.Message)
End If
End Try
End Sub
''' <summary>
''' CLI 调用连续失败的次数。
''' </summary>
Private FailCount As Integer = 0
''' <summary>
''' 根据当前的节点列表获取 Ping 值。
''' 服务端会返回所有节点中最低的 Ping,客户端会返回服务端的 Ping。
''' 0 代表尚不可用。
''' </summary>
Private Function GetPeerPing() As Double
If Peers Is Nothing Then Return 0
Dim PingTargets As IEnumerable(Of Peer)
If IsServerSide Then
PingTargets = Peers
Else
PingTargets = Peers.Where(Function(p) p.Type = Peer.Types.Server)
End If
If Not PingTargets.Any Then
Return 0
Else
Return Math.Max(1, PingTargets.Select(Function(p) p.Ping).Min())
End If
End Function
''' <summary>
''' 获取当前房间中的人数,包括自己。
''' 0 代表不可用。
''' </summary>
Private ReadOnly Property PeopleCount As Integer
Get
If Peers Is Nothing Then Return 0
Return Peers.Where(Function(p) p.Type <> Peer.Types.Misc).Count() + 1 '加上自己
End Get
End Property
#End Region
#Region "定时状态更新"
'启动
Private IsTimerStarted As Boolean = False
Private Sub StartTimerThread() Handles Me.Loaded
If IsTimerStarted Then Return
RunInNewThread(
Sub()
Dim Counter As Integer = 0
Do While True
Try
Thread.Sleep(200)
Counter += 200
'每 4 秒执行一次
If Counter >= 4000 Then
Counter = 0
Update()
End If
'每 200ms 更新进度条
If State = States.Loading Then RunInUi(AddressOf UpdateProgressBar)
Catch ex As Exception
Log(ex, "联机模块主时钟出错", LogLevel.Feedback)
Thread.Sleep(10000)
End Try
Loop
End Sub, "Link Timer")
End Sub
'每 4 秒或进入页面时更新一次信息
Private Sub Update() Handles Me.Loaded
If State <> States.Finished Then Return
'重新获取信息
SyncLock RefreshPeerLoader.LockState
If RefreshPeerLoader.State <> LoadState.Loading Then RefreshPeerLoader.Start(IsForceRestart:=True)
End SyncLock
'更新 Ping 与人数显示
If FrmMain.PageCurrent = FormMain.PageType.Link Then
RunInUi(
Sub()
Dim Ping As Double = GetPeerPing()
If Ping Mod 500 = 0 OrElse $"{Ping:0.0}" = "1.0" OrElse FailCount > 0 Then
LabFinishPing.Text = "连接中"
AniStop("Link Logo Rotation")
Else
LabFinishPing.Text = If(Ping >= 10, $"{Ping:0} ms", $"{Ping:0.0} ms")
AniStart(AaRotateTransform(ImgFinishLogo, 500, 5000), "Link Logo Rotation") 'Logo 旋转动画
End If
LabFinishPlayer.Text = PeopleCount & "  人"
End Sub)
End If
'检查核心状态
CheckCrash()
'检查节点状态
If Not Peers.Any Then
Panic("网络连接已断开", "请检查你的网络环境是否正常。")
Return
End If
If Not IsServerSide AndAlso Not Peers.Any(Function(p) p.Type = Peer.Types.Server) Then
MyMsgBox("房主已离开房间!", "联机结束")
ChangeState(States.Waiting)
Return
End If
End Sub
''' <summary>
''' 检查联机模块是否崩溃。
''' </summary>
Private Sub CheckCrash()
If ProcessCore IsNot Nothing AndAlso Not ProcessCore.HasExited Then Return
Panic("联机模块已崩溃", "近期日志:" & vbCrLf & LogHistory.Join(vbCrLf))
End Sub
#End Region
End Class