Bladeren bron

ore generation optimization

JasonWang 6 jaren geleden
bovenliggende
commit
95d477fc72
20 gewijzigde bestanden met toevoegingen van 745 en 75 verwijderingen
  1. 64 0
      src/MineCase.Core/BatchBlockChange.cs
  2. 5 0
      src/MineCase.Core/Block/BlockStateExtensions.cs
  3. 1 0
      src/MineCase.Core/World/Plants/PlantsType.cs
  4. 63 0
      src/MineCase.Core/World/Position.cs
  5. 48 0
      src/MineCase.Server.Grains/World/ChunkColumnGrain.cs
  6. 10 0
      src/MineCase.Server.Grains/World/Decoration/Biomes/BiomeJungleDecoratorGrain.cs
  7. 9 0
      src/MineCase.Server.Grains/World/Decoration/Biomes/BiomePlainsDecoratorGrain.cs
  8. 9 4
      src/MineCase.Server.Grains/World/Decoration/Mine/MinableGeneratorGrain.cs
  9. 46 0
      src/MineCase.Server.Grains/World/Decoration/Plants/AbstractTreeGeneratorGrain.cs
  10. 173 0
      src/MineCase.Server.Grains/World/Decoration/Plants/HugeTreeGeneratorGrain.cs
  11. 156 0
      src/MineCase.Server.Grains/World/Decoration/Plants/JungleGeneratorGrain.cs
  12. 3 0
      src/MineCase.Server.Grains/World/Decoration/Plants/PlantsGeneratorGrain.cs
  13. 10 21
      src/MineCase.Server.Grains/World/Decoration/Plants/Taiga2GeneratorGrain.cs
  14. 10 21
      src/MineCase.Server.Grains/World/Decoration/Plants/TaigaGeneratorGrain.cs
  15. 18 29
      src/MineCase.Server.Grains/World/Decoration/Plants/TreeGeneratorGrain.cs
  16. 10 0
      src/MineCase.Server.Interfaces/World/Decoration/Biomes/IBiomeJungleDecorator.cs
  17. 10 0
      src/MineCase.Server.Interfaces/World/Decoration/Plants/IHugeTreeGenerator.cs
  18. 10 0
      src/MineCase.Server.Interfaces/World/Decoration/Plants/IJungleGenerator.cs
  19. 2 0
      src/MineCase.Server.Interfaces/World/IChunkColumn.cs
  20. 88 0
      src/MineCase.Server.Interfaces/World/WorldExtensions.cs

+ 64 - 0
src/MineCase.Core/BatchBlockChange.cs

@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using MineCase.Block;
+using MineCase.World;
+
+namespace MineCase
+{
+    public class BlockStateChange
+    {
+        public BlockWorldPos Position { get; set; }
+
+        public BlockState State { get; set; }
+
+        public List<BlockState> Condition { get; set; }
+    }
+
+    public class BatchBlockChange
+    {
+        protected List<BlockStateChange> blockChanges;
+
+        public BatchBlockChange()
+        {
+            blockChanges = new List<BlockStateChange>();
+        }
+
+        public BlockState this[int x, int y, int z]
+        {
+            set
+            {
+                blockChanges.Add(new BlockStateChange { Position = new BlockWorldPos(x, y, z), State = value, Condition = new List<BlockState>() });
+            }
+        }
+
+        public void SetIf(BlockWorldPos pos, BlockState state, BlockState ifState)
+        {
+            blockChanges.Add(new BlockStateChange { Position = pos, State = state, Condition = new List<BlockState> { ifState } } );
+        }
+
+        public void SetIf(BlockWorldPos pos, BlockState state, List<BlockState> ifState)
+        {
+            blockChanges.Add(new BlockStateChange { Position = pos, State = state, Condition = ifState });
+        }
+
+        public Dictionary<ChunkWorldPos, List<BlockStateChange>> GetByPartition()
+        {
+            Dictionary<ChunkWorldPos, List<BlockStateChange>> ret = new Dictionary<ChunkWorldPos, List<BlockStateChange>>();
+            foreach (BlockStateChange blockChange in blockChanges)
+            {
+                var chunkPos = blockChange.Position.ToChunkWorldPos();
+                if (ret.ContainsKey(chunkPos))
+                {
+                    ret[chunkPos].Add(blockChange);
+                }
+                else
+                {
+                    ret[chunkPos] = new List<BlockStateChange>();
+                }
+            }
+
+            return ret;
+        }
+    }
+}

+ 5 - 0
src/MineCase.Core/Block/BlockStateExtensions.cs

@@ -60,6 +60,11 @@ namespace MineCase.Block
             return state.IsSameId(BlockStates.Leaves()) || state.IsSameId(BlockStates.Leaves2());
         }
 
+        public static bool IsWood(this BlockState state)
+        {
+            return state.IsSameId(BlockStates.Wood()) || state.IsSameId(BlockStates.Wood2());
+        }
+
         public static bool CanMobStand(this BlockState state)
         {
             return !state.IsSameId(BlockStates.Air()) &&

+ 1 - 0
src/MineCase.Core/World/Plants/PlantsType.cs

@@ -13,6 +13,7 @@ namespace MineCase.World.Plants
         Oak,
         Spruce,
         Birch,
+        Jungle,
 
         Sunflower,
         Lilac,

+ 63 - 0
src/MineCase.Core/World/Position.cs

@@ -100,6 +100,69 @@ namespace MineCase.World
         }
     }
 
+    public static class BlockWorldPosExtension
+    {
+        public static BlockWorldPos Down(this BlockWorldPos pos)
+        {
+            return new BlockWorldPos(pos.X, pos.Y - 1, pos.Z);
+        }
+
+        public static BlockWorldPos Up(this BlockWorldPos pos)
+        {
+            return new BlockWorldPos(pos.X, pos.Y + 1, pos.Z);
+        }
+
+        public static BlockWorldPos East(this BlockWorldPos pos)
+        {
+            return new BlockWorldPos(pos.X + 1, pos.Y, pos.Z);
+        }
+
+        public static BlockWorldPos South(this BlockWorldPos pos)
+        {
+            return new BlockWorldPos(pos.X, pos.Y, pos.Z + 1);
+        }
+
+        public static BlockWorldPos West(this BlockWorldPos pos)
+        {
+            return new BlockWorldPos(pos.X - 1, pos.Y, pos.Z);
+        }
+
+        public static BlockWorldPos North(this BlockWorldPos pos)
+        {
+            return new BlockWorldPos(pos.X, pos.Y, pos.Z - 1);
+        }
+
+        public static BlockWorldPos Down(this BlockWorldPos pos, int offset)
+        {
+            return new BlockWorldPos(pos.X, pos.Y - offset, pos.Z);
+        }
+
+        public static BlockWorldPos Up(this BlockWorldPos pos, int offset)
+        {
+            return new BlockWorldPos(pos.X, pos.Y + offset, pos.Z);
+        }
+
+        public static BlockWorldPos East(this BlockWorldPos pos, int offset)
+        {
+            return new BlockWorldPos(pos.X + offset, pos.Y, pos.Z);
+        }
+
+        public static BlockWorldPos South(this BlockWorldPos pos, int offset)
+        {
+            return new BlockWorldPos(pos.X, pos.Y, pos.Z + offset);
+        }
+
+        public static BlockWorldPos West(this BlockWorldPos pos, int offset)
+        {
+            return new BlockWorldPos(pos.X - offset, pos.Y, pos.Z);
+        }
+
+        public static BlockWorldPos North(this BlockWorldPos pos, int offset)
+        {
+            return new BlockWorldPos(pos.X, pos.Y, pos.Z - offset);
+        }
+    }
+
     public struct BlockChunkPos : IEquatable<BlockChunkPos>
     {
         public int X { get; set; }

+ 48 - 0
src/MineCase.Server.Grains/World/ChunkColumnGrain.cs

@@ -50,6 +50,37 @@ namespace MineCase.Server.World
             return State.Storage[x, y, z];
         }
 
+        public async Task<List<BlockState>> GetBlockStateListUnsafe(List<BlockChunkPos> blockPos)
+        {
+            await EnsureChunkGenerated();
+            List<BlockState> ret = new List<BlockState>();
+            foreach (var eachPos in blockPos)
+            {
+                ret.Add(State.Storage[eachPos.X, eachPos.Y, eachPos.Z]);
+            }
+
+            return ret;
+        }
+
+        public async Task ApplyChangeUnsafe(List<BlockStateChange> blockChanges)
+        {
+            await EnsureChunkGenerated();
+
+            foreach (var eachChange in blockChanges)
+            {
+                var chunkPos = eachChange.Position.ToChunkWorldPos();
+
+                // filter pos not in the chunk
+                if (chunkPos != this.GetChunkWorldPos())
+                    continue;
+                var blockChunkPos = eachChange.Position.ToBlockChunkPos();
+                if (eachChange.Condition.Contains(State.Storage[blockChunkPos.X, blockChunkPos.Y, blockChunkPos.Z]))
+                {
+                    State.Storage[blockChunkPos.X, blockChunkPos.Y, blockChunkPos.Z] = eachChange.State;
+                }
+            }
+        }
+
         public async Task<ChunkColumnCompactStorage> GetState()
         {
             await EnsureAroundChunkPopulated();
@@ -140,6 +171,23 @@ namespace MineCase.Server.World
             }
         }
 
+        public async Task SetBlockStateListUnsafe(List<BlockChunkPos> blockPos, BlockState blockState)
+        {
+            await EnsureChunkGenerated();
+
+            foreach (var eachPos in blockPos)
+            {
+                var oldState = State.Storage[eachPos.X, eachPos.Y, eachPos.Z];
+
+                if (oldState != blockState)
+                {
+                    State.Storage[eachPos.X, eachPos.Y, eachPos.Z] = blockState;
+                }
+            }
+
+            MarkDirty();
+        }
+
         private async Task EnsureChunkGenerated(bool writeState = true)
         {
             if (!State.Generated)

+ 10 - 0
src/MineCase.Server.Grains/World/Decoration/Biomes/BiomeJungleDecoratorGrain.cs

@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MineCase.Server.World.Decoration.Biomes
+{
+    public class BiomeJungleDecoratorGrain : BiomeDecoratorGrain, IBiomeJungleDecorator
+    {
+    }
+}

+ 9 - 0
src/MineCase.Server.Grains/World/Decoration/Biomes/BiomePlainsDecoratorGrain.cs

@@ -89,6 +89,15 @@ namespace MineCase.Server.World.Decoration.Biomes
                 }));
             await dandelionGenerator.Generate(world, chunkWorldPos, 3);
 
+            var jungletreeGenerator = GrainFactory.GetGrain<IJungleGenerator>(
+                JsonConvert.SerializeObject(new PlantsInfo
+                {
+                    PlantType = PlantsType.Jungle,
+                    TreeHeight = 10,
+                    ExtraHeight = 20
+                }));
+            await jungletreeGenerator.Generate(world, chunkWorldPos, 1);
+
             var oaktreeGenerator = GrainFactory.GetGrain<ITreeGenerator>(
                 JsonConvert.SerializeObject(new PlantsInfo
                 {

+ 9 - 4
src/MineCase.Server.Grains/World/Decoration/Mine/MinableGeneratorGrain.cs

@@ -64,7 +64,6 @@ namespace MineCase.Server.World.Decoration.Mine
                     --minHeight;
             }
 
-            // List<Task> genTasks = new List<Task>();
             for (int j = 0; j < count; ++j)
             {
                 BlockWorldPos blockpos = BlockWorldPos.Add(
@@ -74,8 +73,6 @@ namespace MineCase.Server.World.Decoration.Mine
                     random.Next(16));
                 await GenerateSingle(world, chunkWorldPos, blockpos, blockState, size);
             }
-
-            // await Task.WhenAll(genTasks);
         }
 
         public async Task GenerateSingle(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos, BlockState state, int size)
@@ -101,6 +98,8 @@ namespace MineCase.Server.World.Decoration.Mine
             double startY = (double)(pos.Y + rand.Next(3) - 2);
             double endY = (double)(pos.Y + rand.Next(3) - 2);
 
+            BatchBlockChange batchBlockChange = new BatchBlockChange();
+
             for (int i = 0; i < size; ++i)
             {
                 // 插值参数
@@ -146,7 +145,11 @@ namespace MineCase.Server.World.Decoration.Mine
                                     // 参考椭球方程
                                     if (xDist * xDist + yDist * yDist + zDist * zDist < 1.0D)
                                     {
-                                        await SetIfStone(world, chunkWorldPos, new BlockWorldPos(x, y, z), state);
+                                        var orePos = new BlockWorldPos(x, y, z);
+                                        if (orePos.Y >= 0 && orePos.Y < 255)
+                                        {
+                                            batchBlockChange.SetIf(orePos, state, BlockStates.Stone());
+                                        }
                                     }
                                 }
                             }
@@ -154,6 +157,8 @@ namespace MineCase.Server.World.Decoration.Mine
                     }
                 }
             }
+
+            await world.ApplyChangeUnsafe(this.GrainFactory, batchBlockChange);
         }
     }
 }

+ 46 - 0
src/MineCase.Server.Grains/World/Decoration/Plants/AbstractTreeGeneratorGrain.cs

@@ -1,13 +1,54 @@
 using System;
 using System.Collections.Generic;
 using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
 using MineCase.Block;
 using MineCase.World.Plants;
+using Newtonsoft.Json;
+using Orleans;
 
 namespace MineCase.Server.World.Decoration.Plants
 {
     public abstract class AbstractTreeGeneratorGrain : PlantsGeneratorGrain, IAbstractTreeGenerator
     {
+        protected ILogger _logger;
+
+        protected PlantsInfo _generatorSettings;
+
+        public AbstractTreeGeneratorGrain(ILoggerFactory loggerFactory)
+        {
+            _logger = loggerFactory.CreateLogger<AbstractTreeGeneratorGrain>();
+        }
+
+        public override Task OnActivateAsync()
+        {
+            try
+            {
+                var settings = this.GetPrimaryKeyString();
+                _generatorSettings = JsonConvert.DeserializeObject<PlantsInfo>(settings);
+            }
+            catch (Exception e)
+            {
+                this._logger.LogError(default(EventId), e, e.Message);
+            }
+
+            return Task.CompletedTask;
+        }
+
+        public static bool IsSoil(BlockState state)
+        {
+            if (state == BlockStates.Dirt() ||
+                state == BlockStates.GrassBlock())
+            {
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
         public static bool CanSustainTree(PlantsType type, BlockState state)
         {
             if (state == BlockStates.Dirt() ||
@@ -20,5 +61,10 @@ namespace MineCase.Server.World.Decoration.Plants
                 return false;
             }
         }
+
+        public static bool IsReplaceable(BlockState state)
+        {
+            return state.IsAir() || state.IsLeaves() || state.IsWood();
+        }
     }
 }

+ 173 - 0
src/MineCase.Server.Grains/World/Decoration/Plants/HugeTreeGeneratorGrain.cs

@@ -0,0 +1,173 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using MineCase.Block;
+using MineCase.World;
+
+namespace MineCase.Server.World.Decoration.Plants
+{
+    public abstract class HugeTreeGeneratorGrain : AbstractTreeGeneratorGrain, IHugeTreeGenerator
+    {
+        protected int _baseHeight;
+        protected int _extraRandomHeight;
+        protected BlockState _wood;
+        protected BlockState _leaves;
+
+        public HugeTreeGeneratorGrain(ILoggerFactory loggerFactory)
+            : base(loggerFactory)
+        {
+            _logger = loggerFactory.CreateLogger<HugeTreeGeneratorGrain>();
+        }
+
+        public async override Task OnActivateAsync()
+        {
+            await base.OnActivateAsync();
+            _baseHeight = _generatorSettings.TreeHeight;
+            _extraRandomHeight = _generatorSettings.ExtraHeight;
+        }
+
+        protected int GetHeight(Random rand)
+        {
+            int height = rand.Next(3) + _baseHeight;
+            if (_extraRandomHeight > 1)
+            {
+                height += rand.Next(_extraRandomHeight);
+            }
+
+            return height;
+        }
+
+        protected async Task<bool> IsAirLeaves(IWorld world, BlockWorldPos pos)
+        {
+            var block = await world.GetBlockStateUnsafe(this.GrainFactory, pos);
+            return block.IsAir() || block.IsLeaves();
+        }
+
+        /**
+         * returns whether or not a tree can grow at a specific position.
+         * If it can, it generates surrounding dirt underneath.
+         */
+        protected async Task<bool> EnsureGrowable(IWorld world, Random rand, BlockWorldPos treePos, int height)
+        {
+            return await IsSpaceAt(world, treePos, height) && await CanSustainHugeTree(world, treePos);
+        }
+
+        /**
+         * returns whether or not there is space for a tree to grow at a certain position
+         */
+        private async Task<bool> IsSpaceAt(IWorld world, BlockWorldPos treePos, int height)
+        {
+            bool flag = true;
+
+            // in the world height limit
+            if (treePos.Y >= 1 && treePos.Y + height + 1 <= 256)
+            {
+                // Traverse Y axis
+                for (int yOffset = 0; yOffset <= 1 + height; ++yOffset)
+                {
+                    int xzRange = 2;
+
+                    if (yOffset == 0)
+                    {
+                        xzRange = 1;
+                    }
+                    else if (yOffset >= 1 + height - 2)
+                    {
+                        xzRange = 2;
+                    }
+
+                    for (int xOffset = -xzRange; xOffset <= xzRange && flag; ++xOffset)
+                    {
+                        for (int zOffset = -xzRange; zOffset <= xzRange && flag; ++zOffset)
+                        {
+                            if (treePos.Y + yOffset < 0 ||
+                                treePos.Y + yOffset >= 256)
+                            {
+                                var blockState = await world.GetBlockStateUnsafe(this.GrainFactory, BlockWorldPos.Add(treePos, xOffset, yOffset, zOffset));
+                                if (IsReplaceable(blockState))
+                                    flag = false;
+                            }
+                        }
+                    }
+                }
+
+                return flag;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        private async Task<bool> CanSustainHugeTree(IWorld world, BlockWorldPos pos)
+        {
+            BlockWorldPos blockpos = pos.Down();
+            BlockState state = await world.GetBlockStateUnsafe(this.GrainFactory, blockpos);
+            bool isSoil = IsSoil(state);
+
+            if (isSoil && pos.Y >= 2)
+            {
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        /**
+         * grow leaves in a circle with the outsides being within the circle
+         */
+        protected async Task GrowLeavesLayerStrict(IWorld world, BlockWorldPos layerCenter, int width)
+        {
+            int i = width * width;
+
+            for (int j = -width; j <= width + 1; ++j)
+            {
+                for (int k = -width; k <= width + 1; ++k)
+                {
+                    int l = j - 1;
+                    int i1 = k - 1;
+
+                    if (j * j + k * k <= i || l * l + i1 * i1 <= i || j * j + i1 * i1 <= i || l * l + k * k <= i)
+                    {
+                        BlockWorldPos blockpos = BlockWorldPos.Add(layerCenter, j, 0, k);
+                        BlockState state = await world.GetBlockStateUnsafe(this.GrainFactory, blockpos);
+
+                        if (state.IsAir() || state.IsLeaves())
+                        {
+                            await world.SetBlockStateUnsafe(this.GrainFactory, blockpos, _leaves);
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * grow leaves in a circle
+         */
+        protected async Task GrowLeavesLayer(IWorld world, BlockWorldPos layerCenter, int width)
+        {
+            int i = width * width;
+
+            for (int j = -width; j <= width; ++j)
+            {
+                for (int k = -width; k <= width; ++k)
+                {
+                    if (j * j + k * k <= i)
+                    {
+                        BlockWorldPos blockpos = BlockWorldPos.Add(layerCenter, j, 0, k);
+                        BlockState state = await world.GetBlockStateUnsafe(this.GrainFactory, blockpos);
+
+                        if (state.IsAir() || state.IsLeaves())
+                        {
+                            await world.SetBlockStateUnsafe(this.GrainFactory, blockpos, _leaves);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 156 - 0
src/MineCase.Server.Grains/World/Decoration/Plants/JungleGeneratorGrain.cs

@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using MineCase.Block;
+using MineCase.World;
+using MineCase.World.Plants;
+using Newtonsoft.Json;
+using Orleans;
+using Orleans.Concurrency;
+
+namespace MineCase.Server.World.Decoration.Plants
+{
+    [StatelessWorker]
+    public class JungleGeneratorGrain : HugeTreeGeneratorGrain, IJungleGenerator
+    {
+        public JungleGeneratorGrain(ILoggerFactory loggerFactory)
+            : base(loggerFactory)
+        {
+            _logger = loggerFactory.CreateLogger<JungleGeneratorGrain>();
+        }
+
+        public async override Task OnActivateAsync()
+        {
+            await base.OnActivateAsync();
+            _wood = BlockStates.Wood(WoodType.Jungle);
+            _leaves = BlockStates.Leaves(LeaveType.Jungle);
+        }
+
+        public override async Task GenerateSingle(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        {
+            int seed = await world.GetSeed();
+            int treeSeed = pos.X ^ pos.Z ^ seed;
+            Random rand = new Random(treeSeed);
+            await GenerateImpl(world, chunkWorldPos, pos, rand);
+        }
+
+        protected async Task<bool> GenerateImpl(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos, Random random)
+        {
+            int height = GetHeight(random);
+
+            if (!await EnsureGrowable(world, random, pos, height))
+            {
+                return false;
+            }
+            else
+            {
+                await CreateCrown(world, pos.Up(height), 2);
+                for (int h = pos.Y + height - 2 - random.Next(4); h > pos.Y + height / 2; h -= 2 + random.Next(4))
+                {
+                    float branchDirection = (float)random.NextDouble() * ((float)Math.PI * 2F);
+                    int branchTopX = pos.X + (int)(0.5F + Math.Cos(branchDirection) * 4.0F); // range(0.5,4.5)
+                    int branchTopZ = pos.Z + (int)(0.5F + Math.Sin(branchDirection) * 4.0F); // range(0.5,4.5)
+
+                    for (int len = 0; len < 5; ++len)
+                    {
+                        // gen next branch node
+                        branchTopX = pos.X + (int)(1.5F + Math.Cos(branchDirection) * (float)len); // range(1.5,len + 1.5)
+                        branchTopZ = pos.Z + (int)(1.5F + Math.Sin(branchDirection) * (float)len); // range(1.5,len + 1.5)
+                        await world.SetBlockStateUnsafe(this.GrainFactory, new BlockWorldPos(branchTopX, h - 3 + len / 2, branchTopZ), _wood);
+                    }
+
+                    // now, generate the top leaves layer
+                    int layerHeight = 1 + random.Next(2);
+                    int layerTop = h;
+
+                    for (int layerY = h - layerHeight; layerY <= layerTop; ++layerY)
+                    {
+                        int width = layerY - layerTop;
+                        await GrowLeavesLayer(world, new BlockWorldPos(branchTopX, layerY, branchTopZ), 1 - width);
+                    }
+                }
+
+                for (int i2 = 0; i2 < height; ++i2)
+                {
+                    BlockWorldPos blockpos = pos.Up(i2);
+
+                    if (await IsAirLeaves(world, blockpos))
+                    {
+                        await world.SetBlockStateUnsafe(this.GrainFactory, blockpos, _wood);
+
+                        if (i2 > 0)
+                        {
+                            await PlaceVine(world, random, blockpos.West(), VineType.East);
+                            await PlaceVine(world, random, blockpos.North(), VineType.South);
+                        }
+                    }
+
+                    if (i2 < height - 1)
+                    {
+                        BlockWorldPos blockpos1 = blockpos.East();
+
+                        if (await IsAirLeaves(world, blockpos1))
+                        {
+                            await world.SetBlockStateUnsafe(this.GrainFactory, blockpos1, _wood);
+
+                            if (i2 > 0)
+                            {
+                                await PlaceVine(world, random, blockpos1.East(), VineType.West);
+                                await PlaceVine(world, random, blockpos1.North(), VineType.South);
+                            }
+                        }
+
+                        BlockWorldPos blockpos2 = blockpos.South().East();
+
+                        if (await IsAirLeaves(world, blockpos2))
+                        {
+                            await world.SetBlockStateUnsafe(this.GrainFactory, blockpos2, _wood);
+
+                            if (i2 > 0)
+                            {
+                                await PlaceVine(world, random, blockpos2.East(), VineType.West);
+                                await PlaceVine(world, random, blockpos2.South(), VineType.North);
+                            }
+                        }
+
+                        BlockWorldPos blockpos3 = blockpos.South();
+
+                        if (await IsAirLeaves(world, blockpos3))
+                        {
+                            await world.SetBlockStateUnsafe(this.GrainFactory, blockpos3, _wood);
+
+                            if (i2 > 0)
+                            {
+                                await PlaceVine(world, random, blockpos3.West(), VineType.East);
+                                await PlaceVine(world, random, blockpos3.South(), VineType.North);
+                            }
+                        }
+                    }
+                }
+
+                return true;
+            }
+        }
+
+        private async Task CreateCrown(IWorld world, BlockWorldPos pos, int width)
+        {
+            int crownHeight = 2;
+
+            for (int yOffset = -crownHeight; yOffset <= 0; ++yOffset)
+            {
+                await GrowLeavesLayerStrict(world, pos.Up(yOffset), width + 1 - yOffset);
+            }
+        }
+
+        private async Task PlaceVine(IWorld world, Random random, BlockWorldPos pos, VineType vineType)
+        {
+            var block = await world.GetBlockStateUnsafe(this.GrainFactory, pos);
+            if (random.Next(3) > 0 && block.IsAir())
+            {
+                await world.SetBlockStateUnsafe(this.GrainFactory, pos, BlockStates.Vines(vineType));
+            }
+        }
+    }
+}

+ 3 - 0
src/MineCase.Server.Grains/World/Decoration/Plants/PlantsGeneratorGrain.cs

@@ -18,6 +18,9 @@ namespace MineCase.Server.World.Decoration.Plants
         [JsonProperty(PropertyName = "TreeHeight")]
         public int TreeHeight { get; set; } = 5;
 
+        [JsonProperty(PropertyName = "ExtraHeight")]
+        public int ExtraHeight { get; set; } = 10;
+
         [JsonProperty(PropertyName = "TreeVine")]
         public bool TreeVine { get; set; } = false;
 

+ 10 - 21
src/MineCase.Server.Grains/World/Decoration/Plants/Taiga2GeneratorGrain.cs

@@ -15,8 +15,6 @@ namespace MineCase.Server.World.Decoration.Plants
     [StatelessWorker]
     public class Taiga2GeneratorGrain : AbstractTreeGeneratorGrain, ITaiga2Generator
     {
-        private readonly ILogger _logger;
-
         private int _minTreeHeight;
 
         private bool _vines;
@@ -28,32 +26,23 @@ namespace MineCase.Server.World.Decoration.Plants
         private BlockState _leaves;
 
         public Taiga2GeneratorGrain(ILoggerFactory loggerFactory)
+            : base(loggerFactory)
         {
-            _logger = loggerFactory.CreateLogger<TreeGeneratorGrain>();
+            _logger = loggerFactory.CreateLogger<Taiga2GeneratorGrain>();
         }
 
-        public override Task OnActivateAsync()
+        public async override Task OnActivateAsync()
         {
-            try
-            {
-                var settings = this.GetPrimaryKeyString();
-                PlantsInfo plantsInfo = JsonConvert.DeserializeObject<PlantsInfo>(settings);
+            await base.OnActivateAsync();
 
-                _minTreeHeight = plantsInfo.TreeHeight;
-                _vines = plantsInfo.TreeVine;
-                _treeType = plantsInfo.PlantType;
-                if (plantsInfo.PlantType == PlantsType.Spruce)
-                {
-                    _wood = BlockStates.Wood(WoodType.Spruce);
-                    _leaves = BlockStates.Leaves(LeaveType.Spruce);
-                }
-            }
-            catch (Exception e)
+            _minTreeHeight = _generatorSettings.TreeHeight;
+            _vines = _generatorSettings.TreeVine;
+            _treeType = _generatorSettings.PlantType;
+            if (_generatorSettings.PlantType == PlantsType.Spruce)
             {
-                this._logger.LogError(default(EventId), e, e.Message);
+                _wood = BlockStates.Wood(WoodType.Spruce);
+                _leaves = BlockStates.Leaves(LeaveType.Spruce);
             }
-
-            return base.OnActivateAsync();
         }
 
         public async override Task GenerateSingle(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)

+ 10 - 21
src/MineCase.Server.Grains/World/Decoration/Plants/TaigaGeneratorGrain.cs

@@ -15,8 +15,6 @@ namespace MineCase.Server.World.Decoration.Plants
     [StatelessWorker]
     public class TaigaGeneratorGrain : AbstractTreeGeneratorGrain, ITaigaGenerator
     {
-        private readonly ILogger _logger;
-
         private int _minTreeHeight;
 
         private bool _vines;
@@ -28,32 +26,23 @@ namespace MineCase.Server.World.Decoration.Plants
         private BlockState _leaves;
 
         public TaigaGeneratorGrain(ILoggerFactory loggerFactory)
+            : base(loggerFactory)
         {
-            _logger = loggerFactory.CreateLogger<TreeGeneratorGrain>();
+            _logger = loggerFactory.CreateLogger<TaigaGeneratorGrain>();
         }
 
-        public override Task OnActivateAsync()
+        public async override Task OnActivateAsync()
         {
-            try
-            {
-                var settings = this.GetPrimaryKeyString();
-                PlantsInfo plantsInfo = JsonConvert.DeserializeObject<PlantsInfo>(settings);
+            await base.OnActivateAsync();
 
-                _minTreeHeight = plantsInfo.TreeHeight;
-                _vines = plantsInfo.TreeVine;
-                _treeType = plantsInfo.PlantType;
-                if (plantsInfo.PlantType == PlantsType.Spruce)
-                {
-                    _wood = BlockStates.Wood(WoodType.Spruce);
-                    _leaves = BlockStates.Leaves(LeaveType.Spruce);
-                }
-            }
-            catch (Exception e)
+            _minTreeHeight = _generatorSettings.TreeHeight;
+            _vines = _generatorSettings.TreeVine;
+            _treeType = _generatorSettings.PlantType;
+            if (_generatorSettings.PlantType == PlantsType.Spruce)
             {
-                this._logger.LogError(default(EventId), e, e.Message);
+                _wood = BlockStates.Wood(WoodType.Spruce);
+                _leaves = BlockStates.Leaves(LeaveType.Spruce);
             }
-
-            return base.OnActivateAsync();
         }
 
         public async override Task GenerateSingle(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)

+ 18 - 29
src/MineCase.Server.Grains/World/Decoration/Plants/TreeGeneratorGrain.cs

@@ -15,8 +15,6 @@ namespace MineCase.Server.World.Decoration.Plants
     [StatelessWorker]
     public class TreeGeneratorGrain : AbstractTreeGeneratorGrain, ITreeGenerator
     {
-        private readonly ILogger _logger;
-
         private int _minTreeHeight;
 
         private bool _vines;
@@ -28,42 +26,33 @@ namespace MineCase.Server.World.Decoration.Plants
         private PlantsType _treeType;
 
         public TreeGeneratorGrain(ILoggerFactory loggerFactory)
+            : base(loggerFactory)
         {
             _logger = loggerFactory.CreateLogger<TreeGeneratorGrain>();
         }
 
-        public override Task OnActivateAsync()
+        public async override Task OnActivateAsync()
         {
-            try
-            {
-                var settings = this.GetPrimaryKeyString();
-                PlantsInfo plantsInfo = JsonConvert.DeserializeObject<PlantsInfo>(settings);
+            await base.OnActivateAsync();
 
-                _minTreeHeight = plantsInfo.TreeHeight;
-                _vines = plantsInfo.TreeVine;
-                _treeType = plantsInfo.PlantType;
-                if (plantsInfo.PlantType == PlantsType.Oak)
-                {
-                    _wood = BlockStates.Wood(WoodType.Oak);
-                    _leaves = BlockStates.Leaves(LeaveType.Oak);
-                }
-                else if (plantsInfo.PlantType == PlantsType.Spruce)
-                {
-                    _wood = BlockStates.Wood(WoodType.Spruce);
-                    _leaves = BlockStates.Leaves(LeaveType.Spruce);
-                }
-                else if (plantsInfo.PlantType == PlantsType.Birch)
-                {
-                    _wood = BlockStates.Wood(WoodType.Birch);
-                    _leaves = BlockStates.Leaves(LeaveType.Birch);
-                }
+            _minTreeHeight = _generatorSettings.TreeHeight;
+            _vines = _generatorSettings.TreeVine;
+            _treeType = _generatorSettings.PlantType;
+            if (_generatorSettings.PlantType == PlantsType.Oak)
+            {
+                _wood = BlockStates.Wood(WoodType.Oak);
+                _leaves = BlockStates.Leaves(LeaveType.Oak);
             }
-            catch (Exception e)
+            else if (_generatorSettings.PlantType == PlantsType.Spruce)
             {
-                this._logger.LogError(default(EventId), e, e.Message);
+                _wood = BlockStates.Wood(WoodType.Spruce);
+                _leaves = BlockStates.Leaves(LeaveType.Spruce);
+            }
+            else if (_generatorSettings.PlantType == PlantsType.Birch)
+            {
+                _wood = BlockStates.Wood(WoodType.Birch);
+                _leaves = BlockStates.Leaves(LeaveType.Birch);
             }
-
-            return base.OnActivateAsync();
         }
 
         public override async Task GenerateSingle(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)

+ 10 - 0
src/MineCase.Server.Interfaces/World/Decoration/Biomes/IBiomeJungleDecorator.cs

@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MineCase.Server.World.Decoration.Biomes
+{
+    public interface IBiomeJungleDecorator : IBiomeDecorator
+    {
+    }
+}

+ 10 - 0
src/MineCase.Server.Interfaces/World/Decoration/Plants/IHugeTreeGenerator.cs

@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MineCase.Server.World.Decoration.Plants
+{
+    public interface IHugeTreeGenerator : IAbstractTreeGenerator
+    {
+    }
+}

+ 10 - 0
src/MineCase.Server.Interfaces/World/Decoration/Plants/IJungleGenerator.cs

@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MineCase.Server.World.Decoration.Plants
+{
+    public interface IJungleGenerator : IAbstractTreeGenerator
+    {
+    }
+}

+ 2 - 0
src/MineCase.Server.Interfaces/World/IChunkColumn.cs

@@ -22,6 +22,8 @@ namespace MineCase.Server.World
 
         Task<BlockState> GetBlockStateUnsafe(int x, int y, int z);
 
+        Task ApplyChangeUnsafe(List<BlockStateChange> blockChanges);
+
         Task SetBlockState(int x, int y, int z, BlockState blockState);
 
         Task SetBlockStateUnsafe(int x, int y, int z, BlockState blockState);

+ 88 - 0
src/MineCase.Server.Interfaces/World/WorldExtensions.cs

@@ -73,6 +73,45 @@ namespace MineCase.Server.World
                 blockChunkPos.Z);
         }
 
+        /// <summary>
+        /// Gets the state of the block. Only used in ***decoration*** stage.
+        /// </summary>
+        /// <param name="world">The world Grain.</param>
+        /// <param name="grainFactory">The grain factory.</param>
+        /// <param name="x">The x.</param>
+        /// <param name="y">The y.</param>
+        /// <param name="z">The z.</param>
+        /// <returns>方块类型.</returns>
+        public static Task<BlockState> GetBlockStateUnsafe(this IWorld world, IGrainFactory grainFactory, int x, int y, int z)
+        {
+            var blockWorldPos = new BlockWorldPos(x, y, z);
+            var blockChunkPos = blockWorldPos.ToBlockChunkPos();
+            var chunkColumnKey = world.MakeAddressByPartitionKey(blockWorldPos.ToChunkWorldPos());
+            return grainFactory.GetGrain<IChunkColumn>(chunkColumnKey).GetBlockStateUnsafe(
+                blockChunkPos.X,
+                blockChunkPos.Y,
+                blockChunkPos.Z);
+        }
+
+        /// <summary>
+        /// Gets the state of the block. Only used in ***decoration*** stage.
+        /// </summary>
+        /// <param name="world">The world Grain.</param>
+        /// <param name="grainFactory">The grain factory.</param>
+        /// <param name="x">The x.</param>
+        /// <param name="y">The y.</param>
+        /// <param name="z">The z.</param>
+        /// <returns>方块类型.</returns>
+        public static Task<BlockState> GetBlockStateUnsafe(this IWorld world, IGrainFactory grainFactory, BlockWorldPos pos)
+        {
+            var blockChunkPos = pos.ToBlockChunkPos();
+            var chunkColumnKey = world.MakeAddressByPartitionKey(pos.ToChunkWorldPos());
+            return grainFactory.GetGrain<IChunkColumn>(chunkColumnKey).GetBlockStateUnsafe(
+                blockChunkPos.X,
+                blockChunkPos.Y,
+                blockChunkPos.Z);
+        }
+
         /// <summary>
         /// Sets the state of the block.
         /// </summary>
@@ -113,6 +152,55 @@ namespace MineCase.Server.World
                 state);
         }
 
+        /// <summary>
+        /// Sets the state of the block. Only used in ***decoration*** stage.
+        /// </summary>
+        /// <param name="world">The world grain.</param>
+        /// <param name="grainFactory">The grain factory.</param>
+        /// <param name="x">The x.</param>
+        /// <param name="y">The y.</param>
+        /// <param name="z">The z.</param>
+        /// <param name="state">The state.</param>
+        public static Task SetBlockStateUnsafe(this IWorld world, IGrainFactory grainFactory, int x, int y, int z, BlockState state)
+        {
+            var blockWorldPos = new BlockWorldPos(x, y, z);
+            var blockChunkPos = blockWorldPos.ToBlockChunkPos();
+            var chunkColumnKey = world.MakeAddressByPartitionKey(blockWorldPos.ToChunkWorldPos());
+            return grainFactory.GetGrain<IChunkColumn>(chunkColumnKey).SetBlockStateUnsafe(
+                blockChunkPos.X,
+                blockChunkPos.Y,
+                blockChunkPos.Z,
+                state);
+        }
+
+        /// <summary>
+        /// Sets the state of the block. Only used in ***decoration*** stage.
+        /// </summary>
+        /// <param name="world">The world grain.</param>
+        /// <param name="grainFactory">The grain factory.</param>
+        /// <param name="pos">The position.</param>
+        /// <param name="state">The state.</param>
+        public static Task SetBlockStateUnsafe(this IWorld world, IGrainFactory grainFactory, BlockWorldPos pos, BlockState state)
+        {
+            var blockChunkPos = pos.ToBlockChunkPos();
+            var chunkColumnKey = world.MakeAddressByPartitionKey(pos.ToChunkWorldPos());
+            return grainFactory.GetGrain<IChunkColumn>(chunkColumnKey).SetBlockStateUnsafe(
+                blockChunkPos.X,
+                blockChunkPos.Y,
+                blockChunkPos.Z,
+                state);
+        }
+
+        public static async Task ApplyChangeUnsafe(this IWorld world, IGrainFactory grainFactory, BatchBlockChange change)
+        {
+            var partitionChange = change.GetByPartition();
+            foreach (var eachPartitionChange in partitionChange)
+            {
+                var chunkColumnKey = world.MakeAddressByPartitionKey(eachPartitionChange.Key);
+                await grainFactory.GetGrain<IChunkColumn>(chunkColumnKey).ApplyChangeUnsafe(eachPartitionChange.Value);
+            }
+        }
+
         public static async Task<int> GetHeight(this IWorld world, IGrainFactory grainFactory, BlockWorldPos pos)
         {
             var xOffset = MakeRelativeBlockOffset(pos.X);