private void PopulateBlockEntities(IWorldParser parser, WorldInfoModel worldInfo) { var chests = new List <ChestInfoModel>(); var signs = new List <SignInfoModel>(); foreach (var region in parser.GetRegions(TARGET_REGION)) { foreach (var column in region.Columns) { foreach (var chunk in column.Chunks) { foreach (var block in chunk.Blocks) { if (block is Chest) { var chest = block as Chest; chests.Add(new ChestInfoModel { X = chest.WorldX, Y = chest.WorldY, Z = chest.WorldZ, Items = chest.Items.Select(i => new InventoryItemModel { Name = i.Id, Count = i.Count }).ToList() }); } else if (block is Sign) { var sign = block as Sign; string textRendered = string.Join("\n", sign.TextLines.Select(s => { if (s.Contains('{') && s.Contains('}') && s.Contains("\"text\":")) { var obj = JObject.Parse(s); return(obj.GetValue("text").ToString()); } else { return(s); } })); signs.Add(new SignInfoModel { X = sign.WorldX, Y = sign.WorldY, Z = sign.WorldZ, Text = textRendered }); } } } } } worldInfo.Chests = chests; worldInfo.Signs = signs; }
public RenderReturnModel GenerateBlockBitmap(RegionType regionType) { var regions = _parser.GetRegions(regionType).ToList(); var minX = regions.Min(r => r.X); var maxX = regions.Max(r => r.X); var minZ = regions.Min(r => r.Z); var maxZ = regions.Max(r => r.Z); var imageWidth = (maxX - minX + 1) * 32 * 16; var imageHeight = (maxZ - minZ + 1) * 32 * 16; var dX = minX * 32 * 16; var dZ = minZ * 32 * 16; var bitmap = new Image <Rgba32>(imageWidth, imageHeight); var unknownBlocks = new Dictionary <string, int>(); int actualMinX = Int32.MaxValue, actualMaxX = 0, actualMinZ = Int32.MaxValue, actualMaxZ = 0; bool blockRendered = false; //foreach (var region in regions) Parallel.ForEach(regions, region => { var regionCache = _cacheProvider?.GetRegionMapCache(region.X, region.Z) ?? new RegionMapCache(); var populated = new List <(int, int)>(16 * 16); var lastBaseColor = new Dictionary <(int, int), Color>(16 * 16); foreach (var column in region.Columns) { if (regionCache.ColumnCacheIsStale(column.XWorld, column.ZWorld, column.Modified)) { regionCache.UpdateTimestamp(column.XWorld, column.ZWorld, column.Modified); populated.Clear(); lastBaseColor.Clear(); foreach (var chunk in column.Chunks.OrderByDescending(c => c.YOrder)) { foreach (var block in chunk.Blocks.OrderByDescending(b => b.WorldY)) { //In image coordinates var x = block.WorldX - dX; var z = block.WorldZ - dZ; //Update image edge extents if neccessary actualMinX = Math.Min(actualMinX, x); actualMaxX = Math.Max(actualMaxX, x); actualMinZ = Math.Min(actualMinZ, z); actualMaxZ = Math.Max(actualMaxZ, z); //Do not draw air if (block.BaseId == 0) { continue; } //Coordinates populated and completed, ignore if (populated.Contains((x, z))) { continue; } var baseColor = _textureProvider.GetColorForBlock(block, Color.HotPink); if (baseColor == Color.HotPink) { var key = $"{block.Code} ({block.Id})"; unknownBlocks[key] = unknownBlocks.ContainsKey(key) ? unknownBlocks[key] + 1 : 1; } Color color; if (lastBaseColor.ContainsKey((x, z))) { //Handle non top blocks in transparent stacks if (baseColor == lastBaseColor[(x, z)]) { continue; } if (!TransparentBlocks.Contains(block.BaseId) && block.SkyLight == 0 && block.BlockLight == 0 && bitmap[x, z].A > 0) { populated.Add((x, z)); continue; } color = BlendBlocks(bitmap[x, z], baseColor, Math.Max(block.SkyLight, (byte)(block.BlockLight / 2))); lastBaseColor[(x, z)] = baseColor; } else { //First block in stack, it can either be transparent or not color = baseColor; } //Handle blocklight; ie torches, lava etc //if (block.BlockLight > 0) //{ // color = BlendBlocks(color, _textureProvider.GetBlocklightColor(), (byte)(block.BlockLight / 2)); //} if (TransparentBlocks.Contains(block.BaseId)) { //This is a transparent block stack, save this color for future (next block in -Y order) reference. //This is also the flag that a stack is transparent lastBaseColor[(x, z)] = baseColor;