рдПрдордПрд╕ рдСрд░рд▓рд┐рдпрдиреНрд╕ рдкрд░ рдЧреЗрдо рд╕рд░реНрд╡рд░ - рднрд╛рдЧ 3: рд╕рд╛рд░рд╛рдВрд╢



рдирдорд╕реНрдХрд╛рд░, рд╣реЗрдмреНрд░! рдореИрдВ рдПрдордПрд╕ рдСрд░рд▓рд┐рдпрдиреНрд╕ рдХрд╛ рдЕрдзреНрдпрдпрди рдХрд░рдирд╛ рдЬрд╛рд░реА рд░рдЦрддрд╛ рд╣реВрдВ рдФрд░ рдСрд░рд▓рд┐рдпрдиреНрд╕ рдЕрдирд╛рдЬ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдХрдВрд╕реЛрд▓ рдХреНрд▓рд╛рдЗрдВрдЯ рдФрд░ рд╕рд░реНрд╡рд░ рдХреЗ рд╕рд╛рде рдПрдХ рд╕рд░рд▓ рдСрдирд▓рд╛рдЗрди рдЧреЗрдо рдмрдирд╛рддрд╛ рд╣реВрдВред рдЗрд╕ рдмрд╛рд░ рдореИрдВ рдЖрдкрдХреЛ рдмрддрд╛рдКрдВрдЧрд╛ рдХрд┐ рдпрд╣ рд╕рдм рдХреИрд╕реЗ рд╕рдорд╛рдкреНрдд рд╣реБрдЖ рдФрд░ рдореИрдВрдиреЗ рдЕрдкрдиреЗ рд▓рд┐рдП рдХреНрдпрд╛ рдирд┐рд╖реНрдХрд░реНрд╖ рдирд┐рдХрд╛рд▓реЗред рд╡рд┐рд╡рд░рдг рдХреЗ рд▓рд┐рдП, рдмрд┐рд▓реНрд▓реА рдореЗрдВ рдЖрдкрдХрд╛ рд╕реНрд╡рд╛рдЧрдд рд╣реИред

рдЗрд╕рд▓рд┐рдП, рдпрджрд┐ рдЖрдк рдЗрд╕ рдмрд╛рдд рдореЗрдВ рд░реБрдЪрд┐ рд░рдЦрддреЗ рд╣реИрдВ рдХрд┐ рдбрд╛рдпрдиреЗрдорд┐рдХ рдЧреЗрдореНрд╕ рдХреЗ рд▓рд┐рдП рдЧреЗрдо рд╕рд░реНрд╡рд░ рдХреИрд╕реЗ рдмрдиреЗ рд╣реИрдВ, рдФрд░ рдПрдордПрд╕ рдСрд░рд▓рд┐рдпрдиреНрд╕ рдХреЗ рд╕рд╛рде рдореЗрд░рд╛ рдкреНрд░рдпреЛрдЧ рдирд╣реАрдВ рд╣реИ, рддреЛ рдореЗрд░рд╛ рд╕реБрдЭрд╛рд╡ рд╣реИ рдХрд┐ рдЖрдк рдЗрд╕ рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА (рдпреВрдбреАрдкреА) рдХреЛ рджреЗрдЦреЗрдВ рдФрд░ рдЗрди рд▓реЗрдЦреЛрдВ рдХреЛ рдкрдврд╝реЗрдВ:

  1. habr.com/en/post/303006
  2. habr.com/en/post/328118
  3. habr.com/en/company/pixonic/blog/499642
  4. habr.com/en/company/pixonic/blog/420019

рд╕рд╛рдордЧреНрд░реА



рд╕реЛрд░реНрд╕ рдХреЛрдб


MsOrleansOnlineGame

рдЦреЗрд▓ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ


рдкрд░рд┐рдгрд╛рдо рдПрдХ рд╕рд╛рдзрд╛рд░рдг рд╢реВрдЯрд░ рдерд╛ред рд╣рд░рд╛ # рд╡рд┐рд░реЛрдзреА рд╣реИрдВред рдкреАрд▓рд╛ # рдЖрдкрдХрд╛ рдЪрд░рд┐рддреНрд░ рд╣реИред рд▓рд╛рд▓ $ рдПрдХ рдЧреЛрд▓реА рд╣реИред рд╢реВрдЯрд┐рдВрдЧ рдЙрд╕ рджрд┐рд╢рд╛ рдореЗрдВ рд╣реИ рдЬрд╣рд╛рдВ рдЖрдк рдЬрд╛ рд░рд╣реЗ рд╣реИрдВред рдЖрдВрджреЛрд▓рди рдХреА рджрд┐рд╢рд╛ WASD рдмрдЯрди рдпрд╛ рддреАрд░ рджреНрд╡рд╛рд░рд╛ рдирд┐рдпрдВрддреНрд░рд┐рдд рдХреА рдЬрд╛рддреА рд╣реИред рд╕реНрдкреЗрд╕ рдмрд╛рд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рд╢реЙрдЯ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдореБрдЭреЗ рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЛрдб рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╡рд┐рд╕реНрддрд╛рд░ рд╕реЗ рдмрддрд╛рдиреЗ рдХреА рдмрд╛рдд рдирд╣реАрдВ рджрд┐рдЦрддреА рдХреНрдпреЛрдВрдХрд┐ рдЗрд╕реЗ рд╕рд╛рдорд╛рдиреНрдп рд╕реЗ рдмрджрд▓рдиреЗ рдХреА рдЬрд░реВрд░рдд рд╣реИред рдЧреНрд░рд╛рдлрд┐рдХред

рдЕрднрд┐рдиреЗрддрд╛рдУрдВ (рдЕрдирд╛рдЬ) рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ


рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ: рдореЗрд░рд╛ IMHO рдХрд┐ рдСрд░рд▓рд┐рдпрдиреНрд╕ Azure, рд╕реНрдХреЗрд▓рд┐рдВрдЧ рдФрд░ рдПрдХ рд╕реНрдорд╛рд░рдХ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рддрд╣рдд рд╕рдореНрдорд╛рдирд┐рдд рд╕реНрдЯреЗрд░реЙрдпрдб рдкрд░ рдПрдХ gRPC рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП рдХреИрд╢ рдХреЗ рд╕рд╛рдеред рд╣рд╛рд▓рд╛рдВрдХрд┐ рдПрдХ рд░рд╛рдЬреНрдп рдХреЗ рдмрд┐рдирд╛, рдПрдХ рдирд┐рдпрдорд┐рдд рдЖрд░рдкреАрд╕реА рдХреА рддрд░рд╣, рд╡рд╣ рд╕реНрдЯреЗрдЯрд▓реЗрд╕ рд╡рд░реНрдХрд░ рдЧреНрд░реЗрди рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдХрд╛рдо рдХрд░рдирд╛ рдЬрд╛рдирддрд╛ рд╣реИред рдСрд░рд▓рд┐рдпрдиреНрд╕ рдореЗрдВ рдЕрдирд╛рдЬ (рдЕрднрд┐рдиреЗрддрд╛) Asp.Net рдореЗрдВ рдПрдХ рдирд┐рдпрдВрддреНрд░рдХ рдХреЗ рд░реВрдк рдореЗрдВ рдПрдХ рдкреНрд░рд╡реЗрд╢ рдмрд┐рдВрджреБ рдХреЗ рд░реВрдк рдореЗрдВ рдХрд╛рд░реНрдп рдХрд░ рд╕рдХрддрд╛ рд╣реИред рд▓реЗрдХрд┐рди рдирд┐рдпрдВрддреНрд░рдХ рдХреЗ рд╡рд┐рдкрд░реАрдд, рдЕрдирд╛рдЬ рдореЗрдВ рдПрдХ рдПрдХрд▓ рдЙрджрд╛рд╣рд░рдг рд╣реИ рдЬрд┐рд╕рдХрд╛ рдЕрдкрдирд╛ рдкрд╣рдЪрд╛рдирдХрд░реНрддрд╛ рд╣реИред рдЕрдирд╛рдЬ рддрдм рдЕрдЪреНрдЫреЗ рд╣реЛрддреЗ рд╣реИрдВ рдЬрдм рдЖрдкрдХреЛ рдХреБрдЫ рдереНрд░реЗрдбреНрд╕ рд╕реЗ рдпрд╛ рдХрдИ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рд╕реЗ рдПрдХ рд╣реА рд╕рдордп рдореЗрдВ рдХреБрдЫ рд╕реНрдерд┐рддрд┐ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рд╡реЗ рдЗрд╕рдХреЗ рд╕рд╛рде рдзрд╛рдЧрд╛ рд╕реБрд░рдХреНрд╖рд┐рдд рд╕рдВрдЪрд╛рд▓рди рдкреНрд░рджрд╛рди рдХрд░рддреЗ рд╣реИрдВред

рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдпрд╣рд╛рдВ рд╕рд╛рдорд╛рди рдХреА рдЯреЛрдХрд░реА рдХреЗ рд▓рд┐рдП рдПрдХ рдЕрднрд┐рдиреЗрддрд╛ рд╣реИред рдкрд╣рд▓реА рдХреЙрд▓ рдореЗрдВ, рдЗрд╕реЗ рдмрдирд╛рдпрд╛ рдЬрд╛рдПрдЧрд╛ рдФрд░ рдореЗрдореЛрд░реА рдореЗрдВ рдХреИрд╢ рдХреА рднреВрдорд┐рдХрд╛ рдирд┐рднрд╛рддреЗ рд╣реБрдП рд▓рдЯрдХрд╛ рджрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рдПрдХ рд╣реА рд╕рдордп рдореЗрдВ, рд╣рдЬрд╛рд░реЛрдВ рд╡рд┐рднрд┐рдиреНрди рд╕реНрдЯреНрд░реАрдо рдХреЗ рд╣рдЬрд╛рд░реЛрдВ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдПрдХ рд╕рд╛рде рдЖрдЗрдЯрдо рдЬреЛрдбрд╝рдиреЗ рдФрд░ рд╣рдЯрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдиреБрд░реЛрдз рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЙрд╕рдХреЗ рдЕрдВрджрд░ рдЙрд╕рдХреА рд╕реНрдерд┐рддрд┐ рдХреЗ рд╕рд╛рде рд╕рднреА рдХрд╛рдо рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╕реБрд░рдХреНрд╖рд┐рдд рд╣реЛрдВрдЧреЗред рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ, рд╕рд┐рд╕реНрдЯрдо рдореЗрдВ рдЙрдкрд▓рдмреНрдз рд╕рднреА рдЯреЛрдХрд░рд┐рдпреЛрдВ рдХреА рд╕реВрдЪреА рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд▓рд┐рд╕реНрдЯ рдЧреЗрдЯрдмрд╕реНрдХреЗрдЯреНрд╕ () рд╡рд┐рдзрд┐ рдХреЗ рд╕рд╛рде рдПрдХ рдЕрднрд┐рдиреЗрддрд╛ рдХреА рджреБрдХрд╛рди рдмрдирд╛рдирд╛ рдЙрдкрдпреЛрдЧреА рд╣реЛрдЧрд╛ред рдЗрд╕реА рд╕рдордп, рджреБрдХрд╛рди рднреА рдХреИрд╢ рдХреЗ рд░реВрдк рдореЗрдВ рдореЗрдореЛрд░реА рдореЗрдВ рд▓рдЯрдХрд╛рдПрдЧреА рдФрд░ рдЗрд╕рдХреЗ рд╕рд╛рде рд╕рднреА рдХрд╛рд░реНрдп рдереНрд░реЗрдб рд╕реБрд░рдХреНрд╖рд┐рдд рд╣реЛрдВрдЧреЗред

    public interface IBasket : IGrainWithGuidKey
    {
        Task Add(string item);
        Task Remove(string item);
        Task<List<string>> GetItems();
    }

    public class BasketGrain : Grain, IBasket
    {
        private readonly ILogger<BasketGrain> _logger;
        private readonly IPersistentState<List<string>> _store;

        public BasketGrain(
            ILogger<BasketGrain> logger,
            [PersistentState("basket", "shopState")] IPersistentState<List<string>> store
        )
        {
            _logger = logger;
            _store = store;
        }

         public override Task OnActivateAsync()
        {
             var shop = GrainFactory.GetGrain<IShop>();
           //          .
            await shop.AddBasketIfNotContains(this.GetPrimaryKey())
            return base.OnActivateAsync();
        }

        public override async Task OnDeactivateAsync()
        {
          //       
         //    Asp.Net  . 
        //           - .
        //    -      .
        //         .
     //        
            await _store.WriteStateAsync();
            await base.OnDeactivateAsync();
        }


        public Task Add(string item)
        {
            _store.State.Add(item);
            return Task.CompletedTask;
        }

        public Task Remove(string item)
        {
            _store.State.Remove(item);
            return Task.CompletedTask;
        }

        public Task<List<string>> GetItems()
        {
           //      .
          //        
         //           
            return Task.FromResult(new List<string>(_store.State));
        }
    }

рдХреБрдЫ рдХрдВрд╕реЛрд▓ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг:

         private static async Task DoClientWork(IClusterClient client, Guid baskeId)
        {
            var basket = client.GetGrain<IBasket>(baskeId);
           //   gRPC -                
            await basket.Add("Apple");
        }

рдЦреЗрд▓ рдХреЛрдб



рд╡рд╣ рдирдХреНрд╢рд╛ рдЬрд┐рд╕ рдкрд░ рдЦрд┐рд▓рд╛рдбрд╝реА рд▓рдбрд╝рддреЗ рд╣реИрдВ:

   public interface IFrame : IGrainWithIntegerKey
    {
        Task Update(Frame frame);
        Task<Frame> GetState();
    }

    public class FrameGrain : Grain, IFrame
    {
        private readonly ILogger<FrameGrain> _logger;
        private readonly IPersistentState<Frame> _store;

        public FrameGrain(
            ILogger<FrameGrain> logger,
            [PersistentState("frame", "gameState")] IPersistentState<Frame> store
        )
        {
            _logger = logger;
            _store = store;
        }

        public override Task OnActivateAsync()
        {
            _logger.LogInformation("ACTIVATED");
           //    1  1      .
            _store.State.GameId = this.GetPrimaryKeyLong();
            return base.OnActivateAsync();
        }

        public override async Task OnDeactivateAsync()
        {
            _logger.LogInformation("DEACTIVATED");
            await _store.WriteStateAsync();
            await base.OnDeactivateAsync();
        }

        public Task Update(Frame frame)
        {
            _store.State = frame;
            return Task.CompletedTask;
        }

        public Task<Frame> GetState() => Task.FromResult(_store.State.Clone());
    }

рдЦреЗрд▓ рдХрд╛ рджрд╛рдирд╛ рдЬреЛ рд╡рд░реНрддрдорд╛рди рдЦреЗрд▓ рдХреА рд╕рд╛рдорд╛рдиреНрдп рд╕реНрдерд┐рддрд┐ рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рддрд╛ рд╣реИ рдФрд░ рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЛ рд╕рд┐рдЧреНрдирд▓рдЖрд░ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдб 20 рдмрд╛рд░ рднреЗрдЬрддрд╛ рд╣реИред

    public interface IGame : IGrainWithIntegerKey
    {
        Task Update(Player player);
        Task Update(Bullet bullet);
        Task<List<Player>> GetAlivePlayers();
    }

    public class GameGrain : Grain, IGame
    {
        private const byte WIDTH = 100;
        private const byte HEIGHT = 50;
        private readonly ILogger<GameGrain> _logger;
        private readonly IPersistentState<Game> _store;
        private readonly IHubContext<GameHub> _hub;
        private IDisposable _timer;
        public GameGrain(
            ILogger<GameGrain> logger,
            [PersistentState("game", "gameState")] IPersistentState<Game> store,
            IHubContext<GameHub> hub
            )
        {
            _logger = logger;
            _store = store;
            _hub = hub;
        }

        public override async Task OnActivateAsync()
        {
            _store.State.Id = this.GetPrimaryKeyLong();
            _store.State.Frame = new Frame(WIDTH, HEIGHT) { GameId = _store.State.Id };
            var frame = GrainFactory.GetGrain<IFrame>(_store.State.Id);
            await frame.Update(_store.State.Frame.Clone());
            _logger.LogWarning("ACTIVATED");
            //      50      .       .
            _timer = RegisterTimer(Draw, null, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(50));
            await base.OnActivateAsync();
        }

        public override async Task OnDeactivateAsync()
        {
            _logger.LogWarning("DEACTIVATED");
            _timer?.Dispose();
            _timer = null;
            await _store.WriteStateAsync();
            await base.OnDeactivateAsync();
        }

        public async Task Draw(object obj)
        {
            var state = _store.State;
            state.Bullets.RemoveAll(b => !b.IsAlive);
            state.Players.RemoveAll(p => !p.IsAlive);
            try
            {
                await _hub.Clients.All.SendAsync("gameUpdated", state.Clone());
            }
            catch (Exception e)
            {
                _logger.LogError(e, "Error on send s");
            }
        }

        public Task Update(Player player)
        {
            _store.State.Players.RemoveAll(x => x.Id == player.Id);
            _store.State.Players.Add(player);
            return Task.CompletedTask;
        }
        public Task Update(Bullet bullet)
        {
            _store.State.Bullets.RemoveAll(x => x.Id == bullet.Id);
            _store.State.Bullets.Add(bullet);
            return Task.CompletedTask;
        }

        public Task<List<Player>> GetAlivePlayers() =>
            Task.FromResult(_store.State.Players.Where(p => p.IsAlive).Select(p => p.Clone()).ToList());
    }

рд╕рд┐рдЧреНрдирд▓рдЖрд░ рд╡рд╣ рд╣рдм рд╣реИ рдЬрд┐рд╕рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╣рдо рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЗ рд╕рд╛рде рд╕рдВрд╡рд╛рдж рдХрд░рддреЗ рд╣реИрдВред рдпрд╣ WebGl рдХреНрд▓рд╛рдЗрдВрдЯ рдФрд░ рдСрд░рд▓рд┐рдпрдиреНрд╕ рдХреЗ рдмреАрдЪ рдПрдХ рдкреНрд░реЙрдХреНрд╕реА рдХреЗ рд░реВрдк рдореЗрдВ рдХрд╛рд░реНрдп рдХрд░рддрд╛ рд╣реИред рдЕрдм рддрдХ, рдХреНрд▓рд╛рдЗрдВрдЯ рдХрдВрд╕реЛрд▓ рд╣реИ рдФрд░ рд╡рд╣ рдмреЗрддрд╣рд╛рд╢рд╛ рдЧреВрдВрдЧрд╛ рд╣реИред рдореИрдВ рднрд╡рд┐рд╖реНрдп рдореЗрдВ рдПрдХ рд╡реЗрдм рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЛ рддреАрди.рдЬреЗрдПрд╕ рдкрд░ рдПрдХ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ рдмрдирд╛рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдФрд░ рдЗрд╕рд▓рд┐рдП рдореБрдЭреЗ рд╕рд┐рдЧреНрдирд▓рдЖрд░ рд╡реЗрдм рд╕реЙрдХреЗрдЯ рдкрд░ рдХрдиреЗрдХреНрд╢рди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдСрд░рд▓рд┐рдпрдиреНрд╕ рдХреНрд▓рд╛рдЗрдВрдЯ рд╕реНрд╡рдпрдВ рдЬреАрдЖрд░рдкреАрд╕реА рдХреЗ рд╡рд┐рдкрд░реАрдд, рд╕реА # рдореЗрдВ рд╣реА рд╣реИ, рдЬреЛ рдХрдИ рднрд╛рд╖рд╛рдУрдВ рдореЗрдВ рдЙрдкрд▓рдмреНрдз рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╡реЗрдм рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ рдСрд░рд▓рд┐рдпрдиреНрд╕ рд╕рд░реНрд╡рд░ рдФрд░ рдХреНрд▓рд╛рдЗрдВрдЯреНрд╕ (рдЧреЗрдЯрд╡реЗ asp.net рдХреЛрд░) рдХреЗ рдмреАрдЪ рдПрдХ рдкреНрд░реЙрдХреНрд╕реА рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред

    public class GameHub : Hub
    {
        private readonly IGrainFactory _client;

        public GameHub(IGrainFactory client)
        {
            _client = client;
        }

        public async Task GameInput(Input input)
        {
            var player = _client.GetGrain<IPlayer>(input.PlayerId);
            await player.Handle(input);
        }
    }

рдЕрдиреНрди рдХрд╛ рдЦрд┐рд▓рд╛рдбрд╝реАред рдпрд╣ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рдЯрд╛рдЗрдорд░ рдХреЗ рдЕрдиреБрд╕рд╛рд░ рдЪрд▓рддрд╛ рд╣реИ рдФрд░ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХрдорд╛рдВрдб рдХрд╛ рдЬрд╡рд╛рдм рджреЗрддрд╛ рд╣реИред рдпрджрд┐ рдХреЛрдИ рд╢реВрдЯ рдХрдорд╛рдВрдб рдЖрддрд╛ рд╣реИ, рддреЛ рд╡рд╣ рдПрдХ рдмреБрд▓реЗрдЯ рдЧреНрд░реЗрди рдмрдирд╛рддрд╛ рд╣реИ рдФрд░ рдЙрд╕рдХреЗ рд▓рд┐рдП рдЖрдВрджреЛрд▓рди рдХреА рджрд┐рд╢рд╛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рддрд╛ рд╣реИред

    public class PlayerGrain : Grain, IPlayer
    {
        private readonly ILogger<PlayerGrain> _logger;
        private readonly IPersistentState<Player> _store;
        private IDisposable _timer;
        private readonly Queue<Input> _inputs;
        public PlayerGrain(
            ILogger<PlayerGrain> logger,
            [PersistentState("player", "gameState")] IPersistentState<Player> store
        )
        {
            _logger = logger;
            _store = store;
            _inputs = new Queue<Input>();
        }

        public override Task OnActivateAsync()
        {
            _logger.LogInformation("ACTIVATED");
            // State   POCO     . Entity Player   
            _store.State.Id = this.GetPrimaryKey();
            _timer = RegisterTimer(Update, null, TimeSpan.FromMilliseconds(1), TimeSpan.FromMilliseconds(200));
            return base.OnActivateAsync();
        }

        public override async Task OnDeactivateAsync()
        {
            _logger.LogInformation("ACTIVATED");
            _timer?.Dispose();
            _timer = null;
            await _store.WriteStateAsync();
            await base.OnDeactivateAsync();
        }

        public async Task Handle(Input input)
        {
            _store.State.GameId = input.GameId;
            _inputs.Enqueue(input);
        }

        public async Task Update(object obj)
        {
            if (!_store.State.IsAlive)
            {
                await _store.ClearStateAsync();
               //          .
             //       .       .
                DeactivateOnIdle();
                return;
            }

            while (_inputs.Count > 0)
            {
                var input = _inputs.Dequeue();
                foreach (var direction in input.Directions.Where(d => d != Direction.None))
                {
                    _store.State.Direction = direction;
                }

                foreach (var command in input.Commands.Where(c => c != Command.None))
                {
                    if (command == Command.Shoot)
                    {
                        var bulletId = Guid.NewGuid();
                        var bullet = GrainFactory.GetGrain<IBullet>(bulletId);
                        //  Shot()           .
                        bullet.Update(_store.State.Shot()).Ignore(); //Ignore()        
                    }
                }
            }
            _store.State.Move();
            if (_store.State.GameId.HasValue)
            {
                var frame = GrainFactory.GetGrain<IFrame>(_store.State.GameId.Value);
                var fs = await frame.GetState();
                if (fs.Collide(_store.State))
                    _store.State.MoveBack();
                GrainFactory.GetGrain<IGame>(_store.State.GameId.Value)
                    .Update(_store.State.Clone())
                    .Ignore();
            }
        }

        public async Task Die()
        {
            _store.State.IsAlive = false;
            if (_store.State.GameId.HasValue)
                await GrainFactory.GetGrain<IGame>(_store.State.GameId.Value).Update(_store.State.Clone());
            await _store.ClearStateAsync();
            DeactivateOnIdle();
        }
    }


рдЕрдирд╛рдЬ рдХреА рдЧреЛрд▓рд┐рдпрд╛рдВред рдпрд╣ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рдПрдХ рдЯрд╛рдЗрдорд░ рдкрд░ рдЪрд▓рддрд╛ рд╣реИ рдФрд░, рдЕрдЧрд░ рдпрд╣ рдПрдХ рдЦрд┐рд▓рд╛рдбрд╝реА рд╕реЗ рд╕рд╛рдордирд╛ рдХрд░рддрд╛ рд╣реИ, рддреЛ рдЗрд╕реЗ рдорд░рдиреЗ рдХрд╛ рдЖрджреЗрд╢ рджреЗрддрд╛ рд╣реИред рдпрджрд┐ рд╡рд╣ рдирдХреНрд╢реЗ рдореЗрдВ рдПрдХ рдмрд╛рдзрд╛ рдХрд╛ рд╕рд╛рдордирд╛ рдХрд░рддреА рд╣реИ, рддреЛ рд╡рд╣ рдЦреБрдж рдорд░ рдЬрд╛рддреА рд╣реИред
  public interface IBullet : IGrainWithGuidKey
    {
        Task Update(Bullet dto);
    }

    public class BulletGrain : Grain, IBullet
    {
        private readonly ILogger<BulletGrain> _logger;
        private readonly IPersistentState<Bullet> _store;
        private IDisposable _timer;
        public BulletGrain(
            ILogger<BulletGrain> logger,
            [PersistentState("bullet", "gameState")] IPersistentState<Bullet> store
        )
        {
            _logger = logger;
            _store = store;
        }

        public Task Update(Bullet dto)
        {
            _store.State = dto;
            _store.State.Id = this.GetPrimaryKey();
            return Task.CompletedTask;
        }

        public override Task OnActivateAsync()
        {
            _logger.LogInformation("ACTIVATED");
            _timer = this.RegisterTimer(Update, null, TimeSpan.FromMilliseconds(1), TimeSpan.FromMilliseconds(50));
            return base.OnActivateAsync();
        }

        public override async Task OnDeactivateAsync()
        {
            _logger.LogInformation("DEACTIVATED");
            _timer?.Dispose();
            _timer = null;
            await _store.WriteStateAsync();
            await base.OnDeactivateAsync();
        }

        public async Task Update(object obj)
        {
            if (!_store.State.IsAlive)
            {
                await _store.ClearStateAsync();
                DeactivateOnIdle();
                return;
            }
            _store.State.Move();
            if (_store.State.GameId.HasValue)
            {
                var frame = GrainFactory.GetGrain<IFrame>(_store.State.GameId.Value);
                var fs = await frame.GetState();
                if (fs.Collide(_store.State))
                    _store.State.IsAlive = false;
                if (_store.State.Point.X > fs.Width || _store.State.Point.Y > fs.Height)
                    _store.State.IsAlive = false;
                var game = GrainFactory.GetGrain<IGame>(_store.State.GameId.Value);
                var players = await game.GetAlivePlayers();
                foreach (var player in players)
                {
                    if (player.Collide(_store.State))
                    {
                        _store.State.IsAlive = false;
                        GrainFactory.GetGrain<IPlayer>(player.Id).Die().Ignore();
                        break;
                    }
                }
                game.Update(_store.State.Clone()).Ignore();
            }
        }
    }

All Articles