Ver código fonte

Add Taiga Generator

JasonWang 6 anos atrás
pai
commit
f1b111b16a

+ 1 - 0
.gitattributes

@@ -43,6 +43,7 @@
 #*.jpg   binary
 #*.jpg   binary
 #*.png   binary
 #*.png   binary
 #*.gif   binary
 #*.gif   binary
+#*.bmp   binary
 
 
 ###############################################################################
 ###############################################################################
 # diff behavior for common document formats
 # diff behavior for common document formats

+ 8 - 1
README-zh.md

@@ -45,18 +45,25 @@ MineCase
 	##### 建议:
 	##### 建议:
 	* 你可以输入 `docker-compose stop`停止服务器的运行。
 	* 你可以输入 `docker-compose stop`停止服务器的运行。
 
 
+## 参与开发
+我们需要帮助以使MineCase更好。 您可以通过修复错误,开发新功能,改进文档来帮助我们。
+一些新的贡献者想知道该做些什么来帮助我们的开发。 该项目始于对Minecraft的热爱,所以我们的答案始终是“做你喜欢的事”。
+我们珍爱你们的帮助,更喜爱看到你们在MineCase中做着你们所热爱的工作。
+
 ## 联系我们
 ## 联系我们
   此项目尚在开发之中。
   此项目尚在开发之中。
 您可以使用`Pull Requests`提交您的代码或者通过`e-mail` 或 `issues` 联系我们, 我会将您的加入我们开发团队之中。如果你有任何问题可以在[Issues](https://github.com/dotnetGame/MineCase/issues)中一同讨论。当您在使用MineCase的过程中遇到任何问题或者你有好的建议,也可以通过[Issues](https://github.com/dotnetGame/MineCase/issues)发给我们。
 您可以使用`Pull Requests`提交您的代码或者通过`e-mail` 或 `issues` 联系我们, 我会将您的加入我们开发团队之中。如果你有任何问题可以在[Issues](https://github.com/dotnetGame/MineCase/issues)中一同讨论。当您在使用MineCase的过程中遇到任何问题或者你有好的建议,也可以通过[Issues](https://github.com/dotnetGame/MineCase/issues)发给我们。
 我们欢迎且感谢您对我们项目的贡献。
 我们欢迎且感谢您对我们项目的贡献。
 
 
 * 通过e-mail联系我: [email protected]
 * 通过e-mail联系我: [email protected]
+* [Discord](https://discord.gg/8Z5RSRn) : MineCase
+* QQ群: 667481568
 
 
 [License (MIT)](https://raw.githubusercontent.com/dotnetGame/MineCase/master/LICENSE)
 [License (MIT)](https://raw.githubusercontent.com/dotnetGame/MineCase/master/LICENSE)
 -------------------------------------------------------------------------------
 -------------------------------------------------------------------------------
 	MIT License
 	MIT License
 
 
-	Copyright (c) 2017 MineCase
+	Copyright (c) 2017-2019 MineCase
 
 
 	Permission is hereby granted, free of charge, to any person obtaining a copy
 	Permission is hereby granted, free of charge, to any person obtaining a copy
 	of this software and associated documentation files (the "Software"), to deal
 	of this software and associated documentation files (the "Software"), to deal

+ 3 - 1
README.md

@@ -52,12 +52,14 @@ and also any questions you may have while using this server, or any good suggest
 we welcome and thank your contribution for this project.
 we welcome and thank your contribution for this project.
 
 
 * Reach me via e-mail: [email protected]
 * Reach me via e-mail: [email protected]
+* [Discord](https://discord.gg/8Z5RSRn) : MineCase
+* QQ Group: 667481568
 
 
 [License (MIT)](https://raw.githubusercontent.com/dotnetGame/MineCase/master/LICENSE)
 [License (MIT)](https://raw.githubusercontent.com/dotnetGame/MineCase/master/LICENSE)
 -------------------------------------------------------------------------------
 -------------------------------------------------------------------------------
 	MIT License
 	MIT License
 
 
-	Copyright (c) 2017 MineCase
+	Copyright (c) 2017-2019 MineCase
 
 
 	Permission is hereby granted, free of charge, to any person obtaining a copy
 	Permission is hereby granted, free of charge, to any person obtaining a copy
 	of this software and associated documentation files (the "Software"), to deal
 	of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
src/MineCase.Algorithm/World/Layer/GenLayerBiome.cs

@@ -37,7 +37,7 @@ namespace MineCase.Algorithm.World.Layer
                         {
                         {
                             parentResult[i, j] = (int)BiomeId.Desert;
                             parentResult[i, j] = (int)BiomeId.Desert;
                         }
                         }
-                        else if (r >= 5 && r < 6)
+                        else if (r >= 5 && r < 7)
                         {
                         {
                             parentResult[i, j] = (int)BiomeId.Taiga;
                             parentResult[i, j] = (int)BiomeId.Taiga;
                         }
                         }

+ 12 - 0
src/MineCase.Core/World/Biomes/BiomeId.cs

@@ -27,6 +27,18 @@ namespace MineCase.World.Biomes
         WoodedHills = 18,
         WoodedHills = 18,
         TaigaHills = 19,
         TaigaHills = 19,
         DeepOcean = 24,
         DeepOcean = 24,
+        StoneShore = 25,
+        SnowyBeach = 26,
+        BirchForest = 27,
+        WarmOcean = 44,
+        LukewarmOcean = 45,
+        ColdOcean = 46,
+        DeepWarmOcean = 47,
+        DeepLukewarmOcean = 48,
+        DeepColdOcean = 49,
+        DeepFrozenOcean = 50,
+
         SunflowerPlains = 129,
         SunflowerPlains = 129,
+        FlowerForest = 132,
     }
     }
 }
 }

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

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

+ 93 - 0
src/MineCase.Server.Grains/World/Decoration/Biomes/BiomeHillDecoratorGrain.cs

@@ -1,10 +1,103 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Text;
 using System.Text;
+using System.Threading.Tasks;
+using MineCase.Server.Game.Entities;
+using MineCase.Server.World.Decoration.Plants;
+using MineCase.World;
+using MineCase.World.Biomes;
+using MineCase.World.Plants;
+using Newtonsoft.Json;
+using Orleans;
 
 
 namespace MineCase.Server.World.Decoration.Biomes
 namespace MineCase.Server.World.Decoration.Biomes
 {
 {
     public class BiomeHillDecoratorGrain : BiomeDecoratorGrain, IBiomeHillDecorator
     public class BiomeHillDecoratorGrain : BiomeDecoratorGrain, IBiomeHillDecorator
     {
     {
+        public override Task OnActivateAsync()
+        {
+            if (this.GetPrimaryKeyLong() == (long)BiomeId.ExtremeHills)
+            {
+                BiomeProperties.BaseHeight = 1.0f;
+                BiomeProperties.HeightVariation = 0.5f;
+                BiomeProperties.Temperature = 0.2f;
+                BiomeProperties.Rainfall = 0.3f;
+                BiomeProperties.EnableSnow = false;
+            }
+            else if (this.GetPrimaryKeyLong() == (long)BiomeId.DesertHills)
+            {
+                BiomeProperties.BaseHeight = 0.45f;
+                BiomeProperties.HeightVariation = 0.3f;
+                BiomeProperties.Temperature = 2.0f;
+                BiomeProperties.Rainfall = 0.0f;
+                BiomeProperties.EnableSnow = false;
+            }
+            else if (this.GetPrimaryKeyLong() == (long)BiomeId.TaigaHills)
+            {
+                BiomeProperties.BaseHeight = 0.45f;
+                BiomeProperties.HeightVariation = 0.3f;
+                BiomeProperties.Temperature = 0.25f;
+                BiomeProperties.Rainfall = 0.8f;
+                BiomeProperties.EnableSnow = true;
+            }
+
+            BiomeProperties.WaterColor = 16777215;
+            BiomeProperties.EnableRain = true;
+            TopBlock = BlockStates.GrassBlock();
+            FillerBlock = BlockStates.Dirt();
+
+            PlantsList.Add(PlantsType.TallGrass);
+            PlantsList.Add(PlantsType.Poppy);
+            PlantsList.Add(PlantsType.Dandelion);
+
+            PassiveMobList.Add(MobType.Cow);
+            PassiveMobList.Add(MobType.Sheep);
+            PassiveMobList.Add(MobType.Horse);
+            PassiveMobList.Add(MobType.Donkey);
+
+            MonsterList.Add(MobType.Creeper);
+            MonsterList.Add(MobType.Skeleton);
+            MonsterList.Add(MobType.Zombie);
+            MonsterList.Add(MobType.Spider);
+
+            return Task.CompletedTask;
+        }
+
+        public async override Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos)
+        {
+            var grassGenerator = GrainFactory.GetGrain<IGrassGenerator>(JsonConvert.SerializeObject(new PlantsInfo { }));
+            await grassGenerator.Generate(world, chunkWorldPos, 10);
+
+            var poppyGenerator = GrainFactory.GetGrain<IFlowersGenerator>(
+                JsonConvert.SerializeObject(new PlantsInfo
+                {
+                    PlantType = PlantsType.Poppy
+                }));
+            await poppyGenerator.Generate(world, chunkWorldPos, 3);
+
+            var dandelionGenerator = GrainFactory.GetGrain<IFlowersGenerator>(
+                JsonConvert.SerializeObject(new PlantsInfo
+                {
+                    PlantType = PlantsType.Dandelion
+                }));
+            await dandelionGenerator.Generate(world, chunkWorldPos, 3);
+
+            var oaktreeGenerator = GrainFactory.GetGrain<ITreeGenerator>(
+                JsonConvert.SerializeObject(new PlantsInfo
+                {
+                    PlantType = PlantsType.Oak
+                }));
+            await oaktreeGenerator.Generate(world, chunkWorldPos, 1);
+        }
+
+        public override Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos)
+        {
+            throw new NotImplementedException();
+        }
     }
     }
 }
 }

+ 26 - 0
src/MineCase.Server.Grains/World/Decoration/Biomes/BiomeSavannaDecoratorGrain.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using MineCase.World;
+
+namespace MineCase.Server.World.Decoration.Biomes
+{
+    public class BiomeSavannaDecoratorGrain : BiomeDecoratorGrain, IBiomeSavannaDecorator
+    {
+        public override Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 92 - 0
src/MineCase.Server.Grains/World/Decoration/Biomes/BiomeTaigaDecoratorGrain.cs

@@ -1,10 +1,102 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Text;
 using System.Text;
+using System.Threading.Tasks;
+using MineCase.Server.Game.Entities;
+using MineCase.Server.World.Decoration.Plants;
+using MineCase.World;
+using MineCase.World.Biomes;
+using MineCase.World.Plants;
+using Newtonsoft.Json;
+using Orleans;
 
 
 namespace MineCase.Server.World.Decoration.Biomes
 namespace MineCase.Server.World.Decoration.Biomes
 {
 {
     public class BiomeTaigaDecoratorGrain : BiomeDecoratorGrain, IBiomeTaigaDecorator
     public class BiomeTaigaDecoratorGrain : BiomeDecoratorGrain, IBiomeTaigaDecorator
     {
     {
+        public override Task OnActivateAsync()
+        {
+            if (this.GetPrimaryKeyLong() == (long)BiomeId.Taiga)
+            {
+                BiomeProperties.BaseHeight = 0.2f;
+                BiomeProperties.HeightVariation = 0.2f;
+                BiomeProperties.Temperature = 0.25f;
+                BiomeProperties.Rainfall = 0.8f;
+                BiomeProperties.EnableSnow = false;
+            }
+            else if (this.GetPrimaryKeyLong() == (long)BiomeId.TaigaHills)
+            {
+                BiomeProperties.BaseHeight = 0.45f;
+                BiomeProperties.HeightVariation = 0.3f;
+                BiomeProperties.Temperature = 0.25f;
+                BiomeProperties.Rainfall = 0.8f;
+                BiomeProperties.EnableSnow = false;
+            }
+
+            BiomeProperties.WaterColor = 16777215;
+            BiomeProperties.EnableRain = true;
+            TopBlock = BlockStates.GrassBlock();
+            FillerBlock = BlockStates.Dirt();
+
+            PlantsList.Add(PlantsType.TallGrass);
+            PlantsList.Add(PlantsType.Poppy);
+            PlantsList.Add(PlantsType.Dandelion);
+
+            PassiveMobList.Add(MobType.Cow);
+            PassiveMobList.Add(MobType.Sheep);
+            PassiveMobList.Add(MobType.Horse);
+            PassiveMobList.Add(MobType.Donkey);
+
+            MonsterList.Add(MobType.Creeper);
+            MonsterList.Add(MobType.Skeleton);
+            MonsterList.Add(MobType.Zombie);
+            MonsterList.Add(MobType.Spider);
+
+            return Task.CompletedTask;
+        }
+
+        public async override Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos)
+        {
+            var grassGenerator = GrainFactory.GetGrain<IGrassGenerator>(JsonConvert.SerializeObject(new PlantsInfo { }));
+            await grassGenerator.Generate(world, chunkWorldPos, 4);
+
+            var taiga1Generator = GrainFactory.GetGrain<ITaigaGenerator>(
+                JsonConvert.SerializeObject(new PlantsInfo
+                {
+                    PlantType = PlantsType.Spruce
+                }));
+            await taiga1Generator.Generate(world, chunkWorldPos, 10);
+
+            var taiga2Generator = GrainFactory.GetGrain<ITaiga2Generator>(
+                JsonConvert.SerializeObject(new PlantsInfo
+                {
+                    PlantType = PlantsType.Spruce
+                }));
+            await taiga2Generator.Generate(world, chunkWorldPos, 10);
+
+            var poppyGenerator = GrainFactory.GetGrain<IFlowersGenerator>(
+                JsonConvert.SerializeObject(new PlantsInfo
+                {
+                    PlantType = PlantsType.Poppy
+                }));
+            await poppyGenerator.Generate(world, chunkWorldPos, 1);
+
+            var dandelionGenerator = GrainFactory.GetGrain<IFlowersGenerator>(
+                JsonConvert.SerializeObject(new PlantsInfo
+                {
+                    PlantType = PlantsType.Dandelion
+                }));
+            await dandelionGenerator.Generate(world, chunkWorldPos, 1);
+        }
+
+        public override Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos)
+        {
+            throw new NotImplementedException();
+        }
     }
     }
 }
 }

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

@@ -0,0 +1,195 @@
+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 Taiga2GeneratorGrain : AbstractTreeGeneratorGrain, ITaiga2Generator
+    {
+        private readonly ILogger _logger;
+
+        private int _minTreeHeight;
+
+        private bool _vines;
+
+        private PlantsType _treeType;
+
+        private BlockState _wood;
+
+        private BlockState _leaves;
+
+        public Taiga2GeneratorGrain(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.PlantType;
+                if (plantsInfo.PlantType == PlantsType.Spruce)
+                {
+                    _wood = BlockStates.Wood(WoodType.Spruce);
+                    _leaves = BlockStates.Leaves(LeaveType.Spruce);
+                }
+            }
+            catch (Exception e)
+            {
+                this._logger.LogError(default(EventId), e, e.Message);
+            }
+
+            return base.OnActivateAsync();
+        }
+
+        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 random)
+        {
+            int height = random.Next(4) + 6;
+            int j = 1 + random.Next(2);
+            int k = height - j;
+            int l = 2 + random.Next(2);
+            bool canSustainTreeFlag = true;
+
+            if (pos.Y >= 1 && pos.Y + height + 1 <= 255)
+            {
+                for (int y = pos.Y; y <= pos.Y + 1 + height && canSustainTreeFlag; ++y)
+                {
+                    int xzWidth;
+
+                    if (y - pos.Y < j)
+                    {
+                        xzWidth = 0;
+                    }
+                    else
+                    {
+                        xzWidth = l;
+                    }
+
+                    for (int x = pos.X - xzWidth; x <= pos.X + xzWidth && canSustainTreeFlag; ++x)
+                    {
+                        for (int z = pos.Z - xzWidth; z <= pos.Z + xzWidth && canSustainTreeFlag; ++z)
+                        {
+                            if (y >= 0 && y < 256)
+                            {
+                                BlockState state = await GetBlock(world, chunkWorldPos, new BlockWorldPos(x, y, z));
+
+                                if (!(state.IsAir()
+                                            || state.IsLeaves()))
+                                {
+                                    canSustainTreeFlag = false;
+                                }
+                            }
+                            else
+                            {
+                                canSustainTreeFlag = false;
+                            }
+                        }
+                    }
+                }
+
+                if (!canSustainTreeFlag)
+                {
+                    return false;
+                }
+                else
+                {
+                    BlockState state = await GetBlock(world, chunkWorldPos, new BlockWorldPos(pos.X, pos.Y - 1, pos.Z));
+
+                    if (CanSustainTree(PlantsType.Spruce, state) && pos.Y < 256 - height - 1)
+                    {
+                        int xzWidth = random.Next(2);
+                        int j3 = 1;
+                        int k3 = 0;
+
+                        for (int l3 = 0; l3 <= k; ++l3)
+                        {
+                            int y = pos.Y + height - l3;
+
+                            for (int x = pos.X - xzWidth; x <= pos.X + xzWidth; ++x)
+                            {
+                                int deltaX = x - pos.X;
+
+                                for (int z = pos.Z - xzWidth; z <= pos.Z + xzWidth; ++z)
+                                {
+                                    int deltaZ = z - pos.Z;
+
+                                    if (Math.Abs(deltaX) != xzWidth || Math.Abs(deltaZ) != xzWidth || xzWidth <= 0)
+                                    {
+                                        state = await GetBlock(world, chunkWorldPos, new BlockWorldPos(x, y, z));
+
+                                        if (state.IsAir()
+                                            || state.IsLeaves()
+                                            || state.IsSameId(BlockStates.Vines()))
+                                        {
+                                            await SetBlock(world, chunkWorldPos, new BlockWorldPos(x, y, z), _leaves);
+                                        }
+                                    }
+                                }
+                            }
+
+                            if (xzWidth >= j3)
+                            {
+                                xzWidth = k3;
+                                k3 = 1;
+                                ++j3;
+
+                                if (j3 > l)
+                                {
+                                    j3 = l;
+                                }
+                            }
+                            else
+                            {
+                                ++xzWidth;
+                            }
+                        }
+
+                        int heightLeft = random.Next(3);
+
+                        for (int y = 0; y < height - heightLeft; ++y)
+                        {
+                            BlockWorldPos upN = new BlockWorldPos(pos.X, pos.Y + y, pos.Z);
+                            state = await GetBlock(world, chunkWorldPos, upN);
+
+                            if (state.IsAir() || state.IsSameId(BlockStates.Leaves()) || state.IsSameId(BlockStates.Leaves2()))
+                            {
+                                await SetBlock(world, chunkWorldPos, upN, _wood);
+                            }
+                        }
+
+                        return true;
+                    }
+                    else
+                    {
+                        return false;
+                    }
+                }
+            }
+            else
+            {
+                return false;
+            }
+        }
+    }
+}

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

@@ -0,0 +1,184 @@
+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 TaigaGeneratorGrain : AbstractTreeGeneratorGrain, ITaigaGenerator
+    {
+        private readonly ILogger _logger;
+
+        private int _minTreeHeight;
+
+        private bool _vines;
+
+        private PlantsType _treeType;
+
+        private BlockState _wood;
+
+        private BlockState _leaves;
+
+        public TaigaGeneratorGrain(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.PlantType;
+                if (plantsInfo.PlantType == PlantsType.Spruce)
+                {
+                    _wood = BlockStates.Wood(WoodType.Spruce);
+                    _leaves = BlockStates.Leaves(LeaveType.Spruce);
+                }
+            }
+            catch (Exception e)
+            {
+                this._logger.LogError(default(EventId), e, e.Message);
+            }
+
+            return base.OnActivateAsync();
+        }
+
+        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 random)
+        {
+            int height = random.Next(5) + 7;
+            int heightLeaves = height - random.Next(2) - 3;
+            int k = height - heightLeaves;
+            int l = 1 + random.Next(k + 1);
+
+            if (pos.Y >= 1 && pos.Y + height + 1 <= 256)
+            {
+                bool canSustainTreeFlag = true;
+
+                for (int y = pos.Y; y <= pos.Y + 1 + height && canSustainTreeFlag; ++y)
+                {
+                    int xzWidth = 1;
+
+                    if (y - pos.Y < heightLeaves)
+                    {
+                        xzWidth = 0;
+                    }
+                    else
+                    {
+                        xzWidth = l;
+                    }
+
+                    for (int x = pos.X - xzWidth; x <= pos.X + xzWidth && canSustainTreeFlag; ++x)
+                    {
+                        for (int z = pos.Z - xzWidth; z <= pos.Z + xzWidth && canSustainTreeFlag; ++z)
+                        {
+                            if (y >= 0 && y < 256)
+                            {
+                                BlockState block = await GetBlock(world, chunkWorldPos, new BlockWorldPos(x, y, z));
+                                if (!(block.IsAir()
+                                            || block.IsLeaves()
+                                            || block.IsSameId(BlockStates.Vines())))
+                                {
+                                    canSustainTreeFlag = false;
+                                }
+                            }
+                            else
+                            {
+                                canSustainTreeFlag = false;
+                            }
+                        }
+                    }
+                }
+
+                if (!canSustainTreeFlag)
+                {
+                    return false;
+                }
+                else
+                {
+                    BlockState state = await GetBlock(world, chunkWorldPos, new BlockWorldPos(pos.X, pos.Y - 1, pos.Z));
+                    bool isSoil = CanSustainTree(PlantsType.Spruce, state);
+
+                    if (isSoil && pos.Y < 256 - height - 1)
+                    {
+                        int xzWidth = 0;
+
+                        for (int y = pos.Y + height; y >= pos.Y + heightLeaves; --y)
+                        {
+                            for (int x = pos.X - xzWidth; x <= pos.X + xzWidth; ++x)
+                            {
+                                int deltaX = x - pos.X;
+
+                                for (int z = pos.Z - xzWidth; z <= pos.Z + xzWidth; ++z)
+                                {
+                                    int deltaZ = z - pos.Z;
+
+                                    if (Math.Abs(deltaX) != xzWidth || Math.Abs(deltaZ) != xzWidth || xzWidth <= 0)
+                                    {
+                                        state = await GetBlock(world, chunkWorldPos, new BlockWorldPos(x, y, z));
+
+                                        if (state.IsAir()
+                                            || state.IsLeaves()
+                                            || state.IsSameId(BlockStates.Vines()))
+                                        {
+                                            await SetBlock(world, chunkWorldPos, new BlockWorldPos(x, y, z), _leaves);
+                                        }
+                                    }
+                                }
+                            }
+
+                            if (xzWidth >= 1 && y == pos.Y + heightLeaves + 1)
+                            {
+                                --xzWidth;
+                            }
+                            else if (xzWidth < l)
+                            {
+                                ++xzWidth;
+                            }
+                        }
+
+                        for (int y = 0; y < height - 1; ++y)
+                        {
+                            BlockWorldPos upN = new BlockWorldPos(pos.X, pos.Y + y, pos.Z);
+                            state = await GetBlock(world, chunkWorldPos, upN);
+
+                            if (state.IsAir() || state.IsLeaves())
+                            {
+                                await SetBlock(world, chunkWorldPos, upN, _wood);
+                            }
+                        }
+
+                        return true;
+                    }
+                    else
+                    {
+                        return false;
+                    }
+                }
+            }
+            else
+            {
+                return false;
+            }
+        }
+    }
+}

+ 5 - 0
src/MineCase.Server.Grains/World/Generation/ChunkGeneratorOverworldGrain.cs

@@ -115,6 +115,11 @@ namespace MineCase.Server.World.Generation
                 var decorator = GrainFactory.GetGrain<IBiomeForestDecorator>((long)BiomeId.Forest);
                 var decorator = GrainFactory.GetGrain<IBiomeForestDecorator>((long)BiomeId.Forest);
                 await decorator.Decorate(world, new ChunkWorldPos(x, z));
                 await decorator.Decorate(world, new ChunkWorldPos(x, z));
             }
             }
+            else if (chunkBiome.GetBiomeId() == BiomeId.Taiga)
+            {
+                var decorator = GrainFactory.GetGrain<IBiomeTaigaDecorator>((long)BiomeId.Taiga);
+                await decorator.Decorate(world, new ChunkWorldPos(x, z));
+            }
         }
         }
 
 
         private void GenerateChunk(MapGenerationInfo info, ChunkColumnStorage chunk, int x, int z, GeneratorSettings settings)
         private void GenerateChunk(MapGenerationInfo info, ChunkColumnStorage chunk, int x, int z, GeneratorSettings settings)

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

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

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

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

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

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

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

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