private void writeList(KeyValuePair <string, List <GameObject> > entry) { string outputFile = Path.Combine(outputDirectory, entry.Key + ".json"); List <GameObject> filteredClasses = entry.Value; MapData mapData = LatLonCalculator.ForSave(arkSavegame); CommonFunctions.WriteJson(outputFile, (generator, writingOptions) => { if (statistics) { generator.WriteStartObject(); generator.WriteField("count", filteredClasses.Count); List <int> summaryStatistics = filteredClasses.Where(o => o.IsWild()) .Select(a => a.GetBaseLevel(arkSavegame)).ToList(); if (summaryStatistics.Any()) { generator.WriteField("wildMin", summaryStatistics.Min()); generator.WriteField("wildMax", summaryStatistics.Max()); generator.WriteField("wildAverage", summaryStatistics.Average()); } List <int> tamedBaseStatistics = filteredClasses.Where(o => o.IsTamed()) .Select(a => a.GetBaseLevel(arkSavegame)).ToList(); if (tamedBaseStatistics.Any()) { generator.WriteField("tamedBaseMin", tamedBaseStatistics.Min()); generator.WriteField("tamedBaseMax", tamedBaseStatistics.Max()); generator.WriteField("tamedBaseAverage", tamedBaseStatistics.Average()); } List <int> tamedFullStatistics = filteredClasses.Where(o => o.IsTamed()) .Select(a => a.GetFullLevel(arkSavegame)).ToList(); if (tamedFullStatistics.Any()) { generator.WriteField("tamedFullMin", tamedFullStatistics.Min()); generator.WriteField("tamedFullMax", tamedFullStatistics.Max()); generator.WriteField("tamedFullAverage", tamedFullStatistics.Average()); } generator.WritePropertyName("dinos"); generator.WriteStartArray(); } else { generator.WriteStartArray(); } CustomDataContext context = new CustomDataContext { MapData = mapData, ObjectContainer = container, Savegame = arkSavegame }; foreach (GameObject creatureObject in filteredClasses) { Creature creature = new Creature(creatureObject, container); generator.WriteStartObject(); creature.writeAllProperties(generator, context, writeAllFields); if (!string.IsNullOrWhiteSpace(inventory)) { creature.writeInventory(generator, context, writeAllFields, inventory == "summary"); } generator.WriteEndObject(); } generator.WriteEndArray(); if (statistics) { generator.WriteEndObject(); } }, writingOptions); }
protected override void RunCommand(IEnumerable <string> args) { List <string> argsList = args.ToList(); if (showCommandHelp(argsList)) { return; } bool itemsLong = withItems == "long"; bool inventoryLong = withInventory == "long"; bool tamedLong = tamed == "long"; bool structuresLong = withStructures == "long"; Stopwatch stopwatch = new Stopwatch(GlobalOptions.UseStopWatch); bool mapNeeded = withItems != null || tamed != null || withStructures != null || withInventory != null; if (!GlobalOptions.Quiet && mapNeeded) { Console.WriteLine("Need to load map, this may take some time..."); } string saveGame = argsList.Count > 0 ? argsList[0] : Path.Combine(GlobalOptions.ArkToolsConfiguration.BasePath, GlobalOptions.ArkToolsConfiguration.ArkSavegameFilename); string outputDirectory = argsList.Count > 1 ? argsList[1] : Path.Combine(GlobalOptions.ArkToolsConfiguration.BasePath, GlobalOptions.ArkToolsConfiguration.TribesPath); string saveDir = Path.GetDirectoryName(saveGame); Dictionary <int, HashSet <TribeBase> > baseMap; CustomDataContext context = new CustomDataContext(); if (mapNeeded) { ArkDataManager.LoadData(GlobalOptions.Language); ArkSavegame mapSave = new ArkSavegame().ReadBinary <ArkSavegame>(saveGame, ReadingOptions.Create().WithBuildComponentTree(true)); context.Savegame = mapSave; context.MapData = LatLonCalculator.ForSave(context.Savegame); stopwatch.Stop("Loading map data"); if (withBases) { baseMap = new Dictionary <int, HashSet <TribeBase> >(); foreach (GameObject gameObject in mapSave) { // Skip items and stuff without a location if (gameObject.IsItem || gameObject.Location == null) { continue; } string signText = gameObject.GetPropertyValue <string>("SignText"); long? targetingTeam = gameObject.GetPropertyValue <long?>("TargetingTeam"); if (signText != null && targetingTeam != null) { // Might be a 'Base' sign MatchCollection matcher = basePattern.Matches(signText); if (matcher.Any()) { // Found a base sign, add it to the set, automatically replacing duplicates int tribeId = (int)targetingTeam; LocationData location = gameObject.Location; string baseName = matcher[1].Value; float size = float.Parse(matcher[2].Value); TribeBase tribeBase = new TribeBase(baseName, location.X, location.Y, location.Z, size); if (!baseMap.ContainsKey(tribeId)) { baseMap[tribeId] = new HashSet <TribeBase>(); } baseMap[tribeId].Add(tribeBase); } } } stopwatch.Stop("Collecting bases"); } else { baseMap = null; } if (mapSave.HibernationEntries.Any() && tamed != null) { List <GameObject> combinedObjects = context.Savegame.Objects.ToList(); foreach (HibernationEntry entry in context.Savegame.HibernationEntries) { ObjectCollector collector = new ObjectCollector(entry, 1); combinedObjects.AddRange(collector.Remap(combinedObjects.Count)); } context.ObjectContainer = new GameObjectContainer(combinedObjects); } else { context.ObjectContainer = mapSave; } } else { baseMap = null; } List <Action> tasks = GlobalOptions.Parallel ? new List <Action>() : null; void mapWriter(JsonTextWriter generator, int tribeId) { if (!mapNeeded) { return; } List <GameObject> structures = new List <GameObject>(); List <GameObject> creatures = new List <GameObject>(); List <Item> items = new List <Item>(); List <Item> blueprints = new List <Item>(); List <DroppedItem> droppedItems = new List <DroppedItem>(); // Apparently there is a behavior in ARK causing certain structures to exist twice within a save HashSet <ArkName> processedList = new HashSet <ArkName>(); // Bases HashSet <TribeBase> bases = withBases ? baseMap[tribeId] : null; foreach (GameObject gameObject in context.ObjectContainer) { if (gameObject.IsItem) { continue; } int targetingTeam = gameObject.GetPropertyValue <int>("TargetingTeam", defaultValue: -1); if (targetingTeam == -1) { continue; } TeamType teamType = TeamTypes.ForTeam(targetingTeam); if (tribeId == -1 && teamType != TeamType.Player) { continue; } if (tribeId == 0 && teamType != TeamType.NonPlayer) { continue; } if (tribeId > 0 && tribeId != targetingTeam) { continue; } // Determine base if we have bases TribeBase tribeBase; if (bases != null && gameObject.Location != null) { TribeBase matchedBase = null; foreach (TribeBase potentialBase in bases) { if (potentialBase.InsideBounds(gameObject.Location)) { matchedBase = potentialBase; break; } } tribeBase = matchedBase; } else { tribeBase = null; } if (gameObject.IsCreature()) { if (!processedList.Contains(gameObject.Names[0])) { if (tribeBase != null) { tribeBase.Creatures.Add(gameObject); } else { creatures.Add(gameObject); } processedList.Add(gameObject.Names[0]); } else { // Duped Creature continue; } } else if (!gameObject.IsPlayer() && !gameObject.IsWeapon() && !gameObject.IsDroppedItem()) { // LinkedPlayerDataID: Players ain't structures // AssociatedPrimalItem: Items equipped by sleeping players // MyItem: dropped item if (!processedList.Contains(gameObject.Names[0])) { if (tribeBase != null) { tribeBase.Structures.Add(gameObject); } else { structures.Add(gameObject); } processedList.Add(gameObject.Names[0]); } else { // Duped Structure continue; } } else { if (!processedList.Contains(gameObject.Names[0])) { processedList.Add(gameObject.Names[0]); } else { // Duped Player or dropped Item or weapon continue; } } void itemHandler(ObjectReference itemReference) { GameObject item = itemReference.GetObject(context.Savegame); if (item != null && !Item.isDefaultItem(item)) { if (processedList.Contains(item.Names[0])) { // happens for players having items in their quick bar return; } processedList.Add(item.Names[0]); if (item.HasAnyProperty("bIsBlueprint")) { if (tribeBase != null) { tribeBase.Blueprints.Add(new Item(item)); } else { blueprints.Add(new Item(item)); } } else { if (tribeBase != null) { tribeBase.Items.Add(new Item(item)); } else { items.Add(new Item(item)); } } } } void droppedItemHandler(GameObject droppedItemObject) { DroppedItem droppedItem = new DroppedItem(droppedItemObject, context.Savegame); if (tribeBase != null) { tribeBase.DroppedItems.Add(droppedItem); } else { droppedItems.Add(droppedItem); } } if (withItems != null && withInventory == null) { foreach (GameObject inventory in gameObject.Components.Values) { if (!inventory.IsInventory()) { continue; } List <ObjectReference> inventoryItems = inventory.GetPropertyValue <IArkArray, ArkArrayObjectReference>("InventoryItems"); foreach (ObjectReference itemReference in inventoryItems ?? Enumerable.Empty <ObjectReference>()) { itemHandler(itemReference); } List <ObjectReference> equippedItems = inventory.GetPropertyValue <IArkArray, ArkArrayObjectReference>("EquippedItems"); foreach (ObjectReference itemReference in equippedItems ?? Enumerable.Empty <ObjectReference>()) { itemHandler(itemReference); } } } ObjectReference myItem = gameObject.GetPropertyValue <ObjectReference>("MyItem"); if (myItem != null) { if (withItems != null && withInventory == null) { itemHandler(myItem); } else if (withInventory != null) { droppedItemHandler(gameObject); } } } void writeStructures(IEnumerable <GameObject> structList) { if (withStructures == null) { return; } generator.WriteArrayFieldStart("structures"); if (structuresLong) { foreach (GameObject structureObject in structList) { Structure structure = new Structure(structureObject, context.Savegame); generator.WriteStartObject(); structure.writeAllProperties(generator, context, writeAllFields); if (withInventory != null) { structure.writeInventory(generator, context, writeAllFields, !inventoryLong); } generator.WriteEndObject(); } } else { Dictionary <ArkName, long> structMap = structList.GroupBy(o => o.ClassName).ToDictionary(objects => objects.Key, objects => objects.LongCount()); foreach (KeyValuePair <ArkName, long> entry in structMap.OrderByDescending(pair => pair.Value)) { generator.WriteStartObject(); string name = entry.Key.ToString(); if (ArkDataManager.HasStructure(name)) { name = ArkDataManager.GetStructure(name).Name; } generator.WriteField("name", name); generator.WriteField("count", entry.Value); generator.WriteEndObject(); } } generator.WriteEndArray(); } void writeCreatures(List <GameObject> creaList) { if (tamed == null) { return; } generator.WriteArrayFieldStart("tamed"); if (tamedLong) { foreach (GameObject creatureObject in creaList) { Creature creature = new Creature(creatureObject, context.Savegame); generator.WriteStartObject(); creature.writeAllProperties(generator, context, writeAllFields); if (withInventory != null) { creature.writeInventory(generator, context, writeAllFields, !inventoryLong); } generator.WriteEndObject(); } } else { Dictionary <ArkName, long> creaMap = creaList.GroupBy(o => o.ClassName).ToDictionary(objects => objects.Key, objects => objects.LongCount()); foreach (KeyValuePair <ArkName, long> entry in creaMap.OrderByDescending(pair => pair.Value)) { generator.WriteStartObject(); string name = entry.Key.ToString(); if (ArkDataManager.HasCreature(name)) { name = ArkDataManager.GetCreature(name).Name; } generator.WriteField("name", name); generator.WriteField("count", entry.Value); generator.WriteEndObject(); } } generator.WriteEndArray(); } void writeDroppedItems(List <DroppedItem> droppedList) { if (withInventory == null) { return; } generator.WriteArrayFieldStart("droppedItems"); foreach (DroppedItem droppedItem in droppedList) { generator.WriteStartObject(); droppedItem.writeAllProperties(generator, context, writeAllFields); droppedItem.writeInventory(generator, context, writeAllFields, !inventoryLong); generator.WriteEndObject(); } generator.WriteEndArray(); } if (withBases && bases != null) { generator.WriteArrayFieldStart("bases"); foreach (TribeBase tribeBase in bases) { generator.WriteStartObject(); generator.WriteField("name", tribeBase.Name); generator.WriteField("x", tribeBase.X); generator.WriteField("y", tribeBase.Y); generator.WriteField("z", tribeBase.Z); generator.WriteField("lat", context.MapData.CalculateLat(tribeBase.Y)); generator.WriteField("lon", context.MapData.CalculateLon(tribeBase.X)); generator.WriteField("radius", tribeBase.Size); writeCreatures(tribeBase.Creatures); writeStructures(tribeBase.Structures); writeDroppedItems(tribeBase.DroppedItems); if (itemsLong) { generator.WritePropertyName("items"); Inventory.writeInventoryLong(generator, context, tribeBase.Items, writeAllFields); generator.WritePropertyName("blueprints"); Inventory.writeInventoryLong(generator, context, tribeBase.Blueprints, writeAllFields); } else { generator.WritePropertyName("items"); Inventory.writeInventorySummary(generator, tribeBase.Items); generator.WritePropertyName("blueprints"); Inventory.writeInventorySummary(generator, tribeBase.Blueprints); } generator.WriteEndObject(); } generator.WriteStartObject(); } writeCreatures(creatures); writeStructures(structures); writeDroppedItems(droppedItems); if (itemsLong) { generator.WritePropertyName("items"); Inventory.writeInventoryLong(generator, context, items, writeAllFields); generator.WritePropertyName("blueprints"); Inventory.writeInventoryLong(generator, context, blueprints, writeAllFields); } else { generator.WritePropertyName("items"); Inventory.writeInventorySummary(generator, items); generator.WritePropertyName("blueprints"); Inventory.writeInventorySummary(generator, blueprints); } if (withBases && bases != null) { generator.WriteEndObject(); generator.WriteEndArray(); } } foreach (string path in Directory.EnumerateFiles(saveDir).Where(path => tribePattern.IsMatch(path))) { Action task = () => { try { Tribe tribe = new Tribe(path, ReadingOptions.Create()); string tribeFileName = tribe.tribeId + ".json"; string tribePath = Path.Combine(outputDirectory, tribeFileName); CommonFunctions.WriteJson(tribePath, (generator, writingOptions) => { generator.WriteStartObject(); tribe.writeAllProperties(generator, context, writeAllFields); mapWriter(generator, tribe.tribeId); generator.WriteEndObject(); }, writingOptions); } catch (Exception ex) { Console.Error.WriteLine("Found potentially corrupt ArkTribe: " + path); if (GlobalOptions.Verbose) { Console.Error.WriteLine(ex.Message); Console.Error.WriteLine(ex.StackTrace); } } }; if (tasks != null) { tasks.Add(task); } else { task(); } } if (tasks != null) { Parallel.ForEach(tasks, task => task()); } if (tribeless) { string tribePath = Path.Combine(outputDirectory, "tribeless.json"); CommonFunctions.WriteJson(tribePath, (generator, writingOptions) => { generator.WriteStartObject(); mapWriter(generator, -1); generator.WriteEndObject(); }, writingOptions); } if (nonPlayers) { string tribePath = Path.Combine(outputDirectory, "non-players.json"); CommonFunctions.WriteJson(tribePath, (generator, writingOptions) => { generator.WriteStartObject(); mapWriter(generator, 0); generator.WriteEndObject(); }, writingOptions); } stopwatch.Stop("Loading tribes and writing info"); stopwatch.Print(); }
protected override void RunCommand(IEnumerable <string> args) { List <string> argsList = args.ToList(); if (showCommandHelp(argsList)) { return; } ArkDataManager.LoadData(GlobalOptions.Language); Stopwatch stopwatch = new Stopwatch(GlobalOptions.UseStopWatch); bool mapNeeded = !string.IsNullOrEmpty(inventory) || positions; if (!GlobalOptions.Quiet && mapNeeded) { Console.WriteLine("Need to load map, this may take some time..."); } string saveGame = argsList.Count > 0 ? argsList[0] : Path.Combine(GlobalOptions.ArkToolsConfiguration.BasePath, GlobalOptions.ArkToolsConfiguration.ArkSavegameFilename); string outputDirectory = argsList.Count > 1 ? argsList[1] : Path.Combine(GlobalOptions.ArkToolsConfiguration.BasePath, GlobalOptions.ArkToolsConfiguration.PlayersPath); string saveDir = Path.GetDirectoryName(saveGame); CustomDataContext context = new CustomDataContext(); if (mapNeeded) { ArkSavegame mapSave = new ArkSavegame().ReadBinary <ArkSavegame>(saveGame, ReadingOptions.Create().WithBuildComponentTree(true)); context.ObjectContainer = mapSave; context.Savegame = mapSave; context.MapData = LatLonCalculator.ForSave(mapSave); stopwatch.Stop("Loading map data"); } Dictionary <int, Tribe> tribes = new Dictionary <int, Tribe>(); List <Action> tasks = GlobalOptions.Parallel ? new List <Action>() : null; foreach (string path in Directory.EnumerateFiles(saveDir).Where(path => profilePattern.IsMatch(Path.GetFileName(path)))) { if (maxAge.HasValue) { DateTimeOffset fileTime = File.GetLastWriteTimeUtc(path); if (fileTime < DateTimeOffset.UtcNow.AddSeconds(-maxAge.Value)) { continue; } } Action task = () => { try { Player player = new Player(path, context, ReadingOptions.Create()); long playerId = player.playerDataId; string playerFileName; if (naming == "steamid" || string.IsNullOrWhiteSpace(naming)) { playerFileName = player.uniqueId.NetId + ".json"; } else if (naming == "playerid") { playerFileName = playerId + ".json"; } else { throw new Exception("Invalid value for parameter naming."); } if (player.tribeId != 0) { if (!tribes.ContainsKey(player.tribeId)) { string tribePath = Path.Combine(saveDir, player.tribeId + ".arktribe"); if (File.Exists(tribePath)) { try { tribes[player.tribeId] = new Tribe(tribePath, ReadingOptions.Create()); } catch (Exception ex) { // Either the header didn't match or one of the properties is missing Console.Error.WriteLine("Found potentially corrupt ArkTribe: " + tribePath); if (GlobalOptions.Verbose) { Console.Error.WriteLine(ex.Message); Console.Error.WriteLine(ex.StackTrace); } } } } } string playerPath = Path.Combine(outputDirectory, playerFileName); void writePlayer(JsonTextWriter writer, WritingOptions options) { writer.WriteStartObject(); // Player data player.WriteAllProperties(writer, context, writeAllFields, noPrivacy); // Inventory if (!string.IsNullOrEmpty(inventory)) { player.WriteInventory(writer, context, writeAllFields, inventory.ToLowerInvariant() != "long"); } // Tribe if (player.tribeId != 0 && tribes.TryGetValue(player.tribeId, out Tribe tribe)) { writer.WriteField("tribeName", tribe.tribeName); if (writeAllFields || tribe.ownerPlayerDataId == playerId) { writer.WriteField("tribeOwner", tribe.ownerPlayerDataId == playerId); } if (writeAllFields || tribe.tribeAdmins.Contains((int)playerId)) { writer.WriteField("tribeAdmin", tribe.tribeAdmins.Contains((int)playerId)); } } writer.WriteEndObject(); } CommonFunctions.WriteJson(playerPath, writePlayer, writingOptions); } catch (Exception ex) when(!Debugger.IsAttached) { Console.Error.WriteLine("Found potentially corrupt ArkProfile: " + path); if (GlobalOptions.Verbose) { Console.Error.WriteLine(ex.Message); Console.Error.WriteLine(ex.StackTrace); } } }; if (tasks != null) { tasks.Add(task); } else { task(); } } if (tasks != null) { Parallel.ForEach(tasks, task => task()); } stopwatch.Stop("Loading profiles and writing info"); stopwatch.Print(); }
public void LoadSavegame(string path) { Savegame = new ArkSavegame().ReadBinary <ArkSavegame>(path, ReadingOptions.Create() // Skip things like NPCZoneVolume and non-instanced objects .WithObjectFilter(obj => !obj.FromDataFile && (obj.Names.Count > 1 || obj.Names[0].Instance > 0)) .WithBuildComponentTree(true)); MapData = LatLonCalculator.ForSave(Savegame); if (Savegame.HibernationEntries.Any()) { List <GameObject> combinedObjects = new List <GameObject>(Savegame.Objects); foreach (HibernationEntry entry in Savegame.HibernationEntries) { ObjectCollector collector = new ObjectCollector(entry, 1); combinedObjects.AddRange(collector.Remap(combinedObjects.Count)); } ObjectContainer = new GameObjectContainer(combinedObjects); } else { ObjectContainer = Savegame; } foreach (GameObject gameObject in ObjectContainer) { if (gameObject.FromDataFile || (gameObject.Names.Count == 1 && gameObject.Names[0].Instance == 0)) { // Skip things like NPCZoneVolume and non-instanced objects } else if (gameObject.IsInventory()) { InventoryMap[gameObject.Id] = new Inventory(gameObject); } else { if (!nameObjectMap.ContainsKey(gameObject.Names[0])) { nameObjectMap[gameObject.Names[0]] = gameObject.Id; if (gameObject.IsItem) { ItemMap[gameObject.Id] = new Item(gameObject); } else if (gameObject.IsCreature()) { CreatureMap[gameObject.Id] = new Creature(gameObject, Savegame); } else if (gameObject.Location != null && !gameObject.IsPlayer() && !gameObject.IsDroppedItem() && !gameObject.IsWeapon()) { // Skip players, weapons and items on the ground // is (probably) a structure StructureMap[gameObject.Id] = new Structure(gameObject, Savegame); } else if (gameObject.IsDroppedItem()) { // dropped Item DroppedItemMap[gameObject.Id] = new DroppedItem(gameObject, Savegame); } } } } }