Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Log inner exceptions instead of AggregateException #2352

Merged
merged 1 commit into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 19 additions & 28 deletions ImperatorToCK3/CK3/Characters/CharacterCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,34 +49,25 @@
MaxDegreeOfParallelism = Environment.ProcessorCount - 1,
};

try {
Parallel.ForEach(impWorld.Characters, parallelOptions, irCharacter => {
ImportImperatorCharacter(
irCharacter,
religionMapper,
cultureMapper,
traitMapper,
nicknameMapper,
impWorld.LocDB,
ck3LocDB,
impWorld.MapData,
provinceMapper,
deathReasonMapper,
dnaFactory,
conversionDate,
config,
unlocalizedImperatorNames
);
});
} catch (AggregateException e) {
var innerException = e.InnerExceptions[0];
Logger.Error("Exception thrown during Imperator characters import: " + innerException.Message);
Logger.Debug("Exception stack trace: " + innerException.StackTrace);

// Rethrow the inner exception to stop the program.
throw innerException;
}

Parallel.ForEach(impWorld.Characters, parallelOptions, irCharacter => {
ImportImperatorCharacter(
irCharacter,
religionMapper,
cultureMapper,
traitMapper,
nicknameMapper,
impWorld.LocDB,
ck3LocDB,
impWorld.MapData,
provinceMapper,
deathReasonMapper,
dnaFactory,
conversionDate,
config,
unlocalizedImperatorNames
);
});

if (unlocalizedImperatorNames.Any()) {
Logger.Warn("Found unlocalized Imperator names: " + string.Join(", ", unlocalizedImperatorNames));
}
Expand Down Expand Up @@ -670,156 +661,156 @@
Logger.IncrementProgress();
}

public void GenerateSuccessorsForOldCharacters(Title.LandedTitles titles, CultureCollection cultures, Date irSaveDate, Date ck3BookmarkDate, ulong randomSeed) {
Logger.Info("Generating successors for old characters...");

var oldCharacters = this
.Where(c => c.BirthDate < ck3BookmarkDate && c.DeathDate is null)
.Where(c => ck3BookmarkDate.DiffInYears(c.BirthDate) > 60)
.ToArray();

var titleHolderIds = titles.GetHolderIdsForAllTitlesExceptNobleFamilyTitles(ck3BookmarkDate);

var oldTitleHolders = oldCharacters
.Where(c => titleHolderIds.Contains(c.Id))
.ToArray();

// For characters that don't hold any titles, just set up a death date.
var randomForCharactersWithoutTitles = new Random((int)randomSeed);
foreach (var oldCharacter in oldCharacters.Except(oldTitleHolders)) {
// Roll a dice to determine how much longer the character will live.
var yearsToLive = randomForCharactersWithoutTitles.Next(0, 30);

// If the character is female and pregnant, make sure she doesn't die before the pregnancy ends.
if (oldCharacter is {Female: true, ImperatorCharacter: not null}) {
var lastPregnancy = oldCharacter.Pregnancies.OrderBy(p => p.BirthDate).LastOrDefault();
if (lastPregnancy is not null) {
oldCharacter.DeathDate = lastPregnancy.BirthDate.ChangeByYears(yearsToLive);
continue;
}
}

oldCharacter.DeathDate = irSaveDate.ChangeByYears(yearsToLive);
}

ConcurrentDictionary<string, Title[]> titlesByHolderId = new(titles
.Select(t => new {Title = t, HolderId = t.GetHolderId(ck3BookmarkDate)})
.Where(t => t.HolderId != "0")
.GroupBy(t => t.HolderId)
.ToDictionary(g => g.Key, g => g.Select(t => t.Title).ToArray()));

ConcurrentDictionary<string, string[]> cultureIdToMaleNames = new(cultures
.ToDictionary(c => c.Id, c => c.MaleNames.ToArray()));

// For title holders, generate successors and add them to title history.
Parallel.ForEach(oldTitleHolders, oldCharacter => {
// Get all titles held by the character.
var heldTitles = titlesByHolderId[oldCharacter.Id];
string? dynastyId = oldCharacter.GetDynastyId(ck3BookmarkDate);
string? dynastyHouseId = oldCharacter.GetDynastyHouseId(ck3BookmarkDate);
string? faithId = oldCharacter.GetFaithId(ck3BookmarkDate);
string? cultureId = oldCharacter.GetCultureId(ck3BookmarkDate);
string[] maleNames;
if (cultureId is not null) {
maleNames = cultureIdToMaleNames[cultureId];
} else {
Logger.Warn($"Failed to find male names for successors of {oldCharacter.Id}.");
maleNames = ["Alexander"];
}

var randomSeedForCharacter = randomSeed ^ (oldCharacter.ImperatorCharacter?.Id ?? 0);
var random = new Random((int)randomSeedForCharacter);

int successorCount = 0;
Character currentCharacter = oldCharacter;
Date currentCharacterBirthDate = currentCharacter.BirthDate;
while (ck3BookmarkDate.DiffInYears(currentCharacterBirthDate) >= 90) {
// If the character has living male children, the oldest one will be the successor.
var successorAndBirthDate = currentCharacter.Children
.Where(c => c is {Female: false, DeathDate: null})
.Select(c => new { Character = c, c.BirthDate })
.OrderBy(x => x.BirthDate)
.FirstOrDefault();

Character successor;
Date currentCharacterDeathDate;
Date successorBirthDate;
if (successorAndBirthDate is not null) {
successor = successorAndBirthDate.Character;
successorBirthDate = successorAndBirthDate.BirthDate;

// Roll a dice to determine how much longer the character will live.
// But make sure the successor is at least 16 years old when the old character dies.
var successorAgeAtBookmarkDate = ck3BookmarkDate.DiffInYears(successorBirthDate);
var yearsUntilSuccessorBecomesAnAdult = Math.Max(16 - successorAgeAtBookmarkDate, 0);

var yearsToLive = random.Next((int)Math.Ceiling(yearsUntilSuccessorBecomesAnAdult), 25);
int currentCharacterAge = random.Next(30 + yearsToLive, 80);
currentCharacterDeathDate = currentCharacterBirthDate.ChangeByYears(currentCharacterAge);
// Needs to be after the save date.
if (currentCharacterDeathDate <= irSaveDate) {
currentCharacterDeathDate = irSaveDate.ChangeByDays(1);
}
} else {
// We don't want all the generated successors on the map to have the same birth date.
var yearsUntilHeir = random.Next(1, 5);

// Make the old character live until the heir is at least 16 years old.
var successorAge = random.Next(yearsUntilHeir + 16, 30);
int currentCharacterAge = random.Next(30 + successorAge, 80);
currentCharacterDeathDate = currentCharacterBirthDate.ChangeByYears(currentCharacterAge);
if (currentCharacterDeathDate <= irSaveDate) {
currentCharacterDeathDate = irSaveDate.ChangeByDays(1);
}

// Generate a new successor.
string id = $"irtock3_{oldCharacter.Id}_successor_{successorCount}";
string firstName = maleNames[random.Next(0, maleNames.Length)];

successorBirthDate = currentCharacterDeathDate.ChangeByYears(-successorAge);
successor = new Character(id, firstName, successorBirthDate, this) {FromImperator = true};
Add(successor);
if (currentCharacter.Female) {
successor.Mother = currentCharacter;
} else {
successor.Father = currentCharacter;
}
if (cultureId is not null) {
successor.SetCultureId(cultureId, null);
}
if (faithId is not null) {
successor.SetFaithId(faithId, null);
}
if (dynastyId is not null) {
successor.SetDynastyId(dynastyId, null);
}
if (dynastyHouseId is not null) {
successor.SetDynastyHouseId(dynastyHouseId, null);
}
}

currentCharacter.DeathDate = currentCharacterDeathDate;
// On the old character death date, the successor should inherit all titles.
foreach (var heldTitle in heldTitles) {
heldTitle.SetHolder(successor, currentCharacterDeathDate);
}

// Move to the successor and repeat the process.
currentCharacter = successor;
currentCharacterBirthDate = successorBirthDate;
++successorCount;
}

// After the loop, currentCharacter should represent the successor at bookmark date.
// Set his DNA to avoid weird looking character on the bookmark screen in CK3.
currentCharacter.DNA = oldCharacter.DNA;

// Transfer gold to the living successor.
currentCharacter.Gold = oldCharacter.Gold;
oldCharacter.Gold = null;
});
}

Check notice on line 813 in ImperatorToCK3/CK3/Characters/CharacterCollection.cs

View check run for this annotation

codefactor.io / CodeFactor

ImperatorToCK3/CK3/Characters/CharacterCollection.cs#L664-L813

Complex Method
public void ConvertImperatorCharacterDNA(DNAFactory dnaFactory) {
Logger.Info("Converting Imperator character DNA to CK3...");
foreach (var character in this) {
Expand Down
16 changes: 11 additions & 5 deletions ImperatorToCK3/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using log4net.Core;
using System;
using System.Globalization;
using System.Linq;

namespace ImperatorToCK3;
public static class Program {
Expand All @@ -22,14 +23,19 @@ public static int Main(string[] args) {
}
Converter.ConvertImperatorToCK3(converterVersion);
return 0;
} catch (Exception e) {
Logger.Log(Level.Fatal, e is UserErrorException ? e.Message : $"{e.GetType()}: {e.Message}");
if (e.StackTrace is not null) {
Logger.Debug(e.StackTrace);
} catch (Exception ex) {
// If the exception is an AggregateException, we want the original inner exception's stack trace.
if (ex is AggregateException aggregateEx) {
ex = aggregateEx.Flatten().InnerExceptions.FirstOrDefault() ?? ex;
}

Logger.Log(Level.Fatal, ex is UserErrorException ? ex.Message : $"{ex.GetType()}: {ex.Message}");
if (ex.StackTrace is not null) {
Logger.Debug(ex.StackTrace);
}

// Return exit code 1 for user errors. They should not be reported to Sentry.
if (e is UserErrorException) {
if (ex is UserErrorException) {
return 1;
}
return -1;
Expand Down
Loading