让我们看看。我认为这里的主要问题是设计问题,所以让我们从这个角度出发。我想事先说明,我将仅描述该方法的一个示例:有很多方法可以做到这一点,我正在写出我能想象到的最简单的方法。另外,为了简单起见,处理同步、优雅终止等的代码也被省略了。
首先,你有一个玩家是一个独立的东西,但在你的代码中,它只有在从外部调用它时才会做出反应。当它在实现回合制游戏时看起来像是一种自然的方法时,我们仍然希望进行某种交流。如果球员离开怎么办?如果出现错误情况怎么办?
正如您所说,服务器想要通知玩家游戏结果。似乎我们希望在我们的服务器和播放器之间进行双向消息传递。当然,如果存在 One Server -> Many Players 关系,则另当别论,但我们会保持简单。
让我们准备一些样板代码:
# We will get to this `Start` later
enum EventType <Start Hit Miss>;
# A handy class to hold a position, and likely some other data in the future
class Position {
has Int $.x;
has Int $.y;
}
# A board
class Board {
has @.cell = [ +Bool.pick xx ^10 ] xx ^10;
}
现在这是一个服务器:
class Server {
has Board $!board = Board.new;
has Supply $.shots;
has Channel $.player;
method serve {
react {
# Whenever we get a shot coordinates, sent a Hit or Miss to the player
whenever $!shots -> Position $pos {
$!player.send($!board.cell[$pos.y][$pos.x] ?? Hit !! Miss);
# Don't forget to say "I am ready for new events" for the client
$!player.send(Start);
}
# Somebody should start first, and it will be a Server...
$!player.send(Start);
}
}
}
它有一个板子和两个其他属性 - 一个供应 $.shots 和一个频道 $.player。如果我们想告诉我们的玩家一些事情,我们正在向频道发送消息。同时,我们想知道玩家想让我们知道什么,所以我们正在监听来自 $!shots 异步值流的所有内容。
serve 方法只是执行我们的逻辑 - 对玩家的事件做出反应。
现在我们的播放器:
class Player {
has Channel $.server;
has Supply $.events;
method play {
react {
whenever $!events {
when Start {
# Here can be user's input
# Simulate answer picking
sleep 1;
$!server.send: Position.new(x => (^10).pick, y => (^10).pick);
# Can be something like:
# my ($x, $y) = get.Int, get.Int;
# $!server.send: Position.new(:$x, :$y);
}
when Hit {
say "I hit that! +1 gold coin!";
}
when Miss {
say "No, that's a miss... -1 bullet!"
}
}
}
}
}
Player 也有一个 Channel 和一个 Supply,因为我们想要一个双向关系。 $!server 用于向服务器发送操作,$!events 为我们提供返回的事件流。
play 方法是这样实现的:如果服务器说我们的操作没问题,我们可以采取行动,如果不是 - 我们基本上是在等待,当出现 Hit 或 Miss 事件时,我们会做出反应给它。
现在我们想把这两者联系在一起:
class Game {
has Server $!server;
has Player $!player;
method start {
my $server-to-player = Channel.new;
my $player-to-server = Channel.new;
$!server = Server.new(player => $server-to-player,
shots => $player-to-server.Supply);
$!player = Player.new(server => $player-to-server,
events => $server-to-player.Supply);
start $!server.serve;
sleep 1;
$!player.play;
}
}.new.start;
首先,我们创建了两个具有独立名称的频道。然后我们创建服务器和播放器,将这些通道颠倒过来:播放器可以发送到第一个并收听第二个,服务器可以发送到第二个并收听第一个。
由于react 是一个阻塞结构,我们不能在同一个线程中运行这两种方法,所以我们start 一个服务器在另一个线程中。然后我们休眠 1 秒以确保它为我们服务(这是在这个已经很长的答案中避免协商代码的一种技巧),然后启动播放器(无论是模拟还是真实输入,您都可以尝试两者)。
修改播放器和服务器之间发送的处理程序和数据类型,您可以自己构建更复杂的示例。