Ejemplo n.º 1
0
        public async Task SetAsync(
            [FromBody] Reinforcement param)
        {
            Reinforcement reinforcement;
            MapLog        maplog = null;
            Character     self;

            using (var repo = MainRepository.WithReadAndWrite())
            {
                var chara = await repo.Character.GetByIdAsync(this.AuthData.CharacterId).GetOrErrorAsync(ErrorCode.CharacterNotFoundError);

                var countryData = await repo.Country.GetAliveByIdAsync(chara.CountryId);

                var system = await repo.System.GetAsync();

                self = chara;

                if (system.IsBattleRoyaleMode && param.Status == ReinforcementStatus.Active)
                {
                    // 全面戦争中は援軍にいけない
                    ErrorCode.InvalidOperationError.Throw();
                }

                var oldTownId = chara.TownId;

                if (param.Status == ReinforcementStatus.Requesting || param.Status == ReinforcementStatus.RequestCanceled)
                {
                    // chara: 要求するほう

                    var country = countryData.GetOrError(ErrorCode.CountryNotFoundError);

                    var posts         = (await repo.Country.GetPostsAsync(country.Id)).Where(p => p.CharacterId == chara.Id);
                    var hasPermission = posts.Any(p => p.Type.CanDiplomacy());

                    if (!hasPermission)
                    {
                        ErrorCode.NotPermissionError.Throw();
                    }

                    var targetCharacter = await repo.Character.GetByIdAsync(param.CharacterId).GetOrErrorAsync(ErrorCode.CharacterNotFoundError);

                    var targetCountry = await repo.Country.GetByIdAsync(targetCharacter.CountryId).GetOrErrorAsync(ErrorCode.CountryNotFoundError);

                    var olds = await repo.Reinforcement.GetByCharacterIdAsync(targetCharacter.Id);

                    if (country.Id == targetCountry.Id)
                    {
                        ErrorCode.MeaninglessOperationError.Throw();
                    }
                    var alliance = await repo.CountryDiplomacies.GetCountryAllianceAsync(country.Id, targetCountry.Id).GetOrErrorAsync(ErrorCode.NotPermissionError);

                    if (alliance.Status != CountryAllianceStatus.Available && alliance.Status != CountryAllianceStatus.ChangeRequesting)
                    {
                        ErrorCode.NotPermissionError.Throw();
                    }
                    if (olds.Any(r => r.Status == ReinforcementStatus.Active))
                    {
                        ErrorCode.InvalidOperationError.Throw();
                    }
                    if (olds.Any(r => r.Status == ReinforcementStatus.Requesting && r.RequestedCountryId == chara.CountryId))
                    {
                        if (param.Status == ReinforcementStatus.Requesting)
                        {
                            ErrorCode.MeaninglessOperationError.Throw();
                        }
                    }
                    else
                    {
                        if (param.Status == ReinforcementStatus.RequestCanceled)
                        {
                            ErrorCode.InvalidOperationError.Throw();
                        }
                    }

                    if (param.Status == ReinforcementStatus.Requesting)
                    {
                        reinforcement = new Reinforcement
                        {
                            CharacterId        = targetCharacter.Id,
                            CharacterCountryId = targetCountry.Id,
                            RequestedCountryId = country.Id,
                            Status             = ReinforcementStatus.Requesting,
                        };
                        await repo.Reinforcement.AddAsync(reinforcement);
                    }
                    else
                    {
                        reinforcement        = olds.FirstOrDefault(r => r.RequestedCountryId == chara.CountryId);
                        reinforcement.Status = ReinforcementStatus.RequestCanceled;
                    }
                }
                else if (param.Status == ReinforcementStatus.RequestDismissed ||
                         param.Status == ReinforcementStatus.Active ||
                         param.Status == ReinforcementStatus.Returned ||
                         param.Status == ReinforcementStatus.Submited)
                {
                    // chara: 要求されるほう

                    var olds = await repo.Reinforcement.GetByCharacterIdAsync(chara.Id);

                    var old = olds.FirstOrDefault(r => r.CharacterCountryId == chara.CountryId && r.RequestedCountryId == param.RequestedCountryId);

                    if (old == null && param.Status != ReinforcementStatus.Returned && param.Status != ReinforcementStatus.Submited)
                    {
                        ErrorCode.InvalidOperationError.Throw();
                    }

                    if (param.Status == ReinforcementStatus.RequestDismissed)
                    {
                        if (old.Status != ReinforcementStatus.Requesting)
                        {
                            ErrorCode.InvalidOperationError.Throw();
                        }
                    }
                    if (param.Status == ReinforcementStatus.Active)
                    {
                        var country          = countryData.GetOrError(ErrorCode.CountryNotFoundError);
                        var requestedCountry = await repo.Country.GetByIdAsync(param.RequestedCountryId).GetOrErrorAsync(ErrorCode.CountryNotFoundError);

                        if (old.Status != ReinforcementStatus.Requesting)
                        {
                            ErrorCode.InvalidOperationError.Throw();
                        }
                        if (requestedCountry.HasOverthrown)
                        {
                            ErrorCode.InvalidOperationError.Throw();
                        }

                        var alliance = await repo.CountryDiplomacies.GetCountryAllianceAsync(old.RequestedCountryId, old.CharacterCountryId).GetOrErrorAsync(ErrorCode.NotPermissionError);

                        if (alliance.Status != CountryAllianceStatus.Available && alliance.Status != CountryAllianceStatus.ChangeRequesting)
                        {
                            ErrorCode.NotPermissionError.Throw();
                        }

                        var post      = (await repo.Country.GetPostsAsync(chara.CountryId)).Where(p => p.CharacterId == chara.Id);
                        var isMonarch = post.Any(p => p.Type == CountryPostType.Monarch);

                        if (isMonarch && !Config.Game.IsAllowMonarchReinforcement)
                        {
                            ErrorCode.NotPermissionError.Throw();
                        }

                        await CharacterService.ChangeTownAsync(repo, requestedCountry.CapitalTownId, chara);

                        await CharacterService.ChangeCountryAsync(repo, requestedCountry.Id, new Character[] { chara, });

                        // 君主が援軍に行く場合、君主データを残す
                        if (isMonarch)
                        {
                            var monarchPost = post.First(p => p.Type == CountryPostType.Monarch);
                            var monarch     = new CountryPost
                            {
                                CharacterId = chara.Id,
                                CountryId   = old.CharacterCountryId,
                                Type        = CountryPostType.MonarchDisabled,
                            };
                            repo.Country.RemoveCharacterPost(monarchPost);
                            await repo.Country.AddPostAsync(monarch);

                            await StatusStreaming.Default.SendCountryAsync(ApiData.From(monarch), old.CharacterCountryId);
                        }

                        maplog = new MapLog
                        {
                            ApiGameDateTime = system.GameDateTime,
                            Date            = DateTime.Now,
                            EventType       = EventType.ReinforcementActived,
                            IsImportant     = false,
                            Message         = $"<country>{country.Name}</country> の <character>{chara.Name}</character> は、 <country>{requestedCountry.Name}</country> へ援軍に行きました",
                        };
                    }
                    if (param.Status == ReinforcementStatus.Returned)
                    {
                        old = olds.FirstOrDefault(o => o.Status == ReinforcementStatus.Active);

                        if (old == null)
                        {
                            ErrorCode.InvalidOperationError.Throw();
                        }

                        var originalCountry = await repo.Country.GetByIdAsync(old.CharacterCountryId).GetOrErrorAsync(ErrorCode.CountryNotFoundError);

                        if (originalCountry.HasOverthrown)
                        {
                            ErrorCode.InvalidOperationError.Throw();
                        }

                        await CharacterService.ChangeTownAsync(repo, originalCountry.CapitalTownId, chara);

                        await CharacterService.ChangeCountryAsync(repo, originalCountry.Id, new Character[] { chara, });

                        // 援軍に行ったのが君主の場合、復活する
                        var post = await repo.Country.GetCharacterPostsAsync(chara.Id);

                        if (post.Any(p => p.Type == CountryPostType.MonarchDisabled))
                        {
                            repo.Country.RemoveCharacterPosts(chara.Id);
                            var monarch = new CountryPost
                            {
                                CharacterId = chara.Id,
                                CountryId   = old.CharacterCountryId,
                                Type        = CountryPostType.Monarch,
                            };
                            await repo.Country.AddPostAsync(monarch);

                            await StatusStreaming.Default.SendCountryAsync(ApiData.From(monarch), old.CharacterCountryId);
                        }

                        var countryName = countryData.Data?.Name ?? "無所属";
                        maplog = new MapLog
                        {
                            ApiGameDateTime = system.GameDateTime,
                            Date            = DateTime.Now,
                            EventType       = EventType.ReinforcementReturned,
                            IsImportant     = false,
                            Message         = $"<country>{originalCountry.Name}</country> の援軍 <character>{chara.Name}</character> は、 <country>{countryName}</country> から帰還しました",
                        };
                    }
                    if (param.Status == ReinforcementStatus.Submited)
                    {
                        var country = countryData.GetOrError(ErrorCode.CountryNotFoundError);
                        old = olds.FirstOrDefault(o => o.Status == ReinforcementStatus.Active);

                        if (old == null)
                        {
                            ErrorCode.InvalidOperationError.Throw();
                        }

                        var originalCountry = await repo.Country.GetByIdAsync(old.CharacterCountryId).GetOrErrorAsync(ErrorCode.CountryNotFoundError);

                        if (!originalCountry.HasOverthrown)
                        {
                            ErrorCode.InvalidOperationError.Throw();
                        }

                        maplog = new MapLog
                        {
                            ApiGameDateTime = system.GameDateTime,
                            Date            = DateTime.Now,
                            EventType       = EventType.ReinforcementSubmited,
                            IsImportant     = false,
                            Message         = $"<country>{originalCountry.Name}</country> の援軍 <character>{chara.Name}</character> は、 <country>{country.Name}</country> に帰順しました",
                        };
                    }

                    reinforcement        = old;
                    reinforcement.Status = param.Status;
                }
                else
                {
                    ErrorCode.InvalidParameterError.Throw();
                    reinforcement = null;
                }

                if (maplog != null)
                {
                    await repo.MapLog.AddAsync(maplog);
                }

                await repo.SaveChangesAsync();
            }

            // マップログ、援軍情報を通知
            await StatusStreaming.Default.SendCountryAsync(ApiData.From(reinforcement), new uint[] { reinforcement.CharacterCountryId, reinforcement.RequestedCountryId, });

            if (maplog != null)
            {
                await StatusStreaming.Default.SendAllAsync(ApiData.From(maplog));

                await AnonymousStreaming.Default.SendAllAsync(ApiData.From(maplog));
            }
            await StatusStreaming.Default.SendCharacterAsync(ApiData.From(self), self.Id);
        }
Ejemplo n.º 2
0
        public static async Task EntryAsync(MainRepository repo, string ipAddress, Character newChara, CharacterIcon newIcon, string password, Country newCountry, string invitationCode, bool isFreeCountry)
        {
            var town = await repo.Town.GetByIdAsync(newChara.TownId).GetOrErrorAsync(ErrorCode.TownNotFoundError);

            var system = await repo.System.GetAsync();

            CheckEntryStatus(system.GameDateTime, ipAddress, newChara, password, newIcon, town, newCountry, isFreeCountry);

            // 文字数チェックしてからエスケープ
            newChara.Name    = HtmlUtil.Escape(newChara.Name);
            newCountry.Name  = HtmlUtil.Escape(newCountry.Name);
            newChara.Message = HtmlUtil.Escape(newChara.Message);

            // 既存との重複チェック
            if (await repo.Character.IsAlreadyExistsAsync(newChara.Name, newChara.AliasId))
            {
                ErrorCode.DuplicateCharacterNameOrAliasIdError.Throw();
            }
            if ((system.IsDebug && (await repo.System.GetDebugDataAsync()).IsCheckDuplicateEntry || !system.IsDebug) &&
                await repo.EntryHost.ExistsAsync(ipAddress))
            {
                ErrorCode.DuplicateEntryError.Throw();
            }

            // 招待コードチェック
            Optional <InvitationCode> invitationCodeOptional = default;

            if (system.InvitationCodeRequestedAtEntry)
            {
                invitationCodeOptional = await repo.InvitationCode.GetByCodeAsync(invitationCode);

                if (!invitationCodeOptional.HasData || invitationCodeOptional.Data.HasUsed || invitationCodeOptional.Data.Aim != InvitationCodeAim.Entry)
                {
                    ErrorCode.InvitationCodeRequestedError.Throw();
                }
            }

            var    updateCountriesRequested = false;
            MapLog maplog = null;
            var    chara  = new Character
            {
                Name                = newChara.Name,
                AliasId             = newChara.AliasId,
                Strong              = newChara.Strong,
                StrongEx            = 0,
                Intellect           = newChara.Intellect,
                IntellectEx         = 0,
                Leadership          = newChara.Leadership,
                LeadershipEx        = 0,
                Popularity          = newChara.Popularity,
                PopularityEx        = 0,
                Contribution        = 0,
                Class               = 0,
                DeleteTurn          = (short)(Config.DeleteTurns - 10),
                LastUpdated         = DateTime.Now,
                LastUpdatedGameDate = system.GameDateTime,
                Message             = newChara.Message,
                Money               = 10_0000 + Math.Max(system.GameDateTime.ToInt() - (Config.StartYear + Config.UpdateStartYear + 4) * 12 - Config.StartMonth, 0) * 800,
                Rice                = 5_0000 + Math.Max(system.GameDateTime.ToInt() - (Config.StartYear + Config.UpdateStartYear + 4) * 12 - Config.StartMonth, 0) * 400,
                SkillPoint          = Math.Max(0, (int)((system.IntGameDateTime - Config.UpdateStartYear * 12) * 0.8f)),
                SoldierType         = SoldierType.Common,
                SoldierNumber       = 0,
                Proficiency         = 0,
                TownId              = newChara.TownId,
                From                = newChara.From,
                Religion            = ReligionType.Any,
                FormationType       = newChara.FormationType,
                IsBeginner          = newChara.IsBeginner,
            };

            chara.SetPassword(password);

            // 出身
            var skills = new List <CharacterSkillType>();
            var items  = new List <CharacterItemType>();

            if (chara.From == CharacterFrom.Warrior)
            {
                skills.Add(CharacterSkillType.Strong1);
                chara.Strong += 20;
            }
            else if (chara.From == CharacterFrom.Civilian)
            {
                skills.Add(CharacterSkillType.Intellect1);
                chara.Intellect += 20;
            }
            else if (chara.From == CharacterFrom.Merchant)
            {
                skills.Add(CharacterSkillType.Merchant1);
                chara.Money += 200000;
            }
            else if (chara.From == CharacterFrom.Engineer)
            {
                skills.Add(CharacterSkillType.Engineer1);
                chara.Strong += 10;
                chara.Money  += 100000;
            }
            else if (chara.From == CharacterFrom.Terrorist)
            {
                skills.Add(CharacterSkillType.Terrorist1);
                chara.Strong     += 15;
                chara.Leadership += 5;
            }
            else if (chara.From == CharacterFrom.People)
            {
                skills.Add(CharacterSkillType.People1);
                chara.Popularity += 20;
            }
            else if (chara.From == CharacterFrom.Tactician)
            {
                skills.Add(CharacterSkillType.Tactician1);
                chara.Strong     += 5;
                chara.Leadership += 15;
            }
            else if (chara.From == CharacterFrom.Scholar)
            {
                skills.Add(CharacterSkillType.Scholar1);
                chara.Intellect += 20;
            }
            else if (chara.From == CharacterFrom.Staff)
            {
                skills.Add(CharacterSkillType.Staff1);
                chara.Intellect += 20;
            }
            else if (chara.From == CharacterFrom.Buddhism)
            {
                if (system.RuleSet == GameRuleSet.SimpleBattle)
                {
                    ErrorCode.RuleSetError.Throw();
                }
                skills.Add(CharacterSkillType.Buddhism1);
                chara.Intellect += 15;
                chara.Religion   = ReligionType.Buddhism;
            }
            else if (chara.From == CharacterFrom.Confucianism)
            {
                if (system.RuleSet == GameRuleSet.SimpleBattle)
                {
                    ErrorCode.RuleSetError.Throw();
                }
                skills.Add(CharacterSkillType.Confucianism1);
                chara.Intellect += 15;
                chara.Religion   = ReligionType.Confucianism;
            }
            else if (chara.From == CharacterFrom.Taoism)
            {
                if (system.RuleSet == GameRuleSet.SimpleBattle)
                {
                    ErrorCode.RuleSetError.Throw();
                }
                skills.Add(CharacterSkillType.Taoism1);
                chara.Intellect += 15;
                chara.Religion   = ReligionType.Taoism;
            }
            else
            {
                ErrorCode.InvalidParameterError.Throw();
            }

            // 来月の更新がまだ終わってないタイミングで登録したときの、武将更新時刻の調整
            if (chara.LastUpdated - system.CurrentMonthStartDateTime > TimeSpan.FromSeconds(Config.UpdateTime))
            {
                chara.IntLastUpdatedGameDate++;
            }

            if (isFreeCountry)
            {
                // 無所属で開始
                chara.CountryId = 0;
                await repo.Character.AddAsync(chara);

                maplog = new MapLog
                {
                    Date            = DateTime.Now,
                    ApiGameDateTime = system.GameDateTime,
                    EventType       = EventType.CharacterEntryToFree,
                    IsImportant     = false,
                    Message         = $"<character>{chara.Name}</character> が無所属に出現しました",
                };
                await repo.MapLog.AddAsync(maplog);
            }
            else if (town.CountryId > 0)
            {
                // 武将総数チェック
                var country = await repo.Country.GetByIdAsync(town.CountryId).GetOrErrorAsync(ErrorCode.CountryNotFoundError);

                if (country.IntEstablished + Config.CountryBattleStopDuring > system.GameDateTime.ToInt())
                {
                    var countryCharaCount = await repo.Country.CountCharactersAsync(country.Id, true);

                    if (countryCharaCount >= Config.CountryJoinMaxOnLimited)
                    {
                        ErrorCode.CantJoinAtSuchCountryhError.Throw();
                    }
                    else if (countryCharaCount + 1 == Config.CountryJoinMaxOnLimited)
                    {
                        updateCountriesRequested = true;
                    }
                }

                // AI国家チェック
                if (country.AiType != CountryAiType.Human)
                {
                    ErrorCode.CantJoinAtSuchCountryhError.Throw();
                }

                // 金と米を仕官先国の武将の平均にあわせる
                if (system.GameDateTime.Year >= Config.UpdateStartYear + Config.CountryBattleStopDuring / 12)
                {
                    var countryCharacters = (await repo.Country.GetCharactersAsync(town.CountryId)).Where(c => c.AiType == CharacterAiType.Human || c.AiType == CharacterAiType.Administrator);
                    if (countryCharacters.Any())
                    {
                        chara.Money = Math.Min((int)countryCharacters.Average(c => c.Money) + 10_0000, chara.Money);
                        chara.Rice  = Math.Min((int)countryCharacters.Average(c => c.Rice) + 5_0000, chara.Rice);
                    }
                }

                chara.CountryId = town.CountryId;
                await repo.Character.AddAsync(chara);

                maplog = new MapLog
                {
                    Date            = DateTime.Now,
                    ApiGameDateTime = system.GameDateTime,
                    EventType       = EventType.CharacterEntry,
                    IsImportant     = false,
                    Message         = $"<character>{chara.Name}</character> が <country>{country.Name}</country> に仕官しました",
                };
                await repo.MapLog.AddAsync(maplog);
            }
            else
            {
                // 重複チェック
                if ((await repo.Country.GetAllAsync()).Any(c => c.Name == newCountry.Name || c.CountryColorId == newCountry.CountryColorId))
                {
                    ErrorCode.DuplicateCountryNameOrColorError.Throw();
                }

                if (system.RuleSet == GameRuleSet.SimpleBattle)
                {
                    newCountry.Civilization = CountryCivilization.None;
                }

                var country = new Country
                {
                    Name           = newCountry.Name,
                    CountryColorId = newCountry.CountryColorId,
                    CapitalTownId  = newChara.TownId,
                    IntEstablished = Math.Max(system.GameDateTime.ToInt(), new GameDateTime {
                        Year = Config.UpdateStartYear, Month = 1,
                    }.ToInt()),
                    HasOverthrown         = false,
                    IntOverthrownGameDate = 0,
                    LastMoneyIncomes      = 0,
                    LastRiceIncomes       = 0,
                    PolicyPoint           = 10000,
                    Religion     = RandomService.Next(new ReligionType[] { ReligionType.Buddhism, ReligionType.Confucianism, ReligionType.Taoism, }),
                    Civilization = newCountry.Civilization,
                };
                updateCountriesRequested = true;
                await repo.Country.AddAsync(country);

                var countries = await repo.Country.GetAllAsync();

                var point = 500;

                if (system.RuleSet != GameRuleSet.Wandering)
                {
                    // 大都市に変更
                    town.SubType = town.Type;
                    MapService.UpdateTownType(town, TownType.Large);

                    // 首都の宗教
                    if (system.RuleSet != GameRuleSet.SimpleBattle)
                    {
                        if (country.Religion == ReligionType.Buddhism)
                        {
                            town.Buddhism    += point;
                            town.Confucianism = Math.Min(town.Confucianism, 999);
                            town.Taoism       = Math.Min(town.Taoism, 999);
                        }
                        if (country.Religion == ReligionType.Confucianism)
                        {
                            town.Confucianism += point;
                            town.Buddhism      = Math.Min(town.Buddhism, 999);
                            town.Taoism        = Math.Min(town.Taoism, 999);
                        }
                        if (country.Religion == ReligionType.Taoism)
                        {
                            town.Taoism      += point;
                            town.Buddhism     = Math.Min(town.Buddhism, 999);
                            town.Confucianism = Math.Min(town.Confucianism, 999);
                        }
                    }
                }
                else
                {
                    country.CapitalTownId = 0;
                    items.Add(CharacterItemType.CastleBlueprint);
                    items.Add(CharacterItemType.CastleBlueprint);
                    items.Add(CharacterItemType.CastleBlueprint);
                    chara.Money += 50_0000 * 3;
                }

                await repo.SaveChangesAsync();

                chara.CountryId = country.Id;
                if (system.RuleSet != GameRuleSet.Wandering)
                {
                    town.CountryId = country.Id;
                }
                await repo.Character.AddAsync(chara);

                await repo.SaveChangesAsync();

                var countryPost = new CountryPost
                {
                    Type        = CountryPostType.Monarch,
                    CountryId   = country.Id,
                    CharacterId = chara.Id,
                };
                await repo.Country.AddPostAsync(countryPost);

                if (system.RuleSet == GameRuleSet.Wandering)
                {
                    maplog = new MapLog
                    {
                        Date            = DateTime.Now,
                        ApiGameDateTime = system.GameDateTime,
                        EventType       = EventType.StartWandering,
                        IsImportant     = true,
                        Message         = $"<character>{chara.Name}</character> は <country>{country.Name}</country> の頭領となり放浪を開始しました",
                    };
                }
                else
                {
                    maplog = new MapLog
                    {
                        Date            = DateTime.Now,
                        ApiGameDateTime = system.GameDateTime,
                        EventType       = EventType.Publish,
                        IsImportant     = true,
                        Message         = $"<character>{chara.Name}</character> が <town>{town.Name}</town> に <country>{country.Name}</country> を建国しました",
                    };
                }
                await repo.MapLog.AddAsync(maplog);
            }

            await repo.SaveChangesAsync();

            // 陣形
            var formation = new Formation
            {
                CharacterId = chara.Id,
                Type        = chara.FormationType,
                Level       = 1,
            };
            await repo.Character.AddFormationAsync(formation);

            if (invitationCodeOptional.HasData)
            {
                var code = invitationCodeOptional.Data;
                code.HasUsed     = true;
                code.Used        = DateTime.Now;
                code.CharacterId = chara.Id;
            }

            var icon = new CharacterIcon
            {
                Type        = newIcon.Type,
                IsAvailable = true,
                IsMain      = true,
                FileName    = newIcon.FileName,
                CharacterId = chara.Id,
            };
            await repo.Character.AddCharacterIconAsync(icon);

            var host = new EntryHost
            {
                CharacterId = chara.Id,
                IpAddress   = ipAddress,
            };
            await repo.EntryHost.AddAsync(host);

            var skillItems = skills.Select(s => new CharacterSkill
            {
                CharacterId = chara.Id,
                Type        = s,
                Status      = CharacterSkillStatus.Available,
            });

            foreach (var si in skillItems)
            {
                await SkillService.SetCharacterAndSaveAsync(repo, si, chara);
            }
            var itemData = items.Select(i => new CharacterItem
            {
                Type        = i,
                CharacterId = chara.Id,
                Status      = CharacterItemStatus.CharacterHold,
            });

            foreach (var id in itemData)
            {
                await ItemService.GenerateItemAndSaveAsync(repo, id);
            }

            await repo.SaveChangesAsync();

            if (updateCountriesRequested)
            {
                var countries = await repo.Country.GetAllForAnonymousAsync();

                await AnonymousStreaming.Default.SendAllAsync(countries.Select(c => ApiData.From(c)));

                await StatusStreaming.Default.SendAllAsync(countries.Select(c => ApiData.From(c)));
            }

            var townData   = ApiData.From(new TownForAnonymous(town));
            var maplogData = ApiData.From(maplog);
            await AnonymousStreaming.Default.SendAllAsync(maplogData);

            await StatusStreaming.Default.SendAllAsync(maplogData);

            await AnonymousStreaming.Default.SendAllAsync(townData);

            await StatusStreaming.Default.SendAllAsync(townData);

            await StatusStreaming.Default.SendCountryAsync(ApiData.From(town), town.CountryId);

            await CharacterService.StreamCharacterAsync(repo, chara);
        }