У меня есть 2 надежных актера, которых зовут GameActor
и PlayerActor
. ClientApp
отправляет сообщение на PlayerActor
, когда игрок делает ход. Затем PlayerActor
отправляет сообщение на GameActor
, чтобы указать, что было сделано движение. После вызова метод в GameActor
запускает уведомление. Это уведомление обрабатывается ClientApp GameEventsHandler
. ClientApp
затем вызывает метод на GameActor
для получения последних позиций игрока.
ClientApp -> PlayerActor.MoveTo () -> GameActor.NotifyPlayerMoved () ->
Событие Fire ScoreBoardUpdated
GameEventsHandler, вызванный этим событием ->
GameActor.GetLatestPlayerInfo ()
У меня проблема в следующем. При первом запуске GameEventsHandler
срабатывает и пытается вызвать GameActor
, как и ожидалось. GameActor
получает сообщение и возвращает ожидаемый ответ. Но клиент, похоже, не получает сообщение. Похоже, что он заблокирован, так как он не генерирует ошибки и любые выходные данные Любые последующие уведомления вообще не обрабатываются обработчиком событий.
GameActor
public async Task<IList<PlayerInfo>> GetLatestPlayerInfoAsync(CancellationToken cancellationToken)
{
var allPlayers = await StateManager.GetStateAsync<List<string>>("players", cancellationToken);
var tasks = allPlayers.Select(actorName =>
{
var playerActor = ActorProxy.Create<IPlayerActor>(new ActorId(actorName), new Uri(PlayerActorUri));
return playerActor.GetLatestInfoAsync(cancellationToken);
}).ToList();
await Task.WhenAll(tasks);
return tasks
.Select(t => t.Result)
.ToList();
}
public async Task NotifyPlayerMovedAsync(PlayerInfo lastMovement, CancellationToken cancellationToken)
{
var ev = GetEvent<IGameEvents>();
ev.ScoreboardUpdated(lastMovement);
}
PlayerActor
public async Task MoveToAsync(int x, int y, CancellationToken cancellationToken)
{
var playerName = await StateManager.GetStateAsync<string>("playerName", cancellationToken);
var playerInfo = new PlayerInfo()
{
LastUpdate = DateTimeOffset.Now,
PlayerName = playerName,
XCoordinate = x,
YCoordinate = y
};
await StateManager.AddOrUpdateStateAsync("positions", new List<PlayerInfo>() { playerInfo }, (key, value) =>
{
value.Add(playerInfo);
return value;
}, cancellationToken);
var gameName = await StateManager.GetStateAsync<string>("gameName", cancellationToken);
var gameActor = ActorProxy.Create<IGameActor>(new ActorId(gameName), new Uri(GameActorUri));
await gameActor.NotifyPlayerMovedAsync(playerInfo, cancellationToken);
}
public async Task<PlayerInfo> GetLatestInfoAsync(CancellationToken cancellationToken)
{
var positions = await StateManager.GetStateAsync<List<PlayerInfo>>("positions", cancellationToken);
return positions.Last();
}
Клиент
private static async Task RunDemo(string gameName)
{
var rand = new Random();
Console.WriteLine("Hit return when the service is up...");
Console.ReadLine();
Console.WriteLine("Enter your name:");
var playerName = Console.ReadLine();
Console.WriteLine("This might take a few seconds...");
var gameActor = ActorProxy.Create<IGameActor>(new ActorId(gameName), new Uri(GameActorUri));
await gameActor.SubscribeAsync<IGameEvents>(new GameEventsHandler(gameActor));
var playerActorId = await gameActor.JoinGameAsync(playerName, CancellationToken.None);
var playerActor = ActorProxy.Create<IPlayerActor>(new ActorId(playerActorId), new Uri(PlayerActorUri));
while (true)
{
Console.WriteLine("Press return to move to new location...");
Console.ReadLine();
await playerActor.MoveToAsync(rand.Next(100), rand.Next(100), CancellationToken.None);
}
}
GameEventHandler
public void ScoreboardUpdated(PlayerInfo lastInfo)
{
Console.WriteLine($"Scoreboard updated. (Last move by: {lastInfo.PlayerName})");
var positions = _gameActor.GetLatestPlayerInfoAsync(CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
//this hangs
foreach (var playerInfo in positions) // this line never gits hit
{
Console.WriteLine(
$"Position of {playerInfo.PlayerName} is ({playerInfo.XCoordinate},{playerInfo.YCoordinate})." +
$"\nUpdated at {playerInfo.LastUpdate}\n");
}
}
Но если я оберну логику обработчика событий внутри Task.Run()
, она, похоже, сработает.
Task.Run(async () =>
{
var positions = await _gameActor.GetLatestPlayerInfoAsync(CancellationToken.None);
foreach (var playerInfo in positions)
{
Console.WriteLine(
$"Position of {playerInfo.PlayerName} is ({playerInfo.XCoordinate},{playerInfo.YCoordinate})." +
$"\nUpdated at {playerInfo.LastUpdate}\n");
}
}
);
Полный исходный код для демонстрации здесь https://github.com/dasiths/Service-Fabric-Reliable-Actors-Demo
Уведомления AFAIK не блокируются и не являются надежными. Поэтому я не понимаю, почему моя первоначальная реализация не работает. Шаблон повторного входа здесь не применим согласно моему пониманию. Может кто-нибудь объяснить мне, что здесь происходит? Это ожидаемое поведение или ошибка?