baseline: OpenMU upstream b5a0961 (fresh source)

This commit is contained in:
Acentech Dev
2026-07-14 19:00:35 +03:00
parent 450402e47d
commit 36fc125d5c
11968 changed files with 748705 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
// <copyright file="DropItemGroupExtensions.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.GameLogic.Benchmarks.Helpers;
using Moq;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.DataModel.Configuration.Items;
/// <summary>
/// Extensions for <see cref="DropItemGroup"/>.
/// </summary>
public static class DropItemGroupExtensions
{
/// <summary>
/// Adds the basic drop item groups.
/// </summary>
/// <param name="itemGroups">The item groups.</param>
public static void AddBasicDropItemGroups(this ICollection<DropItemGroup> itemGroups)
{
itemGroups.Add(1, SpecialItemType.RandomItem, true);
itemGroups.Add(1000, SpecialItemType.Excellent, true);
itemGroups.Add(3000, SpecialItemType.Money, true);
}
/// <summary>
/// Adds a new drop item group with the specified data.
/// </summary>
/// <param name="list">The list.</param>
/// <param name="chance">The chance.</param>
/// <param name="itemType">Type of the item.</param>
/// <param name="addItem">if set to <c>true</c>, it adds a test item to the possible item list.</param>
/// <returns>The drop item group which has been added to the list.</returns>
public static DropItemGroup Add(this ICollection<DropItemGroup> list, int chance, SpecialItemType itemType, bool addItem)
{
var dropItemGroup = new Mock<DropItemGroup>();
dropItemGroup.SetupAllProperties();
dropItemGroup.Object.Chance = chance / 10000.0;
dropItemGroup.Object.ItemType = itemType;
var itemList = new List<ItemDefinition>();
dropItemGroup.Setup(g => g.PossibleItems).Returns(itemList);
if (addItem)
{
var itemDefinition = new Mock<ItemDefinition>();
itemDefinition.SetupAllProperties();
itemDefinition.Object.DropsFromMonsters = true;
itemDefinition.Setup(d => d.PossibleItemSetGroups).Returns(new List<ItemSetGroup>());
itemDefinition.Setup(d => d.PossibleItemOptions).Returns(new List<ItemOptionDefinition>());
itemList.Add(itemDefinition.Object);
}
list.Add(dropItemGroup.Object);
return dropItemGroup.Object;
}
}

View File

@@ -0,0 +1,110 @@
// <copyright file="GameConfigurationTestHelper.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.GameLogic.Benchmarks.Helpers;
using Moq;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.GameLogic.Attributes;
/// <summary>
/// Helper for creating test game configurations.
/// </summary>
public static class GameConfigurationTestHelper
{
/// <summary>
/// Creates a mock game configuration for drop generation testing.
/// </summary>
/// <returns>A mock game configuration.</returns>
public static GameConfiguration Create()
{
var gameConfiguration = new Mock<GameConfiguration>();
gameConfiguration.SetupAllProperties();
gameConfiguration.Object.ExcellentItemDropLevelDelta = 50;
gameConfiguration.Object.MaximumItemOptionLevelDrop = 3;
var items = CreateItems();
gameConfiguration.Setup(c => c.Items).Returns(items);
return gameConfiguration.Object;
}
private static IList<ItemDefinition> CreateItems()
{
var items = new List<ItemDefinition>();
var random = new Random(42);
for (byte dropLevel = 0; dropLevel <= 200; dropLevel++)
{
int itemsAtThisLevel = random.Next(5, 20);
for (int i = 0; i < itemsAtThisLevel; i++)
{
var item = new Mock<ItemDefinition>();
item.SetupAllProperties();
item.Setup(d => d.PossibleItemSetGroups).Returns(new List<ItemSetGroup>());
item.Setup(d => d.PossibleItemOptions).Returns(CreateItemOptions(dropLevel));
item.Setup(d => d.Requirements).Returns(new List<AttributeRequirement>());
item.Setup(d => d.BasePowerUpAttributes).Returns(new List<ItemBasePowerUpDefinition>());
item.Setup(d => d.DropItems).Returns(new List<ItemDropItemGroup>());
item.Setup(d => d.QualifiedCharacters).Returns(new List<CharacterClass>());
item.Object.DropsFromMonsters = true;
item.Object.DropLevel = dropLevel;
item.Object.Width = (byte)(i % 4 + 1);
item.Object.Height = (byte)(i % 4 + 1);
item.Object.MaximumItemLevel = 13;
item.Object.MaximumSockets = dropLevel > 100 ? 5 : 0;
item.Object.Group = (byte)(dropLevel % 16);
item.Object.Number = (short)(i % 256);
items.Add(item.Object);
}
}
return items;
}
private static IList<ItemOptionDefinition> CreateItemOptions(byte dropLevel)
{
var options = new List<ItemOptionDefinition>();
if (dropLevel > 30)
{
var excellentOption = new Mock<ItemOptionDefinition>();
excellentOption.SetupAllProperties();
excellentOption.Setup(o => o.PossibleOptions).Returns(CreateExcellentOptions());
excellentOption.Object.AddsRandomly = true;
excellentOption.Object.AddChance = 100;
excellentOption.Object.MaximumOptionsPerItem = 6;
options.Add(excellentOption.Object);
}
if (dropLevel > 50)
{
var luckOption = new Mock<ItemOptionDefinition>();
luckOption.SetupAllProperties();
luckOption.Setup(o => o.PossibleOptions).Returns(new List<IncreasableItemOption>());
luckOption.Object.AddsRandomly = true;
luckOption.Object.AddChance = 100;
luckOption.Object.MaximumOptionsPerItem = 1;
options.Add(luckOption.Object);
}
return options;
}
private static IList<IncreasableItemOption> CreateExcellentOptions()
{
var options = new List<IncreasableItemOption>();
for (int i = 0; i < 6; i++)
{
var option = new Mock<IncreasableItemOption>();
option.SetupAllProperties();
option.Setup(o => o.LevelDependentOptions).Returns(new List<ItemOptionOfLevel>());
options.Add(option.Object);
}
return options;
}
}

View File

@@ -0,0 +1,30 @@
// <copyright file="MockViewPlugInContainer.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.GameLogic.Benchmarks.Helpers;
using Moq;
using MUnique.OpenMU.GameLogic.Views;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// A view plugin container which automatically create mocks for requested view plugins.
/// </summary>
public class MockViewPlugInContainer : ICustomPlugInContainer<IViewPlugIn>
{
private readonly Dictionary<Type, IViewPlugIn> _mocks = new();
/// <inheritdoc />
public T GetPlugIn<T>()
where T : class, IViewPlugIn
{
if (!this._mocks.TryGetValue(typeof(T), out var mock))
{
mock = new Mock<T>().Object;
this._mocks.Add(typeof(T), mock);
}
return (T)mock;
}
}

View File

@@ -0,0 +1,36 @@
// <copyright file="MonsterTestHelper.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.GameLogic.Benchmarks.Helpers;
using Moq;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.GameLogic.Attributes;
/// <summary>
/// Helper for creating test monsters.
/// </summary>
public static class MonsterTestHelper
{
/// <summary>
/// Creates a mock monster definition.
/// </summary>
/// <param name="numberOfDrops">The maximum number of item drops.</param>
/// <param name="level">The monster level.</param>
/// <returns>A mock monster definition.</returns>
public static MonsterDefinition Create(int numberOfDrops, byte level)
{
var monster = new Mock<MonsterDefinition>();
monster.SetupAllProperties();
monster.Setup(m => m.DropItemGroups).Returns(new List<DropItemGroup>());
monster.Setup(m => m.Attributes).Returns(new List<MonsterAttribute>());
monster.Object.NumberOfMaximumItemDrops = numberOfDrops;
monster.Object.Attributes.Add(new MonsterAttribute { AttributeDefinition = Stats.Level, Value = level });
monster.Object.DropItemGroups.AddBasicDropItemGroups();
return monster.Object;
}
}

View File

@@ -0,0 +1,156 @@
// <copyright file="PlayerTestHelper.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.GameLogic.Benchmarks.Helpers;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.GameLogic.Attributes;
using MUnique.OpenMU.GameLogic.Views;
using MUnique.OpenMU.Persistence.InMemory;
using MUnique.OpenMU.PlugIns;
/// <summary>
/// Helper functions to create test players.
/// </summary>
public static class PlayerTestHelper
{
/// <summary>
/// Gets a test player with a new in-memory game context.
/// </summary>
/// <returns>The test player.</returns>
public static async ValueTask<Player> CreatePlayerAsync()
{
var gameConfig = new Mock<GameConfiguration>();
gameConfig.SetupAllProperties();
gameConfig.Setup(c => c.Maps).Returns(new List<GameMapDefinition>());
gameConfig.Setup(c => c.Items).Returns(new List<ItemDefinition>());
gameConfig.Setup(c => c.Skills).Returns(new List<Skill>());
gameConfig.Setup(c => c.PlugInConfigurations).Returns(new List<PlugInConfiguration>());
gameConfig.Setup(c => c.CharacterClasses).Returns(new List<CharacterClass>());
gameConfig.Setup(c => c.GlobalAttributeCombinations).Returns(new List<AttributeRelationship>());
gameConfig.Setup(c => c.GlobalBaseAttributeValues).Returns(new List<ConstValueAttribute>
{
new(1, Stats.MoneyAmountRate),
});
var map = new Mock<GameMapDefinition>();
map.SetupAllProperties();
map.Setup(m => m.DropItemGroups).Returns(new List<DropItemGroup>());
map.Setup(m => m.MonsterSpawns).Returns(new List<MonsterSpawnArea>());
map.Object.TerrainData = new byte[ushort.MaxValue + 3];
gameConfig.Object.RecoveryInterval = int.MaxValue;
gameConfig.Object.Maps.Add(map.Object);
var mapInitializer = new MapInitializer(gameConfig.Object, new NullLogger<MapInitializer>(), NullDropGenerator.Instance, null);
var gameContext = new GameContext(gameConfig.Object, new InMemoryPersistenceContextProvider(), mapInitializer, new NullLoggerFactory(), new PlugInManager(null, new NullLoggerFactory(), null, null), NullDropGenerator.Instance, new ConfigurationChangeMediator());
mapInitializer.PlugInManager = gameContext.PlugInManager;
mapInitializer.PathFinderPool = gameContext.PathFinderPool;
return await CreatePlayerAsync(gameContext).ConfigureAwait(false);
}
/// <summary>
/// Gets a test player with the specified game context.
/// </summary>
/// <param name="gameContext">The game context.</param>
/// <returns>The test player.</returns>
public static async ValueTask<Player> CreatePlayerAsync(IGameContext gameContext)
{
var characterMock = new Mock<Character>();
characterMock.SetupAllProperties();
characterMock.Setup(c => c.LearnedSkills).Returns(new List<SkillEntry>());
characterMock.Setup(c => c.Attributes).Returns(new List<StatAttribute>());
characterMock.Setup(c => c.DropItemGroups).Returns(new List<DropItemGroup>());
var inventoryMock = new Mock<ItemStorage>();
inventoryMock.SetupAllProperties();
inventoryMock.Setup(i => i.Items).Returns(new List<Item>());
var character = characterMock.Object;
character.Inventory = inventoryMock.Object;
character.CurrentMap = gameContext.Configuration.Maps.FirstOrDefault(m => m.Number == 0);
var characterClassMock = new Mock<CharacterClass>();
characterClassMock.Setup(c => c.StatAttributes).Returns(
new List<StatAttributeDefinition>
{
new (Stats.Level, 0, false),
new (Stats.BaseStrength, 28, true),
new (Stats.BaseAgility, 20, true),
new (Stats.BaseVitality, 25, true),
new (Stats.BaseEnergy, 10, true),
new (Stats.CurrentHealth, 0, false),
new (Stats.CurrentMana, 0, false),
new (Stats.CurrentShield, 0, false),
new (Stats.Resets, 0, false),
new (Stats.PointsPerReset, 0, false),
});
characterClassMock.Setup(c => c.AttributeCombinations).Returns(new List<AttributeRelationship>
{
new (Stats.TotalStrength, 1, Stats.BaseStrength),
new (Stats.TotalAgility, 1, Stats.BaseAgility),
new (Stats.TotalVitality, 1, Stats.BaseVitality),
new (Stats.TotalEnergy, 1, Stats.BaseEnergy),
new (Stats.MaximumAbility, 1, Stats.TotalEnergy),
new (Stats.MaximumAbility, 0.3f, Stats.TotalVitality),
new (Stats.MaximumAbility, 0.2f, Stats.TotalAgility),
new (Stats.MaximumAbility, 0.15f, Stats.TotalStrength),
new (Stats.MaximumShield, 1.2f, Stats.TotalEnergy),
new (Stats.MaximumShield, 1.2f, Stats.TotalVitality),
new (Stats.MaximumShield, 1.2f, Stats.TotalAgility),
new (Stats.MaximumShield, 1.2f, Stats.TotalStrength),
new (Stats.MaximumShield, 0.5f, Stats.DefenseBase),
new (Stats.MaximumMana, 1, Stats.TotalEnergy),
new (Stats.MaximumMana, 0.5f, Stats.Level),
new (Stats.MaximumHealth, 2, Stats.Level),
new (Stats.MaximumHealth, 3, Stats.TotalVitality),
});
characterClassMock.Setup(c => c.BaseAttributeValues).Returns(new List<ConstValueAttribute>
{
new (10, Stats.MaximumMana),
new (35, Stats.MaximumHealth),
new (2, Stats.SkillMultiplier),
new (2, Stats.AbilityRecoveryMultiplier),
new (1, Stats.DamageReceiveDecrement),
new (1, Stats.AttackDamageIncrease),
});
character.CharacterClass = characterClassMock.Object;
foreach (var attributeDef in character.CharacterClass.StatAttributes)
{
character.Attributes.Add(new StatAttribute(attributeDef.Attribute!, attributeDef.BaseValue));
}
var accountMock = new Mock<Account>();
accountMock.Setup(mock => mock.Attributes).Returns(new List<StatAttribute>());
var player = new TestPlayer(gameContext) { Account = accountMock.Object };
await player.PlayerState.TryAdvanceToAsync(PlayerState.LoginScreen).ConfigureAwait(false);
await player.PlayerState.TryAdvanceToAsync(PlayerState.Authenticated).ConfigureAwait(false);
await player.PlayerState.TryAdvanceToAsync(PlayerState.CharacterSelection).ConfigureAwait(false);
await player.SetSelectedCharacterAsync(character).ConfigureAwait(false);
player.Attributes!.AddElement(new SimpleElement(200.0f, AggregateType.AddRaw), Stats.TotalLevel);
return player;
}
private class TestPlayer : Player
{
public TestPlayer(IGameContext gameContext)
: base(gameContext)
{
}
protected override ICustomPlugInContainer<IViewPlugIn> CreateViewPlugInContainer()
{
return new MockViewPlugInContainer();
}
}
}

View File

@@ -0,0 +1,28 @@
// <copyright file="RandomizerTestHelper.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.GameLogic.Benchmarks.Helpers;
using Moq;
using MUnique.OpenMU.AttributeSystem;
/// <summary>
/// Helper for creating test randomizers.
/// </summary>
public static class RandomizerTestHelper
{
/// <summary>
/// Creates a mock randomizer with random behavior.
/// </summary>
/// <returns>A mock randomizer.</returns>
public static IRandomizer Create()
{
var randomizer = new Mock<IRandomizer>();
var random = new Random();
randomizer.Setup(r => r.NextInt(It.IsAny<int>(), It.IsAny<int>())).Returns((int min, int max) => random.Next(min, max));
randomizer.Setup(r => r.NextDouble()).Returns(() => random.NextDouble());
randomizer.Setup(r => r.NextRandomBool(It.IsAny<int>())).Returns((int chance) => random.Next(100) < chance);
return randomizer.Object;
}
}