diff --git a/build.bat b/build.bat index 280966a..c2f86a7 100644 --- a/build.bat +++ b/build.bat @@ -7,6 +7,7 @@ mkdir @ExileLootDrop\addons %MSBUILD% src\ExileLootDrop.sln /property:Configuration=release /target:Rebuild /verbosity:normal /nologo copy src\ExileLootDrop\bin\Release\ExileLootDrop.dll @ExileLootDrop copy src\ExileLootDrop\bin\Release\ExileLootDrop.cfg @ExileLootDrop +copy src\ExileLootDrop\bin\Release\ExileLootDrop.ini @ExileLootDrop copy src\ExileLootDropTester\bin\Release\ExileLootDropTester.exe @ExileLootDrop copy LICENSE.txt @ExileLootDrop copy README.md @ExileLootDrop diff --git a/src/ExileLootDrop/ExileLootDrop.csproj b/src/ExileLootDrop/ExileLootDrop.csproj index e06dae1..1286c06 100644 --- a/src/ExileLootDrop/ExileLootDrop.csproj +++ b/src/ExileLootDrop/ExileLootDrop.csproj @@ -47,6 +47,7 @@ + @@ -63,6 +64,9 @@ Always + + Always + diff --git a/src/ExileLootDrop/ExileLootDrop.ini b/src/ExileLootDrop/ExileLootDrop.ini new file mode 100644 index 0000000..7ffbccd --- /dev/null +++ b/src/ExileLootDrop/ExileLootDrop.ini @@ -0,0 +1,3 @@ +[General] +LootCfg=ExileLootDrop.cfg +CacheItems=50000 \ No newline at end of file diff --git a/src/ExileLootDrop/IniParser.cs b/src/ExileLootDrop/IniParser.cs new file mode 100644 index 0000000..b07af91 --- /dev/null +++ b/src/ExileLootDrop/IniParser.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace ExileLootDrop +{ + public class IniParser + { + private readonly string _iniFilePath; + private readonly Hashtable _keyPairs = new Hashtable(); + private readonly List _tmpList = new List(); + + public readonly string Name; + + public IniParser(string iniPath) + { + string str2 = null; + _iniFilePath = iniPath; + Name = Path.GetFileNameWithoutExtension(iniPath); + + if (!File.Exists(iniPath)) + throw new FileNotFoundException("Unable to locate " + iniPath); + + using (TextReader reader = new StreamReader(iniPath)) + { + for (var str = reader.ReadLine(); str != null; str = reader.ReadLine()) + { + str = str.Trim(); + if (str == "") + continue; + + if (str.StartsWith("[") && str.EndsWith("]")) + str2 = str.Substring(1, str.Length - 2); + else + { + SectionPair pair; + + if (str.StartsWith(";")) + str = str.Replace("=", "%eq%") + @"=%comment%"; + + var strArray = str.Split(new[] { '=' }, 2); + string str3 = null; + if (str2 == null) + { + str2 = "ROOT"; + } + pair.Section = str2; + pair.Key = strArray[0]; + if (strArray.Length > 1) + { + str3 = strArray[1]; + } + _keyPairs.Add(pair, str3); + _tmpList.Add(pair); + } + } + } + } + + public void AddSetting(string sectionName, string settingName) + { + AddSetting(sectionName, settingName, null); + } + + public void AddSetting(string sectionName, string settingName, string settingValue) + { + SectionPair pair; + pair.Section = sectionName; + pair.Key = settingName; + if (_keyPairs.ContainsKey(pair)) + { + _keyPairs.Remove(pair); + } + if (_tmpList.Contains(pair)) + { + _tmpList.Remove(pair); + } + _keyPairs.Add(pair, settingValue); + _tmpList.Add(pair); + } + + public int Count() + { + return Sections.Length; + } + + public void DeleteSetting(string sectionName, string settingName) + { + SectionPair pair; + pair.Section = sectionName; + pair.Key = settingName; + if (!_keyPairs.ContainsKey(pair)) return; + _keyPairs.Remove(pair); + _tmpList.Remove(pair); + } + + public string[] EnumSection(string sectionName) + { + return (from pair in _tmpList where !pair.Key.StartsWith(";") where pair.Section == sectionName select pair.Key).ToArray(); + } + + public string[] Sections => (from pair in _tmpList + group pair by pair.Section into bySection + from IGrouping g in bySection + select g.Key).ToArray(); + + public string GetSetting(string sectionName, string settingName, string defaultValue = "") + { + SectionPair pair; + pair.Section = sectionName; + pair.Key = settingName; + return !_keyPairs.ContainsKey(pair) ? defaultValue : ((string)_keyPairs[pair]).Trim(); + } + + public bool GetBoolSetting(string sectionName, string settingName, bool defaultValue = false) + { + if (defaultValue) + return (GetSetting(sectionName, settingName).ToLower() != "false"); + return (GetSetting(sectionName, settingName).ToLower() == "true"); + } + + public void Save() + { + SaveSettings(_iniFilePath); + } + + public void SaveSettings(string newFilePath) + { + var list = new ArrayList(); + var str2 = ""; + foreach (var pair in _tmpList.Where(pair => !list.Contains(pair.Section))) + { + list.Add(pair.Section); + } + foreach (string str3 in list) + { + str2 = str2 + "[" + str3 + "]\r\n"; + foreach (var pair2 in _tmpList) + { + if (pair2.Section == str3) + { + var str = (string)_keyPairs[pair2]; + if (str != null) + { + if (str == "%comment%") + { + str = ""; + } + else + { + str = "=" + str; + } + } + str2 = str2 + pair2.Key.Replace("%eq%", "=") + str + "\r\n"; + } + } + str2 = str2 + "\r\n"; + } + + using (TextWriter writer = new StreamWriter(newFilePath)) + writer.Write(str2); + } + + public void SetSetting(string sectionName, string settingName, string value) + { + SectionPair pair; + pair.Section = sectionName; + pair.Key = settingName; + if (_keyPairs.ContainsKey(pair)) + { + _keyPairs[pair] = value; + } + else + { + AddSetting(sectionName, settingName, value); + } + } + + public bool ContainsSetting(string sectionName, string settingName) + { + SectionPair pair; + pair.Section = sectionName; + pair.Key = settingName; + return _keyPairs.Contains(pair); + } + + [StructLayout(LayoutKind.Sequential)] + private struct SectionPair + { + public string Section; + public string Key; + } + } +} diff --git a/src/ExileLootDrop/Loot.cs b/src/ExileLootDrop/Loot.cs index ccdd87b..35b19f3 100644 --- a/src/ExileLootDrop/Loot.cs +++ b/src/ExileLootDrop/Loot.cs @@ -56,13 +56,22 @@ public static string Invoke(string input) private readonly List _cfgGroups; private Dictionary Table { get; } = new Dictionary(); - + /// /// Loot constructor /// private Loot() { - const string cfgpath = "ExileLootDrop.cfg"; + var assembly = Assembly.GetExecutingAssembly(); + var assemblyName = Path.GetFileNameWithoutExtension(assembly.Location); + var iniPath = Path.Combine(BasePath, $"{assemblyName}.ini"); + if (!File.Exists(iniPath)) + throw new LootException($"{assemblyName}.ini was not found"); + + var ini = new IniParser(iniPath); + var cfgpath = ini.GetSetting("General", "LootCfg", "ExileLootDrop.cfg"); + var cacheCount = Convert.ToInt32(ini.GetSetting("General", "CacheItems", "0")); + Logger.LoggerHandlerManager.AddHandler(new ConsoleLoggerHandler()); var logfile = Path.Combine(BasePath, "output.log"); try @@ -98,11 +107,11 @@ private Loot() foreach (var group in _cfgGroups) { var list = FlattenGroups(group); - var table = new LootTable(group.Name, list); + var table = new LootTable(group.Name, list, cacheCount); Table.Add(group.Name, table); } var span = DateTime.Now - start; - Logger.Log($"Took {span.Milliseconds}ms to load and parse loot cfg"); + Logger.Log($"Took {span.TotalMilliseconds}ms to load and parse loot cfg"); } /// @@ -157,11 +166,11 @@ public string[] GetItems(string table, int count = 1) if (!Table.ContainsKey(table)) throw new LootTableNotFoundException($"No loot table called {table}"); - var items = new List(); + var items = new string[count]; for (var i = 0; i < count; i++) - items.Add(Table[table].Drop()); + items[i] = Table[table].Drop(); - return items.ToArray(); + return items; } /// diff --git a/src/ExileLootDrop/LootTable.cs b/src/ExileLootDrop/LootTable.cs index 3057b07..91e27bd 100644 --- a/src/ExileLootDrop/LootTable.cs +++ b/src/ExileLootDrop/LootTable.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; namespace ExileLootDrop { @@ -16,14 +17,18 @@ public class LootTable public LootItem[] LootItems { get; } private readonly Random _rnd = new Random(); + private readonly int _cacheCount; + private int _cachePtr; + private readonly LootItem[] _cacheItems; /// /// LootTable constuctor /// /// Table name /// Item list - public LootTable(string name, List lootList) + public LootTable(string name, List lootList, int cacheCount = 0) { + _cacheCount = cacheCount; Name = name; var sum = 0m; lootList.ForEach(i => @@ -32,6 +37,19 @@ public LootTable(string name, List lootList) i.Sum = sum; }); LootItems = lootList.ToArray(); + Logger.Log($"Pre caching loot for {Name}"); + var cache = new List(); + for (var i = 0; i < _cacheCount; i++) + { + var rnd = (decimal)_rnd.NextDouble(); + foreach (var item in LootItems.Where(item => item.Sum >= rnd)) + { + cache.Add(item); + break; + } + } + _cachePtr = 0; + _cacheItems = cache.ToArray(); } /// @@ -40,6 +58,11 @@ public LootTable(string name, List lootList) /// Item classname public string Drop() { + if (_cacheCount != 0 && _cachePtr < _cacheCount) + { + return _cacheItems[_cachePtr++].Item; + } + var rnd = (decimal)_rnd.NextDouble(); foreach (var item in LootItems) { diff --git a/src/ExileLootDropTester/Program.cs b/src/ExileLootDropTester/Program.cs index f71988b..2359751 100644 --- a/src/ExileLootDropTester/Program.cs +++ b/src/ExileLootDropTester/Program.cs @@ -7,7 +7,7 @@ namespace ExileLootDropTester { class Program { - const int Loops = 1000000; + const int Loops = 50000; static void Main(string[] args) {