JasonWang 6 роки тому
батько
коміт
4b494b81f9
18 змінених файлів з 612 додано та 54 видалено
  1. 3 0
      src/MineCase.Core/World/ChunkColumnCompactStorage.cs
  2. 227 0
      src/MineCase.Core/World/Facing.cs
  3. 2 1
      src/MineCase.Core/World/Plants/PlantsType.cs
  4. 16 0
      src/MineCase.Core/World/Position.cs
  5. 8 0
      src/MineCase.Server.Grains/World/Decoration/Biomes/BiomePlainsDecoratorGrain.cs
  6. 5 15
      src/MineCase.Server.Grains/World/Decoration/Plants/AbstractTreeGeneratorGrain.cs
  7. 65 0
      src/MineCase.Server.Grains/World/Decoration/Plants/DoublePlantGeneratorGrain.cs
  8. 5 17
      src/MineCase.Server.Grains/World/Decoration/Plants/FlowersGeneratorGrain.cs
  9. 12 0
      src/MineCase.Server.Grains/World/Decoration/Plants/GrassGeneratorGrain.cs
  10. 0 2
      src/MineCase.Server.Grains/World/Decoration/Plants/HugeTreeGeneratorGrain.cs
  11. 8 7
      src/MineCase.Server.Grains/World/Decoration/Plants/JungleGeneratorGrain.cs
  12. 25 0
      src/MineCase.Server.Grains/World/Decoration/Plants/PlantsGeneratorGrain.cs
  13. 216 0
      src/MineCase.Server.Grains/World/Decoration/Plants/SavannaTreeGeneratorGrain.cs
  14. 0 4
      src/MineCase.Server.Grains/World/Decoration/Plants/Taiga2GeneratorGrain.cs
  15. 0 4
      src/MineCase.Server.Grains/World/Decoration/Plants/TaigaGeneratorGrain.cs
  16. 0 4
      src/MineCase.Server.Grains/World/Decoration/Plants/TreeGeneratorGrain.cs
  17. 10 0
      src/MineCase.Server.Interfaces/World/Decoration/Plants/IDoublePlantGenerator.cs
  18. 10 0
      src/MineCase.Server.Interfaces/World/Decoration/Plants/ISavannaTreeGenerator.cs

+ 3 - 0
src/MineCase.Core/World/ChunkColumnCompactStorage.cs

@@ -8,11 +8,14 @@ namespace MineCase.World
 {
     public static class ChunkConstants
     {
+        public const int ChunkHeight = 256;
         public const int SectionsPerChunk = 16;
 
         public const int BlockEdgeWidthInSection = 16;
 
         public const int BlocksInSection = BlockEdgeWidthInSection * BlockEdgeWidthInSection * BlockEdgeWidthInSection;
+
+        public const int BlocksInChunk = BlocksInSection * SectionsPerChunk;
     }
 
     public sealed class ChunkColumnCompactStorage : IChunkColumnStorage

+ 227 - 0
src/MineCase.Core/World/Facing.cs

@@ -0,0 +1,227 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Text;
+
+namespace MineCase.World
+{
+    public enum Plane
+    {
+        XY,
+        XZ,
+        YZ
+    }
+
+    public enum Axis
+    {
+        X,
+        Y,
+        Z
+    }
+
+    public enum AxisDirection : int
+    {
+        Positive = 1,
+        Negative = -1
+    }
+
+    public enum EnumFacing
+    {
+        Up,
+        Down,
+        West,
+        East,
+        North,
+        South
+    }
+
+    public class Facing : IEquatable<Facing>
+    {
+        private EnumFacing _facing;
+
+        private Axis _axis;
+
+        public Facing(EnumFacing facing)
+        {
+            _facing = facing;
+            if (facing == EnumFacing.Up || facing == EnumFacing.Down)
+            {
+                _axis = Axis.Y;
+            }
+            else if (facing == EnumFacing.East || facing == EnumFacing.West)
+            {
+                _axis = Axis.X;
+            }
+            else if (facing == EnumFacing.North || facing == EnumFacing.South)
+            {
+                _axis = Axis.Z;
+            }
+        }
+
+        public Axis GetAxis()
+        {
+            return _axis;
+        }
+
+        bool IEquatable<Facing>.Equals(Facing other)
+        {
+            return _facing == other._facing;
+        }
+
+        public BlockVector ToBlockVector()
+        {
+            if (_facing == EnumFacing.Up)
+            {
+                return new BlockVector(0, 1, 0);
+            }
+            else if (_facing == EnumFacing.East)
+            {
+                return new BlockVector(1, 0, 0);
+            }
+            else if (_facing == EnumFacing.North)
+            {
+                return new BlockVector(0, 0, -1);
+            }
+            else if (_facing == EnumFacing.Down)
+            {
+                return new BlockVector(0, -1, 0);
+            }
+            else if (_facing == EnumFacing.West)
+            {
+                return new BlockVector(-1, 0, 0);
+            }
+            else if (_facing == EnumFacing.South)
+            {
+                return new BlockVector(0, 0, 1);
+            }
+            else
+            {
+                return new BlockVector(0, 0, 0);
+            }
+        }
+
+        public static Facing RadomFacing(Random random)
+        {
+            var v = random.Next(6);
+            switch (v)
+            {
+                case 0:
+                    return new Facing(EnumFacing.Down);
+                case 1:
+                    return new Facing(EnumFacing.East);
+                case 2:
+                    return new Facing(EnumFacing.North);
+                case 3:
+                    return new Facing(EnumFacing.South);
+                case 4:
+                    return new Facing(EnumFacing.Up);
+                case 5:
+                    return new Facing(EnumFacing.West);
+                default:
+                    throw new System.NotSupportedException("It should never reach it here.");
+            }
+        }
+
+        public static Facing RadomFacing(Random random, Axis axis)
+        {
+            var v = random.Next(2);
+            if (axis == Axis.X)
+            {
+                switch (v)
+                {
+                    case 0:
+                        return new Facing(EnumFacing.East);
+                    case 1:
+                        return new Facing(EnumFacing.West);
+                    default:
+                        throw new System.NotSupportedException("It should never reach it here.");
+                }
+            }
+            else if (axis == Axis.Y)
+            {
+                switch (v)
+                {
+                    case 0:
+                        return new Facing(EnumFacing.Up);
+                    case 1:
+                        return new Facing(EnumFacing.Down);
+                    default:
+                        throw new System.NotSupportedException("It should never reach it here.");
+                }
+            }
+            else if (axis == Axis.Z)
+            {
+                switch (v)
+                {
+                    case 0:
+                        return new Facing(EnumFacing.North);
+                    case 1:
+                        return new Facing(EnumFacing.South);
+                    default:
+                        throw new System.NotSupportedException("It should never reach it here.");
+                }
+            }
+            else
+            {
+                throw new System.NotSupportedException("It should never reach it here.");
+            }
+        }
+
+        public static Facing RadomFacing(Random random, Plane plane)
+        {
+            var v = random.Next(4);
+            if (plane == Plane.XY)
+            {
+                switch (v)
+                {
+                    case 0:
+                        return new Facing(EnumFacing.East);
+                    case 1:
+                        return new Facing(EnumFacing.West);
+                    case 2:
+                        return new Facing(EnumFacing.Up);
+                    case 3:
+                        return new Facing(EnumFacing.Down);
+                    default:
+                        throw new System.NotSupportedException("It should never reach it here.");
+                }
+            }
+            else if (plane == Plane.XZ)
+            {
+                switch (v)
+                {
+                    case 0:
+                        return new Facing(EnumFacing.East);
+                    case 1:
+                        return new Facing(EnumFacing.West);
+                    case 2:
+                        return new Facing(EnumFacing.North);
+                    case 3:
+                        return new Facing(EnumFacing.South);
+                    default:
+                        throw new System.NotSupportedException("It should never reach it here.");
+                }
+            }
+            else if (plane == Plane.YZ)
+            {
+                switch (v)
+                {
+                    case 0:
+                        return new Facing(EnumFacing.South);
+                    case 1:
+                        return new Facing(EnumFacing.North);
+                    case 2:
+                        return new Facing(EnumFacing.Up);
+                    case 3:
+                        return new Facing(EnumFacing.Down);
+                    default:
+                        throw new System.NotSupportedException("It should never reach it here.");
+                }
+            }
+            else
+            {
+                throw new System.NotSupportedException("It should never reach it here.");
+            }
+        }
+    }
+}

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

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

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

@@ -100,6 +100,22 @@ namespace MineCase.World
         }
     }
 
+    public struct BlockVector
+    {
+        public int X { get; set; }
+
+        public int Y { get; set; }
+
+        public int Z { get; set; }
+
+        public BlockVector(int x, int y, int z)
+        {
+            X = x;
+            Y = y;
+            Z = z;
+        }
+    }
+
     public static class BlockWorldPosExtension
     {
         public static BlockWorldPos Down(this BlockWorldPos pos)

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

@@ -89,6 +89,7 @@ namespace MineCase.Server.World.Decoration.Biomes
                 }));
             await dandelionGenerator.Generate(world, chunkWorldPos, 3);
 
+            /*
             var jungletreeGenerator = GrainFactory.GetGrain<IJungleGenerator>(
                 JsonConvert.SerializeObject(new PlantsInfo
                 {
@@ -97,6 +98,13 @@ namespace MineCase.Server.World.Decoration.Biomes
                     ExtraHeight = 20
                 }));
             await jungletreeGenerator.Generate(world, chunkWorldPos, 1);
+            */
+            var savannatreeGenerator = GrainFactory.GetGrain<ISavannaTreeGenerator>(
+                JsonConvert.SerializeObject(new PlantsInfo
+                {
+                    PlantType = PlantsType.SavannaTree,
+                }));
+            await savannatreeGenerator.Generate(world, chunkWorldPos, 1);
 
             var oaktreeGenerator = GrainFactory.GetGrain<ITreeGenerator>(
                 JsonConvert.SerializeObject(new PlantsInfo

+ 5 - 15
src/MineCase.Server.Grains/World/Decoration/Plants/AbstractTreeGeneratorGrain.cs

@@ -12,28 +12,18 @@ namespace MineCase.Server.World.Decoration.Plants
 {
     public abstract class AbstractTreeGeneratorGrain : PlantsGeneratorGrain, IAbstractTreeGenerator
     {
-        protected ILogger _logger;
-
-        protected PlantsInfo _generatorSettings;
+        protected BlockState _wood;
+        protected BlockState _leaves;
 
         public AbstractTreeGeneratorGrain(ILoggerFactory loggerFactory)
+            : base(loggerFactory)
         {
             _logger = loggerFactory.CreateLogger<AbstractTreeGeneratorGrain>();
         }
 
-        public override Task OnActivateAsync()
+        public async 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;
+            await base.OnActivateAsync();
         }
 
         public static bool IsSoil(BlockState state)

+ 65 - 0
src/MineCase.Server.Grains/World/Decoration/Plants/DoublePlantGeneratorGrain.cs

@@ -0,0 +1,65 @@
+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 Orleans.Concurrency;
+
+namespace MineCase.Server.World.Decoration.Plants
+{
+    [StatelessWorker]
+    public class DoublePlantGeneratorGrain : PlantsGeneratorGrain, IDoublePlantGenerator
+    {
+        private PlantsType _plantType;
+
+        public DoublePlantGeneratorGrain(ILoggerFactory loggerFactory)
+            : base(loggerFactory)
+        {
+            _logger = loggerFactory.CreateLogger<DoublePlantGeneratorGrain>();
+        }
+
+        public async override Task OnActivateAsync()
+        {
+            await base.OnActivateAsync();
+            _plantType = _generatorSettings.PlantType;
+        }
+
+        public override Task GenerateSingle(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        {
+            throw new NotImplementedException();
+        }
+
+        private async Task<bool> GenerateImpl(IWorld world, BlockWorldPos pos, Random random)
+        {
+            if (await CanPlaceAt(world, pos, _plantType))
+            {
+                await PlaceAt(world, pos, _plantType);
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        private async Task PlaceAt(IWorld world, BlockWorldPos pos, PlantsType type)
+        {
+            if (type == PlantsType.DoubleTallgrass)
+            {
+                await world.SetBlockStateUnsafe(this.GrainFactory, pos, BlockStates.Grass(GrassType.TallGrass));
+                await world.SetBlockStateUnsafe(this.GrainFactory, pos.Up(), BlockStates.Grass(GrassType.TallGrass));
+            }
+        }
+
+        private async Task<bool> CanPlaceAt(IWorld world, BlockWorldPos pos, PlantsType type)
+        {
+            var block = await world.GetBlockStateUnsafe(this.GrainFactory, pos);
+            var upBlock = await world.GetBlockStateUnsafe(this.GrainFactory, pos.Up());
+
+            return block.IsAir() && upBlock.IsAir();
+        }
+    }
+}

+ 5 - 17
src/MineCase.Server.Grains/World/Decoration/Plants/FlowersGeneratorGrain.cs

@@ -15,30 +15,18 @@ namespace MineCase.Server.World.Decoration.Plants
     [StatelessWorker]
     public class FlowersGeneratorGrain : PlantsGeneratorGrain, IFlowersGenerator
     {
-        private readonly ILogger _logger;
-
         protected PlantsType _flowerType;
 
         public FlowersGeneratorGrain(ILoggerFactory loggerFactory)
+            : base(loggerFactory)
         {
-            _logger = loggerFactory.CreateLogger<TreeGeneratorGrain>();
+            _logger = loggerFactory.CreateLogger<FlowersGeneratorGrain>();
         }
 
-        public override Task OnActivateAsync()
+        public async override Task OnActivateAsync()
         {
-            try
-            {
-                var settings = this.GetPrimaryKeyString();
-                PlantsInfo plantsInfo = JsonConvert.DeserializeObject<PlantsInfo>(settings);
-
-                _flowerType = plantsInfo.PlantType;
-            }
-            catch (Exception e)
-            {
-                this._logger.LogError(default(EventId), e, e.Message);
-            }
-
-            return base.OnActivateAsync();
+            await base.OnActivateAsync();
+            _flowerType = _generatorSettings.PlantType;
         }
 
         public override async Task GenerateSingle(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)

+ 12 - 0
src/MineCase.Server.Grains/World/Decoration/Plants/GrassGeneratorGrain.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Text;
 using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
 using MineCase.Block;
 using MineCase.World;
 using Orleans.Concurrency;
@@ -11,6 +12,17 @@ namespace MineCase.Server.World.Decoration.Plants
     [StatelessWorker]
     public class GrassGeneratorGrain : PlantsGeneratorGrain, IGrassGenerator
     {
+        public GrassGeneratorGrain(ILoggerFactory loggerFactory)
+            : base(loggerFactory)
+        {
+            _logger = loggerFactory.CreateLogger<GrassGeneratorGrain>();
+        }
+
+        public async override Task OnActivateAsync()
+        {
+            await base.OnActivateAsync();
+        }
+
         public override async Task GenerateSingle(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
         {
             // TODO use block accessor

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

@@ -12,8 +12,6 @@ namespace MineCase.Server.World.Decoration.Plants
     {
         protected int _baseHeight;
         protected int _extraRandomHeight;
-        protected BlockState _wood;
-        protected BlockState _leaves;
 
         public HugeTreeGeneratorGrain(ILoggerFactory loggerFactory)
             : base(loggerFactory)

+ 8 - 7
src/MineCase.Server.Grains/World/Decoration/Plants/JungleGeneratorGrain.cs

@@ -72,22 +72,23 @@ namespace MineCase.Server.World.Decoration.Plants
                     }
                 }
 
-                for (int i2 = 0; i2 < height; ++i2)
+                // generate wood
+                for (int y = 0; y < height; ++y)
                 {
-                    BlockWorldPos blockpos = pos.Up(i2);
+                    BlockWorldPos blockpos = pos.Up(y);
 
                     if (await IsAirLeaves(world, blockpos))
                     {
                         await world.SetBlockStateUnsafe(this.GrainFactory, blockpos, _wood);
 
-                        if (i2 > 0)
+                        if (y > 0)
                         {
                             await PlaceVine(world, random, blockpos.West(), VineType.East);
                             await PlaceVine(world, random, blockpos.North(), VineType.South);
                         }
                     }
 
-                    if (i2 < height - 1)
+                    if (y < height - 1)
                     {
                         BlockWorldPos blockpos1 = blockpos.East();
 
@@ -95,7 +96,7 @@ namespace MineCase.Server.World.Decoration.Plants
                         {
                             await world.SetBlockStateUnsafe(this.GrainFactory, blockpos1, _wood);
 
-                            if (i2 > 0)
+                            if (y > 0)
                             {
                                 await PlaceVine(world, random, blockpos1.East(), VineType.West);
                                 await PlaceVine(world, random, blockpos1.North(), VineType.South);
@@ -108,7 +109,7 @@ namespace MineCase.Server.World.Decoration.Plants
                         {
                             await world.SetBlockStateUnsafe(this.GrainFactory, blockpos2, _wood);
 
-                            if (i2 > 0)
+                            if (y > 0)
                             {
                                 await PlaceVine(world, random, blockpos2.East(), VineType.West);
                                 await PlaceVine(world, random, blockpos2.South(), VineType.North);
@@ -121,7 +122,7 @@ namespace MineCase.Server.World.Decoration.Plants
                         {
                             await world.SetBlockStateUnsafe(this.GrainFactory, blockpos3, _wood);
 
-                            if (i2 > 0)
+                            if (y > 0)
                             {
                                 await PlaceVine(world, random, blockpos3.West(), VineType.East);
                                 await PlaceVine(world, random, blockpos3.South(), VineType.North);

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

@@ -1,5 +1,6 @@
 using System;
 using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
 using MineCase.Algorithm.World.Biomes;
 using MineCase.Block;
 using MineCase.Server.World;
@@ -30,6 +31,30 @@ namespace MineCase.Server.World.Decoration.Plants
 
     public abstract class PlantsGeneratorGrain : Grain, IPlantsGenerator
     {
+        protected ILogger _logger;
+
+        protected PlantsInfo _generatorSettings;
+
+        public PlantsGeneratorGrain(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;
+        }
+
         protected virtual Task SetBlock(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos, BlockState state)
         {
             var chunkColumnKey = world.MakeAddressByPartitionKey(pos.ToChunkWorldPos());

+ 216 - 0
src/MineCase.Server.Grains/World/Decoration/Plants/SavannaTreeGeneratorGrain.cs

@@ -0,0 +1,216 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using MineCase.Block;
+using MineCase.World;
+using Orleans.Concurrency;
+
+namespace MineCase.Server.World.Decoration.Plants
+{
+    [StatelessWorker]
+    public class SavannaTreeGeneratorGrain : AbstractTreeGeneratorGrain, ISavannaTreeGenerator
+    {
+        public SavannaTreeGeneratorGrain(ILoggerFactory loggerFactory)
+            : base(loggerFactory)
+        {
+            _logger = loggerFactory.CreateLogger<JungleGeneratorGrain>();
+        }
+
+        public async override Task OnActivateAsync()
+        {
+            await base.OnActivateAsync();
+            _wood = BlockStates.Wood2(Wood2Type.Acacia);
+            _leaves = BlockStates.Leaves2(Leave2Type.Acacia);
+        }
+
+        public async override 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);
+        }
+
+        private async Task<bool> GenerateImpl(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos, Random rand)
+        {
+            int i = rand.Next(3) + rand.Next(3) + 5;
+            bool flag = true;
+
+            if (pos.Y >= 1 && pos.Y + i + 1 <= 256)
+            {
+                for (int j = pos.Y; j <= pos.Y + 1 + i; ++j)
+                {
+                    int k = 1;
+
+                    if (j == pos.Y)
+                    {
+                        k = 0;
+                    }
+
+                    if (j >= pos.Y + 1 + i - 2)
+                    {
+                        k = 2;
+                    }
+
+                    for (int l = pos.X - k; l <= pos.X + k && flag; ++l)
+                    {
+                        for (int i1 = pos.Z - k; i1 <= pos.Z + k && flag; ++i1)
+                        {
+                            if (j >= 0 && j < 256)
+                            {
+                                var blockState = await world.GetBlockStateUnsafe(this.GrainFactory, new BlockWorldPos(l, j, i1));
+                                if (!IsReplaceable(blockState))
+                                {
+                                    flag = false;
+                                }
+                            }
+                            else
+                            {
+                                flag = false;
+                            }
+                        }
+                    }
+                }
+
+                if (!flag)
+                {
+                    return false;
+                }
+                else
+                {
+                    BlockWorldPos down = pos.Down();
+                    BlockState state = await world.GetBlockStateUnsafe(this.GrainFactory, down);
+                    bool isSoil = IsSoil(state);
+
+                    if (isSoil && pos.Y < ChunkConstants.ChunkHeight - i - 1)
+                    {
+                        // state.getBlock().onPlantGrow(state, world, down, pos);
+                        Facing enumfacing = Facing.RadomFacing(rand, Plane.XZ);
+                        int k2 = i - rand.Next(4) - 1;
+                        int l2 = 3 - rand.Next(3);
+                        int i3 = pos.X;
+                        int j1 = pos.Z;
+                        int k1 = 0;
+
+                        for (int l1 = 0; l1 < i; ++l1)
+                        {
+                            int i2 = pos.Y + l1;
+
+                            if (l1 >= k2 && l2 > 0)
+                            {
+                                i3 += enumfacing.ToBlockVector().X;
+                                j1 += enumfacing.ToBlockVector().Z;
+                                --l2;
+                            }
+
+                            BlockWorldPos blockpos = new BlockWorldPos(i3, i2, j1);
+                            state = await world.GetBlockStateUnsafe(this.GrainFactory, blockpos);
+
+                            if (state.IsAir() || state.IsLeaves())
+                            {
+                                await world.SetBlockStateUnsafe(this.GrainFactory, blockpos, _wood);
+                                k1 = i2;
+                            }
+                        }
+
+                        BlockWorldPos blockpos2 = new BlockWorldPos(i3, k1, j1);
+
+                        for (int j3 = -3; j3 <= 3; ++j3)
+                        {
+                            for (int i4 = -3; i4 <= 3; ++i4)
+                            {
+                                if (Math.Abs(j3) != 3 || Math.Abs(i4) != 3)
+                                {
+                                    await world.SetBlockStateUnsafe(this.GrainFactory, BlockWorldPos.Add(blockpos2, j3, 0, i4), _leaves);
+                                }
+                            }
+                        }
+
+                        blockpos2 = blockpos2.Up();
+
+                        for (int k3 = -1; k3 <= 1; ++k3)
+                        {
+                            for (int j4 = -1; j4 <= 1; ++j4)
+                            {
+                                await world.SetBlockStateUnsafe(this.GrainFactory, BlockWorldPos.Add(blockpos2, k3, 0, j4), _leaves);
+                            }
+                        }
+
+                        await world.SetBlockStateUnsafe(this.GrainFactory, blockpos2.East(2), _leaves);
+                        await world.SetBlockStateUnsafe(this.GrainFactory, blockpos2.West(2), _leaves);
+                        await world.SetBlockStateUnsafe(this.GrainFactory, blockpos2.South(2), _leaves);
+                        await world.SetBlockStateUnsafe(this.GrainFactory, blockpos2.North(2), _leaves);
+                        i3 = pos.X;
+                        j1 = pos.Z;
+                        Facing enumfacing1 = Facing.RadomFacing(rand, Plane.XZ);
+
+                        if (enumfacing1 != enumfacing)
+                        {
+                            int l3 = k2 - rand.Next(2) - 1;
+                            int k4 = 1 + rand.Next(3);
+                            k1 = 0;
+
+                            for (int l4 = l3; l4 < i && k4 > 0; --k4)
+                            {
+                                if (l4 >= 1)
+                                {
+                                    int j2 = pos.Y + l4;
+                                    i3 += enumfacing1.ToBlockVector().X;
+                                    j1 += enumfacing1.ToBlockVector().Z;
+                                    BlockWorldPos blockpos1 = new BlockWorldPos(i3, j2, j1);
+                                    state = await world.GetBlockStateUnsafe(this.GrainFactory, blockpos1);
+
+                                    if (state.IsAir() || state.IsLeaves())
+                                    {
+                                        await world.SetBlockStateUnsafe(this.GrainFactory, blockpos1, _wood);
+                                        k1 = j2;
+                                    }
+                                }
+
+                                ++l4;
+                            }
+
+                            if (k1 > 0)
+                            {
+                                BlockWorldPos blockpos3 = new BlockWorldPos(i3, k1, j1);
+
+                                for (int i5 = -2; i5 <= 2; ++i5)
+                                {
+                                    for (int k5 = -2; k5 <= 2; ++k5)
+                                    {
+                                        if (Math.Abs(i5) != 2 || Math.Abs(k5) != 2)
+                                        {
+                                            await world.SetBlockStateUnsafe(this.GrainFactory, BlockWorldPos.Add(blockpos3, i5, 0, k5), _leaves);
+                                        }
+                                    }
+                                }
+
+                                blockpos3 = blockpos3.Up();
+
+                                for (int j5 = -1; j5 <= 1; ++j5)
+                                {
+                                    for (int l5 = -1; l5 <= 1; ++l5)
+                                    {
+                                        await world.SetBlockStateUnsafe(this.GrainFactory, BlockWorldPos.Add(blockpos3, j5, 0, l5), _leaves);
+                                    }
+                                }
+                            }
+                        }
+
+                        return true;
+                    }
+                    else
+                    {
+                        return false;
+                    }
+                }
+            }
+            else
+            {
+                return false;
+            }
+        }
+    }
+}

+ 0 - 4
src/MineCase.Server.Grains/World/Decoration/Plants/Taiga2GeneratorGrain.cs

@@ -21,10 +21,6 @@ namespace MineCase.Server.World.Decoration.Plants
 
         private PlantsType _treeType;
 
-        private BlockState _wood;
-
-        private BlockState _leaves;
-
         public Taiga2GeneratorGrain(ILoggerFactory loggerFactory)
             : base(loggerFactory)
         {

+ 0 - 4
src/MineCase.Server.Grains/World/Decoration/Plants/TaigaGeneratorGrain.cs

@@ -21,10 +21,6 @@ namespace MineCase.Server.World.Decoration.Plants
 
         private PlantsType _treeType;
 
-        private BlockState _wood;
-
-        private BlockState _leaves;
-
         public TaigaGeneratorGrain(ILoggerFactory loggerFactory)
             : base(loggerFactory)
         {

+ 0 - 4
src/MineCase.Server.Grains/World/Decoration/Plants/TreeGeneratorGrain.cs

@@ -19,10 +19,6 @@ namespace MineCase.Server.World.Decoration.Plants
 
         private bool _vines;
 
-        private BlockState _wood;
-
-        private BlockState _leaves;
-
         private PlantsType _treeType;
 
         public TreeGeneratorGrain(ILoggerFactory loggerFactory)

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

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

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

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