JasonWang преди 6 години
родител
ревизия
f1b111b16a

+ 1 - 0
.gitattributes

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

+ 8 - 1
README-zh.md

@@ -45,18 +45,25 @@ MineCase
 	##### 建议:
 	* 你可以输入 `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)发给我们。
 我们欢迎且感谢您对我们项目的贡献。
 
 * 通过e-mail联系我: [email protected]
+* [Discord](https://discord.gg/8Z5RSRn) : MineCase
+* QQ群: 667481568
 
 [License (MIT)](https://raw.githubusercontent.com/dotnetGame/MineCase/master/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
 	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.
 
 * 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)
 -------------------------------------------------------------------------------
 	MIT License
 
-	Copyright (c) 2017 MineCase
+	Copyright (c) 2017-2019 MineCase
 
 	Permission is hereby granted, free of charge, to any person obtaining a copy
 	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;
                         }
-                        else if (r >= 5 && r < 6)
+                        else if (r >= 5 && r < 7)
                         {
                             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,
         TaigaHills = 19,
         DeepOcean = 24,
+        StoneShore = 25,
+        SnowyBeach = 26,
+        BirchForest = 27,
+        WarmOcean = 44,
+        LukewarmOcean = 45,
+        ColdOcean = 46,
+        DeepWarmOcean = 47,
+        DeepLukewarmOcean = 48,
+        DeepColdOcean = 49,
+        DeepFrozenOcean = 50,
+
         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.Collections.Generic;
 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
 {
     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.Collections.Generic;
 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
 {
     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);
                 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)

+ 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
+    {
+    }
+}