Check-in [6d1e1922b6]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:docks view
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:6d1e1922b63a259e46b6746ab30b9034ce102931
User & Date: linnaea 2016-10-27 13:01:47
Context
2016-10-27
13:32
downgrade to .NET FX 4.5.2 check-in: 9f7c2c746c user: linnaea tags: trunk
13:01
docks view check-in: 6d1e1922b6 user: linnaea tags: trunk
2016-10-24
12:37
update equipment list when new ship is added check-in: afaf915d7c user: linnaea tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to Linnaea.Network.Protocols/Http/Http1Session.cs.

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
..
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
                    } else {
                        client = value.Destination; server = value.Source;
                    }
                }

                if(Equals(client, value.Source)) {
                    qRequests.Enqueue(value.Data);
                    OnRequest(value.Data);
                } else if(Equals(server, value.Source)) {
                    res = value.Data;
                    if(qRequests.Count > 0) {
                        req = qRequests.Dequeue();
                        res.MaxBufferedBody = req.MaxBufferedBody;
                    }
                } else {
................................................................................
                if(res.MaxBufferedBody <= 0) {
                    Emit(new Http1Session(req, res, server, client));
                } else {
                    res.Subscribe(_ => { }, () => Emit(new Http1Session(req, res, server, client)));
                }
            }
        }

        protected virtual void OnRequest(HttpMessage msg)
        {
            onRequest?.Invoke(msg);
        }
    }
    
    public class Http1Session
    {
        public HttpMessage Request { get; }
        public HttpMessage Response { get; }
        public EndPoint Client { get; }







|







 







<
<
<
<
<







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
..
46
47
48
49
50
51
52





53
54
55
56
57
58
59
                    } else {
                        client = value.Destination; server = value.Source;
                    }
                }

                if(Equals(client, value.Source)) {
                    qRequests.Enqueue(value.Data);
                    onRequest?.Invoke(value.Data);
                } else if(Equals(server, value.Source)) {
                    res = value.Data;
                    if(qRequests.Count > 0) {
                        req = qRequests.Dequeue();
                        res.MaxBufferedBody = req.MaxBufferedBody;
                    }
                } else {
................................................................................
                if(res.MaxBufferedBody <= 0) {
                    Emit(new Http1Session(req, res, server, client));
                } else {
                    res.Subscribe(_ => { }, () => Emit(new Http1Session(req, res, server, client)));
                }
            }
        }





    }
    
    public class Http1Session
    {
        public HttpMessage Request { get; }
        public HttpMessage Response { get; }
        public EndPoint Client { get; }

Changes to WarshipGirlsRSniffer/App.xaml.cs.

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

            captureSource = new Linnaea.Network.Captures.Sources.TcpServer(5000);
            var frames = captureSource.Select(x => x.ParsePcap()).FirstActive();

            //var captureSource = new Linnaea.Network.Captures.Sources.TcpClient("127.0.0.1", 49000);
            //var frames = captureSource.ParsePcap();

            var sessions = new IObservable<Linnaea.Network.CaptureFrame>[] {
                frames,
                frames.ParseEthernet().ToCaptureFrame()
            }.AllActive(true)
                .ParseIp().ParseTcpPacket().ToTcpSessions()
                .Select(
                    x => x.ParseHttp1Messages()
                          .ParseHttp1Sessions(msg => msg.MaxBufferedBody = msg.Host?.EndsWith(".jr.moefantasy.com") == true ? 1 << 19 : 0)







|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

            captureSource = new Linnaea.Network.Captures.Sources.TcpServer(5000);
            var frames = captureSource.Select(x => x.ParsePcap()).FirstActive();

            //var captureSource = new Linnaea.Network.Captures.Sources.TcpClient("127.0.0.1", 49000);
            //var frames = captureSource.ParsePcap();

            var sessions = new[] {
                frames,
                frames.ParseEthernet().ToCaptureFrame()
            }.AllActive(true)
                .ParseIp().ParseTcpPacket().ToTcpSessions()
                .Select(
                    x => x.ParseHttp1Messages()
                          .ParseHttp1Sessions(msg => msg.MaxBufferedBody = msg.Host?.EndsWith(".jr.moefantasy.com") == true ? 1 << 19 : 0)

Changes to WarshipGirlsRSniffer/View/Controls/ShipStatus.xaml.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<UserControl x:Class="WarshipGirlsRSniffer.View.Controls.ShipStatus"
             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:WarshipGirlsRSniffer.View.Controls"
             xmlns:vm="clr-namespace:WarshipGirlsRSniffer.Vm"
             mc:Ignorable="d" d:DesignWidth="300" d:DataContext="{d:DesignInstance vm:ShipVm}">
    <TextBlock TextAlignment="Center" Padding="9,3">
        <TextBlock.Style>
            <Style TargetType="TextBlock">
                <Setter Property="Text" Value="----" />
                <Setter Property="Background" Value="LightGray" />

                <Style.Triggers>







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<UserControl x:Class="WarshipGirlsRSniffer.View.Controls.ShipStatus"
             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:WarshipGirlsRSniffer.View.Controls"
             xmlns:vm="clr-namespace:WarshipGirlsRSniffer.Vm"
             mc:Ignorable="d" d:DesignWidth="60" d:DataContext="{d:DesignInstance vm:ShipVm}">
    <TextBlock TextAlignment="Center" Padding="9,3">
        <TextBlock.Style>
            <Style TargetType="TextBlock">
                <Setter Property="Text" Value="----" />
                <Setter Property="Background" Value="LightGray" />

                <Style.Triggers>

Added WarshipGirlsRSniffer/View/Converter/BuildShipTypeToDescriptionConverter.cs.























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System;
using System.Globalization;
using System.Windows.Data;

namespace WarshipGirlsRSniffer.View.Converter
{
    public class BuildShipTypeToDescriptionConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if(!(value is Vo.BuildShipType)) {
                return null;
            }

            switch((Vo.BuildShipType)value) {
                case Vo.BuildShipType.Cruiser: return "巡洋舰";
                case Vo.BuildShipType.Destroyer: return "驱逐舰";
                default:return (byte)(Vo.BuildShipType)value + "类";
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

Added WarshipGirlsRSniffer/View/Docks.xaml.































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<UserControl x:Class="WarshipGirlsRSniffer.View.Docks"
             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:WarshipGirlsRSniffer.View"
             xmlns:vm="clr-namespace:WarshipGirlsRSniffer.Vm"
             mc:Ignorable="d" d:DataContext="{d:DesignInstance vm:VmDock}"
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <DataTemplate x:Key="dockTemplate">
            <Border Padding="5" Margin="0,0,0,5" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>

                    <TextBlock Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Text="{Binding Id}" VerticalAlignment="Center" Margin="10,0" FontSize="16" />
                    <TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding Title}" Margin="0,4" FontSize="14" />
                    <TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding RemainingTime, StringFormat={}{0:hh':'mm':'ss}}" Margin="0,4" />
                </Grid>
                <Border.Style>
                    <Style TargetType="Border">
                        <Setter Property="Visibility" Value="Visible" />
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Locked}" Value="True">
                                <Setter Property="Visibility" Value="Collapsed" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Border.Style>
            </Border>
        </DataTemplate>
        <ItemsPanelTemplate x:Key="stackTemplate">
            <StackPanel Orientation="Vertical" />
        </ItemsPanelTemplate>
    </UserControl.Resources>
    
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <Border Grid.Column="0" BorderThickness="0,0,1,1" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"><TextBlock Text="修理渠" TextAlignment="Center" FontSize="14" Padding="2" /></Border>
        <Border Grid.Column="1" BorderThickness="0,0,1,1" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"><TextBlock Text="建造渠" TextAlignment="Center" FontSize="14" Padding="2" /></Border>
        <Border Grid.Column="2" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"><TextBlock Text="装备渠" TextAlignment="Center" FontSize="14" Padding="2" /></Border>

        <ItemsControl BorderThickness="0,0,1,1" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}" Grid.Row="1" Grid.Column="0" Padding="5,0" ItemTemplate="{StaticResource dockTemplate}" ItemsPanel="{StaticResource stackTemplate}" ItemsSource="{Binding RepairDock}" />
        <ItemsControl BorderThickness="0,0,1,1" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}" Grid.Row="1" Grid.Column="1" Padding="5,0" ItemTemplate="{StaticResource dockTemplate}" ItemsPanel="{StaticResource stackTemplate}" ItemsSource="{Binding BuildDock}" />
        <ItemsControl BorderThickness="0,0,0,1" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}" Grid.Row="1" Grid.Column="2" Padding="5,0" ItemTemplate="{StaticResource dockTemplate}" ItemsPanel="{StaticResource stackTemplate}" ItemsSource="{Binding EquipmentDock}" />
    </Grid>
</UserControl>

Added WarshipGirlsRSniffer/View/Docks.xaml.cs.

























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WarshipGirlsRSniffer.View
{
    /// <summary>
    /// Docks.xaml 的交互逻辑
    /// </summary>
    public partial class Docks : UserControl
    {
        public Docks()
        {
            InitializeComponent();
        }
    }
}

Changes to WarshipGirlsRSniffer/View/MainWindow.xaml.

40
41
42
43
44
45
46

47
48
49
50

        <control:VerticalTabView Grid.Row="1" DataContext="{Binding CurrentSession, Mode=OneWay}">
            <x:Array Type="FrameworkElement">
                <local:Fleets control:TabView.TabName="舰队" DataContext="{Binding Yard.Fleets}" />
                <local:Fleets control:TabView.TabName="战役舰队" DataContext="{Binding Yard.CampaignFleets}" />
                <!--<local:Ships control:TabView.TabName="舰船" DataContext="{Binding Yard.Ships}" />-->
                <local:Equipments control:TabView.TabName="装备" DataContext="{Binding Yard.Equipments}" />

            </x:Array>
        </control:VerticalTabView>
    </Grid>
</Window>







>




40
41
42
43
44
45
46
47
48
49
50
51

        <control:VerticalTabView Grid.Row="1" DataContext="{Binding CurrentSession, Mode=OneWay}">
            <x:Array Type="FrameworkElement">
                <local:Fleets control:TabView.TabName="舰队" DataContext="{Binding Yard.Fleets}" />
                <local:Fleets control:TabView.TabName="战役舰队" DataContext="{Binding Yard.CampaignFleets}" />
                <!--<local:Ships control:TabView.TabName="舰船" DataContext="{Binding Yard.Ships}" />-->
                <local:Equipments control:TabView.TabName="装备" DataContext="{Binding Yard.Equipments}" />
                <local:Docks control:TabView.TabName="船渠" DataContext="{Binding Dock}" />
            </x:Array>
        </control:VerticalTabView>
    </Grid>
</Window>

Changes to WarshipGirlsRSniffer/Vm/BuildDockVm.cs.



1
2
3
4
5
6


7
8

9
10
11
12

















13
14
15
16
17
18

















19
20
21


using WarshipGirlsRSniffer.Vo;

namespace WarshipGirlsRSniffer.Vm
{
    public class BuildDockVm : VmBase<BuildDockVm>
    {


        private BuildDock vo;
        private readonly VmDock dock;


        public BuildDock Vo {
            get { return vo; }
            set { vo = value; RaiseMultiPropertyChanged(); }

















        }

        public BuildDockVm(BuildDock vo, VmDock dock)
        {
            this.vo = vo;
            this.dock = dock;

















        }
    }
}
>
>






>
>


>



|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using System;
using System.Threading;
using WarshipGirlsRSniffer.Vo;

namespace WarshipGirlsRSniffer.Vm
{
    public class BuildDockVm : VmBase<BuildDockVm>
    {
        private static readonly View.Converter.BuildShipTypeToDescriptionConverter cvt = new View.Converter.BuildShipTypeToDescriptionConverter();

        private BuildDock vo;
        private readonly VmDock dock;
        private readonly Timer timer;

        public BuildDock Vo {
            get { return vo; }
            set { vo = value; RaiseMultiPropertyChanged(); UpdateTimer(this); }
        }

        private DateTimeOffset? _endTime;
        public DateTimeOffset? EndTime => _endTime;
        public byte Id => vo.id;
        public bool Locked => vo.locked != 0;
        public BuildShipType ShipType => (BuildShipType)vo.shipType;
        public string Title => cvt.Convert(ShipType, typeof(string), null, null) as string;
        public TimeSpan? RemainingTime {
            get
            {
                if(_endTime == null) {
                    return null;
                }
                var delta = _endTime.Value - DateTimeOffset.Now;
                return delta.Ticks < 0 ? TimeSpan.Zero : delta;
            }
        }

        public BuildDockVm(BuildDock vo, VmDock dock)
        {
            this.vo = vo;
            this.dock = dock;

            timer = new Timer(UpdateTimer, null, -1, -1);
            UpdateTimer(this);
        }

        private void UpdateTimer(object _)
        {
            RaisePropertyChanged(nameof(RemainingTime));
            if(_ != null) {
                _endTime = vo.endTime == 0 ? (DateTimeOffset?)null : DateTimeOffset.FromUnixTimeSeconds(vo.endTime);
                if(RemainingTime.HasValue && RemainingTime != TimeSpan.Zero) {
                    timer.Change(1000, 1000);
                }
            }
            if(!RemainingTime.HasValue || RemainingTime == TimeSpan.Zero) {
                timer.Change(-1, -1);
            }
        }
    }
}

Changes to WarshipGirlsRSniffer/Vm/EquipmentDockVm.cs.



1
2
3
4
5
6
7
8

9
10
11
12

















13
14
15
16
17
18

















19
20
21


using WarshipGirlsRSniffer.Vo;

namespace WarshipGirlsRSniffer.Vm
{
    public class EquipmentDockVm : VmBase<RepairDockVm>
    {
        private EquipmentDock vo;
        private readonly VmDock dock;


        public EquipmentDock Vo {
            get { return vo; }
            set { vo = value; RaiseMultiPropertyChanged(); }

















        }

        public EquipmentDockVm(EquipmentDock vo, VmDock dock)
        {
            this.vo = vo;
            this.dock = dock;

















        }
    }
}
>
>








>



|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using System;
using System.Threading;
using WarshipGirlsRSniffer.Vo;

namespace WarshipGirlsRSniffer.Vm
{
    public class EquipmentDockVm : VmBase<RepairDockVm>
    {
        private EquipmentDock vo;
        private readonly VmDock dock;
        private readonly Timer timer;

        public EquipmentDock Vo {
            get { return vo; }
            set { vo = value; RaiseMultiPropertyChanged(); UpdateTimer(this); }
        }

        private DateTimeOffset? _endTime;
        public DateTimeOffset? EndTime => _endTime;
        public byte Id => vo.id;
        public bool Locked => vo.locked != 0;
        public string Title => Equipment.title;
        public ShipEquipment Equipment => Client.WGRSession.InitTxt.ShipEquipment[vo.equipmentCid];
        public TimeSpan? RemainingTime {
            get
            {
                if(_endTime == null) {
                    return null;
                }
                var delta = _endTime.Value - DateTimeOffset.Now;
                return delta.Ticks < 0 ? TimeSpan.Zero : delta;
            }
        }

        public EquipmentDockVm(EquipmentDock vo, VmDock dock)
        {
            this.vo = vo;
            this.dock = dock;

            timer = new Timer(UpdateTimer, null, -1, -1);
            UpdateTimer(this);
        }

        private void UpdateTimer(object _)
        {
            RaisePropertyChanged(nameof(RemainingTime));
            if(_ != null) {
                _endTime = vo.endTime == 0 ? (DateTimeOffset?)null : DateTimeOffset.FromUnixTimeSeconds(vo.endTime);
                if(RemainingTime.HasValue && RemainingTime != TimeSpan.Zero) {
                    timer.Change(1000, 1000);
                }
            }
            if(!RemainingTime.HasValue || RemainingTime == TimeSpan.Zero) {
                timer.Change(-1, -1);
            }
        }
    }
}

Changes to WarshipGirlsRSniffer/Vm/RepairDockVm.cs.



1
2
3
4
5
6
7
8

9
10
11
12

















13
14
15
16
17
18

















19
20
21


using WarshipGirlsRSniffer.Vo;

namespace WarshipGirlsRSniffer.Vm
{
    public class RepairDockVm : VmBase<RepairDockVm>
    {
        private RepairDock vo;
        private readonly VmDock dock;


        public RepairDock Vo {
            get { return vo; }
            set { vo = value; RaiseMultiPropertyChanged(); }

















        }

        public RepairDockVm(RepairDock vo, VmDock dock)
        {
            this.vo = vo;
            this.dock = dock;

















        }
    }
}
>
>








>



|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using System;
using System.Threading;
using WarshipGirlsRSniffer.Vo;

namespace WarshipGirlsRSniffer.Vm
{
    public class RepairDockVm : VmBase<RepairDockVm>
    {
        private RepairDock vo;
        private readonly VmDock dock;
        private readonly Timer timer;

        public RepairDock Vo {
            get { return vo; }
            set { vo = value; RaiseMultiPropertyChanged(); UpdateTimer(this); }
        }

        private DateTimeOffset? _endTime;
        public DateTimeOffset? EndTime => _endTime;
        public byte Id => vo.id;
        public bool Locked => vo.locked != 0;
        public string Title => Ship?.Name;
        public ShipVm Ship => dock.Session.Yard.Ships[vo.shipId];
        public TimeSpan? RemainingTime {
            get
            {
                if(_endTime == null) {
                    return null;
                }
                var delta = _endTime.Value - DateTimeOffset.Now;
                return delta.Ticks < 0 ? TimeSpan.Zero : delta;
            }
        }

        public RepairDockVm(RepairDock vo, VmDock dock)
        {
            this.vo = vo;
            this.dock = dock;

            timer = new Timer(UpdateTimer, null, -1, -1);
            UpdateTimer(this);
        }

        private void UpdateTimer(object _)
        {
            RaisePropertyChanged(nameof(RemainingTime));
            if(_ != null) {
                _endTime = vo.endTime == 0 ? (DateTimeOffset?)null : DateTimeOffset.FromUnixTimeSeconds(vo.endTime);
                if(RemainingTime.HasValue && RemainingTime != TimeSpan.Zero) {
                    timer.Change(1000, 1000);
                }
            }
            if(!RemainingTime.HasValue || RemainingTime == TimeSpan.Zero) {
                timer.Change(-1, -1);
            }
        }
    }
}

Changes to WarshipGirlsRSniffer/Vm/VmBase.cs.

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
..
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;

namespace WarshipGirlsRSniffer.Vm
{
    [Serializable]
    public abstract class VmBase<T> : INotifyPropertyChanged
        where T : VmBase<T>
    {
        private static IReadOnlyDictionary<string, IReadOnlyCollection<string>> _propertyChangePath = null;
        private static Lazy<string[]> _allProperties;
        
        protected virtual IReadOnlyDictionary<string, IReadOnlyCollection<string>> PropertyDependency => null;

        [field: NonSerialized]
................................................................................
            return handle;
        }

        [OnDeserialized]
        private void Deserialized(StreamingContext context)
        {
            if(_allProperties == null) {
                System.Threading.Interlocked.CompareExchange(ref _allProperties, new Lazy<string[]>(() => {
                    return GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(info => info.Name).ToArray();
                }), null);
            }

            if(_propertyChangePath != null) return;
            var pdNames = PropertyDependency;
            if(pdNames == null) {
                return;
            }

            var _cp = new Dictionary<string, HashSet<string>>();
            var closed = new HashSet<string>();
            var open = new LinkedList<string>();
            foreach(var dpPair in PropertyDependency) {                    //Resolve all depencency of one particular property
                if(dpPair.Key == null) continue;

                closed.Clear(); open.Clear();
                open.AddLast(dpPair.Key);
................................................................................
                    if(!closed.Add(visit)) continue;
                    if(!PropertyDependency.ContainsKey(visit)) continue;
                    foreach(var dp in PropertyDependency[visit]) {
                        if(dp != null) open.AddLast(dp);
                    }
                }
                foreach(var source in closed) {                             //Flip the dependency list to form the lookup table
                    if(!_cp.ContainsKey(source)) {
                        _cp[source] = new HashSet<string>();
                        _cp[source].Add(source);
                    }
                    _cp[source].Add(dpPair.Key);
                }
            }

            System.Threading.Interlocked.CompareExchange(ref _propertyChangePath, _cp.ToDictionary(x => x.Key, x => x.Value.ToArray() as IReadOnlyCollection<string>), null);
        }

        protected void RaisePropertyChanged([CallerMemberName] string property = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if(handler == null) return;
            lock(_suppressNotification)







|







 







|
|
|








|







 







<
<
|
<
<



|







6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
..
62
63
64
65
66
67
68


69


70
71
72
73
74
75
76
77
78
79
80
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;

namespace WarshipGirlsRSniffer.Vm
{
    [Serializable]
    public abstract class VmBase<T> : INotifyPropertyChanged
        //where T : VmBase<T>
    {
        private static IReadOnlyDictionary<string, IReadOnlyCollection<string>> _propertyChangePath = null;
        private static Lazy<string[]> _allProperties;
        
        protected virtual IReadOnlyDictionary<string, IReadOnlyCollection<string>> PropertyDependency => null;

        [field: NonSerialized]
................................................................................
            return handle;
        }

        [OnDeserialized]
        private void Deserialized(StreamingContext context)
        {
            if(_allProperties == null) {
                System.Threading.Interlocked.CompareExchange(ref _allProperties, new Lazy<string[]>(() =>
                    GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(info => info.Name).ToArray()
                ), null);
            }

            if(_propertyChangePath != null) return;
            var pdNames = PropertyDependency;
            if(pdNames == null) {
                return;
            }

            var _cp = MultiValueDictionary<string, string>.Create<HashSet<string>>();
            var closed = new HashSet<string>();
            var open = new LinkedList<string>();
            foreach(var dpPair in PropertyDependency) {                    //Resolve all depencency of one particular property
                if(dpPair.Key == null) continue;

                closed.Clear(); open.Clear();
                open.AddLast(dpPair.Key);
................................................................................
                    if(!closed.Add(visit)) continue;
                    if(!PropertyDependency.ContainsKey(visit)) continue;
                    foreach(var dp in PropertyDependency[visit]) {
                        if(dp != null) open.AddLast(dp);
                    }
                }
                foreach(var source in closed) {                             //Flip the dependency list to form the lookup table


                    _cp.Add(source, dpPair.Key);


                }
            }

            System.Threading.Interlocked.CompareExchange(ref _propertyChangePath, _cp, null);
        }

        protected void RaisePropertyChanged([CallerMemberName] string property = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if(handler == null) return;
            lock(_suppressNotification)

Changes to WarshipGirlsRSniffer/Vm/VmDock.cs.

11
12
13
14
15
16
17

18
19
20


21
22
23
24
25
26
27
..
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
    public class VmDock
    {
        private CompositeDisposable _disposable = new CompositeDisposable();

        public DefaultTable<BuildDockVm> BuildDock { get; } = new DefaultTable<BuildDockVm>();
        public DefaultTable<RepairDockVm> RepairDock { get; } = new DefaultTable<RepairDockVm>();
        public DefaultTable<EquipmentDockVm> EquipmentDock { get; } = new DefaultTable<EquipmentDockVm>();


        public VmDock(VmSession _)
        {


            _disposable.Add(_.Session.Api_Api_InitGame.Subscribe(OnGameInit));

            _disposable.Add(_.Session.Api_Boat_Repair.Subscribe(p => p.Item2.repairDockVo.ForEach(UpdateDock)));
            _disposable.Add(_.Session.Api_Boat_RepairComplete.Subscribe(p => p.Item2.repairDockVo.ForEach(UpdateDock)));
            _disposable.Add(_.Session.Api_Boat_InstantRepair.Subscribe(p => p.Item2.repairDockVo.ForEach(UpdateDock)));
            _disposable.Add(_.Session.Api_Dock_BuildBoat.Subscribe(p => p.Item2.dockVo.ForEach(UpdateDock)));
            _disposable.Add(_.Session.Api_Dock_BuildEquipment.Subscribe(p => p.Item2.equipmentDockVo.ForEach(UpdateDock)));
................................................................................
            _disposable.Add(_.Session.Api_Dock_GetEquipment.Subscribe(p => p.Item2.equipmentDockVo.ForEach(UpdateDock)));
            _disposable.Add(_.Session.Api_Dock_InstantBuild.Subscribe(p => p.Item2.dockVo.ForEach(UpdateDock)));
            _disposable.Add(_.Session.Api_Dock_InstantEquipmentBuild.Subscribe(p => p.Item2.equipmentDockVo.ForEach(UpdateDock)));
        }

        private void OnGameInit(Tuple<Protocol.Request.Api.InitGame, Protocol.Response.Api.InitGame> p)
        {
            App.BeginDispatcherInvoke(() => {
                BuildDock.Clear();
                RepairDock.Clear();
                EquipmentDock.Clear();

                p.Item2.dockVo.ForEach(x => BuildDock[x.id] = new BuildDockVm(x, this));
                p.Item2.repairDockVo.ForEach(x => RepairDock[x.id] = new RepairDockVm(x, this));
                p.Item2.equipmentDockVo.ForEach(x => EquipmentDock[x.id] = new EquipmentDockVm(x, this));
            });
        }

        private void UpdateDock(Vo.BuildDock vo)
        {
            var dock = BuildDock[vo.id];
            if(dock != null) {
                dock.Vo = vo;







>



>
>







 







<
<
<
<
<
|
|
|
<







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
..
32
33
34
35
36
37
38





39
40
41

42
43
44
45
46
47
48
    public class VmDock
    {
        private CompositeDisposable _disposable = new CompositeDisposable();

        public DefaultTable<BuildDockVm> BuildDock { get; } = new DefaultTable<BuildDockVm>();
        public DefaultTable<RepairDockVm> RepairDock { get; } = new DefaultTable<RepairDockVm>();
        public DefaultTable<EquipmentDockVm> EquipmentDock { get; } = new DefaultTable<EquipmentDockVm>();
        public readonly VmSession Session;

        public VmDock(VmSession _)
        {
            Session = _;

            _disposable.Add(_.Session.Api_Api_InitGame.Subscribe(OnGameInit));

            _disposable.Add(_.Session.Api_Boat_Repair.Subscribe(p => p.Item2.repairDockVo.ForEach(UpdateDock)));
            _disposable.Add(_.Session.Api_Boat_RepairComplete.Subscribe(p => p.Item2.repairDockVo.ForEach(UpdateDock)));
            _disposable.Add(_.Session.Api_Boat_InstantRepair.Subscribe(p => p.Item2.repairDockVo.ForEach(UpdateDock)));
            _disposable.Add(_.Session.Api_Dock_BuildBoat.Subscribe(p => p.Item2.dockVo.ForEach(UpdateDock)));
            _disposable.Add(_.Session.Api_Dock_BuildEquipment.Subscribe(p => p.Item2.equipmentDockVo.ForEach(UpdateDock)));
................................................................................
            _disposable.Add(_.Session.Api_Dock_GetEquipment.Subscribe(p => p.Item2.equipmentDockVo.ForEach(UpdateDock)));
            _disposable.Add(_.Session.Api_Dock_InstantBuild.Subscribe(p => p.Item2.dockVo.ForEach(UpdateDock)));
            _disposable.Add(_.Session.Api_Dock_InstantEquipmentBuild.Subscribe(p => p.Item2.equipmentDockVo.ForEach(UpdateDock)));
        }

        private void OnGameInit(Tuple<Protocol.Request.Api.InitGame, Protocol.Response.Api.InitGame> p)
        {





            p.Item2.dockVo.ForEach(UpdateDock);
            p.Item2.repairDockVo.ForEach(UpdateDock);
            p.Item2.equipmentDockVo.ForEach(UpdateDock);

        }

        private void UpdateDock(Vo.BuildDock vo)
        {
            var dock = BuildDock[vo.id];
            if(dock != null) {
                dock.Vo = vo;

Changes to WarshipGirlsRSniffer/Vo/Constants.cs.

28
29
30
31
32
33
34
35







        SS = 14,
        SM = 15,
        AP = 16,
        Fortress = 17,
        Airfield = 18,
        Port = 19
    }
}














|
>
>
>
>
>
>
>
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
        SS = 14,
        SM = 15,
        AP = 16,
        Fortress = 17,
        Airfield = 18,
        Port = 19
    }

    public enum BuildShipType : byte
    {
        Unknown = 0,
        Destroyer = 1,
        Cruiser = 2
    }
}

Changes to WarshipGirlsRSniffer/WarshipGirlsRSniffer.csproj.

206
207
208
209
210
211
212

213



214
215
216
217
218
219
220
...
254
255
256
257
258
259
260




261
262
263
264
265
266
267
      <DependentUpon>ProgressBar.xaml</DependentUpon>
    </Compile>
    <Compile Include="View\Controls\ShipStatus.xaml.cs">
      <DependentUpon>ShipStatus.xaml</DependentUpon>
    </Compile>
    <Compile Include="View\Converter\ShipResourceToFillBrushConverter.cs" />
    <Compile Include="View\Converter\HpToFillBrushConverter.cs" />

    <Compile Include="View\Converter\ShipTypeToDescriptionConverter.cs" />



    <Compile Include="View\Equipments.xaml.cs">
      <DependentUpon>Equipments.xaml</DependentUpon>
    </Compile>
    <Compile Include="View\Fleets.xaml.cs">
      <DependentUpon>Fleets.xaml</DependentUpon>
    </Compile>
    <Compile Include="View\Ships.xaml.cs">
................................................................................
      <SubType>Designer</SubType>
    </Page>
    <Page Include="View\Controls\ProgressBar.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="View\Controls\ShipStatus.xaml">




      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="View\Equipments.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>







>

>
>
>







 







>
>
>
>







206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
...
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
      <DependentUpon>ProgressBar.xaml</DependentUpon>
    </Compile>
    <Compile Include="View\Controls\ShipStatus.xaml.cs">
      <DependentUpon>ShipStatus.xaml</DependentUpon>
    </Compile>
    <Compile Include="View\Converter\ShipResourceToFillBrushConverter.cs" />
    <Compile Include="View\Converter\HpToFillBrushConverter.cs" />
    <Compile Include="View\Converter\BuildShipTypeToDescriptionConverter.cs" />
    <Compile Include="View\Converter\ShipTypeToDescriptionConverter.cs" />
    <Compile Include="View\Docks.xaml.cs">
      <DependentUpon>Docks.xaml</DependentUpon>
    </Compile>
    <Compile Include="View\Equipments.xaml.cs">
      <DependentUpon>Equipments.xaml</DependentUpon>
    </Compile>
    <Compile Include="View\Fleets.xaml.cs">
      <DependentUpon>Fleets.xaml</DependentUpon>
    </Compile>
    <Compile Include="View\Ships.xaml.cs">
................................................................................
      <SubType>Designer</SubType>
    </Page>
    <Page Include="View\Controls\ProgressBar.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="View\Controls\ShipStatus.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="View\Docks.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="View\Equipments.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>