From 963791b41ad9a7608126834ae1a747309cc83378 Mon Sep 17 00:00:00 2001 From: JunWang Date: Wed, 29 Nov 2017 00:31:29 +0800 Subject: [PATCH 01/11] Fix a recipe bug --- data/crafting.txt | 52 +++++++++++------------ src/MineCase.Core/CraftingRecipeLoader.cs | 11 ++++- tests/UnitTest/CraftingRecipeTest.cs | 23 +++++++++- 3 files changed, 58 insertions(+), 28 deletions(-) diff --git a/data/crafting.txt b/data/crafting.txt index 907a08aa..0f5fcb3c 100644 --- a/data/crafting.txt +++ b/data/crafting.txt @@ -177,39 +177,39 @@ CraftingTable = WoodPlanks^-1, 1:1, 1:2, 2:1, 2:2 ## # ## Axes: -#DiamondAxe = Stick, 2:2, 2:3 | Diamond, 2:1, 1:1, 1:2 -#DiamondAxe = Stick, 2:2, 2:3 | Diamond, 2:1, 3:1, 3:2 -#GoldenAxe = Stick, 2:2, 2:3 | GoldIngot, 2:1, 1:1, 1:2 -#GoldenAxe = Stick, 2:2, 2:3 | GoldIngot, 2:1, 3:1, 3:2 -#IronAxe = Stick, 2:2, 2:3 | IronIngot, 2:1, 1:1, 1:2 -#IronAxe = Stick, 2:2, 2:3 | IronIngot, 2:1, 3:1, 3:2 -#StoneAxe = Stick, 2:2, 2:3 | Cobblestone, 2:1, 1:1, 1:2 -#StoneAxe = Stick, 2:2, 2:3 | Cobblestone, 2:1, 3:1, 3:2 -#WoodenAxe = Stick, 2:2, 2:3 | Planks^-1, 2:1, 1:1, 1:2 -#WoodenAxe = Stick, 2:2, 2:3 | Planks^-1, 2:1, 3:1, 3:2 +DiamondAxe = Stick, 2:2, 2:3 | Diamond, 2:1, 1:1, 1:2 +DiamondAxe = Stick, 2:2, 2:3 | Diamond, 2:1, 3:1, 3:2 +GoldenAxe = Stick, 2:2, 2:3 | GoldIngot, 2:1, 1:1, 1:2 +GoldenAxe = Stick, 2:2, 2:3 | GoldIngot, 2:1, 3:1, 3:2 +IronAxe = Stick, 2:2, 2:3 | IronIngot, 2:1, 1:1, 1:2 +IronAxe = Stick, 2:2, 2:3 | IronIngot, 2:1, 3:1, 3:2 +StoneAxe = Stick, 2:2, 2:3 | Cobblestone, 2:1, 1:1, 1:2 +StoneAxe = Stick, 2:2, 2:3 | Cobblestone, 2:1, 3:1, 3:2 +WoodenAxe = Stick, 2:2, 2:3 | WoodPlanks^-1, 2:1, 1:1, 1:2 +WoodenAxe = Stick, 2:2, 2:3 | WoodPlanks^-1, 2:1, 3:1, 3:2 # ## Pickaxes: -#DiamondPickaxe = Stick, 2:2, 2:3 | Diamond, 1:1, 2:1, 3:1 -#GoldenPickaxe = Stick, 2:2, 2:3 | GoldIngot, 1:1, 2:1, 3:1 -#IronPickaxe = Stick, 2:2, 2:3 | IronIngot, 1:1, 2:1, 3:1 -#StonePickaxe = Stick, 2:2, 2:3 | Cobblestone, 1:1, 2:1, 3:1 -#WoodenPickaxe = Stick, 2:2, 2:3 | Planks^-1, 1:1, 2:1, 3:1 +DiamondPickaxe = Stick, 2:2, 2:3 | Diamond, 1:1, 2:1, 3:1 +GoldenPickaxe = Stick, 2:2, 2:3 | GoldIngot, 1:1, 2:1, 3:1 +IronPickaxe = Stick, 2:2, 2:3 | IronIngot, 1:1, 2:1, 3:1 +StonePickaxe = Stick, 2:2, 2:3 | Cobblestone, 1:1, 2:1, 3:1 +WoodenPickaxe = Stick, 2:2, 2:3 | WoodPlanks^-1, 1:1, 2:1, 3:1 # ## Shovels: -#DiamondShovel = Stick, 2:2, 2:3 | Diamond, 2:1 -#GoldenShovel = Stick, 2:2, 2:3 | GoldIngot, 2:1 -#IronShovel = Stick, 2:2, 2:3 | IronIngot, 2:1 -#StoneShovel = Stick, 2:2, 2:3 | Cobblestone, 2:1 -#WoodenShovel = Stick, 2:2, 2:3 | Planks^-1, 2:1 +DiamondShovel = Stick, 2:2, 2:3 | Diamond, 2:1 +GoldenShovel = Stick, 2:2, 2:3 | GoldIngot, 2:1 +IronShovel = Stick, 2:2, 2:3 | IronIngot, 2:1 +StoneShovel = Stick, 2:2, 2:3 | Cobblestone, 2:1 +WoodenShovel = Stick, 2:2, 2:3 | WoodPlanks^-1, 2:1 # ## Hoes: -#DiamondHoe = Stick, 2:2, 2:3 | Diamond, 2:1, *:1 -#GoldenHoe = Stick, 2:2, 2:3 | GoldIngot, 2:1, *:1 -#IronHoe = Stick, 2:2, 2:3 | IronIngot, 2:1, *:1 -#StoneHoe = Stick, 2:2, 2:3 | Cobblestone, 2:1, *:1 -#WoodenHoe = Stick, 2:2, 2:3 | Planks^-1, 2:1, *:1 +DiamondHoe = Stick, 2:2, 2:3 | Diamond, 2:1, *:1 +GoldenHoe = Stick, 2:2, 2:3 | GoldIngot, 2:1, *:1 +IronHoe = Stick, 2:2, 2:3 | IronIngot, 2:1, *:1 +StoneHoe = Stick, 2:2, 2:3 | Cobblestone, 2:1, *:1 +WoodenHoe = Stick, 2:2, 2:3 | WoodPlanks^-1, 2:1, *:1 # -#Bucket = IronIngot, 1:1, 2:2, 3:1 +Bucket = IronIngot, 1:1, 2:2, 3:1 #Compass = IronIngot, 2:1, 1:2, 3:2, 2:3 | RedstoneDust, 2:2 #EmptyMap = Paper, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Compass, 2:2 #FireCharge, 3 = BlazePowder, * | Coal, * | Gunpowder, * diff --git a/src/MineCase.Core/CraftingRecipeLoader.cs b/src/MineCase.Core/CraftingRecipeLoader.cs index 9e522051..917f6ea1 100644 --- a/src/MineCase.Core/CraftingRecipeLoader.cs +++ b/src/MineCase.Core/CraftingRecipeLoader.cs @@ -72,6 +72,7 @@ private unsafe void ParseLine(string line) restSpan = restSpan.Slice(ingredientSplitter + 1); } + NormalizeIngredients(recipe); Recipes.Add(recipe); } @@ -130,8 +131,16 @@ int ParsePoint(ReadOnlySpan span) distributionSpan = distributionSpan.Slice(positionSplitter + 1); } while (true); + + if (recipe.Inputs != null) + { + foreach (CraftingRecipeSlot each in recipe.Inputs) + { + recipeSlots.Add(each); + } + } + recipe.Inputs = recipeSlots.ToArray(); - NormalizeIngredients(recipe); } private void NormalizeIngredients(CraftingRecipe recipe) diff --git a/tests/UnitTest/CraftingRecipeTest.cs b/tests/UnitTest/CraftingRecipeTest.cs index bb60f073..13c8668d 100644 --- a/tests/UnitTest/CraftingRecipeTest.cs +++ b/tests/UnitTest/CraftingRecipeTest.cs @@ -32,7 +32,7 @@ public async Task TestCraftingRecipeLoader() } var recipes = loader.Recipes; - Assert.Equal(13, recipes.Count); + Assert.Equal(13 + 26, recipes.Count); } [Fact] @@ -55,5 +55,26 @@ public async Task TestCraftingRecipeMatcher() Assert.Equal((short)BlockStates.WoodPlanks().Id, recipe.Result.BlockId); Assert.True(recipe.AfterTake.Cast().All(o => o.IsEmpty)); } + + [Fact] + public async Task TestTools() + { + var loader = new CraftingRecipeLoader(); + using (var sr = new StreamReader(File.OpenRead(Path.Combine(RootDir, "crafting.txt")))) + { + await loader.LoadRecipes(sr); + } + + var matcher = new CraftingRecipeMatcher(loader.Recipes); + var recipe = matcher.FindRecipe(new Slot[,] + { + { new Slot { BlockId = (short)BlockStates.WoodPlanks().Id, ItemCount = 1 }, Slot.Empty, Slot.Empty }, + { new Slot { BlockId = (short)BlockStates.WoodPlanks().Id, ItemCount = 1 }, new Slot { BlockId = (short)ItemId.Stick, ItemCount = 1 }, new Slot { BlockId = (short)ItemId.Stick, ItemCount = 1 }}, + { new Slot { BlockId = (short)BlockStates.WoodPlanks().Id, ItemCount = 1 }, Slot.Empty, Slot.Empty } + }); + Assert.NotNull(recipe); + Assert.Equal((short)ItemId.WoodenPickaxe, recipe.Result.BlockId); + Assert.True(recipe.AfterTake.Cast().All(o => o.IsEmpty)); + } } } From f0decc66fa8ef0977c773c62461800ce2ce16f7c Mon Sep 17 00:00:00 2001 From: SunnyCase Date: Wed, 29 Nov 2017 10:16:56 +0800 Subject: [PATCH 02/11] more elegant --- src/MineCase.Core/CraftingRecipeLoader.cs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/MineCase.Core/CraftingRecipeLoader.cs b/src/MineCase.Core/CraftingRecipeLoader.cs index 917f6ea1..302b49b2 100644 --- a/src/MineCase.Core/CraftingRecipeLoader.cs +++ b/src/MineCase.Core/CraftingRecipeLoader.cs @@ -60,30 +60,31 @@ private unsafe void ParseLine(string line) recipe.Output.ItemCount = byte.Parse(ToString(resultSpan.Slice(resultSplitter + 1))); } + var recipeSlots = new List(); var restSpan = ingredientsSpan; while (!restSpan.IsEmpty) { var ingredientSplitter = restSpan.IndexOf('|'); var ingredientSpan = ingredientSplitter == -1 ? restSpan : restSpan.Slice(0, ingredientSplitter); - ParseIngredient(ingredientSpan, recipe); + ParseIngredient(ingredientSpan, recipeSlots); if (ingredientSplitter == -1) break; else restSpan = restSpan.Slice(ingredientSplitter + 1); } + recipe.Inputs = recipeSlots.ToArray(); NormalizeIngredients(recipe); Recipes.Add(recipe); } - private void ParseIngredient(Span ingredientSpan, CraftingRecipe recipe) + private void ParseIngredient(Span ingredientSpan, ICollection recipeSlots) { var slot = new Slot { ItemCount = 1 }; var splitter = ingredientSpan.IndexOf(','); ParseItem(ingredientSpan.Slice(0, splitter), ref slot); var distributionSpan = ingredientSpan.Slice(splitter + 1); - var recipeSlots = new List(); do { var positionSplitter = distributionSpan.IndexOf(','); @@ -131,16 +132,6 @@ int ParsePoint(ReadOnlySpan span) distributionSpan = distributionSpan.Slice(positionSplitter + 1); } while (true); - - if (recipe.Inputs != null) - { - foreach (CraftingRecipeSlot each in recipe.Inputs) - { - recipeSlots.Add(each); - } - } - - recipe.Inputs = recipeSlots.ToArray(); } private void NormalizeIngredients(CraftingRecipe recipe) From 399372f48085ee02f695bd9e1b89a20791594357 Mon Sep 17 00:00:00 2001 From: JunWang Date: Wed, 29 Nov 2017 14:13:26 +0800 Subject: [PATCH 03/11] Add armors' craft --- data/crafting.txt | 8 ++++---- data/furnace.txt | 10 +++++----- tests/UnitTest/CraftingRecipeTest.cs | 2 +- tests/UnitTest/FurnaceRecipeTest.cs | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/data/crafting.txt b/data/crafting.txt index 0f5fcb3c..ae0a8576 100644 --- a/data/crafting.txt +++ b/data/crafting.txt @@ -248,10 +248,10 @@ Bucket = IronIngot, 1:1, 2:2, 3:1 ## # ## Helmets: -#DiamondHelmet = Diamond, 1:1, 2:1, 3:1, 1:2, 3:2 -#GoldenHelmet = GoldIngot, 1:1, 2:1, 3:1, 1:2, 3:2 -#IronHelmet = IronIngot, 1:1, 2:1, 3:1, 1:2, 3:2 -#LeatherHelmet = Leather, 1:1, 2:1, 3:1, 1:2, 3:2 +DiamondHelmet = Diamond, 1:1, 2:1, 3:1, 1:2, 3:2 +GoldenHelmet = GoldIngot, 1:1, 2:1, 3:1, 1:2, 3:2 +IronHelmet = IronIngot, 1:1, 2:1, 3:1, 1:2, 3:2 +LeatherCap = Leather, 1:1, 2:1, 3:1, 1:2, 3:2 # ## Chestplates: #DiamondChestplate = Diamond, 1:1, 3:1, 1:2, 2:2, 3:2, 1:3, 2:3, 3:3 diff --git a/data/furnace.txt b/data/furnace.txt index 67cf5e62..d9a1ebfa 100644 --- a/data/furnace.txt +++ b/data/furnace.txt @@ -53,16 +53,16 @@ #ChainmailLeggings = IronNugget #Chicken = CookedChicken #ChorusFruit = PoppedChorusFruit -#Clay = Brick +Clay = Brick #ClayBlock = HardenedClay -#CoalOre = Coal +CoalOre = Coal #Cobblestone = Stone #CrackedStonebrick = Stonebrick #CyanTerracotta = CyanGlazedTerracotta #DiamondOre = Diamond #EmeraldOre = Emerald #Fish = CookedFish -#GoldOre = GoldIngot +GoldOre = GoldIngot #GoldAxe = GoldNugget #GoldBoots = GoldNugget #GoldChestplate = GoldNugget @@ -75,7 +75,7 @@ #GoldSword = GoldNugget #GrayTerracotta = GrayGlazedTerracotta #GreenTerracotta = GreenGlazedTerracotta -#IronOre = IronIngot +IronOre = IronIngot #IronAxe = IronNugget #IronBoots = IronNugget #IronChestplate = IronNugget @@ -105,7 +105,7 @@ Wood2^-1 = Coal^Charcoal #RedTerracotta = RedGlazedTerracotta #RedstoneOre = Redstone #Salmon = CookedSalmon -#Sand = Glass +Sand = Glass #StoneBrick = CrackedStoneBricks #WetSponge = Sponge #WhiteTerracotta = WhiteGlazedTerracotta diff --git a/tests/UnitTest/CraftingRecipeTest.cs b/tests/UnitTest/CraftingRecipeTest.cs index 13c8668d..8ea80efb 100644 --- a/tests/UnitTest/CraftingRecipeTest.cs +++ b/tests/UnitTest/CraftingRecipeTest.cs @@ -32,7 +32,7 @@ public async Task TestCraftingRecipeLoader() } var recipes = loader.Recipes; - Assert.Equal(13 + 26, recipes.Count); + Assert.Equal(13 + 26 + 4, recipes.Count); } [Fact] diff --git a/tests/UnitTest/FurnaceRecipeTest.cs b/tests/UnitTest/FurnaceRecipeTest.cs index 06bc9783..59b78824 100644 --- a/tests/UnitTest/FurnaceRecipeTest.cs +++ b/tests/UnitTest/FurnaceRecipeTest.cs @@ -32,7 +32,7 @@ public async Task TestFurnaceRecipeLoader() } var recipes = loader.Recipes; - Assert.Equal(2, recipes.Count); + Assert.Equal(2 + 5, recipes.Count); var fuels = loader.Fuels; Assert.Equal(4, fuels.Count); } From 89602d201c1ef91072fc2e7e365062ea912ccc90 Mon Sep 17 00:00:00 2001 From: sunnycase Date: Wed, 29 Nov 2017 01:41:16 -0600 Subject: [PATCH 04/11] Performance optimization (#103) * Remove asyncs on dp * Add mongodb storage * Update * Update * Add FixedUpdateComponent * Fix a serialization bug * Move fixedUpdate --- src/Common/Engine/Component.cs | 51 +---- .../Engine/Data/DependencyValueStorage.cs | 92 +-------- .../Engine/Data/IDependencyValueStorage.cs | 19 +- src/Common/Engine/Data/IEffectiveValue.cs | 14 +- .../Data/LocalDependencyValueExtensions.cs | 22 +-- .../Data/LocalDependencyValueProvider.cs | 37 +--- src/Common/Engine/DependencyObject.cs | 178 ++++-------------- src/Common/Engine/DependencyProperty.cs | 11 +- src/Common/Engine/PropertyMetadata.cs | 49 +---- .../Game/Entity/Ai/MobAi/CreatureAi.cs | 4 +- src/MineCase.Core/MineCase.Core.csproj | 4 + src/MineCase.Core/World/GameTickArgs.cs | 3 + .../DependencyObject.Server.cs | 21 ++- src/MineCase.Gateway/Program.cs | 2 + .../AddressByPartitionKeyComponent.cs | 18 +- .../Components/BlockWorldPositionComponent.cs | 2 +- .../Components/EntityIdComponent.cs | 3 +- .../Components/EntityLookComponent.cs | 6 +- .../Components/EntityOnGroundComponent.cs | 2 +- .../EntityWorldPositionComponent.cs | 2 +- .../Components/FixedUpdateComponent.cs | 64 +++++++ .../Components/GameTickComponent.cs | 24 +-- .../Components/IsEnabledComponent.cs | 6 +- .../Components/NameComponent.cs | 2 +- .../Components/WorldComponent.cs | 2 +- .../Game/BlockEntities/BlockEntityGrain.cs | 18 +- .../BlockEntities/ChestBlockEntityGrain.cs | 8 +- .../BlockEntityLiftTimeComponent.cs | 7 +- .../Components/ChestComponent.cs | 23 ++- .../Components/FurnaceComponent.cs | 15 +- .../Game/BlockEntities/FurnaceBlockEntity.cs | 8 +- .../ActiveWorldPartitionComponent.cs | 19 +- .../Components/ChunkLoaderComponent.cs | 7 +- .../Entities/Components/ColliderComponent.cs | 38 ++-- .../Components/DiscoveryRegisterComponent.cs | 24 +-- .../Components/DraggedSlotComponent.cs | 5 +- .../Entities/Components/EntityAiComponent.cs | 26 ++- .../EntityDiscoveryComponentBase.cs | 5 - .../Components/EntityLifeTimeComponent.cs | 11 +- .../Components/EntityLookComponentBase.cs | 5 - .../Components/EntityMoveComponent.cs | 5 - .../Game/Entities/Components/FoodComponent.cs | 6 +- .../Entities/Components/HealthComponent.cs | 4 +- .../Entities/Components/HeldItemComponent.cs | 7 +- .../Components/MobDiscoveryComponent.cs | 7 +- .../Components/MobSpawnerComponent.cs | 6 +- .../Entities/Components/MobTypeComponent.cs | 2 +- .../Components/PickupDiscoveryComponent.cs | 6 +- .../Components/PickupMetadataComponent.cs | 5 +- .../Components/SlotContainerComponent.cs | 24 +-- .../Components/StandaloneHeldItemComponent.cs | 5 +- .../Components/SyncMobStateComponent.cs | 18 +- .../Components/SyncPlayerStateComponent.cs | 18 +- .../Components/WindowManagerComponent.cs | 3 +- .../Game/Entities/EntityGrain.cs | 22 +-- .../Game/Entities/MobGrain.cs | 30 +-- .../Game/Entities/MonsterGrain.cs | 28 +-- .../Game/Entities/PassiveMobGrain.cs | 26 +-- .../Game/Entities/PickupGrain.cs | 16 +- .../Game/Entities/PlayerGrain.cs | 68 +++---- .../Game/GameSession.cs | 56 +++--- .../MineCase.Server.Grains.csproj | 1 + .../Play/ServerboundPacketComponent.cs | 46 ++--- .../Persistence/AppDbContext.cs | 7 +- .../Components/AutoSaveStateComponent.cs | 3 +- .../Components/PeriodicSaveStateComponent.cs | 36 ++++ .../Persistence/Components/StateComponent.cs | 14 +- .../PersistableDependencyObject.cs | 34 +++- src/MineCase.Server.Grains/StreamProviders.cs | 4 + .../User/NonAuthenticatedUserGrain.cs | 4 +- src/MineCase.Server.Grains/User/UserGrain.cs | 17 +- .../World/ChunkColumnGrain.cs | 19 +- .../World/ChunkTrackingHub.cs | 5 - .../World/CollectableFinder.cs | 15 +- .../World/TickEmitterGrain.cs | 63 +++---- .../World/WorldGrain.cs | 26 +-- .../World/WorldPartitionGrain.cs | 35 +--- .../Game/ITickable.cs | 11 -- .../World/IChunkColumn.cs | 2 - .../World/IChunkTrackingHub.cs | 2 - .../World/ICollectableFinder.cs | 2 - .../World/ITickEmitter.cs | 2 - .../World/IWorldPartition.cs | 2 - src/MineCase.Server/MineCase.Server.csproj | 1 + .../OrleansConfiguration.dev.xml | 4 +- .../OrleansConfiguration.docker.xml | 4 +- src/MineCase.Server/Program.cs | 7 +- src/MineCase.Server/config.docker.json | 2 +- src/MineCase.Server/config.json | 2 +- tests/UnitTest/SerializationTest.cs | 14 +- 90 files changed, 664 insertions(+), 939 deletions(-) create mode 100644 src/MineCase.Server.Grains/Components/FixedUpdateComponent.cs create mode 100644 src/MineCase.Server.Grains/Persistence/Components/PeriodicSaveStateComponent.cs delete mode 100644 src/MineCase.Server.Interfaces/Game/ITickable.cs diff --git a/src/Common/Engine/Component.cs b/src/Common/Engine/Component.cs index b7efd868..8c467fe8 100644 --- a/src/Common/Engine/Component.cs +++ b/src/Common/Engine/Component.cs @@ -12,19 +12,9 @@ namespace MineCase.Engine { internal interface IComponentIntern { -#if ECS_SERVER - Task -#else - void -#endif - Attach(DependencyObject dependencyObject, ServiceProviderType serviceProvider); + void Attach(DependencyObject dependencyObject, ServiceProviderType serviceProvider); -#if ECS_SERVER - Task -#else - void -#endif - Detach(); + void Detach(); int GetMessageOrder(object message); } @@ -58,61 +48,34 @@ public Component(string name) Name = name; } -#if ECS_SERVER - Task -#else - void -#endif - IComponentIntern.Attach(DependencyObject dependencyObject, ServiceProviderType serviceProvider) + void IComponentIntern.Attach(DependencyObject dependencyObject, ServiceProviderType serviceProvider) { AttachedObject = dependencyObject; ServiceProvider = serviceProvider; AttatchPartial(dependencyObject, serviceProvider); -#if ECS_SERVER - return -#endif OnAttached(); } partial void AttatchPartial(DependencyObject dependencyObject, ServiceProviderType serviceProvider); -#if ECS_SERVER - Task -#else - void -#endif - IComponentIntern.Detach() + void IComponentIntern.Detach() { - AttachedObject = null; -#if ECS_SERVER - return -#endif OnDetached(); + AttachedObject = null; } /// /// 组件被附加到实体时 /// - protected virtual - -#if ECS_SERVER - Task -#else - void -#endif - OnAttached() + protected virtual void OnAttached() { -#if ECS_SERVER - return Task.CompletedTask; -#endif } /// /// 组件从实体卸载时 /// - protected virtual Task OnDetached() + protected virtual void OnDetached() { - return Task.CompletedTask; } /// diff --git a/src/Common/Engine/Data/DependencyValueStorage.cs b/src/Common/Engine/Data/DependencyValueStorage.cs index e0c96203..6188bfb5 100644 --- a/src/Common/Engine/Data/DependencyValueStorage.cs +++ b/src/Common/Engine/Data/DependencyValueStorage.cs @@ -27,13 +27,7 @@ public IEnumerable Keys public bool IsDirty { get; set; } - public event -#if ECS_SERVER - AsyncEventHandler -#else - EventHandler -#endif - CurrentValueChanged; + public event EventHandler CurrentValueChanged; public DependencyValueStorage() { @@ -54,12 +48,7 @@ public DependencyValueStorage(Dictionary AddOrUpdate(IDependencyValueProvider provider, DependencyProperty key, Func> addValueFactory, Func, Task>> updateValueFactory) -#else - IEffectiveValue AddOrUpdate(IDependencyValueProvider provider, DependencyProperty key, Func> addValueFactory, Func, IEffectiveValue> updateValueFactory) -#endif + public IEffectiveValue AddOrUpdate(IDependencyValueProvider provider, DependencyProperty key, Func> addValueFactory, Func, IEffectiveValue> updateValueFactory) { var storage = GetStorage(provider, key); var priority = provider.Priority; @@ -70,43 +59,30 @@ IEffectiveValue AddOrUpdate(IDependencyValueProvider provider, DependencyProp var value = addValueFactory(key); storage.Add(priority, value); value.ValueChanged = (s, e) => OnEffectiveValueChanged(priority, key, e.OldValue, e.NewValue); + IsDirty = true; result = value; var raiseChanged = storage.IndexOfKey(priority) == 0; if (raiseChanged) - { -#if ECS_SERVER - await -#endif OnCurrentValueChanged(key, false, null, true, value.Value); - } } else { var oldValue = (IEffectiveValue)storage.Values[oldIdx]; - var newValue = -#if ECS_SERVER - await -#endif - updateValueFactory(key, oldValue); + var newValue = updateValueFactory(key, oldValue); if (oldValue != newValue) { oldValue.ValueChanged = null; newValue.ValueChanged = (s, e) => OnEffectiveValueChanged(priority, key, e.OldValue, e.NewValue); storage[priority] = newValue; + IsDirty = true; var raiseChanged = oldIdx == 0; if (raiseChanged) - { -#if ECS_SERVER - await -#endif OnCurrentValueChanged(key, true, oldValue.Value, true, newValue.Value); - } } result = newValue; } - IsDirty = true; return result; } @@ -149,28 +125,12 @@ public bool TryGetCurrentEffectiveValue(DependencyProperty key, out IEffectiveVa return false; } - private -#if ECS_SERVER - Task -#else - void -#endif - OnCurrentValueChanged(DependencyProperty key, bool hasOldValue, object oldValue, bool hasNewValue, object newValue) + private void OnCurrentValueChanged(DependencyProperty key, bool hasOldValue, object oldValue, bool hasNewValue, object newValue) { - IsDirty = true; -#if ECS_SERVER - return -#endif CurrentValueChanged.InvokeSerial(this, new CurrentValueChangedEventArgs(key, hasOldValue, oldValue, hasNewValue, newValue)); } - private -#if ECS_SERVER - Task -#else - void -#endif - OnEffectiveValueCleared(int index, DependencyProperty key, object oldValue) + private void OnEffectiveValueCleared(int index, DependencyProperty key, object oldValue) { IsDirty = true; if (index == 0) @@ -184,37 +144,18 @@ public bool TryGetCurrentEffectiveValue(DependencyProperty key, out IEffectiveVa newValue = ((dynamic)list.Values[0]).Value; } -#if ECS_SERVER - return -#endif OnCurrentValueChanged(key, true, oldValue, hasNewValue, newValue); } - -#if ECS_SERVER - return Task.CompletedTask; -#endif } - private -#if ECS_SERVER - Task -#else - void -#endif - OnEffectiveValueChanged(float priority, DependencyProperty key, object oldValue, object newValue) + private void OnEffectiveValueChanged(float priority, DependencyProperty key, object oldValue, object newValue) { IsDirty = true; SortedList list; if (_dict.TryGetValue(key, out list) && list.IndexOfKey(priority) == 0) { -#if ECS_SERVER - return -#endif OnCurrentValueChanged(key, true, oldValue, true, newValue); } -#if ECS_SERVER - return Task.CompletedTask; -#endif } public bool TryGetValue(IDependencyValueProvider provider, DependencyProperty key, out IEffectiveValue value) @@ -231,13 +172,7 @@ public bool TryGetValue(IDependencyValueProvider provider, DependencyProperty return false; } - public -#if ECS_SERVER - Task -#else - bool -#endif - TryRemove(IDependencyValueProvider provider, DependencyProperty key, out IEffectiveValue value) + public bool TryRemove(IDependencyValueProvider provider, DependencyProperty key, out IEffectiveValue value) { var storage = GetStorage(provider, key); var priority = provider.Priority; @@ -247,21 +182,12 @@ public bool TryGetValue(IDependencyValueProvider provider, DependencyProperty value = (IEffectiveValue)eValue; var index = storage.IndexOfKey(priority); storage.RemoveAt(index); -#if ECS_SERVER - return OnEffectiveValueCleared(index, key, value.Value) - .ContinueWith(t => true); -#else OnEffectiveValueCleared(index, key, value.Value); return true; -#endif } value = null; -#if ECS_SERVER - return Task.FromResult(false); -#else return false; -#endif } private SortedList GetStorage(IDependencyValueProvider provider, DependencyProperty key) diff --git a/src/Common/Engine/Data/IDependencyValueStorage.cs b/src/Common/Engine/Data/IDependencyValueStorage.cs index 26e1f72f..39dafae7 100644 --- a/src/Common/Engine/Data/IDependencyValueStorage.cs +++ b/src/Common/Engine/Data/IDependencyValueStorage.cs @@ -18,13 +18,7 @@ public interface IDependencyValueStorage /// /// 当前值变更事件 /// - event -#if ECS_SERVER - AsyncEventHandler -#else - EventHandler -#endif - CurrentValueChanged; + event EventHandler CurrentValueChanged; /// /// 添加或更新 @@ -35,11 +29,7 @@ public interface IDependencyValueStorage /// 添加工厂 /// 更新工厂 /// 新的值 -#if ECS_SERVER - Task AddOrUpdate(IDependencyValueProvider provider, DependencyProperty key, Func> addValueFactory, Func, Task>> updateValueFactory); -#else IEffectiveValue AddOrUpdate(IDependencyValueProvider provider, DependencyProperty key, Func> addValueFactory, Func, IEffectiveValue> updateValueFactory); -#endif /// /// 尝试获取值 @@ -59,12 +49,7 @@ public interface IDependencyValueStorage /// 依赖属性 /// 值 /// 是否成功删除 -#if ECS_SERVER - Task -#else - bool -#endif - TryRemove(IDependencyValueProvider provider, DependencyProperty key, out IEffectiveValue value); + bool TryRemove(IDependencyValueProvider provider, DependencyProperty key, out IEffectiveValue value); /// /// 尝试获取当前值 diff --git a/src/Common/Engine/Data/IEffectiveValue.cs b/src/Common/Engine/Data/IEffectiveValue.cs index 00e534d3..ecf3a8c7 100644 --- a/src/Common/Engine/Data/IEffectiveValue.cs +++ b/src/Common/Engine/Data/IEffectiveValue.cs @@ -18,12 +18,7 @@ public interface IEffectiveValue /// /// 获取值改变处理器 /// -#if ECS_SERVER - AsyncEventHandler -#else - EventHandler -#endif - ValueChanged { set; } + EventHandler ValueChanged { set; } } /// @@ -46,12 +41,7 @@ public interface IEffectiveValue : IEffectiveValue /// 设置值 /// /// 值 -#if ECS_SERVER - Task -#else - void -#endif - SetValue(T value); + void SetValue(T value); } /// diff --git a/src/Common/Engine/Data/LocalDependencyValueExtensions.cs b/src/Common/Engine/Data/LocalDependencyValueExtensions.cs index 995bd5c7..9ff92c19 100644 --- a/src/Common/Engine/Data/LocalDependencyValueExtensions.cs +++ b/src/Common/Engine/Data/LocalDependencyValueExtensions.cs @@ -31,17 +31,8 @@ public static bool TryGetLocalValue(this DependencyObject d, DependencyProper /// 依赖对象 /// 依赖属性 /// 值 - public static -#if ECS_SERVER - Task -#else - void -#endif - SetLocalValue(this DependencyObject d, DependencyProperty property, T value) + public static void SetLocalValue(this DependencyObject d, DependencyProperty property, T value) { -#if ECS_SERVER - return -#endif LocalDependencyValueProvider.Current.SetValue(property, d.ValueStorage, value); } @@ -51,17 +42,8 @@ public static /// 值类型 /// 依赖对象 /// 依赖属性 - public static -#if ECS_SERVER - Task -#else - void -#endif - ClearLocalValue(this DependencyObject d, DependencyProperty property) + public static void ClearLocalValue(this DependencyObject d, DependencyProperty property) { -#if ECS_SERVER - return -#endif LocalDependencyValueProvider.Current.ClearValue(property, d.ValueStorage); } } diff --git a/src/Common/Engine/Data/LocalDependencyValueProvider.cs b/src/Common/Engine/Data/LocalDependencyValueProvider.cs index f1c3652a..1bf81471 100644 --- a/src/Common/Engine/Data/LocalDependencyValueProvider.cs +++ b/src/Common/Engine/Data/LocalDependencyValueProvider.cs @@ -25,16 +25,6 @@ public class LocalDependencyValueProvider : IDependencyValueProvider /// 依赖属性 /// 值存储 /// 值 -#if ECS_SERVER - public Task SetValue(DependencyProperty property, IDependencyValueStorage storage, T value) - { - return storage.AddOrUpdate(this, property, o => new LocalEffectiveValue(value), async (k, o) => - { - await ((LocalEffectiveValue)o).SetValue(value); - return o; - }); - } -#else public void SetValue(DependencyProperty property, IDependencyValueStorage storage, T value) { storage.AddOrUpdate(this, property, o => new LocalEffectiveValue(value), (k, o) => @@ -43,7 +33,6 @@ public void SetValue(DependencyProperty property, IDependencyValueStorage return o; }); } -#endif /// /// 尝试获取值 @@ -72,18 +61,9 @@ public bool TryGetValue(DependencyProperty property, IDependencyValueStora /// 值类型 /// 依赖属性 /// 值存储 - public -#if ECS_SERVER - Task -#else - void -#endif - ClearValue(DependencyProperty property, IDependencyValueStorage storage) + public void ClearValue(DependencyProperty property, IDependencyValueStorage storage) { IEffectiveValue eValue; -#if ECS_SERVER - return -#endif storage.TryRemove(this, property, out eValue); } @@ -95,13 +75,7 @@ internal static IEffectiveValue FromValue(T value) internal class LocalEffectiveValue : IEffectiveValue { /// - public -#if ECS_SERVER - AsyncEventHandler -#else - EventHandler -#endif - ValueChanged { get; set; } + public EventHandler ValueChanged { get; set; } /// public bool CanSetValue => true; @@ -119,19 +93,12 @@ public LocalEffectiveValue(T value) } /// -#if ECS_SERVER - public async Task SetValue(T value) -#else public void SetValue(T value) -#endif { if (!EqualityComparer.Default.Equals(_value, value)) { var oldValue = _value; _value = value; -#if ECS_SERVER - await -#endif ValueChanged.InvokeSerial(this, new EffectiveValueChangedEventArgs(oldValue, value)); } } diff --git a/src/Common/Engine/DependencyObject.cs b/src/Common/Engine/DependencyObject.cs index 526646bd..1af69290 100644 --- a/src/Common/Engine/DependencyObject.cs +++ b/src/Common/Engine/DependencyObject.cs @@ -77,22 +77,13 @@ public T GetUnityComponent() /// 设置组件 /// /// 组件 - public -#if ECS_SERVER - async Task -#else - void -#endif - SetComponent(Component component) + public void SetComponent(Component component) { var name = component.Name; if (_components.TryGetValue(name, out var old)) { if (old == component) return; Unsubscribe(old); -#if ECS_SERVER - await -#endif old.Detach(); _indexes.Remove(old); _components.Remove(name); @@ -100,9 +91,6 @@ async Task _components.Add(name, component); _indexes.Add(component, _index++); -#if ECS_SERVER - await -#endif ((IComponentIntern)component).Attach(this, ServiceProvider); Subscribe(component); } @@ -111,22 +99,13 @@ async Task /// 清除组件 /// /// 组件类型 - public -#if ECS_SERVER - async Task -#else - void -#endif - ClearComponent() + public void ClearComponent() where T : Component { var components = _components.Where(o => o.Value is T); foreach (var component in components) { Unsubscribe(component.Value); -#if ECS_SERVER - await -#endif component.Value.Detach(); _indexes.Remove(component.Value); _components.Remove(component.Key); @@ -142,8 +121,8 @@ async Task /// public IDependencyValueStorage ValueStorage => _valueStorage; - private readonly ConcurrentDictionary _propertyChangedHandlers = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _propertyChangedHandlersGen = new ConcurrentDictionary(); + private readonly Dictionary _propertyChangedHandlers = new Dictionary(); + private readonly Dictionary _propertyChangedHandlersGen = new Dictionary(); private Delegate _anyPropertyChangedHandler; /// @@ -167,46 +146,24 @@ public T GetValue(DependencyProperty property) /// 值类型 /// 依赖属性 /// 值 - public -#if ECS_SERVER - Task -#else - void -#endif - SetCurrentValue(DependencyProperty property, T value) + public void SetCurrentValue(DependencyProperty property, T value) { IEffectiveValue eValue; if (_valueStorage.TryGetCurrentEffectiveValue(property, out eValue) && eValue.CanSetValue) { -#if ECS_SERVER - return -#endif eValue.SetValue(value); } else { -#if ECS_SERVER - return -#endif this.SetLocalValue(property, value); } } private static readonly MethodInfo _raisePropertyChangedHelper = typeof(DependencyObject).GetRuntimeMethods().Single(o => o.Name == nameof(RaisePropertyChangedHelper)); - private -#if ECS_SERVER - Task -#else - void -#endif - ValueStorage_CurrentValueChanged(object sender, CurrentValueChangedEventArgs e) + private void ValueStorage_CurrentValueChanged(object sender, CurrentValueChangedEventArgs e) { -#if ECS_SERVER - return (Task)_raisePropertyChangedHelper.MakeGenericMethod(e.Property.PropertyType).Invoke(this, new object[] { e.Property, e }); -#else _raisePropertyChangedHelper.MakeGenericMethod(e.Property.PropertyType).Invoke(this, new object[] { e.Property, e }); -#endif } /// @@ -215,16 +172,13 @@ public T GetValue(DependencyProperty property) /// 值类型 /// 依赖属性 /// 处理器 - public void RegisterPropertyChangedHandler( - DependencyProperty property, -#if ECS_SERVER - AsyncEventHandler> -#else - EventHandler> -#endif - handler) + public void RegisterPropertyChangedHandler(DependencyProperty property, EventHandler> handler) { - _propertyChangedHandlers.AddOrUpdate(property, handler, (k, old) => Delegate.Combine(old, handler)); + if (_propertyChangedHandlers.TryGetValue(property, out var newHandler)) + newHandler = Delegate.Combine(newHandler, handler); + else + newHandler = handler; + _propertyChangedHandlers[property] = newHandler; } /// @@ -233,19 +187,15 @@ public void RegisterPropertyChangedHandler( /// 值类型 /// 依赖属性 /// 处理器 - public void RemovePropertyChangedHandler( - DependencyProperty property, -#if ECS_SERVER - AsyncEventHandler> -#else - EventHandler> -#endif - handler) + public void RemovePropertyChangedHandler(DependencyProperty property, EventHandler> handler) { - Delegate d = null; - _propertyChangedHandlers.TryRemove(property, out d); - if (d != (Delegate)handler) - _propertyChangedHandlers.AddOrUpdate(property, k => Delegate.Remove(d, handler), (k, old) => Delegate.Combine(old, Delegate.Remove(d, handler))); + if (_propertyChangedHandlers.TryGetValue(property, out var newHandler)) + newHandler = Delegate.Remove(newHandler, handler); + + if (newHandler == null) + _propertyChangedHandlers.Remove(property); + else + _propertyChangedHandlers[property] = newHandler; } /// @@ -253,16 +203,13 @@ public void RemovePropertyChangedHandler( /// /// 依赖属性 /// 处理器 - public void RegisterPropertyChangedHandler( - DependencyProperty property, -#if ECS_SERVER - AsyncEventHandler -#else - EventHandler -#endif - handler) + public void RegisterPropertyChangedHandler(DependencyProperty property, EventHandler handler) { - _propertyChangedHandlersGen.AddOrUpdate(property, handler, (k, old) => Delegate.Combine(old, handler)); + if (_propertyChangedHandlersGen.TryGetValue(property, out var newHandler)) + newHandler = Delegate.Combine(newHandler, handler); + else + newHandler = handler; + _propertyChangedHandlersGen[property] = newHandler; } /// @@ -270,32 +217,22 @@ public void RegisterPropertyChangedHandler( /// /// 依赖属性 /// 处理器 - public void RemovePropertyChangedHandler( - DependencyProperty property, -#if ECS_SERVER - AsyncEventHandler -#else - EventHandler -#endif - handler) + public void RemovePropertyChangedHandler(DependencyProperty property, EventHandler handler) { - Delegate d = null; - _propertyChangedHandlersGen.TryRemove(property, out d); - if (d != (Delegate)handler) - _propertyChangedHandlersGen.AddOrUpdate(property, k => Delegate.Remove(d, handler), (k, old) => Delegate.Combine(old, Delegate.Remove(d, handler))); + if (_propertyChangedHandlersGen.TryGetValue(property, out var newHandler)) + newHandler = Delegate.Remove(newHandler, handler); + + if (newHandler == null) + _propertyChangedHandlersGen.Remove(property); + else + _propertyChangedHandlersGen[property] = newHandler; } /// /// 注册任意属性变更处理器 /// /// 处理器 - public void RegisterAnyPropertyChangedHandler( -#if ECS_SERVER - AsyncEventHandler -#else - EventHandler -#endif - handler) + public void RegisterAnyPropertyChangedHandler(EventHandler handler) { _anyPropertyChangedHandler = Delegate.Combine(_anyPropertyChangedHandler, handler); } @@ -304,24 +241,12 @@ public void RegisterAnyPropertyChangedHandler( /// 删除任意属性变更处理器 /// /// 处理器 - public void RemoveAnyPropertyChangedHandler( -#if ECS_SERVER - AsyncEventHandler -#else - EventHandler -#endif - handler) + public void RemoveAnyPropertyChangedHandler(EventHandler handler) { _anyPropertyChangedHandler = Delegate.Remove(_anyPropertyChangedHandler, handler); } - internal -#if ECS_SERVER - async Task -#else - void -#endif - RaisePropertyChangedHelper(DependencyProperty property, CurrentValueChangedEventArgs e) + internal void RaisePropertyChangedHelper(DependencyProperty property, CurrentValueChangedEventArgs e) { var oldValue = e.HasOldValue ? (T)e.OldValue : GetDefaultValue(property); var newValue = e.HasNewValue ? (T)e.NewValue : GetDefaultValue(property); @@ -330,17 +255,8 @@ async Task return; var args = new PropertyChangedEventArgs(property, oldValue, newValue); -#if ECS_SERVER - await -#endif property.RaisePropertyChanged(_realType, this, args); -#if ECS_SERVER - await -#endif InvokeLocalPropertyChangedHandlers(args); -#if ECS_SERVER - await -#endif OnDependencyPropertyChanged(args); } @@ -349,29 +265,10 @@ async Task /// /// 值类型 /// 参数 -#if ECS_SERVER - public virtual Task OnDependencyPropertyChanged(PropertyChangedEventArgs args) - { - return Task.CompletedTask; - } -#else public virtual void OnDependencyPropertyChanged(PropertyChangedEventArgs args) { } -#endif -#if ECS_SERVER - private async Task InvokeLocalPropertyChangedHandlers(PropertyChangedEventArgs e) - { - Delegate d; - if (_propertyChangedHandlers.TryGetValue(e.Property, out d)) - await ((AsyncEventHandler>)d).InvokeSerial(this, e); - - if (_propertyChangedHandlersGen.TryGetValue(e.Property, out d)) - await ((AsyncEventHandler)d).InvokeSerial(this, e); - await ((AsyncEventHandler)_anyPropertyChangedHandler).InvokeSerial(this, e); - } -#else private void InvokeLocalPropertyChangedHandlers(PropertyChangedEventArgs e) { Delegate d; @@ -382,7 +279,6 @@ private void InvokeLocalPropertyChangedHandlers(PropertyChangedEventArgs e ((EventHandler)d).InvokeSerial(this, e); ((EventHandler)_anyPropertyChangedHandler).InvokeSerial(this, e); } -#endif private T GetDefaultValue(DependencyProperty property) { diff --git a/src/Common/Engine/DependencyProperty.cs b/src/Common/Engine/DependencyProperty.cs index 7bfc92a3..ab48c475 100644 --- a/src/Common/Engine/DependencyProperty.cs +++ b/src/Common/Engine/DependencyProperty.cs @@ -334,17 +334,8 @@ public bool TryGetDefaultValue(DependencyObject d, Type type, out T value) return GetMetadata(type).TryGetDefaultValue(d, this, out value); } - internal -#if ECS_SERVER - Task -#else - void -#endif - RaisePropertyChanged(Type type, object sender, PropertyChangedEventArgs e) + internal void RaisePropertyChanged(Type type, object sender, PropertyChangedEventArgs e) { -#if ECS_SERVER - return -#endif GetMetadata(type).RaisePropertyChanged(sender, e); } diff --git a/src/Common/Engine/PropertyMetadata.cs b/src/Common/Engine/PropertyMetadata.cs index af198c94..7ff68ca1 100644 --- a/src/Common/Engine/PropertyMetadata.cs +++ b/src/Common/Engine/PropertyMetadata.cs @@ -27,27 +27,14 @@ public class PropertyMetadata /// /// 属性更改事件 /// - public event -#if ECS_SERVER - AsyncEventHandler> -#else - EventHandler> -#endif - PropertyChanged; + public event EventHandler> PropertyChanged; /// /// Initializes a new instance of the class. /// /// 默认值 /// 属性更改处理器 - public PropertyMetadata( - T defaultValue, -#if ECS_SERVER - AsyncEventHandler> -#else - EventHandler> -#endif - propertyChangedHandler = null) + public PropertyMetadata(T defaultValue, EventHandler> propertyChangedHandler = null) { _defaultValue = defaultValue; _defaultValueSet = true; @@ -60,14 +47,7 @@ public PropertyMetadata( /// /// 未设置默认值 /// 属性更改处理器 - public PropertyMetadata( - DependencyProperty.UnsetValueType unsetValue, -#if ECS_SERVER - AsyncEventHandler> -#else - EventHandler> -#endif - propertyChangedHandler = null) + public PropertyMetadata(DependencyProperty.UnsetValueType unsetValue, EventHandler> propertyChangedHandler = null) { _defaultValueSet = false; if (propertyChangedHandler != null) @@ -107,35 +87,20 @@ protected virtual bool TryGetDefaultValueOverride(DependencyObject d, Dependency return false; } -#if ECS_SERVER - internal async Task RaisePropertyChanged(object sender, PropertyChangedEventArgs e) - { - await OnPropertyChanged(sender, e); - await PropertyChanged.InvokeSerial(sender, e); - } -#else internal void RaisePropertyChanged(object sender, PropertyChangedEventArgs e) { OnPropertyChanged(sender, e); PropertyChanged.InvokeSerial(sender, e); } -#endif /// /// 当属性修改时 /// /// 发送方 /// 参数 -#if ECS_SERVER - protected virtual Task OnPropertyChanged(object sender, PropertyChangedEventArgs e) - { - return Task.CompletedTask; - } -#else protected virtual void OnPropertyChanged(object sender, PropertyChangedEventArgs e) { } -#endif /// /// 合并属性元数据 @@ -155,13 +120,7 @@ internal void Merge(PropertyMetadata old, bool ownerIsDerived) if (ownerIsDerived) { - PropertyChanged = -#if ECS_SERVER - (AsyncEventHandler> -#else - (EventHandler> -#endif -)Delegate.Combine(old.PropertyChanged, PropertyChanged); + PropertyChanged = (EventHandler>)Delegate.Combine(old.PropertyChanged, PropertyChanged); } MergeOverride(old); diff --git a/src/MineCase.Algorithm/Game/Entity/Ai/MobAi/CreatureAi.cs b/src/MineCase.Algorithm/Game/Entity/Ai/MobAi/CreatureAi.cs index b8b342ae..7d401edc 100644 --- a/src/MineCase.Algorithm/Game/Entity/Ai/MobAi/CreatureAi.cs +++ b/src/MineCase.Algorithm/Game/Entity/Ai/MobAi/CreatureAi.cs @@ -22,8 +22,8 @@ public CreatureAi(Func getter, Action setter) _stateMachine = stateMachine; } - public Task FireAsync(CreatureEvent @event) => - _stateMachine.FireAsync(@event); + public void Fire(CreatureEvent @event) => + _stateMachine.Fire(@event); protected abstract void Configure(StateMachine stateMachine); } diff --git a/src/MineCase.Core/MineCase.Core.csproj b/src/MineCase.Core/MineCase.Core.csproj index 55fbb0ef..37988cbc 100644 --- a/src/MineCase.Core/MineCase.Core.csproj +++ b/src/MineCase.Core/MineCase.Core.csproj @@ -38,6 +38,10 @@ + + + + diff --git a/src/MineCase.Core/World/GameTickArgs.cs b/src/MineCase.Core/World/GameTickArgs.cs index a7000745..2ce1b9bc 100644 --- a/src/MineCase.Core/World/GameTickArgs.cs +++ b/src/MineCase.Core/World/GameTickArgs.cs @@ -4,6 +4,9 @@ namespace MineCase.World { +#if !NET46 + [Orleans.Concurrency.Immutable] +#endif public sealed class GameTickArgs { public TimeSpan DeltaTime { get; set; } diff --git a/src/MineCase.Engine/DependencyObject.Server.cs b/src/MineCase.Engine/DependencyObject.Server.cs index b2616488..0f9b7546 100644 --- a/src/MineCase.Engine/DependencyObject.Server.cs +++ b/src/MineCase.Engine/DependencyObject.Server.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using MineCase.Engine.Serialization; +using Orleans.Streams; namespace MineCase.Engine { @@ -18,9 +19,9 @@ public partial class DependencyObject public override async Task OnActivateAsync() { Logger = ServiceProvider.GetRequiredService().CreateLogger(GetType()); - await InitializePreLoadComponent(); + InitializePreLoadComponent(); await ReadStateAsync(); - await InitializeComponents(); + InitializeComponents(); } public override async Task OnDeactivateAsync() @@ -35,14 +36,12 @@ public void Destroy() DeactivateOnIdle(); } - protected virtual Task InitializeComponents() + protected virtual void InitializeComponents() { - return Task.CompletedTask; } - protected virtual Task InitializePreLoadComponent() + protected virtual void InitializePreLoadComponent() { - return Task.CompletedTask; } public async Task ReadStateAsync() @@ -115,5 +114,15 @@ public async Task ClearOperationQueue() await _operationQueue.Dequeue()(); } } + + public IAsyncStream GetStream(string providerName, Guid streamId, string streamNamespace) + { + return GetStreamProvider(providerName).GetStream(streamId, streamNamespace); + } + + public new IDisposable RegisterTimer(Func callback, object state, TimeSpan dueTime, TimeSpan period) + { + return base.RegisterTimer(callback, state, dueTime, period); + } } } diff --git a/src/MineCase.Gateway/Program.cs b/src/MineCase.Gateway/Program.cs index 842cf499..7d1ce730 100644 --- a/src/MineCase.Gateway/Program.cs +++ b/src/MineCase.Gateway/Program.cs @@ -26,6 +26,7 @@ static void Main(string[] args) Configuration = LoadConfiguration(); Startup(); _exitEvent.WaitOne(); + _clusterClient?.Dispose(); } private static void ConfigureApplicationParts(IClientBuilder builder) @@ -44,6 +45,7 @@ private static async void Startup() (ex, timeSpan) => logger?.LogError($"Cluster connection failed. Next retry: {timeSpan.TotalSeconds} secs later.")); await retryPolicy.ExecuteAsync(async () => { + _clusterClient?.Dispose(); var builder = new ClientBuilder() .LoadConfiguration("OrleansConfiguration.dev.xml") .ConfigureServices(ConfigureServices) diff --git a/src/MineCase.Server.Grains/Components/AddressByPartitionKeyComponent.cs b/src/MineCase.Server.Grains/Components/AddressByPartitionKeyComponent.cs index d89b1afc..7f94d0e0 100644 --- a/src/MineCase.Server.Grains/Components/AddressByPartitionKeyComponent.cs +++ b/src/MineCase.Server.Grains/Components/AddressByPartitionKeyComponent.cs @@ -14,37 +14,33 @@ internal class AddressByPartitionKeyComponent : Component public static readonly DependencyProperty AddressByPartitionKeyProperty = DependencyProperty.Register("AddressByPartitionKey", typeof(AddressByPartitionKeyComponent), new PropertyMetadata(string.Empty, OnAddressByPartitionKeyChanged)); - public event AsyncEventHandler<(string oldKey, string newKey)> KeyChanged; + public event EventHandler<(string oldKey, string newKey)> KeyChanged; public AddressByPartitionKeyComponent(string name = "addressByPartitionKey") : base(name) { } - protected override Task OnAttached() + protected override void OnAttached() { AttachedObject.RegisterPropertyChangedHandler(WorldComponent.WorldProperty, OnWorldChanged); AttachedObject.RegisterPropertyChangedHandler(EntityWorldPositionComponent.EntityWorldPositionProperty, OnEntityWorldPositionChanged); AttachedObject.RegisterPropertyChangedHandler(BlockWorldPositionComponent.BlockWorldPositionProperty, OnBlockWorldPositionChanged); - return Task.CompletedTask; } - private Task OnBlockWorldPositionChanged(object sender, PropertyChangedEventArgs e) + private void OnBlockWorldPositionChanged(object sender, PropertyChangedEventArgs e) { UpdateKey(); - return Task.CompletedTask; } - private Task OnWorldChanged(object sender, PropertyChangedEventArgs e) + private void OnWorldChanged(object sender, PropertyChangedEventArgs e) { UpdateKey(); - return Task.CompletedTask; } - private Task OnEntityWorldPositionChanged(object sender, PropertyChangedEventArgs e) + private void OnEntityWorldPositionChanged(object sender, PropertyChangedEventArgs e) { UpdateKey(); - return Task.CompletedTask; } private void UpdateKey() @@ -65,11 +61,11 @@ private void UpdateKey() } } - private static async Task OnAddressByPartitionKeyChanged(object sender, PropertyChangedEventArgs e) + private static void OnAddressByPartitionKeyChanged(object sender, PropertyChangedEventArgs e) { var component = ((DependencyObject)sender).GetComponent(); if (component != null) - await component.KeyChanged.InvokeSerial(sender, (e.OldValue, e.NewValue)); + component.KeyChanged?.Invoke(sender, (e.OldValue, e.NewValue)); } } diff --git a/src/MineCase.Server.Grains/Components/BlockWorldPositionComponent.cs b/src/MineCase.Server.Grains/Components/BlockWorldPositionComponent.cs index 30d0bb15..c8fdd694 100644 --- a/src/MineCase.Server.Grains/Components/BlockWorldPositionComponent.cs +++ b/src/MineCase.Server.Grains/Components/BlockWorldPositionComponent.cs @@ -19,7 +19,7 @@ public BlockWorldPositionComponent(string name = "blockWorldPosition") { } - public Task SetBlockWorldPosition(BlockWorldPos value) => + public void SetBlockWorldPosition(BlockWorldPos value) => AttachedObject.SetLocalValue(BlockWorldPositionProperty, value); } } diff --git a/src/MineCase.Server.Grains/Components/EntityIdComponent.cs b/src/MineCase.Server.Grains/Components/EntityIdComponent.cs index 4625466b..f1a85e89 100644 --- a/src/MineCase.Server.Grains/Components/EntityIdComponent.cs +++ b/src/MineCase.Server.Grains/Components/EntityIdComponent.cs @@ -21,7 +21,8 @@ public EntityIdComponent(string name = "entityId") Task IHandle.Handle(SpawnEntity message) { - return AttachedObject.SetLocalValue(EntityIdProperty, message.EntityId); + AttachedObject.SetLocalValue(EntityIdProperty, message.EntityId); + return Task.CompletedTask; } } } diff --git a/src/MineCase.Server.Grains/Components/EntityLookComponent.cs b/src/MineCase.Server.Grains/Components/EntityLookComponent.cs index e04a2e7f..56e81e23 100644 --- a/src/MineCase.Server.Grains/Components/EntityLookComponent.cs +++ b/src/MineCase.Server.Grains/Components/EntityLookComponent.cs @@ -29,13 +29,13 @@ public EntityLookComponent(string name = "entityLook") { } - public Task SetPitch(float value) => + public void SetPitch(float value) => AttachedObject.SetLocalValue(PitchProperty, value); - public Task SetYaw(float value) => + public void SetYaw(float value) => AttachedObject.SetLocalValue(YawProperty, value); - public Task SetHeadYaw(float value) => + public void SetHeadYaw(float value) => AttachedObject.SetLocalValue(HeadYawProperty, value); } } diff --git a/src/MineCase.Server.Grains/Components/EntityOnGroundComponent.cs b/src/MineCase.Server.Grains/Components/EntityOnGroundComponent.cs index acd89404..2b5832d4 100644 --- a/src/MineCase.Server.Grains/Components/EntityOnGroundComponent.cs +++ b/src/MineCase.Server.Grains/Components/EntityOnGroundComponent.cs @@ -18,7 +18,7 @@ public EntityOnGroundComponent(string name = "isOnGround") { } - public Task SetIsOnGround(bool value) => + public void SetIsOnGround(bool value) => AttachedObject.SetLocalValue(IsOnGroundProperty, value); } } diff --git a/src/MineCase.Server.Grains/Components/EntityWorldPositionComponent.cs b/src/MineCase.Server.Grains/Components/EntityWorldPositionComponent.cs index 417c37ac..349410d1 100644 --- a/src/MineCase.Server.Grains/Components/EntityWorldPositionComponent.cs +++ b/src/MineCase.Server.Grains/Components/EntityWorldPositionComponent.cs @@ -18,7 +18,7 @@ public EntityWorldPositionComponent(string name = "entityWorldPosition") { } - public Task SetPosition(EntityWorldPos entityWorldPos) + public void SetPosition(EntityWorldPos entityWorldPos) => AttachedObject.SetLocalValue(EntityWorldPositionProperty, entityWorldPos); } diff --git a/src/MineCase.Server.Grains/Components/FixedUpdateComponent.cs b/src/MineCase.Server.Grains/Components/FixedUpdateComponent.cs new file mode 100644 index 00000000..4c4d9c45 --- /dev/null +++ b/src/MineCase.Server.Grains/Components/FixedUpdateComponent.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using MineCase.Engine; +using MineCase.Server.World; +using MineCase.World; + +namespace MineCase.Server.Components +{ + public class FixedUpdateComponent : Component + { + private IDisposable _tickTimer; + private Stopwatch _stopwatch; + private long _worldAge; + private long _actualAge; + private TimeSpan _lastUpdate; + private static readonly long _updateTick = TimeSpan.FromMilliseconds(50).Ticks; + + public event AsyncEventHandler Tick; + + public FixedUpdateComponent(string name = "fixedUpdate") + : base(name) + { + } + + public async Task Start(IWorld world) + { + _tickTimer?.Dispose(); + _worldAge = await world.GetAge(); + _actualAge = 0; + _stopwatch = new Stopwatch(); + _stopwatch.Start(); + _tickTimer = AttachedObject.RegisterTimer(OnTick, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(5)); + } + + private async Task OnTick(object arg) + { + var expectedAge = _stopwatch.ElapsedTicks / _updateTick; + var e = new GameTickArgs { DeltaTime = TimeSpan.FromMilliseconds(50) }; + var updateTimes = expectedAge - _actualAge; + var now = _stopwatch.Elapsed; + for (int i = 0; i < updateTimes; i++) + { + e.WorldAge = _worldAge; + e.TimeOfDay = _worldAge % 24000; + await Tick.InvokeSerial(this, e); + _worldAge++; + _actualAge++; + } + + var deltaTime = now - _lastUpdate; + _lastUpdate = now; + } + + public void Stop() + { + _tickTimer?.Dispose(); + _tickTimer = null; + } + } +} diff --git a/src/MineCase.Server.Grains/Components/GameTickComponent.cs b/src/MineCase.Server.Grains/Components/GameTickComponent.cs index 73d09a8d..d75844f6 100644 --- a/src/MineCase.Server.Grains/Components/GameTickComponent.cs +++ b/src/MineCase.Server.Grains/Components/GameTickComponent.cs @@ -19,35 +19,37 @@ public GameTickComponent(string name = "gameTick") { } - protected override Task OnAttached() + protected override void OnAttached() { AttachedObject.GetComponent() .KeyChanged += OnAddressByPartitionKeyChanged; AttachedObject.RegisterPropertyChangedHandler(IsEnabledComponent.IsEnabledProperty, OnIsEnabledChanged); AttachedObject.QueueOperation(TrySubscribe); - return base.OnAttached(); } - protected override async Task OnDetached() + protected override void OnDetached() { AttachedObject.GetComponent() .KeyChanged -= OnAddressByPartitionKeyChanged; - await TryUnsubscribe(); + AttachedObject.QueueOperation(TryUnsubscribe); } - private async Task OnAddressByPartitionKeyChanged(object sender, (string oldKey, string newKey) e) + private void OnAddressByPartitionKeyChanged(object sender, (string oldKey, string newKey) e) { - if (!string.IsNullOrEmpty(e.oldKey)) - await GrainFactory.GetGrain(e.oldKey).Unsubscribe(AttachedObject); - await TrySubscribe(); + AttachedObject.QueueOperation(async () => + { + if (!string.IsNullOrEmpty(e.oldKey)) + await GrainFactory.GetGrain(e.oldKey).Unsubscribe(AttachedObject); + await TrySubscribe(); + }); } - private Task OnIsEnabledChanged(object sender, PropertyChangedEventArgs e) + private void OnIsEnabledChanged(object sender, PropertyChangedEventArgs e) { if (e.NewValue) - return TrySubscribe(); + AttachedObject.QueueOperation(TrySubscribe); else - return TryUnsubscribe(); + AttachedObject.QueueOperation(TryUnsubscribe); } public Task OnGameTick(GameTickArgs e) diff --git a/src/MineCase.Server.Grains/Components/IsEnabledComponent.cs b/src/MineCase.Server.Grains/Components/IsEnabledComponent.cs index 94fb0557..bd859e21 100644 --- a/src/MineCase.Server.Grains/Components/IsEnabledComponent.cs +++ b/src/MineCase.Server.Grains/Components/IsEnabledComponent.cs @@ -20,12 +20,14 @@ public IsEnabledComponent(string name = "isEnabled") Task IHandle.Handle(Enable message) { - return AttachedObject.SetLocalValue(IsEnabledProperty, true); + AttachedObject.SetLocalValue(IsEnabledProperty, true); + return Task.CompletedTask; } Task IHandle.Handle(Disable message) { - return AttachedObject.SetLocalValue(IsEnabledProperty, false); + AttachedObject.SetLocalValue(IsEnabledProperty, false); + return Task.CompletedTask; } } } diff --git a/src/MineCase.Server.Grains/Components/NameComponent.cs b/src/MineCase.Server.Grains/Components/NameComponent.cs index 2be0366e..6976b865 100644 --- a/src/MineCase.Server.Grains/Components/NameComponent.cs +++ b/src/MineCase.Server.Grains/Components/NameComponent.cs @@ -18,7 +18,7 @@ public NameComponent(string name = "name") { } - public Task SetName(string value) => + public void SetName(string value) => AttachedObject.SetLocalValue(NameProperty, value); } } diff --git a/src/MineCase.Server.Grains/Components/WorldComponent.cs b/src/MineCase.Server.Grains/Components/WorldComponent.cs index 089affb1..d406eb4f 100644 --- a/src/MineCase.Server.Grains/Components/WorldComponent.cs +++ b/src/MineCase.Server.Grains/Components/WorldComponent.cs @@ -17,7 +17,7 @@ public WorldComponent(string name = "world") { } - public Task SetWorld(IWorld value) => + public void SetWorld(IWorld value) => AttachedObject.SetLocalValue(WorldProperty, value); } diff --git a/src/MineCase.Server.Grains/Game/BlockEntities/BlockEntityGrain.cs b/src/MineCase.Server.Grains/Game/BlockEntities/BlockEntityGrain.cs index c09c1887..132f2493 100644 --- a/src/MineCase.Server.Grains/Game/BlockEntities/BlockEntityGrain.cs +++ b/src/MineCase.Server.Grains/Game/BlockEntities/BlockEntityGrain.cs @@ -22,16 +22,16 @@ internal abstract class BlockEntityGrain : PersistableDependencyObject, IBlockEn public BlockWorldPos Position => GetValue(BlockWorldPositionComponent.BlockWorldPositionProperty); - protected override async Task InitializeComponents() + protected override void InitializeComponents() { - await SetComponent(new IsEnabledComponent()); - await SetComponent(new WorldComponent()); - await SetComponent(new BlockWorldPositionComponent()); - await SetComponent(new AddressByPartitionKeyComponent()); - await SetComponent(new ChunkEventBroadcastComponent()); - await SetComponent(new GameTickComponent()); - await SetComponent(new BlockEntityLiftTimeComponent()); - await SetComponent(new AutoSaveStateComponent(AutoSaveStateComponent.PerMinute)); + SetComponent(new IsEnabledComponent()); + SetComponent(new WorldComponent()); + SetComponent(new BlockWorldPositionComponent()); + SetComponent(new AddressByPartitionKeyComponent()); + SetComponent(new ChunkEventBroadcastComponent()); + SetComponent(new GameTickComponent()); + SetComponent(new BlockEntityLiftTimeComponent()); + SetComponent(new AutoSaveStateComponent(AutoSaveStateComponent.PerMinute)); } Task IBlockEntity.GetWorld() => diff --git a/src/MineCase.Server.Grains/Game/BlockEntities/ChestBlockEntityGrain.cs b/src/MineCase.Server.Grains/Game/BlockEntities/ChestBlockEntityGrain.cs index 2d63813f..65ad0f08 100644 --- a/src/MineCase.Server.Grains/Game/BlockEntities/ChestBlockEntityGrain.cs +++ b/src/MineCase.Server.Grains/Game/BlockEntities/ChestBlockEntityGrain.cs @@ -16,11 +16,11 @@ namespace MineCase.Server.Game.BlockEntities [Reentrant] internal class ChestBlockEntityGrain : BlockEntityGrain, IChestBlockEntity { - protected override async Task InitializeComponents() + protected override void InitializeComponents() { - await base.InitializeComponents(); - await SetComponent(new SlotContainerComponent(ChestSlotArea.ChestSlotsCount)); - await SetComponent(new ChestComponent()); + base.InitializeComponents(); + SetComponent(new SlotContainerComponent(ChestSlotArea.ChestSlotsCount)); + SetComponent(new ChestComponent()); } } } diff --git a/src/MineCase.Server.Grains/Game/BlockEntities/Components/BlockEntityLiftTimeComponent.cs b/src/MineCase.Server.Grains/Game/BlockEntities/Components/BlockEntityLiftTimeComponent.cs index 6cdf2185..9b4f7ebf 100644 --- a/src/MineCase.Server.Grains/Game/BlockEntities/Components/BlockEntityLiftTimeComponent.cs +++ b/src/MineCase.Server.Grains/Game/BlockEntities/Components/BlockEntityLiftTimeComponent.cs @@ -14,16 +14,17 @@ public BlockEntityLiftTimeComponent(string name = "blockEntityLifeTime") { } - async Task IHandle.Handle(SpawnBlockEntity message) + Task IHandle.Handle(SpawnBlockEntity message) { - await AttachedObject.GetComponent().SetWorld(message.World); - await AttachedObject.GetComponent().SetBlockWorldPosition(message.Position); + AttachedObject.GetComponent().SetWorld(message.World); + AttachedObject.GetComponent().SetBlockWorldPosition(message.Position); AttachedObject.QueueOperation(async () => { await AttachedObject.Tell(Enable.Default); if (AttachedObject.ValueStorage.IsDirty) await AttachedObject.WriteStateAsync(); }); + return Task.CompletedTask; } Task IHandle.Handle(DestroyBlockEntity message) diff --git a/src/MineCase.Server.Grains/Game/BlockEntities/Components/ChestComponent.cs b/src/MineCase.Server.Grains/Game/BlockEntities/Components/ChestComponent.cs index fa801a89..576981e1 100644 --- a/src/MineCase.Server.Grains/Game/BlockEntities/Components/ChestComponent.cs +++ b/src/MineCase.Server.Grains/Game/BlockEntities/Components/ChestComponent.cs @@ -29,23 +29,32 @@ public ChestComponent(string name = "chest") { } - Task IHandle.Handle(NeighborEntityChanged message) => + Task IHandle.Handle(NeighborEntityChanged message) + { AttachedObject.SetLocalValue(NeighborEntityProperty, message.Entity); + return Task.CompletedTask; + } - private static async Task OnNeighborEntityChanged(object sender, PropertyChangedEventArgs e) + private static void OnNeighborEntityChanged(object sender, PropertyChangedEventArgs e) { var component = ((DependencyObject)sender).GetComponent(); var window = component?.ChestWindow; if (window == null) return; if (e.NewValue == null) { - await window.Destroy(); - await window.SetEntities(new[] { component.AttachedObject.AsReference() }.AsImmutable()); + component.AttachedObject.QueueOperation(async () => + { + await window.Destroy(); + await window.SetEntities(new[] { component.AttachedObject.AsReference() }.AsImmutable()); + }); } else { - await window.Destroy(); - await window.SetEntities(new[] { component.AttachedObject.AsReference(), e.NewValue.AsReference() }.AsImmutable()); + component.AttachedObject.QueueOperation(async () => + { + await window.Destroy(); + await window.SetEntities(new[] { component.AttachedObject.AsReference(), e.NewValue.AsReference() }.AsImmutable()); + }); } } @@ -62,7 +71,7 @@ async Task IHandle.Handle(UseBy message) { if (ChestWindow == null) { - await AttachedObject.SetLocalValue(ChestWindowProperty, GrainFactory.GetGrain(Guid.NewGuid())); + AttachedObject.SetLocalValue(ChestWindowProperty, GrainFactory.GetGrain(Guid.NewGuid())); await ChestWindow.SetEntities((NeighborEntity == null ? new[] { AttachedObject.AsReference() } : new[] { AttachedObject.AsReference(), NeighborEntity }).AsImmutable()); diff --git a/src/MineCase.Server.Grains/Game/BlockEntities/Components/FurnaceComponent.cs b/src/MineCase.Server.Grains/Game/BlockEntities/Components/FurnaceComponent.cs index 566e56b7..c232eefb 100644 --- a/src/MineCase.Server.Grains/Game/BlockEntities/Components/FurnaceComponent.cs +++ b/src/MineCase.Server.Grains/Game/BlockEntities/Components/FurnaceComponent.cs @@ -46,7 +46,7 @@ public FurnaceComponent(string name = "furnace") { } - protected override Task OnAttached() + protected override void OnAttached() { if (State == null) { @@ -57,7 +57,6 @@ protected override Task OnAttached() } Register(); - return base.OnAttached(); } private bool CanCook() @@ -114,7 +113,7 @@ async Task IHandle.Handle(SetSlot message) private Slot GetSlot(int index) => AttachedObject.GetComponent().GetSlot(index); - private Task SetSlot(int index, Slot slot) => + private void SetSlot(int index, Slot slot) => AttachedObject.GetComponent().SetSlot(index, slot); private async Task UpdateFuel() @@ -143,9 +142,9 @@ private async Task Produce() { var state = State; if (GetSlot(2).IsEmpty) - await SetSlot(2, state.CurrentRecipe.Output); + SetSlot(2, state.CurrentRecipe.Output); else - await SetSlot(2, GetSlot(2).AddItemCount(state.CurrentRecipe.Output.ItemCount)); + SetSlot(2, GetSlot(2).AddItemCount(state.CurrentRecipe.Output.ItemCount)); state.CookProgress = 0; MarkDirty(); if (FurnaceWindow != null) @@ -160,7 +159,7 @@ private async Task TakeFuel() var state = State; var slot = GetSlot(1).AddItemCount(-state.CurrentFuel.Slot.ItemCount); slot.MakeEmptyIfZero(); - await SetSlot(1, slot); + SetSlot(1, slot); state.MaxFuelTime = state.FuelLeft = state.CurrentFuel.Time; MarkDirty(); if (FurnaceWindow != null) @@ -181,7 +180,7 @@ private async Task TakeIngredient() { var slot = GetSlot(0).AddItemCount(-state.CurrentRecipe.Input.ItemCount); slot.MakeEmptyIfZero(); - await SetSlot(0, slot); + SetSlot(0, slot); state.CookProgress = 0; state.MaxProgress = state.CurrentRecipe.Time; MarkDirty(); @@ -242,7 +241,7 @@ async Task IHandle.Handle(UseBy message) { if (FurnaceWindow == null) { - await AttachedObject.SetLocalValue(FurnaceWindowProperty, GrainFactory.GetGrain(Guid.NewGuid())); + AttachedObject.SetLocalValue(FurnaceWindowProperty, GrainFactory.GetGrain(Guid.NewGuid())); await FurnaceWindow.SetEntity(AttachedObject); } diff --git a/src/MineCase.Server.Grains/Game/BlockEntities/FurnaceBlockEntity.cs b/src/MineCase.Server.Grains/Game/BlockEntities/FurnaceBlockEntity.cs index 571fbbf1..92be1493 100644 --- a/src/MineCase.Server.Grains/Game/BlockEntities/FurnaceBlockEntity.cs +++ b/src/MineCase.Server.Grains/Game/BlockEntities/FurnaceBlockEntity.cs @@ -18,11 +18,11 @@ namespace MineCase.Server.Game.BlockEntities [Reentrant] internal class FurnaceBlockEntity : BlockEntityGrain, IFurnaceBlockEntity { - protected override async Task InitializeComponents() + protected override void InitializeComponents() { - await base.InitializeComponents(); - await SetComponent(new SlotContainerComponent(FurnaceSlotArea.FurnaceSlotsCount)); - await SetComponent(new FurnaceComponent()); + base.InitializeComponents(); + SetComponent(new SlotContainerComponent(FurnaceSlotArea.FurnaceSlotsCount)); + SetComponent(new FurnaceComponent()); } } } diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/ActiveWorldPartitionComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/ActiveWorldPartitionComponent.cs index 4320076f..767417a2 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/ActiveWorldPartitionComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/ActiveWorldPartitionComponent.cs @@ -15,26 +15,27 @@ public ActiveWorldPartitionComponent(string name = "activeWorldPartition") { } - protected override Task OnAttached() + protected override void OnAttached() { AttachedObject.GetComponent() .KeyChanged += ActiveWorldPartitionComponent_KeyChanged; - return base.OnAttached(); } - protected override Task OnDetached() + protected override void OnDetached() { AttachedObject.GetComponent() .KeyChanged -= ActiveWorldPartitionComponent_KeyChanged; - return base.OnDetached(); } - private async Task ActiveWorldPartitionComponent_KeyChanged(object sender, (string oldKey, string newKey) e) + private void ActiveWorldPartitionComponent_KeyChanged(object sender, (string oldKey, string newKey) e) { - if (!string.IsNullOrEmpty(e.oldKey)) - await GrainFactory.GetGrain(e.oldKey).Leave(AttachedObject); - if (!string.IsNullOrEmpty(e.newKey)) - await GrainFactory.GetGrain(e.newKey).Enter(AttachedObject); + AttachedObject.QueueOperation(async () => + { + if (!string.IsNullOrEmpty(e.oldKey)) + await GrainFactory.GetGrain(e.oldKey).Leave(AttachedObject); + if (!string.IsNullOrEmpty(e.newKey)) + await GrainFactory.GetGrain(e.newKey).Enter(AttachedObject); + }); } } } diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/ChunkLoaderComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/ChunkLoaderComponent.cs index e4979093..d575f2a3 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/ChunkLoaderComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/ChunkLoaderComponent.cs @@ -20,13 +20,12 @@ public ChunkLoaderComponent(string name = "chunkLoader") { } - protected override Task OnAttached() + protected override void OnAttached() { _loaded = false; _chunkLoader = GrainFactory.GetGrain(AttachedObject.GetPrimaryKey()); AttachedObject.RegisterPropertyChangedHandler(ViewDistanceComponent.ViewDistanceProperty, OnViewDistanceChanged); AttachedObject.GetComponent().Tick += OnGameTick; - return base.OnAttached(); } private Task OnGameTick(object sender, GameTickArgs e) @@ -36,9 +35,9 @@ private Task OnGameTick(object sender, GameTickArgs e) return Task.CompletedTask; } - private Task OnViewDistanceChanged(object sender, PropertyChangedEventArgs e) + private void OnViewDistanceChanged(object sender, PropertyChangedEventArgs e) { - return _chunkLoader.SetViewDistance(e.NewValue); + AttachedObject.QueueOperation(() => _chunkLoader.SetViewDistance(e.NewValue)); } async Task IHandle.Handle(PlayerLoggedIn message) diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/ColliderComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/ColliderComponent.cs index 60fbdb78..11d84ef0 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/ColliderComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/ColliderComponent.cs @@ -22,55 +22,57 @@ public ColliderComponent(string name = "collider") { } - protected override Task OnAttached() + protected override void OnAttached() { AttachedObject.GetComponent() .KeyChanged += AddressByPartitionKeyChanged; AttachedObject.RegisterPropertyChangedHandler(IsEnabledComponent.IsEnabledProperty, OnIsEnabledChanged); AttachedObject.QueueOperation(TrySubscribe); - return base.OnAttached(); } - protected override async Task OnDetached() + protected override void OnDetached() { AttachedObject.GetComponent() .KeyChanged -= AddressByPartitionKeyChanged; - await TryUnsubscribe(); + AttachedObject.QueueOperation(TryUnsubscribe); } - private async Task AddressByPartitionKeyChanged(object sender, (string oldKey, string newKey) e) + private void AddressByPartitionKeyChanged(object sender, (string oldKey, string newKey) e) { - var shape = ColliderShape; - if (!string.IsNullOrEmpty(e.oldKey)) - await GrainFactory.GetGrain(e.oldKey).UnregisterCollider(AttachedObject); - await TrySubscribe(); + AttachedObject.QueueOperation(async () => + { + var shape = ColliderShape; + if (!string.IsNullOrEmpty(e.oldKey)) + await GrainFactory.GetGrain(e.oldKey).UnregisterCollider(AttachedObject); + await TrySubscribe(); + }); } - private Task OnIsEnabledChanged(object sender, PropertyChangedEventArgs e) + private void OnIsEnabledChanged(object sender, PropertyChangedEventArgs e) { if (e.NewValue) - return TrySubscribe(); + AttachedObject.QueueOperation(TrySubscribe); else - return TryUnsubscribe(); + AttachedObject.QueueOperation(TryUnsubscribe); } - private async Task OnColliderShapeChanged(PropertyChangedEventArgs e) + private void OnColliderShapeChanged(PropertyChangedEventArgs e) { var shape = ColliderShape; var key = AttachedObject.GetValue(AddressByPartitionKeyComponent.AddressByPartitionKeyProperty); if (shape != null) - await GrainFactory.GetGrain(key).RegisterCollider(AttachedObject, shape); + AttachedObject.QueueOperation(() => GrainFactory.GetGrain(key).RegisterCollider(AttachedObject, shape)); else - await TrySubscribe(); + AttachedObject.QueueOperation(TrySubscribe); } - private static Task OnColliderShapeChanged(object sender, PropertyChangedEventArgs e) + private static void OnColliderShapeChanged(object sender, PropertyChangedEventArgs e) { var component = ((DependencyObject)sender).GetComponent(); - return component.OnColliderShapeChanged(e); + component.OnColliderShapeChanged(e); } - public Task SetColliderShape(Shape value) => + public void SetColliderShape(Shape value) => AttachedObject.SetLocalValue(ColliderShapeProperty, value); private async Task TrySubscribe() diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/DiscoveryRegisterComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/DiscoveryRegisterComponent.cs index 14b7dd19..64043d1e 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/DiscoveryRegisterComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/DiscoveryRegisterComponent.cs @@ -15,35 +15,37 @@ public DiscoveryRegisterComponent(string name = "discoveryRegister") { } - protected override Task OnAttached() + protected override void OnAttached() { AttachedObject.GetComponent() .KeyChanged += AddressByPartitionKeyChanged; AttachedObject.RegisterPropertyChangedHandler(IsEnabledComponent.IsEnabledProperty, OnIsEnabledChanged); AttachedObject.QueueOperation(TrySubscribe); - return base.OnAttached(); } - protected override async Task OnDetached() + protected override void OnDetached() { AttachedObject.GetComponent() .KeyChanged -= AddressByPartitionKeyChanged; - await TryUnsubscribe(); + AttachedObject.QueueOperation(TryUnsubscribe); } - private async Task AddressByPartitionKeyChanged(object sender, (string oldKey, string newKey) e) + private void AddressByPartitionKeyChanged(object sender, (string oldKey, string newKey) e) { - if (!string.IsNullOrEmpty(e.oldKey)) - await GrainFactory.GetGrain(e.oldKey).UnsubscribeDiscovery(AttachedObject); - await TrySubscribe(); + AttachedObject.QueueOperation(async () => + { + if (!string.IsNullOrEmpty(e.oldKey)) + await GrainFactory.GetGrain(e.oldKey).UnsubscribeDiscovery(AttachedObject); + await TrySubscribe(); + }); } - private Task OnIsEnabledChanged(object sender, PropertyChangedEventArgs e) + private void OnIsEnabledChanged(object sender, PropertyChangedEventArgs e) { if (e.NewValue) - return TrySubscribe(); + AttachedObject.QueueOperation(TrySubscribe); else - return TryUnsubscribe(); + AttachedObject.QueueOperation(TryUnsubscribe); } private async Task TrySubscribe() diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/DraggedSlotComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/DraggedSlotComponent.cs index c3149cbf..76a92f13 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/DraggedSlotComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/DraggedSlotComponent.cs @@ -18,12 +18,13 @@ public DraggedSlotComponent(string name = "draggedSlot") { } - public Task SetDraggedSlot(Slot value) => + public void SetDraggedSlot(Slot value) => AttachedObject.SetLocalValue(DraggedSlotProperty, value); Task IHandle.Handle(SetDraggedSlot message) { - return SetDraggedSlot(message.Slot); + SetDraggedSlot(message.Slot); + return Task.CompletedTask; } Task IHandle.Handle(AskDraggedSlot message) diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/EntityAiComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/EntityAiComponent.cs index 5f0507b8..8e08f4b8 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/EntityAiComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/EntityAiComponent.cs @@ -38,24 +38,22 @@ public EntityAiComponent(string name = "entityAi") random = new Random(); } - protected override async Task OnAttached() + protected override void OnAttached() { Register(); - await AttachedObject.SetLocalValue(EntityAiComponent.CreatureStateProperty, CreatureState.Stop); + AttachedObject.SetLocalValue(EntityAiComponent.CreatureStateProperty, CreatureState.Stop); CreateAi(MobType); AttachedObject.RegisterPropertyChangedHandler(MobTypeComponent.MobTypeProperty, OnMobTypeChanged); } - private Task OnMobTypeChanged(object sender, PropertyChangedEventArgs e) + private void OnMobTypeChanged(object sender, PropertyChangedEventArgs e) { CreateAi(e.NewValue); - return Task.CompletedTask; } - protected override Task OnDetached() + protected override void OnDetached() { Unregister(); - return base.OnDetached(); } private void Register() @@ -73,7 +71,7 @@ private void Unregister() private void CreateAi(MobType mobType) { Func getter = () => AttachedObject.GetValue(CreatureStateProperty); - Action setter = v => AttachedObject.SetLocalValue(CreatureStateProperty, v).Wait(); + Action setter = v => AttachedObject.SetLocalValue(CreatureStateProperty, v); CreatureAi ai; switch (mobType) @@ -150,8 +148,8 @@ private async Task ActionWalk() head = (float)(yaw / 180.0f * Math.PI); } - await AttachedObject.SetLocalValue(EntityLookComponent.YawProperty, (float)(head / Math.PI * 180.0f)); - await AttachedObject.SetLocalValue(EntityLookComponent.HeadYawProperty, (float)(head / Math.PI * 180.0f)); + AttachedObject.SetLocalValue(EntityLookComponent.YawProperty, (float)(head / Math.PI * 180.0f)); + AttachedObject.SetLocalValue(EntityLookComponent.HeadYawProperty, (float)(head / Math.PI * 180.0f)); // 新的位置 EntityWorldPos entityPos = new EntityWorldPos(pos.X - step * (float)Math.Sin(head), pos.Y, pos.Z + step * (float)Math.Cos(head)); @@ -195,7 +193,7 @@ private async Task ActionWalk() if (!isCollided && canWalk) { - await AttachedObject.SetLocalValue( + AttachedObject.SetLocalValue( EntityWorldPositionComponent.EntityWorldPositionProperty, EntityWorldPos.Add(entityPos, 0, yJumpHeight, 0)); } @@ -219,9 +217,9 @@ private async Task ActionLook() { (var yaw, var pitch) = VectorToYawAndPitch(entityPos, playerPosition); - await AttachedObject.SetLocalValue(EntityLookComponent.YawProperty, yaw); - await AttachedObject.SetLocalValue(EntityLookComponent.HeadYawProperty, yaw); - await AttachedObject.SetLocalValue(EntityLookComponent.PitchProperty, pitch); + AttachedObject.SetLocalValue(EntityLookComponent.YawProperty, yaw); + AttachedObject.SetLocalValue(EntityLookComponent.HeadYawProperty, yaw); + AttachedObject.SetLocalValue(EntityLookComponent.PitchProperty, pitch); break; } } @@ -272,7 +270,7 @@ private async Task GenerateEvent() nextEvent = CreatureEvent.Stop; } - await _ai.FireAsync(nextEvent); + _ai.Fire(nextEvent); } private async Task OnGameTick(object sender, GameTickArgs e) diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/EntityDiscoveryComponentBase.cs b/src/MineCase.Server.Grains/Game/Entities/Components/EntityDiscoveryComponentBase.cs index 89eb442c..1b36ecbd 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/EntityDiscoveryComponentBase.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/EntityDiscoveryComponentBase.cs @@ -19,11 +19,6 @@ public EntityDiscoveryComponentBase(string name) { } - protected override Task OnAttached() - { - return base.OnAttached(); - } - private ClientPlayPacketGenerator GetPlayerPacketGenerator(IPlayer player) => new ClientPlayPacketGenerator(new ForwardToPlayerPacketSink(player, ServiceProvider.GetRequiredService())); diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/EntityLifeTimeComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/EntityLifeTimeComponent.cs index 7c07a4a2..fbb5cc05 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/EntityLifeTimeComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/EntityLifeTimeComponent.cs @@ -15,13 +15,14 @@ public EntityLifeTimeComponent(string name = "entityLiftTime") { } - async Task IHandle.Handle(SpawnEntity message) + Task IHandle.Handle(SpawnEntity message) { - await AttachedObject.GetComponent().SetWorld(message.World); - await AttachedObject.GetComponent().SetPosition(message.Position); + AttachedObject.GetComponent().SetWorld(message.World); + AttachedObject.GetComponent().SetPosition(message.Position); var lookComponent = AttachedObject.GetComponent(); - await lookComponent.SetPitch(message.Pitch); - await lookComponent.SetYaw(message.Yaw); + lookComponent.SetPitch(message.Pitch); + lookComponent.SetYaw(message.Yaw); + return Task.CompletedTask; } async Task IHandle.Handle(DestroyEntity message) diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/EntityLookComponentBase.cs b/src/MineCase.Server.Grains/Game/Entities/Components/EntityLookComponentBase.cs index ba1028e2..9d8e9469 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/EntityLookComponentBase.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/EntityLookComponentBase.cs @@ -19,11 +19,6 @@ public EntityLookComponentBase(string name) { } - protected override Task OnAttached() - { - return base.OnAttached(); - } - private ClientPlayPacketGenerator GetPlayerPacketGenerator(IPlayer player) => new ClientPlayPacketGenerator(new ForwardToPlayerPacketSink(player, ServiceProvider.GetRequiredService())); diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/EntityMoveComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/EntityMoveComponent.cs index fd48b319..6c6b43b2 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/EntityMoveComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/EntityMoveComponent.cs @@ -20,11 +20,6 @@ public EntityMoveComponent(string name) { } - protected override Task OnAttached() - { - return base.OnAttached(); - } - private ClientPlayPacketGenerator GetPlayerPacketGenerator(IPlayer player) => new ClientPlayPacketGenerator(new ForwardToPlayerPacketSink(player, ServiceProvider.GetRequiredService())); diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/FoodComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/FoodComponent.cs index 0752b93f..c6cfb8cc 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/FoodComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/FoodComponent.cs @@ -28,13 +28,13 @@ public FoodComponent(string name = "food") { } - public Task SetFood(uint value) => + public void SetFood(uint value) => AttachedObject.SetLocalValue(FoodProperty, value); - public Task SetMaxFood(uint value) => + public void SetMaxFood(uint value) => AttachedObject.SetLocalValue(MaxFoodProperty, value); - public Task SetFoodSaturation(float value) => + public void SetFoodSaturation(float value) => AttachedObject.SetLocalValue(FoodSaturationProperty, value); } } diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/HealthComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/HealthComponent.cs index 87a219a0..f609882f 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/HealthComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/HealthComponent.cs @@ -23,10 +23,10 @@ public HealthComponent(string name = "health") { } - public Task SetHealth(uint value) => + public void SetHealth(uint value) => AttachedObject.SetLocalValue(HealthProperty, value); - public Task SetMaxHealth(uint value) => + public void SetMaxHealth(uint value) => AttachedObject.SetLocalValue(MaxHealthProperty, value); } } diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/HeldItemComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/HeldItemComponent.cs index b7cb0a3b..4925cf36 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/HeldItemComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/HeldItemComponent.cs @@ -25,11 +25,14 @@ public HeldItemComponent(string name = "heldItem") return (index, await inventory.GetSlot(AttachedObject, index)); } - public Task SetHeldItemIndex(int index) => + public void SetHeldItemIndex(int index) => AttachedObject.SetLocalValue(HeldItemIndexProperty, index); - Task IHandle.Handle(SetHeldItemIndex message) => + Task IHandle.Handle(SetHeldItemIndex message) + { SetHeldItemIndex(message.Index); + return Task.CompletedTask; + } Task<(int index, Slot slot)> IHandle.Handle(AskHeldItem message) => GetHeldItem(); diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/MobDiscoveryComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/MobDiscoveryComponent.cs index 8a4d59a6..d2e1d582 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/MobDiscoveryComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/MobDiscoveryComponent.cs @@ -19,14 +19,15 @@ public MobDiscoveryComponent(string name = "mobDiscovery") { } - async Task IHandle.Handle(SpawnMob message) + Task IHandle.Handle(SpawnMob message) { - await AttachedObject.Tell(message); - await AttachedObject.SetLocalValue(MobTypeComponent.MobTypeProperty, message.MobType); + AttachedObject.Tell(message); + AttachedObject.SetLocalValue(MobTypeComponent.MobTypeProperty, message.MobType); CompleteSpawn(); // Logger.LogInformation($"Mob spawn, key: {AttachedObject.GetAddressByPartitionKey()}"); // Logger.LogInformation($"Mob spawn, type: {message.MobType}"); + return Task.CompletedTask; } protected override Task SendSpawnPacket(ClientPlayPacketGenerator generator) diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/MobSpawnerComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/MobSpawnerComponent.cs index ab431263..a2df87db 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/MobSpawnerComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/MobSpawnerComponent.cs @@ -32,16 +32,14 @@ public MobSpawnerComponent(string name = "mobSpawner") random = new Random(); } - protected override Task OnAttached() + protected override void OnAttached() { Register(); - return base.OnAttached(); } - protected override Task OnDetached() + protected override void OnDetached() { Unregister(); - return base.OnDetached(); } private void Register() diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/MobTypeComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/MobTypeComponent.cs index 3d0e58eb..ddd0c2d9 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/MobTypeComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/MobTypeComponent.cs @@ -18,7 +18,7 @@ public MobTypeComponent(string name = "mobType") { } - public Task SetMobType(MobType value) => + public void SetMobType(MobType value) => AttachedObject.SetLocalValue(MobTypeProperty, value); } } diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/PickupDiscoveryComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/PickupDiscoveryComponent.cs index 6353d975..adb70e2e 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/PickupDiscoveryComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/PickupDiscoveryComponent.cs @@ -31,13 +31,15 @@ Task IHandle.Handle(DestroyEntity message) .DestroyEntities(new[] { AttachedObject.EntityId }); } - async Task IHandle.Handle(SpawnEntity message) + Task IHandle.Handle(SpawnEntity message) { var pos = message.Position; var bb = BoundingBox.Item(); var box = new Cuboid(new Point3d(pos.X, pos.Z, pos.Y), new Size(bb.X, bb.Y, bb.Z)); - await AttachedObject.SetLocalValue(ColliderComponent.ColliderShapeProperty, box); + AttachedObject.SetLocalValue(ColliderComponent.ColliderShapeProperty, box); CompleteSpawn(); + + return Task.CompletedTask; } } } diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/PickupMetadataComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/PickupMetadataComponent.cs index 409d5cea..90ccfcb9 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/PickupMetadataComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/PickupMetadataComponent.cs @@ -21,10 +21,9 @@ public PickupMetadataComponent(string name = "pickupMetadata") { } - protected override async Task OnAttached() + protected override void OnAttached() { - await base.OnAttached(); - await AttachedObject.SetLocalValue(PickupMetadataProperty, new Pickup()); + AttachedObject.SetLocalValue(PickupMetadataProperty, new Pickup()); } async Task IHandle.Handle(SetSlot message) diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/SlotContainerComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/SlotContainerComponent.cs index 3211a578..fee69653 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/SlotContainerComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/SlotContainerComponent.cs @@ -14,7 +14,7 @@ internal class SlotContainerComponent : Component, IHandle, IHandle SlotChanged; + public event EventHandler<(int index, Slot slot)> SlotChanged; public SlotContainerComponent(int slotsCount, string name = "slotContainer") : base(name) @@ -22,42 +22,42 @@ public SlotContainerComponent(int slotsCount, string name = "slotContainer") _slotsCount = slotsCount; } - protected override async Task OnAttached() + protected override void OnAttached() { var slots = AttachedObject.GetValue(SlotsProperty); if (slots == null || slots.Length != _slotsCount) - await AttachedObject.SetLocalValue(SlotsProperty, Enumerable.Repeat(Slot.Empty, _slotsCount).ToArray()); - await base.OnAttached(); + AttachedObject.SetLocalValue(SlotsProperty, Enumerable.Repeat(Slot.Empty, _slotsCount).ToArray()); } public Slot GetSlot(int index) => AttachedObject.GetValue(SlotsProperty)[index]; - public Task SetSlot(int index, Slot slot) + public void SetSlot(int index, Slot slot) { ref var old = ref AttachedObject.GetValue(SlotsProperty)[index]; if (old != slot) { old = slot; MarkDirty(); - return SlotChanged.InvokeSerial(this, (index, slot)); + SlotChanged?.Invoke(this, (index, slot)); } - - return Task.CompletedTask; } - public async Task SetSlots(Slot[] slots) + public void SetSlots(Slot[] slots) { if (slots.Length != _slotsCount) throw new ArgumentException(nameof(slots)); - await AttachedObject.SetLocalValue(SlotsProperty, slots); + AttachedObject.SetLocalValue(SlotsProperty, slots); for (int i = 0; i < _slotsCount; i++) - await SlotChanged.InvokeSerial(this, (i, slots[i])); + SlotChanged?.Invoke(this, (i, slots[i])); } Task IHandle.Handle(SetSlot message) - => SetSlot(message.Index, message.Slot); + { + SetSlot(message.Index, message.Slot); + return Task.CompletedTask; + } Task IHandle.Handle(AskSlot message) => Task.FromResult(GetSlot(message.Index)); diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/StandaloneHeldItemComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/StandaloneHeldItemComponent.cs index 0a8594ce..888eaabc 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/StandaloneHeldItemComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/StandaloneHeldItemComponent.cs @@ -18,7 +18,7 @@ public StandaloneHeldItemComponent(string name = "standaloneHeldItem") { } - public Task SetHeldItem(Slot value) => + public void SetHeldItem(Slot value) => AttachedObject.SetLocalValue(HeldItemProperty, value); Task IHandle.Handle(SetHeldItemIndex message) @@ -33,7 +33,8 @@ Task IHandle.Handle(SetHeldItemIndex message) Task IHandle.Handle(SetHeldItem message) { - return SetHeldItem(message.Slot); + SetHeldItem(message.Slot); + return Task.CompletedTask; } } } diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/SyncMobStateComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/SyncMobStateComponent.cs index 7f1da07d..50dd3c0e 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/SyncMobStateComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/SyncMobStateComponent.cs @@ -16,11 +16,10 @@ public SyncMobStateComponent(string name = "syncPlayerState") { } - protected override Task OnAttached() + protected override void OnAttached() { if (AttachedObject.GetValue(IsEnabledComponent.IsEnabledProperty)) InstallPropertyChangedHandlers(); - return base.OnAttached(); } Task IHandle.Handle(SpawnMob message) @@ -37,14 +36,14 @@ private void InstallPropertyChangedHandlers() AttachedObject.RegisterPropertyChangedHandler(EntityWorldPositionComponent.EntityWorldPositionProperty, OnPositionChanged); } - private Task OnHeadYawChanged(object sender, PropertyChangedEventArgs e) + private void OnHeadYawChanged(object sender, PropertyChangedEventArgs e) { uint eid = AttachedObject.GetValue(EntityIdComponent.EntityIdProperty); byte headyaw = (byte)(AttachedObject.GetValue(EntityLookComponent.HeadYawProperty) / 360 * 255); - return AttachedObject.GetComponent().GetGenerator().EntityHeadLook(eid, headyaw); + AttachedObject.QueueOperation(() => AttachedObject.GetComponent().GetGenerator().EntityHeadLook(eid, headyaw)); } - private Task OnYawChanged(object sender, PropertyChangedEventArgs e) + private void OnYawChanged(object sender, PropertyChangedEventArgs e) { /* uint eid = AttachedObject.GetValue(EntityIdComponent.EntityIdProperty); @@ -55,10 +54,9 @@ private Task OnYawChanged(object sender, PropertyChangedEventArgs e) // return AttachedObject.GetComponent().GetGenerator().EntityLook(eid, yaw, pitch, onGround); return AttachedObject.GetComponent().GetGenerator().EntityLookAndRelativeMove(eid, 0, 0, 0, yaw, pitch, onGround); */ - return Task.CompletedTask; } - private Task OnPitchChanged(object sender, PropertyChangedEventArgs e) + private void OnPitchChanged(object sender, PropertyChangedEventArgs e) { uint eid = AttachedObject.GetValue(EntityIdComponent.EntityIdProperty); byte yaw = (byte)(AttachedObject.GetValue(EntityLookComponent.YawProperty) / 360 * 255); @@ -66,10 +64,10 @@ private Task OnPitchChanged(object sender, PropertyChangedEventArgs e) bool onGround = AttachedObject.GetValue(EntityOnGroundComponent.IsOnGroundProperty); // return AttachedObject.GetComponent().GetGenerator().EntityLook(eid, yaw, pitch, onGround); - return AttachedObject.GetComponent().GetGenerator().EntityLookAndRelativeMove(eid, 0, 0, 0, yaw, pitch, onGround); + AttachedObject.QueueOperation(() => AttachedObject.GetComponent().GetGenerator().EntityLookAndRelativeMove(eid, 0, 0, 0, yaw, pitch, onGround)); } - private Task OnPositionChanged(object sender, PropertyChangedEventArgs e) + private void OnPositionChanged(object sender, PropertyChangedEventArgs e) { uint eid = AttachedObject.GetValue(EntityIdComponent.EntityIdProperty); short x = (short)((e.NewValue.X - e.OldValue.X) * 32 * 128); @@ -79,7 +77,7 @@ private Task OnPositionChanged(object sender, PropertyChangedEventArgs().GetGenerator().EntityLookAndRelativeMove(eid, x, y, z, yaw, pitch, isOnGround); + AttachedObject.QueueOperation(() => AttachedObject.GetComponent().GetGenerator().EntityLookAndRelativeMove(eid, x, y, z, yaw, pitch, isOnGround)); } } } diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/SyncPlayerStateComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/SyncPlayerStateComponent.cs index 19fcbba9..60627f75 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/SyncPlayerStateComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/SyncPlayerStateComponent.cs @@ -52,17 +52,17 @@ private void InstallPropertyChangedHandlers() AttachedObject.RegisterPropertyChangedHandler(EntityWorldPositionComponent.EntityWorldPositionProperty, OnEntityWorldPositionChanged); } - private Task OnEntityWorldPositionChanged(object sender, PropertyChangedEventArgs e) + private void OnEntityWorldPositionChanged(object sender, PropertyChangedEventArgs e) { var pos = e.NewValue; var box = new Cuboid(new Point3d(pos.X, pos.Z, pos.Y), new Size(0.6f, 0.6f, 1.75f)); - return AttachedObject.SetLocalValue(ColliderComponent.ColliderShapeProperty, box); + AttachedObject.SetLocalValue(ColliderComponent.ColliderShapeProperty, box); } - private Task OnDraggedSlotChanged(object sender, PropertyChangedEventArgs e) + private void OnDraggedSlotChanged(object sender, PropertyChangedEventArgs e) { - return AttachedObject.GetComponent().GetGenerator() - .SetSlot(0xFF, 0, e.NewValue); + AttachedObject.QueueOperation(() => AttachedObject.GetComponent().GetGenerator() + .SetSlot(0xFF, 0, e.NewValue)); } async Task IHandle.Handle(BindToUser message) @@ -70,15 +70,15 @@ async Task IHandle.Handle(BindToUser message) AttachedObject.GetComponent().SlotChanged -= InventorySlotChanged; _user = message.User; - await AttachedObject.GetComponent().SetName(await message.User.GetName()); - await AttachedObject.GetComponent().SetSlots(await message.User.GetInventorySlots()); + AttachedObject.GetComponent().SetName(await message.User.GetName()); + AttachedObject.GetComponent().SetSlots(await message.User.GetInventorySlots()); AttachedObject.GetComponent().SlotChanged += InventorySlotChanged; } - private Task InventorySlotChanged(object sender, (int index, Slot slot) e) + private void InventorySlotChanged(object sender, (int index, Slot slot) e) { - return _user.SetInventorySlot(e.index, e.slot); + AttachedObject.QueueOperation(() => _user.SetInventorySlot(e.index, e.slot)); } } } diff --git a/src/MineCase.Server.Grains/Game/Entities/Components/WindowManagerComponent.cs b/src/MineCase.Server.Grains/Game/Entities/Components/WindowManagerComponent.cs index e0b6c05c..a82afba4 100644 --- a/src/MineCase.Server.Grains/Game/Entities/Components/WindowManagerComponent.cs +++ b/src/MineCase.Server.Grains/Game/Entities/Components/WindowManagerComponent.cs @@ -19,13 +19,12 @@ public WindowManagerComponent(string name = "windowManager") { } - protected override Task OnAttached() + protected override void OnAttached() { _windows = new Dictionary { { 0, new WindowContext { Window = AttachedObject.GetComponent().GetInventoryWindow() } } }; - return base.OnAttached(); } public async Task ClickWindow(byte windowId, short slot, ClickAction clickAction, short actionNumber, Slot clickedItem) diff --git a/src/MineCase.Server.Grains/Game/Entities/EntityGrain.cs b/src/MineCase.Server.Grains/Game/Entities/EntityGrain.cs index b4593689..beb9d63e 100644 --- a/src/MineCase.Server.Grains/Game/Entities/EntityGrain.cs +++ b/src/MineCase.Server.Grains/Game/Entities/EntityGrain.cs @@ -32,18 +32,18 @@ internal abstract class EntityGrain : PersistableDependencyObject, IEntity public float Yaw => GetValue(EntityLookComponent.YawProperty); - protected override async Task InitializeComponents() + protected override void InitializeComponents() { - await SetComponent(new IsEnabledComponent()); - await SetComponent(new EntityIdComponent()); - await SetComponent(new WorldComponent()); - await SetComponent(new EntityWorldPositionComponent()); - await SetComponent(new EntityLookComponent()); - await SetComponent(new AddressByPartitionKeyComponent()); - await SetComponent(new ChunkEventBroadcastComponent()); - await SetComponent(new GameTickComponent()); - await SetComponent(new ChunkAccessorComponent()); - await SetComponent(new AutoSaveStateComponent(AutoSaveStateComponent.PerMinute)); + SetComponent(new IsEnabledComponent()); + SetComponent(new EntityIdComponent()); + SetComponent(new WorldComponent()); + SetComponent(new EntityWorldPositionComponent()); + SetComponent(new EntityLookComponent()); + SetComponent(new AddressByPartitionKeyComponent()); + SetComponent(new ChunkEventBroadcastComponent()); + SetComponent(new GameTickComponent()); + SetComponent(new ChunkAccessorComponent()); + SetComponent(new AutoSaveStateComponent(AutoSaveStateComponent.PerMinute)); } Task IEntity.GetEntityId() => diff --git a/src/MineCase.Server.Grains/Game/Entities/MobGrain.cs b/src/MineCase.Server.Grains/Game/Entities/MobGrain.cs index 47cf901a..f4e6a46b 100644 --- a/src/MineCase.Server.Grains/Game/Entities/MobGrain.cs +++ b/src/MineCase.Server.Grains/Game/Entities/MobGrain.cs @@ -13,21 +13,21 @@ namespace MineCase.Server.Game.Entities [Reentrant] internal class MobGrain : EntityGrain, IMob { - protected override async Task InitializeComponents() + protected override void InitializeComponents() { - await base.InitializeComponents(); + base.InitializeComponents(); // await SetComponent(new ActiveWorldPartitionComponent()); - await SetComponent(new EntityLifeTimeComponent()); - await SetComponent(new EntityOnGroundComponent()); - await SetComponent(new HealthComponent()); - await SetComponent(new StandaloneHeldItemComponent()); - await SetComponent(new NameComponent()); - await SetComponent(new EntityAiComponent()); - await SetComponent(new DiscoveryRegisterComponent()); - await SetComponent(new MobDiscoveryComponent()); - await SetComponent(new MobTypeComponent()); - await SetComponent(new SyncMobStateComponent()); + SetComponent(new EntityLifeTimeComponent()); + SetComponent(new EntityOnGroundComponent()); + SetComponent(new HealthComponent()); + SetComponent(new StandaloneHeldItemComponent()); + SetComponent(new NameComponent()); + SetComponent(new EntityAiComponent()); + SetComponent(new DiscoveryRegisterComponent()); + SetComponent(new MobDiscoveryComponent()); + SetComponent(new MobTypeComponent()); + SetComponent(new SyncMobStateComponent()); /* if (MobType == MobType.Enderman) { @@ -40,9 +40,9 @@ protected override async Task InitializeComponents() public async override Task OnActivateAsync() { await base.OnActivateAsync(); - await this.SetLocalValue(HealthComponent.MaxHealthProperty, 20u); - await this.SetLocalValue(HealthComponent.HealthProperty, GetValue(HealthComponent.MaxHealthProperty)); - await this.SetLocalValue(EntityOnGroundComponent.IsOnGroundProperty, true); + this.SetLocalValue(HealthComponent.MaxHealthProperty, 20u); + this.SetLocalValue(HealthComponent.HealthProperty, GetValue(HealthComponent.MaxHealthProperty)); + this.SetLocalValue(EntityOnGroundComponent.IsOnGroundProperty, true); } } } diff --git a/src/MineCase.Server.Grains/Game/Entities/MonsterGrain.cs b/src/MineCase.Server.Grains/Game/Entities/MonsterGrain.cs index 7f12e4d3..f661341d 100644 --- a/src/MineCase.Server.Grains/Game/Entities/MonsterGrain.cs +++ b/src/MineCase.Server.Grains/Game/Entities/MonsterGrain.cs @@ -31,26 +31,26 @@ internal class MonsterGrain : EntityGrain, IMonster private Queue _tasks; */ - protected override async Task InitializeComponents() + protected override void InitializeComponents() { - await base.InitializeComponents(); - await SetComponent(new BlockPlacementComponent()); // 末影人 - await SetComponent(new DiggingComponent()); // 末影人 - await SetComponent(new EntityLifeTimeComponent()); - await SetComponent(new EntityOnGroundComponent()); - await SetComponent(new HealthComponent()); - await SetComponent(new StandaloneHeldItemComponent()); - await SetComponent(new NameComponent()); - await SetComponent(new DiscoveryRegisterComponent()); + base.InitializeComponents(); + SetComponent(new BlockPlacementComponent()); // 末影人 + SetComponent(new DiggingComponent()); // 末影人 + SetComponent(new EntityLifeTimeComponent()); + SetComponent(new EntityOnGroundComponent()); + SetComponent(new HealthComponent()); + SetComponent(new StandaloneHeldItemComponent()); + SetComponent(new NameComponent()); + SetComponent(new DiscoveryRegisterComponent()); } public async override Task OnActivateAsync() { await base.OnActivateAsync(); - await this.SetLocalValue(HealthComponent.MaxHealthProperty, 20u); - await this.SetLocalValue(FoodComponent.MaxFoodProperty, 20u); - await this.SetLocalValue(HealthComponent.HealthProperty, GetValue(HealthComponent.MaxHealthProperty)); - await this.SetLocalValue(FoodComponent.FoodProperty, GetValue(FoodComponent.MaxFoodProperty)); + this.SetLocalValue(HealthComponent.MaxHealthProperty, 20u); + this.SetLocalValue(FoodComponent.MaxFoodProperty, 20u); + this.SetLocalValue(HealthComponent.HealthProperty, GetValue(HealthComponent.MaxHealthProperty)); + this.SetLocalValue(FoodComponent.FoodProperty, GetValue(FoodComponent.MaxFoodProperty)); } /* diff --git a/src/MineCase.Server.Grains/Game/Entities/PassiveMobGrain.cs b/src/MineCase.Server.Grains/Game/Entities/PassiveMobGrain.cs index 411b89cb..3ac8d3c8 100644 --- a/src/MineCase.Server.Grains/Game/Entities/PassiveMobGrain.cs +++ b/src/MineCase.Server.Grains/Game/Entities/PassiveMobGrain.cs @@ -32,29 +32,29 @@ internal class PassiveMobGrain : EntityGrain, IPassiveMob private Queue _tasks; */ - protected override async Task InitializeComponents() + protected override void InitializeComponents() { - await base.InitializeComponents(); + base.InitializeComponents(); // await SetComponent(new BlockPlacementComponent()); // 末影人 // await SetComponent(new DiggingComponent()); // 末影人 - await SetComponent(new EntityLifeTimeComponent()); - await SetComponent(new EntityOnGroundComponent()); - await SetComponent(new HealthComponent()); - await SetComponent(new StandaloneHeldItemComponent()); - await SetComponent(new NameComponent()); - await SetComponent(new DiscoveryRegisterComponent()); - await SetComponent(new EntityAiComponent()); + SetComponent(new EntityLifeTimeComponent()); + SetComponent(new EntityOnGroundComponent()); + SetComponent(new HealthComponent()); + SetComponent(new StandaloneHeldItemComponent()); + SetComponent(new NameComponent()); + SetComponent(new DiscoveryRegisterComponent()); + SetComponent(new EntityAiComponent()); } public async override Task OnActivateAsync() { await base.OnActivateAsync(); - await this.SetLocalValue(HealthComponent.MaxHealthProperty, 20u); - await this.SetLocalValue(FoodComponent.MaxFoodProperty, 20u); - await this.SetLocalValue(HealthComponent.HealthProperty, GetValue(HealthComponent.MaxHealthProperty)); - await this.SetLocalValue(FoodComponent.FoodProperty, GetValue(FoodComponent.MaxFoodProperty)); + this.SetLocalValue(HealthComponent.MaxHealthProperty, 20u); + this.SetLocalValue(FoodComponent.MaxFoodProperty, 20u); + this.SetLocalValue(HealthComponent.HealthProperty, GetValue(HealthComponent.MaxHealthProperty)); + this.SetLocalValue(FoodComponent.FoodProperty, GetValue(FoodComponent.MaxFoodProperty)); } /* diff --git a/src/MineCase.Server.Grains/Game/Entities/PickupGrain.cs b/src/MineCase.Server.Grains/Game/Entities/PickupGrain.cs index ecb20c78..c8c75080 100644 --- a/src/MineCase.Server.Grains/Game/Entities/PickupGrain.cs +++ b/src/MineCase.Server.Grains/Game/Entities/PickupGrain.cs @@ -14,15 +14,15 @@ namespace MineCase.Server.Game.Entities [Reentrant] internal class PickupGrain : EntityGrain, IPickup { - protected override async Task InitializeComponents() + protected override void InitializeComponents() { - await base.InitializeComponents(); - await SetComponent(new EntityLifeTimeComponent()); - await SetComponent(new PickupMetadataComponent()); - await SetComponent(new DiscoveryRegisterComponent()); - await SetComponent(new PickupDiscoveryComponent()); - await SetComponent(new CollectorComponent()); - await SetComponent(new ColliderComponent()); + base.InitializeComponents(); + SetComponent(new EntityLifeTimeComponent()); + SetComponent(new PickupMetadataComponent()); + SetComponent(new DiscoveryRegisterComponent()); + SetComponent(new PickupDiscoveryComponent()); + SetComponent(new CollectorComponent()); + SetComponent(new ColliderComponent()); } /* diff --git a/src/MineCase.Server.Grains/Game/Entities/PlayerGrain.cs b/src/MineCase.Server.Grains/Game/Entities/PlayerGrain.cs index 882b166f..368ab6bb 100644 --- a/src/MineCase.Server.Grains/Game/Entities/PlayerGrain.cs +++ b/src/MineCase.Server.Grains/Game/Entities/PlayerGrain.cs @@ -25,46 +25,46 @@ namespace MineCase.Server.Game.Entities [Reentrant] internal class PlayerGrain : EntityGrain, IPlayer { - protected override async Task InitializeComponents() + protected override void InitializeComponents() { - await base.InitializeComponents(); - await SetComponent(new EntityLifeTimeComponent()); - await SetComponent(new ActiveWorldPartitionComponent()); - await SetComponent(new BlockPlacementComponent()); - await SetComponent(new ClientboundPacketComponent()); - await SetComponent(new ChunkLoaderComponent()); - await SetComponent(new DiggingComponent()); - await SetComponent(new DiscoveryRegisterComponent()); - await SetComponent(new DraggedSlotComponent()); - await SetComponent(new ExperienceComponent()); - await SetComponent(new EntityOnGroundComponent()); - await SetComponent(new FoodComponent()); - await SetComponent(new HealthComponent()); - await SetComponent(new HeldItemComponent()); - await SetComponent(new InventoryComponent()); - await SetComponent(new KeepAliveComponent()); - await SetComponent(new NameComponent()); - await SetComponent(new PlayerListComponent()); - await SetComponent(new PlayerDiscoveryComponent()); - await SetComponent(new ServerboundPacketComponent()); - await SetComponent(new SlotContainerComponent(SlotArea.UserSlotsCount)); - await SetComponent(new SyncPlayerStateComponent()); - await SetComponent(new TeleportComponent()); - await SetComponent(new TossPickupComponent()); - await SetComponent(new ViewDistanceComponent()); - await SetComponent(new WindowManagerComponent()); - await SetComponent(new CollectorComponent()); - await SetComponent(new ColliderComponent()); - await SetComponent(new MobSpawnerComponent()); + base.InitializeComponents(); + SetComponent(new EntityLifeTimeComponent()); + SetComponent(new ActiveWorldPartitionComponent()); + SetComponent(new BlockPlacementComponent()); + SetComponent(new ClientboundPacketComponent()); + SetComponent(new ChunkLoaderComponent()); + SetComponent(new DiggingComponent()); + SetComponent(new DiscoveryRegisterComponent()); + SetComponent(new DraggedSlotComponent()); + SetComponent(new ExperienceComponent()); + SetComponent(new EntityOnGroundComponent()); + SetComponent(new FoodComponent()); + SetComponent(new HealthComponent()); + SetComponent(new HeldItemComponent()); + SetComponent(new InventoryComponent()); + SetComponent(new KeepAliveComponent()); + SetComponent(new NameComponent()); + SetComponent(new PlayerListComponent()); + SetComponent(new PlayerDiscoveryComponent()); + SetComponent(new ServerboundPacketComponent()); + SetComponent(new SlotContainerComponent(SlotArea.UserSlotsCount)); + SetComponent(new SyncPlayerStateComponent()); + SetComponent(new TeleportComponent()); + SetComponent(new TossPickupComponent()); + SetComponent(new ViewDistanceComponent()); + SetComponent(new WindowManagerComponent()); + SetComponent(new CollectorComponent()); + SetComponent(new ColliderComponent()); + SetComponent(new MobSpawnerComponent()); } public override async Task OnActivateAsync() { await base.OnActivateAsync(); - await this.SetLocalValue(HealthComponent.MaxHealthProperty, 20u); - await this.SetLocalValue(FoodComponent.MaxFoodProperty, 20u); - await this.SetLocalValue(HealthComponent.HealthProperty, GetValue(HealthComponent.MaxHealthProperty)); - await this.SetLocalValue(FoodComponent.FoodProperty, GetValue(FoodComponent.MaxFoodProperty)); + this.SetLocalValue(HealthComponent.MaxHealthProperty, 20u); + this.SetLocalValue(FoodComponent.MaxFoodProperty, 20u); + this.SetLocalValue(HealthComponent.HealthProperty, GetValue(HealthComponent.MaxHealthProperty)); + this.SetLocalValue(FoodComponent.FoodProperty, GetValue(FoodComponent.MaxFoodProperty)); } public Task OnSwingHand(SwingHandState handState) diff --git a/src/MineCase.Server.Grains/Game/GameSession.cs b/src/MineCase.Server.Grains/Game/GameSession.cs index 85680df2..4057702e 100644 --- a/src/MineCase.Server.Grains/Game/GameSession.cs +++ b/src/MineCase.Server.Grains/Game/GameSession.cs @@ -5,8 +5,11 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using MineCase.Engine; using MineCase.Protocol.Play; +using MineCase.Server.Components; using MineCase.Server.Network.Play; +using MineCase.Server.Persistence.Components; using MineCase.Server.User; using MineCase.Server.World; using MineCase.World; @@ -16,24 +19,38 @@ namespace MineCase.Server.Game { [Reentrant] - internal class GameSession : Grain, IGameSession + internal class GameSession : DependencyObject, IGameSession { private IWorld _world; private IChunkSender _chunkSender; + private FixedUpdateComponent _fixedUpdate; private readonly Dictionary _users = new Dictionary(); - private IDisposable _gameTick; - private DateTime _lastGameTickTime; - private ILogger _logger; public override async Task OnActivateAsync() { + await base.OnActivateAsync(); _logger = ServiceProvider.GetRequiredService().CreateLogger(); _world = await GrainFactory.GetGrain(0).GetWorld(this.GetPrimaryKeyString()); _chunkSender = GrainFactory.GetGrain(this.GetPrimaryKeyString()); - _lastGameTickTime = DateTime.UtcNow; - _gameTick = RegisterTimer(OnGameTick, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(5)); + await _fixedUpdate.Start(_world); + } + + protected override void InitializeComponents() + { + SetComponent(new PeriodicSaveStateComponent(TimeSpan.FromMinutes(1))); + + _fixedUpdate = new FixedUpdateComponent(); + _fixedUpdate.Tick += OnFixedUpdate; + SetComponent(_fixedUpdate); + } + + private async Task OnFixedUpdate(object sender, GameTickArgs e) + { + await _world.OnGameTick(e); + await Task.WhenAll(from u in _users.Keys + select u.OnGameTick(e)); } public async Task JoinGame(IUser user) @@ -93,33 +110,6 @@ await item.GetName() == senderName) } } - private async Task OnGameTick(object state) - { - try - { - var now = DateTime.UtcNow; - var deltaTime = now - _lastGameTickTime; - - if (deltaTime.TotalMilliseconds < 30) return; - - _lastGameTickTime = now; - - var worldTime = await _world.GetTime(); - var timeArgs = new GameTickArgs { DeltaTime = deltaTime, WorldAge = worldTime.WorldAge, TimeOfDay = worldTime.TimeOfDay }; - - await _world.OnGameTick(timeArgs); - Task.WhenAll(from u in _users.Keys - select u.OnGameTick(timeArgs)).Ignore(); - - if (worldTime.WorldAge % 20 == 0) - _logger.LogInformation($"Delta Game Tick: {deltaTime.TotalMilliseconds}ms."); - } - catch (Exception ex) - { - _logger.LogError(ex, ex.Message); - } - } - private Task CreateStandardChatMessage(string name, string message) { StringComponent nameComponent = new StringComponent(name); diff --git a/src/MineCase.Server.Grains/MineCase.Server.Grains.csproj b/src/MineCase.Server.Grains/MineCase.Server.Grains.csproj index 4553f064..9488692f 100644 --- a/src/MineCase.Server.Grains/MineCase.Server.Grains.csproj +++ b/src/MineCase.Server.Grains/MineCase.Server.Grains.csproj @@ -22,6 +22,7 @@ + diff --git a/src/MineCase.Server.Grains/Network/Play/ServerboundPacketComponent.cs b/src/MineCase.Server.Grains/Network/Play/ServerboundPacketComponent.cs index 56dd725c..f0163b3a 100644 --- a/src/MineCase.Server.Grains/Network/Play/ServerboundPacketComponent.cs +++ b/src/MineCase.Server.Grains/Network/Play/ServerboundPacketComponent.cs @@ -30,18 +30,16 @@ public ServerboundPacketComponent(string name = "serverboundPacket") _receivePacket = new ActionBlock((Action)OnReceivePacket); } - protected override Task OnAttached() + protected override void OnAttached() { AttachedObject.GetComponent() .Tick += OnGameTick; - return base.OnAttached(); } - protected override Task OnDetached() + protected override void OnDetached() { AttachedObject.GetComponent() .Tick -= OnGameTick; - return base.OnDetached(); } private async Task OnGameTick(object sender, GameTickArgs e) @@ -182,9 +180,10 @@ private Task DispatchPacket(ServerboundChatMessage packet) return Task.CompletedTask; } - private async Task DispatchPacket(ClientSettings packet) + private Task DispatchPacket(ClientSettings packet) { - await AttachedObject.SetLocalValue(ViewDistanceComponent.ViewDistanceProperty, packet.ViewDistance); + AttachedObject.SetLocalValue(ViewDistanceComponent.ViewDistanceProperty, packet.ViewDistance); + return Task.CompletedTask; } private Task DispatchPacket(ServerboundPluginMessage packet) @@ -197,38 +196,42 @@ private Task DispatchPacket(ServerboundKeepAlive packet) return AttachedObject.GetComponent().ReceiveResponse(packet.KeepAliveId); } - private async Task DispatchPacket(ServerboundPositionAndLook packet) + private Task DispatchPacket(ServerboundPositionAndLook packet) { - await AttachedObject.GetComponent() + AttachedObject.GetComponent() .SetPosition(new EntityWorldPos((float)packet.X, (float)packet.FeetY, (float)packet.Z)); var lookComponent = AttachedObject.GetComponent(); - await lookComponent.SetPitch(packet.Pitch); - await lookComponent.SetYaw(packet.Yaw); - await AttachedObject.GetComponent() + lookComponent.SetPitch(packet.Pitch); + lookComponent.SetYaw(packet.Yaw); + AttachedObject.GetComponent() .SetIsOnGround(packet.OnGround); + return Task.CompletedTask; } - private async Task DispatchPacket(PlayerOnGround packet) + private Task DispatchPacket(PlayerOnGround packet) { - await AttachedObject.GetComponent() + AttachedObject.GetComponent() .SetIsOnGround(packet.OnGround); + return Task.CompletedTask; } - private async Task DispatchPacket(PlayerPosition packet) + private Task DispatchPacket(PlayerPosition packet) { - await AttachedObject.GetComponent() + AttachedObject.GetComponent() .SetPosition(new EntityWorldPos((float)packet.X, (float)packet.FeetY, (float)packet.Z)); - await AttachedObject.GetComponent() + AttachedObject.GetComponent() .SetIsOnGround(packet.OnGround); + return Task.CompletedTask; } - private async Task DispatchPacket(PlayerLook packet) + private Task DispatchPacket(PlayerLook packet) { var lookComponent = AttachedObject.GetComponent(); - await lookComponent.SetPitch(packet.Pitch); - await lookComponent.SetYaw(packet.Yaw); - await AttachedObject.GetComponent() + lookComponent.SetPitch(packet.Pitch); + lookComponent.SetYaw(packet.Yaw); + AttachedObject.GetComponent() .SetIsOnGround(packet.OnGround); + return Task.CompletedTask; } private Task DispatchPacket(UseEntity packet) @@ -242,7 +245,8 @@ private Task DispatchPacket(UseEntity packet) private Task DispatchPacket(ServerboundHeldItemChange packet) { - return AttachedObject.GetComponent().SetHeldItemIndex(packet.Slot); + AttachedObject.GetComponent().SetHeldItemIndex(packet.Slot); + return Task.CompletedTask; } private async Task DispatchPacket(PlayerDigging packet) diff --git a/src/MineCase.Server.Grains/Persistence/AppDbContext.cs b/src/MineCase.Server.Grains/Persistence/AppDbContext.cs index 322154da..07827c34 100644 --- a/src/MineCase.Server.Grains/Persistence/AppDbContext.cs +++ b/src/MineCase.Server.Grains/Persistence/AppDbContext.cs @@ -10,14 +10,13 @@ namespace MineCase.Server.Persistence { internal class AppDbContext { - public const string DatabaseName = "minecase"; - private readonly IMongoDatabase _db; public AppDbContext(IOptions options) { - var client = new MongoClient(options.Value.ConnectionString); - _db = client.GetDatabase(DatabaseName); + var url = new MongoUrl(options.Value.ConnectionString); + var client = new MongoClient(url); + _db = client.GetDatabase(url.DatabaseName); } public IMongoCollection GetEntityStateCollection(string name) diff --git a/src/MineCase.Server.Grains/Persistence/Components/AutoSaveStateComponent.cs b/src/MineCase.Server.Grains/Persistence/Components/AutoSaveStateComponent.cs index fe107808..9820f3bc 100644 --- a/src/MineCase.Server.Grains/Persistence/Components/AutoSaveStateComponent.cs +++ b/src/MineCase.Server.Grains/Persistence/Components/AutoSaveStateComponent.cs @@ -20,12 +20,11 @@ public AutoSaveStateComponent(int periodTime, string name = "autoSaveState") _periodTime = periodTime; } - protected override Task OnAttached() + protected override void OnAttached() { var tickComponent = AttachedObject.GetComponent(); if (tickComponent != null) tickComponent.Tick += OnGameTick; - return base.OnAttached(); } public Task OnGameTick(object sender, GameTickArgs e) diff --git a/src/MineCase.Server.Grains/Persistence/Components/PeriodicSaveStateComponent.cs b/src/MineCase.Server.Grains/Persistence/Components/PeriodicSaveStateComponent.cs new file mode 100644 index 00000000..253b7811 --- /dev/null +++ b/src/MineCase.Server.Grains/Persistence/Components/PeriodicSaveStateComponent.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using MineCase.Engine; +using MineCase.Server.Components; +using MineCase.World; + +namespace MineCase.Server.Persistence.Components +{ + internal class PeriodicSaveStateComponent : Component + { + private readonly TimeSpan _periodTime; + + public PeriodicSaveStateComponent(TimeSpan periodTime, string name = "periodicSaveState") + : base(name) + { + _periodTime = periodTime; + } + + protected override void OnAttached() + { + AttachedObject.RegisterTimer(SaveIfDirty, null, _periodTime, _periodTime); + } + + private Task SaveIfDirty(object state) + { + if (AttachedObject.ValueStorage.IsDirty) + { + return AttachedObject.WriteStateAsync(); + } + + return Task.CompletedTask; + } + } +} diff --git a/src/MineCase.Server.Grains/Persistence/Components/StateComponent.cs b/src/MineCase.Server.Grains/Persistence/Components/StateComponent.cs index d37e5098..1f4295bd 100644 --- a/src/MineCase.Server.Grains/Persistence/Components/StateComponent.cs +++ b/src/MineCase.Server.Grains/Persistence/Components/StateComponent.cs @@ -19,27 +19,29 @@ public class StateComponent : Component, IHandle, IHandle AttachedObject.GetValue(StateProperty); - public event AsyncEventHandler BeforeWriteState; + public event EventHandler BeforeWriteState; - public event AsyncEventHandler AfterReadState; + public event EventHandler AfterReadState; public StateComponent(string name = "state") : base(name) { } - async Task IHandle.Handle(AfterReadState message) + Task IHandle.Handle(AfterReadState message) { // 如果为 null 需要初始化状态 if (State == null) - await AttachedObject.SetLocalValue(StateProperty, (T)Activator.CreateInstance(typeof(T), InitializeStateMark.Default)); + AttachedObject.SetLocalValue(StateProperty, (T)Activator.CreateInstance(typeof(T), InitializeStateMark.Default)); - await AfterReadState.InvokeSerial(this, EventArgs.Empty); + AfterReadState?.Invoke(this, EventArgs.Empty); + return Task.CompletedTask; } Task IHandle.Handle(BeforeWriteState message) { - return BeforeWriteState.InvokeSerial(this, EventArgs.Empty); + BeforeWriteState?.Invoke(this, EventArgs.Empty); + return Task.CompletedTask; } } } diff --git a/src/MineCase.Server.Grains/Persistence/PersistableDependencyObject.cs b/src/MineCase.Server.Grains/Persistence/PersistableDependencyObject.cs index 48f82808..ddcd865a 100644 --- a/src/MineCase.Server.Grains/Persistence/PersistableDependencyObject.cs +++ b/src/MineCase.Server.Grains/Persistence/PersistableDependencyObject.cs @@ -27,23 +27,34 @@ public abstract class PersistableDependencyObject : DependencyObject { protected override async Task SerializeStateAsync(DependencyObjectState state) { - var coll = GetStateCollection(); - var key = GrainReference.ToKeyString(); - await coll.ReplaceOneAsync(o => o.GrainKeyString == key, state, new UpdateOptions { IsUpsert = true }); + if (CanPersist()) + { + var coll = GetStateCollection(); + var key = GrainReference.ToKeyString(); + await coll.ReplaceOneAsync(o => o.GrainKeyString == key, state, new UpdateOptions { IsUpsert = true }); + } } protected override async Task DeserializeStateAsync() { - var coll = GetStateCollection(); - var key = GrainReference.ToKeyString(); - return await coll.Find(o => o.GrainKeyString == key).FirstOrDefaultAsync(); + if (CanPersist()) + { + var coll = GetStateCollection(); + var key = GrainReference.ToKeyString(); + return await coll.Find(o => o.GrainKeyString == key).FirstOrDefaultAsync(); + } + + return null; } protected override async Task ClearStateAsync() { - var coll = GetStateCollection(); - var key = GrainReference.ToKeyString(); - await coll.DeleteOneAsync(o => o.GrainKeyString == key); + if (CanPersist()) + { + var coll = GetStateCollection(); + var key = GrainReference.ToKeyString(); + await coll.DeleteOneAsync(o => o.GrainKeyString == key); + } } private IMongoCollection GetStateCollection() @@ -56,5 +67,10 @@ private string GetTablePrefix() { return GetType().GetCustomAttribute().TableName; } + + private bool CanPersist() + { + return GetType().IsDefined(typeof(PersistTableName), true); + } } } diff --git a/src/MineCase.Server.Grains/StreamProviders.cs b/src/MineCase.Server.Grains/StreamProviders.cs index eb904300..b85bdda0 100644 --- a/src/MineCase.Server.Grains/StreamProviders.cs +++ b/src/MineCase.Server.Grains/StreamProviders.cs @@ -8,9 +8,13 @@ internal class StreamProviders { public const string JobsProvider = "JobsProvider"; + public const string TransientProvider = "TransientProvider"; + public static class Namespaces { public const string ChunkSender = "ChunkSender"; + + public const string TickEmitter = "TickEmitter"; } } } diff --git a/src/MineCase.Server.Grains/User/NonAuthenticatedUserGrain.cs b/src/MineCase.Server.Grains/User/NonAuthenticatedUserGrain.cs index 89ba6dbc..75057aa7 100644 --- a/src/MineCase.Server.Grains/User/NonAuthenticatedUserGrain.cs +++ b/src/MineCase.Server.Grains/User/NonAuthenticatedUserGrain.cs @@ -15,9 +15,9 @@ internal class NonAuthenticatedUserGrain : PersistableDependencyObject, INonAuth { private StateHolder State => GetValue(StateComponent.StateProperty); - protected override async Task InitializePreLoadComponent() + protected override void InitializePreLoadComponent() { - await SetComponent(new StateComponent()); + SetComponent(new StateComponent()); } public Task GetUUID() => Task.FromResult(State.UUID); diff --git a/src/MineCase.Server.Grains/User/UserGrain.cs b/src/MineCase.Server.Grains/User/UserGrain.cs index 58ba7213..ec723843 100644 --- a/src/MineCase.Server.Grains/User/UserGrain.cs +++ b/src/MineCase.Server.Grains/User/UserGrain.cs @@ -36,23 +36,26 @@ internal class UserGrain : PersistableDependencyObject, IUser private StateHolder State => GetValue(StateComponent.StateProperty); - protected override async Task InitializePreLoadComponent() + protected override void InitializePreLoadComponent() { var stateComponent = new StateComponent(); - await SetComponent(stateComponent); + SetComponent(stateComponent); stateComponent.AfterReadState += StateComponent_AfterReadState; _autoSave = new AutoSaveStateComponent(AutoSaveStateComponent.PerMinute); - await SetComponent(_autoSave); + SetComponent(_autoSave); } - private async Task StateComponent_AfterReadState(object sender, EventArgs e) + private void StateComponent_AfterReadState(object sender, EventArgs e) { if (State.World == null) { - var world = await GrainFactory.GetGrain(0).GetDefaultWorld(); - State.World = world; - MarkDirty(); + QueueOperation(async () => + { + var world = await GrainFactory.GetGrain(0).GetDefaultWorld(); + State.World = world; + MarkDirty(); + }); } } diff --git a/src/MineCase.Server.Grains/World/ChunkColumnGrain.cs b/src/MineCase.Server.Grains/World/ChunkColumnGrain.cs index c58c6c7c..b9f4ec17 100644 --- a/src/MineCase.Server.Grains/World/ChunkColumnGrain.cs +++ b/src/MineCase.Server.Grains/World/ChunkColumnGrain.cs @@ -15,6 +15,8 @@ using MineCase.World; using MineCase.World.Biomes; using MineCase.World.Generation; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.Serialization.Options; using Orleans; using Orleans.Concurrency; @@ -24,19 +26,16 @@ namespace MineCase.Server.World [Reentrant] internal class ChunkColumnGrain : AddressByPartitionGrain, IChunkColumn { - private AutoSaveStateComponent _autoSave; - private StateHolder State => GetValue(StateComponent.StateProperty); - protected override async Task InitializePreLoadComponent() + protected override void InitializePreLoadComponent() { - await SetComponent(new StateComponent()); + SetComponent(new StateComponent()); } - protected override async Task InitializeComponents() + protected override void InitializeComponents() { - _autoSave = new AutoSaveStateComponent(AutoSaveStateComponent.PerMinute * 5); - await SetComponent(_autoSave); + SetComponent(new PeriodicSaveStateComponent(TimeSpan.FromMinutes(1))); } public async Task GetBlockState(int x, int y, int z) @@ -187,11 +186,6 @@ public Task OnBlockNeighborChanged(int x, int y, int z, BlockWorldPos neighborPo return Task.CompletedTask; } - public Task OnGameTick(GameTickArgs e) - { - return _autoSave.OnGameTick(this, e); - } - private void MarkDirty() { ValueStorage.IsDirty = true; @@ -203,6 +197,7 @@ internal class StateHolder public ChunkColumnCompactStorage Storage { get; set; } + [BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)] public Dictionary BlockEntities { get; set; } public StateHolder() diff --git a/src/MineCase.Server.Grains/World/ChunkTrackingHub.cs b/src/MineCase.Server.Grains/World/ChunkTrackingHub.cs index be1f97b2..9b343292 100644 --- a/src/MineCase.Server.Grains/World/ChunkTrackingHub.cs +++ b/src/MineCase.Server.Grains/World/ChunkTrackingHub.cs @@ -59,10 +59,5 @@ public Task> GetTrackedPlayers() { return Task.FromResult(_trackingPlayers.Keys.ToList()); } - - public Task OnGameTick(GameTickArgs e) - { - return Task.CompletedTask; - } } } diff --git a/src/MineCase.Server.Grains/World/CollectableFinder.cs b/src/MineCase.Server.Grains/World/CollectableFinder.cs index 6ac61520..6e8d8e4f 100644 --- a/src/MineCase.Server.Grains/World/CollectableFinder.cs +++ b/src/MineCase.Server.Grains/World/CollectableFinder.cs @@ -27,7 +27,6 @@ public static readonly (int x, int z)[] CrossCoords = new[] (0, 0), (-1, 0), (-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1) }; - private AutoSaveStateComponent _autoSave; private List<(Cuboid box, ICollectableFinder finder)> _neighborFinders; private StateHolder State => GetValue(StateComponent.StateProperty); @@ -44,16 +43,15 @@ public override async Task OnActivateAsync() } } - protected override async Task InitializePreLoadComponent() + protected override void InitializePreLoadComponent() { var state = new StateComponent(); - await SetComponent(state); + SetComponent(state); } - protected override async Task InitializeComponents() + protected override void InitializeComponents() { - _autoSave = new AutoSaveStateComponent(AutoSaveStateComponent.PerMinute); - await SetComponent(_autoSave); + SetComponent(new PeriodicSaveStateComponent(TimeSpan.FromMinutes(1))); } public Task RegisterCollider(IDependencyObject entity, Shape colliderShape) @@ -117,11 +115,6 @@ private void MarkDirty() ValueStorage.IsDirty = true; } - public Task OnGameTick(GameTickArgs e) - { - return _autoSave.OnGameTick(this, e); - } - public class StateHolder { public Dictionary Colliders { get; set; } diff --git a/src/MineCase.Server.Grains/World/TickEmitterGrain.cs b/src/MineCase.Server.Grains/World/TickEmitterGrain.cs index 5c1ad6e7..f5bb44fb 100644 --- a/src/MineCase.Server.Grains/World/TickEmitterGrain.cs +++ b/src/MineCase.Server.Grains/World/TickEmitterGrain.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -11,66 +12,52 @@ using MineCase.World; using Orleans; using Orleans.Concurrency; +using Orleans.Streams; namespace MineCase.Server.World { - [PersistTableName("tickEmitter")] [Reentrant] - internal class TickEmitterGrain : PersistableDependencyObject, ITickEmitter + internal class TickEmitterGrain : AddressByPartitionGrain, ITickEmitter { - private AutoSaveStateComponent _autoSave; + private ImmutableHashSet _tickables = ImmutableHashSet.Empty; - private StateHolder State => GetValue(StateComponent.StateProperty); + private FixedUpdateComponent _fixedUpdate; - protected override async Task InitializePreLoadComponent() + protected override void InitializeComponents() { - await SetComponent(new StateComponent()); - } + SetComponent(new PeriodicSaveStateComponent(TimeSpan.FromMinutes(1))); - protected override async Task InitializeComponents() - { - _autoSave = new AutoSaveStateComponent(AutoSaveStateComponent.PerMinute); - await SetComponent(_autoSave); + _fixedUpdate = new FixedUpdateComponent(); + _fixedUpdate.Tick += OnFixedUpdate; + SetComponent(_fixedUpdate); } - public async Task OnGameTick(GameTickArgs e) + private Task OnFixedUpdate(object sender, GameTickArgs e) { - var message = new GameTick { Args = e }; - await Task.WhenAll(from en in State.Subscription select en.Tell(message)); - await _autoSave.OnGameTick(this, e); + var msg = new GameTick { Args = e }; + return Task.WhenAll(from t in _tickables select t.Tell(msg)); } - public Task Subscribe(IDependencyObject observer) + public async Task Subscribe(IDependencyObject observer) { - if (State.Subscription.Add(observer)) - MarkDirty(); - return Task.CompletedTask; - } - - public Task Unsubscribe(IDependencyObject observer) - { - if (State.Subscription.Remove(observer)) - MarkDirty(); - return Task.CompletedTask; - } + bool active = _tickables.IsEmpty; + _tickables = _tickables.Add(observer); - private void MarkDirty() - { - ValueStorage.IsDirty = true; + if (active) + { + await _fixedUpdate.Start(World); + } } - internal class StateHolder + public Task Unsubscribe(IDependencyObject observer) { - public HashSet Subscription { get; set; } - - public StateHolder() + _tickables = _tickables.Remove(observer); + if (_tickables.IsEmpty) { + _fixedUpdate.Stop(); } - public StateHolder(InitializeStateMark mark) - { - Subscription = new HashSet(); - } + return Task.CompletedTask; } } } diff --git a/src/MineCase.Server.Grains/World/WorldGrain.cs b/src/MineCase.Server.Grains/World/WorldGrain.cs index 69d2dcce..45319e3e 100644 --- a/src/MineCase.Server.Grains/World/WorldGrain.cs +++ b/src/MineCase.Server.Grains/World/WorldGrain.cs @@ -20,14 +20,23 @@ internal class WorldGrain : PersistableDependencyObject, IWorld { private GeneratorSettings _genSettings; // 生成设置 private string _seed; // 世界种子 - private AutoSaveStateComponent _autoSave; private readonly HashSet _activedPartitions = new HashSet(); private StateHolder State => GetValue(StateComponent.StateProperty); - protected override async Task InitializePreLoadComponent() + protected override void InitializePreLoadComponent() { - await SetComponent(new StateComponent()); + SetComponent(new StateComponent()); + } + + protected override void InitializeComponents() + { + SetComponent(new PeriodicSaveStateComponent(TimeSpan.FromMinutes(1))); + } + + public override async Task OnActivateAsync() + { + await base.OnActivateAsync(); var serverSettings = GrainFactory.GetGrain(0); _genSettings = new GeneratorSettings(); @@ -35,12 +44,6 @@ protected override async Task InitializePreLoadComponent() _seed = (await serverSettings.GetSettings()).LevelSeed; } - protected override async Task InitializeComponents() - { - _autoSave = new AutoSaveStateComponent(AutoSaveStateComponent.PerMinute); - await SetComponent(_autoSave); - } - public Task GetTime() { return Task.FromResult(new WorldTime { WorldAge = State.WorldAge, TimeOfDay = State.WorldAge % 24000 }); @@ -53,12 +56,11 @@ public Task NewEntityId() return Task.FromResult(id); } - public async Task OnGameTick(GameTickArgs e) + public Task OnGameTick(GameTickArgs e) { State.WorldAge++; MarkDirty(); - await Task.WhenAll(from p in _activedPartitions select p.OnGameTick(e)); - await _autoSave.OnGameTick(this, e); + return Task.CompletedTask; } public Task GetAge() => Task.FromResult(State.WorldAge); diff --git a/src/MineCase.Server.Grains/World/WorldPartitionGrain.cs b/src/MineCase.Server.Grains/World/WorldPartitionGrain.cs index 646e314c..eed759ee 100644 --- a/src/MineCase.Server.Grains/World/WorldPartitionGrain.cs +++ b/src/MineCase.Server.Grains/World/WorldPartitionGrain.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; +using MineCase.Server.Components; using MineCase.Server.Game.BlockEntities; using MineCase.Server.Game.Entities; using MineCase.Server.Game.Entities.Components; @@ -18,42 +20,27 @@ namespace MineCase.Server.World [Reentrant] internal class WorldPartitionGrain : AddressByPartitionGrain, IWorldPartition { - private ITickEmitter _tickEmitter; - private ICollectableFinder _collectableFinder; - private IChunkTrackingHub _chunkTrackingHub; - private IChunkColumn _chunkColumn; - private AutoSaveStateComponent _autoSave; private HashSet _players = new HashSet(); private StateHolder State => GetValue(StateComponent.StateProperty); - protected override async Task InitializePreLoadComponent() + protected override void InitializePreLoadComponent() { - await SetComponent(new StateComponent()); - - _tickEmitter = GrainFactory.GetPartitionGrain(this); - _collectableFinder = GrainFactory.GetPartitionGrain(this); - _chunkTrackingHub = GrainFactory.GetPartitionGrain(this); - _chunkColumn = GrainFactory.GetPartitionGrain(this); + SetComponent(new StateComponent()); } - protected override async Task InitializeComponents() + protected override void InitializeComponents() { - _autoSave = new AutoSaveStateComponent(AutoSaveStateComponent.PerMinute); - await SetComponent(_autoSave); + SetComponent(new PeriodicSaveStateComponent(TimeSpan.FromMinutes(1))); } public async Task Enter(IPlayer player) { - bool active = _players.Count == 0; if (_players.Add(player)) { var message = new DiscoveredByPlayer { Player = player }; await Task.WhenAll(from e in State.DiscoveryEntities select e.Tell(message)); - - if (active) - await World.ActivePartition(this); } } @@ -69,16 +56,6 @@ public async Task Leave(IPlayer player) } } - public async Task OnGameTick(GameTickArgs e) - { - await Task.WhenAll( - _tickEmitter.OnGameTick(e), - _collectableFinder.OnGameTick(e), - _chunkTrackingHub.OnGameTick(e), - _chunkColumn.OnGameTick(e)); - await _autoSave.OnGameTick(this, e); - } - async Task IWorldPartition.SubscribeDiscovery(IEntity entity) { if (State.DiscoveryEntities.Add(entity)) diff --git a/src/MineCase.Server.Interfaces/Game/ITickable.cs b/src/MineCase.Server.Interfaces/Game/ITickable.cs deleted file mode 100644 index 9b0c89f0..00000000 --- a/src/MineCase.Server.Interfaces/Game/ITickable.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Threading.Tasks; -using Orleans; - -namespace MineCase.Server.Game -{ - public interface ITickable : IGrainWithStringKey - { - Task OnGameTick(TimeSpan deltaTime, long worldAge); - } -} diff --git a/src/MineCase.Server.Interfaces/World/IChunkColumn.cs b/src/MineCase.Server.Interfaces/World/IChunkColumn.cs index 59fd0188..e10e6c22 100644 --- a/src/MineCase.Server.Interfaces/World/IChunkColumn.cs +++ b/src/MineCase.Server.Interfaces/World/IChunkColumn.cs @@ -24,7 +24,5 @@ public interface IChunkColumn : IAddressByPartition Task GetBlockEntity(int x, int y, int z); Task OnBlockNeighborChanged(int x, int y, int z, BlockWorldPos neighborPosition, BlockState oldState, BlockState newState); - - Task OnGameTick(GameTickArgs e); } } diff --git a/src/MineCase.Server.Interfaces/World/IChunkTrackingHub.cs b/src/MineCase.Server.Interfaces/World/IChunkTrackingHub.cs index 8f419ea7..aecc8ba2 100644 --- a/src/MineCase.Server.Interfaces/World/IChunkTrackingHub.cs +++ b/src/MineCase.Server.Interfaces/World/IChunkTrackingHub.cs @@ -18,7 +18,5 @@ public interface IChunkTrackingHub : IAddressByPartition, IPacketSink Task Unsubscribe(IPlayer player); Task> GetTrackedPlayers(); - - Task OnGameTick(GameTickArgs e); } } diff --git a/src/MineCase.Server.Interfaces/World/ICollectableFinder.cs b/src/MineCase.Server.Interfaces/World/ICollectableFinder.cs index 95624dd7..c5a623b6 100644 --- a/src/MineCase.Server.Interfaces/World/ICollectableFinder.cs +++ b/src/MineCase.Server.Interfaces/World/ICollectableFinder.cs @@ -22,7 +22,5 @@ public interface ICollectableFinder : IAddressByPartition Task> CollisionInChunk(Shape colliderShape); Task SpawnPickup(Vector3 position, Immutable slots); - - Task OnGameTick(GameTickArgs e); } } diff --git a/src/MineCase.Server.Interfaces/World/ITickEmitter.cs b/src/MineCase.Server.Interfaces/World/ITickEmitter.cs index 1aedd256..8ab6af8b 100644 --- a/src/MineCase.Server.Interfaces/World/ITickEmitter.cs +++ b/src/MineCase.Server.Interfaces/World/ITickEmitter.cs @@ -12,8 +12,6 @@ namespace MineCase.Server.World { public interface ITickEmitter : IAddressByPartition { - Task OnGameTick(GameTickArgs e); - Task Subscribe(IDependencyObject observer); Task Unsubscribe(IDependencyObject observer); diff --git a/src/MineCase.Server.Interfaces/World/IWorldPartition.cs b/src/MineCase.Server.Interfaces/World/IWorldPartition.cs index affa59c7..9fafe118 100644 --- a/src/MineCase.Server.Interfaces/World/IWorldPartition.cs +++ b/src/MineCase.Server.Interfaces/World/IWorldPartition.cs @@ -14,8 +14,6 @@ public interface IWorldPartition : IAddressByPartition Task Leave(IPlayer player); - Task OnGameTick(GameTickArgs e); - Task SubscribeDiscovery(IEntity entity); Task UnsubscribeDiscovery(IEntity entity); diff --git a/src/MineCase.Server/MineCase.Server.csproj b/src/MineCase.Server/MineCase.Server.csproj index dc669649..50499f0b 100644 --- a/src/MineCase.Server/MineCase.Server.csproj +++ b/src/MineCase.Server/MineCase.Server.csproj @@ -36,6 +36,7 @@ + diff --git a/src/MineCase.Server/OrleansConfiguration.dev.xml b/src/MineCase.Server/OrleansConfiguration.dev.xml index 947f7a17..c7f8de0e 100644 --- a/src/MineCase.Server/OrleansConfiguration.dev.xml +++ b/src/MineCase.Server/OrleansConfiguration.dev.xml @@ -3,11 +3,9 @@ - - - + diff --git a/src/MineCase.Server/OrleansConfiguration.docker.xml b/src/MineCase.Server/OrleansConfiguration.docker.xml index e353a922..7cdc61a6 100644 --- a/src/MineCase.Server/OrleansConfiguration.docker.xml +++ b/src/MineCase.Server/OrleansConfiguration.docker.xml @@ -3,11 +3,9 @@ - - - + diff --git a/src/MineCase.Server/Program.cs b/src/MineCase.Server/Program.cs index 2e360e4d..4b7fa497 100644 --- a/src/MineCase.Server/Program.cs +++ b/src/MineCase.Server/Program.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using MineCase.Serialization.Serializers; +using Orleans; namespace MineCase.Server { @@ -52,7 +53,11 @@ private static ClusterConfiguration LoadClusterConfiguration() { var cluster = new ClusterConfiguration(); cluster.LoadFromFile("OrleansConfiguration.dev.xml"); - cluster.AddMemoryStorageProvider(); + cluster.AddMongoDBStorageProvider("PubSubStore", c => + { + c.ConnectionString = Configuration.GetSection("persistenceOptions")["connectionString"]; + c.UseJsonFormat = true; + }); return cluster; } diff --git a/src/MineCase.Server/config.docker.json b/src/MineCase.Server/config.docker.json index 3cad04bd..cf0adbc3 100644 --- a/src/MineCase.Server/config.docker.json +++ b/src/MineCase.Server/config.docker.json @@ -1,5 +1,5 @@ { "persistenceOptions": { - "connectionString": "mongodb://minecase.persistdb:27017" + "connectionString": "mongodb://minecase.persistdb:27017/minecase" } } \ No newline at end of file diff --git a/src/MineCase.Server/config.json b/src/MineCase.Server/config.json index 03bcbdf3..d22abd3e 100644 --- a/src/MineCase.Server/config.json +++ b/src/MineCase.Server/config.json @@ -1,5 +1,5 @@ { "persistenceOptions": { - "connectionString": "mongodb://localhost:27017" + "connectionString": "mongodb://localhost:27017/minecase" } } \ No newline at end of file diff --git a/tests/UnitTest/SerializationTest.cs b/tests/UnitTest/SerializationTest.cs index a8e5dd54..eac39c57 100644 --- a/tests/UnitTest/SerializationTest.cs +++ b/tests/UnitTest/SerializationTest.cs @@ -19,16 +19,16 @@ public class SerializationTest public DependencyProperty SlotProperty = DependencyProperty.Register("Slot", typeof(SerializationTest), new PropertyMetadata(Slot.Empty)); public DependencyProperty ShapeProperty = DependencyProperty.Register("Shape", typeof(SerializationTest)); public DependencyProperty CuboidProperty = DependencyProperty.Register("Cuboid", typeof(SerializationTest)); - internal DependencyProperty StateProperty = DependencyProperty.Register("State", typeof(SerializationTest)); + public DependencyProperty StateProperty = DependencyProperty.Register("State", typeof(SerializationTest)); - internal class Pair + public class Pair { public int Int { get; set; } public Shape Collider { get; set; } } - internal class StateHolder + public class StateHolder { public List Shape { get; set; } } @@ -47,10 +47,10 @@ public async Task Test1() var entity = new TestEntity(); await entity.ReadStateAsync(); - await entity.SetCurrentValue(SlotProperty, slot); - await entity.SetCurrentValue(ShapeProperty, shape); - await entity.SetCurrentValue(CuboidProperty, shape); - await entity.SetCurrentValue(StateProperty, state); + entity.SetCurrentValue(SlotProperty, slot); + entity.SetCurrentValue(ShapeProperty, shape); + entity.SetCurrentValue(CuboidProperty, shape); + entity.SetCurrentValue(StateProperty, state); var doc = Serialize(entity); From 804076322ee66d2bf5e33fec30b86aae791b4a6e Mon Sep 17 00:00:00 2001 From: JunWang Date: Wed, 29 Nov 2017 20:01:36 +0800 Subject: [PATCH 05/11] Add DropBlockLoader --- data/dropblock.txt | 16 ++ .../DropBlockMappingMatcher.cs | 31 +++ src/MineCase.Core/DropBlockMappingLoader.cs | 250 ++++++++++++++++++ .../Game/DropBlockMappingGrain.cs | 44 +++ .../Game/IDropBlockMapping.cs | 14 + tests/UnitTest/CraftingRecipeTest.cs | 2 +- 6 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 data/dropblock.txt create mode 100644 src/MineCase.Algorithm/DropBlockMappingMatcher.cs create mode 100644 src/MineCase.Core/DropBlockMappingLoader.cs create mode 100644 src/MineCase.Server.Grains/Game/DropBlockMappingGrain.cs create mode 100644 src/MineCase.Server.Interfaces/Game/IDropBlockMapping.cs diff --git a/data/dropblock.txt b/data/dropblock.txt new file mode 100644 index 00000000..adbd142f --- /dev/null +++ b/data/dropblock.txt @@ -0,0 +1,16 @@ +#********************# +# Drop Block Mapping # +#********************# +# +# +#******************************************************# +# Basic Notation Help +# +# +#******************************************************# + + +#Block DroppedBlock Hardness Tool Hand Wooden Stone Iron Diamand Golden Shears Sword +Barrier = -, INF, -, INF, -, -, -, -, -, -, - +Stone = Cobblestone, 1.5, WoodenPickaxe, 7.5, 1.15, 0.6, 0.4, 0.3, 0.2, -, - + diff --git a/src/MineCase.Algorithm/DropBlockMappingMatcher.cs b/src/MineCase.Algorithm/DropBlockMappingMatcher.cs new file mode 100644 index 00000000..f76a0a12 --- /dev/null +++ b/src/MineCase.Algorithm/DropBlockMappingMatcher.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MineCase.Algorithm +{ + public class DropBlockMappingMatcher + { + private Dictionary _mapping; + + public DropBlockMappingMatcher(Dictionary mapping) + { + _mapping = mapping; + } + + public uint DropBlock(uint item, BlockState block) + { + if (_mapping.ContainsKey(block)) + { + DropBlockEntry entry = _mapping[block]; + + // TODO drop test + return block.Id; + } + else + { + return uint.MaxValue; + } + } + } +} diff --git a/src/MineCase.Core/DropBlockMappingLoader.cs b/src/MineCase.Core/DropBlockMappingLoader.cs new file mode 100644 index 00000000..814ffe67 --- /dev/null +++ b/src/MineCase.Core/DropBlockMappingLoader.cs @@ -0,0 +1,250 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace MineCase +{ + public class DropBlockMappingLoader + { + public Dictionary Mapping { get; } = new Dictionary(); + + public async Task LoadMapping(StreamReader streamReader) + { + while (!streamReader.EndOfStream) + { + var line = await streamReader.ReadLineAsync(); + ParseLine(line); + } + } + + private unsafe void ParseLine(string line) + { + var lineSpan = line.AsSpan(); + var commentIndex = lineSpan.IndexOf('#'); + if (commentIndex != -1) + lineSpan = lineSpan.Slice(0, commentIndex); + if (lineSpan.IsEmpty) return; + + char* buffer = stackalloc char[lineSpan.Length]; + int bufLen = 0; + + // 删除空格 + { + for (int i = 0; i < lineSpan.Length; i++) + { + if (!char.IsWhiteSpace(lineSpan[i])) + buffer[bufLen++] = lineSpan[i]; + } + } + + var normLine = new Span(buffer, bufLen); + if (normLine.IsEmpty) return; + var splitter = normLine.IndexOf('='); + + var resultSpan = normLine.Slice(0, splitter); + var ingredientsSpan = normLine.Slice(splitter + 1); + + var recipe = new CraftingRecipe(); + var resultSplitter = resultSpan.IndexOf(','); + if (resultSplitter == -1) + { + ParseItem(resultSpan, ref recipe.Output); + recipe.Output.ItemCount = 1; + } + else + { + ParseItem(resultSpan.Slice(0, resultSplitter), ref recipe.Output); + recipe.Output.ItemCount = byte.Parse(ToString(resultSpan.Slice(resultSplitter + 1))); + } + + var recipeSlots = new List(); + var restSpan = ingredientsSpan; + while (!restSpan.IsEmpty) + { + var ingredientSplitter = restSpan.IndexOf('|'); + var ingredientSpan = ingredientSplitter == -1 ? restSpan : restSpan.Slice(0, ingredientSplitter); + ParseIngredient(ingredientSpan, recipeSlots); + if (ingredientSplitter == -1) + break; + else + restSpan = restSpan.Slice(ingredientSplitter + 1); + } + + recipe.Inputs = recipeSlots.ToArray(); + NormalizeIngredients(recipe); + + // Recipes.Add(recipe); + } + + private void ParseIngredient(Span ingredientSpan, ICollection recipeSlots) + { + var slot = new Slot { ItemCount = 1 }; + var splitter = ingredientSpan.IndexOf(','); + ParseItem(ingredientSpan.Slice(0, splitter), ref slot); + var distributionSpan = ingredientSpan.Slice(splitter + 1); + + do + { + var positionSplitter = distributionSpan.IndexOf(','); + var positionSpan = positionSplitter == -1 ? distributionSpan : distributionSpan.Slice(0, positionSplitter); + + var recipeSlot = new CraftingRecipeSlot { Slot = slot }; + if (positionSpan.Length == 1 && positionSpan[0] == '*') + { + recipeSlot.X = -1; + recipeSlot.Y = -1; + } + else + { + splitter = positionSpan.IndexOf(':'); + + int ParsePoint(ReadOnlySpan span) + { + if (span.Length != 1) + throw new ArgumentOutOfRangeException(nameof(span)); + + switch (span[0]) + { + case '1': + return 0; + case '2': + return 1; + case '3': + return 2; + case '*': + return -1; + default: + throw new ArgumentOutOfRangeException(nameof(span)); + } + } + + recipeSlot.X = ParsePoint(positionSpan.Slice(0, splitter)); + recipeSlot.Y = ParsePoint(positionSpan.Slice(splitter + 1)); + } + + recipeSlots.Add(recipeSlot); + + if (positionSplitter == -1) + break; + else + distributionSpan = distributionSpan.Slice(positionSplitter + 1); + } + while (true); + } + + private void NormalizeIngredients(CraftingRecipe recipe) + { + int minX = 2, minY = 2; + int maxX = 0, maxY = 0; + + for (int i = 0; i < recipe.Inputs.Length; i++) + { + ref var recipeSlot = ref recipe.Inputs[i]; + if (recipeSlot.X >= 0) + { + minX = Math.Min(minX, recipeSlot.X); + maxX = Math.Max(maxX, recipeSlot.X); + } + + if (recipeSlot.Y >= 0) + { + minY = Math.Min(minY, recipeSlot.Y); + maxY = Math.Max(maxY, recipeSlot.Y); + } + } + + // 移动到左上角 + for (int i = 0; i < recipe.Inputs.Length; i++) + { + ref var recipeSlot = ref recipe.Inputs[i]; + if (recipeSlot.X >= 0) + recipeSlot.X -= minX; + + if (recipeSlot.Y >= 0) + recipeSlot.Y -= minY; + } + + recipe.Width = Math.Max(1, maxX - minX + 1); + recipe.Height = Math.Max(1, maxY - minY + 1); + } + + private void ParseItem(Span span, ref Slot slot) + { + var metaSplitter = span.IndexOf('^'); + var idSpan = metaSplitter == -1 ? span : span.Slice(0, metaSplitter); + + bool isBlock; + var text = ToString(idSpan); + if (Enum.TryParse(text, out ItemId item)) + { + slot.BlockId = (short)item; + isBlock = false; + } + else if (Enum.TryParse(text, out BlockId block)) + { + slot.BlockId = (short)block; + isBlock = true; + } + else + { + throw new ArgumentOutOfRangeException($"Invalid item name: {text}."); + } + + if (metaSplitter != -1) + { + var metaText = ToString(span.Slice(metaSplitter + 1)); + if (short.TryParse(metaText, out var value)) + { + slot.ItemDamage = value; + } + else + { + var itemsType = isBlock ? typeof(BlockStates) : typeof(ItemStates); + var paramEnum = itemsType.GetMethod(text).GetParameters()[0].ParameterType; + slot.ItemDamage = Convert.ToInt16(Enum.Parse(paramEnum, metaText)); + } + } + else + { + slot.ItemDamage = 0; + } + } + + private static unsafe string ToString(ReadOnlySpan span) + { + return new string((char*)Unsafe.AsPointer(ref span.DangerousGetPinnableReference()), 0, span.Length); + } + } + + public enum Tools + { + Hand, + Axes, + PickAxes, + Shovels, + Hoes + } + + public enum ToolMaterial + { + Hand, + Wooden, + Stone, + Iron, + Diamand, + Golden + } + + public class DropBlockEntry + { + public BlockState TargetBlock; + public BlockState DroppedBlock; + public float Hardness; + public Tools Tool; + public float Shears; + public float Sword; + } +} diff --git a/src/MineCase.Server.Grains/Game/DropBlockMappingGrain.cs b/src/MineCase.Server.Grains/Game/DropBlockMappingGrain.cs new file mode 100644 index 00000000..6e2b3b3a --- /dev/null +++ b/src/MineCase.Server.Grains/Game/DropBlockMappingGrain.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; +using MineCase.Algorithm; +using MineCase.Server.World; +using Orleans; +using Orleans.Concurrency; + +namespace MineCase.Server.Game +{ + [StatelessWorker] + internal class DropBlockMappingGrain : Grain, IDropBlockMapping + { + private const string _mappingFileName = "dropblock.txt"; + + private DropBlockMappingMatcher _mappingMatcher; + private IFileProvider _fileProvider; + + public DropBlockMappingGrain() + { + _fileProvider = new PhysicalFileProvider(AppContext.BaseDirectory); + } + + public Task DropBlock(uint itemId, BlockState block) + { + return Task.FromResult(_mappingMatcher.DropBlock(itemId, block)); + } + + public override async Task OnActivateAsync() + { + var file = _fileProvider.GetFileInfo(_mappingFileName); + + var recipeLoader = new DropBlockMappingLoader(); + using (var sr = new StreamReader(file.CreateReadStream())) + await recipeLoader.LoadMapping(sr); + _mappingMatcher = new DropBlockMappingMatcher(recipeLoader.Mapping); + } + } +} diff --git a/src/MineCase.Server.Interfaces/Game/IDropBlockMapping.cs b/src/MineCase.Server.Interfaces/Game/IDropBlockMapping.cs new file mode 100644 index 00000000..e89e012e --- /dev/null +++ b/src/MineCase.Server.Interfaces/Game/IDropBlockMapping.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Orleans; +using Orleans.Concurrency; + +namespace MineCase.Server.Game +{ + public interface IDropBlockMapping : IGrainWithIntegerKey + { + Task DropBlock(uint itemId, BlockState block); + } +} diff --git a/tests/UnitTest/CraftingRecipeTest.cs b/tests/UnitTest/CraftingRecipeTest.cs index 8ea80efb..3c206954 100644 --- a/tests/UnitTest/CraftingRecipeTest.cs +++ b/tests/UnitTest/CraftingRecipeTest.cs @@ -69,7 +69,7 @@ public async Task TestTools() var recipe = matcher.FindRecipe(new Slot[,] { { new Slot { BlockId = (short)BlockStates.WoodPlanks().Id, ItemCount = 1 }, Slot.Empty, Slot.Empty }, - { new Slot { BlockId = (short)BlockStates.WoodPlanks().Id, ItemCount = 1 }, new Slot { BlockId = (short)ItemId.Stick, ItemCount = 1 }, new Slot { BlockId = (short)ItemId.Stick, ItemCount = 1 }}, + { new Slot { BlockId = (short)BlockStates.WoodPlanks().Id, ItemCount = 1 }, new Slot { BlockId = (short)ItemId.Stick, ItemCount = 1 }, new Slot { BlockId = (short)ItemId.Stick, ItemCount = 1 } }, { new Slot { BlockId = (short)BlockStates.WoodPlanks().Id, ItemCount = 1 }, Slot.Empty, Slot.Empty } }); Assert.NotNull(recipe); From d48885dbdb79f06bed193d2c1ebdadcceae43700 Mon Sep 17 00:00:00 2001 From: JunWang Date: Wed, 29 Nov 2017 00:31:29 +0800 Subject: [PATCH 06/11] Fix a recipe bug --- data/crafting.txt | 52 +++++++++++------------ src/MineCase.Core/CraftingRecipeLoader.cs | 11 ++++- tests/UnitTest/CraftingRecipeTest.cs | 23 +++++++++- 3 files changed, 58 insertions(+), 28 deletions(-) diff --git a/data/crafting.txt b/data/crafting.txt index 907a08aa..0f5fcb3c 100644 --- a/data/crafting.txt +++ b/data/crafting.txt @@ -177,39 +177,39 @@ CraftingTable = WoodPlanks^-1, 1:1, 1:2, 2:1, 2:2 ## # ## Axes: -#DiamondAxe = Stick, 2:2, 2:3 | Diamond, 2:1, 1:1, 1:2 -#DiamondAxe = Stick, 2:2, 2:3 | Diamond, 2:1, 3:1, 3:2 -#GoldenAxe = Stick, 2:2, 2:3 | GoldIngot, 2:1, 1:1, 1:2 -#GoldenAxe = Stick, 2:2, 2:3 | GoldIngot, 2:1, 3:1, 3:2 -#IronAxe = Stick, 2:2, 2:3 | IronIngot, 2:1, 1:1, 1:2 -#IronAxe = Stick, 2:2, 2:3 | IronIngot, 2:1, 3:1, 3:2 -#StoneAxe = Stick, 2:2, 2:3 | Cobblestone, 2:1, 1:1, 1:2 -#StoneAxe = Stick, 2:2, 2:3 | Cobblestone, 2:1, 3:1, 3:2 -#WoodenAxe = Stick, 2:2, 2:3 | Planks^-1, 2:1, 1:1, 1:2 -#WoodenAxe = Stick, 2:2, 2:3 | Planks^-1, 2:1, 3:1, 3:2 +DiamondAxe = Stick, 2:2, 2:3 | Diamond, 2:1, 1:1, 1:2 +DiamondAxe = Stick, 2:2, 2:3 | Diamond, 2:1, 3:1, 3:2 +GoldenAxe = Stick, 2:2, 2:3 | GoldIngot, 2:1, 1:1, 1:2 +GoldenAxe = Stick, 2:2, 2:3 | GoldIngot, 2:1, 3:1, 3:2 +IronAxe = Stick, 2:2, 2:3 | IronIngot, 2:1, 1:1, 1:2 +IronAxe = Stick, 2:2, 2:3 | IronIngot, 2:1, 3:1, 3:2 +StoneAxe = Stick, 2:2, 2:3 | Cobblestone, 2:1, 1:1, 1:2 +StoneAxe = Stick, 2:2, 2:3 | Cobblestone, 2:1, 3:1, 3:2 +WoodenAxe = Stick, 2:2, 2:3 | WoodPlanks^-1, 2:1, 1:1, 1:2 +WoodenAxe = Stick, 2:2, 2:3 | WoodPlanks^-1, 2:1, 3:1, 3:2 # ## Pickaxes: -#DiamondPickaxe = Stick, 2:2, 2:3 | Diamond, 1:1, 2:1, 3:1 -#GoldenPickaxe = Stick, 2:2, 2:3 | GoldIngot, 1:1, 2:1, 3:1 -#IronPickaxe = Stick, 2:2, 2:3 | IronIngot, 1:1, 2:1, 3:1 -#StonePickaxe = Stick, 2:2, 2:3 | Cobblestone, 1:1, 2:1, 3:1 -#WoodenPickaxe = Stick, 2:2, 2:3 | Planks^-1, 1:1, 2:1, 3:1 +DiamondPickaxe = Stick, 2:2, 2:3 | Diamond, 1:1, 2:1, 3:1 +GoldenPickaxe = Stick, 2:2, 2:3 | GoldIngot, 1:1, 2:1, 3:1 +IronPickaxe = Stick, 2:2, 2:3 | IronIngot, 1:1, 2:1, 3:1 +StonePickaxe = Stick, 2:2, 2:3 | Cobblestone, 1:1, 2:1, 3:1 +WoodenPickaxe = Stick, 2:2, 2:3 | WoodPlanks^-1, 1:1, 2:1, 3:1 # ## Shovels: -#DiamondShovel = Stick, 2:2, 2:3 | Diamond, 2:1 -#GoldenShovel = Stick, 2:2, 2:3 | GoldIngot, 2:1 -#IronShovel = Stick, 2:2, 2:3 | IronIngot, 2:1 -#StoneShovel = Stick, 2:2, 2:3 | Cobblestone, 2:1 -#WoodenShovel = Stick, 2:2, 2:3 | Planks^-1, 2:1 +DiamondShovel = Stick, 2:2, 2:3 | Diamond, 2:1 +GoldenShovel = Stick, 2:2, 2:3 | GoldIngot, 2:1 +IronShovel = Stick, 2:2, 2:3 | IronIngot, 2:1 +StoneShovel = Stick, 2:2, 2:3 | Cobblestone, 2:1 +WoodenShovel = Stick, 2:2, 2:3 | WoodPlanks^-1, 2:1 # ## Hoes: -#DiamondHoe = Stick, 2:2, 2:3 | Diamond, 2:1, *:1 -#GoldenHoe = Stick, 2:2, 2:3 | GoldIngot, 2:1, *:1 -#IronHoe = Stick, 2:2, 2:3 | IronIngot, 2:1, *:1 -#StoneHoe = Stick, 2:2, 2:3 | Cobblestone, 2:1, *:1 -#WoodenHoe = Stick, 2:2, 2:3 | Planks^-1, 2:1, *:1 +DiamondHoe = Stick, 2:2, 2:3 | Diamond, 2:1, *:1 +GoldenHoe = Stick, 2:2, 2:3 | GoldIngot, 2:1, *:1 +IronHoe = Stick, 2:2, 2:3 | IronIngot, 2:1, *:1 +StoneHoe = Stick, 2:2, 2:3 | Cobblestone, 2:1, *:1 +WoodenHoe = Stick, 2:2, 2:3 | WoodPlanks^-1, 2:1, *:1 # -#Bucket = IronIngot, 1:1, 2:2, 3:1 +Bucket = IronIngot, 1:1, 2:2, 3:1 #Compass = IronIngot, 2:1, 1:2, 3:2, 2:3 | RedstoneDust, 2:2 #EmptyMap = Paper, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Compass, 2:2 #FireCharge, 3 = BlazePowder, * | Coal, * | Gunpowder, * diff --git a/src/MineCase.Core/CraftingRecipeLoader.cs b/src/MineCase.Core/CraftingRecipeLoader.cs index 9e522051..917f6ea1 100644 --- a/src/MineCase.Core/CraftingRecipeLoader.cs +++ b/src/MineCase.Core/CraftingRecipeLoader.cs @@ -72,6 +72,7 @@ private unsafe void ParseLine(string line) restSpan = restSpan.Slice(ingredientSplitter + 1); } + NormalizeIngredients(recipe); Recipes.Add(recipe); } @@ -130,8 +131,16 @@ int ParsePoint(ReadOnlySpan span) distributionSpan = distributionSpan.Slice(positionSplitter + 1); } while (true); + + if (recipe.Inputs != null) + { + foreach (CraftingRecipeSlot each in recipe.Inputs) + { + recipeSlots.Add(each); + } + } + recipe.Inputs = recipeSlots.ToArray(); - NormalizeIngredients(recipe); } private void NormalizeIngredients(CraftingRecipe recipe) diff --git a/tests/UnitTest/CraftingRecipeTest.cs b/tests/UnitTest/CraftingRecipeTest.cs index bb60f073..13c8668d 100644 --- a/tests/UnitTest/CraftingRecipeTest.cs +++ b/tests/UnitTest/CraftingRecipeTest.cs @@ -32,7 +32,7 @@ public async Task TestCraftingRecipeLoader() } var recipes = loader.Recipes; - Assert.Equal(13, recipes.Count); + Assert.Equal(13 + 26, recipes.Count); } [Fact] @@ -55,5 +55,26 @@ public async Task TestCraftingRecipeMatcher() Assert.Equal((short)BlockStates.WoodPlanks().Id, recipe.Result.BlockId); Assert.True(recipe.AfterTake.Cast().All(o => o.IsEmpty)); } + + [Fact] + public async Task TestTools() + { + var loader = new CraftingRecipeLoader(); + using (var sr = new StreamReader(File.OpenRead(Path.Combine(RootDir, "crafting.txt")))) + { + await loader.LoadRecipes(sr); + } + + var matcher = new CraftingRecipeMatcher(loader.Recipes); + var recipe = matcher.FindRecipe(new Slot[,] + { + { new Slot { BlockId = (short)BlockStates.WoodPlanks().Id, ItemCount = 1 }, Slot.Empty, Slot.Empty }, + { new Slot { BlockId = (short)BlockStates.WoodPlanks().Id, ItemCount = 1 }, new Slot { BlockId = (short)ItemId.Stick, ItemCount = 1 }, new Slot { BlockId = (short)ItemId.Stick, ItemCount = 1 }}, + { new Slot { BlockId = (short)BlockStates.WoodPlanks().Id, ItemCount = 1 }, Slot.Empty, Slot.Empty } + }); + Assert.NotNull(recipe); + Assert.Equal((short)ItemId.WoodenPickaxe, recipe.Result.BlockId); + Assert.True(recipe.AfterTake.Cast().All(o => o.IsEmpty)); + } } } From d31011958301af1d760c47e93605a6fd7fbfc485 Mon Sep 17 00:00:00 2001 From: SunnyCase Date: Wed, 29 Nov 2017 10:16:56 +0800 Subject: [PATCH 07/11] more elegant --- src/MineCase.Core/CraftingRecipeLoader.cs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/MineCase.Core/CraftingRecipeLoader.cs b/src/MineCase.Core/CraftingRecipeLoader.cs index 917f6ea1..302b49b2 100644 --- a/src/MineCase.Core/CraftingRecipeLoader.cs +++ b/src/MineCase.Core/CraftingRecipeLoader.cs @@ -60,30 +60,31 @@ private unsafe void ParseLine(string line) recipe.Output.ItemCount = byte.Parse(ToString(resultSpan.Slice(resultSplitter + 1))); } + var recipeSlots = new List(); var restSpan = ingredientsSpan; while (!restSpan.IsEmpty) { var ingredientSplitter = restSpan.IndexOf('|'); var ingredientSpan = ingredientSplitter == -1 ? restSpan : restSpan.Slice(0, ingredientSplitter); - ParseIngredient(ingredientSpan, recipe); + ParseIngredient(ingredientSpan, recipeSlots); if (ingredientSplitter == -1) break; else restSpan = restSpan.Slice(ingredientSplitter + 1); } + recipe.Inputs = recipeSlots.ToArray(); NormalizeIngredients(recipe); Recipes.Add(recipe); } - private void ParseIngredient(Span ingredientSpan, CraftingRecipe recipe) + private void ParseIngredient(Span ingredientSpan, ICollection recipeSlots) { var slot = new Slot { ItemCount = 1 }; var splitter = ingredientSpan.IndexOf(','); ParseItem(ingredientSpan.Slice(0, splitter), ref slot); var distributionSpan = ingredientSpan.Slice(splitter + 1); - var recipeSlots = new List(); do { var positionSplitter = distributionSpan.IndexOf(','); @@ -131,16 +132,6 @@ int ParsePoint(ReadOnlySpan span) distributionSpan = distributionSpan.Slice(positionSplitter + 1); } while (true); - - if (recipe.Inputs != null) - { - foreach (CraftingRecipeSlot each in recipe.Inputs) - { - recipeSlots.Add(each); - } - } - - recipe.Inputs = recipeSlots.ToArray(); } private void NormalizeIngredients(CraftingRecipe recipe) From e84b30b78d8541f33033e8a9f5c1fa033c6cecc4 Mon Sep 17 00:00:00 2001 From: JunWang Date: Wed, 29 Nov 2017 14:13:26 +0800 Subject: [PATCH 08/11] Add armors' craft --- data/crafting.txt | 8 ++++---- data/furnace.txt | 10 +++++----- tests/UnitTest/CraftingRecipeTest.cs | 2 +- tests/UnitTest/FurnaceRecipeTest.cs | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/data/crafting.txt b/data/crafting.txt index 0f5fcb3c..ae0a8576 100644 --- a/data/crafting.txt +++ b/data/crafting.txt @@ -248,10 +248,10 @@ Bucket = IronIngot, 1:1, 2:2, 3:1 ## # ## Helmets: -#DiamondHelmet = Diamond, 1:1, 2:1, 3:1, 1:2, 3:2 -#GoldenHelmet = GoldIngot, 1:1, 2:1, 3:1, 1:2, 3:2 -#IronHelmet = IronIngot, 1:1, 2:1, 3:1, 1:2, 3:2 -#LeatherHelmet = Leather, 1:1, 2:1, 3:1, 1:2, 3:2 +DiamondHelmet = Diamond, 1:1, 2:1, 3:1, 1:2, 3:2 +GoldenHelmet = GoldIngot, 1:1, 2:1, 3:1, 1:2, 3:2 +IronHelmet = IronIngot, 1:1, 2:1, 3:1, 1:2, 3:2 +LeatherCap = Leather, 1:1, 2:1, 3:1, 1:2, 3:2 # ## Chestplates: #DiamondChestplate = Diamond, 1:1, 3:1, 1:2, 2:2, 3:2, 1:3, 2:3, 3:3 diff --git a/data/furnace.txt b/data/furnace.txt index 67cf5e62..d9a1ebfa 100644 --- a/data/furnace.txt +++ b/data/furnace.txt @@ -53,16 +53,16 @@ #ChainmailLeggings = IronNugget #Chicken = CookedChicken #ChorusFruit = PoppedChorusFruit -#Clay = Brick +Clay = Brick #ClayBlock = HardenedClay -#CoalOre = Coal +CoalOre = Coal #Cobblestone = Stone #CrackedStonebrick = Stonebrick #CyanTerracotta = CyanGlazedTerracotta #DiamondOre = Diamond #EmeraldOre = Emerald #Fish = CookedFish -#GoldOre = GoldIngot +GoldOre = GoldIngot #GoldAxe = GoldNugget #GoldBoots = GoldNugget #GoldChestplate = GoldNugget @@ -75,7 +75,7 @@ #GoldSword = GoldNugget #GrayTerracotta = GrayGlazedTerracotta #GreenTerracotta = GreenGlazedTerracotta -#IronOre = IronIngot +IronOre = IronIngot #IronAxe = IronNugget #IronBoots = IronNugget #IronChestplate = IronNugget @@ -105,7 +105,7 @@ Wood2^-1 = Coal^Charcoal #RedTerracotta = RedGlazedTerracotta #RedstoneOre = Redstone #Salmon = CookedSalmon -#Sand = Glass +Sand = Glass #StoneBrick = CrackedStoneBricks #WetSponge = Sponge #WhiteTerracotta = WhiteGlazedTerracotta diff --git a/tests/UnitTest/CraftingRecipeTest.cs b/tests/UnitTest/CraftingRecipeTest.cs index 13c8668d..8ea80efb 100644 --- a/tests/UnitTest/CraftingRecipeTest.cs +++ b/tests/UnitTest/CraftingRecipeTest.cs @@ -32,7 +32,7 @@ public async Task TestCraftingRecipeLoader() } var recipes = loader.Recipes; - Assert.Equal(13 + 26, recipes.Count); + Assert.Equal(13 + 26 + 4, recipes.Count); } [Fact] diff --git a/tests/UnitTest/FurnaceRecipeTest.cs b/tests/UnitTest/FurnaceRecipeTest.cs index 06bc9783..59b78824 100644 --- a/tests/UnitTest/FurnaceRecipeTest.cs +++ b/tests/UnitTest/FurnaceRecipeTest.cs @@ -32,7 +32,7 @@ public async Task TestFurnaceRecipeLoader() } var recipes = loader.Recipes; - Assert.Equal(2, recipes.Count); + Assert.Equal(2 + 5, recipes.Count); var fuels = loader.Fuels; Assert.Equal(4, fuels.Count); } From d0d7047a5819ea2ab0d75df845460ac9eddd6438 Mon Sep 17 00:00:00 2001 From: JunWang Date: Wed, 29 Nov 2017 20:01:36 +0800 Subject: [PATCH 09/11] Add DropBlockLoader --- data/dropblock.txt | 16 ++ .../DropBlockMappingMatcher.cs | 31 +++ src/MineCase.Core/DropBlockMappingLoader.cs | 250 ++++++++++++++++++ .../Game/DropBlockMappingGrain.cs | 44 +++ .../Game/IDropBlockMapping.cs | 14 + tests/UnitTest/CraftingRecipeTest.cs | 2 +- 6 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 data/dropblock.txt create mode 100644 src/MineCase.Algorithm/DropBlockMappingMatcher.cs create mode 100644 src/MineCase.Core/DropBlockMappingLoader.cs create mode 100644 src/MineCase.Server.Grains/Game/DropBlockMappingGrain.cs create mode 100644 src/MineCase.Server.Interfaces/Game/IDropBlockMapping.cs diff --git a/data/dropblock.txt b/data/dropblock.txt new file mode 100644 index 00000000..adbd142f --- /dev/null +++ b/data/dropblock.txt @@ -0,0 +1,16 @@ +#********************# +# Drop Block Mapping # +#********************# +# +# +#******************************************************# +# Basic Notation Help +# +# +#******************************************************# + + +#Block DroppedBlock Hardness Tool Hand Wooden Stone Iron Diamand Golden Shears Sword +Barrier = -, INF, -, INF, -, -, -, -, -, -, - +Stone = Cobblestone, 1.5, WoodenPickaxe, 7.5, 1.15, 0.6, 0.4, 0.3, 0.2, -, - + diff --git a/src/MineCase.Algorithm/DropBlockMappingMatcher.cs b/src/MineCase.Algorithm/DropBlockMappingMatcher.cs new file mode 100644 index 00000000..f76a0a12 --- /dev/null +++ b/src/MineCase.Algorithm/DropBlockMappingMatcher.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MineCase.Algorithm +{ + public class DropBlockMappingMatcher + { + private Dictionary _mapping; + + public DropBlockMappingMatcher(Dictionary mapping) + { + _mapping = mapping; + } + + public uint DropBlock(uint item, BlockState block) + { + if (_mapping.ContainsKey(block)) + { + DropBlockEntry entry = _mapping[block]; + + // TODO drop test + return block.Id; + } + else + { + return uint.MaxValue; + } + } + } +} diff --git a/src/MineCase.Core/DropBlockMappingLoader.cs b/src/MineCase.Core/DropBlockMappingLoader.cs new file mode 100644 index 00000000..814ffe67 --- /dev/null +++ b/src/MineCase.Core/DropBlockMappingLoader.cs @@ -0,0 +1,250 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace MineCase +{ + public class DropBlockMappingLoader + { + public Dictionary Mapping { get; } = new Dictionary(); + + public async Task LoadMapping(StreamReader streamReader) + { + while (!streamReader.EndOfStream) + { + var line = await streamReader.ReadLineAsync(); + ParseLine(line); + } + } + + private unsafe void ParseLine(string line) + { + var lineSpan = line.AsSpan(); + var commentIndex = lineSpan.IndexOf('#'); + if (commentIndex != -1) + lineSpan = lineSpan.Slice(0, commentIndex); + if (lineSpan.IsEmpty) return; + + char* buffer = stackalloc char[lineSpan.Length]; + int bufLen = 0; + + // 删除空格 + { + for (int i = 0; i < lineSpan.Length; i++) + { + if (!char.IsWhiteSpace(lineSpan[i])) + buffer[bufLen++] = lineSpan[i]; + } + } + + var normLine = new Span(buffer, bufLen); + if (normLine.IsEmpty) return; + var splitter = normLine.IndexOf('='); + + var resultSpan = normLine.Slice(0, splitter); + var ingredientsSpan = normLine.Slice(splitter + 1); + + var recipe = new CraftingRecipe(); + var resultSplitter = resultSpan.IndexOf(','); + if (resultSplitter == -1) + { + ParseItem(resultSpan, ref recipe.Output); + recipe.Output.ItemCount = 1; + } + else + { + ParseItem(resultSpan.Slice(0, resultSplitter), ref recipe.Output); + recipe.Output.ItemCount = byte.Parse(ToString(resultSpan.Slice(resultSplitter + 1))); + } + + var recipeSlots = new List(); + var restSpan = ingredientsSpan; + while (!restSpan.IsEmpty) + { + var ingredientSplitter = restSpan.IndexOf('|'); + var ingredientSpan = ingredientSplitter == -1 ? restSpan : restSpan.Slice(0, ingredientSplitter); + ParseIngredient(ingredientSpan, recipeSlots); + if (ingredientSplitter == -1) + break; + else + restSpan = restSpan.Slice(ingredientSplitter + 1); + } + + recipe.Inputs = recipeSlots.ToArray(); + NormalizeIngredients(recipe); + + // Recipes.Add(recipe); + } + + private void ParseIngredient(Span ingredientSpan, ICollection recipeSlots) + { + var slot = new Slot { ItemCount = 1 }; + var splitter = ingredientSpan.IndexOf(','); + ParseItem(ingredientSpan.Slice(0, splitter), ref slot); + var distributionSpan = ingredientSpan.Slice(splitter + 1); + + do + { + var positionSplitter = distributionSpan.IndexOf(','); + var positionSpan = positionSplitter == -1 ? distributionSpan : distributionSpan.Slice(0, positionSplitter); + + var recipeSlot = new CraftingRecipeSlot { Slot = slot }; + if (positionSpan.Length == 1 && positionSpan[0] == '*') + { + recipeSlot.X = -1; + recipeSlot.Y = -1; + } + else + { + splitter = positionSpan.IndexOf(':'); + + int ParsePoint(ReadOnlySpan span) + { + if (span.Length != 1) + throw new ArgumentOutOfRangeException(nameof(span)); + + switch (span[0]) + { + case '1': + return 0; + case '2': + return 1; + case '3': + return 2; + case '*': + return -1; + default: + throw new ArgumentOutOfRangeException(nameof(span)); + } + } + + recipeSlot.X = ParsePoint(positionSpan.Slice(0, splitter)); + recipeSlot.Y = ParsePoint(positionSpan.Slice(splitter + 1)); + } + + recipeSlots.Add(recipeSlot); + + if (positionSplitter == -1) + break; + else + distributionSpan = distributionSpan.Slice(positionSplitter + 1); + } + while (true); + } + + private void NormalizeIngredients(CraftingRecipe recipe) + { + int minX = 2, minY = 2; + int maxX = 0, maxY = 0; + + for (int i = 0; i < recipe.Inputs.Length; i++) + { + ref var recipeSlot = ref recipe.Inputs[i]; + if (recipeSlot.X >= 0) + { + minX = Math.Min(minX, recipeSlot.X); + maxX = Math.Max(maxX, recipeSlot.X); + } + + if (recipeSlot.Y >= 0) + { + minY = Math.Min(minY, recipeSlot.Y); + maxY = Math.Max(maxY, recipeSlot.Y); + } + } + + // 移动到左上角 + for (int i = 0; i < recipe.Inputs.Length; i++) + { + ref var recipeSlot = ref recipe.Inputs[i]; + if (recipeSlot.X >= 0) + recipeSlot.X -= minX; + + if (recipeSlot.Y >= 0) + recipeSlot.Y -= minY; + } + + recipe.Width = Math.Max(1, maxX - minX + 1); + recipe.Height = Math.Max(1, maxY - minY + 1); + } + + private void ParseItem(Span span, ref Slot slot) + { + var metaSplitter = span.IndexOf('^'); + var idSpan = metaSplitter == -1 ? span : span.Slice(0, metaSplitter); + + bool isBlock; + var text = ToString(idSpan); + if (Enum.TryParse(text, out ItemId item)) + { + slot.BlockId = (short)item; + isBlock = false; + } + else if (Enum.TryParse(text, out BlockId block)) + { + slot.BlockId = (short)block; + isBlock = true; + } + else + { + throw new ArgumentOutOfRangeException($"Invalid item name: {text}."); + } + + if (metaSplitter != -1) + { + var metaText = ToString(span.Slice(metaSplitter + 1)); + if (short.TryParse(metaText, out var value)) + { + slot.ItemDamage = value; + } + else + { + var itemsType = isBlock ? typeof(BlockStates) : typeof(ItemStates); + var paramEnum = itemsType.GetMethod(text).GetParameters()[0].ParameterType; + slot.ItemDamage = Convert.ToInt16(Enum.Parse(paramEnum, metaText)); + } + } + else + { + slot.ItemDamage = 0; + } + } + + private static unsafe string ToString(ReadOnlySpan span) + { + return new string((char*)Unsafe.AsPointer(ref span.DangerousGetPinnableReference()), 0, span.Length); + } + } + + public enum Tools + { + Hand, + Axes, + PickAxes, + Shovels, + Hoes + } + + public enum ToolMaterial + { + Hand, + Wooden, + Stone, + Iron, + Diamand, + Golden + } + + public class DropBlockEntry + { + public BlockState TargetBlock; + public BlockState DroppedBlock; + public float Hardness; + public Tools Tool; + public float Shears; + public float Sword; + } +} diff --git a/src/MineCase.Server.Grains/Game/DropBlockMappingGrain.cs b/src/MineCase.Server.Grains/Game/DropBlockMappingGrain.cs new file mode 100644 index 00000000..6e2b3b3a --- /dev/null +++ b/src/MineCase.Server.Grains/Game/DropBlockMappingGrain.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; +using MineCase.Algorithm; +using MineCase.Server.World; +using Orleans; +using Orleans.Concurrency; + +namespace MineCase.Server.Game +{ + [StatelessWorker] + internal class DropBlockMappingGrain : Grain, IDropBlockMapping + { + private const string _mappingFileName = "dropblock.txt"; + + private DropBlockMappingMatcher _mappingMatcher; + private IFileProvider _fileProvider; + + public DropBlockMappingGrain() + { + _fileProvider = new PhysicalFileProvider(AppContext.BaseDirectory); + } + + public Task DropBlock(uint itemId, BlockState block) + { + return Task.FromResult(_mappingMatcher.DropBlock(itemId, block)); + } + + public override async Task OnActivateAsync() + { + var file = _fileProvider.GetFileInfo(_mappingFileName); + + var recipeLoader = new DropBlockMappingLoader(); + using (var sr = new StreamReader(file.CreateReadStream())) + await recipeLoader.LoadMapping(sr); + _mappingMatcher = new DropBlockMappingMatcher(recipeLoader.Mapping); + } + } +} diff --git a/src/MineCase.Server.Interfaces/Game/IDropBlockMapping.cs b/src/MineCase.Server.Interfaces/Game/IDropBlockMapping.cs new file mode 100644 index 00000000..e89e012e --- /dev/null +++ b/src/MineCase.Server.Interfaces/Game/IDropBlockMapping.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Orleans; +using Orleans.Concurrency; + +namespace MineCase.Server.Game +{ + public interface IDropBlockMapping : IGrainWithIntegerKey + { + Task DropBlock(uint itemId, BlockState block); + } +} diff --git a/tests/UnitTest/CraftingRecipeTest.cs b/tests/UnitTest/CraftingRecipeTest.cs index 8ea80efb..3c206954 100644 --- a/tests/UnitTest/CraftingRecipeTest.cs +++ b/tests/UnitTest/CraftingRecipeTest.cs @@ -69,7 +69,7 @@ public async Task TestTools() var recipe = matcher.FindRecipe(new Slot[,] { { new Slot { BlockId = (short)BlockStates.WoodPlanks().Id, ItemCount = 1 }, Slot.Empty, Slot.Empty }, - { new Slot { BlockId = (short)BlockStates.WoodPlanks().Id, ItemCount = 1 }, new Slot { BlockId = (short)ItemId.Stick, ItemCount = 1 }, new Slot { BlockId = (short)ItemId.Stick, ItemCount = 1 }}, + { new Slot { BlockId = (short)BlockStates.WoodPlanks().Id, ItemCount = 1 }, new Slot { BlockId = (short)ItemId.Stick, ItemCount = 1 }, new Slot { BlockId = (short)ItemId.Stick, ItemCount = 1 } }, { new Slot { BlockId = (short)BlockStates.WoodPlanks().Id, ItemCount = 1 }, Slot.Empty, Slot.Empty } }); Assert.NotNull(recipe); From 3c2d071e925d3a0f13e8b860041d4a5200583db4 Mon Sep 17 00:00:00 2001 From: JunWang Date: Sat, 20 Jan 2018 01:10:56 +0800 Subject: [PATCH 10/11] parse dropblock data --- data/dropblock.txt | 175 +++++++++++++++++- src/MineCase.Core/DropBlockMappingLoader.cs | 190 +++----------------- 2 files changed, 194 insertions(+), 171 deletions(-) diff --git a/data/dropblock.txt b/data/dropblock.txt index adbd142f..d79ad579 100644 --- a/data/dropblock.txt +++ b/data/dropblock.txt @@ -5,12 +5,173 @@ # #******************************************************# # Basic Notation Help +#******************************************************# # # -#******************************************************# - - -#Block DroppedBlock Hardness Tool Hand Wooden Stone Iron Diamand Golden Shears Sword -Barrier = -, INF, -, INF, -, -, -, -, -, -, - -Stone = Cobblestone, 1.5, WoodenPickaxe, 7.5, 1.15, 0.6, 0.4, 0.3, 0.2, -, - - +#Block DroppedBlock Hardness Tool Hand Wooden Stone Iron Diamand Golden Shears Sword +Barrier = -, Infinity, -, Infinity, -, -, -, -, -, -, - +Bedrock = -, Infinity, -, Infinity, -, -, -, -, -, -, - +CommandBlock = -, Infinity, -, Infinity, -, -, -, -, -, -, - +EndPortal = -, Infinity, -, Infinity, -, -, -, -, -, -, - +EndPortalFrame = -, Infinity, -, Infinity, -, -, -, -, -, -, - +Portal = -, Infinity, -, Infinity, -, -, -, -, -, -, - +Lava = -, 100, -, Infinity, -, -, -, -, -, -, - +StructureBlock = -, Infinity, -, Infinity, -, -, -, -, -, -, - +StructureVoid = -, 0, -, 0.05, -, -, -, -, -, -, - +Water = -, 100, -, Infinity, -, -, -, -, -, -, - +Obsidian = Obsidian, 50, DiamondPickaxe, 250, 125, 62.5, 41.7, 9.4, 20.85, -, - +EnderChest = Obsidian, 22.5, WoodenPickaxe, 112.5, 16.9, 8.45, 5.65, 4.25, 2.85, -, - +Anvil = Anvil, 5, WoodenPickaxe, 25, 3.75, 1.9, 1.25, 0.95, 0.65, -, - +BlockOfCoal = BlockOfCoal, 5, WoodenPickaxe, 25, 3.75, 1.9, 1.25, 0.95, 0.65, -, - +BlockOfDiamond = BlockOfDiamond, 5, IronPickaxe, 25, 12.5, 6.25, 1.25, 0.95, 2.1, -, - +BlockOfEmerald = BlockOfEmerald, 5, IronPickaxe, 25, 12.5, 6.25, 1.25, 0.95, 2.1, -, - +BlockOfIron = BlockOfIron, 5, StonePickaxe, 25, 12.5, 1.9, 1.25, 0.95, 2.1, -, - +BlockOfRedstone = BlockOfRedstone, 5, WoodenPickaxe, 25, 3.75, 1.9, 1.25, 0.95, 0.65, -, - +EnchantmentTable = EnchantmentTable, 5, WoodenPickaxe, 25, 3.75, 1.9, 1.25, 0.95, 0.65, -, - +IronBars = IronBars, 5, WoodenPickaxe, 25, 3.75, 1.9, 1.25, 0.95, 0.65, -, - +IronDoor = IronDoor, 5, WoodenPickaxe, 25, 3.75, 1.9, 1.25, 0.95, 0.65, -, - +IronTrapdoor = IronTrapdoor, , WoodenPickaxe5, 25, 3.75, 1.9, 1.25, 0.95, 0.65, -, - +MonsterSpawner = -, 5, WoodenPickaxe, 25, 3.75, 1.9, 1.25, 0.95, 0.65, -, - +* Cobweb = -, 4, -, 20, -, -, -, -, -, 0.4, 0.4 +Dispenser = Dispenser, 3.5, WoodenPickaxe, 17.5, 2.65, 1.35, 0.9, 0.7, 0.45, -, - +Dropper = Dropper, 3.5, WoodenPickaxe, 17.5, 2.65, 1.35, 0.9, 0.7, 0.45, -, - +Furnace = Furnace, 3.5, WoodenPickaxe, 17.5, 2.65, 1.35, 0.9, 0.7, 0.45, -, - +Beacon = Beacon, 3, -, 4.5, -, -, -, -, -, -, - +BlockOfGold = BlockOfGold, 3, IronPickaxe, 15, 7.5, 3.75, 0.75, 0.6, 1.25, -, - +CoalOre = CoalOre, 3, WoodenPickaxe, 15, 2.25, 1.15, 0.75, 0.6, 0.4, -, - +DragonEgg = DragonEgg, 3, -, 4.5, -, -, -, -, -, -, - +DiamondOre = DiamondOre, 3, IronPickaxe, 15, 7.5, 3.75, 0.75, 0.6, 1.25, -, - +EmeraldOre = EmeraldOre, 3, IronPickaxe, 15, 7.5, 3.75, 0.75, 0.6, 1.25, -, - +EndStone = EndStone, 3, WoodenPickaxe, 15, 2.25, 1.15, 0.75, 0.6, 0.4, -, - +GoldOre = GoldOre, 3, IronPickaxe, 15, 7.5, 3.75, 0.75, 0.6, 1.25, -, - +Hopper = Hopper, 3, WoodenPickaxe, 15, 2.25, 1.15, 0.75, 0.6, 0.4, -, - +IronOre = IronOre, 3, StonePickaxe, 15, 7.5, 1.15, 0.75, 0.6, 1.25, -, - +LapisLazuliBlock = LapisLazuliBlock, 3, StonePickaxe, 15, 7.5, 1.15, 0.75, 0.6, 1.25, -, - +LapisLazuliOre = LapisLazuliOre, 3, StonePickaxe, 15, 7.5, 1.15, 0.75, 0.6, 1.25, -, - +NetherQuartzOre = NetherQuartzOre, 3, WoodenPickaxe, 15, 2.25, 1.15, 0.75, 0.6, 0.4, -, - +RedstoneOre = RedstoneOre, 3, IronPickaxe, 15, 7.5, 3.75, 0.75, 0.6, 1.25, -, - +WoodenTrapdoor = Trapdoor, 3, Axe, 4.5, 2.25, 1.15, 0.75, 0.6, 0.4, -, - +* WoodenDoor = -, 3, Axe, 4.5, 2.25, 1.15, 0.75, 0.6, 0.4, -, - +Chest = Chest, 2.5, Axe, 3.75, 1.9, 0.95, 0.65, 0.5, 0.35, -, - +TrappedChest = TrappedChest, 2.5, Axe, 3.75, 1.9, 0.95, 0.65, 0.5, 0.35, -, - +CraftingTable = CraftingTable, 2.5, Axe, 3.75, 1.9, 0.95, 0.65, 0.5, 0.35, -, - +BoneBlock = BoneBlock, 2, WoodenPickaxe, 10, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +BrickStairs = BrickStairs, 2, WoodenPickaxe, 10, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +Bricks = Bricks, 2, WoodenPickaxe, 10, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +Cauldron = Cauldron, 2, WoodenPickaxe, 10, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +Cobblestone = Cobblestone, 2, WoodenPickaxe, 10, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +CobblestoneStairs = CobblestoneStairs, 2, WoodenPickaxe, 10, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +CobblestoneWall = CobblestoneWall, 2, WoodenPickaxe, 10, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +* Fence = -, 2, Axe, 3, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +FenceGate = FenceGate, 2, Axe, 3, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +Jukebox = Jukebox, 2, Axe, 3, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +MossStone = MossStone, 2, WoodenPickaxe, 10, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +NetherBrick = NetherBrick, 2, WoodenPickaxe, 10, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +RedNetherBrick = RedNetherBrick, 2, WoodenPickaxe, 10, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +NetherBrickFence = NetherBrickFence, 2, WoodenPickaxe, 10, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +NetherBrickStairs = NetherBrickStairs, 2, WoodenPickaxe, 10, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +* StoneSlab = StoneSlab, 2, WoodenPickaxe, 10, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +Wood = Wood, 2, Axe, 3, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +WoodPlanks = WoodPlanks, 2, Axe, 3, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +* WoodenSlabs = WoodenSlab, 2, Axe, 3, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +* WoodenStairs = OakWoodStairs, 2, Axe, 3, 1.5, 0.75, 0.5, 0.4, 0.25, -, - +Concrete = Concrete, 1.8, Pickaxe, 2.7, 1.35, 0.7, 0.45, 0.35, 0.25, -, - +* Andesite = -, 1.5, WoodenPickaxe, 7.5, 1.15, 0.6, 0.4, 0.3, 0.2, -, - +* Bookshelf = -, 1.5, Axe, 2.25, 1.15, 0.6, 0.4, 0.3, 0.2, -, - +DarkPrismarine = Prismarine, 1.5, WoodenPickaxe, 7.5, 1.15, 0.6, 0.4, 0.3, 0.2, -, - +* Diorite = -, 1.5, WoodenPickaxe, 7.5, 1.15, 0.6, 0.4, 0.3, 0.2, -, - +* Granite = -, 1.5, WoodenPickaxe, 7.5, 1.15, 0.6, 0.4, 0.3, 0.2, -, - +Prismarine = Prismarine, 1.5, WoodenPickaxe, 7.5, 1.15, 0.6, 0.4, 0.3, 0.2, -, - +PrismarineBricks = Prismarine, 1.5, WoodenPickaxe, 7.5, 1.15, 0.6, 0.4, 0.3, 0.2, -, - +Stone = Cobblestone, 1.5, WoodenPickaxe, 7.5, 1.15, 0.6, 0.4, 0.3, 0.2, -, - +StoneBrick = StoneBricks, 1.5, WoodenPickaxe, 7.5, 1.15, 0.6, 0.4, 0.3, 0.2, -, - +StoneBrickStairs = StoneBrickStairs, 1.5, WoodenPickaxe, 7.5, 1.15, 0.6, 0.4, 0.3, 0.2, -, - +* GlazedTerracotta = -, 1.4, Pickaxe, 2.1, 1.05, 0.55, 0.35, 0.3, 0.2, -, - +* Terracotta = -, 1.25, WoodenPickaxe, 6.25, 0.95, 0.5, 0.35, 0.25, 0.2, -, - +* Banner = -, 1, Axe, 1.5, 0.75, 0.4, 0.25, 0.2, 0.15, -, - +JackLantern = JackLantern, 1, Axe, 1.5, 0.75, 0.4, 0.25, 0.2, 0.15, -, 1 +Melon = Melon, 1, Axe, 1.5, 0.75, 0.4, 0.25, 0.2, 0.15, -, 1 +MobHead = MobHead, 1, -, 1.5, -, -, -, -, -, -, - +NetherWartBlock = NetherWartBlock, 1, -, 1.5, -, -, -, -, -, -, - +Pumpkin = Pumpkin, 1, Axe, 1.5, 0.75, 0.4, 0.25, 0.2, 0.15, -, 1 +Sign = StandingSign, 1, Axe, 1.5, 0.75, 0.4, 0.25, 0.2, 0.15, -, - +BlockOfQuartz = BlockOfQuartz, 0.8, WoodenPickaxe, 4, 0.65, 0.35, 0.2, 0.2, 0.1, -, - +NoteBlock = NoteBlock, 0.8, Axe, 1.25, 0.65, 0.35, 0.2, 0.2, 0.1, -, - +QuartzStairs = QuartzStairs, 0.8, WoodenPickaxe, 4, 0.65, 0.35, 0.2, 0.2, 0.1, -, - +RedSandstone = RedSandstone, 0.8, WoodenPickaxe, 4, 0.65, 0.35, 0.2, 0.2, 0.1, -, - +RedSandstoneStairs = RedSandstoneStairs, 0.8, WoodenPickaxe, 4, 0.65, 0.35, 0.2, 0.2, 0.1, -, - +Sandstone = Sandstone, 0.8, WoodenPickaxe, 4, 0.65, 0.35, 0.2, 0.2, 0.1, -, - +SandstoneStairs = SandstoneStairs, 0.8, WoodenPickaxe, 4, 0.65, 0.35, 0.2, 0.2, 0.1, -, - +Wool = Wool, 0.8, -, 1.25, -, -, -, -, -, 0.25, - +* MonsterEgg = -, 0.75, -, 1.15, -, -, -, -, -, -, - +Rails = Rail, 0.7, Pickaxe, 1.05, 0.55, 0.3, 0.2, 0.15, 0.1, -, - +Clay = Clay, 0.6, Shovel, 0.9, 0.45, 0.25, 0.15, 0.15, 0.1, -, - +Farmland = Dirt, 0.6, Shovel, 0.9, 0.45, 0.25, 0.15, 0.15, 0.1, -, - +GrassBlock = Dirt, 0.6, Shovel, 0.9, 0.45, 0.25, 0.15, 0.15, 0.1, -, - +* Gravel = Gravel, 0.6, Shovel, 0.9, 0.45, 0.25, 0.15, 0.15, 0.1, -, - +Mycelium = Dirt, 0.6, Shovel, 0.9, 0.45, 0.25, 0.15, 0.15, 0.1, -, - +LilyPad = LilyPad, 0, -, 0.05, -, -, -, -, -, -, - +Sponge = Sponge, 0.6, -, 0.9, -, -, -, -, -, -, - +WetSponge = Sponge, 0.6, -, 0.9, -, -, -, -, -, -, - +BrewingStand = BrewingStand, 0.5, WoodenPickaxe, 2.5, 0.4, 0.2, 0.15, 0.1, 0.1, -, - +* Button = -, 0.5, -, 0.75, -, -, -, -, -, -, - +Cake = -, 0.5, -, 0.75, -, -, -, -, -, -, - +CoarseDirt = Dirt, 0.5, Shovel, 0.75, 0.4, 0.2, 0.15, 0.1, 0.1, -, - +ConcretePowder = ConcretePowder, 0.5, Shovel, 0.75, 0.4, 0.2, 0.15, 0.1, 0.1, -, - +Dirt = Dirt, 0.5, Shovel, 0.75, 0.4, 0.2, 0.15, 0.1, 0.1, -, - +FrostedIce = -, 0.5, -, 2.5, -, -, -, -, -, -, - +HayBale = HayBale, 0.5, -, 0.75, -, -, -, -, -, -, - +Ice = -, 0.5, Pickaxe, 0.75, 0.4, 0.2, 0.15, 0.1, 0.1, -, - +Lever = Lever, 0.5, -, 0.75, -, -, -, -, -, -, - +MagmaBlock = MagmaBlock, 0.5, WoodenPickaxe, 2.5, 0.4, 0.2, 0.15, 0.1, 0.1, -, - +PackedIce = -, 0.5, Pickaxe, 0.75, 0.4, 0.2, 0.15, 0.1, 0.1, -, - +FrostedIce = -, 0.5, Pickaxe, 0.75, 0.4, 0.2, 0.15, 0.1, 0.1, -, - +Piston = Piston, 0.5, -, 0.75, -, -, -, -, -, -, - +Sand = Sand, 0.5, Shovel, 0.75, 0.4, 0.2, 0.15, 0.1, 0.1, -, - +SoulSand = SoulSand, 0.5, Shovel, 0.75, 0.4, 0.2, 0.15, 0.1, 0.1, -, - +StonePressurePlate = StonePressurePlate, 0.5, WoodenPickaxe, 2.5, 0.4, 0.2, 0.15, 0.1, 0.1, -, - +* WeightedPressurePlate = -, 0.5, WoodenPickaxe, 2.5, 0.4, 0.2, 0.15, 0.1, 0.1, -, - +WoodenPressurePlate = WoodenPressurePlate, 0.5, Axe, 0.75, 0.4, 0.2, 0.15, 0.1, 0.1, -, - +Cactus = Cactus, 0.4, -, 0.65, -, -, -, -, -, -, - +Ladder = Ladder, 0.4, Axe, 0.65, 0.35, 0.2, 0.1, 0.1, 0.05, -, - +Netherrack = Netherrack, 0.4, WoodenPickaxe, 2, 0.35, 0.2, 0.1, 0.1, 0.05, -, - +Glass = -, 0.3, -, 0.45, -, -, -, -, -, -, - +GlassPane = -, 0.3, -, 0.45, -, -, -, -, -, -, - +Glowstone = Glowstone, 0.3, -, 0.45, -, -, -, -, -, -, - +RedstoneLamp = RedstoneLampInactive, 0.3, -, 0.45, -, -, -, -, -, -, - +* SeaLantern = Prismarine, 0.3, -, 0.45, -, -, -, -, -, -, - +StainedGlass = -, 0.3, -, 0.45, -, -, -, -, -, -, - +StainedGlassPane = -, 0.3, -, 0.45, -, -, -, -, -, -, - +Bed = Bed, 0.2, -, 0.35, -, -, -, -, -, -, - +Cocoa = Cocoa, 0.2, Axe, 0.35, 0.2, 0.1, 0.05, 0.05, 0.05, -, 0.2 +DaylightSensor = DaylightSensor, 0.2, Axe, 0.35, 0.2, 0.1, 0.05, 0.05, 0.05, -, - +* HugeMushrooms = -, 0.2, Axe, 0.35, 0.2, 0.1, 0.05, 0.05, 0.05, -, - +* Leaves = -, 0.2, -, 0.35, -, -, -, -, -, 0.05, 0.2 +Snow = Snow, 0.2, WoodenShovel, 1, 0.2, 0.1, 0.05, 0.05, 0.05, -, - +Vines = Vines, 0.2, Axe, 0.35, 0.2, 0.1, 0.05, 0.05, 0.05, 0.35, 0.2 +Carpet = Carpet, 0.1, -, 0.2, -, -, -, -, -, -, - +SnowLayer = Snow, 0.1, WoodenShovel, 0.5, 0.1, 0.05, 0.05, 0.05, 0.05, -, - +Air = -, 0, -, Infinity, -, -, -, -, -, -, - +* Carrots = Carrot, 0, -, 0.05, -, -, -, -, -, -, - +* DeadBush = -, 0, -, 0.05, -, -, -, -, -, 0.05, - +Fire = -, 0, -, 0.05, -, -, -, -, -, -, - +FlowerPot = FlowerPot, 0, -, 0.05, -, -, -, -, -, -, - +* Flowers = -, 0, -, 0.05, -, -, -, -, -, -, - +* Grass = -, 0, -, 0.05, -, -, -, -, -, 0.05, - +* MelonStem = -, 0, -, 0.05, -, -, -, -, -, -, - +* Mushrooms = -, 0, -, 0.05, -, -, -, -, -, -, - +* NetherWart = -, 0, -, 0.05, -, -, -, -, -, -, - +* Potatoes = Potato, 0, -, 0.05, -, -, -, -, -, -, - +* PumpkinStem = -, 0, -, 0.05, -, -, -, -, -, -, - +RedstoneComparator = RedstoneComparator, 0, -, 0.05, -, -, -, -, -, -, - +RedstoneRepeater = RedstoneRepeaterInactive, 0, -, 0.05, -, -, -, -, -, -, - +RedstoneTorch = RedstoneTorchInactive, 0, -, 0.05, -, -, -, -, -, -, - +* RedstoneWire = -, 0, -, 0.05, -, -, -, -, -, -, - +Saplings = Sapling, 0, -, 0.05, -, -, -, -, -, -, - +SlimeBlock = SlimeBlock, 0, -, 0.05, -, -, -, -, -, -, - +SugarCane = SugarCane, 0, -, 0.05, -, -, -, -, -, -, - +Tnt = TNT, 0, -, 0.05, -, -, -, -, -, -, - +Torch = Torch, 0, -, 0.05, -, -, -, -, -, -, - +* Tripwire = -, 0, -, 0.05, -, -, -, -, -, -, - +TripwireHook = TripwireHook, 0, -, 0.05, -, -, -, -, -, -, - +* Wheat = -, 0, -, 0.05, -, -, -, -, -, -, - \ No newline at end of file diff --git a/src/MineCase.Core/DropBlockMappingLoader.cs b/src/MineCase.Core/DropBlockMappingLoader.cs index 814ffe67..73d13e12 100644 --- a/src/MineCase.Core/DropBlockMappingLoader.cs +++ b/src/MineCase.Core/DropBlockMappingLoader.cs @@ -47,170 +47,26 @@ private unsafe void ParseLine(string line) var resultSpan = normLine.Slice(0, splitter); var ingredientsSpan = normLine.Slice(splitter + 1); - var recipe = new CraftingRecipe(); - var resultSplitter = resultSpan.IndexOf(','); - if (resultSplitter == -1) - { - ParseItem(resultSpan, ref recipe.Output); - recipe.Output.ItemCount = 1; - } - else - { - ParseItem(resultSpan.Slice(0, resultSplitter), ref recipe.Output); - recipe.Output.ItemCount = byte.Parse(ToString(resultSpan.Slice(resultSplitter + 1))); - } - - var recipeSlots = new List(); - var restSpan = ingredientsSpan; - while (!restSpan.IsEmpty) - { - var ingredientSplitter = restSpan.IndexOf('|'); - var ingredientSpan = ingredientSplitter == -1 ? restSpan : restSpan.Slice(0, ingredientSplitter); - ParseIngredient(ingredientSpan, recipeSlots); - if (ingredientSplitter == -1) - break; - else - restSpan = restSpan.Slice(ingredientSplitter + 1); - } - - recipe.Inputs = recipeSlots.ToArray(); - NormalizeIngredients(recipe); - - // Recipes.Add(recipe); - } - - private void ParseIngredient(Span ingredientSpan, ICollection recipeSlots) - { - var slot = new Slot { ItemCount = 1 }; - var splitter = ingredientSpan.IndexOf(','); - ParseItem(ingredientSpan.Slice(0, splitter), ref slot); - var distributionSpan = ingredientSpan.Slice(splitter + 1); - - do - { - var positionSplitter = distributionSpan.IndexOf(','); - var positionSpan = positionSplitter == -1 ? distributionSpan : distributionSpan.Slice(0, positionSplitter); - - var recipeSlot = new CraftingRecipeSlot { Slot = slot }; - if (positionSpan.Length == 1 && positionSpan[0] == '*') - { - recipeSlot.X = -1; - recipeSlot.Y = -1; - } - else - { - splitter = positionSpan.IndexOf(':'); - - int ParsePoint(ReadOnlySpan span) - { - if (span.Length != 1) - throw new ArgumentOutOfRangeException(nameof(span)); - - switch (span[0]) - { - case '1': - return 0; - case '2': - return 1; - case '3': - return 2; - case '*': - return -1; - default: - throw new ArgumentOutOfRangeException(nameof(span)); - } - } - - recipeSlot.X = ParsePoint(positionSpan.Slice(0, splitter)); - recipeSlot.Y = ParsePoint(positionSpan.Slice(splitter + 1)); - } - - recipeSlots.Add(recipeSlot); - - if (positionSplitter == -1) - break; - else - distributionSpan = distributionSpan.Slice(positionSplitter + 1); - } - while (true); - } - - private void NormalizeIngredients(CraftingRecipe recipe) - { - int minX = 2, minY = 2; - int maxX = 0, maxY = 0; - - for (int i = 0; i < recipe.Inputs.Length; i++) - { - ref var recipeSlot = ref recipe.Inputs[i]; - if (recipeSlot.X >= 0) - { - minX = Math.Min(minX, recipeSlot.X); - maxX = Math.Max(maxX, recipeSlot.X); - } - - if (recipeSlot.Y >= 0) - { - minY = Math.Min(minY, recipeSlot.Y); - maxY = Math.Max(maxY, recipeSlot.Y); - } - } - - // 移动到左上角 - for (int i = 0; i < recipe.Inputs.Length; i++) - { - ref var recipeSlot = ref recipe.Inputs[i]; - if (recipeSlot.X >= 0) - recipeSlot.X -= minX; - - if (recipeSlot.Y >= 0) - recipeSlot.Y -= minY; - } - - recipe.Width = Math.Max(1, maxX - minX + 1); - recipe.Height = Math.Max(1, maxY - minY + 1); - } - - private void ParseItem(Span span, ref Slot slot) - { - var metaSplitter = span.IndexOf('^'); - var idSpan = metaSplitter == -1 ? span : span.Slice(0, metaSplitter); - - bool isBlock; - var text = ToString(idSpan); - if (Enum.TryParse(text, out ItemId item)) - { - slot.BlockId = (short)item; - isBlock = false; - } - else if (Enum.TryParse(text, out BlockId block)) - { - slot.BlockId = (short)block; - isBlock = true; - } - else - { - throw new ArgumentOutOfRangeException($"Invalid item name: {text}."); - } - - if (metaSplitter != -1) - { - var metaText = ToString(span.Slice(metaSplitter + 1)); - if (short.TryParse(metaText, out var value)) - { - slot.ItemDamage = value; - } - else - { - var itemsType = isBlock ? typeof(BlockStates) : typeof(ItemStates); - var paramEnum = itemsType.GetMethod(text).GetParameters()[0].ParameterType; - slot.ItemDamage = Convert.ToInt16(Enum.Parse(paramEnum, metaText)); - } - } - else - { - slot.ItemDamage = 0; - } + var entry = new DropBlockEntry(); + var resultString = resultSpan.ToString(); + string[] splittedItems = resultString.Split(','); + + if (splittedItems.Length != 11) + throw new ArgumentOutOfRangeException(nameof(splittedItems)); + + entry.DroppedBlock = BlockStates.FromString(splittedItems[0]); + entry.Hardness = float.Parse(splittedItems[1]); + entry.Tool = ItemStates.FromString(splittedItems[2]); + entry.Hand = float.Parse(splittedItems[3]); + entry.Wooden = float.Parse(splittedItems[4]); + entry.Stone = float.Parse(splittedItems[5]); + entry.Iron = float.Parse(splittedItems[6]); + entry.Diamand = float.Parse(splittedItems[7]); + entry.Golden = float.Parse(splittedItems[8]); + entry.Shears = float.Parse(splittedItems[9]); + entry.Sword = float.Parse(splittedItems[10]); + + Mapping.Add(BlockStates.FromString(resultSpan.ToString()), entry); } private static unsafe string ToString(ReadOnlySpan span) @@ -244,6 +100,12 @@ public class DropBlockEntry public BlockState DroppedBlock; public float Hardness; public Tools Tool; + public float Hand; + public float Wooden; + public float Stone; + public float Iron; + public float Diamand; + public float Golden; public float Shears; public float Sword; } From 903ef9344d7053372e32f915703e5af270030abe Mon Sep 17 00:00:00 2001 From: JunWang Date: Tue, 23 Jan 2018 11:14:48 +0800 Subject: [PATCH 11/11] Get material and type of tools from ItemsState --- .../DropBlockMappingMatcher.cs | 13 +- src/MineCase.Core/DropBlockMappingLoader.cs | 118 +++++++++++++++++- 2 files changed, 123 insertions(+), 8 deletions(-) diff --git a/src/MineCase.Algorithm/DropBlockMappingMatcher.cs b/src/MineCase.Algorithm/DropBlockMappingMatcher.cs index f76a0a12..bf58fe76 100644 --- a/src/MineCase.Algorithm/DropBlockMappingMatcher.cs +++ b/src/MineCase.Algorithm/DropBlockMappingMatcher.cs @@ -19,12 +19,19 @@ public uint DropBlock(uint item, BlockState block) { DropBlockEntry entry = _mapping[block]; - // TODO drop test - return block.Id; + if (DropBlockMappingLoader.ItemsToTools(entry.Tool) == DropBlockMappingLoader.ItemsToTools(new ItemState { Id = item }) + && DropBlockMappingLoader.ItemsToToolMaterial(new ItemState { Id = item }) >= DropBlockMappingLoader.ItemsToToolMaterial(entry.Tool)) + { + return entry.DroppedBlock.Id; + } + else + { + return uint.MaxValue; + } } else { - return uint.MaxValue; + return block.Id; } } } diff --git a/src/MineCase.Core/DropBlockMappingLoader.cs b/src/MineCase.Core/DropBlockMappingLoader.cs index 73d13e12..df6e6f12 100644 --- a/src/MineCase.Core/DropBlockMappingLoader.cs +++ b/src/MineCase.Core/DropBlockMappingLoader.cs @@ -54,9 +54,11 @@ private unsafe void ParseLine(string line) if (splittedItems.Length != 11) throw new ArgumentOutOfRangeException(nameof(splittedItems)); - entry.DroppedBlock = BlockStates.FromString(splittedItems[0]); + Enum.TryParse(splittedItems[0], out BlockId droppedBlock); + entry.DroppedBlock = new BlockState { Id = (uint)droppedBlock }; entry.Hardness = float.Parse(splittedItems[1]); - entry.Tool = ItemStates.FromString(splittedItems[2]); + Enum.TryParse(splittedItems[2], out ItemId tool); + entry.Tool = new ItemState { Id = (uint)tool }; entry.Hand = float.Parse(splittedItems[3]); entry.Wooden = float.Parse(splittedItems[4]); entry.Stone = float.Parse(splittedItems[5]); @@ -66,7 +68,112 @@ private unsafe void ParseLine(string line) entry.Shears = float.Parse(splittedItems[9]); entry.Sword = float.Parse(splittedItems[10]); - Mapping.Add(BlockStates.FromString(resultSpan.ToString()), entry); + Enum.TryParse(resultSpan.ToString(), out BlockId targetBlock); + Mapping.Add(new BlockState { Id = (uint)targetBlock }, entry); + } + + public static Tools ItemsToTools(ItemState state) + { + if ((int)state.Id == -1) + { + return Tools.Hand; + } + else if (state.Id == (uint)ItemId.WoodenAxe || + state.Id == (uint)ItemId.StoneAxe || + state.Id == (uint)ItemId.IronAxe || + state.Id == (uint)ItemId.GoldenAxe || + state.Id == (uint)ItemId.DiamondAxe) + { + return Tools.Axes; + } + else if (state.Id == (uint)ItemId.WoodenHoe || + state.Id == (uint)ItemId.StoneHoe || + state.Id == (uint)ItemId.IronHoe || + state.Id == (uint)ItemId.GoldenHoe || + state.Id == (uint)ItemId.DiamondHoe) + { + return Tools.Hoes; + } + else if (state.Id == (uint)ItemId.WoodenPickaxe || + state.Id == (uint)ItemId.StonePickaxe || + state.Id == (uint)ItemId.IronPickaxe || + state.Id == (uint)ItemId.GoldenPickaxe || + state.Id == (uint)ItemId.DiamondPickaxe) + { + return Tools.PickAxes; + } + else if (state.Id == (uint)ItemId.WoodenShovel || + state.Id == (uint)ItemId.StoneShovel || + state.Id == (uint)ItemId.IronShovel || + state.Id == (uint)ItemId.GoldenShovel || + state.Id == (uint)ItemId.DiamondShovel) + { + return Tools.Shovels; + } + else if (state.Id == (uint)ItemId.WoodenSword || + state.Id == (uint)ItemId.StoneSword || + state.Id == (uint)ItemId.IronSword || + state.Id == (uint)ItemId.GoldenSword || + state.Id == (uint)ItemId.DiamondSword) + { + return Tools.Swords; + } + else + { + return Tools.Hand; + } + } + + public static ToolMaterial ItemsToToolMaterial(ItemState state) + { + if ((int)state.Id == -1) + { + return ToolMaterial.Hand; + } + else if (state.Id == (uint)ItemId.WoodenAxe || + state.Id == (uint)ItemId.WoodenHoe || + state.Id == (uint)ItemId.WoodenPickaxe || + state.Id == (uint)ItemId.WoodenShovel || + state.Id == (uint)ItemId.WoodenSword) + { + return ToolMaterial.Wooden; + } + else if (state.Id == (uint)ItemId.StoneAxe || + state.Id == (uint)ItemId.StoneHoe || + state.Id == (uint)ItemId.StonePickaxe || + state.Id == (uint)ItemId.StoneShovel || + state.Id == (uint)ItemId.StoneSword) + { + return ToolMaterial.Stone; + } + else if (state.Id == (uint)ItemId.IronAxe || + state.Id == (uint)ItemId.IronHoe || + state.Id == (uint)ItemId.IronPickaxe || + state.Id == (uint)ItemId.IronShovel || + state.Id == (uint)ItemId.IronSword) + { + return ToolMaterial.Iron; + } + else if (state.Id == (uint)ItemId.GoldenAxe || + state.Id == (uint)ItemId.GoldenHoe || + state.Id == (uint)ItemId.GoldenPickaxe || + state.Id == (uint)ItemId.GoldenShovel || + state.Id == (uint)ItemId.GoldenSword) + { + return ToolMaterial.Golden; + } + else if (state.Id == (uint)ItemId.DiamondAxe || + state.Id == (uint)ItemId.DiamondHoe || + state.Id == (uint)ItemId.DiamondPickaxe || + state.Id == (uint)ItemId.DiamondShovel || + state.Id == (uint)ItemId.DiamondSword) + { + return ToolMaterial.Diamand; + } + else + { + return ToolMaterial.Hand; + } } private static unsafe string ToString(ReadOnlySpan span) @@ -81,7 +188,8 @@ public enum Tools Axes, PickAxes, Shovels, - Hoes + Hoes, + Swords } public enum ToolMaterial @@ -99,7 +207,7 @@ public class DropBlockEntry public BlockState TargetBlock; public BlockState DroppedBlock; public float Hardness; - public Tools Tool; + public ItemState Tool; public float Hand; public float Wooden; public float Stone;