feat: Itemdata pooling & benchmarks for storage options
Some checks failed
Build, Package and Push Images / preprocess (push) Successful in 2s
Build, Package and Push Images / build (push) Successful in 27s
Build, Package and Push Images / sonarqube (push) Has been skipped
Build, Package and Push Images / sbom-scan (push) Successful in 33s
Build, Package and Push Images / container-build (push) Failing after 1m10s
Build, Package and Push Images / container-sbom-scan (push) Has been skipped
Some checks failed
Build, Package and Push Images / preprocess (push) Successful in 2s
Build, Package and Push Images / build (push) Successful in 27s
Build, Package and Push Images / sonarqube (push) Has been skipped
Build, Package and Push Images / sbom-scan (push) Successful in 33s
Build, Package and Push Images / container-build (push) Failing after 1m10s
Build, Package and Push Images / container-sbom-scan (push) Has been skipped
This commit is contained in:
parent
cb087f53e9
commit
f5cd6c380e
17 changed files with 755 additions and 3 deletions
|
@ -4,7 +4,7 @@
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>12</LangVersion>
|
||||||
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
|
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<Nullable>warnings</Nullable>
|
<Nullable>warnings</Nullable>
|
||||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
<RootNamespace>Server</RootNamespace>
|
<RootNamespace>Server</RootNamespace>
|
||||||
<LangVersion>default</LangVersion>
|
<LangVersion>12</LangVersion>
|
||||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||||
<TargetFrameworks>net8.0;net7.0</TargetFrameworks>
|
<TargetFrameworks>net8.0;net7.0</TargetFrameworks>
|
||||||
<EnableNETAnalyzers>true</EnableNETAnalyzers>
|
<EnableNETAnalyzers>true</EnableNETAnalyzers>
|
||||||
|
|
51
Server/Services/ItemObjectPoolService.cs
Normal file
51
Server/Services/ItemObjectPoolService.cs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Wonderking.Game.Data;
|
||||||
|
using Wonderking.Game.Reader;
|
||||||
|
|
||||||
|
namespace Server.Services;
|
||||||
|
|
||||||
|
public class ItemObjectPoolService : IHostedService
|
||||||
|
{
|
||||||
|
readonly ConcurrentDictionary<uint, ItemObject> _itemObjectPool = new();
|
||||||
|
private readonly ItemReader _itemReader;
|
||||||
|
|
||||||
|
public ItemObjectPoolService(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
_itemReader = new ItemReader(configuration.GetSection("Game").GetSection("Data").GetValue<string>("Path") ??
|
||||||
|
string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var amountOfEntries = _itemReader.GetAmountOfEntries();
|
||||||
|
ParallelEnumerable.Range(0, (int)amountOfEntries).AsParallel().ForAll(i =>
|
||||||
|
{
|
||||||
|
var itemObject = _itemReader.GetEntry((uint)i);
|
||||||
|
_itemObjectPool.TryAdd(itemObject.ItemID, itemObject);
|
||||||
|
});
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_itemReader.Dispose();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemObject GetItem(ushort itemId)
|
||||||
|
{
|
||||||
|
return _itemObjectPool[itemId];
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsItem(ushort itemId)
|
||||||
|
{
|
||||||
|
return _itemObjectPool.ContainsKey(itemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQueryable<ItemObject> QueryItems()
|
||||||
|
{
|
||||||
|
return _itemObjectPool.AsReadOnly().Values.AsQueryable();
|
||||||
|
}
|
||||||
|
}
|
10
Wonderking/Game/Data/Item/ContainedItem.cs
Normal file
10
Wonderking/Game/Data/Item/ContainedItem.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Wonderking.Game.Data.Item;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct ContainedItem
|
||||||
|
{
|
||||||
|
public short ID { get; internal set; }
|
||||||
|
public float ObtainChance { get; internal set; }
|
||||||
|
}
|
11
Wonderking/Game/Data/Item/CraftMaterial.cs
Normal file
11
Wonderking/Game/Data/Item/CraftMaterial.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Wonderking.Game.Data.Item;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct CraftMaterial
|
||||||
|
{
|
||||||
|
public uint ID;
|
||||||
|
public uint Amount;
|
||||||
|
}
|
||||||
|
|
55
Wonderking/Game/Data/Item/ElementalStats.cs
Normal file
55
Wonderking/Game/Data/Item/ElementalStats.cs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Wonderking.GameData.Item;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 64)]
|
||||||
|
public struct ElementalStats
|
||||||
|
{
|
||||||
|
[FieldOffset(0), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int MinimumFireDamage;
|
||||||
|
|
||||||
|
[FieldOffset(4), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int MinimumWaterDamage;
|
||||||
|
|
||||||
|
[FieldOffset(8), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int MinimumDarkDamage;
|
||||||
|
|
||||||
|
[FieldOffset(12), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int MinimumHolyDamage;
|
||||||
|
|
||||||
|
[FieldOffset(16), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int MaximumFireDamage;
|
||||||
|
|
||||||
|
[FieldOffset(20), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int MaximumWaterDamage;
|
||||||
|
|
||||||
|
[FieldOffset(24), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int MaximumDarkDamage;
|
||||||
|
|
||||||
|
[FieldOffset(28), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int MaximumHolyDamage;
|
||||||
|
|
||||||
|
[FieldOffset(32), MarshalAs(UnmanagedType.U4)]
|
||||||
|
public uint ElementFire;
|
||||||
|
|
||||||
|
[FieldOffset(36), MarshalAs(UnmanagedType.U4)]
|
||||||
|
public uint ElementWater;
|
||||||
|
|
||||||
|
[FieldOffset(40), MarshalAs(UnmanagedType.U4)]
|
||||||
|
public uint ElementDark;
|
||||||
|
|
||||||
|
[FieldOffset(44), MarshalAs(UnmanagedType.U4)]
|
||||||
|
public uint ElementHoly;
|
||||||
|
|
||||||
|
[FieldOffset(48), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int FireResistance;
|
||||||
|
|
||||||
|
[FieldOffset(52), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int WaterResistance;
|
||||||
|
|
||||||
|
[FieldOffset(56), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int DarkResistance;
|
||||||
|
|
||||||
|
[FieldOffset(60), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int HolyResistance;
|
||||||
|
}
|
7
Wonderking/Game/Data/Item/ItemOptions.cs
Normal file
7
Wonderking/Game/Data/Item/ItemOptions.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Wonderking.Game.Data.Item;
|
||||||
|
|
||||||
|
public struct ItemOptions
|
||||||
|
{
|
||||||
|
public ICollection<byte> OptionIDs { get; internal set; }
|
||||||
|
public bool OptionAvailable { get; internal set; }
|
||||||
|
}
|
25
Wonderking/Game/Data/Item/Stats.cs
Normal file
25
Wonderking/Game/Data/Item/Stats.cs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Wonderking.GameData.Item;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 24)]
|
||||||
|
public struct Stats
|
||||||
|
{
|
||||||
|
[FieldOffset(0), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int Strength;
|
||||||
|
|
||||||
|
[FieldOffset(4), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int Dexterity;
|
||||||
|
|
||||||
|
[FieldOffset(8), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int Intelligence;
|
||||||
|
|
||||||
|
[FieldOffset(12), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int Vitality;
|
||||||
|
|
||||||
|
[FieldOffset(16), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int Luck;
|
||||||
|
|
||||||
|
[FieldOffset(20), MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int Wisdom;
|
||||||
|
}
|
171
Wonderking/Game/Data/ItemObject.cs
Normal file
171
Wonderking/Game/Data/ItemObject.cs
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Wonderking.Game.Data.Item;
|
||||||
|
using Wonderking.GameData.Item;
|
||||||
|
using Wonderking.Utils;
|
||||||
|
|
||||||
|
namespace Wonderking.Game.Data;
|
||||||
|
|
||||||
|
[GameDataMetadata(932, "baseitemdata.dat", 197)]
|
||||||
|
public struct ItemObject
|
||||||
|
{
|
||||||
|
public uint ItemID { get; set; }
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
public uint ItemType { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown2 { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown3 { get; set; }
|
||||||
|
|
||||||
|
public uint ClassNo1 { get; set; }
|
||||||
|
public uint ClassNo2 { get; set; }
|
||||||
|
public uint ClassNo3 { get; set; }
|
||||||
|
public uint ClassNo4 { get; set; }
|
||||||
|
public uint SlotNo1 { get; set; }
|
||||||
|
public uint SlotNo2 { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown4 { get; set; }
|
||||||
|
|
||||||
|
public uint IsCash { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown5 { get; set; }
|
||||||
|
|
||||||
|
public uint Price { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown7 { get; set; }
|
||||||
|
|
||||||
|
public uint MaxNumber { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown17 { get; set; }
|
||||||
|
|
||||||
|
public uint MaximumLevelRequirement { get; set; }
|
||||||
|
public uint SexNo { get; set; }
|
||||||
|
public uint WeaponSomething { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown8 { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R2C { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown9 { get; set; }
|
||||||
|
|
||||||
|
public Stats Stats { get; set; }
|
||||||
|
public ElementalStats ElementalStats { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R7C { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R8C { get; set; }
|
||||||
|
|
||||||
|
public float Speed { get; set; }
|
||||||
|
|
||||||
|
public float Jump { get; set; }
|
||||||
|
public int StatDefense { get; set; }
|
||||||
|
public uint MagicID { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown13 { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown14 { get; set; }
|
||||||
|
|
||||||
|
public int AdditionalHealthRecoveryVolume { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R9C_1 { get; set; }
|
||||||
|
|
||||||
|
public int AdditionalManaRecoveryVolume { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R9C_2 { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R10C { get; set; }
|
||||||
|
|
||||||
|
public int AdditionalHealthPoints { get; set; }
|
||||||
|
public int AdditionalManaPoints { get; set; }
|
||||||
|
public bool IsArrow { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown18 { get; set; }
|
||||||
|
|
||||||
|
public int AdditionalEvasionRate { get; set; }
|
||||||
|
public int HitRate { get; set; }
|
||||||
|
|
||||||
|
public int ChanceToHit { get; set; }
|
||||||
|
public int MagicalDamage { get; set; }
|
||||||
|
public int CriticalHitChance { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R12C { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown16 { get; set; }
|
||||||
|
|
||||||
|
public int MinimalAttackDamage { get; set; }
|
||||||
|
public int MaximalAttackDamage { get; set; }
|
||||||
|
public int PhysicalDamage { get; set; }
|
||||||
|
public CraftMaterial[] CraftMaterial { get; set; }
|
||||||
|
public uint CraftResultAmount { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R14C { get; set; }
|
||||||
|
|
||||||
|
public uint CraftResultItem { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R15C { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] R16C { get; set; }
|
||||||
|
|
||||||
|
public int InventoryX { get; set; }
|
||||||
|
public int InventoryY { get; set; }
|
||||||
|
public int InventoryWidth { get; set; }
|
||||||
|
public int InventoryHeight { get; set; }
|
||||||
|
public int SheetID { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown1 { get; set; }
|
||||||
|
|
||||||
|
public bool IsEnchantable { get; set; }
|
||||||
|
|
||||||
|
public uint SetID { get; set; }
|
||||||
|
public uint[] SetItems { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown1_2 { get; set; }
|
||||||
|
|
||||||
|
public ItemOptions Options { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown19 { get; set; }
|
||||||
|
|
||||||
|
public byte PetID { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown20 { get; set; }
|
||||||
|
|
||||||
|
public byte HitBoxScaling { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown20_2 { get; set; }
|
||||||
|
|
||||||
|
public ContainedItem[] ContainedItems { get; set; }
|
||||||
|
|
||||||
|
public bool IsQuestItem { get; set; }
|
||||||
|
public byte MinimumLevelRequirement { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ByteArrayConverter))]
|
||||||
|
public byte[] Unknown21_2 { get; set; }
|
||||||
|
}
|
50
Wonderking/Game/DataReader.cs
Normal file
50
Wonderking/Game/DataReader.cs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Wonderking.Game;
|
||||||
|
|
||||||
|
public abstract class DataReader<T>
|
||||||
|
{
|
||||||
|
protected DataReader(string path)
|
||||||
|
{
|
||||||
|
Path = path;
|
||||||
|
DatFileContent = new(GetDatFileContent(path).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private protected string Path { get; init; }
|
||||||
|
|
||||||
|
public abstract uint GetAmountOfEntries();
|
||||||
|
public abstract T GetEntry(uint entryId);
|
||||||
|
|
||||||
|
protected ushort GetSizeOfEntry()
|
||||||
|
{
|
||||||
|
return typeof(T).GetCustomAttribute<GameDataMetadataAttribute>()?.DataEntrySize ??
|
||||||
|
throw new NotSupportedException("DataEntrySize is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetDatFileName()
|
||||||
|
{
|
||||||
|
return typeof(T).GetCustomAttribute<GameDataMetadataAttribute>()?.DatFileName ??
|
||||||
|
throw new NotSupportedException("DatFileName is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte GetXorKey()
|
||||||
|
{
|
||||||
|
return typeof(T).GetCustomAttribute<GameDataMetadataAttribute>()?.XorKey ??
|
||||||
|
throw new NotSupportedException("XorKey is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected MemoryStream DatFileContent { get; }
|
||||||
|
|
||||||
|
private static Span<byte> GetDatFileContent(string path)
|
||||||
|
{
|
||||||
|
var fileData = File.ReadAllBytes(path + GetDatFileName());
|
||||||
|
var data = new byte[fileData.Length];
|
||||||
|
|
||||||
|
for (var i = 0; i < fileData.Length; i++)
|
||||||
|
{
|
||||||
|
data[i] = (byte)(fileData[i] ^ GetXorKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
12
Wonderking/Game/GameDataMetadataAttribute.cs
Normal file
12
Wonderking/Game/GameDataMetadataAttribute.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace Wonderking.Game;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)]
|
||||||
|
public class GameDataMetadataAttribute(ushort dataEntrySize, string datFileName, byte xorKey) : Attribute
|
||||||
|
{
|
||||||
|
[UsedImplicitly] public byte XorKey { get; init; } = xorKey;
|
||||||
|
[UsedImplicitly] public ushort DataEntrySize { get; init; } = dataEntrySize;
|
||||||
|
|
||||||
|
[UsedImplicitly] internal string DatFileName { get; init; } = datFileName;
|
||||||
|
}
|
75
Wonderking/Game/Reader/BinaryReader.cs
Normal file
75
Wonderking/Game/Reader/BinaryReader.cs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Wonderking.Game.Reader;
|
||||||
|
|
||||||
|
public static class BinaryReader<T> where T : new()
|
||||||
|
{
|
||||||
|
public static readonly Func<BinaryReader, T> Read;
|
||||||
|
|
||||||
|
static BinaryReader()
|
||||||
|
{
|
||||||
|
var type = typeof(T);
|
||||||
|
|
||||||
|
if (type == typeof(bool))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, bool>)(p => p.ReadBoolean());
|
||||||
|
}
|
||||||
|
else if (type == typeof(char))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, char>)(p => p.ReadChar());
|
||||||
|
}
|
||||||
|
else if (type == typeof(string))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, string>)(p => p.ReadString());
|
||||||
|
}
|
||||||
|
else if (type == typeof(sbyte))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, sbyte>)(p => p.ReadSByte());
|
||||||
|
}
|
||||||
|
else if (type == typeof(short))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, short>)(p => p.ReadInt16());
|
||||||
|
}
|
||||||
|
else if (type == typeof(int))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, int>)(p => p.ReadInt32());
|
||||||
|
}
|
||||||
|
else if (type == typeof(long))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, long>)(p => p.ReadInt64());
|
||||||
|
}
|
||||||
|
else if (type == typeof(byte))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, byte>)(p => p.ReadByte());
|
||||||
|
}
|
||||||
|
else if (type == typeof(ushort))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, ushort>)(p => p.ReadUInt16());
|
||||||
|
}
|
||||||
|
else if (type == typeof(uint))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, uint>)(p => p.ReadUInt32());
|
||||||
|
}
|
||||||
|
else if (type == typeof(ulong))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, ulong>)(p => p.ReadUInt64());
|
||||||
|
}
|
||||||
|
else if (type == typeof(float))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, float>)(p => p.ReadSingle());
|
||||||
|
}
|
||||||
|
else if (type == typeof(double))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, double>)(p => p.ReadDouble());
|
||||||
|
}
|
||||||
|
else if (type == typeof(decimal))
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(Delegate)(Func<BinaryReader, decimal>)(p => p.ReadDecimal());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Read = (Func<BinaryReader, T>)(p =>
|
||||||
|
(T)(object)p.ReadBytes(Marshal.SizeOf(new T())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
Wonderking/Game/Reader/GenericReaderExtensions.cs
Normal file
37
Wonderking/Game/Reader/GenericReaderExtensions.cs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
|
||||||
|
/* Nicht gemergte Änderung aus Projekt "Wonderking(net7.0)"
|
||||||
|
Vor:
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
Nach:
|
||||||
|
using System.Text;
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Wonderking.Game.Reader;
|
||||||
|
|
||||||
|
public static class GenericReaderExtensions
|
||||||
|
{
|
||||||
|
public static string ReadString(this BinaryReader reader, int length)
|
||||||
|
{
|
||||||
|
var ret = Encoding.ASCII.GetString(reader.ReadBytes(length)).Replace("\0", "");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T[] ReadArray<T>(this BinaryReader pReader, int pLength) where T : new()
|
||||||
|
{
|
||||||
|
var array = new T[pLength];
|
||||||
|
for (var index = 0; index < pLength; ++index)
|
||||||
|
{
|
||||||
|
array[index] = pReader.Read<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Read<T>(this BinaryReader br) where T : new()
|
||||||
|
{
|
||||||
|
return BinaryReader<T>.Read(br);
|
||||||
|
}
|
||||||
|
}
|
107
Wonderking/Game/Reader/ItemReader.cs
Normal file
107
Wonderking/Game/Reader/ItemReader.cs
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
using Wonderking.Game.Data;
|
||||||
|
using Wonderking.Game.Data.Item;
|
||||||
|
|
||||||
|
namespace Wonderking.Game.Reader;
|
||||||
|
|
||||||
|
public class ItemReader(string path) : DataReader<ItemObject>(path), IDisposable
|
||||||
|
{
|
||||||
|
public override uint GetAmountOfEntries()
|
||||||
|
{
|
||||||
|
return (uint)((this.DatFileContent.Length - 9) / this.GetSizeOfEntry());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ItemObject GetEntry(uint entryId)
|
||||||
|
{
|
||||||
|
var item = new ItemObject();
|
||||||
|
this.DatFileContent.Position = 9 + entryId * this.GetSizeOfEntry();
|
||||||
|
var reader = new BinaryReader(this.DatFileContent);
|
||||||
|
item.ItemID = reader.ReadUInt32(); //9
|
||||||
|
item.Disabled = reader.ReadUInt32() == 1; //13
|
||||||
|
item.ItemType = reader.ReadUInt32(); //17
|
||||||
|
item.Unknown2 = reader.ReadBytes(4); //21
|
||||||
|
item.Unknown3 = reader.ReadBytes(4); //25
|
||||||
|
item.ClassNo1 = reader.ReadUInt32(); //29
|
||||||
|
item.ClassNo2 = reader.ReadUInt32(); //33
|
||||||
|
item.ClassNo3 = reader.ReadUInt32(); //37
|
||||||
|
item.ClassNo4 = reader.ReadUInt32(); //41
|
||||||
|
item.SlotNo1 = reader.ReadUInt32(); //45
|
||||||
|
item.SlotNo2 = reader.ReadUInt32(); //49
|
||||||
|
item.Unknown4 = reader.ReadBytes(4); //53
|
||||||
|
item.IsCash = reader.ReadUInt32(); //57
|
||||||
|
item.Unknown5 = reader.ReadBytes(4); //61
|
||||||
|
item.Price = reader.ReadUInt32(); //65
|
||||||
|
item.Unknown7 = reader.ReadBytes(4); //69
|
||||||
|
item.MaxNumber = reader.ReadUInt32(); //73
|
||||||
|
item.Unknown17 = reader.ReadBytes(12); //77
|
||||||
|
item.MaximumLevelRequirement = reader.ReadUInt32(); //89
|
||||||
|
item.SexNo = reader.ReadUInt32(); //93
|
||||||
|
item.WeaponSomething = reader.ReadUInt32(); //97
|
||||||
|
item.Unknown8 = reader.ReadBytes(4); //101
|
||||||
|
item.R2C = reader.ReadBytes(16); //105
|
||||||
|
item.Unknown9 = reader.ReadBytes(4); //121
|
||||||
|
item.Stats = reader.ReadStats(); //125
|
||||||
|
item.ElementalStats = reader.ReadElementalStats(); //149
|
||||||
|
item.R7C = reader.ReadBytes(4); //213
|
||||||
|
item.R8C = reader.ReadBytes(8); //217
|
||||||
|
item.Speed = reader.ReadSingle(); //225
|
||||||
|
item.Jump = reader.ReadSingle(); //229
|
||||||
|
item.StatDefense = reader.ReadInt32(); //233
|
||||||
|
item.MagicID = reader.ReadUInt32(); //237
|
||||||
|
item.Unknown13 = reader.ReadBytes(4); //241
|
||||||
|
item.Unknown14 = reader.ReadBytes(4); //245
|
||||||
|
item.AdditionalHealthRecoveryVolume = reader.ReadInt32(); //249
|
||||||
|
item.R9C_1 = reader.ReadBytes(4); //253
|
||||||
|
item.AdditionalManaRecoveryVolume = reader.ReadInt32(); //257
|
||||||
|
item.R9C_2 = reader.ReadBytes(4); //261
|
||||||
|
item.R10C = reader.ReadBytes(8); //265
|
||||||
|
item.AdditionalHealthPoints = reader.ReadInt32(); //273
|
||||||
|
item.AdditionalManaPoints = reader.ReadInt32(); //277
|
||||||
|
item.IsArrow = reader.ReadBoolean(); //281
|
||||||
|
item.Unknown18 = reader.ReadBytes(7); //282
|
||||||
|
item.AdditionalEvasionRate = reader.ReadInt32(); //289
|
||||||
|
item.HitRate = reader.ReadInt32(); //293
|
||||||
|
item.ChanceToHit = reader.ReadInt32(); //297
|
||||||
|
item.MagicalDamage = reader.ReadInt32(); //301
|
||||||
|
item.CriticalHitChance = reader.ReadInt32(); //305
|
||||||
|
item.R12C = reader.ReadBytes(4); //309
|
||||||
|
item.Unknown16 = reader.ReadBytes(4); //313
|
||||||
|
item.MinimalAttackDamage = reader.ReadInt32(); //317
|
||||||
|
item.MaximalAttackDamage = reader.ReadInt32(); //321
|
||||||
|
item.PhysicalDamage = reader.ReadInt32(); //325
|
||||||
|
item.CraftMaterial = reader.ReadCraftMaterial(); //329
|
||||||
|
item.CraftResultAmount = reader.ReadUInt32(); //361
|
||||||
|
item.R14C = reader.ReadBytes(4); //365
|
||||||
|
item.CraftResultItem = reader.ReadUInt32(); //369
|
||||||
|
item.R15C = reader.ReadBytes(4); //373
|
||||||
|
item.R16C = reader.ReadBytes(20); //377
|
||||||
|
item.InventoryX = reader.ReadInt32(); //397
|
||||||
|
item.InventoryY = reader.ReadInt32(); //401
|
||||||
|
item.InventoryWidth = reader.ReadInt32(); //405
|
||||||
|
item.InventoryHeight = reader.ReadInt32(); //409
|
||||||
|
item.SheetID = reader.ReadInt32(); //413
|
||||||
|
item.Name = reader.ReadString(20); //417
|
||||||
|
item.Description = reader.ReadString(85); //427
|
||||||
|
item.Unknown1 = reader.ReadBytes(175); //493
|
||||||
|
item.IsEnchantable = reader.ReadUInt32() == 1; //687
|
||||||
|
item.Unknown1_2 = reader.ReadBytes(104); //687
|
||||||
|
item.SetItems = reader.ReadArray<uint>(5);
|
||||||
|
item.SetID = reader.ReadUInt32(); //691
|
||||||
|
item.Options = reader.ReadItemOptions(); //819
|
||||||
|
item.Unknown19 = reader.ReadBytes(23); //835
|
||||||
|
item.PetID = reader.ReadByte(); //858
|
||||||
|
item.Unknown20 = reader.ReadBytes(20); //859
|
||||||
|
item.HitBoxScaling = reader.ReadByte(); //879
|
||||||
|
item.Unknown20_2 = reader.ReadBytes(13); //880
|
||||||
|
item.ContainedItems = reader.ReadContainedItems(); //893
|
||||||
|
item.IsQuestItem = reader.ReadBoolean(); //923
|
||||||
|
item.MinimumLevelRequirement = reader.ReadByte(); //924
|
||||||
|
item.Unknown21_2 = reader.ReadBytes(6); //925
|
||||||
|
reader.Dispose(); //931
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
this.DatFileContent.Dispose();
|
||||||
|
}
|
||||||
|
}
|
96
Wonderking/Game/Reader/ItemReaderExtensions.cs
Normal file
96
Wonderking/Game/Reader/ItemReaderExtensions.cs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
using Wonderking.GameData.Item;
|
||||||
|
|
||||||
|
namespace Wonderking.Game.Data.Item;
|
||||||
|
|
||||||
|
public static class ItemReaderExtensions
|
||||||
|
{
|
||||||
|
public static Stats ReadStats(this BinaryReader reader)
|
||||||
|
{
|
||||||
|
return new Stats()
|
||||||
|
{
|
||||||
|
Strength = reader.ReadInt32(), //125
|
||||||
|
Dexterity = reader.ReadInt32(), //129
|
||||||
|
Intelligence = reader.ReadInt32(), //133
|
||||||
|
Vitality = reader.ReadInt32(), //137
|
||||||
|
Luck = reader.ReadInt32(), //141
|
||||||
|
Wisdom = reader.ReadInt32(), //145
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ElementalStats ReadElementalStats(this BinaryReader reader)
|
||||||
|
{
|
||||||
|
return new ElementalStats()
|
||||||
|
{
|
||||||
|
MinimumFireDamage = reader.ReadInt32(), //149
|
||||||
|
MinimumWaterDamage = reader.ReadInt32(), //153
|
||||||
|
MinimumDarkDamage = reader.ReadInt32(), //157
|
||||||
|
MinimumHolyDamage = reader.ReadInt32(), //161
|
||||||
|
MaximumFireDamage = reader.ReadInt32(), //165
|
||||||
|
MaximumWaterDamage = reader.ReadInt32(), //169
|
||||||
|
MaximumDarkDamage = reader.ReadInt32(), //173
|
||||||
|
MaximumHolyDamage = reader.ReadInt32(), //177
|
||||||
|
ElementFire = reader.ReadUInt32(), //181
|
||||||
|
ElementWater = reader.ReadUInt32(), //185
|
||||||
|
ElementDark = reader.ReadUInt32(), //189
|
||||||
|
ElementHoly = reader.ReadUInt32(), //193
|
||||||
|
FireResistance = reader.ReadInt32(), //197
|
||||||
|
WaterResistance = reader.ReadInt32(), //201
|
||||||
|
DarkResistance = reader.ReadInt32(), //205
|
||||||
|
HolyResistance = reader.ReadInt32(), //209
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ContainedItem[] ReadContainedItems(this BinaryReader reader)
|
||||||
|
{
|
||||||
|
var list = new ContainedItem[5];
|
||||||
|
//893
|
||||||
|
for (var i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
list[i].ID = reader.ReadInt16();
|
||||||
|
}
|
||||||
|
|
||||||
|
//903
|
||||||
|
for (var i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
list[i].ObtainChance = reader.ReadSingle();
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CraftMaterial[] ReadCraftMaterial(this BinaryReader reader)
|
||||||
|
{
|
||||||
|
var mats = new CraftMaterial[4];
|
||||||
|
//329
|
||||||
|
for (var i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
mats[i].ID = reader.ReadUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
//345
|
||||||
|
for (var i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
mats[i].ID = reader.ReadUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
return mats;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemOptions ReadItemOptions(this BinaryReader reader)
|
||||||
|
{
|
||||||
|
var options = new ItemOptions();
|
||||||
|
|
||||||
|
options.OptionAvailable = reader.ReadInt32() == 1; //819
|
||||||
|
|
||||||
|
var optionIDs = new List<byte>(4);
|
||||||
|
//823
|
||||||
|
for (var i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
optionIDs.Add((byte)reader.ReadUInt32());
|
||||||
|
}
|
||||||
|
|
||||||
|
options.OptionIDs = optionIDs;
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}
|
36
Wonderking/Utils/ByteArrayConverter.cs
Normal file
36
Wonderking/Utils/ByteArrayConverter.cs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Wonderking.Utils;
|
||||||
|
|
||||||
|
public class ByteArrayConverter : JsonConverter<byte[]>
|
||||||
|
{
|
||||||
|
public override byte[] Read(
|
||||||
|
ref Utf8JsonReader reader,
|
||||||
|
Type typeToConvert,
|
||||||
|
JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var hexData = reader.GetString();
|
||||||
|
if (hexData != null)
|
||||||
|
{
|
||||||
|
return hexData.Split('-').Select(b => Convert.ToByte(b, 16)).ToArray();
|
||||||
|
}
|
||||||
|
throw new JsonException("Hex string is null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(
|
||||||
|
Utf8JsonWriter writer,
|
||||||
|
byte[]? value,
|
||||||
|
JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
writer.WriteNullValue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var hexData = BitConverter.ToString(value).Replace("-", string.Empty);
|
||||||
|
writer.WriteStringValue(hexData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<TargetFrameworks>net8.0;net7.0</TargetFrameworks>
|
<TargetFrameworks>net8.0;net7.0</TargetFrameworks>
|
||||||
<Features>strict</Features>
|
<Features>strict</Features>
|
||||||
|
<LangVersion>12</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -30,5 +31,13 @@
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\LICENSE">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath>\</PackagePath>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Game\Writer\" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
Loading…
Reference in a new issue