/// <summary> /// generate a snapshot package the specified character and freeze the character if it's a migration. /// </summary> /// <returns></returns> public static async Task <PackageMetadata> CreatePackage(PackageMetadata metadata) { metadata.Cookie = ThreadSafeRandom.NextString(TransferManagerConstants.CookieChars, TransferManagerConstants.CookieLength); // obtain character snapshot CharacterSnapshot snapshot = new CharacterSnapshot(); TaskCompletionSource <object> tsc = new TaskCompletionSource <object>(); OfflinePlayer offlinePlayer = PlayerManager.GetOfflinePlayer(metadata.CharacterId); if (offlinePlayer == null) { return(null); // character must be logged out } snapshot.Player = offlinePlayer.Biota; DatabaseManager.Shard.GetCharacters(metadata.AccountId, false, new Action <List <Character> >(new Action <List <Character> >((chars) => { snapshot.Character = chars.FirstOrDefault(k => k.Id == metadata.CharacterId); if (snapshot.Character == null) { tsc.SetResult(new object()); return; } if (snapshot.Character.IsReadOnly) { snapshot.Character = null; tsc.SetResult(new object()); return; } if (metadata.PackageType == PackageType.Migrate) { // place character in migrating state snapshot.Character.IsReadOnly = true; DatabaseManager.Shard.SaveCharacter(snapshot.Character, new ReaderWriterLockSlim(), null); } DatabaseManager.Shard.GetPossessedBiotasInParallel(snapshot.Character.Id, new Action <PossessedBiotas>((pb) => { snapshot.PossessedBiotas = pb; tsc.SetResult(new object()); })); }))); await tsc.Task; if (snapshot.Character == null) { return(null); // character not found } // prepare scratch directory string basePath = Path.Combine(TransferManagerUtil.EnsureTransferPath(log), metadata.Cookie); Directory.CreateDirectory(basePath); // serialize, save, and sign string playerPath = Path.Combine(basePath, snapshot.Player.Id + ".json"); File.WriteAllText(playerPath, JsonConvert.SerializeObject(snapshot.Player, TransferManagerUtil.GetSerializationSettings())); CertificateManager.SignFile(playerPath); foreach (Biota biota in snapshot.PossessedBiotas.Inventory.Union(snapshot.PossessedBiotas.WieldedItems)) { string biotaPath = Path.Combine(basePath, biota.Id + ".json"); File.WriteAllText(biotaPath, JsonConvert.SerializeObject(biota, TransferManagerUtil.GetSerializationSettings())); CertificateManager.SignFile(biotaPath); } string metaPath = Path.Combine(basePath, "packinfo.json"); File.WriteAllText(metaPath, JsonConvert.SerializeObject(metadata, TransferManagerUtil.GetSerializationSettings())); CertificateManager.SignFile(metaPath); CertificateManager.ExportCert(Path.Combine(basePath, "signer.crt")); // compress metadata.FilePath = Path.Combine(TransferManagerUtil.EnsureTransferPath(log), metadata.Cookie + ".zip"); ZipFile.CreateFromDirectory(basePath, metadata.FilePath); // cleanup Directory.Delete(basePath, true); // save DatabaseManager.Shard.SaveCharacterTransfer(new CharacterTransfer() { AccountId = metadata.AccountId, PackageSourceId = metadata.CharacterId, TransferType = (uint)metadata.PackageType, TransferTime = (ulong)Time.GetUnixTime(), Cookie = metadata.Cookie, }, new ReaderWriterLockSlim(), null); return(metadata); }