我的想法是用一台服务器和至少两台客户端实现纸牌游戏。现在我卡在玩家手中(一个可见,一个面朝下),一堆牌组(包含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;
}
}
<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>
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类中,客户端还有其他事情要做吗?
我读了一些关于代表的内容,但实际上很难理解......
答案 0 :(得分:0)
在您的服务器端代码中,永远不会调用回调来将数据发送回客户端(假设您使用的是双工WCF通道,它看起来就像您一样)。
你的回调契约应该有一个像“CardDrawn”这样的函数,在代理/客户端,它会以UI更新的方式更改视图模型的属性。这可以通过调用RaisePropertyChanged的直接属性集或通过执行此集的事件来完成。您已经在使用ObservableCollection,因此修改这些集合也应该导致UI更新。
回答更新: 您获得的错误是由于在服务方法中调用回调。您可以更改代码,以便在单独的线程上完成回调调用(允许服务方法完成)。如错误所示,您可以在 CallbackBehaviorAttribute 上指定Reentrant来解决此问题。在您的代码中,它是根据服务行为设置的。
以下是关于可能对您有所帮助的双工服务的MSDN文章,通常研究双工WCF会有所帮助:MSDN