Skip to content

Commit

Permalink
Optimize transfer
Browse files Browse the repository at this point in the history
I hestitate to make it async since we'd be losing the optimization of bulk actions unless we like grouped the maps by 100 or so. But maybe if I can get Parallel.ForEach to work with those batches, and get them to undo/redo properly, we can
  • Loading branch information
tryashtar committed Aug 25, 2022
1 parent d9c21f1 commit 7b5a24c
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 106 deletions.
6 changes: 3 additions & 3 deletions ImageMap4/ImageMap4.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
<Copyright>Copyright © 2022 tryashtar</Copyright>
<PackageProjectUrl>https://github.com/tryashtar/image-map</PackageProjectUrl>
<RepositoryUrl>https://github.com/tryashtar/image-map</RepositoryUrl>
<AssemblyVersion>4.0.0.0</AssemblyVersion>
<FileVersion>4.0.0.0</FileVersion>
<Version>4.0.0</Version>
<AssemblyVersion>4.1.0.0</AssemblyVersion>
<FileVersion>4.1.0.0</FileVersion>
<Version>4.1.0</Version>
<SignAssembly>false</SignAssembly>
</PropertyGroup>

Expand Down
14 changes: 3 additions & 11 deletions ImageMap4/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,29 +85,22 @@ public MainViewModel()
return;
var overwritten = ExistingMaps.Where(x => ConflictingIDs.Contains(x.Item.ID)).ToList();
var importing = ImportingMaps.ToList();
var structures = ImportingStructures.ToList();
int index = Properties.Settings.Default.InventoryChoice;
if (index < 0 || index >= PlayerList.Count)
index = 1;
var inventory = PlayerList[index];
bool send_structures = CreateStructures;
if (CreateStructures)
SelectedWorld.AddStructures(ImportingStructures, inventory);
ImportingStructures.Clear();
UndoHistory.Perform(() =>
{
SelectedWorld.AddMaps(importing.Select(x => x.Item));
if (send_structures)
{
foreach (var structure in structures)
{
SelectedWorld.AddStructure(structure, inventory);
}
}
foreach (var item in importing)
{
Insert(item, ExistingMaps);
}
RemoveRange(overwritten, ExistingMaps);
ImportingMaps.Clear();
ImportingStructures.Clear();
}, () =>
{
SelectedWorld.RemoveMaps(importing.Select(x => x.Item.ID));
Expand All @@ -118,7 +111,6 @@ public MainViewModel()
Insert(item, ExistingMaps);
}
ImportingMaps.AddRange(importing);
ImportingStructures.AddRange(structures);
});
});
UndoCommand = new RelayCommand(() => UndoHistory.Undo());
Expand Down
2 changes: 1 addition & 1 deletion ImageMap4/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ private void GenerateStructureButton_Click(object sender, RoutedEventArgs e)
StructureWindow = new(new StructureViewModel(new GridMakerViewModel(this.ViewModel, this.ViewModel.ExistingMaps)));
StructureWindow.Owner = this;
StructureWindow.ViewModel.JavaMode = ViewModel.SelectedWorld is JavaWorld;
StructureWindow.ViewModel.OnConfirmed += (s, e) => ViewModel.SelectedWorld.AddStructure(e.grid, e.inventory);
StructureWindow.ViewModel.OnConfirmed += (s, e) => ViewModel.SelectedWorld.AddStructures(new[] { e.grid }, e.inventory);
}
StructureWindow.Show();
StructureWindow.Activate();
Expand Down
133 changes: 70 additions & 63 deletions ImageMap4/Models/BedrockWorld.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,80 +36,87 @@ public BedrockWorld(string folder) : base(folder)
AccessDate = File.GetLastWriteTime(leveldat.Name);
}

public override void AddStructure(StructureGrid structure, IInventory inventory)
public override void AddStructures(IEnumerable<StructureGrid> structures, IInventory inventory)
{
var blockdata = new NbtCompound("block_position_data");
var mapids = structure.ToIDGrid();
for (int y = 0; y < structure.GridHeight; y++)
var db = OpenDB();
using var batch = new WriteBatch();
var items = new List<NbtCompound>();
foreach (var structure in structures)
{
for (int x = 0; x < structure.GridWidth; x++)
var blockdata = new NbtCompound("block_position_data");
var mapids = structure.ToIDGrid();
for (int y = 0; y < structure.GridHeight; y++)
{
long? id = mapids[x, structure.GridHeight - y - 1];
if (id.HasValue)
for (int x = 0; x < structure.GridWidth; x++)
{
blockdata.Add(new NbtCompound((y * structure.GridWidth + x).ToString()) {
new NbtCompound("block_entity_data") {
new NbtString("id", structure.GlowingFrames ? "GlowItemFrame" : "ItemFrame"),
new NbtCompound("Item") {
new NbtString("Name", "minecraft:filled_map"),
new NbtByte("Count", 1),
new NbtCompound("tag") {
new NbtLong("map_uuid", id.Value)
long? id = mapids[x, structure.GridHeight - y - 1];
if (id.HasValue)
{
blockdata.Add(new NbtCompound((y * structure.GridWidth + x).ToString()) {
new NbtCompound("block_entity_data") {
new NbtString("id", structure.GlowingFrames ? "GlowItemFrame" : "ItemFrame"),
new NbtCompound("Item") {
new NbtString("Name", "minecraft:filled_map"),
new NbtByte("Count", 1),
new NbtCompound("tag") {
new NbtLong("map_uuid", id.Value)
}
}
}
}
});
});
}
}
}
}
var nbt = new NbtCompound("") {
new NbtInt("format_version", 1),
new NbtList("size") { new NbtInt(1), new NbtInt(structure.GridHeight), new NbtInt(structure.GridWidth) },
new NbtCompound("structure") {
new NbtList("block_indices") {
new NbtList(Enumerable.Repeat(0, structure.GridHeight * structure.GridWidth).Select(x => new NbtInt(x))),
new NbtList(Enumerable.Repeat(-1, structure.GridHeight * structure.GridWidth).Select(x => new NbtInt(x)))
},
new NbtList("entities"),
new NbtCompound("palette") {
new NbtCompound("default") {
new NbtList("block_palette") {
new NbtCompound() {
new NbtString("name", structure.GlowingFrames ? "minecraft:glow_frame" : "minecraft:frame"),
new NbtCompound("states") {
new NbtInt("facing_direction", 4),
new NbtByte("item_frame_map_bit", 1)
var nbt = new NbtCompound("") {
new NbtInt("format_version", 1),
new NbtList("size") { new NbtInt(1), new NbtInt(structure.GridHeight), new NbtInt(structure.GridWidth) },
new NbtCompound("structure") {
new NbtList("block_indices") {
new NbtList(Enumerable.Repeat(0, structure.GridHeight * structure.GridWidth).Select(x => new NbtInt(x))),
new NbtList(Enumerable.Repeat(-1, structure.GridHeight * structure.GridWidth).Select(x => new NbtInt(x)))
},
new NbtList("entities"),
new NbtCompound("palette") {
new NbtCompound("default") {
new NbtList("block_palette") {
new NbtCompound() {
new NbtString("name", structure.GlowingFrames ? "minecraft:glow_frame" : "minecraft:frame"),
new NbtCompound("states") {
new NbtInt("facing_direction", 4),
new NbtByte("item_frame_map_bit", 1)
}
}
}
},
blockdata
},
blockdata
}
}
},
new NbtList("structure_world_origin") { new NbtInt(0), new NbtInt(0), new NbtInt(0) }
};
var key = "structuretemplate_" + structure.Identifier;
var file = new NbtFile(nbt) { BigEndian = false };
batch.Put(key, file.SaveToBuffer(NbtCompression.None));
var item = new NbtCompound {
new NbtString("Name", "minecraft:structure_block"),
new NbtByte("Count", 1),
new NbtCompound("tag") {
new NbtInt("data", 2),
new NbtString("structureName", structure.Identifier),
new NbtInt("xStructureOffset", 0),
new NbtInt("yStructureOffset", 0),
new NbtInt("zStructureOffset", 0),
new NbtInt("xStructureSize", 1),
new NbtInt("yStructureSize", structure.GridHeight),
new NbtInt("zStructureSize", structure.GridWidth),
new NbtCompound("display") {
new NbtString("Name", $"§r§d{structure.Identifier}§r")
}
}
},
new NbtList("structure_world_origin") { new NbtInt(0), new NbtInt(0), new NbtInt(0) }
};
var key = "structuretemplate_" + structure.Identifier;
var file = new NbtFile(nbt) { BigEndian = false };
var db = OpenDB();
db.Put(key, file.SaveToBuffer(NbtCompression.None));
var item = new NbtCompound {
new NbtString("Name", "minecraft:structure_block"),
new NbtByte("Count", 1),
new NbtCompound("tag") {
new NbtInt("data", 2),
new NbtString("structureName", structure.Identifier),
new NbtInt("xStructureOffset", 0),
new NbtInt("yStructureOffset", 0),
new NbtInt("zStructureOffset", 0),
new NbtInt("xStructureSize", 1),
new NbtInt("yStructureSize", structure.GridHeight),
new NbtInt("zStructureSize", structure.GridWidth),
new NbtCompound("display") {
new NbtString("Name", $"§r§d{structure.Identifier}§r")
}
}
};
inventory.AddItem(item);
};
items.Add(item);
}
db.Write(batch);
inventory.AddItems(items);
}

public override IEnumerable<IInventory> GetInventories()
Expand Down
45 changes: 26 additions & 19 deletions ImageMap4/Models/Inventory.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using fNbt;
using LevelDBWrapper;
using System;
using System.Collections.Generic;
using System.ComponentModel;
Expand All @@ -16,14 +17,14 @@ namespace ImageMap4;
public interface IInventory
{
string Name { get; }
void AddItem(NbtCompound item);
void AddItems(IEnumerable<NbtCompound> items);
}

// I would prefer if we just used null for this, but we need something to show up for "Name" in the combobox
public class NoInventory : IInventory
{
public string Name => "None";
public void AddItem(NbtCompound item) { }
public void AddItems(IEnumerable<NbtCompound> items) { }
}

public class JavaInventory : IInventory, INotifyPropertyChanged
Expand Down Expand Up @@ -84,18 +85,21 @@ public JavaInventory(string name, string file, NbtPath path)
private static readonly HttpClient Client = new();
public event PropertyChangedEventHandler? PropertyChanged;

public void AddItem(NbtCompound item)
public void AddItems(IEnumerable<NbtCompound> items)
{
var file = new NbtFile(FilePath);
var items = DataPath.Traverse(file.GetRootTag<NbtCompound>()).First() as NbtList;
var occupied_slots = items.Cast<NbtCompound>().Select(x => x.Get<NbtByte>("Slot").Value).ToHashSet();
for (byte i = 0; i < 36; i++)
var inventory = DataPath.Traverse(file.GetRootTag<NbtCompound>()).First() as NbtList;
var occupied_slots = inventory.Cast<NbtCompound>().Select(x => x.Get<NbtByte>("Slot").Value).ToHashSet();
foreach (var item in items)
{
if (!occupied_slots.Contains(i))
for (byte i = 0; i < 36; i++)
{
item.Add(new NbtByte("Slot", i));
items.Add(item);
break;
if (!occupied_slots.Contains(i))
{
item.Add(new NbtByte("Slot", i));
inventory.Add(item);
break;
}
}
}
file.SaveToFile(FilePath, file.FileCompression);
Expand All @@ -115,22 +119,25 @@ public BedrockInventory(string name, BedrockWorld world, string key)
Key = key;
}

public void AddItem(NbtCompound item)
public void AddItems(IEnumerable<NbtCompound> items)
{
using var db = World.OpenDB();
var db = World.OpenDB();
var bytes = db.Get(Key);
var file = new NbtFile() { BigEndian = false };
file.LoadFromBuffer(bytes, 0, bytes.Length, NbtCompression.None);
var items = file.GetRootTag<NbtCompound>().Get<NbtList>("Inventory");
var inventory = file.GetRootTag<NbtCompound>().Get<NbtList>("Inventory");
// remember bedrock saves empty slots
foreach (NbtCompound slot in items.ToList())
foreach (var item in items)
{
if (slot.Get<NbtByte>("Count").Value == 0)
foreach (NbtCompound slot in inventory.ToList())
{
items.Remove(slot);
item.Add((NbtByte)slot.Get<NbtByte>("Slot").Clone());
items.Add(item);
break;
if (slot.Get<NbtByte>("Count").Value == 0)
{
inventory.Remove(slot);
item.Add((NbtByte)slot.Get<NbtByte>("Slot").Clone());
inventory.Add(item);
break;
}
}
}
bytes = file.SaveToBuffer(NbtCompression.None);
Expand Down
21 changes: 13 additions & 8 deletions ImageMap4/Models/JavaWorld.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,20 @@ public JavaWorld(string folder) : base(folder)
AccessDate = File.GetLastWriteTime(leveldat.FileName);
}

public override void AddStructure(StructureGrid structure, IInventory inventory)
public override void AddStructures(IEnumerable<StructureGrid> structures, IInventory inventory)
{
var nbt = Version.CreateStructureFile(structure);
var path = Version.StructureFileLocation(Folder, structure.Identifier);
var file = new NbtFile(nbt) { BigEndian = true };
Directory.CreateDirectory(Path.GetDirectoryName(path));
file.SaveToFile(path, NbtCompression.GZip);
var item = Version.MakeStructureItem(structure);
inventory.AddItem(item);
var items = new List<NbtCompound>();
foreach (var structure in structures)
{
var nbt = Version.CreateStructureFile(structure);
var path = Version.StructureFileLocation(Folder, structure.Identifier);
var file = new NbtFile(nbt) { BigEndian = true };
Directory.CreateDirectory(Path.GetDirectoryName(path));
file.SaveToFile(path, NbtCompression.GZip);
var item = Version.MakeStructureItem(structure);
items.Add(item);
}
inventory.AddItems(items);
}

public override IEnumerable<IInventory> GetInventories()
Expand Down
2 changes: 1 addition & 1 deletion ImageMap4/Models/World.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public World(string folder)
public abstract IAsyncEnumerable<Map> GetMapsAsync();
public abstract void AddMaps(IEnumerable<Map> maps);
public abstract void RemoveMaps(IEnumerable<long> ids);
public abstract void AddStructure(StructureGrid structure, IInventory inventory);
public abstract void AddStructures(IEnumerable<StructureGrid> structures, IInventory inventory);
public abstract IEnumerable<IInventory> GetInventories();
protected abstract void ProcessImage(Image<Rgba32> image, ProcessSettings settings);
protected abstract byte[] EncodeColors(Image<Rgba32> image);
Expand Down

0 comments on commit 7b5a24c

Please sign in to comment.