Explorar el Código

move biome decorators from algorithm to grains

JasonWang hace 6 años
padre
commit
40f3df1ed7
Se han modificado 32 ficheros con 573 adiciones y 52 borrados
  1. 1 0
      src/MineCase.Algorithm/World/Biomes/Biome.cs
  2. 1 0
      src/MineCase.Algorithm/World/Biomes/BiomeForest.cs
  3. 1 0
      src/MineCase.Algorithm/World/Biomes/BiomePlains.cs
  4. 1 0
      src/MineCase.Algorithm/World/Biomes/BiomeSwamp.cs
  5. 1 0
      src/MineCase.Algorithm/World/Biomes/BiomeTaiga.cs
  6. 1 0
      src/MineCase.Algorithm/World/Plants/AbstractTreeGenerator.cs
  7. 1 0
      src/MineCase.Algorithm/World/Plants/DoubleFlowersGenerator.cs
  8. 1 0
      src/MineCase.Algorithm/World/Plants/DoubleGrassGenerator.cs
  9. 1 0
      src/MineCase.Algorithm/World/Plants/FlowersGenerator.cs
  10. 1 0
      src/MineCase.Algorithm/World/Plants/Taiga2Generator.cs
  11. 1 0
      src/MineCase.Algorithm/World/Plants/TaigaGenerator.cs
  12. 1 0
      src/MineCase.Algorithm/World/Plants/TreeGenerator.cs
  13. 14 1
      src/MineCase.Core/World/Biomes/BiomeId.cs
  14. 9 2
      src/MineCase.Core/World/Biomes/BiomeProperties.cs
  15. 5 1
      src/MineCase.Core/World/Plants/PlantsType.cs
  16. 128 13
      src/MineCase.Server.Grains/World/ChunkColumnGrain.cs
  17. 55 0
      src/MineCase.Server.Grains/World/Decoration/Biomes/BiomeDecoratorGrain.cs
  18. 67 0
      src/MineCase.Server.Grains/World/Decoration/Biomes/BiomeForestDecoratorGrain.cs
  19. 71 0
      src/MineCase.Server.Grains/World/Decoration/Biomes/BiomeOceanDecoratorGrain.cs
  20. 83 0
      src/MineCase.Server.Grains/World/Decoration/Biomes/BiomePlainsDecoratorGrain.cs
  21. 4 9
      src/MineCase.Server.Grains/World/Decoration/Plants/GrassGeneratorGrain.cs
  22. 32 19
      src/MineCase.Server.Grains/World/Decoration/Plants/PlantsGeneratorGrain.cs
  23. 7 0
      src/MineCase.Server.Grains/World/Generation/ChunkGeneratorFlatGrain.cs
  24. 21 2
      src/MineCase.Server.Grains/World/Generation/ChunkGeneratorOverworldGrain.cs
  25. 6 2
      src/MineCase.Server.Interfaces/MineCase.Server.Interfaces.csproj
  26. 18 0
      src/MineCase.Server.Interfaces/World/Decoration/Biomes/IBiomeDecorator.cs
  27. 10 0
      src/MineCase.Server.Interfaces/World/Decoration/Biomes/IBiomeForestDecorator.cs
  28. 10 0
      src/MineCase.Server.Interfaces/World/Decoration/Biomes/IBiomeOceanDecorator.cs
  29. 10 0
      src/MineCase.Server.Interfaces/World/Decoration/Biomes/IBiomePlainsDecorator.cs
  30. 1 3
      src/MineCase.Server.Interfaces/World/Decoration/Plants/IPlantsGenerator.cs
  31. 2 0
      src/MineCase.Server.Interfaces/World/Generation/IChunkGenerator.cs
  32. 8 0
      src/MineCase.Server.Interfaces/World/IChunkColumn.cs

+ 1 - 0
src/MineCase.Algorithm/World/Biomes/Biome.cs

@@ -9,6 +9,7 @@ using MineCase.Server.World.EntitySpawner;
 using MineCase.World;
 using MineCase.World.Biomes;
 using MineCase.World.Generation;
+using MineCase.World.Plants;
 using Orleans;
 
 namespace MineCase.Algorithm.World.Biomes

+ 1 - 0
src/MineCase.Algorithm/World/Biomes/BiomeForest.cs

@@ -6,6 +6,7 @@ using MineCase.Server.World.EntitySpawner;
 using MineCase.World;
 using MineCase.World.Biomes;
 using MineCase.World.Generation;
+using MineCase.World.Plants;
 using Orleans;
 
 namespace MineCase.Algorithm.World.Biomes

+ 1 - 0
src/MineCase.Algorithm/World/Biomes/BiomePlains.cs

@@ -6,6 +6,7 @@ using MineCase.Server.World.EntitySpawner;
 using MineCase.World;
 using MineCase.World.Biomes;
 using MineCase.World.Generation;
+using MineCase.World.Plants;
 using Orleans;
 
 namespace MineCase.Algorithm.World.Biomes

+ 1 - 0
src/MineCase.Algorithm/World/Biomes/BiomeSwamp.cs

@@ -6,6 +6,7 @@ using MineCase.Server.World.EntitySpawner;
 using MineCase.World;
 using MineCase.World.Biomes;
 using MineCase.World.Generation;
+using MineCase.World.Plants;
 using Orleans;
 
 namespace MineCase.Algorithm.World.Biomes

+ 1 - 0
src/MineCase.Algorithm/World/Biomes/BiomeTaiga.cs

@@ -6,6 +6,7 @@ using MineCase.Server.World.EntitySpawner;
 using MineCase.World;
 using MineCase.World.Biomes;
 using MineCase.World.Generation;
+using MineCase.World.Plants;
 using Orleans;
 
 namespace MineCase.Algorithm.World.Biomes

+ 1 - 0
src/MineCase.Algorithm/World/Plants/AbstractTreeGenerator.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Text;
+using MineCase.World.Plants;
 
 namespace MineCase.Algorithm.World.Plants
 {

+ 1 - 0
src/MineCase.Algorithm/World/Plants/DoubleFlowersGenerator.cs

@@ -2,6 +2,7 @@
 using MineCase.Algorithm.World.Biomes;
 using MineCase.Server.World;
 using MineCase.World;
+using MineCase.World.Plants;
 using Orleans;
 
 namespace MineCase.Algorithm.World.Plants

+ 1 - 0
src/MineCase.Algorithm/World/Plants/DoubleGrassGenerator.cs

@@ -2,6 +2,7 @@
 using MineCase.Algorithm.World.Biomes;
 using MineCase.Server.World;
 using MineCase.World;
+using MineCase.World.Plants;
 using Orleans;
 
 namespace MineCase.Algorithm.World.Plants

+ 1 - 0
src/MineCase.Algorithm/World/Plants/FlowersGenerator.cs

@@ -2,6 +2,7 @@
 using MineCase.Algorithm.World.Biomes;
 using MineCase.Server.World;
 using MineCase.World;
+using MineCase.World.Plants;
 using Orleans;
 
 namespace MineCase.Algorithm.World.Plants

+ 1 - 0
src/MineCase.Algorithm/World/Plants/Taiga2Generator.cs

@@ -4,6 +4,7 @@ using System.Text;
 using MineCase.Algorithm.World.Biomes;
 using MineCase.Server.World;
 using MineCase.World;
+using MineCase.World.Plants;
 using Orleans;
 
 namespace MineCase.Algorithm.World.Plants

+ 1 - 0
src/MineCase.Algorithm/World/Plants/TaigaGenerator.cs

@@ -4,6 +4,7 @@ using System.Text;
 using MineCase.Algorithm.World.Biomes;
 using MineCase.Server.World;
 using MineCase.World;
+using MineCase.World.Plants;
 using Orleans;
 
 namespace MineCase.Algorithm.World.Plants

+ 1 - 0
src/MineCase.Algorithm/World/Plants/TreeGenerator.cs

@@ -2,6 +2,7 @@
 using MineCase.Algorithm.World.Biomes;
 using MineCase.Server.World;
 using MineCase.World;
+using MineCase.World.Plants;
 using Orleans;
 
 namespace MineCase.Algorithm.World.Plants

+ 14 - 1
src/MineCase.Core/World/Biomes/BiomeId.cs

@@ -14,6 +14,19 @@ namespace MineCase.World.Biomes
         Taiga = 5,
         Swampland = 6,
         River = 7,
-        Beach = 16
+        Nether = 8,
+        TheEnd = 9,
+        FrozenOcean = 10,
+        FrozenRiver = 11,
+        SnowyTundra = 12,
+        SnowyMountains = 13,
+        MushroomFields = 14,
+        MushroomFieldShore = 15,
+        Beach = 16,
+        DesertHills = 17,
+        WoodedHills = 18,
+        TaigaHills = 19,
+        DeepOcean = 24,
+        SunflowerPlains = 129,
     }
 }

+ 9 - 2
src/MineCase.Core/World/Biomes/BiomeProperties.cs

@@ -6,22 +6,29 @@ namespace MineCase.World.Biomes
 {
     public class BiomeProperties
     {
-        public string BiomeName { get; set; }
+        public string BiomeName { get; set; } = "InvalidBiome";
 
-        public BiomeId BiomeId { get; set; }
+        public BiomeId BiomeId { get; set; } = BiomeId.Ocean;
 
+        /** The base height of this biome. Default 0.1. */
         public float BaseHeight { get; set; } = 0.1F;
 
+        /** The variation from the base height of the biome. Default 0.2. */
         public float HeightVariation { get; set; } = 0.2F;
 
+        /** The temperature of this biome. */
         public float Temperature { get; set; } = 0.5F;
 
+        /** The rainfall in this biome. */
         public float Rainfall { get; set; } = 0.5F;
 
+        /** Color tint applied to water depending on biome */
         public int WaterColor { get; set; } = 16777215;
 
+        /** Set to true if snow is enabled for this biome. */
         public bool EnableSnow { get; set; } = false;
 
+        /** Is true (default) if the biome support rain (desert and nether can't have rain) */
         public bool EnableRain { get; set; } = true;
     }
 }

+ 5 - 1
src/MineCase.Algorithm/World/Plants/PlantsType.cs → src/MineCase.Core/World/Plants/PlantsType.cs

@@ -1,4 +1,8 @@
-namespace MineCase.Algorithm.World.Plants
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MineCase.World.Plants
 {
     public enum PlantsType : uint
     {

+ 128 - 13
src/MineCase.Server.Grains/World/ChunkColumnGrain.cs

@@ -23,7 +23,6 @@ using Orleans.Concurrency;
 namespace MineCase.Server.World
 {
     [PersistTableName("chunkColumn")]
-    [Reentrant]
     internal class ChunkColumnGrain : AddressByPartitionGrain, IChunkColumn
     {
         private StateHolder State => GetValue(StateComponent<StateHolder>.StateProperty);
@@ -39,12 +38,24 @@ namespace MineCase.Server.World
         }
 
         public async Task<BlockState> GetBlockState(int x, int y, int z)
+        {
+            await EnsureChunkPopulated();
+            return State.Storage[x, y, z];
+        }
+
+        public async Task<BlockState> GetBlockStateUnsafe(int x, int y, int z)
         {
             await EnsureChunkGenerated();
             return State.Storage[x, y, z];
         }
 
         public async Task<ChunkColumnCompactStorage> GetState()
+        {
+            await EnsureChunkPopulated();
+            return State.Storage;
+        }
+
+        public async Task<ChunkColumnCompactStorage> GetStateUnsafe()
         {
             await EnsureChunkGenerated();
             return State.Storage;
@@ -63,7 +74,7 @@ namespace MineCase.Server.World
 
         public async Task SetBlockState(int x, int y, int z, BlockState blockState)
         {
-            await EnsureChunkGenerated();
+            await EnsureChunkPopulated();
             var state = State;
             var oldState = state.Storage[x, y, z];
 
@@ -103,15 +114,27 @@ namespace MineCase.Server.World
 
                 // 通知周围 Block 更改
                 await Task.WhenAll(CrossCoords.Select(crossCoord =>
-               {
-                   var neighborPos = blockWorldPos;
-                   neighborPos.X += crossCoord.x;
-                   neighborPos.Z += crossCoord.z;
-                   var chunk = neighborPos.ToChunkWorldPos();
-                   var blockChunkPos = neighborPos.ToBlockChunkPos();
-                   return GrainFactory.GetPartitionGrain<IChunkColumn>(World, chunk).OnBlockNeighborChanged(
-                       blockChunkPos.X, blockChunkPos.Y, blockChunkPos.Z, blockWorldPos, oldState, blockState);
-               }));
+                {
+                    var neighborPos = blockWorldPos;
+                    neighborPos.X += crossCoord.x;
+                    neighborPos.Z += crossCoord.z;
+                    var chunk = neighborPos.ToChunkWorldPos();
+                    var blockChunkPos = neighborPos.ToBlockChunkPos();
+                    return GrainFactory.GetPartitionGrain<IChunkColumn>(World, chunk).OnBlockNeighborChanged(
+                        blockChunkPos.X, blockChunkPos.Y, blockChunkPos.Z, blockWorldPos, oldState, blockState);
+                }));
+                MarkDirty();
+            }
+        }
+
+        public async Task SetBlockStateUnsafe(int x, int y, int z, BlockState blockState)
+        {
+            await EnsureChunkGenerated();
+            var oldState = State.Storage[x, y, z];
+
+            if (oldState != blockState)
+            {
+                State.Storage[x, y, z] = blockState;
                 MarkDirty();
             }
         }
@@ -158,6 +181,83 @@ namespace MineCase.Server.World
 
                 State.Generated = true;
 
+                // await WriteStateAsync();
+            }
+        }
+
+        private async Task EnsureChunkPopulated()
+        {
+            if (!State.Generated)
+            {
+                var serverSetting = GrainFactory.GetGrain<IServerSettings>(0);
+                string worldType = (await serverSetting.GetSettings()).LevelType;
+                if (worldType == "DEFAULT" || worldType == "default")
+                {
+                    var generator = GrainFactory.GetGrain<IChunkGeneratorOverworld>(await World.GetSeed());
+                    GeneratorSettings settings = new GeneratorSettings
+                    {
+                    };
+                    State.Storage = await generator.Generate(World, ChunkWorldPos.X, ChunkWorldPos.Z, settings);
+                }
+                else if (worldType == "FLAT" || worldType == "flat")
+                {
+                    var generator = GrainFactory.GetGrain<IChunkGeneratorFlat>(await World.GetSeed());
+                    GeneratorSettings settings = new GeneratorSettings
+                    {
+                        FlatBlockId = new BlockState?[]
+                        {
+                            BlockStates.Bedrock(),
+                            BlockStates.Stone(),
+                            BlockStates.Stone(),
+                            BlockStates.Dirt(),
+                            BlockStates.Dirt(),
+                            BlockStates.GrassBlock()
+                        }
+                    };
+                    State.Storage = await generator.Generate(World, ChunkWorldPos.X, ChunkWorldPos.Z, settings);
+                }
+                else
+                {
+                    throw new System.NotSupportedException("Unknown world type in server setting file.");
+                }
+
+                State.Generated = true;
+            }
+
+            if (!State.Populated)
+            {
+                var serverSetting = GrainFactory.GetGrain<IServerSettings>(0);
+                string worldType = (await serverSetting.GetSettings()).LevelType;
+                if (worldType == "DEFAULT" || worldType == "default")
+                {
+                    var generator = GrainFactory.GetGrain<IChunkGeneratorOverworld>(await World.GetSeed());
+                    GeneratorSettings settings = new GeneratorSettings { };
+                    await generator.Populate(World, ChunkWorldPos.X, ChunkWorldPos.Z, settings);
+                }
+                else if (worldType == "FLAT" || worldType == "flat")
+                {
+                    var generator = GrainFactory.GetGrain<IChunkGeneratorFlat>(await World.GetSeed());
+                    GeneratorSettings settings = new GeneratorSettings
+                    {
+                        FlatBlockId = new BlockState?[]
+                        {
+                            BlockStates.Bedrock(),
+                            BlockStates.Stone(),
+                            BlockStates.Stone(),
+                            BlockStates.Dirt(),
+                            BlockStates.Dirt(),
+                            BlockStates.GrassBlock()
+                        }
+                    };
+                    await generator.Populate(World, ChunkWorldPos.X, ChunkWorldPos.Z, settings);
+                }
+                else
+                {
+                    throw new System.NotSupportedException("Unknown world type in server setting file.");
+                }
+
+                State.Populated = true;
+
                 await WriteStateAsync();
             }
         }
@@ -174,6 +274,21 @@ namespace MineCase.Server.World
             return Task.FromResult<IBlockEntity>(null);
         }
 
+        public async Task<int> GetGroundHeight(int x, int z)
+        {
+            await EnsureChunkGenerated();
+            var storage = State.Storage;
+            for (int y = 255; y >= 0; --y)
+            {
+                if (!storage[x, y, z].IsAir())
+                {
+                    return y + 1;
+                }
+            }
+
+            return 0;
+        }
+
         public Task OnBlockNeighborChanged(int x, int y, int z, BlockWorldPos neighborPosition, BlockState oldState, BlockState newState)
         {
             if (State.Generated)
@@ -194,9 +309,9 @@ namespace MineCase.Server.World
 
         internal class StateHolder
         {
-            public bool Populated { get; set; }
+            public bool Populated { get; set; } = false;
 
-            public bool Generated { get; set; }
+            public bool Generated { get; set; } = false;
 
             public ChunkColumnCompactStorage Storage { get; set; }
 

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

@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using MineCase.Algorithm.Noise;
+using MineCase.Server.Game.Entities;
+using MineCase.Server.World.Decoration.Plants;
+using MineCase.World;
+using MineCase.World.Biomes;
+using MineCase.World.Plants;
+using Orleans;
+
+namespace MineCase.Server.World.Decoration.Biomes
+{
+    public abstract class BiomeDecoratorGrain : Grain, IBiomeDecorator
+    {
+        public BiomeProperties BiomeProperties { get; set; } = new BiomeProperties();
+
+        /** The block expected to be on the top of this biome */
+        public BlockState TopBlock { get; set; } = BlockStates.GrassBlock();
+
+        /** The block to fill spots in when not on the top */
+        public BlockState FillerBlock { get; set; } = BlockStates.Dirt();
+
+        // define some noise
+        protected static readonly OctavedNoise<Algorithm.Noise.PerlinNoise> _temperatureNoise =
+            new OctavedNoise<PerlinNoise>(new PerlinNoise(1234), 4, 0.5F);
+
+        protected static readonly OctavedNoise<PerlinNoise> _grassColorNoise =
+            new OctavedNoise<PerlinNoise>(new PerlinNoise(2345), 4, 0.5F);
+
+        // plants
+        public List<PlantsType> PlantsList { get; set; } = new List<PlantsType>();
+
+        // creatures
+        public List<MobType> PassiveMobList { get; set; } = new List<MobType>();
+
+        public List<MobType> MonsterList { get; set; } = new List<MobType>();
+
+        public virtual Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        {
+            throw new NotImplementedException();
+        }
+
+        public virtual Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        {
+            throw new NotImplementedException();
+        }
+
+        public virtual Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 67 - 0
src/MineCase.Server.Grains/World/Decoration/Biomes/BiomeForestDecoratorGrain.cs

@@ -0,0 +1,67 @@
+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 Orleans;
+using Orleans.Concurrency;
+
+namespace MineCase.Server.World.Decoration.Biomes
+{
+    [StatelessWorker]
+    public class BiomeForestDecoratorGrain : BiomeDecoratorGrain, IBiomeForestDecorator
+    {
+        public override Task OnActivateAsync()
+        {
+            if (this.GetPrimaryKeyLong() == (long)BiomeId.Forest)
+            {
+                BiomeProperties.BaseHeight = 0.1f;
+                BiomeProperties.HeightVariation = 0.2f;
+                BiomeProperties.Temperature = 0.7f;
+                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.RedFlower);
+            PlantsList.Add(PlantsType.YellowFlower);
+
+            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 override Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        {
+            var grassGenerator = GrainFactory.GetGrain<IGrassGenerator>(0);
+            return grassGenerator.Generate(world, chunkWorldPos, 10, 0);
+        }
+
+        public override Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

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

@@ -0,0 +1,71 @@
+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 Orleans;
+using Orleans.Concurrency;
+
+namespace MineCase.Server.World.Decoration.Biomes
+{
+    [StatelessWorker]
+    public class BiomeOceanDecoratorGrain : BiomeDecoratorGrain, IBiomeOceanDecorator
+    {
+        public override Task OnActivateAsync()
+        {
+            if (this.GetPrimaryKeyLong() == (long)BiomeId.Ocean)
+            {
+                BiomeProperties.BaseHeight = -1.0f;
+                BiomeProperties.HeightVariation = 0.1f;
+                BiomeProperties.Temperature = 0.5f;
+                BiomeProperties.Rainfall = 0.5f;
+                BiomeProperties.EnableSnow = false;
+            }
+            else if (this.GetPrimaryKeyLong() == (long)BiomeId.FrozenOcean)
+            {
+                BiomeProperties.BaseHeight = -1.0f;
+                BiomeProperties.HeightVariation = 0.1f;
+                BiomeProperties.Temperature = 0.0f;
+                BiomeProperties.Rainfall = 0.5f;
+                BiomeProperties.EnableSnow = true;
+            }
+            else if (this.GetPrimaryKeyLong() == (long)BiomeId.DeepOcean)
+            {
+                BiomeProperties.BaseHeight = -1.8f;
+                BiomeProperties.HeightVariation = 0.1f;
+                BiomeProperties.Temperature = 0.5f;
+                BiomeProperties.Rainfall = 0.5f;
+                BiomeProperties.EnableSnow = false;
+            }
+
+            TopBlock = BlockStates.Dirt();
+            FillerBlock = BlockStates.Dirt();
+
+            MonsterList.Add(MobType.Creeper);
+            MonsterList.Add(MobType.Skeleton);
+            MonsterList.Add(MobType.Zombie);
+            MonsterList.Add(MobType.Spider);
+
+            return Task.CompletedTask;
+        }
+
+        public override Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        {
+            return Task.CompletedTask;
+        }
+
+        public override Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

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

@@ -0,0 +1,83 @@
+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 Orleans;
+using Orleans.Concurrency;
+
+namespace MineCase.Server.World.Decoration.Biomes
+{
+    [StatelessWorker]
+    public class BiomePlainsDecoratorGrain : BiomeDecoratorGrain, IBiomePlainsDecorator
+    {
+        public override Task OnActivateAsync()
+        {
+            if (this.GetPrimaryKeyLong() == (long)BiomeId.Plains)
+            {
+                BiomeProperties.BaseHeight = 0.125f;
+                BiomeProperties.HeightVariation = 0.05f;
+                BiomeProperties.Temperature = 0.8f;
+                BiomeProperties.Rainfall = 0.4f;
+                BiomeProperties.EnableSnow = false;
+            }
+            else if (this.GetPrimaryKeyLong() == (long)BiomeId.SunflowerPlains)
+            {
+                BiomeProperties.BaseHeight = 0.125f;
+                BiomeProperties.HeightVariation = 0.05f;
+                BiomeProperties.Temperature = 0.8f;
+                BiomeProperties.Rainfall = 0.4f;
+                BiomeProperties.EnableSnow = false;
+            }
+            else if (this.GetPrimaryKeyLong() == (long)BiomeId.SnowyTundra)
+            {
+                BiomeProperties.BaseHeight = 0.125f;
+                BiomeProperties.HeightVariation = 0.05f;
+                BiomeProperties.Temperature = 0.0f;
+                BiomeProperties.Rainfall = 0.5f;
+                BiomeProperties.EnableSnow = true;
+            }
+
+            BiomeProperties.WaterColor = 16777215;
+            BiomeProperties.EnableRain = true;
+            TopBlock = BlockStates.GrassBlock();
+            FillerBlock = BlockStates.Dirt();
+
+            PlantsList.Add(PlantsType.TallGrass);
+            PlantsList.Add(PlantsType.RedFlower);
+            PlantsList.Add(PlantsType.YellowFlower);
+
+            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 override Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        {
+            var grassGenerator = GrainFactory.GetGrain<IGrassGenerator>(0);
+            return grassGenerator.Generate(world, chunkWorldPos, 10, 0);
+        }
+
+        public override Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 4 - 9
src/MineCase.Server.Grains/World/Decoration/Plants/GrassGeneratorGrain.cs

@@ -10,20 +10,15 @@ namespace MineCase.Server.World.Decoration.Plants
     [StatelessWorker]
     public class GrassGeneratorGrain : PlantsGeneratorGrain, IGrassGenerator
     {
-        public override async Task GenerateSingle(IWorld world, ChunkColumnCompactStorage chunk, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        public override async Task GenerateSingle(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
         {
-            BlockChunkPos chunkPos = pos.ToBlockChunkPos();
-            int x = chunkPos.X;
-            int y = chunkPos.Y;
-            int z = chunkPos.Z;
-
             // TODO use block accessor
-            var curBlock = await GetBlock(world, chunk, chunkWorldPos, pos);
-            var downBlock = await GetBlock(world, chunk, chunkWorldPos, pos);
+            var curBlock = await GetBlock(world, chunkWorldPos, pos);
+            var downBlock = await GetBlock(world, chunkWorldPos, new BlockWorldPos { X = pos.X, Y = pos.Y - 1, Z = pos.Z });
             if (curBlock.IsAir() &&
                 downBlock == BlockStates.GrassBlock())
             {
-                SetBlock(world, chunk, chunkWorldPos, pos, BlockStates.Grass(GrassType.TallGrass));
+                await SetBlock(world, chunkWorldPos, pos, BlockStates.Grass(GrassType.TallGrass));
             }
         }
     }

+ 32 - 19
src/MineCase.Server.Grains/World/Decoration/Plants/PlantsGeneratorGrain.cs

@@ -11,44 +11,57 @@ namespace MineCase.Server.World.Decoration.Plants
 {
     public abstract class PlantsGeneratorGrain : Grain, IPlantsGenerator
     {
-        protected virtual void SetBlock(IWorld world, ChunkColumnCompactStorage chunk, ChunkWorldPos chunkWorldPos, BlockWorldPos pos, BlockState state)
+        protected virtual Task SetBlock(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos, BlockState state)
         {
+            var chunkColumnKey = world.MakeAddressByPartitionKey(pos.ToChunkWorldPos());
+            var chunkGrain = GrainFactory.GetGrain<IChunkColumn>(chunkColumnKey);
             if (pos.ToChunkWorldPos() == chunkWorldPos)
             {
                 BlockChunkPos blockChunkPos = pos.ToBlockChunkPos();
-                chunk[blockChunkPos.X, blockChunkPos.Y, blockChunkPos.Z] = state;
+                return chunkGrain.SetBlockStateUnsafe(blockChunkPos.X, blockChunkPos.Y, blockChunkPos.Z, state);
             }
+
+            return Task.CompletedTask;
         }
 
-        protected virtual Task<BlockState> GetBlock(IWorld world, ChunkColumnCompactStorage chunk, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
+        protected virtual Task<BlockState> GetBlock(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos)
         {
-            if (pos.ToChunkWorldPos() == chunkWorldPos)
-            {
-                BlockChunkPos blockChunkPos = pos.ToBlockChunkPos();
-                return Task.FromResult(chunk[blockChunkPos.X, blockChunkPos.Y, blockChunkPos.Z]);
-            }
-            else
-            {
-                var chunkColumnKey = world.MakeAddressByPartitionKey(pos.ToChunkWorldPos());
-                return GrainFactory.GetGrain<IChunkColumn>(chunkColumnKey).GetBlockState(pos.X, pos.Y, pos.Z);
-            }
+            var chunkColumnKey = world.MakeAddressByPartitionKey(pos.ToChunkWorldPos());
+            var chunkGrain = GrainFactory.GetGrain<IChunkColumn>(chunkColumnKey);
+            BlockChunkPos blockChunkPos = pos.ToBlockChunkPos();
+
+            return chunkGrain.GetBlockStateUnsafe(blockChunkPos.X, blockChunkPos.Y, blockChunkPos.Z);
+        }
+
+        private Task<int> GetGroundHeight(IWorld world, ChunkWorldPos chunkWorldPos, int x, int z)
+        {
+            var blockChunkPos = new BlockWorldPos { X = x, Z = z }.ToBlockChunkPos();
+            var chunkColumnKey = world.MakeAddressByPartitionKey(chunkWorldPos);
+            return GrainFactory.GetGrain<IChunkColumn>(chunkColumnKey).GetGroundHeight(blockChunkPos.X, blockChunkPos.Z);
         }
 
-        public abstract Task GenerateSingle(IWorld world, ChunkColumnCompactStorage chunk, ChunkWorldPos chunkWorldPos, BlockWorldPos pos);
+        public abstract Task GenerateSingle(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos);
 
-        public virtual async Task Generate(IWorld world, ChunkColumnCompactStorage chunk, ChunkWorldPos pos, int countPerChunk, int range)
+        public virtual async Task Generate(IWorld world, ChunkWorldPos pos, int countPerChunk, int range)
         {
             int seed = await world.GetSeed();
 
-            for (int x = -range; x <= range; ++x)
+            for (int chunkXOffset = -range; chunkXOffset <= range; ++chunkXOffset)
             {
-                for (int z = -range; z <= range; ++z)
+                for (int chunkZOffset = -range; chunkZOffset <= range; ++chunkZOffset)
                 {
-                    int chunkSeed = (pos.X + x) ^ (pos.Z + z) ^ seed;
+                    ChunkWorldPos curChunkWorldPos = new ChunkWorldPos { X = pos.X + chunkXOffset, Z = pos.Z + chunkZOffset };
+                    BlockWorldPos curChunkCorner = curChunkWorldPos.ToBlockWorldPos();
+
+                    int chunkSeed = (pos.X + chunkXOffset) ^ (pos.Z + chunkZOffset) ^ seed;
                     Random rand = new Random(chunkSeed);
+
                     for (int count = 0; count < countPerChunk; ++count)
                     {
-                        await GenerateSingle(world, chunk, pos, new BlockWorldPos { X = rand.Next(16), Z = rand.Next(16) });
+                        int x = curChunkCorner.X + rand.Next(16);
+                        int z = curChunkCorner.Z + rand.Next(16);
+                        int groundHeight = await GetGroundHeight(world, pos, x, z);
+                        await GenerateSingle(world, pos, new BlockWorldPos { X = x, Y = groundHeight, Z = z });
                     }
                 }
             }

+ 7 - 0
src/MineCase.Server.Grains/World/Generation/ChunkGeneratorFlatGrain.cs

@@ -25,6 +25,13 @@ namespace MineCase.Server.World.Generation
             return Task.FromResult(chunkColumn);
         }
 
+        public async Task Populate(IWorld world, int x, int z, GeneratorSettings settings)
+        {
+            var chunkColumnKey = world.MakeAddressByPartitionKey(new ChunkWorldPos { X = x, Z = z });
+            ChunkColumnCompactStorage chunkColumn = await GrainFactory.GetGrain<IChunkColumn>(chunkColumnKey).GetState();
+            PopulateChunk(world, chunkColumn, x, z, settings);
+        }
+
         private void GenerateChunk(IWorld world, ChunkColumnCompactStorage chunk, int x, int z, GeneratorSettings settings)
         {
             // 按照flat模式每层的设置给chunk赋值

+ 21 - 2
src/MineCase.Server.Grains/World/Generation/ChunkGeneratorOverworldGrain.cs

@@ -11,7 +11,9 @@ using MineCase.Algorithm.World;
 using MineCase.Algorithm.World.Biomes;
 using MineCase.Algorithm.World.Layer;
 using MineCase.Algorithm.World.Mine;
+using MineCase.Server.World.Decoration.Biomes;
 using MineCase.World;
+using MineCase.World.Biomes;
 using MineCase.World.Generation;
 using Newtonsoft.Json;
 using Orleans;
@@ -95,6 +97,13 @@ namespace MineCase.Server.World.Generation
             return chunkColumn.Compact();
         }
 
+        public async Task Populate(IWorld world, int x, int z, GeneratorSettings settings)
+        {
+            var chunkColumnKey = world.MakeAddressByPartitionKey(new ChunkWorldPos { X = x, Z = z });
+            ChunkColumnCompactStorage chunkColumn = await GrainFactory.GetGrain<IChunkColumn>(chunkColumnKey).GetStateUnsafe();
+            PopulateChunk(world, chunkColumn, x, z, settings);
+        }
+
         private void GenerateChunk(MapGenerationInfo info, ChunkColumnStorage chunk, int x, int z, GeneratorSettings settings)
         {
             // 生物群系生成
@@ -153,8 +162,18 @@ namespace MineCase.Server.World.Generation
             int blockZ = z * 16;
             Biome chunkBiome = Biome.GetBiome(chunk.Biomes[7 * 16 + 7], settings);
 
-            chunkBiome.Decorate(world, GrainFactory, chunk, _random, new BlockWorldPos { X = blockX, Y = 0, Z = blockZ });
-            chunkBiome.SpawnMob(world, GrainFactory, chunk, _random, new BlockWorldPos { X = blockX, Y = 0, Z = blockZ });
+            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)

+ 6 - 2
src/MineCase.Server.Interfaces/MineCase.Server.Interfaces.csproj

@@ -11,6 +11,12 @@
     <DebugSymbols>true</DebugSymbols>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Remove="World\Biomes\**" />
+    <EmbeddedResource Remove="World\Biomes\**" />
+    <None Remove="World\Biomes\**" />
+  </ItemGroup>
+
   <ItemGroup>
     <PackageReference Include="Microsoft.Orleans.CodeGenerator.MSBuild" Version="2.3.4">
       <PrivateAssets>all</PrivateAssets>
@@ -32,8 +38,6 @@
     <Folder Include="Play\" />
 
     <AdditionalFiles Include="..\..\build\stylecop.json" />
-
-    <Folder Include="World\Biomes\" />
   </ItemGroup>
 
 </Project>

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

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using MineCase.World;
+using Orleans;
+
+namespace MineCase.Server.World.Decoration.Biomes
+{
+    public interface IBiomeDecorator : IGrainWithIntegerKey
+    {
+        Task Decorate(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos);
+
+        Task SpawnMob(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos);
+
+        Task SpawnMonster(IWorld world, ChunkWorldPos chunkWorldPos, BlockWorldPos pos);
+    }
+}

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

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

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

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

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

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

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

@@ -9,8 +9,6 @@ namespace MineCase.Server.World.Decoration.Plants
 {
     public interface IPlantsGenerator : IGrainWithIntegerKey
     {
-        Task GenerateSingle(IWorld world, ChunkColumnCompactStorage chunk, ChunkWorldPos chunkWorldPos, BlockWorldPos pos);
-
-        Task Generate(IWorld world, ChunkColumnCompactStorage chunk, ChunkWorldPos pos, int countPerChunk, int range);
+        Task Generate(IWorld world, ChunkWorldPos pos, int countPerChunk, int range);
     }
 }

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

@@ -8,5 +8,7 @@ namespace MineCase.Server.World.Generation
     public interface IChunkGenerator
     {
         Task<ChunkColumnCompactStorage> Generate(IWorld world, int x, int z, GeneratorSettings settings);
+
+        Task Populate(IWorld world, int x, int z, GeneratorSettings settings);
     }
 }

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

@@ -15,14 +15,22 @@ namespace MineCase.Server.World
     {
         Task<ChunkColumnCompactStorage> GetState();
 
+        Task<ChunkColumnCompactStorage> GetStateUnsafe();
+
         Task<BlockState> GetBlockState(int x, int y, int z);
 
+        Task<BlockState> GetBlockStateUnsafe(int x, int y, int z);
+
         Task SetBlockState(int x, int y, int z, BlockState blockState);
 
+        Task SetBlockStateUnsafe(int x, int y, int z, BlockState blockState);
+
         Task<BiomeId> GetBlockBiome(int x, int z);
 
         Task<IBlockEntity> GetBlockEntity(int x, int y, int z);
 
+        Task<int> GetGroundHeight(int x, int z);
+
         Task OnBlockNeighborChanged(int x, int y, int z, BlockWorldPos neighborPosition, BlockState oldState, BlockState newState);
     }
 }