{"id":302988,"date":"2020-05-04T15:00:18","date_gmt":"2020-05-04T15:00:18","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=302988"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=302988","title":{"rendered":"\u0418\u0437\u0443\u0447\u0430\u044e Akka.NET: \u0421\u0435\u0440\u0432\u0435\u0440 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u043e\u043d\u043b\u0430\u0439\u043d \u0438\u0433\u0440\u044b"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/500232\/\">\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u0420\u0435\u0448\u0438\u043b \u044f \u0437\u043d\u0430\u0447\u0438\u0442 \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u0442\u044c \u0442\u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u043b \u0441 MS Orleans \u043d\u0430 Akka.NET \u043f\u0440\u043e\u0441\u0442\u043e \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0438 \u044d\u0442\u0443 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044e \u0442\u043e\u0436\u0435. \u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u0447\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u0434\u043e \u0434\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0434 \u043a\u0430\u0442.<br \/>  <a name=\"habracut\"><\/a>  <\/p>\n<h2>\u0418\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438<\/h2>\n<p>  <a href=\"https:\/\/gitlab.com\/VictorWinbringer\/msorleansonlinegame\/-\/tree\/master\/Server\/AkkaActors\" rel=\"nofollow\">gitlab.com\/VictorWinbringer\/msorleansonlinegame\/-\/tree\/master\/Server\/AkkaActors<\/a><\/p>\n<h2>\u041e\u0431 \u0438\u0433\u0440\u0435<\/h2>\n<p>  \u0421\u0442\u0440\u0435\u043b\u044f\u043b\u043a\u0430 \u0441 \u0440\u0435\u0436\u0438\u043c\u043e\u043c \u0434\u0435\u0441 \u043c\u0430\u0442\u0447. \u0412\u0441\u0435 \u043f\u0440\u043e\u0442\u0438\u0432 \u0432\u0441\u0435\u0445. \u0417\u0435\u043b\u0435\u043d\u044b\u0435 # \u044d\u0442\u043e \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u0438\u043a\u0438. \u0416\u0435\u043b\u0442\u044b\u0439 # \u044d\u0442\u043e \u0432\u0430\u0448 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436. \u041a\u0440\u0430\u0441\u043d\u044b\u0439 $ \u044d\u0442\u043e \u043f\u0443\u043b\u044f. \u0421\u0442\u0440\u0435\u043b\u044c\u0431\u0430 \u0432\u0435\u0434\u0435\u0442\u0441\u044f \u0432 \u0442\u043e\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u043a\u0443\u0434\u0430 \u0432\u044b \u0434\u0432\u0438\u0436\u0435\u0442\u0435\u0441\u044c. <\/p>\n<p>  \u041d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f \u0440\u0435\u0433\u0443\u043b\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043a\u043d\u043e\u043f\u043a\u0430\u043c\u0438 W A S D \u0438\u043b\u0438 \u0441\u0442\u0440\u0435\u043b\u043e\u0447\u043a\u0430\u043c\u0438. \u0414\u043b\u044f \u0432\u044b\u0441\u0442\u0440\u0435\u043b\u0430 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0430 \u043a\u043b\u0430\u0432\u0438\u0448\u0430 \u043f\u0440\u043e\u0431\u0435\u043b\u0430. \u0425\u043e\u0447\u0443 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043a\u043b\u0438\u0435\u043d\u0442 \u043d\u0430 Three.js \u0438 \u0432\u044b\u043b\u043e\u0436\u0438\u0442\u044c \u0438\u0433\u0440\u0443 \u043d\u0430 \u043a\u0430\u043a\u043e\u0439 \u043d\u0438\u0431\u0443\u0434\u044c \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433. \u041f\u043e\u043a\u0430 \u0447\u0442\u043e \u0435\u0441\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442. <\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/2l\/n1\/j0\/2ln1j0xw4cm-reechlymcdrx86q.png\"><\/p>\n<h2>\u041b\u0438\u0447\u043d\u044b\u0435 \u0432\u043f\u0435\u0447\u0430\u0442\u043b\u0435\u043d\u0438\u044f<\/h2>\n<p>  \u0412 \u043e\u0431\u0449\u0435\u043c \u0442\u043e \u043e\u043d\u0438 \u043e\u0431\u0430 \u0440\u0435\u0448\u0430\u044e\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u043a\u043e\u0433\u0434\u0430 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0440\u0430\u0441\u043f\u0430\u0440\u0430\u043b\u0435\u043b\u0438\u0442\u044c \u0441\u0432\u043e\u0438 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c lock(object). \u0413\u0440\u0443\u0431\u043e \u0433\u043e\u0432\u043e\u0440\u044f \u0432\u0435\u0441\u044c \u043a\u043e\u0434 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443 \u0432\u0430\u0441 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432\u043d\u0443\u0442\u0440\u0438 lock \u043e\u0431\u044b\u0447\u043d\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432 \u0430\u043a\u0442\u043e\u0440. \u041a\u0440\u043e\u043c\u0435 \u044d\u0442\u043e\u0433\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0430\u043a\u0442\u043e\u0440 \u0436\u0438\u0432\u0435\u0442 \u0441\u0432\u043e\u0435\u0439 \u0436\u0438\u0437\u043d\u044c\u044e \u0438 \u0435\u0433\u043e \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u0434\u0440\u0443\u0433\u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044f \u0436\u0438\u0437\u043d\u0435\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c \u0432\u0441\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0432 \u0446\u0435\u043b\u043e\u043c. \u041e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u044c \u0432 \u043e\u0431\u0449\u0435\u043c. MS Orleans \u043c\u043d\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0438 \u0437\u0430\u0442\u043e\u0447\u0435\u043d\u043d\u044b\u043c \u043f\u043e\u0434 RPC. Akka.NET \u043f\u0440\u043e\u0449\u0435 \u0438 \u043c\u0435\u043d\u044c\u0448\u0435. \u0415\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043a\u0430\u043a \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0434\u043b\u044f \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0439 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c. MS Orleans \u0441\u0440\u0430\u0437\u0443 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0441\u0435\u0431\u0435 \u0432\u044b\u0434\u0435\u043b\u0438\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0434\u043b\u044f \u0441\u0435\u0431\u044f \u0445\u043e\u0441\u0442 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c\u0441\u044f \u043f\u0440\u0438 \u0441\u0442\u0430\u0440\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. Akka.NET \u0436\u0435 \u0432 \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0442\u0430\u0446\u0438\u0438 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043d\u0430\u0434\u043e. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b nuget \u043f\u0430\u043a\u0435\u0442 \u0438 \u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0448\u044c\u0441\u044f. \u0417\u0430\u0442\u043e \u0443 MS Orleans \u0441\u0442\u0440\u043e\u0433\u043e \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u0434\u043b\u044f \u0430\u043a\u0442\u043e\u0440\u043e\u0432(\u0433\u0440\u0435\u0439\u043d\u043e\u0432). \u0412 \u0446\u0435\u043b\u043e\u043c \u0435\u0441\u043b\u0438 \u0431\u044b \u043c\u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441 \u0446\u0435\u043b\u0438\u043a\u043e\u043c \u043d\u0430 \u0430\u043a\u0442\u043e\u0440\u0430\u0445 \u0442\u043e \u044f \u0432\u044b\u0431\u0440\u0430\u043b \u0431\u044b MS Orleans \u0435\u0441\u043b\u0438 \u0436\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u0432 \u043e\u0434\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u0440\u0430\u0441\u043f\u0430\u0440\u0430\u043b\u0435\u043b\u0438\u0442\u044c \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u0438 \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 lock, AutoResetEventSlim \u0438\u043b\u0438 \u0435\u0449\u0435 \u0447\u0442\u043e-\u0442\u043e \u0432 \u044d\u0442\u043e\u043c \u0440\u043e\u0434\u0435 \u0442\u043e Akka.NET. \u0422\u0430\u043a\u0438 \u0434\u0430, \u0431\u044b\u0442\u0443\u0435\u0442 \u0437\u0430\u0431\u043b\u0443\u0436\u0434\u0435\u043d\u0438\u0435 \u044f\u043a\u043e\u0431\u044b \u0441\u0435\u0440\u0432\u0435\u0440 \u0441\u0442\u0440\u0435\u043b\u044f\u043b\u043a\u0438 Hallo \u0441\u0434\u0435\u043b\u0430\u043d \u043d\u0430 \u0430\u043a\u0442\u043e\u0440\u0430\u0445. \u041e\u0439 \u0442\u0430\u043a\u0438 \u0432\u0435\u0439. \u0422\u0430\u043c \u043d\u0430 \u0430\u043a\u0442\u043e\u0440\u0430\u0445 \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u0441\u044f\u043a\u0430\u044f \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0432\u0440\u043e\u0434\u0435 \u043f\u043b\u0430\u0442\u0435\u0436\u0435\u0439 \u0438 \u043f\u0440\u043e\u0447\u0435\u0433\u043e. \u0421\u0430\u043c\u0430 \u043b\u043e\u0433\u0438\u043a\u0430 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f \u0438\u0433\u0440\u043e\u043a\u0430 \u0438 \u043f\u043e\u043f\u0430\u0434\u0430\u043d\u0438\u044f \u0432\u044b\u0441\u0442\u0440\u0435\u043b\u0430, \u0442\u043e\u0431\u0438\u0448\u044c \u0438\u0433\u0440\u043e\u0432\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430, \u043e\u043d\u0430 \u0432 C++ \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442\u0441\u044f. \u0412\u043e\u0442 \u0432 MMO \u0432\u0440\u043e\u0434\u0435 WoW \u0433\u0434\u0435 \u0433\u0434\u0435 \u0432\u044b \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442\u0435 \u044f\u0432\u043d\u043e \u0446\u0435\u043b\u044c \u0438 \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u0435\u0440\u0435\u0437\u0430\u0440\u044f\u0434\u043a\u0430 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u043c \u0432 \u043f\u043e\u0447\u0442\u0438 1 \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u043d\u0430 \u0432\u0441\u0435 \u0437\u0430\u043a\u043b\u0438\u043d\u0430\u043d\u0438\u044f \u0442\u0430\u043c \u0447\u0430\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0430\u043a\u0442\u043e\u0440\u044b.<\/p>\n<h2>\u041a\u043e\u0434 \u0438 \u043a\u043e\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438<\/h2>\n<p>  <\/p>\n<h3>\u0412\u0445\u043e\u0434\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u043d\u0430\u0448\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430. SignalR Hub<\/h3>\n<p>  <\/p>\n<pre><code class=\"cs\">      public class GameHub : Hub     {         private readonly ActorSystem _system;         private readonly IServiceProvider _provider;         private readonly IGrainFactory _client;          public GameHub(             IGrainFactory client,             ActorSystem system,             IServiceProvider provider             )         {             _client = client;             _system = system;             _provider = provider;         }          public async Task JoinGame(long gameId, Guid playerId)         { \/\/IActorRef \u044d\u0442\u043e \u043f\u0440\u043e\u043a\u0441\u0438 \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u0430\u043a\u0442\u043e\u0440\u0443. \/\/ \u041e\u043d \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0430\u043a\u0442\u043e\u0440\u0443 \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0441\u044b\u043b\u0430\u0435\u0442\u0441\u044f             var gameFactory = _provider.GetRequiredServiceByName&lt;Func&lt;long, IActorRef&gt;&gt;(&quot;game&quot;);             var game = gameFactory(gameId);             var random = new Random();             var player = new Player()             {                 IsAlive = true,                 GameId = gameId,                 Id = playerId,                 Point = new Point()                 {                     X = (byte)random.Next(1, GameActor.WIDTH - 1),                     Y = (byte)random.Next(1, GameActor.HEIGHT - 1)                 }             };             game.Tell(player);         }          public async Task GameInput(Input input, long gameId)         { \/\/ \u041f\u0443\u0442\u044c \u043a \u0430\u043a\u0442\u043e\u0440\u0443 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u0438\u043c\u0435\u043d\u0438 \u0435\u0433\u043e \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044f \u0438 \u0435\u0433\u043e \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u0438\u043c\u0435\u043d\u0438\u044f.  \/\/ user \u044d\u0442\u043e \u0432\u0441\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0430\u043a\u0442\u043e\u0440\u044b. \/\/ \u0446\u0435\u043b\u0438\u043a\u043e\u043c \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0447\u0442\u043e-\u0442\u043e \u0442\u0438\u043f\u043e akka:\/\/game\/user\/1\/2             _system.ActorSelection($&quot;user\/{gameId}\/{input.PlayerId}&quot;).Tell(input);         }     } <\/code><\/pre>\n<p>  \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043d\u0430\u0448\u0435\u0439 \u0430\u043a\u0442\u043e\u0440 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0432 DI:<\/p>\n<pre><code class=\"cs\">services.AddSingleton(ActorSystem.Create(&quot;game&quot;)); var games = new Dictionary&lt;long, IActorRef&gt;(); services.AddSingletonNamedService&lt;Func&lt;long, IActorRef&gt;&gt;(     &quot;game&quot;, (sp, name) =&gt; gameId =&gt; {     lock (games)     {         if (!games.TryGetValue(gameId, out IActorRef gameActor))         {             var frame = new Frame(GameActor.WIDTH, GameActor.HEIGHT) { GameId = gameId };             var gameEntity = new Game()             {                 Id = gameId,                 Frame = frame             }; \/\/\u0424\u0430\u0431\u0440\u0438\u043a\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u043e\u0432 \u0430\u043a\u0442\u043e\u0440\u0430. \/\/ \u041f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0442\u0443\u0434\u0430 IServiceProvide \u0447\u0442\u043e\u0431\u044b \u0430\u043a\u0442\u043e\u0440 \u043c\u043e\u0433 \u0440\u0435\u0437\u043e\u043b\u0432\u0438\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u0435\u043c\u0443 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438             var props = Props.Create(() =&gt; new GameActor(gameEntity, sp));             var actorSystem = sp.GetRequiredService&lt;ActorSystem&gt;(); \/\/\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u0430\u043a\u0442\u043e\u0440 \u0435\u0441\u043b\u0438 \u0442\u0430\u043a\u043e\u0439 \u043c\u044b \u0435\u0449\u0435 \u043d\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043b\u0438.             gameActor = actorSystem.ActorOf(props, gameId.ToString());             games[gameId] = gameActor;         }         return gameActor;     } }); <\/code><\/pre>\n<p>  <\/p>\n<h3>\u0410\u043a\u0442\u043e\u0440\u044b<\/h3>\n<p>  <\/p>\n<h4>GameActor<\/h4>\n<p>  <\/p>\n<pre><code class=\"cs\">    public sealed class GameActor : UntypedActor     {         public const byte WIDTH = 100;         public const byte HEIGHT = 50;         private DateTime _updateTime;         private double _totalTime;         private readonly Game _game;         private readonly IHubContext&lt;GameHub&gt; _hub;          public GameActor(Game game, IServiceProvider provider)         {             _updateTime = DateTime.Now;             _game = game;             _hub = (IHubContext&lt;GameHub&gt;)provider.GetService(typeof(IHubContext&lt;GameHub&gt;)); \/\/\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0448\u0435\u0434\u0443\u043b\u0435\u0440 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043d\u0430\u0448\u0435\u043c\u0443 \u0430\u043a\u0442\u043e\u0440\u0443 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \/\/RunMessage \u0433\u043e\u0432\u043e\u0440\u044f\u0449\u0435\u0435 \u0435\u043c\u0443 \u043f\u0440\u043e\u0431\u0435\u0436\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u0433\u0440\u043e\u0432\u043e\u0439 \u0446\u0438\u043a\u043b. \/\/\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0438 \u043e\u043f\u0440\u0430\u0432\u043a\u0430 \u043d\u043e\u0432\u043e\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442.             Context                 .System                 .Scheduler                 .ScheduleTellRepeatedly( \/\/\u0421\u043a\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0434\u043e \u043f\u043e\u0434\u043e\u0436\u0434\u0430\u0442\u044c \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043d\u0430\u0447\u0430\u0442\u044c \u043e\u0442\u0441\u044b\u043b\u0430\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f.                     TimeSpan.FromMilliseconds(100),  \/\/\u0420\u0430\u0437 \u0432 \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043b\u043b\u0438\u0441\u0435\u043a\u0443\u043d\u0434 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f                     TimeSpan.FromMilliseconds(1), \/\/\u041f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f                     Context.Self,  \/\/\u0427\u0442\u043e \u0437\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044e                     new RunMessage(), \/\/\u041a\u0442\u043e \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f. Nobody \u0437\u043d\u0430\u0447\u0438\u0442 \u0447\u0442\u043e \u043d\u0438\u043a\u0442\u043e. null \u0442\u043e\u0431\u0438\u0448\u044c.                     ActorRefs.Nobody                     );         }  \/\/\u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430 \u043d\u0430\u0448\u0435\u0433\u043e \u0430\u043a\u0442\u043e\u0440\u0430. \/\/\u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043a\u043e\u0433\u0434\u0430 \u0430\u043a\u0442\u043e\u0440 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043a\u0430\u043a\u043e\u0435-\u0442\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0441\u043d\u0430\u0440\u0443\u0436\u0438.         protected override void OnReceive(object message)         {             if (message is RunMessage run)                 Handle(run);             if (message is Player player)                 Handle(player);             if (message is Bullet bullet)                 Handle(bullet);         }  \/\/\u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0443 Create or Update \/\/\u0415\u0441\u043b\u0438 \u0438\u0433\u0440\u043e\u0432\u0430\u044f \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u043c\u0435\u0440\u0442\u0432\u0430 \u0442\u043e \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0435\u0435 \u0430\u043a\u0442\u043e\u0440 \u0438 \u0443\u0434\u0430\u043b\u044f\u0435\u0442 \u0435\u0435 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430         private void Update&lt;T&gt;(             List&lt;T&gt; entities,             T entity,             Func&lt;object&gt; createInitMessage,             Func&lt;Props&gt; createProps             )             where T : IGameEntity         {             if (!entity.IsAlive)             {                 var actor = Context.Child(entity.Id.ToString());                 if (!actor.IsNobody())                     Context.Stop(actor);                 entities.RemoveAll(b =&gt; b.Id == entity.Id);             } \/\/Create             else if (!entities.Any(b =&gt; b.Id == entity.Id))             {                 Context.ActorOf(createProps(), entity.Id.ToString());                 entities.Add(entity);                 Context.Child(entity.Id.ToString()).Tell(createInitMessage());             } \/\/Update             else             {                 entities.RemoveAll(b =&gt; b.Id == entity.Id);                 entities.Add(entity);             }         }          private void Handle(Bullet bullet)         {             Update(                 _game.Bullets,                 bullet,                 () =&gt; new InitBulletMessage(bullet.Clone(), _game.Frame.Clone()),                 () =&gt; Props.Create(() =&gt; new BulletActor())                 );         }          private void Handle(Player player)         {             Update(                 _game.Players,                 player,                 () =&gt; new InitPlayerMessage(player.Clone(), _game.Frame.Clone()),                 () =&gt; Props.Create(() =&gt; new PlayerActor())             );         }          private void Handle(RunMessage run)         {             var deltaTime = DateTime.Now - _updateTime;             _updateTime = DateTime.Now;             var delta = deltaTime.TotalMilliseconds;             Update(delta);             Draw(delta);         }          private void Update(double deltaTime)         {             var players = _game.Players.Select(p =&gt; p.Clone()).ToList();             foreach (var child in Context.GetChildren())             {                 child.Tell(new UpdateMessage(deltaTime, players));             }         }          private void Draw(double deltaTime)         {             _totalTime += deltaTime;             if (_totalTime &lt; 50)                 return;             _totalTime = 0; \/\/PipeTo \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0431\u043e\u0442\u044b Task \u044d\u0442\u043e\u043c\u0443 \u0430\u043a\u0442\u043e\u0440\u0443 \u0432 \u0432\u0438\u0434\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f. \/\/\u0414\u043b\u044f ReciveActor \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 async await \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c             _hub.Clients.All.SendAsync(&quot;gameUpdated&quot;, _game.Clone()).PipeTo(Self);         }     } <\/code><\/pre>\n<p>  <\/p>\n<h4>BulletActor<\/h4>\n<p>  <\/p>\n<pre><code class=\"cs\">    public class BulletActor : UntypedActor     {         private Bullet _bullet;         private Frame _frame;          protected override void OnReceive(object message)         {             if (message is InitBulletMessage bullet)                 Handle(bullet);             if (message is UpdateMessage update)                 Handle(update);         }          private void Handle(InitBulletMessage message)         {             _bullet = message.Bullet;             _frame = message.Frame;         }          private void Handle(UpdateMessage message)         {             if (_bullet == null)                 return;             if (!_bullet.IsAlive)             {                 Context.Parent.Tell(_bullet.Clone());                 return;             }             _bullet.Move(message.DeltaTime);             if (_frame.Collide(_bullet))                 _bullet.IsAlive = false;             if (!_bullet.IsInFrame(_frame))                 _bullet.IsAlive = false;             foreach (var player in message.Players)             {                 if (player.Id == _bullet.PlayerId)                     continue;  \/\/\u0415\u0441\u043b\u0438 \u043f\u0443\u043b\u044f \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0441 \u0438\u0433\u0440\u043e\u043a\u043e\u043c \u0442\u043e \u043e\u043d\u0430 \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u0435\u043c\u0443 \u0443\u043c\u0435\u0440\u0435\u0442\u044c \/\/\u0438 \u0443\u043c\u0438\u0440\u0430\u0435\u0442 \u0441\u0430\u043c\u0430                 if (player.Collide(_bullet))                 {                     _bullet.IsAlive = false;                     Context                         .ActorSelection(Context.Parent.Path.ToString() + &quot;\/&quot; + player.Id.ToString())                         .Tell(new DieMessage());                 }             }             Context.Parent.Tell(_bullet.Clone());         }     } <\/code><\/pre>\n<p>  <\/p>\n<h4>PlayerActor<\/h4>\n<p>  <\/p>\n<pre><code class=\"cs\">    public class PlayerActor : UntypedActor     {         private Player _player;         private Queue&lt;Direction&gt; _directions;         private Queue&lt;Command&gt; _commands;         private Frame _frame;          public PlayerActor()         {             _directions = new Queue&lt;Direction&gt;();             _commands = new Queue&lt;Command&gt;();         }          protected override void OnReceive(object message)         {             if (message is Input input)                 Handle(input);             if (message is UpdateMessage update)                 Handle(update);             if (message is InitPlayerMessage init)                 Handle(init);             if (message is DieMessage)             {                 _player.IsAlive = false;                 Context.Parent.Tell(_player.Clone());             }         }          private void Handle(InitPlayerMessage message)         {             _player = message.Player;             _frame = message.Frame;         }          private void Handle(Input message)         {             if (_player == null)                 return;             if (_player.IsAlive)             {                 foreach (var command in message.Commands)                 {                     _commands.Enqueue(command);                 }                  foreach (var direction in message.Directions)                 {                     _directions.Enqueue(direction);                 }             }         }          private void Handle(UpdateMessage update)         {             if (_player == null)                 return;             if (_player.IsAlive)             {                 HandleCommands(update.DeltaTime);                 HandleDirections();                 Move(update.DeltaTime);             }             Context.Parent.Tell(_player.Clone());         }          private void HandleDirections()         {             while (_directions.Count &gt; 0)             {                 _player.Direction = _directions.Dequeue();             }         }          private void HandleCommands(double delta)         {             _player.TimeAfterLastShot += delta;             if (!_player.HasColldown &amp;&amp; _commands.Any(command =&gt; command == Command.Shoot))             { \/\/Shot \u043f\u0440\u043e\u0441\u0442\u043e \u0444\u0430\u0431\u0440\u0438\u0447\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u043f\u0443\u043b\u044e \/\/ \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0434\u0432\u0438\u0436\u0435\u0442\u0441\u044f \u0432 \u0442\u043e\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u043a\u0443\u0434\u0430 \u0441\u043c\u043e\u0442\u0440\u0438 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436 \u0438\u0433\u0440\u043e\u043a\u0430                 var bullet = _player.Shot();                 Context.Parent.Tell(bullet.Clone());                 _commands.Clear();             }         }          private void Move(double delta)         {             _player.Move(delta);             if (_frame.Collide(_player))                 _player.MoveBack();         }     } <\/code><\/pre>\n<p>  <\/p>\n<h3>\u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u0430\u0435\u043c\u044b\u0435 \u043c\u0435\u0436\u0434\u0443 \u0430\u043a\u0442\u043e\u0440\u0430\u043c\u0438<\/h3>\n<p>  <\/p>\n<pre><code class=\"cs\">\/\/\u041f\u0440\u043e\u0441\u0442\u043e \u043f\u0440\u0438\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0443 \u0438\u0433\u0440\u043e\u043a\u0430 \u0443\u043c\u0435\u0440\u0435\u0442\u044c. \/\/\u0415\u0441\u043b\u0438 \u043e\u043d \u0432\u0434\u0440\u0443\u0433 \u0432 \u044d\u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0435\u0443\u044f\u0437\u0432\u0438\u043c \u0442\u043e \u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u043e\u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435. public sealed class DieMessage { } <\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"cs\">\/\/\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u0430\u043a\u0442\u043e\u0440 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0449\u0435\u0439 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u043f\u0443\u043b\u0438 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \/\/\u0422\u043e\u0447\u043d\u0435\u0435 \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u0430\u043a\u0442\u043e\u0440\u0443 \u0447\u0442\u043e\u0431\u044b \u043e\u043d \u0441\u0435\u0431\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043b \u044d\u0442\u0438\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438     public sealed class InitBulletMessage     {         public Bullet Bullet { get; }         public Frame Frame { get; }          public InitBulletMessage(Bullet bullet, Frame frame)         {             Bullet = bullet ?? throw new ApplicationException(&quot;\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043f\u0443\u043b\u044e&quot;);             Frame = frame ?? throw new ApplicationException(&quot;\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0444\u0440\u0435\u0439\u043c&quot;);         }     } <\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"cs\">\/\/\u0413\u043e\u0432\u043e\u0440\u0438\u0442 \u0430\u043a\u0442\u043e\u0440\u0443 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0449\u0435\u043c\u0443 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0430 \u0438\u0433\u0440\u043e\u043a\u0430  \/\/\u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u044d\u0442\u0438\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0434\u043b\u044f \u0441\u0432\u043e\u0435\u0439 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438     public class InitPlayerMessage     {         public Player Player { get; }         public Frame Frame { get; }          public InitPlayerMessage(Player player, Frame frame)         {             Player = player ?? throw new ApplicationException(&quot;\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0438\u0433\u0440\u043e\u043a\u0430!&quot;);             Frame = frame ?? throw new ApplicationException(&quot;\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0444\u0440\u0435\u0439\u043c&quot;);         }     } <\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"cs\">\/\/\u041f\u0440\u043e\u0441\u0442\u043e \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u0430\u043a\u0442\u043e\u0440\u0443 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0449\u0435\u043c\u0443 \u0432\u0441\u0435\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0438\u0433\u0440\u044b \u043f\u0440\u043e\u0431\u0435\u0436\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u0433\u0440\u043e\u0432\u043e\u0439 \u0446\u0438\u043a\u043b public sealed class RunMessage { } <\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"cs\">\/\/\u0413\u043e\u0432\u043e\u0440\u0438\u0442 \u0430\u043a\u0442\u043e\u0440\u0443 \u043f\u0443\u043b\u0438 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0441\u0432\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \/\/ \u0441 \u0443\u0447\u0435\u0442\u043e\u043c \u043f\u0440\u043e\u0448\u0435\u0434\u0448\u0435\u0433\u043e \u0441 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u0438.     public sealed class UpdateMessage     {         public double DeltaTime { get; } \/\/\u041d\u0443\u0436\u043d\u044b \u0447\u0442\u043e\u0431\u044b \u043f\u0443\u043b\u044f \u043c\u043e\u0433\u043b\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u0432\u043e\u0435 \u043f\u043e\u043f\u0430\u0434\u0430\u043d\u0438\u0435 \u0432 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u043d\u0438\u0445         public List&lt;Player&gt; Players { get; }          public UpdateMessage(double deltaTime, List&lt;Player&gt; players)         {             DeltaTime = deltaTime;             Players = players ?? throw new ApplicationException(&quot;\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0438\u0433\u0440\u043e\u043a\u043e\u0432!&quot;);         }     } <\/code><\/pre>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/500232\/\"> https:\/\/habr.com\/ru\/post\/500232\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/500232\/\">\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u0420\u0435\u0448\u0438\u043b \u044f \u0437\u043d\u0430\u0447\u0438\u0442 \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u0442\u044c \u0442\u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u043b \u0441 MS Orleans \u043d\u0430 Akka.NET \u043f\u0440\u043e\u0441\u0442\u043e \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0438 \u044d\u0442\u0443 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044e \u0442\u043e\u0436\u0435. \u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u0447\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u0434\u043e \u0434\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0434 \u043a\u0430\u0442.  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-302988","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/302988","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=302988"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/302988\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=302988"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=302988"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=302988"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}