JasonWang před 6 roky
rodič
revize
df93615e04

+ 1 - 1
src/MineCase.Core/Settings/ServerSettings.cs

@@ -38,7 +38,7 @@ namespace MineCase.Server.Settings
         /// <summary>
         /// Gets or sets by default it allows packets that are n-1 bytes big to go normally, but a packet that n bytes or more will be compressed down. So, lower number means more compression but compressing small amounts of bytes might actually end up with a larger result than what went in.
         /// -1 - disable compression entirely
-        /// 0 - compress everything
+        /// 0 - compress everything.
         /// </summary>
         [JsonProperty(PropertyName = "network-compression-threshold")]
         [DefaultValue(256)]

+ 3 - 3
src/MineCase.Server.Grains/World/Decoration/Biomes/BiomeDecoratorGrain.cs

@@ -37,17 +37,17 @@ namespace MineCase.Server.World.Decoration.Biomes
 
         public List<MobType> MonsterList { get; set; } = new List<MobType>();
 
-        public virtual Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        public virtual Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos)
         {
             throw new NotImplementedException();
         }
 
-        public virtual Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        public virtual Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos)
         {
             throw new NotImplementedException();
         }
 
-        public virtual Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        public virtual Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos)
         {
             throw new NotImplementedException();
         }

+ 12 - 5
src/MineCase.Server.Grains/World/Decoration/Biomes/BiomeForestDecoratorGrain.cs

@@ -7,6 +7,7 @@ using MineCase.Server.World.Decoration.Plants;
 using MineCase.World;
 using MineCase.World.Biomes;
 using MineCase.World.Plants;
+using Newtonsoft.Json;
 using Orleans;
 using Orleans.Concurrency;
 
@@ -48,18 +49,24 @@ namespace MineCase.Server.World.Decoration.Biomes
             return Task.CompletedTask;
         }
 
-        public override Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        public async override Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos)
         {
-            var grassGenerator = GrainFactory.GetGrain<IGrassGenerator>(0);
-            return grassGenerator.Generate(world, chunkWorldPos, 10, 0);
+            PlantsInfo info = new PlantsInfo();
+            String infoString = JsonConvert.SerializeObject(info);
+
+            var grassGenerator = GrainFactory.GetGrain<IGrassGenerator>(infoString);
+            await grassGenerator.Generate(world, chunkWorldPos, 10, 0);
+
+            var treeGenerator = GrainFactory.GetGrain<ITreeGenerator>(infoString);
+            await treeGenerator.Generate(world, chunkWorldPos, 1, 1);
         }
 
-        public override Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        public override Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos)
         {
             throw new NotImplementedException();
         }
 
-        public override Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        public override Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos)
         {
             throw new NotImplementedException();
         }

+ 3 - 3
src/MineCase.Server.Grains/World/Decoration/Biomes/BiomeOceanDecoratorGrain.cs

@@ -53,17 +53,17 @@ namespace MineCase.Server.World.Decoration.Biomes
             return Task.CompletedTask;
         }
 
-        public override Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        public override Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos)
         {
             return Task.CompletedTask;
         }
 
-        public override Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        public override Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos)
         {
             throw new NotImplementedException();
         }
 
-        public override Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        public override Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos)
         {
             throw new NotImplementedException();
         }

+ 12 - 5
src/MineCase.Server.Grains/World/Decoration/Biomes/BiomePlainsDecoratorGrain.cs

@@ -7,6 +7,7 @@ using MineCase.Server.World.Decoration.Plants;
 using MineCase.World;
 using MineCase.World.Biomes;
 using MineCase.World.Plants;
+using Newtonsoft.Json;
 using Orleans;
 using Orleans.Concurrency;
 
@@ -64,18 +65,24 @@ namespace MineCase.Server.World.Decoration.Biomes
             return Task.CompletedTask;
         }
 
-        public override Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        public async override Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos)
         {
-            var grassGenerator = GrainFactory.GetGrain<IGrassGenerator>(0);
-            return grassGenerator.Generate(world, chunkWorldPos, 10, 0);
+            PlantsInfo info = new PlantsInfo();
+            String infoString = JsonConvert.SerializeObject(info);
+
+            var grassGenerator = GrainFactory.GetGrain<IGrassGenerator>(infoString);
+            await grassGenerator.Generate(world, chunkWorldPos, 10, 0);
+
+            var treeGenerator = GrainFactory.GetGrain<ITreeGenerator>(infoString);
+            await treeGenerator.Generate(world, chunkWorldPos, 2, 1);
         }
 
-        public override Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        public override Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos)
         {
             throw new NotImplementedException();
         }
 
-        public override Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        public override Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos)
         {
             throw new NotImplementedException();
         }

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

@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using MineCase.World.Plants;
+
+namespace MineCase.Server.World.Decoration.Plants
+{
+    public abstract class AbstractTreeGeneratorGrain : PlantsGeneratorGrain, IAbstractTreeGenerator
+    {
+        public static bool CanSustainTree(PlantsType type, BlockState state)
+        {
+            if (state == BlockStates.Dirt() ||
+                state == BlockStates.GrassBlock())
+            {
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+    }
+}

+ 53 - 1
src/MineCase.Server.Grains/World/Decoration/Plants/PlantsGeneratorGrain.cs

@@ -4,11 +4,26 @@ using MineCase.Algorithm.World.Biomes;
 using MineCase.Server.World;
 using MineCase.Server.World.Decoration;
 using MineCase.World;
+using MineCase.World.Plants;
+using Newtonsoft.Json;
 using Orleans;
 using Orleans.Concurrency;
 
 namespace MineCase.Server.World.Decoration.Plants
 {
+    [JsonObject(MemberSerialization.OptOut)]
+    public class PlantsInfo
+    {
+        [JsonProperty(PropertyName = "TreeHeight")]
+        public int TreeHeight { get; set; } = 5;
+
+        [JsonProperty(PropertyName = "TreeVine")]
+        public bool TreeVine { get; set; } = false;
+
+        [JsonProperty(PropertyName = "TreeType")]
+        public PlantsType TreeType { get; set; } = PlantsType.Oak;
+    }
+
     public abstract class PlantsGeneratorGrain : Grain, IPlantsGenerator
     {
         protected virtual Task SetBlock(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos, BlockState state)
@@ -33,6 +48,42 @@ namespace MineCase.Server.World.Decoration.Plants
             return chunkGrain.GetBlockStateUnsafe(blockChunkPos.X, blockChunkPos.Y, blockChunkPos.Z);
         }
 
+        protected async virtual Task SetIfAir(IWorld world, ChunkWorldPos chunkWorldPos, int x, int y, int z, BlockState block)
+        {
+            BlockState state = await GetBlock(world, chunkWorldPos, new BlockWorldPos(x, y, z));
+            if (state.IsAir())
+            {
+                await SetBlock(world, chunkWorldPos, new BlockWorldPos(x, y, z), block);
+            }
+        }
+
+        protected async virtual Task SetIfAir(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos, BlockState block)
+        {
+            BlockState state = await GetBlock(world, chunkWorldPos, pos);
+            if (state.IsAir())
+            {
+                await SetBlock(world, chunkWorldPos, pos, block);
+            }
+        }
+
+        protected async virtual Task RandomSetIfAir(IWorld world, ChunkWorldPos chunkWorldPos, int x, int y, int z, BlockState block, Random rand, float freq)
+        {
+            BlockState state = await GetBlock(world, chunkWorldPos, new BlockWorldPos(x, y, z));
+            if (rand.NextDouble() < freq && state.IsAir())
+            {
+                await SetBlock(world, chunkWorldPos, new BlockWorldPos(x, y, z), block);
+            }
+        }
+
+        protected async virtual Task RandomSetIfAir(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos, BlockState block, Random rand, float freq)
+        {
+            BlockState state = await GetBlock(world, chunkWorldPos, pos);
+            if (rand.NextDouble() < freq && state.IsAir())
+            {
+                await SetBlock(world, chunkWorldPos, pos, block);
+            }
+        }
+
         private Task<int> GetGroundHeight(IWorld world, ChunkWorldPos chunkWorldPos, int x, int z)
         {
             var blockChunkPos = new BlockWorldPos { X = x, Z = z }.ToBlockChunkPos();
@@ -55,8 +106,9 @@ namespace MineCase.Server.World.Decoration.Plants
 
                     int chunkSeed = (pos.X + chunkXOffset) ^ (pos.Z + chunkZOffset) ^ seed;
                     Random rand = new Random(chunkSeed);
+                    int countCurChunk = rand.Next(countPerChunk);
 
-                    for (int count = 0; count < countPerChunk; ++count)
+                    for (int count = 0; count < countCurChunk; ++count)
                     {
                         int x = curChunkCorner.X + rand.Next(16);
                         int z = curChunkCorner.Z + rand.Next(16);

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

@@ -0,0 +1,233 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using MineCase.World;
+using MineCase.World.Plants;
+using Newtonsoft.Json;
+using Orleans;
+using Orleans.Concurrency;
+
+namespace MineCase.Server.World.Decoration.Plants
+{
+    [StatelessWorker]
+    public class TreeGeneratorGrain : AbstractTreeGeneratorGrain, ITreeGenerator
+    {
+        private readonly ILogger _logger;
+
+        private int _minTreeHeight;
+
+        private bool _vines;
+
+        private BlockState _wood;
+
+        private BlockState _leaves;
+
+        private PlantsType _treeType;
+
+        public TreeGeneratorGrain(ILoggerFactory loggerFactory)
+        {
+            _logger = loggerFactory.CreateLogger<TreeGeneratorGrain>();
+        }
+
+        public override Task OnActivateAsync()
+        {
+            try
+            {
+                var settings = this.GetPrimaryKeyString();
+                PlantsInfo plantsInfo = JsonConvert.DeserializeObject<PlantsInfo>(settings);
+
+                _minTreeHeight = plantsInfo.TreeHeight;
+                _vines = plantsInfo.TreeVine;
+                _treeType = plantsInfo.TreeType;
+                if (plantsInfo.TreeType == PlantsType.Oak)
+                {
+                    _wood = BlockStates.Wood(WoodType.Oak);
+                    _leaves = BlockStates.Leaves(LeaveType.Oak);
+                }
+                else if (plantsInfo.TreeType == PlantsType.Spruce)
+                {
+                    _wood = BlockStates.Wood(WoodType.Spruce);
+                    _leaves = BlockStates.Leaves(LeaveType.Spruce);
+                }
+                else if (plantsInfo.TreeType == PlantsType.Birch)
+                {
+                    _wood = BlockStates.Wood(WoodType.Birch);
+                    _leaves = BlockStates.Leaves(LeaveType.Birch);
+                }
+            }
+            catch (Exception e)
+            {
+                this._logger.LogError(default(EventId), e, e.Message);
+            }
+
+            return base.OnActivateAsync();
+        }
+
+        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);
+        }
+
+        public async Task<bool> CanTreeGrow(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos, int height)
+        {
+            bool result = true;
+
+            // 检查所有方块可替换
+            for (int y = pos.Y; y <= pos.Y + 1 + height; ++y)
+            {
+                int xzSize = 1;
+
+                // 底端
+                if (y == pos.Y)
+                {
+                    xzSize = 0;
+                }
+
+                // 顶端
+                if (y >= pos.Y + height - 1)
+                {
+                    xzSize = 2;
+                }
+
+                // 检查这个平面所有方块可替换
+                for (int x = pos.X - xzSize; x <= pos.X + xzSize && result; ++x)
+                {
+                    for (int z = pos.Z - xzSize; z <= pos.Z + xzSize && result; ++z)
+                    {
+                        if (y >= 0 && y < 256)
+                        {
+                            BlockState state = await GetBlock(world, chunkWorldPos, pos);
+                            if (!state.IsAir() &&
+                                state.IsSameId(BlockStates.Leaves()) &&
+                                state.IsSameId(BlockStates.Leaves2()))
+                            {
+                                result = false;
+                            }
+                        }
+                        else
+                        {
+                            result = false;
+                        }
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        protected async Task GenerateImpl(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos, Random random)
+        {
+            int height = random.Next(3) + _minTreeHeight;
+
+            // 不超出世界边界
+            if (pos.Y >= 1 && pos.Y + height + 1 <= 256)
+            {
+                bool canTreeGrow = await CanTreeGrow(world, chunkWorldPos, pos, height);
+                if (canTreeGrow)
+                {
+                    BlockWorldPos downPos = new BlockWorldPos(pos.X, pos.Y - 1, pos.Z);
+                    BlockState downBlock = await GetBlock(world, chunkWorldPos, downPos);
+
+                    // 是可生成树的土壤
+                    bool isSoil = CanSustainTree(_treeType, downBlock);
+
+                    if (isSoil && pos.Y < 256 - height - 1)
+                    {
+                        // 生成叶子
+                        for (int y = pos.Y + height - 3; y <= pos.Y + height; ++y)
+                        {
+                            int restHeight = y - (pos.Y + height);
+                            int xzSize = 1 - restHeight / 2;
+
+                            for (int x = pos.X - xzSize; x <= pos.X + xzSize; ++x)
+                            {
+                                int xOffset = x - pos.X;
+
+                                for (int z = pos.Z - xzSize; z <= pos.Z + xzSize; ++z)
+                                {
+                                    int zOffset = z - pos.Z;
+
+                                    if (Math.Abs(xOffset) != xzSize
+                                        || Math.Abs(zOffset) != xzSize // 不在边缘4个点
+                                        || (random.Next(2) != 0
+                                        && restHeight != 0))
+                                    {
+                                        BlockWorldPos blockpos = new BlockWorldPos(x, y, z);
+                                        BlockState block = await GetBlock(world, chunkWorldPos, blockpos);
+
+                                        if (block.IsAir()
+                                            || block.IsSameId(BlockStates.Leaves())
+                                            || block.IsSameId(BlockStates.Vines()))
+                                        {
+                                            await SetBlock(world, chunkWorldPos, blockpos, _leaves);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+
+                        // 生成木头
+                        BlockWorldPos upPos = pos;
+                        for (int y = 0; y < height; ++y)
+                        {
+                            BlockState upBlock = await GetBlock(world, chunkWorldPos, upPos);
+
+                            if (upBlock.IsAir()
+                                            || upBlock.IsSameId(BlockStates.Leaves())
+                                            || upBlock.IsSameId(BlockStates.Vines()))
+                            {
+                                await SetBlock(world, chunkWorldPos, upPos, _wood);
+                            }
+
+                            // 生成藤蔓
+                            if (_vines && y > 0)
+                            {
+                                await RandomSetIfAir(world, chunkWorldPos, upPos.X - 1, upPos.Y, upPos.Z, BlockStates.Vines(VineType.East), random, 0.666f);
+
+                                await RandomSetIfAir(world, chunkWorldPos, upPos.X + 1, upPos.Y, upPos.Z, BlockStates.Vines(VineType.West), random, 0.666f);
+
+                                await RandomSetIfAir(world, chunkWorldPos, upPos.X, upPos.Y, upPos.Z - 1, BlockStates.Vines(VineType.South), random, 0.666f);
+
+                                await RandomSetIfAir(world, chunkWorldPos, upPos.X, upPos.Y, upPos.Z + 1, BlockStates.Vines(VineType.North), random, 0.666f);
+                            }
+
+                            ++upPos.Y;
+                        }
+
+                        // 生成藤蔓
+                        if (_vines)
+                        {
+                            for (int y = pos.Y + height - 3; y <= pos.Y + height; ++y)
+                            {
+                                int restHeight = y - (pos.Y + height);
+                                int xzSize = 2 - restHeight / 2;
+
+                                for (int x = pos.X - xzSize; x <= pos.X + xzSize; ++x)
+                                {
+                                    for (int z = pos.Z - xzSize; z <= pos.Z + xzSize; ++z)
+                                    {
+                                        if ((await GetBlock(world, chunkWorldPos, new BlockWorldPos(x, y, z))).IsLeaves())
+                                        {
+                                            await RandomSetIfAir(world, chunkWorldPos, x - 1, y, z, BlockStates.Vines(VineType.East), random, 0.25f);
+
+                                            await RandomSetIfAir(world, chunkWorldPos, x + 1, y, z, BlockStates.Vines(VineType.West), random, 0.25f);
+
+                                            await RandomSetIfAir(world, chunkWorldPos, x, y, z - 1, BlockStates.Vines(VineType.South), random, 0.25f);
+
+                                            await RandomSetIfAir(world, chunkWorldPos, x, y, z + 1, BlockStates.Vines(VineType.North), random, 0.25f);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 14 - 17
src/MineCase.Server.Grains/World/Generation/ChunkGeneratorOverworldGrain.cs

@@ -101,7 +101,20 @@ namespace MineCase.Server.World.Generation
         {
             var chunkColumnKey = world.MakeAddressByPartitionKey(new ChunkWorldPos { X = x, Z = z });
             ChunkColumnCompactStorage chunkColumn = await GrainFactory.GetGrain<IChunkColumn>(chunkColumnKey).GetStateUnsafe();
-            PopulateChunk(world, chunkColumn, x, z, settings);
+            Biome chunkBiome = Biome.GetBiome(chunkColumn.Biomes[7 * 16 + 7], settings);
+
+            if (chunkBiome.GetBiomeId() == BiomeId.Plains)
+            {
+                var decorator = GrainFactory.GetGrain<IBiomePlainsDecorator>((long)BiomeId.Plains);
+                await decorator.Decorate(world, new ChunkWorldPos(x, z));
+
+                // decorator.SpawnMob(world, chunk, new ChunkWorldPos(x, z), new BlockWorldPos { X = blockX, Z = blockZ });
+            }
+            else if (chunkBiome.GetBiomeId() == BiomeId.Forest)
+            {
+                var decorator = GrainFactory.GetGrain<IBiomeForestDecorator>((long)BiomeId.Forest);
+                await decorator.Decorate(world, new ChunkWorldPos(x, z));
+            }
         }
 
         private void GenerateChunk(MapGenerationInfo info, ChunkColumnStorage chunk, int x, int z, GeneratorSettings settings)
@@ -158,22 +171,6 @@ namespace MineCase.Server.World.Generation
 
         public void PopulateChunk(IWorld world, ChunkColumnCompactStorage chunk, int x, int z, GeneratorSettings settings)
         {
-            int blockX = x * 16;
-            int blockZ = z * 16;
-            Biome chunkBiome = Biome.GetBiome(chunk.Biomes[7 * 16 + 7], settings);
-
-            if (chunkBiome.GetBiomeId() == BiomeId.Plains)
-            {
-                var decorator = GrainFactory.GetGrain<IBiomePlainsDecorator>((long)BiomeId.Plains);
-                decorator.Decorate(world, new ChunkWorldPos(x, z), new BlockWorldPos { X = blockX, Z = blockZ });
-
-                // decorator.SpawnMob(world, chunk, new ChunkWorldPos(x, z), new BlockWorldPos { X = blockX, Z = blockZ });
-            }
-            else if (chunkBiome.GetBiomeId() == BiomeId.Forest)
-            {
-                var decorator = GrainFactory.GetGrain<IBiomeForestDecorator>((long)BiomeId.Forest);
-                decorator.Decorate(world, new ChunkWorldPos(x, z), new BlockWorldPos { X = blockX, Z = blockZ });
-            }
         }
 
         private void GenerateBasicTerrain(ChunkColumnStorage chunk, int x, int z, GeneratorSettings settings)

+ 3 - 3
src/MineCase.Server.Interfaces/World/Decoration/Biomes/IBiomeDecorator.cs

@@ -9,10 +9,10 @@ namespace MineCase.Server.World.Decoration.Biomes
 {
     public interface IBiomeDecorator : IGrainWithIntegerKey
     {
-        Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos);
+        Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos);
 
-        Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos);
+        Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos);
 
-        Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos);
+        Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos);
     }
 }

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

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

+ 1 - 1
src/MineCase.Server.Interfaces/World/Decoration/Plants/IPlantsGenerator.cs

@@ -7,7 +7,7 @@ using Orleans;
 
 namespace MineCase.Server.World.Decoration.Plants
 {
-    public interface IPlantsGenerator : IGrainWithIntegerKey
+    public interface IPlantsGenerator : IGrainWithStringKey
     {
         Task Generate(IWorld world, ChunkWorldPos pos, int countPerChunk, int range);
     }

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

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