制作其他玩家可以看到的卡片集合

时间:2014-02-13 19:30:22

标签: c# wpf multithreading xaml visual-studio-2013

我的想法是用一台服务器和至少两台客户端实现纸牌游戏。现在我卡在玩家手中(一个可见,一个面朝下),一堆牌组(包含32张牌)和一堆抛出的牌中显示牌,所以两个客户都可以看到它们。每当玩家拿出一张卡片/卡片或将它们拿走时,UI也应该更新。还有一个系统操作窗口,告诉玩家到目前为止已经做了什么动作,但它也没有在两个客户端上更新。

由于我是c#编程的新手,我真的需要一些建议,欢迎任何链接!我已经阅读了一些关于INotifyPropertyChanged和INotifyCollectionChanged的文章,但我似乎无法理解它们,我尝试了很多方法并希望自己弄明白。这是我到目前为止编码的内容:

客户机侧

namespace CardGame.Client
    {
    public sealed partial class gamePage : Page, IPlayerCallback
    {

    ObservableCollection<Card> Hand = new ObservableCollection<Card>();
    ObservableCollection<Card> throwedCards = new ObservableCollection<Card>();
    ObservableCollection<Card> Deck = new ObservableCollection<Card>();
    ObservableCollection<Card> enemyDeck = new ObservableCollection<Card>();
    int switchcase = 1;

    public gamePage()
    {
        login();
        throwable.ItemsSource = throwedCards;
        lstCards.ItemsSource = Hand;
        timerDisplay();

    }


    //OK!
    private async void login()
    {
        await myClient.anmeldenAsync(loginPage.username);
    }

    //OK!
    private async void draw_Tapped(object sender, TappedRoutedEventArgs e)
    {
        if (Deck.Count != 0)
        {
            if (await myClient.drawCardAsync() == true)
            {
                Hand.Add(await myClient.theDrawingCardAsync());
            }

        }
        switchcase = 1;
        redrawTable();
        showMessage();
    }

    private async void throw_Tapped(object sender, DoubleTappedRoutedEventArgs e)
    {
        foreach (Card eachItem in lstCards.SelectedItems)
        {
            await myClient.throwingCardAsync(eachItem);

            if (await myClient.canThrowAsync() == true)
            {
                Hand.Remove(eachItem);
                await myClient.throwTheCardAsync(eachItem);

                switchcase = 2;
                redrawTable();
                showMessage();

            }
        }
    }
    private async void showMessage()
    {
        SystemMessage.Text = await myClient.getMessageAsync();
    }

    private void timerDisplay()
    {
        DispatcherTimer countdown = new DispatcherTimer();
        countdown.Tick += countdown_tick;
        countdown.Interval = new TimeSpan(0, 0, 1);
        countdown.Start();
    }

    public async void countdown_tick(object sender, object e)
    {
        int timer = await myClient.RemainingSecondsAsync();
        Time.Text = timer.ToString();
        redrawTable();
        showMessage();

        if (timer < 6)
        {
            Time.Foreground = new SolidColorBrush(Colors.Red);
        }

        if (timer > 5)
        {
            Time.Foreground = new SolidColorBrush(Colors.White);
        }
    }

    public async void redrawTable()
    {
        switch (switchcase) {
            case 1:
                Deck = await myClient.leftDeckAsync();
                .....
                break;
            case 2:
                ObservableCollection<Card> thrown = await myClient.thrownCardStackAsync();
                    if (thrown.Count != 0)
                    {
                        throwedCards.Add(await myClient.getTopCardAsync());
                    }
                    ....
                break;  
        }
    }

XAML

<Grid VerticalAlignment="Bottom" Width="1200" HorizontalAlignment="Right">
    <ListView Name="lstCards" HorizontalAlignment="Left" 
    VerticalAlignment="Top" 
    Height="160" 
    RequestedTheme="Dark" BorderBrush="#FFFD0E0E" Background="DarkGreen" Foreground="#FFFF0A0A" 
    BorderThickness="5,5,5,5" ScrollViewer.VerticalScrollBarVisibility="Disabled" 
    ScrollViewer.HorizontalScrollBarVisibility="Auto"  Width="650" Margin="150,0,0,0" >
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </ListView.ItemsPanel>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Canvas VerticalAlignment="Bottom" HorizontalAlignment="Center" 
                    IsTapEnabled="True" AllowDrop="True" MaxWidth="800" 
                     DoubleTapped="throw_Tapped">
                        <Image Source="{Binding Path=Path}" Width="150" Height="150" 
                          MinWidth="150"/>
                    </Canvas>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    <TextBox Name="SystemMessage" HorizontalAlignment="Right" TextWrapping="Wrap" Text="" 
    VerticalAlignment="Bottom" Width="405" Height="160" Background="{x:Null}" Foreground="White" 
    FontSize="15" BorderBrush="#FFFD0E0E" BorderThickness="5" IsReadOnly="True" />
</Grid>

了Serverside

namespace CardGameServer
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,
                 ConcurrencyMode = ConcurrencyMode.Reentrant)]
public sealed class Player : IPlayer
{
.....
    public void login(string name)
    {
        this.Name = name;

        ICallbackContract callback =
        OperationContext.Current.GetCallbackChannel<ICallbackContract>();
        if (!callBackList.Contains(callback))
        {
            callBackList.Add(callback);
        }

        game = GameServer.Instance.login(this);
        systemMessage = "Player " + name + " has logged in" + Environment.NewLine + 
        systemMessage;
    }

    //OK!
    public bool drawCard()
    {
        try
        {
            if (GameServer.isTwoPlayer == true)
            {
                drawedCard = game.Deck.getTopCard();

                deckOfPlayer.Stack.Add(drawedCard);

                systemMessage = "Player " + this.Name.ToString() + " has drawn a card." + 
                Environment.NewLine + systemMessage;

                foreach (ICallbackContract callback in Spieler.callBackList)
                {
                    callback.zeichneTischNeu();
                }
                return true;
            }
        }
        catch (ArgumentNullException)
        {
            throw;
        }
        return false;
    }

    //OK!
    public Card theDrawingCard()
    {
        return drawedCard;
    }

    //OK!
    public void throwingCard(Card throwingTheCard)
    {
        try
        {
            game.stackOfThrownCard.Stack.Add(throwingTheCard);
            systemMessage = "Player " + this.Name.ToString() + " has thrown the card: " 
            + throwingTheCard.Color.ToString() + ", " + throwingTheCard.Value.ToString() + 
            Environment.NewLine + systemMessage;

            foreach (ICallbackContract callback in Spieler.callBackList)
                {
                    callback.zeichneTischNeu();
                }
        }
        catch (Exception)
        {

            throw;
        }
    }


    public interface ICallbackContract
    {
        [OperationContract(IsOneWay = true)]
        void redrawTable();
    }

如果需要更多信息,请告诉我。

更新:现在我在服务器端实现了ICallbackContract,并在客户端上实现了redrawTable()方法。但每当我启动应用程序并尝试绘制卡片时,它会显示错误,显示以下消息:

此操作会死锁,因为在当前Message完成处理之前无法接收回复。如果要允许无序消息处理,请在CallbackBehaviorAttribute上指定Remediate的ConcurrencyMode或Multiple。

但为什么呢?我已经实施了 [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,                      ConcurrencyMode = ConcurrencyMode.Reentrant)] 在Player类中,客户端还有其他事情要做吗?

我读了一些关于代表的内容,但实际上很难理解......

1 个答案:

答案 0 :(得分:0)

在您的服务器端代码中,永远不会调用回调来将数据发送回客户端(假设您使用的是双工WCF通道,它看起来就像您一样)。

你的回调契约应该有一个像“CardDrawn”这样的函数,在代理/客户端,它会以UI更新的方式更改视图模型的属性。这可以通过调用RaisePropertyChanged的直接属性集或通过执行此集的事件来完成。您已经在使用ObservableCollection,因此修改这些集合也应该导致UI更新。

回答更新: 您获得的错误是由于在服务方法中调用回调。您可以更改代码,以便在单独的线程上完成回调调用(允许服务方法完成)。如错误所示,您可以在 CallbackBehaviorAttribute 上指定Reentrant来解决此问题。在您的代码中,它是根据服务行为设置的。

以下是关于可能对您有所帮助的双工服务的MSDN文章,通常研究双工WCF会有所帮助:MSDN