Decompiled source of CustomKill v1.1.51
BepInEx/plugins/CustomKill.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Net; using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.Json; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using CustomKill.Combat; using CustomKill.Config; using CustomKill.Database; using CustomKill.Services; using CustomKill.Utils; using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using LiteDB; using Microsoft.CodeAnalysis; using ProjectM; using ProjectM.Gameplay.Systems; using ProjectM.Network; using Stunlock.Core; using Stunlock.Network; using Unity.Collections; using Unity.Entities; using UnityEngine; using VampireCommandFramework; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("BeardMagics, Morphine, OA: Irozer")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Custom Killfeed for Blood Wars server - forked from Sunrise")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("CustomKill")] [assembly: AssemblyTitle("CustomKill")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace CustomKill { public static class Core { public static World Server { get; } = GetWorld("Server") ?? throw new Exception("Server World not found. Mod installation incorrect or missing resources."); public static EntityManager EntityManager => Server.EntityManager; public static PrefabCollectionSystem PrefabCollection { get; } = Server.GetExistingSystemManaged<PrefabCollectionSystem>(); private static World GetWorld(string name) { Enumerator<World> enumerator = World.s_AllWorlds.GetEnumerator(); while (enumerator.MoveNext()) { World current = enumerator.Current; if (current.Name == name) { return current; } } return null; } public static void LogException(Exception e, [CallerMemberName] string caller = null) { Debug.LogError(Object.op_Implicit($"[CustomKill] Exception in {caller}: {e.Message}\n{e.StackTrace}")); } } public static class KillfeedSettings { public static ConfigEntry<bool> RestrictDamageToAdmin; public static ConfigEntry<bool> RestrictKillsToAdmin; public static ConfigEntry<bool> RestrictDeathsToAdmin; public static ConfigEntry<bool> RestrictAssistsToAdmin; public static ConfigEntry<bool> RestrictMaxStreakToAdmin; public static ConfigEntry<int> LevelTrackingMode; internal static ConfigEntry<int> ClanTrackingDays; public static ConfigEntry<string> WebhookURL; public static ConfigEntry<string> StatsWebhookURL; public static ConfigEntry<string> KillerNameColor; public static ConfigEntry<string> VictimNameColor; public static ConfigEntry<string> ClanTagColor; public static ConfigEntry<string> AllowedLevelColor; public static ConfigEntry<string> ForbiddenLevelColor; public static ConfigEntry<string> KillMessageFormat; public static ConfigEntry<int> MaxLevelGapNormal; public static ConfigEntry<int> MaxLevelGapHigh; public static void Init(ConfigFile config) { LevelTrackingMode = config.Bind<int>("Killfeed", "LevelTrackingMode", 1, "1 = Highest equipped gearscore, 2 = Current equipped gearscore on kill"); } public static void Init(string configPath) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Expected O, but got Unknown ConfigFile val = new ConfigFile(Paths.ConfigPath + "\\CustomKill.cfg", true); ClanTrackingDays = val.Bind<int>("Clan Member Stats", "ClanMemberMinDaysForStats", 7, "Minimum days a clan member must be in a clan to be counted in .top clan damage/kills statistics."); WebhookURL = val.Bind<string>("Discord", "WebhookURL", "", "Discord Webhook URL for broadcasting kill messages."); StatsWebhookURL = val.Bind<string>("Discord", "StatsWebhookURL", "", "Discord Webhook URL for broadcasting player stats."); LevelTrackingMode = val.Bind<int>("Level Service Mode", "LevelTrackingMode", 1, "Level tracking mode: 1 = Track by max logged gearscore , 2 = Track using live gearscore data (Unity ECS Snapshot at time of kill)"); RestrictDamageToAdmin = val.Bind<bool>("Top Command Access", "RestrictDamageToAdmin", false, "Restrict access to the 'damage' leaderboard to admins only."); RestrictKillsToAdmin = val.Bind<bool>("Top Command Access", "RestrictKillsToAdmin", false, "Restrict access to the 'kills' leaderboard to admins only."); RestrictDeathsToAdmin = val.Bind<bool>("Top Command Access", "RestrictDeathsToAdmin", true, "Restrict access to the 'deaths' leaderboard to admins only."); RestrictAssistsToAdmin = val.Bind<bool>("Top Command Access", "RestrictAssistsToAdmin", false, "Restrict access to the 'assists' leaderboard to admins only."); RestrictMaxStreakToAdmin = val.Bind<bool>("Top Command Access", "RestrictMaxStreakToAdmin", false, "Restrict access to the 'maxstreak' leaderboard to admins only."); KillerNameColor = val.Bind<string>("Killfeed Colors", "KillerNameColor", "#ffffff", "Color of the killer's name."); VictimNameColor = val.Bind<string>("Killfeed Colors", "VictimNameColor", "#ffffff", "Color of the victim's name."); ClanTagColor = val.Bind<string>("Killfeed Colors", "ClanTagColor", "#888888", "Color of the clan tags."); AllowedLevelColor = val.Bind<string>("Killfeed Colors", "AllowedLevelColor", "#55ff55", "Color for allowed kills (fair level difference)."); ForbiddenLevelColor = val.Bind<string>("Killfeed Colors", "ForbiddenLevelColor", "#ff5555", "Color for forbidden kills (too high level difference)."); KillMessageFormat = val.Bind<string>("Kill Message Format", "KillMessageFormat", "<color={ClanTagColor}>[{KillerClan}]</color><color={KillerNameColor}>{Killer}</color>[<color={LevelColor}>{KillerLevel}</color>] killed <color={ClanTagColor}>[{VictimClan}]</color><color={VictimNameColor}>{Victim}</color>[<color={LevelColor}>{VictimLevel}</color>]", "Killfeed message format. Available placeholders: {Killer}, {Victim}, {KillerClan}, {VictimClan}, {KillerLevel}, {VictimLevel}, {LevelColor}, {KillerNameColor}, {VictimNameColor}, {ClanTagColor}"); MaxLevelGapNormal = val.Bind<int>("Level Restrictions", "MaxLevelGapNormal", 15, "Maximum level difference allowed for fair kills when killer is below level 91."); MaxLevelGapHigh = val.Bind<int>("Level Restrictions", "MaxLevelGapHigh", 21, "Maximum level difference allowed for fair kills when killer is level 91 or higher."); ManualLogSource logger = Plugin.Logger; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(52, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[CustomKill] Loaded Discord WebhookURL from config: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(WebhookURL.Value); } logger.LogInfo(val2); } } [BepInPlugin("com.beardmagics.customkill", "CustomKill", "1.1.51")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BasePlugin { private Harmony _harmony; public static ManualLogSource Logger; public static Plugin Instance { get; private set; } public override void Load() { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown Instance = this; Logger = ((BasePlugin)this).Log; CommandRegistry.RegisterAll(Assembly.GetExecutingAssembly()); ColorSettings.Init(Paths.ConfigPath); PvPStatsService.Init(); KillfeedSettings.Init(Paths.ConfigPath); Logger.LogInfo((object)"CustomKill config initialized."); Logger.LogInfo((object)"CustomKill v1.1.51 is loading..."); _harmony = new Harmony("com.beardmagics.customkill"); _harmony.PatchAll(); Logger.LogInfo((object)"CustomKill fully loaded."); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "com.beardmagics.customkill"; public const string PLUGIN_NAME = "CustomKill"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace CustomKill.Utils { public static class DiscordBroadcaster { private static readonly HttpClient client = new HttpClient(); public static async void SendKillMessage(string message) { string text = KillfeedSettings.WebhookURL?.Value; if (string.IsNullOrWhiteSpace(text)) { Plugin.Logger.LogWarning((object)"[CustomKill] Webhook URL is null or empty. Skipping Discord message."); } else { await PostToWebhook(text, message, "[CustomKill] Kill Message"); } } public static async void PostTopStatsToDiscord(ChatCommandContext ctx) { Dictionary<string, PlayerStats> stats = PvPStatsService.GetAllStats(); List<PlayerStats> list = (from s in stats.Values where s.Kills.GetValueOrDefault() > 0 orderby s.Kills.GetValueOrDefault() descending select s).Take(10).ToList(); var list2 = (from c in PvPStatsService.GetAllClans().Select(delegate(KeyValuePair<string, List<string>> clan) { PlayerStats value; List<PlayerStats> source = (from name in clan.Value select (!stats.TryGetValue(name, out value)) ? null : value into s where s != null select s).ToList(); return new { ClanName = clan.Key, TotalKills = source.Sum((PlayerStats s) => s.Kills.GetValueOrDefault()), TotalDeaths = source.Sum((PlayerStats s) => s.Deaths.GetValueOrDefault()), TotalAssists = source.Sum((PlayerStats s) => s.Assists.GetValueOrDefault()), TotalDamage = source.Sum((PlayerStats s) => s.Damage.GetValueOrDefault()) }; }) orderby c.TotalKills descending select c).Take(5).ToList(); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("**\ud83d\udcca Top 10 Players**"); for (int i = 0; i < list.Count; i++) { PlayerStats playerStats = list[i]; StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(31, 6, stringBuilder2); handler.AppendFormatted(i + 1); handler.AppendLiteral(". **"); handler.AppendFormatted(playerStats.Name); handler.AppendLiteral("** — \ud83d\udd25 "); handler.AppendFormatted(playerStats.Damage); handler.AppendLiteral(" / \ud83d\udde1\ufe0f "); handler.AppendFormatted(playerStats.Kills); handler.AppendLiteral(" / \ud83d\udc80 "); handler.AppendFormatted(playerStats.Deaths); handler.AppendLiteral(" / \ud83e\udd1d "); handler.AppendFormatted(playerStats.Assists); stringBuilder3.AppendLine(ref handler); } stringBuilder.AppendLine(); stringBuilder.AppendLine("**\ud83c\udff0 Top 5 Clans**"); for (int j = 0; j < list2.Count; j++) { var anon = list2[j]; StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(31, 6, stringBuilder2); handler.AppendFormatted(j + 1); handler.AppendLiteral(". **"); handler.AppendFormatted(anon.ClanName); handler.AppendLiteral("** — \ud83d\udd25 "); handler.AppendFormatted(anon.TotalDamage); handler.AppendLiteral(" / \ud83d\udde1\ufe0f "); handler.AppendFormatted(anon.TotalKills); handler.AppendLiteral(" / \ud83d\udc80 "); handler.AppendFormatted(anon.TotalDeaths); handler.AppendLiteral(" / \ud83e\udd1d "); handler.AppendFormatted(anon.TotalAssists); stringBuilder4.AppendLine(ref handler); } string text = KillfeedSettings.StatsWebhookURL?.Value; if (string.IsNullOrWhiteSpace(text)) { Plugin.Logger.LogWarning((object)"[CustomKill] Stats webhook URL is null or empty. Skipping post."); ctx.Reply("⚠\ufe0f Stats webhook URL not configured."); } else { await PostToWebhook(text, stringBuilder.ToString(), "[CustomKill] Stats Post"); ctx.Reply("[ OK ] Stats have been posted to Discord."); } } private static async Task PostToWebhook(string webhookUrl, string message, string logContext) { StringContent content = new StringContent(JsonSerializer.Serialize(new { content = message }), Encoding.UTF8, "application/json"); bool flag = default(bool); try { HttpResponseMessage httpResponseMessage = await client.PostAsync(webhookUrl, content); ManualLogSource logger = Plugin.Logger; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(19, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(logContext); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" webhook response: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<HttpStatusCode>(httpResponseMessage.StatusCode); } logger.LogInfo(val); if (!httpResponseMessage.IsSuccessStatusCode) { ManualLogSource logger2 = Plugin.Logger; BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(9, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(logContext); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" failed: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<HttpStatusCode>(httpResponseMessage.StatusCode); } logger2.LogWarning(val2); } } catch (HttpRequestException ex) { ManualLogSource logger3 = Plugin.Logger; BepInExErrorLogInterpolatedStringHandler val3 = new BepInExErrorLogInterpolatedStringHandler(16, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(logContext); ((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" webhook error: "); ((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(ex.Message); } logger3.LogError(val3); } } } public static class KillCache { private static readonly Dictionary<Entity, Entity> DownerMap = new Dictionary<Entity, Entity>(); public static void SetDowner(Entity victim, Entity downer) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) DownerMap[victim] = downer; } public static Entity GetDowner(Entity victim) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!DownerMap.TryGetValue(victim, out var value)) { return Entity.Null; } return value; } public static void Clear(Entity victim) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) DownerMap.Remove(victim); } } public static class VWorld { public static World Server { get { Enumerator<World> enumerator = World.All.GetEnumerator(); while (enumerator.MoveNext()) { World current = enumerator.Current; if (current.Name == "Server") { return current; } } return null; } } public static EntityManager ServerEntityManager => Server.EntityManager; } } namespace CustomKill.Systems { public class AssistTrackerSystem : SystemBase { public override void OnCreate() { ((ComponentSystemBase)this).OnCreate(); StatChangeHook.OnStatChanged += HandleStatChanged; } public override void OnDestroy() { StatChangeHook.OnStatChanged -= HandleStatChanged; ((ComponentSystemBase)this).OnDestroy(); } public override void OnUpdate() { } private void HandleStatChanged(StatChangeEvent ev) { } } } namespace CustomKill.Services { public class LevelService { public static LevelService Instance = new LevelService(); public int GetMaxLevel(string playerName) { return (DatabaseWrapper.Instance.Collection.FindById(BsonValue.op_Implicit(playerName))?.MaxGearScore).GetValueOrDefault(); } public void UpdatePlayerLevel(Entity userEntity) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Core.Server.EntityManager; if (!((EntityManager)(ref entityManager)).HasComponent<User>(userEntity)) { return; } User componentData = ((EntityManager)(ref entityManager)).GetComponentData<User>(userEntity); Entity entity = componentData.LocalCharacter._Entity; if (((EntityManager)(ref entityManager)).HasComponent<Equipment>(entity)) { Equipment componentData2 = ((EntityManager)(ref entityManager)).GetComponentData<Equipment>(entity); int num = Mathf.RoundToInt(ModifiableFloat.op_Implicit(componentData2.ArmorLevel) + ModifiableFloat.op_Implicit(componentData2.SpellLevel) + ModifiableFloat.op_Implicit(componentData2.WeaponLevel)); string text = ((object)(FixedString64Bytes)(ref componentData.CharacterName)).ToString(); ILiteCollection<PlayerStats> collection = DatabaseWrapper.Instance.Collection; PlayerStats playerStats = collection.FindById(BsonValue.op_Implicit(text)); if (playerStats == null) { playerStats = new PlayerStats { Name = text, SteamID = componentData.PlatformId, MaxGearScore = num }; collection.Insert(playerStats); Debug.Log(Object.op_Implicit($"[CustomKill] Inserted new PlayerStats for {text} with MaxGearScore: {num}")); } else if (num > playerStats.MaxGearScore.GetValueOrDefault()) { playerStats.MaxGearScore = num; collection.Update(playerStats); Debug.Log(Object.op_Implicit($"[CustomKill] Updated MaxGearScore for {text}: {num}")); } } } } } namespace CustomKill.Config { public static class ColorSettings { public static ConfigEntry<string> Top_TitleColor; public static ConfigEntry<string> Top_DamageColor; public static ConfigEntry<string> Top_KillsColor; public static ConfigEntry<string> Top_DeathsColor; public static ConfigEntry<string> Top_AssistsColor; public static ConfigEntry<string> Top_MaxStreakColor; public static ConfigEntry<string> Top_PlayerNameColor; public static ConfigEntry<string> Top_ClanTitleColor; public static ConfigEntry<string> Top_ClanNameColor; public static ConfigEntry<string> Top_ClanKillsColor; public static ConfigEntry<string> Top_ClanDamageColor; public static ConfigEntry<string> Stats_TitleColor; public static ConfigEntry<string> Stats_DamageColor; public static ConfigEntry<string> Stats_KillsColor; public static ConfigEntry<string> Stats_DeathsColor; public static ConfigEntry<string> Stats_AssistsColor; public static ConfigEntry<string> Stats_KillStreakColor; public static ConfigEntry<string> Stats_MaxStreakColor; public static ConfigEntry<string> Stats_PlayerNameColor; public static void Init(string configPath) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) ConfigFile val = new ConfigFile(Path.Combine(configPath, "CustomKill_top.cfg"), true); Top_TitleColor = val.Bind<string>("Top 5 Colors", "TitleColor", "#ffaa00", "Color for the title in .top"); Top_DamageColor = val.Bind<string>("Top 5 Colors", "DamageColor", "#ffffff", "Color for the damage in .top"); Top_KillsColor = val.Bind<string>("Top 5 Colors", "KillsColor", "#55ff55", "Color for kills shown"); Top_DeathsColor = val.Bind<string>("Top 5 Colors", "DeathsColor", "#ff5555", "Color for deaths shown"); Top_AssistsColor = val.Bind<string>("Top 5 Colors", "AssistsColor", "#aaaaaa", "Color for assists shown"); Top_MaxStreakColor = val.Bind<string>("Top 5 Colors", "MaxStreakColor", "#55aaff", "Color for max streak shown"); Top_PlayerNameColor = val.Bind<string>("Top 5 Colors", "PlayerNameColor", "#ffffff", "Color for player names"); Top_ClanTitleColor = val.Bind<string>("Clan Colors", "ClanTitleColor", "#ffaa00", "Color for the clan title in .top"); Top_ClanNameColor = val.Bind<string>("Clan Colors", "ClanNameColor", "#ffaa00", "Color for the clan name in .top"); Top_ClanKillsColor = val.Bind<string>("Clan Colors", "ClanKillsColor", "#55ff55", "Color for clan kills shown"); Top_ClanDamageColor = val.Bind<string>("Clan Colors", "ClanDamageColor", "#ffffff", "Color for clan damage shown"); ConfigFile val2 = new ConfigFile(Path.Combine(configPath, "CustomKill_stats.cfg"), true); Stats_TitleColor = val2.Bind<string>("Stats Colors", "TitleColor", "#34abeb", "Color for the title in .stats"); Stats_DamageColor = val2.Bind<string>("Stats Colors", "DamageColor", "#EB621F", "Color for the damage in .stats"); Stats_KillsColor = val2.Bind<string>("Stats Colors", "KillsColor", "#69ff55", "Color for kills shown"); Stats_DeathsColor = val2.Bind<string>("Stats Colors", "DeathsColor", "#e34d4d", "Color for deaths shown"); Stats_AssistsColor = val2.Bind<string>("Stats Colors", "AssistsColor", "#63f5ff", "Color for assists shown"); Stats_KillStreakColor = val2.Bind<string>("Stats Colors", "KillStreakColor", "#e84848", "Color for current kill streak"); Stats_MaxStreakColor = val2.Bind<string>("Stats Colors", "MaxStreakColor", "#55aaff", "Color for max streak"); Stats_PlayerNameColor = val2.Bind<string>("Stats Colors", "PlayerNameColor", "#9428fa", "Color for player names"); } } } namespace CustomKill.Patches { [HarmonyPatch(typeof(DeathEventListenerSystem), "OnUpdate")] public static class KillfeedPatch { public static void Postfix(DeathEventListenerSystem __instance) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_045d: Unknown result type (might be due to invalid IL or missing references) //IL_0462: Unknown result type (might be due to invalid IL or missing references) //IL_0464: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Core.Server.EntityManager; EntityQuery val = ((EntityManager)(ref entityManager)).CreateEntityQuery((ComponentType[])(object)new ComponentType[1] { ComponentType.ReadOnly<DeathEvent>() }); NativeArray<Entity> val2 = ((EntityQuery)(ref val)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2)); Enumerator<Entity> enumerator = val2.GetEnumerator(); while (enumerator.MoveNext()) { Entity current = enumerator.Current; if (!((EntityManager)(ref entityManager)).HasComponent<DeathEvent>(current)) { continue; } DeathEvent componentData = ((EntityManager)(ref entityManager)).GetComponentData<DeathEvent>(current); Entity died = componentData.Died; Entity downer = KillCache.GetDowner(died); Entity val3 = ((downer != Entity.Null) ? downer : componentData.Killer); KillCache.Clear(died); if (!((EntityManager)(ref entityManager)).HasComponent<PlayerCharacter>(died) || !((EntityManager)(ref entityManager)).HasComponent<PlayerCharacter>(val3)) { continue; } Entity userEntity = ((EntityManager)(ref entityManager)).GetComponentData<PlayerCharacter>(died).UserEntity; Entity userEntity2 = ((EntityManager)(ref entityManager)).GetComponentData<PlayerCharacter>(val3).UserEntity; if (!((EntityManager)(ref entityManager)).HasComponent<User>(userEntity) || !((EntityManager)(ref entityManager)).HasComponent<User>(userEntity2)) { continue; } User componentData2 = ((EntityManager)(ref entityManager)).GetComponentData<User>(userEntity); User componentData3 = ((EntityManager)(ref entityManager)).GetComponentData<User>(userEntity2); string text = ((object)(FixedString64Bytes)(ref componentData2.CharacterName)).ToString(); string text2 = ((object)(FixedString64Bytes)(ref componentData3.CharacterName)).ToString(); if (string.IsNullOrWhiteSpace(text) || string.IsNullOrWhiteSpace(text2) || text == text2) { continue; } LevelService.Instance.UpdatePlayerLevel(userEntity2); LevelService.Instance.UpdatePlayerLevel(userEntity); PvPStatsService.RegisterPvPStats(text2, componentData3.PlatformId, isKill: true, isDeath: false, isAssist: false); PvPStatsService.RegisterPvPStats(text, componentData2.PlatformId, isKill: false, isDeath: true, isAssist: false); int num; int num2; if (KillfeedSettings.LevelTrackingMode.Value == 1) { num = LevelService.Instance.GetMaxLevel(text2); num2 = LevelService.Instance.GetMaxLevel(text); } else { num = GetLiveGearLevel(entityManager, userEntity2); num2 = GetLiveGearLevel(entityManager, userEntity); } string text3 = TruncateClan(GetClanName(entityManager, componentData3)); string newValue = TruncateClan(GetClanName(entityManager, componentData2)); string newValue2 = (IsKillAllowed(num, num2) ? KillfeedSettings.AllowedLevelColor.Value : KillfeedSettings.ForbiddenLevelColor.Value); string text4 = KillfeedSettings.KillMessageFormat.Value.Replace("{Killer}", text2).Replace("{Victim}", text).Replace("{KillerClan}", text3) .Replace("{VictimClan}", newValue) .Replace("{KillerLevel}", num.ToString()) .Replace("{VictimLevel}", num2.ToString()) .Replace("{LevelColor}", newValue2) .Replace("{KillerNameColor}", KillfeedSettings.KillerNameColor.Value) .Replace("{VictimNameColor}", KillfeedSettings.VictimNameColor.Value) .Replace("{ClanTagColor}", KillfeedSettings.ClanTagColor.Value); ulong platformId = componentData2.PlatformId; ulong killerSteamId = componentData3.PlatformId; List<KeyValuePair<ulong, (string, int)>> list = (from x in PlayerHitStore.GetRecentAttackersWithLvl(platformId) where x.Key != killerSteamId select x).ToList(); string text5 = $"⚔\ufe0f [{text3}] **{text2}** ({num}) killed **{text}** ({num2})"; if (list.Count > 0) { IEnumerable<string> values = list.Select<KeyValuePair<ulong, (string, int)>, string>((KeyValuePair<ulong, (string Name, int Level)> e) => $"*{e.Value.Name}* ({e.Value.Level})"); text5 = text5 + "\n-- Assisters: " + string.Join(", ", values); IEnumerable<string> values2 = list.Select<KeyValuePair<ulong, (string, int)>, string>((KeyValuePair<ulong, (string Name, int Level)> e) => $"<color={ColorSettings.Stats_AssistsColor.Value}>{e.Value.Name}</color> ({e.Value.Level})"); text4 = text4 + "\n<color=#FFFFFF>[Assist(s): " + string.Join(", ", values2) + "]</color>"; foreach (KeyValuePair<ulong, (string, int)> item in list) { PvPStatsService.RegisterPvPStats(item.Value.Item1, item.Key, isKill: false, isDeath: false, isAssist: true); } } FixedString512Bytes val4 = FixedString512Bytes.op_Implicit(text4); ServerChatUtils.SendSystemMessageToAllClients(entityManager, ref val4); DiscordBroadcaster.SendKillMessage(text5); } val2.Dispose(); } private static string GetClanName(EntityManager em, User user) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) Entity entity = user.ClanEntity._Entity; if (!(entity != Entity.Null) || !((EntityManager)(ref em)).HasComponent<ClanTeam>(entity)) { return "-"; } ClanTeam componentData = ((EntityManager)(ref em)).GetComponentData<ClanTeam>(entity); return ((object)(FixedString64Bytes)(ref componentData.Name)).ToString(); } private static string TruncateClan(string name) { if (!string.IsNullOrEmpty(name)) { if (name.Length > 3) { return name.Substring(0, 3).ToUpper(); } return name.ToUpper(); } return "---"; } private static bool IsKillAllowed(int killerLevel, int victimLevel) { int num = killerLevel - victimLevel; if (killerLevel < 91) { return num <= KillfeedSettings.MaxLevelGapNormal.Value; } return num <= KillfeedSettings.MaxLevelGapHigh.Value; } private static int GetLiveGearLevel(EntityManager entityManager, Entity userEntity) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) if (!((EntityManager)(ref entityManager)).HasComponent<User>(userEntity)) { return 0; } Entity entity = ((EntityManager)(ref entityManager)).GetComponentData<User>(userEntity).LocalCharacter._Entity; if (!((EntityManager)(ref entityManager)).HasComponent<Equipment>(entity)) { return 0; } Equipment componentData = ((EntityManager)(ref entityManager)).GetComponentData<Equipment>(entity); return Mathf.RoundToInt(ModifiableFloat.op_Implicit(componentData.ArmorLevel) + ModifiableFloat.op_Implicit(componentData.SpellLevel) + ModifiableFloat.op_Implicit(componentData.WeaponLevel)); } } [HarmonyPatch(typeof(ServerBootstrapSystem), "OnUserConnected")] public static class PlayerConnectPatch { public static void Postfix(ServerBootstrapSystem __instance, NetConnectionId netConnectionId) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) try { EntityManager entityManager = ((ComponentSystemBase)__instance).EntityManager; int num = default(int); if (!__instance._NetEndPointToApprovedUserIndex.TryGetValue(netConnectionId, ref num)) { return; } Il2CppReferenceArray<ServerClient> approvedUsersLookup = __instance._ApprovedUsersLookup; if (num < 0 || num >= ((Il2CppArrayBase<ServerClient>)(object)approvedUsersLookup).Length) { return; } Entity userEntity = ((Il2CppArrayBase<ServerClient>)(object)approvedUsersLookup)[num].UserEntity; if (((EntityManager)(ref entityManager)).HasComponent<User>(userEntity)) { User componentData = ((EntityManager)(ref entityManager)).GetComponentData<User>(userEntity); string text = ((object)(FixedString64Bytes)(ref componentData.CharacterName)).ToString(); if (!string.IsNullOrWhiteSpace(text)) { LevelService.Instance.UpdatePlayerLevel(userEntity); Debug.Log(Object.op_Implicit("[CustomKill] " + text + " connected. Level updated.")); } } } catch (Exception ex) { Debug.LogError(Object.op_Implicit("[CustomKill] Error during OnUserConnected: " + ex.Message + "\n" + ex.StackTrace)); } } } [HarmonyPatch(typeof(Destroy_TravelBuffSystem), "OnUpdate")] public class PlayerCreationPatch { public static void Postfix(Destroy_TravelBuffSystem __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = ((ComponentSystemBase)__instance).EntityManager; EntityQuery _query_615927226_ = __instance.__query_615927226_0; NativeArray<Entity> val = ((EntityQuery)(ref _query_615927226_)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2)); Enumerator<Entity> enumerator = val.GetEnumerator(); while (enumerator.MoveNext()) { Entity current = enumerator.Current; PrefabGUID componentData = ((EntityManager)(ref entityManager)).GetComponentData<PrefabGUID>(current); if (((PrefabGUID)(ref componentData)).GuidHash != 722466953) { continue; } Entity owner = ((EntityManager)(ref entityManager)).GetComponentData<EntityOwner>(current).Owner; if (!((EntityManager)(ref entityManager)).HasComponent<PlayerCharacter>(owner)) { continue; } Entity userEntity = ((EntityManager)(ref entityManager)).GetComponentData<PlayerCharacter>(owner).UserEntity; if (((EntityManager)(ref entityManager)).HasComponent<User>(userEntity)) { User componentData2 = ((EntityManager)(ref entityManager)).GetComponentData<User>(userEntity); string text = ((object)(FixedString64Bytes)(ref componentData2.CharacterName)).ToString(); if (!string.IsNullOrWhiteSpace(text)) { Debug.Log(Object.op_Implicit("[CustomKill] New player detected: " + text + ".")); } } } val.Dispose(); } } [HarmonyPatch(typeof(VampireDownedServerEventSystem), "OnUpdate")] public static class VampireDownedPatch { public static void Prefix(VampireDownedServerEventSystem __instance) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Core.Server.EntityManager; EntityQuery _query_1174204813_ = __instance.__query_1174204813_0; NativeArray<Entity> val = ((EntityQuery)(ref _query_1174204813_)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2)); Enumerator<Entity> enumerator = val.GetEnumerator(); Entity val2 = default(Entity); Entity val3 = default(Entity); while (enumerator.MoveNext()) { Entity current = enumerator.Current; if (VampireDownedServerEventSystem.TryFindRootOwner(current, 1, entityManager, ref val2) && VampireDownedServerEventSystem.TryFindRootOwner(((EntityManager)(ref entityManager)).GetComponentData<VampireDownedBuff>(current).Source, 1, entityManager, ref val3) && ((EntityManager)(ref entityManager)).HasComponent<PlayerCharacter>(val2) && ((EntityManager)(ref entityManager)).HasComponent<PlayerCharacter>(val3)) { KillCache.SetDowner(val2, val3); User val4 = val3.Read<PlayerCharacter>().UserEntity.Read<User>(); User val5 = val2.Read<PlayerCharacter>().UserEntity.Read<User>(); PlayerHitStore.AddHit(val4.PlatformId, ((object)(FixedString64Bytes)(ref val4.CharacterName)).ToString(), LevelService.Instance.GetMaxLevel(((object)(FixedString64Bytes)(ref val4.CharacterName)).ToString()), val5.PlatformId, ((object)(FixedString64Bytes)(ref val5.CharacterName)).ToString(), LevelService.Instance.GetMaxLevel(((object)(FixedString64Bytes)(ref val5.CharacterName)).ToString()), -1, 1); } } val.Dispose(); } } } namespace CustomKill.Database { public class ClanMemberRecord { public string Id { get; set; } public ulong SteamID { get; set; } public string ClanName { get; set; } public DateTime JoinedAt { get; set; } } public sealed class DatabaseWrapper : IDisposable { private static DatabaseWrapper _instance; private readonly LiteDatabase _database; public static DatabaseWrapper Instance => _instance ?? (_instance = new DatabaseWrapper()); public ILiteCollection<PlayerStats> Collection { get; } public ILiteCollection<ClanMemberRecord> ClanMembersCollection => _database.GetCollection<ClanMemberRecord>("clan_members", (BsonAutoId)10); private DatabaseWrapper() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown string text = Path.Combine(Paths.ConfigPath, "CustomKillDB"); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); } string text2 = Path.Combine(text, "PlayerStats.db"); ManualLogSource logger = Plugin.Logger; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(28, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[CustomKill] Database path: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text2); } logger.LogInfo(val); _database = new LiteDatabase("Filename=" + text2 + "; Connection=shared", (BsonMapper)null); Collection = _database.GetCollection<PlayerStats>("player_stats", (BsonAutoId)10); Collection.EnsureIndex<string>((Expression<Func<PlayerStats, string>>)((PlayerStats x) => x.Name), false); Collection.EnsureIndex<ulong>((Expression<Func<PlayerStats, ulong>>)((PlayerStats x) => x.SteamID), false); ClanMembersCollection.EnsureIndex<string>((Expression<Func<ClanMemberRecord, string>>)((ClanMemberRecord x) => x.Id), false); ClanMembersCollection.EnsureIndex<DateTime>((Expression<Func<ClanMemberRecord, DateTime>>)((ClanMemberRecord x) => x.JoinedAt), false); } public void Dispose() { LiteDatabase database = _database; if (database != null) { database.Dispose(); } } } public class PlayerStats { [BsonId] public string Name { get; set; } public ulong SteamID { get; set; } public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public int? Kills { get; set; } = 0; public int? Damage { get; set; } = 0; public int? DamageTaken { get; set; } = 0; public int? Deaths { get; set; } = 0; public int? Assists { get; set; } = 0; public int? MaxStreak { get; set; } = 0; public int? KillStreak { get; set; } = 0; public int? MaxGearScore { get; set; } = 0; } public static class PvPStatsService { public static void RegisterPvPStats(string name, ulong steamID, bool isKill, bool isDeath, bool isAssist) { //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Expected O, but got Unknown //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown bool flag = default(bool); try { ILiteCollection<PlayerStats> collection = DatabaseWrapper.Instance.Collection; PlayerStats playerStats = collection.FindById(BsonValue.op_Implicit(name)); if (playerStats == null || (!playerStats.Kills.HasValue && !playerStats.Deaths.HasValue && !playerStats.Assists.HasValue && !playerStats.MaxStreak.HasValue && !playerStats.KillStreak.HasValue)) { playerStats = new PlayerStats { Name = name, SteamID = steamID, Kills = 0, Deaths = 0, Assists = 0, MaxStreak = 0, KillStreak = 0 }; collection.Insert(playerStats); } ManualLogSource logger = Plugin.Logger; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(54, 4, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Registering PvP stats for "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" - Kill: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(isKill); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", Death: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(isDeath); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", Assist: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(isAssist); } logger.LogInfo(val); if (isKill) { playerStats.Kills = playerStats.Kills.GetValueOrDefault() + 1; playerStats.KillStreak = playerStats.KillStreak.GetValueOrDefault() + 1; if (playerStats.KillStreak > playerStats.MaxStreak) { playerStats.MaxStreak = playerStats.KillStreak; } } if (isDeath) { playerStats.Deaths = playerStats.Deaths.GetValueOrDefault() + 1; playerStats.KillStreak = 0; } if (isAssist) { playerStats.Assists = playerStats.Assists.GetValueOrDefault() + 1; } collection.Update(playerStats); } catch (Exception ex) { ManualLogSource logger2 = Plugin.Logger; BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(43, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[PvPStatsService] Error while registering: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message); } logger2.LogError(val2); } } public static void RegisterDamage(string name, ulong steamID, int amount) { ILiteCollection<PlayerStats> collection = DatabaseWrapper.Instance.Collection; PlayerStats playerStats = collection.FindById(BsonValue.op_Implicit(name)) ?? new PlayerStats { Name = name, SteamID = steamID }; playerStats.Damage = playerStats.Damage.GetValueOrDefault() + amount; collection.Upsert(playerStats); } public static Dictionary<string, List<string>> GetAllClans() { DatabaseWrapper instance = DatabaseWrapper.Instance; List<ClanMemberRecord> source = instance.ClanMembersCollection.FindAll().ToList(); Dictionary<ulong, string> playerStats = instance.Collection.FindAll().ToDictionary((PlayerStats p) => p.SteamID, (PlayerStats p) => p.Name); string value; return (from m in source group m by m.ClanName).ToDictionary((IGrouping<string, ClanMemberRecord> g) => g.Key, (IGrouping<string, ClanMemberRecord> g) => g.Select((ClanMemberRecord m) => (!playerStats.TryGetValue(m.SteamID, out value)) ? $"Unknown({m.SteamID})" : value).ToList()); } public static void RegisterDamageTaken(string name, ulong steamID, int amount) { ILiteCollection<PlayerStats> collection = DatabaseWrapper.Instance.Collection; PlayerStats playerStats = collection.FindById(BsonValue.op_Implicit(name)) ?? new PlayerStats { Name = name, SteamID = steamID }; playerStats.DamageTaken = playerStats.DamageTaken.GetValueOrDefault() + amount; collection.Upsert(playerStats); } public static Dictionary<string, PlayerStats> GetAllStats() { return DatabaseWrapper.Instance.Collection.FindAll().ToDictionary((PlayerStats ps) => ps.Name, (PlayerStats ps) => ps); } public static PlayerStats GetStats(string name) { return DatabaseWrapper.Instance.Collection.FindById(BsonValue.op_Implicit(name)) ?? new PlayerStats { Name = name }; } public static void Init() { _ = DatabaseWrapper.Instance; Plugin.Logger.LogInfo((object)"[PvPStatsService] Database initialized."); } } } namespace CustomKill.Commands { public static class ExportStatsCommand { [Command("exportstats", null, null, "Exports all player stats and clan members to a CSV file.", null, true)] public static void ExportStats(ChatCommandContext ctx) { //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Expected O, but got Unknown //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Expected O, but got Unknown bool flag = default(bool); try { Dictionary<string, PlayerStats> allStats = PvPStatsService.GetAllStats(); Dictionary<string, List<string>> allClans = PvPStatsService.GetAllClans(); StringBuilder stringBuilder = new StringBuilder(); Dictionary<string, string> dictionary = new Dictionary<string, string>(); foreach (KeyValuePair<string, List<string>> item in allClans) { foreach (string item2 in item.Value) { if (!dictionary.ContainsKey(item2)) { dictionary[item2] = item.Key; } } } stringBuilder.AppendLine("PlayerName,ClanName,ClanTag,Damage,Kills,Deaths,Assists"); foreach (KeyValuePair<string, PlayerStats> item3 in allStats.OrderBy((KeyValuePair<string, PlayerStats> p) => p.Value.Name)) { PlayerStats value = item3.Value; string value2; string text = (dictionary.TryGetValue(value.Name, out value2) ? value2 : ""); string value3 = ((!string.IsNullOrWhiteSpace(text)) ? TruncateClan(text) : "---"); StringBuilder stringBuilder2 = stringBuilder; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(6, 7, stringBuilder2); handler.AppendFormatted(value.Name); handler.AppendLiteral(","); handler.AppendFormatted(text); handler.AppendLiteral(","); handler.AppendFormatted(value3); handler.AppendLiteral(","); handler.AppendFormatted(value.Damage.GetValueOrDefault()); handler.AppendLiteral(","); handler.AppendFormatted(value.Kills.GetValueOrDefault()); handler.AppendLiteral(","); handler.AppendFormatted(value.Deaths.GetValueOrDefault()); handler.AppendLiteral(","); handler.AppendFormatted(value.Assists.GetValueOrDefault()); stringBuilder2.AppendLine(ref handler); } string text2 = Path.Combine(Paths.PluginPath, "CustomKill", "Exports"); Directory.CreateDirectory(text2); string text3 = $"PvPStatsExport_{DateTime.Now:yyyyMMdd_HHmmss}.csv"; string text4 = Path.Combine(text2, text3); File.WriteAllText(text4, stringBuilder.ToString()); ctx.Reply("Export completed. File saved to: " + text3); ManualLogSource logger = Plugin.Logger; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(39, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[ExportStatsCommand] Exported stats to "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text4); } logger.LogInfo(val); } catch (Exception ex) { ctx.Reply("Export failed: " + ex.Message); ManualLogSource logger2 = Plugin.Logger; BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(28, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[ExportStatsCommand] Error: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex); } logger2.LogError(val2); } } private static string TruncateClan(string name) { if (string.IsNullOrEmpty(name)) { return "---"; } if (name.Length > 3) { return name.Substring(0, 3).ToUpper(); } return name.ToUpper(); } } public static class KDCommand { [Command("kd", null, null, "Shows your Kill/Death ratio.", null, false)] public static void ShowKD(ChatCommandContext ctx) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) User user = ctx.Event.User; PlayerStats stats = PvPStatsService.GetStats(((object)(FixedString64Bytes)(ref user.CharacterName)).ToString()); int valueOrDefault = stats.Kills.GetValueOrDefault(); int valueOrDefault2 = stats.Deaths.GetValueOrDefault(); if (valueOrDefault == 0 && valueOrDefault2 == 0) { ctx.Reply("<color=#aaaaaa>You have no kills or deaths recorded yet.</color>"); return; } string text = ((valueOrDefault2 == 0) ? ((float)valueOrDefault) : ((float)valueOrDefault / (float)valueOrDefault2)).ToString("0.00"); string text2 = "<color=#e34d4d>Your K/D Ratio:</color> <color=#FFD700>" + text + "</color>"; ctx.Reply(text2); } [Command("kda", null, null, "Shows your Kill/Death/Assist ratio.", null, false)] public static void ShowKDA(ChatCommandContext ctx) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) User user = ctx.Event.User; PlayerStats stats = PvPStatsService.GetStats(((object)(FixedString64Bytes)(ref user.CharacterName)).ToString()); int valueOrDefault = stats.Kills.GetValueOrDefault(); int valueOrDefault2 = stats.Deaths.GetValueOrDefault(); int valueOrDefault3 = stats.Assists.GetValueOrDefault(); if (valueOrDefault == 0 && valueOrDefault2 == 0 && valueOrDefault3 == 0) { ctx.Reply("<color=#aaaaaa>You have no K/D/A stats recorded yet.</color>"); return; } float num = (float)valueOrDefault + (float)valueOrDefault3 * 0.5f; string text = ((valueOrDefault2 == 0) ? num : (num / (float)valueOrDefault2)).ToString("0.00"); string text2 = "<color=#e34d4d>Your K/D/A Ratio:</color> <color=#00FFFF>" + text + "</color>"; ctx.Reply(text2); } } internal class PlayerInfo { public static class PlayerInfoCommand { [Command("playerinfo", "pi", null, "Displays information about a player.", null, false)] public static void HandleCommand(ChatCommandContext ctx, string arg) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_033a: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) ctx.Reply("Usage: .pi <playernamehere> - Display Clan name, members, and level of player."); LevelService instance = LevelService.Instance; World serverWorld = GetServerWorld(); if (serverWorld == null) { ctx.Reply("Server world is not available."); return; } EntityManager entityManager = serverWorld.EntityManager; EntityQuery val = ((EntityManager)(ref entityManager)).CreateEntityQuery((ComponentType[])(object)new ComponentType[1] { ComponentType.ReadOnly<User>() }); NativeArray<Entity> val2 = ((EntityQuery)(ref val)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2)); val = ((EntityManager)(ref entityManager)).CreateEntityQuery((ComponentType[])(object)new ComponentType[1] { ComponentType.ReadOnly<PlayerCharacter>() }); NativeArray<Entity> val3 = ((EntityQuery)(ref val)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2)); Entity val4 = Entity.Null; User val5 = default(User); bool flag = false; Enumerator<Entity> enumerator = val2.GetEnumerator(); while (enumerator.MoveNext()) { Entity current = enumerator.Current; User componentData = ((EntityManager)(ref entityManager)).GetComponentData<User>(current); bool num = ((object)(FixedString64Bytes)(ref componentData.CharacterName)).ToString().ToLower().Contains(arg.ToLower()); bool flag2 = componentData.PlatformId.ToString() == arg; if (!(num || flag2)) { continue; } val4 = current; val5 = componentData; Enumerator<Entity> enumerator2 = val3.GetEnumerator(); while (enumerator2.MoveNext()) { Entity current2 = enumerator2.Current; if (((EntityManager)(ref entityManager)).GetComponentData<PlayerCharacter>(current2).UserEntity == current) { flag = true; break; } } instance.UpdatePlayerLevel(current); break; } if (val4 == Entity.Null) { ctx.Reply("No player found."); val2.Dispose(); val3.Dispose(); return; } string value = "No clan"; Entity entity = val5.ClanEntity._Entity; List<string> list = new List<string>(); if (entity != Entity.Null && ((EntityManager)(ref entityManager)).HasComponent<ClanTeam>(entity)) { ClanTeam componentData2 = ((EntityManager)(ref entityManager)).GetComponentData<ClanTeam>(entity); value = ((object)(FixedString64Bytes)(ref componentData2.Name)).ToString(); if (((EntityManager)(ref entityManager)).HasComponent<SyncToUserBuffer>(entity)) { Enumerator<SyncToUserBuffer> enumerator3 = ((EntityManager)(ref entityManager)).GetBuffer<SyncToUserBuffer>(entity, false).GetEnumerator(); while (enumerator3.MoveNext()) { SyncToUserBuffer current3 = enumerator3.Current; if (((EntityManager)(ref entityManager)).Exists(current3.UserEntity) && ((EntityManager)(ref entityManager)).HasComponent<User>(current3.UserEntity)) { User componentData3 = ((EntityManager)(ref entityManager)).GetComponentData<User>(current3.UserEntity); instance.UpdatePlayerLevel(current3.UserEntity); string value2 = (componentData3.IsConnected ? "#00FF00" : "#FF0000"); int maxLevel = instance.GetMaxLevel(((object)(FixedString64Bytes)(ref componentData3.CharacterName)).ToString()); list.Add($"<color={value2}>{componentData3.CharacterName} [{maxLevel}]</color>"); } } } } int maxLevel2 = instance.GetMaxLevel(((object)(FixedString64Bytes)(ref val5.CharacterName)).ToString()); string value3 = (flag ? "#00FF00" : "#FF0000"); string value4 = $"<color={value3}>{val5.CharacterName} [{maxLevel2}]</color>"; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(); stringBuilder.AppendLine("--- <color=#FFD700>Player Info</color> ---"); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(29, 1, stringBuilder2); handler.AppendLiteral("<color=#FFFF00>Name:</color> "); handler.AppendFormatted(value4); stringBuilder3.AppendLine(ref handler); stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(52, 1, stringBuilder2); handler.AppendLiteral("<color=#FFFF00>Clan:</color> <color=#FFFFFF>"); handler.AppendFormatted(value); handler.AppendLiteral("</color>"); stringBuilder4.AppendLine(ref handler); if (list.Count > 0) { string value5 = string.Join(" - ", list); stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(37, 1, stringBuilder2); handler.AppendLiteral("<color=#FFFF00>Clan Members:</color> "); handler.AppendFormatted(value5); stringBuilder5.AppendLine(ref handler); } ctx.Reply(stringBuilder.ToString()); val2.Dispose(); val3.Dispose(); } private static World? GetServerWorld() { Enumerator<World> enumerator = World.All.GetEnumerator(); while (enumerator.MoveNext()) { World current = enumerator.Current; if (current.Name == "Server") { return current; } } return null; } } } public static class ResetStatsCommands { private static readonly HashSet<ulong> PendingConfirmations = new HashSet<ulong>(); [Command("resetstats", "rs", null, "Resets all PvP stats and clan memberships (admin only)", null, true)] public static void ResetStats(ChatCommandContext ctx) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) ulong platformId = ctx.User.PlatformId; if (!PendingConfirmations.Contains(platformId)) { PendingConfirmations.Add(platformId); ctx.Reply("⚠ Are you sure you want to reset ALL PvP stats and clan membership records? This cannot be undone.\nPlease confirm by typing `.y` to proceed or `.n` to cancel."); } else { ctx.Reply("⚠ A reset confirmation is already pending. Please type `.y` to confirm or `.n` to cancel."); } } [Command("y", null, null, "Confirm pending reset (admin only)", null, true)] public static void ConfirmReset(ChatCommandContext ctx) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) ulong platformId = ctx.User.PlatformId; if (PendingConfirmations.Contains(platformId)) { int value = DatabaseWrapper.Instance.Collection.DeleteAll(); int value2 = DatabaseWrapper.Instance.ClanMembersCollection.DeleteAll(); ctx.Reply($"[ ! ] Reset complete. Deleted {value} player stat entries and {value2} clan membership entries."); PendingConfirmations.Remove(platformId); } } [Command("n", null, null, "Cancel pending reset (admin only)", null, true)] public static void CancelReset(ChatCommandContext ctx) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) ulong platformId = ctx.User.PlatformId; if (PendingConfirmations.Contains(platformId)) { ctx.Reply("[ X ] Reset cancelled. No data was changed."); PendingConfirmations.Remove(platformId); } } } public static class StatsCommand { [Command("stats", null, "playerName", "Shows your PvP stats or another player's.", null, false)] public static void HandleCommand(ChatCommandContext ctx, string playerName = null) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) User user = ctx.Event.User; string text = ((object)(FixedString64Bytes)(ref user.CharacterName)).ToString(); bool flag = string.IsNullOrWhiteSpace(playerName) || playerName == text; string text2 = (flag ? text : playerName); PlayerStats stats = PvPStatsService.GetStats(text2); if (stats.Kills.GetValueOrDefault() == 0 && stats.Deaths.GetValueOrDefault() == 0 && stats.Assists.GetValueOrDefault() == 0 && stats.KillStreak.GetValueOrDefault() == 0 && stats.MaxStreak.GetValueOrDefault() == 0 && stats.Damage.GetValueOrDefault() == 0) { ctx.Reply(flag ? "<color=#aaaaaa>You have no PvP stats to display.</color>" : $"<color=#aaaaaa>No stats found for player <color={ColorSettings.Stats_PlayerNameColor.Value}>{text2}</color>.</color>"); return; } bool isAdmin = ctx.Event.User.IsAdmin; string value = ((flag || isAdmin || !KillfeedSettings.RestrictDamageToAdmin.Value) ? $"{stats.Damage}" : "<color=#eb34db>Denied</color>"); string value2 = ((flag || isAdmin || !KillfeedSettings.RestrictKillsToAdmin.Value) ? $"{stats.Kills}" : "<color=#eb34db>Denied</color>"); string value3 = ((flag || isAdmin || !KillfeedSettings.RestrictDeathsToAdmin.Value) ? $"{stats.Deaths}" : "<color=#eb34db>Denied</color>"); string value4 = ((flag || isAdmin || !KillfeedSettings.RestrictAssistsToAdmin.Value) ? $"{stats.Assists}" : "<color=#eb34db>Denied</color>"); string value5 = ((flag || isAdmin || !KillfeedSettings.RestrictMaxStreakToAdmin.Value) ? $"{stats.MaxStreak}" : "<color=#eb34db>Denied</color>"); string value6 = $"{stats.KillStreak.GetValueOrDefault()}"; string text3 = $"<color={ColorSettings.Stats_TitleColor.Value}>Displaying stats for</color> <color={ColorSettings.Stats_PlayerNameColor.Value}>{text2}</color>\nDamage: <color={ColorSettings.Stats_DamageColor.Value}>{value}</color> | Kills: <color={ColorSettings.Stats_KillsColor.Value}>{value2}</color> | Deaths: <color={ColorSettings.Stats_DeathsColor.Value}>{value3}</color> | Assists: <color={ColorSettings.Stats_AssistsColor.Value}>{value4}</color>\nKill Streak: <color={ColorSettings.Stats_KillStreakColor.Value}>{value6}</color> | Max Streak: <color={ColorSettings.Stats_MaxStreakColor.Value}>{value5}</color>"; ctx.Reply(text3); } [Command("ptd", null, null, "Post top player and clan stats to Discord", null, true)] public static void PostStatsToDiscord(ChatCommandContext ctx) { DiscordBroadcaster.PostTopStatsToDiscord(ctx); } } internal class Top { public static class TopCommand { [Command("top", null, "[category] [subcategory]", "Displays leaderboards for players or clans.", null, false)] public static void HandleCommand(ChatCommandContext ctx, string category = null, string metric = null) { //IL_06e5: Unknown result type (might be due to invalid IL or missing references) category = (category ?? string.Empty).ToLowerInvariant(); metric = (metric ?? string.Empty).ToLowerInvariant(); StringBuilder stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler; if (category == "clan") { if (metric != "kills" && metric != "damage") { ctx.Reply($"<color={ColorSettings.Top_ClanTitleColor.Value}>Usage:</color> <color={ColorSettings.Top_ClanTitleColor.Value}>.top clan [category]</color>\n <color={ColorSettings.Top_ClanTitleColor.Value}>Categories:</color>\n <color={ColorSettings.Top_ClanKillsColor.Value}>kills</color>\n <color={ColorSettings.Top_ClanDamageColor.Value}>damage</color>\n"); return; } int value = KillfeedSettings.ClanTrackingDays.Value; DateTime cutoff = DateTime.UtcNow.AddDays(-value); List<ClanMemberRecord> source = DatabaseWrapper.Instance.ClanMembersCollection.Find((Expression<Func<ClanMemberRecord, bool>>)((ClanMemberRecord x) => x.JoinedAt <= cutoff), 0, int.MaxValue).ToList(); if (!source.Any()) { ctx.Reply($"<color={ColorSettings.Top_ClanTitleColor.Value}>No clans with members older than {value} day{((value == 1) ? "" : "s")}.</color>"); return; } var source2 = (from m in source group m by m.ClanName into g select new { Clan = g.Key, Kills = g.Sum((ClanMemberRecord m) => PvPStatsService.GetStats(m.SteamID.ToString()).Kills.GetValueOrDefault()), Damage = g.Sum((ClanMemberRecord m) => PvPStatsService.GetStats(m.SteamID.ToString()).Damage.GetValueOrDefault()) }).ToList(); var list = ((metric == "damage") ? source2.OrderByDescending(c => c.Damage) : source2.OrderByDescending(c => c.Kills)).Take(3).ToList(); if (!list.Any()) { ctx.Reply("<color=#aaaaaa>No clan stats to display.</color>"); return; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine((metric == "damage") ? $"<color={ColorSettings.Top_ClanTitleColor.Value}>Top Clans by</color> <color={ColorSettings.Top_ClanDamageColor.Value}Damage \ud83c\udfc6</color>" : $"<color={ColorSettings.Top_ClanTitleColor.Value}>Top Clans by</color> <color={ColorSettings.Top_ClanKillsColor.Value}Kills \ud83c\udfc6</color>"); for (int i = 0; i < list.Count; i++) { var anon = list[i]; int value2 = ((metric == "damage") ? anon.Damage : anon.Kills); string value3 = ((metric == "damage") ? ColorSettings.Top_ClanDamageColor.Value : ColorSettings.Top_ClanKillsColor.Value); stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(37, 5, stringBuilder2); handler.AppendFormatted(i + 1); handler.AppendLiteral(". <color="); handler.AppendFormatted(ColorSettings.Top_ClanNameColor.Value); handler.AppendLiteral(">"); handler.AppendFormatted(anon.Clan); handler.AppendLiteral("</color> — "); handler.AppendLiteral("<color="); handler.AppendFormatted(value3); handler.AppendLiteral(">"); handler.AppendFormatted(value2); handler.AppendLiteral("</color>"); stringBuilder3.AppendLine(ref handler); } ctx.Reply(stringBuilder.ToString()); return; } Dictionary<string, PlayerStats> allStats = PvPStatsService.GetAllStats(); if (allStats.Count == 0) { ctx.Reply("<color=#aaaaaa>No player stats found.</color>"); return; } string text = "<color=#ff5555>Missing or invalid category.</color> Usage: <color=#ffff00>.top [category]</color>\nCategories:\n <color=#ffaa00>clan</color>)\n <color=#ffaa00>damage</color>" + (KillfeedSettings.RestrictDamageToAdmin.Value ? " (Admin Only)" : "") + "\n <color=#ffaa00>kills</color>" + (KillfeedSettings.RestrictKillsToAdmin.Value ? " (Admin Only)" : "") + "\n <color=#ffaa00>deaths</color>" + (KillfeedSettings.RestrictDeathsToAdmin.Value ? " (Admin Only)" : "") + "\n <color=#ffaa00>assists</color>" + (KillfeedSettings.RestrictAssistsToAdmin.Value ? " (Admin Only)" : "") + "\n <color=#ffaa00>maxstreak</color>" + (KillfeedSettings.RestrictMaxStreakToAdmin.Value ? " (Admin Only)" : ""); if (string.IsNullOrWhiteSpace(category)) { ctx.Reply(text); return; } if (category == "ms") { category = "maxstreak"; } if (!new string[6] { "clan", "damage", "kills", "deaths", "assists", "maxstreak" }.Contains(category)) { ctx.Reply(text); return; } if (((category == "damage" && KillfeedSettings.RestrictDamageToAdmin.Value) || (category == "kills" && KillfeedSettings.RestrictKillsToAdmin.Value) || (category == "deaths" && KillfeedSettings.RestrictDeathsToAdmin.Value) || (category == "assists" && KillfeedSettings.RestrictAssistsToAdmin.Value) || (category == "maxstreak" && KillfeedSettings.RestrictMaxStreakToAdmin.Value)) && !ctx.Event.User.IsAdmin) { ctx.Reply("<color=#ff5555>Access to the '" + category + "' leaderboard is restricted to admins only.</color>"); return; } List<KeyValuePair<string, PlayerStats>> list2 = (category switch { "damage" => (from p in allStats where p.Value.Damage.GetValueOrDefault() > 0 orderby p.Value.Damage descending select p).Take(5), "kills" => (from p in allStats where p.Value.Kills.GetValueOrDefault() > 0 orderby p.Value.Kills descending select p).Take(5), "deaths" => (from p in allStats where p.Value.Deaths.GetValueOrDefault() > 0 orderby p.Value.Deaths descending select p).Take(5), "assists" => (from p in allStats where p.Value.Assists.GetValueOrDefault() > 0 orderby p.Value.Assists descending select p).Take(5), "maxstreak" => (from p in allStats where p.Value.MaxStreak.GetValueOrDefault() > 0 orderby p.Value.MaxStreak descending select p).Take(5), _ => Enumerable.Empty<KeyValuePair<string, PlayerStats>>(), }).ToList(); if (!list2.Any()) { ctx.Reply("<color=#aaaaaa>No " + category + " to display.</color>"); return; } StringBuilder stringBuilder4 = new StringBuilder(); stringBuilder2 = stringBuilder4; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(34, 2, stringBuilder2); handler.AppendLiteral("<color="); handler.AppendFormatted(ColorSettings.Top_TitleColor.Value); handler.AppendLiteral(">Top 5 players by "); handler.AppendFormatted(category); handler.AppendLiteral(":</color>"); stringBuilder5.AppendLine(ref handler); int num = 1; foreach (KeyValuePair<string, PlayerStats> item in list2) { string value4 = category switch { "damage" => ColorSettings.Top_DamageColor.Value, "kills" => ColorSettings.Top_KillsColor.Value, "deaths" => ColorSettings.Top_DeathsColor.Value, "assists" => ColorSettings.Top_AssistsColor.Value, "maxstreak" => ColorSettings.Top_MaxStreakColor.Value, _ => "#ffffff", }; int value5 = category switch { "damage" => item.Value.Damage.GetValueOrDefault(), "kills" => item.Value.Kills.GetValueOrDefault(), "deaths" => item.Value.Deaths.GetValueOrDefault(), "assists" => item.Value.Assists.GetValueOrDefault(), "maxstreak" => item.Value.MaxStreak.GetValueOrDefault(), _ => 0, }; stringBuilder2 = stringBuilder4; StringBuilder stringBuilder6 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(37, 5, stringBuilder2); handler.AppendFormatted(num); handler.AppendLiteral(". <color="); handler.AppendFormatted(ColorSettings.Top_PlayerNameColor.Value); handler.AppendLiteral(">"); handler.AppendFormatted(item.Key); handler.AppendLiteral("</color> - <color="); handler.AppendFormatted(value4); handler.AppendLiteral(">"); handler.AppendFormatted(value5); handler.AppendLiteral("</color>"); stringBuilder6.AppendLine(ref handler); num++; } ctx.Reply(stringBuilder4.ToString()); } } } } namespace CustomKill.Combat { public static class ECSExtensions { public unsafe static T Read<T>(this Entity entity) where T : struct { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) ComponentType val = default(ComponentType); ((ComponentType)(ref val))..ctor(Il2CppType.Of<T>(), (AccessMode)0); EntityManager entityManager = Helpers.Server.EntityManager; return Marshal.PtrToStructure<T>(new IntPtr(((EntityManager)(ref entityManager)).GetComponentDataRawRO(entity, val.TypeIndex))); } public static bool Has<T>(this Entity entity) where T : struct { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) ComponentType val = default(ComponentType); ((ComponentType)(ref val))..ctor(Il2CppType.Of<T>(), (AccessMode)0); EntityManager entityManager = Helpers.Server.EntityManager; return ((EntityManager)(ref entityManager)).HasComponent(entity, val); } } public static class Helpers { public static World Server { get; } = GetWorld("Server"); private static World GetWorld(string name) { Enumerator<World> enumerator = World.s_AllWorlds.GetEnumerator(); while (enumerator.MoveNext()) { World current = enumerator.Current; if (current.Name == name) { return current; } } return null; } } public struct HitInteraction { public ulong AttackerSteamId; public ulong VictimSteamId; public string AttackerName; public int AttackerLevel; public string VictimName; public int VictimLevel; public long Timestamp; public int DmgSourceGUID; public int DmgAmount; } public class PlayerHitData { public List<HitInteraction> Attacks { get; } = new List<HitInteraction>(); public List<HitInteraction> Defenses { get; } = new List<HitInteraction>(); } public static class PlayerHitStore { private const double PVP_WINDOW_SECONDS = 30.0; private static readonly Dictionary<ulong, PlayerHitData> interactionsByPlayer = new Dictionary<ulong, PlayerHitData>(); public static IReadOnlyDictionary<ulong, PlayerHitData> InteractionsByPlayer => interactionsByPlayer; public static void AddHit(ulong attackerSteamId, string attackerName, int attackerLevel, ulong victimSteamId, string victimName, int victimLevel, int dmgSourceGUID, int damageAmount) { HitInteraction hitInteraction = default(HitInteraction); hitInteraction.AttackerSteamId = attackerSteamId; hitInteraction.AttackerName = attackerName; hitInteraction.AttackerLevel = attackerLevel; hitInteraction.VictimSteamId = victimSteamId; hitInteraction.VictimName = victimName; hitInteraction.VictimLevel = victimLevel; hitInteraction.Timestamp = DateTime.UtcNow.Ticks; hitInteraction.DmgSourceGUID = dmgSourceGUID; hitInteraction.DmgAmount = damageAmount; HitInteraction hit = hitInteraction; AddAttack(attackerSteamId, hit); AddDefense(victimSteamId, hit); if (interactionsByPlayer.TryGetValue(victimSteamId, out var value) && value.Defenses.Count >= 500) { CleanupOldHitInteractionsByPlayer(victimSteamId); } } private static void AddAttack(ulong playerSteamId, HitInteraction hit) { if (!interactionsByPlayer.TryGetValue(playerSteamId, out var value)) { value = new PlayerHitData(); interactionsByPlayer[playerSteamId] = value; } value.Attacks.Add(hit); } private static void AddDefense(ulong playerSteamId, HitInteraction hit) { if (!interactionsByPlayer.TryGetValue(playerSteamId, out var value)) { value = new PlayerHitData(); interactionsByPlayer[playerSteamId] = value; } value.Defenses.Add(hit); } public static IReadOnlyList<HitInteraction> GetAttacks(ulong playerSteamId) { if (interactionsByPlayer.TryGetValue(playerSteamId, out var value)) { return value.Attacks; } return Array.Empty<HitInteraction>(); } public static IReadOnlyList<HitInteraction> GetDefenses(ulong playerSteamId) { if (interactionsByPlayer.TryGetValue(playerSteamId, out var value)) { return value.Defenses; } return Array.Empty<HitInteraction>(); } public static Dictionary<ulong, (string Name, int Level)> GetRecentAttackersWithLvl(ulong playerSteamId, double pvpWindowSeconds = 30.0) { Dictionary<ulong, (string, int)> dictionary = new Dictionary<ulong, (string, int)>(); if (!interactionsByPlayer.TryGetValue(playerSteamId, out var value)) { return dictionary; } long ticks = DateTime.UtcNow.Ticks; long ticks2 = TimeSpan.FromSeconds(pvpWindowSeconds).Ticks; foreach (HitInteraction defense in value.Defenses) { if (ticks - defense.Timestamp > ticks2 || playerSteamId == defense.AttackerSteamId) { continue; } if (dictionary.TryGetValue(defense.AttackerSteamId, out var value2)) { if (defense.AttackerLevel > value2.Item2) { dictionary[defense.AttackerSteamId] = (defense.AttackerName, defense.AttackerLevel); } } else { dictionary[defense.AttackerSteamId] = (defense.AttackerName, defense.AttackerLevel); } } return dictionary; } public static int GetHighestLvlUsedOnKiller(ulong victimSteamId, ulong killerSteamId, double pvpWindowSeconds = 30.0) { int num = -1; if (!interactionsByPlayer.TryGetValue(victimSteamId, out var value)) { return num; } long ticks = DateTime.UtcNow.Ticks; long ticks2 = TimeSpan.FromSeconds(pvpWindowSeconds).Ticks; foreach (HitInteraction attack in value.Attacks) { if (ticks - attack.Timestamp <= ticks2 && killerSteamId == attack.VictimSteamId) { num = Math.Max(num, attack.AttackerLevel); } } return num; } public static IReadOnlyList<HitInteraction> GetRecentInteractions(ulong playerSteamId, double pvpWindowSeconds = 30.0) { if (!interactionsByPlayer.TryGetValue(playerSteamId, out var value)) { return Array.Empty<HitInteraction>(); } long ticks = DateTime.UtcNow.Ticks; long ticks2 = TimeSpan.FromSeconds(pvpWindowSeconds).Ticks; long earliest = ticks - ticks2; return (from hit in value.Attacks.Concat(value.Defenses) where hit.Timestamp >= earliest orderby hit.Timestamp select hit).ToList(); } public static void CleanupOldHitInteractionsByPlayer(ulong playerSteamId, double pvpWindowSeconds = 30.0) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown if (interactionsByPlayer.TryGetValue(playerSteamId, out var value)) { long nowTicks = DateTime.UtcNow.Ticks; long windowTicks = TimeSpan.FromSeconds(pvpWindowSeconds).Ticks; int count = value.Defenses.Count; value.Defenses.RemoveAll((HitInteraction hit) => nowTicks - hit.Timestamp > windowTicks); int count2 = value.Defenses.Count; ManualLogSource logger = Plugin.Logger; bool flag = default(bool); BepInExMessageLogInterpolatedStringHandler val = new BepInExMessageLogInterpolatedStringHandler(46, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("CLEANED up "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(count - count2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" old hit interactions for SteamID: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<ulong>(playerSteamId); } logger.LogMessage(val); } } public static void ResetPlayerHitInteractions(ulong playerSteamId) { if (interactionsByPlayer.TryGetValue(playerSteamId, out var value)) { value.Attacks.Clear(); value.Defenses.Clear(); } } public static void Clear() { interactionsByPlayer.Clear(); } } public static class StatChangeHook { [HarmonyPatch(typeof(StatChangeSystem), "OnUpdate")] public static class Patch { public static void Prefix(StatChangeSystem __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might b
BepInEx/plugins/LiteDB.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Buffers; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using LiteDB.Engine; using LiteDB.Utils; using LiteDB.Utils.Extensions; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("LiteDB.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010029e66990e22110ce40a7197e37f8f82df3332c399e696df7f27d09e14ee590ac2dda735d4777fe554c427540bde93b14d3d26c04731c963383dcaa18859c8cbcd4a1a9c394d1204f474c2ab6f23a2eaadf81eb8a7a3d3cc73658868b0302163b92a2614ca050ab703be33c3e1d76f55b11f4f87cb73558f3aa69c1ce726d9ee8")] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Maurício David")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("MIT")] [assembly: AssemblyDescription("LiteDB - A lightweight embedded .NET NoSQL document store in a single datafile")] [assembly: AssemblyFileVersion("5.0.21")] [assembly: AssemblyInformationalVersion("5.0.21+391cc9318c5be6e56cb71b3ce14b1ed9cb324763")] [assembly: AssemblyProduct("LiteDB")] [assembly: AssemblyTitle("LiteDB")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/mbdavid/LiteDB")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("5.0.21.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LiteDB { public sealed class LiteCollection<T> : ILiteCollection<T> { private readonly string _collection; private readonly ILiteEngine _engine; private readonly List<BsonExpression> _includes; private readonly BsonMapper _mapper; private readonly EntityMapper _entity; private readonly MemberMapper _id; private readonly BsonAutoId _autoId; public string Name => _collection; public BsonAutoId AutoId => _autoId; public EntityMapper EntityMapper => _entity; public int Count() { return Query().Count(); } public int Count(BsonExpression predicate) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return Query().Where(predicate).Count(); } public int Count(string predicate, BsonDocument parameters) { return Count(BsonExpression.Create(predicate, parameters)); } public int Count(string predicate, params BsonValue[] args) { return Count(BsonExpression.Create(predicate, args)); } public int Count(Expression<Func<T, bool>> predicate) { return Count(_mapper.GetExpression(predicate)); } public int Count(Query query) { return new LiteQueryable<T>(_engine, _mapper, _collection, query).Count(); } public long LongCount() { return Query().LongCount(); } public long LongCount(BsonExpression predicate) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return Query().Where(predicate).LongCount(); } public long LongCount(string predicate, BsonDocument parameters) { return LongCount(BsonExpression.Create(predicate, parameters)); } public long LongCount(string predicate, params BsonValue[] args) { return LongCount(BsonExpression.Create(predicate, args)); } public long LongCount(Expression<Func<T, bool>> predicate) { return LongCount(_mapper.GetExpression(predicate)); } public long LongCount(Query query) { return new LiteQueryable<T>(_engine, _mapper, _collection, query).Count(); } public bool Exists(BsonExpression predicate) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return Query().Where(predicate).Exists(); } public bool Exists(string predicate, BsonDocument parameters) { return Exists(BsonExpression.Create(predicate, parameters)); } public bool Exists(string predicate, params BsonValue[] args) { return Exists(BsonExpression.Create(predicate, args)); } public bool Exists(Expression<Func<T, bool>> predicate) { return Exists(_mapper.GetExpression(predicate)); } public bool Exists(Query query) { return new LiteQueryable<T>(_engine, _mapper, _collection, query).Exists(); } public BsonValue Min(BsonExpression keySelector) { if (string.IsNullOrEmpty(keySelector)) { throw new ArgumentNullException("keySelector"); } BsonDocument bsonDocument = Query().OrderBy(keySelector).Select(keySelector).ToDocuments() .First(); return bsonDocument[bsonDocument.Keys.First()]; } public BsonValue Min() { return Min("_id"); } public K Min<K>(Expression<Func<T, K>> keySelector) { if (keySelector == null) { throw new ArgumentNullException("keySelector"); } BsonExpression expression = _mapper.GetExpression(keySelector); BsonValue value = Min(expression); return (K)_mapper.Deserialize(typeof(K), value); } public BsonValue Max(BsonExpression keySelector) { if (string.IsNullOrEmpty(keySelector)) { throw new ArgumentNullException("keySelector"); } BsonDocument bsonDocument = Query().OrderByDescending(keySelector).Select(keySelector).ToDocuments() .First(); return bsonDocument[bsonDocument.Keys.First()]; } public BsonValue Max() { return Max("_id"); } public K Max<K>(Expression<Func<T, K>> keySelector) { if (keySelector == null) { throw new ArgumentNullException("keySelector"); } BsonExpression expression = _mapper.GetExpression(keySelector); BsonValue value = Max(expression); return (K)_mapper.Deserialize(typeof(K), value); } public bool Delete(BsonValue id) { if (id == null || id.IsNull) { throw new ArgumentNullException("id"); } return _engine.Delete(_collection, new BsonValue[1] { id }) == 1; } public int DeleteAll() { return _engine.DeleteMany(_collection, null); } public int DeleteMany(BsonExpression predicate) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return _engine.DeleteMany(_collection, predicate); } public int DeleteMany(string predicate, BsonDocument parameters) { return DeleteMany(BsonExpression.Create(predicate, parameters)); } public int DeleteMany(string predicate, params BsonValue[] args) { return DeleteMany(BsonExpression.Create(predicate, args)); } public int DeleteMany(Expression<Func<T, bool>> predicate) { return DeleteMany(_mapper.GetExpression(predicate)); } public ILiteQueryable<T> Query() { return new LiteQueryable<T>(_engine, _mapper, _collection, new Query()).Include(_includes); } public IEnumerable<T> Find(BsonExpression predicate, int skip = 0, int limit = int.MaxValue) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return Query().Include(_includes).Where(predicate).Skip(skip) .Limit(limit) .ToEnumerable(); } public IEnumerable<T> Find(Query query, int skip = 0, int limit = int.MaxValue) { if (query == null) { throw new ArgumentNullException("query"); } if (skip != 0) { query.Offset = skip; } if (limit != int.MaxValue) { query.Limit = limit; } return new LiteQueryable<T>(_engine, _mapper, _collection, query).ToEnumerable(); } public IEnumerable<T> Find(Expression<Func<T, bool>> predicate, int skip = 0, int limit = int.MaxValue) { return Find(_mapper.GetExpression(predicate), skip, limit); } public T FindById(BsonValue id) { if (id == null || id.IsNull) { throw new ArgumentNullException("id"); } return Find(BsonExpression.Create("_id = @0", id)).FirstOrDefault(); } public T FindOne(BsonExpression predicate) { return Find(predicate).FirstOrDefault(); } public T FindOne(string predicate, BsonDocument parameters) { return FindOne(BsonExpression.Create(predicate, parameters)); } public T FindOne(BsonExpression predicate, params BsonValue[] args) { return FindOne(BsonExpression.Create(predicate, args)); } public T FindOne(Expression<Func<T, bool>> predicate) { return FindOne(_mapper.GetExpression(predicate)); } public T FindOne(Query query) { return Find(query).FirstOrDefault(); } public IEnumerable<T> FindAll() { return Query().Include(_includes).ToEnumerable(); } public ILiteCollection<T> Include<K>(Expression<Func<T, K>> keySelector) { if (keySelector == null) { throw new ArgumentNullException("keySelector"); } BsonExpression expression = _mapper.GetExpression(keySelector); return Include(expression); } public ILiteCollection<T> Include(BsonExpression keySelector) { if (string.IsNullOrEmpty(keySelector)) { throw new ArgumentNullException("keySelector"); } LiteCollection<T> liteCollection = new LiteCollection<T>(_collection, _autoId, _engine, _mapper); liteCollection._includes.AddRange(_includes); liteCollection._includes.Add(keySelector); return liteCollection; } public bool EnsureIndex(string name, BsonExpression expression, bool unique = false) { if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException("name"); } if (expression == null) { throw new ArgumentNullException("expression"); } return _engine.EnsureIndex(_collection, name, expression, unique); } public bool EnsureIndex(BsonExpression expression, bool unique = false) { if (expression == null) { throw new ArgumentNullException("expression"); } string name = Regex.Replace(expression.Source, "[^a-z0-9]", "", RegexOptions.IgnoreCase | RegexOptions.Compiled); return EnsureIndex(name, expression, unique); } public bool EnsureIndex<K>(Expression<Func<T, K>> keySelector, bool unique = false) { BsonExpression indexExpression = GetIndexExpression(keySelector); return EnsureIndex(indexExpression, unique); } public bool EnsureIndex<K>(string name, Expression<Func<T, K>> keySelector, bool unique = false) { BsonExpression indexExpression = GetIndexExpression(keySelector); return EnsureIndex(name, indexExpression, unique); } private BsonExpression GetIndexExpression<K>(Expression<Func<T, K>> keySelector) { BsonExpression bsonExpression = _mapper.GetIndexExpression(keySelector); if (typeof(K).IsEnumerable() && bsonExpression.IsScalar) { if (bsonExpression.Type != BsonExpressionType.Path) { throw new LiteException(0, "Expression `" + bsonExpression.Source + "` must return a enumerable expression"); } bsonExpression = bsonExpression.Source + "[*]"; } return bsonExpression; } public bool DropIndex(string name) { return _engine.DropIndex(_collection, name); } public BsonValue Insert(T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } BsonDocument bsonDocument = _mapper.ToDocument(entity); bool flag = RemoveDocId(bsonDocument); _engine.Insert(_collection, new BsonDocument[1] { bsonDocument }, _autoId); BsonValue bsonValue = bsonDocument["_id"]; if (flag) { _id.Setter(entity, bsonValue.RawValue); } return bsonValue; } public void Insert(BsonValue id, T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } if (id == null || id.IsNull) { throw new ArgumentNullException("id"); } BsonDocument bsonDocument = _mapper.ToDocument(entity); bsonDocument["_id"] = id; _engine.Insert(_collection, new BsonDocument[1] { bsonDocument }, _autoId); } public int Insert(IEnumerable<T> entities) { if (entities == null) { throw new ArgumentNullException("entities"); } return _engine.Insert(_collection, GetBsonDocs(entities), _autoId); } [Obsolete("Use normal Insert()")] public int InsertBulk(IEnumerable<T> entities, int batchSize = 5000) { if (entities == null) { throw new ArgumentNullException("entities"); } return _engine.Insert(_collection, GetBsonDocs(entities), _autoId); } private IEnumerable<BsonDocument> GetBsonDocs(IEnumerable<T> documents) { foreach (T document in documents) { BsonDocument doc = _mapper.ToDocument(document); bool removed = RemoveDocId(doc); yield return doc; if (removed && _id != null) { _id.Setter(document, doc["_id"].RawValue); } } } private bool RemoveDocId(BsonDocument doc) { if (_id != null && doc.TryGetValue("_id", out var value) && ((_autoId == BsonAutoId.Int32 && value.IsInt32 && value.AsInt32 == 0) || (_autoId == BsonAutoId.ObjectId && (value.IsNull || (value.IsObjectId && value.AsObjectId == ObjectId.Empty))) || (_autoId == BsonAutoId.Guid && value.IsGuid && value.AsGuid == Guid.Empty) || (_autoId == BsonAutoId.Int64 && value.IsInt64 && value.AsInt64 == 0L))) { doc.Remove("_id"); return true; } return false; } public bool Update(T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } BsonDocument bsonDocument = _mapper.ToDocument(entity); return _engine.Update(_collection, new BsonDocument[1] { bsonDocument }) > 0; } public bool Update(BsonValue id, T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } if (id == null || id.IsNull) { throw new ArgumentNullException("id"); } BsonDocument bsonDocument = _mapper.ToDocument(entity); bsonDocument["_id"] = id; return _engine.Update(_collection, new BsonDocument[1] { bsonDocument }) > 0; } public int Update(IEnumerable<T> entities) { if (entities == null) { throw new ArgumentNullException("entities"); } return _engine.Update(_collection, entities.Select((T x) => _mapper.ToDocument(x))); } public int UpdateMany(BsonExpression transform, BsonExpression predicate) { if (transform == null) { throw new ArgumentNullException("transform"); } if (predicate == null) { throw new ArgumentNullException("predicate"); } if (transform.Type != BsonExpressionType.Document) { throw new ArgumentException("Extend expression must return a document. Eg: `col.UpdateMany('{ Name: UPPER(Name) }', 'Age > 10')`"); } return _engine.UpdateMany(_collection, transform, predicate); } public int UpdateMany(Expression<Func<T, T>> extend, Expression<Func<T, bool>> predicate) { if (extend == null) { throw new ArgumentNullException("extend"); } if (predicate == null) { throw new ArgumentNullException("predicate"); } BsonExpression expression = _mapper.GetExpression(extend); BsonExpression expression2 = _mapper.GetExpression(predicate); if (expression.Type != BsonExpressionType.Document) { throw new ArgumentException("Extend expression must return an anonymous class to be merge with entities. Eg: `col.UpdateMany(x => new { Name = x.Name.ToUpper() }, x => x.Age > 10)`"); } return _engine.UpdateMany(_collection, expression, expression2); } public bool Upsert(T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } return Upsert(new T[1] { entity }) == 1; } public int Upsert(IEnumerable<T> entities) { if (entities == null) { throw new ArgumentNullException("entities"); } return _engine.Upsert(_collection, GetBsonDocs(entities), _autoId); } public bool Upsert(BsonValue id, T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } if (id == null || id.IsNull) { throw new ArgumentNullException("id"); } BsonDocument bsonDocument = _mapper.ToDocument(entity); bsonDocument["_id"] = id; return _engine.Upsert(_collection, new BsonDocument[1] { bsonDocument }, _autoId) > 0; } internal LiteCollection(string name, BsonAutoId autoId, ILiteEngine engine, BsonMapper mapper) { _collection = name ?? mapper.ResolveCollectionName(typeof(T)); _engine = engine; _mapper = mapper; _includes = new List<BsonExpression>(); if (typeof(T) == typeof(BsonDocument)) { _entity = null; _id = null; _autoId = autoId; return; } _entity = mapper.GetEntityMapper(typeof(T)); _id = _entity.Id; if (_id != null && _id.AutoId) { _autoId = ((_id.DataType == typeof(int) || _id.DataType == typeof(int?)) ? BsonAutoId.Int32 : ((_id.DataType == typeof(long) || _id.DataType == typeof(long?)) ? BsonAutoId.Int64 : ((_id.DataType == typeof(Guid) || _id.DataType == typeof(Guid?)) ? BsonAutoId.Guid : BsonAutoId.ObjectId))); } else { _autoId = autoId; } } } public interface ILiteCollection<T> { string Name { get; } BsonAutoId AutoId { get; } EntityMapper EntityMapper { get; } ILiteCollection<T> Include<K>(Expression<Func<T, K>> keySelector); ILiteCollection<T> Include(BsonExpression keySelector); bool Upsert(T entity); int Upsert(IEnumerable<T> entities); bool Upsert(BsonValue id, T entity); bool Update(T entity); bool Update(BsonValue id, T entity); int Update(IEnumerable<T> entities); int UpdateMany(BsonExpression transform, BsonExpression predicate); int UpdateMany(Expression<Func<T, T>> extend, Expression<Func<T, bool>> predicate); BsonValue Insert(T entity); void Insert(BsonValue id, T entity); int Insert(IEnumerable<T> entities); int InsertBulk(IEnumerable<T> entities, int batchSize = 5000); bool EnsureIndex(string name, BsonExpression expression, bool unique = false); bool EnsureIndex(BsonExpression expression, bool unique = false); bool EnsureIndex<K>(Expression<Func<T, K>> keySelector, bool unique = false); bool EnsureIndex<K>(string name, Expression<Func<T, K>> keySelector, bool unique = false); bool DropIndex(string name); ILiteQueryable<T> Query(); IEnumerable<T> Find(BsonExpression predicate, int skip = 0, int limit = int.MaxValue); IEnumerable<T> Find(Query query, int skip = 0, int limit = int.MaxValue); IEnumerable<T> Find(Expression<Func<T, bool>> predicate, int skip = 0, int limit = int.MaxValue); T FindById(BsonValue id); T FindOne(BsonExpression predicate); T FindOne(string predicate, BsonDocument parameters); T FindOne(BsonExpression predicate, params BsonValue[] args); T FindOne(Expression<Func<T, bool>> predicate); T FindOne(Query query); IEnumerable<T> FindAll(); bool Delete(BsonValue id); int DeleteAll(); int DeleteMany(BsonExpression predicate); int DeleteMany(string predicate, BsonDocument parameters); int DeleteMany(string predicate, params BsonValue[] args); int DeleteMany(Expression<Func<T, bool>> predicate); int Count(); int Count(BsonExpression predicate); int Count(string predicate, BsonDocument parameters); int Count(string predicate, params BsonValue[] args); int Count(Expression<Func<T, bool>> predicate); int Count(Query query); long LongCount(); long LongCount(BsonExpression predicate); long LongCount(string predicate, BsonDocument parameters); long LongCount(string predicate, params BsonValue[] args); long LongCount(Expression<Func<T, bool>> predicate); long LongCount(Query query); bool Exists(BsonExpression predicate); bool Exists(string predicate, BsonDocument parameters); bool Exists(string predicate, params BsonValue[] args); bool Exists(Expression<Func<T, bool>> predicate); bool Exists(Query query); BsonValue Min(BsonExpression keySelector); BsonValue Min(); K Min<K>(Expression<Func<T, K>> keySelector); BsonValue Max(BsonExpression keySelector); BsonValue Max(); K Max<K>(Expression<Func<T, K>> keySelector); } public interface ILiteDatabase : IDisposable { BsonMapper Mapper { get; } ILiteStorage<string> FileStorage { get; } int UserVersion { get; set; } TimeSpan Timeout { get; set; } bool UtcDate { get; set; } long LimitSize { get; set; } int CheckpointSize { get; set; } Collation Collation { get; } ILiteCollection<T> GetCollection<T>(string name, BsonAutoId autoId = BsonAutoId.ObjectId); ILiteCollection<T> GetCollection<T>(); ILiteCollection<T> GetCollection<T>(BsonAutoId autoId); ILiteCollection<BsonDocument> GetCollection(string name, BsonAutoId autoId = BsonAutoId.ObjectId); bool BeginTrans(); bool Commit(); bool Rollback(); ILiteStorage<TFileId> GetStorage<TFileId>(string filesCollection = "_files", string chunksCollection = "_chunks"); IEnumerable<string> GetCollectionNames(); bool CollectionExists(string name); bool DropCollection(string name); bool RenameCollection(string oldName, string newName); IBsonDataReader Execute(TextReader commandReader, BsonDocument parameters = null); IBsonDataReader Execute(string command, BsonDocument parameters = null); IBsonDataReader Execute(string command, params BsonValue[] args); void Checkpoint(); long Rebuild(RebuildOptions options = null); BsonValue Pragma(string name); BsonValue Pragma(string name, BsonValue value); } public interface ILiteQueryable<T> : ILiteQueryableResult<T> { ILiteQueryable<T> Include(BsonExpression path); ILiteQueryable<T> Include(List<BsonExpression> paths); ILiteQueryable<T> Include<K>(Expression<Func<T, K>> path); ILiteQueryable<T> Where(BsonExpression predicate); ILiteQueryable<T> Where(string predicate, BsonDocument parameters); ILiteQueryable<T> Where(string predicate, params BsonValue[] args); ILiteQueryable<T> Where(Expression<Func<T, bool>> predicate); ILiteQueryable<T> OrderBy(BsonExpression keySelector, int order = 1); ILiteQueryable<T> OrderBy<K>(Expression<Func<T, K>> keySelector, int order = 1); ILiteQueryable<T> OrderByDescending(BsonExpression keySelector); ILiteQueryable<T> OrderByDescending<K>(Expression<Func<T, K>> keySelector); ILiteQueryable<T> GroupBy(BsonExpression keySelector); ILiteQueryable<T> Having(BsonExpression predicate); ILiteQueryableResult<BsonDocument> Select(BsonExpression selector); ILiteQueryableResult<K> Select<K>(Expression<Func<T, K>> selector); } public interface ILiteQueryableResult<T> { ILiteQueryableResult<T> Limit(int limit); ILiteQueryableResult<T> Skip(int offset); ILiteQueryableResult<T> Offset(int offset); ILiteQueryableResult<T> ForUpdate(); BsonDocument GetPlan(); IBsonDataReader ExecuteReader(); IEnumerable<BsonDocument> ToDocuments(); IEnumerable<T> ToEnumerable(); List<T> ToList(); T[] ToArray(); int Into(string newCollection, BsonAutoId autoId = BsonAutoId.ObjectId); T First(); T FirstOrDefault(); T Single(); T SingleOrDefault(); int Count(); long LongCount(); bool Exists(); } public interface ILiteRepository : IDisposable { ILiteDatabase Database { get; } BsonValue Insert<T>(T entity, string collectionName = null); int Insert<T>(IEnumerable<T> entities, string collectionName = null); bool Update<T>(T entity, string collectionName = null); int Update<T>(IEnumerable<T> entities, string collectionName = null); bool Upsert<T>(T entity, string collectionName = null); int Upsert<T>(IEnumerable<T> entities, string collectionName = null); bool Delete<T>(BsonValue id, string collectionName = null); int DeleteMany<T>(BsonExpression predicate, string collectionName = null); int DeleteMany<T>(Expression<Func<T, bool>> predicate, string collectionName = null); ILiteQueryable<T> Query<T>(string collectionName = null); bool EnsureIndex<T>(string name, BsonExpression expression, bool unique = false, string collectionName = null); bool EnsureIndex<T>(BsonExpression expression, bool unique = false, string collectionName = null); bool EnsureIndex<T, K>(Expression<Func<T, K>> keySelector, bool unique = false, string collectionName = null); bool EnsureIndex<T, K>(string name, Expression<Func<T, K>> keySelector, bool unique = false, string collectionName = null); T SingleById<T>(BsonValue id, string collectionName = null); List<T> Fetch<T>(BsonExpression predicate, string collectionName = null); List<T> Fetch<T>(Expression<Func<T, bool>> predicate, string collectionName = null); T First<T>(BsonExpression predicate, string collectionName = null); T First<T>(Expression<Func<T, bool>> predicate, string collectionName = null); T FirstOrDefault<T>(BsonExpression predicate, string collectionName = null); T FirstOrDefault<T>(Expression<Func<T, bool>> predicate, string collectionName = null); T Single<T>(BsonExpression predicate, string collectionName = null); T Single<T>(Expression<Func<T, bool>> predicate, string collectionName = null); T SingleOrDefault<T>(BsonExpression predicate, string collectionName = null); T SingleOrDefault<T>(Expression<Func<T, bool>> predicate, string collectionName = null); } public class LiteDatabase : ILiteDatabase, IDisposable { private readonly ILiteEngine _engine; private readonly BsonMapper _mapper; private readonly bool _disposeOnClose; private ILiteStorage<string> _fs; public BsonMapper Mapper => _mapper; public ILiteStorage<string> FileStorage => _fs ?? (_fs = GetStorage<string>()); public int UserVersion { get { return _engine.Pragma("USER_VERSION"); } set { _engine.Pragma("USER_VERSION", value); } } public TimeSpan Timeout { get { return TimeSpan.FromSeconds(_engine.Pragma("TIMEOUT").AsInt32); } set { _engine.Pragma("TIMEOUT", (int)value.TotalSeconds); } } public bool UtcDate { get { return _engine.Pragma("UTC_DATE"); } set { _engine.Pragma("UTC_DATE", value); } } public long LimitSize { get { return _engine.Pragma("LIMIT_SIZE"); } set { _engine.Pragma("LIMIT_SIZE", value); } } public int CheckpointSize { get { return _engine.Pragma("CHECKPOINT"); } set { _engine.Pragma("CHECKPOINT", value); } } public Collation Collation => new Collation(_engine.Pragma("COLLATION").AsString); public LiteDatabase(string connectionString, BsonMapper mapper = null) : this(new ConnectionString(connectionString), mapper) { } public LiteDatabase(ConnectionString connectionString, BsonMapper mapper = null) { if (connectionString == null) { throw new ArgumentNullException("connectionString"); } _engine = connectionString.CreateEngine(); _mapper = mapper ?? BsonMapper.Global; _disposeOnClose = true; } public LiteDatabase(Stream stream, BsonMapper mapper = null, Stream logStream = null) { EngineSettings settings = new EngineSettings { DataStream = (stream ?? throw new ArgumentNullException("stream")), LogStream = logStream }; _engine = new LiteEngine(settings); _mapper = mapper ?? BsonMapper.Global; _disposeOnClose = true; } public LiteDatabase(ILiteEngine engine, BsonMapper mapper = null, bool disposeOnClose = true) { _engine = engine ?? throw new ArgumentNullException("engine"); _mapper = mapper ?? BsonMapper.Global; _disposeOnClose = disposeOnClose; } public ILiteCollection<T> GetCollection<T>(string name, BsonAutoId autoId = BsonAutoId.ObjectId) { return new LiteCollection<T>(name, autoId, _engine, _mapper); } public ILiteCollection<T> GetCollection<T>() { return GetCollection<T>(null); } public ILiteCollection<T> GetCollection<T>(BsonAutoId autoId) { return GetCollection<T>(null, autoId); } public ILiteCollection<BsonDocument> GetCollection(string name, BsonAutoId autoId = BsonAutoId.ObjectId) { if (name.IsNullOrWhiteSpace()) { throw new ArgumentNullException("name"); } return new LiteCollection<BsonDocument>(name, autoId, _engine, _mapper); } public bool BeginTrans() { return _engine.BeginTrans(); } public bool Commit() { return _engine.Commit(); } public bool Rollback() { return _engine.Rollback(); } public ILiteStorage<TFileId> GetStorage<TFileId>(string filesCollection = "_files", string chunksCollection = "_chunks") { return new LiteStorage<TFileId>(this, filesCollection, chunksCollection); } public IEnumerable<string> GetCollectionNames() { return (from x in GetCollection("$cols").Query().Where("type = 'user'").ToDocuments() select x["name"].AsString).ToArray(); } public bool CollectionExists(string name) { if (name.IsNullOrWhiteSpace()) { throw new ArgumentNullException("name"); } return GetCollectionNames().Contains<string>(name, StringComparer.OrdinalIgnoreCase); } public bool DropCollection(string name) { if (name.IsNullOrWhiteSpace()) { throw new ArgumentNullException("name"); } return _engine.DropCollection(name); } public bool RenameCollection(string oldName, string newName) { if (oldName.IsNullOrWhiteSpace()) { throw new ArgumentNullException("oldName"); } if (newName.IsNullOrWhiteSpace()) { throw new ArgumentNullException("newName"); } return _engine.RenameCollection(oldName, newName); } public IBsonDataReader Execute(TextReader commandReader, BsonDocument parameters = null) { if (commandReader == null) { throw new ArgumentNullException("commandReader"); } Tokenizer tokenizer = new Tokenizer(commandReader); return new SqlParser(_engine, tokenizer, parameters).Execute(); } public IBsonDataReader Execute(string command, BsonDocument parameters = null) { if (command == null) { throw new ArgumentNullException("command"); } Tokenizer tokenizer = new Tokenizer(command); return new SqlParser(_engine, tokenizer, parameters).Execute(); } public IBsonDataReader Execute(string command, params BsonValue[] args) { BsonDocument bsonDocument = new BsonDocument(); int num = 0; foreach (BsonValue value in args) { bsonDocument[num.ToString()] = value; num++; } return Execute(command, bsonDocument); } public void Checkpoint() { _engine.Checkpoint(); } public long Rebuild(RebuildOptions options = null) { return _engine.Rebuild(options ?? new RebuildOptions()); } public BsonValue Pragma(string name) { return _engine.Pragma(name); } public BsonValue Pragma(string name, BsonValue value) { return _engine.Pragma(name, value); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } ~LiteDatabase() { Dispose(disposing: false); } protected virtual void Dispose(bool disposing) { if (disposing && _disposeOnClose) { _engine.Dispose(); } } } public class LiteQueryable<T> : ILiteQueryable<T>, ILiteQueryableResult<T> { protected readonly ILiteEngine _engine; protected readonly BsonMapper _mapper; protected readonly string _collection; protected readonly Query _query; private readonly bool _isSimpleType = Reflection.IsSimpleType(typeof(T)); internal LiteQueryable(ILiteEngine engine, BsonMapper mapper, string collection, Query query) { _engine = engine; _mapper = mapper; _collection = collection; _query = query; } public ILiteQueryable<T> Include<K>(Expression<Func<T, K>> path) { _query.Includes.Add(_mapper.GetExpression(path)); return this; } public ILiteQueryable<T> Include(BsonExpression path) { _query.Includes.Add(path); return this; } public ILiteQueryable<T> Include(List<BsonExpression> paths) { _query.Includes.AddRange(paths); return this; } public ILiteQueryable<T> Where(BsonExpression predicate) { _query.Where.Add(predicate); return this; } public ILiteQueryable<T> Where(string predicate, BsonDocument parameters) { _query.Where.Add(BsonExpression.Create(predicate, parameters)); return this; } public ILiteQueryable<T> Where(string predicate, params BsonValue[] args) { _query.Where.Add(BsonExpression.Create(predicate, args)); return this; } public ILiteQueryable<T> Where(Expression<Func<T, bool>> predicate) { return Where(_mapper.GetExpression(predicate)); } public ILiteQueryable<T> OrderBy(BsonExpression keySelector, int order = 1) { if (_query.OrderBy != null) { throw new ArgumentException("ORDER BY already defined in this query builder"); } _query.OrderBy = keySelector; _query.Order = order; return this; } public ILiteQueryable<T> OrderBy<K>(Expression<Func<T, K>> keySelector, int order = 1) { return OrderBy(_mapper.GetExpression(keySelector), order); } public ILiteQueryable<T> OrderByDescending(BsonExpression keySelector) { return OrderBy(keySelector, -1); } public ILiteQueryable<T> OrderByDescending<K>(Expression<Func<T, K>> keySelector) { return OrderBy(keySelector, -1); } public ILiteQueryable<T> GroupBy(BsonExpression keySelector) { if (_query.GroupBy != null) { throw new ArgumentException("GROUP BY already defined in this query"); } _query.GroupBy = keySelector; return this; } public ILiteQueryable<T> Having(BsonExpression predicate) { if (_query.Having != null) { throw new ArgumentException("HAVING already defined in this query"); } _query.Having = predicate; return this; } public ILiteQueryableResult<BsonDocument> Select(BsonExpression selector) { _query.Select = selector; return new LiteQueryable<BsonDocument>(_engine, _mapper, _collection, _query); } public ILiteQueryableResult<K> Select<K>(Expression<Func<T, K>> selector) { if (_query.GroupBy != null) { throw new ArgumentException("Use Select(BsonExpression selector) when using GroupBy query"); } _query.Select = _mapper.GetExpression(selector); return new LiteQueryable<K>(_engine, _mapper, _collection, _query); } public ILiteQueryableResult<T> ForUpdate() { _query.ForUpdate = true; return this; } public ILiteQueryableResult<T> Offset(int offset) { _query.Offset = offset; return this; } public ILiteQueryableResult<T> Skip(int offset) { return Offset(offset); } public ILiteQueryableResult<T> Limit(int limit) { _query.Limit = limit; return this; } public IBsonDataReader ExecuteReader() { _query.ExplainPlan = false; return _engine.Query(_collection, _query); } public IEnumerable<BsonDocument> ToDocuments() { using IBsonDataReader reader = ExecuteReader(); while (reader.Read()) { yield return reader.Current as BsonDocument; } } public IEnumerable<T> ToEnumerable() { if (_isSimpleType) { return from x in ToDocuments() select x[x.Keys.First()] into x select (T)_mapper.Deserialize(typeof(T), x); } return from x in ToDocuments() select (T)_mapper.Deserialize(typeof(T), x); } public List<T> ToList() { return ToEnumerable().ToList(); } public T[] ToArray() { return ToEnumerable().ToArray(); } public BsonDocument GetPlan() { _query.ExplainPlan = true; return _engine.Query(_collection, _query).ToEnumerable().FirstOrDefault()?.AsDocument; } public T Single() { return ToEnumerable().Single(); } public T SingleOrDefault() { return ToEnumerable().SingleOrDefault(); } public T First() { return ToEnumerable().First(); } public T FirstOrDefault() { return ToEnumerable().FirstOrDefault(); } public int Count() { BsonExpression select = _query.Select; try { Select("{ count: COUNT(*._id) }"); return ToDocuments().Single()["count"].AsInt32; } finally { _query.Select = select; } } public long LongCount() { BsonExpression select = _query.Select; try { Select("{ count: COUNT(*._id) }"); return ToDocuments().Single()["count"].AsInt64; } finally { _query.Select = select; } } public bool Exists() { BsonExpression select = _query.Select; try { Select("{ exists: ANY(*._id) }"); return ToDocuments().Single()["exists"].AsBoolean; } finally { _query.Select = select; } } public int Into(string newCollection, BsonAutoId autoId = BsonAutoId.ObjectId) { _query.Into = newCollection; _query.IntoAutoId = autoId; using IBsonDataReader bsonDataReader = ExecuteReader(); return bsonDataReader.Current.AsInt32; } } public class LiteRepository : ILiteRepository, IDisposable { private readonly ILiteDatabase _db; public ILiteDatabase Database => _db; public LiteRepository(ILiteDatabase database) { _db = database; } public LiteRepository(string connectionString, BsonMapper mapper = null) { _db = new LiteDatabase(connectionString, mapper); } public LiteRepository(ConnectionString connectionString, BsonMapper mapper = null) { _db = new LiteDatabase(connectionString, mapper); } public LiteRepository(Stream stream, BsonMapper mapper = null, Stream logStream = null) { _db = new LiteDatabase(stream, mapper, logStream); } public BsonValue Insert<T>(T entity, string collectionName = null) { return _db.GetCollection<T>(collectionName).Insert(entity); } public int Insert<T>(IEnumerable<T> entities, string collectionName = null) { return _db.GetCollection<T>(collectionName).Insert(entities); } public bool Update<T>(T entity, string collectionName = null) { return _db.GetCollection<T>(collectionName).Update(entity); } public int Update<T>(IEnumerable<T> entities, string collectionName = null) { return _db.GetCollection<T>(collectionName).Update(entities); } public bool Upsert<T>(T entity, string collectionName = null) { return _db.GetCollection<T>(collectionName).Upsert(entity); } public int Upsert<T>(IEnumerable<T> entities, string collectionName = null) { return _db.GetCollection<T>(collectionName).Upsert(entities); } public bool Delete<T>(BsonValue id, string collectionName = null) { return _db.GetCollection<T>(collectionName).Delete(id); } public int DeleteMany<T>(BsonExpression predicate, string collectionName = null) { return _db.GetCollection<T>(collectionName).DeleteMany(predicate); } public int DeleteMany<T>(Expression<Func<T, bool>> predicate, string collectionName = null) { return _db.GetCollection<T>(collectionName).DeleteMany(predicate); } public ILiteQueryable<T> Query<T>(string collectionName = null) { return _db.GetCollection<T>(collectionName).Query(); } public bool EnsureIndex<T>(string name, BsonExpression expression, bool unique = false, string collectionName = null) { return _db.GetCollection<T>(collectionName).EnsureIndex(name, expression, unique); } public bool EnsureIndex<T>(BsonExpression expression, bool unique = false, string collectionName = null) { return _db.GetCollection<T>(collectionName).EnsureIndex(expression, unique); } public bool EnsureIndex<T, K>(Expression<Func<T, K>> keySelector, bool unique = false, string collectionName = null) { return _db.GetCollection<T>(collectionName).EnsureIndex(keySelector, unique); } public bool EnsureIndex<T, K>(string name, Expression<Func<T, K>> keySelector, bool unique = false, string collectionName = null) { return _db.GetCollection<T>(collectionName).EnsureIndex(name, keySelector, unique); } public T SingleById<T>(BsonValue id, string collectionName = null) { return _db.GetCollection<T>(collectionName).Query().Where("_id = @0", id) .Single(); } public List<T> Fetch<T>(BsonExpression predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).ToList(); } public List<T> Fetch<T>(Expression<Func<T, bool>> predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).ToList(); } public T First<T>(BsonExpression predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).First(); } public T First<T>(Expression<Func<T, bool>> predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).First(); } public T FirstOrDefault<T>(BsonExpression predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).FirstOrDefault(); } public T FirstOrDefault<T>(Expression<Func<T, bool>> predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).FirstOrDefault(); } public T Single<T>(BsonExpression predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).Single(); } public T Single<T>(Expression<Func<T, bool>> predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).Single(); } public T SingleOrDefault<T>(BsonExpression predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).SingleOrDefault(); } public T SingleOrDefault<T>(Expression<Func<T, bool>> predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).SingleOrDefault(); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } ~LiteRepository() { Dispose(disposing: false); } protected virtual void Dispose(bool disposing) { if (disposing) { _db.Dispose(); } } } public class BsonCtorAttribute : Attribute { } public class BsonFieldAttribute : Attribute { public string Name { get; set; } public BsonFieldAttribute(string name) { Name = name; } public BsonFieldAttribute() { } } public class BsonIdAttribute : Attribute { public bool AutoId { get; private set; } public BsonIdAttribute() { AutoId = true; } public BsonIdAttribute(bool autoId) { AutoId = autoId; } } public class BsonIgnoreAttribute : Attribute { } public class BsonRefAttribute : Attribute { public string Collection { get; set; } public BsonRefAttribute(string collection) { Collection = collection; } public BsonRefAttribute() { Collection = null; } } public class BsonMapper { public delegate BsonValue DeserializationCallback(BsonMapper sender, Type target, BsonValue value); private readonly Dictionary<Type, EntityMapper> _entities = new Dictionary<Type, EntityMapper>(); private readonly ConcurrentDictionary<Type, Func<object, BsonValue>> _customSerializer = new ConcurrentDictionary<Type, Func<object, BsonValue>>(); private readonly ConcurrentDictionary<Type, Func<BsonValue, object>> _customDeserializer = new ConcurrentDictionary<Type, Func<BsonValue, object>>(); private readonly Func<Type, object> _typeInstantiator; private readonly ITypeNameBinder _typeNameBinder; public static BsonMapper Global = new BsonMapper(); public Func<string, string> ResolveFieldName; public Action<Type, MemberInfo, MemberMapper> ResolveMember; public Func<Type, string> ResolveCollectionName; private readonly Regex _lowerCaseDelimiter = new Regex("(?!(^[A-Z]))([A-Z])", RegexOptions.Compiled); private readonly HashSet<Type> _bsonTypes = new HashSet<Type> { typeof(string), typeof(int), typeof(long), typeof(bool), typeof(Guid), typeof(DateTime), typeof(byte[]), typeof(ObjectId), typeof(double), typeof(decimal) }; private readonly HashSet<Type> _basicTypes = new HashSet<Type> { typeof(short), typeof(ushort), typeof(uint), typeof(float), typeof(char), typeof(byte), typeof(sbyte) }; public bool SerializeNullValues { get; set; } public bool TrimWhitespace { get; set; } public bool EmptyStringToNull { get; set; } public bool EnumAsInteger { get; set; } public bool IncludeFields { get; set; } public bool IncludeNonPublic { get; set; } public int MaxDepth { get; set; } public DeserializationCallback? OnDeserialization { get; set; } public BsonMapper(Func<Type, object> customTypeInstantiator = null, ITypeNameBinder typeNameBinder = null) { SerializeNullValues = false; TrimWhitespace = true; EmptyStringToNull = true; EnumAsInteger = false; ResolveFieldName = (string s) => s; ResolveMember = delegate { }; ResolveCollectionName = (Type t) => (!Reflection.IsEnumerable(t)) ? t.Name : Reflection.GetListItemType(t).Name; IncludeFields = false; MaxDepth = 20; _typeInstantiator = customTypeInstantiator ?? ((Func<Type, object>)((Type t) => null)); _typeNameBinder = typeNameBinder ?? DefaultTypeNameBinder.Instance; RegisterType((Uri uri) => uri.IsAbsoluteUri ? uri.AbsoluteUri : uri.ToString(), (BsonValue bson) => new Uri(bson.AsString)); RegisterType((DateTimeOffset value) => new BsonValue(value.UtcDateTime), (BsonValue bson) => bson.AsDateTime.ToUniversalTime()); RegisterType((TimeSpan value) => new BsonValue(value.Ticks), (BsonValue bson) => new TimeSpan(bson.AsInt64)); RegisterType((Regex r) => (r.Options != 0) ? new BsonDocument { { "p", r.ToString() }, { "o", (int)r.Options } } : new BsonValue(r.ToString()), (BsonValue value) => (!value.IsString) ? new Regex(value.AsDocument["p"].AsString, (RegexOptions)value.AsDocument["o"].AsInt32) : new Regex(value)); } public void RegisterType<T>(Func<T, BsonValue> serialize, Func<BsonValue, T> deserialize) { _customSerializer[typeof(T)] = (object o) => serialize((T)o); _customDeserializer[typeof(T)] = (BsonValue b) => deserialize(b); } public void RegisterType(Type type, Func<object, BsonValue> serialize, Func<BsonValue, object> deserialize) { _customSerializer[type] = (object o) => serialize(o); _customDeserializer[type] = (BsonValue b) => deserialize(b); } public EntityBuilder<T> Entity<T>() { return new EntityBuilder<T>(this, _typeNameBinder); } public BsonExpression GetExpression<T, K>(Expression<Func<T, K>> predicate) { return new LinqExpressionVisitor(this, predicate).Resolve(typeof(K) == typeof(bool)); } public BsonExpression GetIndexExpression<T, K>(Expression<Func<T, K>> predicate) { return new LinqExpressionVisitor(this, predicate).Resolve(predicate: false); } public BsonMapper UseCamelCase() { ResolveFieldName = (string s) => char.ToLower(s[0]) + s.Substring(1); return this; } public BsonMapper UseLowerCaseDelimiter(char delimiter = '_') { ResolveFieldName = (string s) => _lowerCaseDelimiter.Replace(s, delimiter + "$2").ToLower(); return this; } internal EntityMapper GetEntityMapper(Type type) { if (!_entities.TryGetValue(type, out var value)) { lock (_entities) { if (!_entities.TryGetValue(type, out value)) { return BuildAddEntityMapper(type); } } } return value; } protected virtual EntityMapper BuildAddEntityMapper(Type type) { EntityMapper entityMapper = new EntityMapper(type); _entities[type] = entityMapper; Type typeFromHandle = typeof(BsonIdAttribute); Type typeFromHandle2 = typeof(BsonIgnoreAttribute); Type typeFromHandle3 = typeof(BsonFieldAttribute); Type typeFromHandle4 = typeof(BsonRefAttribute); IEnumerable<MemberInfo> typeMembers = GetTypeMembers(type); MemberInfo idMember = GetIdMember(typeMembers); foreach (MemberInfo item in typeMembers) { if (!CustomAttributeExtensions.IsDefined(item, typeFromHandle2, inherit: true)) { string name = ResolveFieldName(item.Name); BsonFieldAttribute bsonFieldAttribute = (BsonFieldAttribute)CustomAttributeExtensions.GetCustomAttributes(item, typeFromHandle3, inherit: true).FirstOrDefault(); if (bsonFieldAttribute != null && bsonFieldAttribute.Name != null) { name = bsonFieldAttribute.Name; } if (item == idMember) { name = "_id"; } GenericGetter getter = Reflection.CreateGenericGetter(type, item); GenericSetter setter = Reflection.CreateGenericSetter(type, item); BsonIdAttribute bsonIdAttribute = (BsonIdAttribute)CustomAttributeExtensions.GetCustomAttributes(item, typeFromHandle, inherit: true).FirstOrDefault(); Type type2 = ((item is PropertyInfo) ? (item as PropertyInfo).PropertyType : (item as FieldInfo).FieldType); bool flag = Reflection.IsEnumerable(type2); MemberMapper memberMapper = new MemberMapper { AutoId = (bsonIdAttribute?.AutoId ?? true), FieldName = name, MemberName = item.Name, DataType = type2, IsEnumerable = flag, UnderlyingType = (flag ? Reflection.GetListItemType(type2) : type2), Getter = getter, Setter = setter }; BsonRefAttribute bsonRefAttribute = (BsonRefAttribute)CustomAttributeExtensions.GetCustomAttributes(item, typeFromHandle4, inherit: false).FirstOrDefault(); if (bsonRefAttribute != null && item is PropertyInfo) { RegisterDbRef(this, memberMapper, _typeNameBinder, bsonRefAttribute.Collection ?? ResolveCollectionName((item as PropertyInfo).PropertyType)); } ResolveMember?.Invoke(type, item, memberMapper); if (memberMapper.FieldName != null && !entityMapper.Members.Any((MemberMapper x) => x.FieldName.Equals(name, StringComparison.OrdinalIgnoreCase)) && !memberMapper.IsIgnore) { entityMapper.Members.Add(memberMapper); } } } return entityMapper; } protected virtual MemberInfo GetIdMember(IEnumerable<MemberInfo> members) { return Reflection.SelectMember(members, (MemberInfo x) => CustomAttributeExtensions.IsDefined(x, typeof(BsonIdAttribute), inherit: true), (MemberInfo x) => x.Name.Equals("Id", StringComparison.OrdinalIgnoreCase), (MemberInfo x) => x.Name.Equals(x.DeclaringType.Name + "Id", StringComparison.OrdinalIgnoreCase)); } protected virtual IEnumerable<MemberInfo> GetTypeMembers(Type type) { List<MemberInfo> list = new List<MemberInfo>(); BindingFlags bindingAttr = (IncludeNonPublic ? (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) : (BindingFlags.Instance | BindingFlags.Public)); list.AddRange((from x in type.GetProperties(bindingAttr) where x.CanRead && x.GetIndexParameters().Length == 0 select x).Select((Func<PropertyInfo, MemberInfo>)((PropertyInfo x) => x))); if (IncludeFields) { list.AddRange((from x in type.GetFields(bindingAttr) where !x.Name.EndsWith("k__BackingField") && !x.IsStatic select x).Select((Func<FieldInfo, MemberInfo>)((FieldInfo x) => x))); } return list; } protected virtual CreateObject GetTypeCtor(EntityMapper mapper) { Type type = mapper.ForType; List<CreateObject> list = new List<CreateObject>(); bool flag = false; ConstructorInfo[] constructors = type.GetConstructors(); foreach (ConstructorInfo constructorInfo in constructors) { ParameterInfo[] parameters = constructorInfo.GetParameters(); if (parameters.Length == 0) { flag = true; continue; } KeyValuePair<string, Type>[] paramMap = new KeyValuePair<string, Type>[parameters.Length]; int j; for (j = 0; j < parameters.Length; j++) { ParameterInfo parameterInfo = parameters[j]; MemberMapper memberMapper = null; foreach (MemberMapper member in mapper.Members) { if (member.MemberName.ToLower() == parameterInfo.Name.ToLower() && member.DataType == parameterInfo.ParameterType) { memberMapper = member; break; } } if (memberMapper == null) { break; } paramMap[j] = new KeyValuePair<string, Type>(memberMapper.FieldName, memberMapper.DataType); } if (j < parameters.Length) { continue; } CreateObject createObject = (BsonDocument value) => Activator.CreateInstance(type, paramMap.Select((KeyValuePair<string, Type> x) => Deserialize(x.Value, value[x.Key])).ToArray()); if (constructorInfo.GetCustomAttribute<BsonCtorAttribute>() != null) { return createObject; } list.Add(createObject); } if (flag) { return null; } return list.FirstOrDefault(); } internal static void RegisterDbRef(BsonMapper mapper, MemberMapper member, ITypeNameBinder typeNameBinder, string collection) { member.IsDbRef = true; if (member.IsEnumerable) { RegisterDbRefList(mapper, member, typeNameBinder, collection); } else { RegisterDbRefItem(mapper, member, typeNameBinder, collection); } } private static void RegisterDbRefItem(BsonMapper mapper, MemberMapper member, ITypeNameBinder typeNameBinder, string collection) { EntityMapper entity = mapper.GetEntityMapper(member.DataType); member.Serialize = delegate(object obj, BsonMapper m) { if (obj == null) { return BsonValue.Null; } object obj2 = (entity.Id ?? throw new LiteException(0, "There is no _id field mapped in your type: " + member.DataType.FullName)).Getter(obj); BsonDocument bsonDocument = new BsonDocument { ["$id"] = m.Serialize(obj2.GetType(), obj2, 0), ["$ref"] = collection }; if (member.DataType != obj.GetType()) { bsonDocument["$type"] = typeNameBinder.GetName(obj.GetType()); } return bsonDocument; }; member.Deserialize = delegate(BsonValue bson, BsonMapper m) { if (bson == null || !bson.IsDocument) { return null; } BsonDocument asDocument = bson.AsDocument; BsonValue value = asDocument["$id"]; bool num = asDocument["$missing"] == true; bool flag = !asDocument.ContainsKey("$ref"); if (num) { return null; } if (flag) { asDocument["_id"] = value; if (asDocument.ContainsKey("$type")) { asDocument["_type"] = bson["$type"]; } return m.Deserialize(entity.ForType, asDocument); } return m.Deserialize(entity.ForType, asDocument.ContainsKey("$type") ? new BsonDocument { ["_id"] = value, ["_type"] = bson["$type"] } : new BsonDocument { ["_id"] = value }); }; } private static void RegisterDbRefList(BsonMapper mapper, MemberMapper member, ITypeNameBinder typeNameBinder, string collection) { EntityMapper entity = mapper.GetEntityMapper(member.UnderlyingType); member.Serialize = delegate(object list, BsonMapper m) { if (list == null) { return BsonValue.Null; } BsonArray bsonArray2 = new BsonArray(); MemberMapper id = entity.Id; foreach (object item in (IEnumerable)list) { if (item != null) { object obj = id.Getter(item); BsonDocument bsonDocument2 = new BsonDocument { ["$id"] = m.Serialize(obj.GetType(), obj, 0), ["$ref"] = collection }; if (member.UnderlyingType != item.GetType()) { bsonDocument2["$type"] = typeNameBinder.GetName(item.GetType()); } bsonArray2.Add(bsonDocument2); } } return bsonArray2; }; member.Deserialize = delegate(BsonValue bson, BsonMapper m) { if (!bson.IsArray) { return null; } BsonArray asArray = bson.AsArray; if (asArray.Count == 0) { return m.Deserialize(member.DataType, asArray); } BsonArray bsonArray = new BsonArray(); foreach (BsonValue item2 in asArray) { if (item2.IsDocument) { BsonDocument asDocument = item2.AsDocument; BsonValue value = asDocument["$id"]; bool flag = asDocument["$missing"] == true; bool flag2 = !asDocument.ContainsKey("$ref"); if (!flag) { if (flag2) { item2["_id"] = value; if (item2.AsDocument.ContainsKey("$type")) { item2["_type"] = item2["$type"]; } bsonArray.Add(item2); } else { BsonDocument bsonDocument = new BsonDocument { ["_id"] = value }; if (item2.AsDocument.ContainsKey("$type")) { bsonDocument["_type"] = item2["$type"]; } bsonArray.Add(bsonDocument); } } } } return m.Deserialize(member.DataType, bsonArray); }; } public virtual object ToObject(Type type, BsonDocument doc) { if (doc == null) { throw new ArgumentNullException("doc"); } if (type == typeof(BsonDocument)) { return doc; } return Deserialize(type, doc); } public virtual T ToObject<T>(BsonDocument doc) { return (T)ToObject(typeof(T), doc); } public T Deserialize<T>(BsonValue value) { if (value == null) { return default(T); } return (T)Deserialize(typeof(T), value); } public object Deserialize(Type type, BsonValue value) { if (OnDeserialization != null) { BsonValue bsonValue = OnDeserialization(this, type, value); if ((object)bsonValue != null) { value = bsonValue; } } if (value.IsNull) { return null; } if (Reflection.IsNullable(type)) { type = Reflection.UnderlyingTypeOf(type); } if (_customDeserializer.TryGetValue(type, out var value2)) { return value2(value); } TypeInfo typeInfo = type.GetTypeInfo(); if (type == typeof(BsonValue)) { return value; } if (type == typeof(BsonDocument)) { return value.AsDocument; } if (type == typeof(BsonArray)) { return value.AsArray; } if (_bsonTypes.Contains(type)) { return value.RawValue; } if (_basicTypes.Contains(type)) { return Convert.ChangeType(value.RawValue, type); } if (type == typeof(ulong)) { return (ulong)value.AsInt64; } if (typeInfo.IsEnum) { if (value.IsString) { return Enum.Parse(type, value.AsString); } if (value.IsNumber) { return Enum.ToObject(type, value.AsInt32); } } else { if (value.IsArray) { if (type == typeof(object)) { return DeserializeArray(typeof(object), value.AsArray); } if (type.IsArray) { return DeserializeArray(type.GetElementType(), value.AsArray); } return DeserializeList(type, value.AsArray); } if (value.IsDocument) { if (type.IsAnonymousType()) { return DeserializeAnonymousType(type, value.AsDocument); } BsonDocument asDocument = value.AsDocument; if (asDocument.TryGetValue("_type", out var value3) && value3.IsString) { Type type2 = _typeNameBinder.GetType(value3.AsString); if (type2 == null) { throw LiteException.InvalidTypedName(value3.AsString); } if (!type.IsAssignableFrom(type2)) { throw LiteException.DataTypeNotAssignable(type.FullName, type2.FullName); } type = type2; } else if (type == typeof(object)) { type = typeof(Dictionary<string, object>); } EntityMapper entity = GetEntityMapper(type); if (entity.CreateInstance == null) { entity.CreateInstance = GetTypeCtor(entity) ?? ((CreateObject)((BsonDocument v) => Reflection.CreateInstance(entity.ForType))); } object obj = _typeInstantiator(type) ?? entity.CreateInstance(asDocument); if (obj is IDictionary dict) { if (obj.GetType().GetTypeInfo().IsGenericType) { Type k = type.GetGenericArguments()[0]; Type t = type.GetGenericArguments()[1]; DeserializeDictionary(k, t, dict, value.AsDocument); } else { DeserializeDictionary(typeof(object), typeof(object), dict, value.AsDocument); } } else { DeserializeObject(entity, obj, asDocument); } return obj; } } return value.RawValue; } private object DeserializeArray(Type type, BsonArray array) { Array array2 = Array.CreateInstance(type, array.Count); int num = 0; foreach (BsonValue item in array) { array2.SetValue(Deserialize(type, item), num++); } return array2; } private object DeserializeList(Type type, BsonArray value) { Type listItemType = Reflection.GetListItemType(type); IEnumerable enumerable = (IEnumerable)Reflection.CreateInstance(type); if (enumerable is IList list) { foreach (BsonValue item in value) { list.Add(Deserialize(listItemType, item)); } } else { MethodInfo method = type.GetMethod("Add", new Type[1] { listItemType }); foreach (BsonValue item2 in value) { method.Invoke(enumerable, new object[1] { Deserialize(listItemType, item2) }); } } return enumerable; } private void DeserializeDictionary(Type K, Type T, IDictionary dict, BsonDocument value) { bool isEnum = K.GetTypeInfo().IsEnum; foreach (KeyValuePair<string, BsonValue> element in value.GetElements()) { object key = (isEnum ? Enum.Parse(K, element.Key) : ((K == typeof(Uri)) ? new Uri(element.Key) : Convert.ChangeType(element.Key, K))); object value2 = Deserialize(T, element.Value); dict.Add(key, value2); } } private void DeserializeObject(EntityMapper entity, object obj, BsonDocument value) { foreach (MemberMapper item in entity.Members.Where((MemberMapper x) => x.Setter != null)) { if (value.TryGetValue(item.FieldName, out var value2)) { if (item.Deserialize != null) { item.Setter(obj, item.Deserialize(value2, this)); } else { item.Setter(obj, Deserialize(item.DataType, value2)); } } } } private object DeserializeAnonymousType(Type type, BsonDocument value) { List<object> list = new List<object>(); ParameterInfo[] parameters = type.GetConstructors()[0].GetParameters(); foreach (ParameterInfo parameterInfo in parameters) { object obj = Deserialize(parameterInfo.ParameterType, value[parameterInfo.Name]); if (obj == null && StringComparer.OrdinalIgnoreCase.Equals(parameterInfo.Name, "Id") && value.TryGetValue("_id", out var value2)) { obj = Deserialize(parameterInfo.ParameterType, value2); } list.Add(obj); } return Activator.CreateInstance(type, list.ToArray()); } public virtual BsonDocument ToDocument(Type type, object entity) { if (entity == null) { throw new ArgumentNullException("entity"); } if (entity is BsonDocument) { return (BsonDocument)entity; } return Serialize(type, entity, 0).AsDocument; } public virtual BsonDocument ToDocument<T>(T entity) { return ToDocument(typeof(T), entity)?.AsDocument; } public BsonValue Serialize<T>(T obj) { return Serialize(typeof(T), obj, 0); } public BsonValue Serialize(Type type, object obj) { return Serialize(type, obj, 0); } internal BsonValue Serialize(Type type, object obj, int depth) { if (++depth > MaxDepth) { throw LiteException.DocumentMaxDepth(MaxDepth, type); } if (obj == null) { return BsonValue.Null; } if (obj is BsonValue result) { return result; } if (_customSerializer.TryGetValue(type, out var value) || _customSerializer.TryGetValue(obj.GetType(), out value)) { return value(obj); } if (obj is string) { string text = (TrimWhitespace ? (obj as string).Trim() : ((string)obj)); if (EmptyStringToNull && text.Length == 0) { return BsonValue.Null; } return new BsonValue(text); } if (obj is int) { return new BsonValue((int)obj); } if (obj is long) { return new BsonValue((long)obj); } if (obj is double) { return new BsonValue((double)obj); } if (obj is decimal) { return new BsonValue((decimal)obj); } if (obj is byte[]) { return new BsonValue((byte[])obj); } if (obj is ObjectId) { return new BsonValue((ObjectId)obj); } if (obj is Guid) { return new BsonValue((Guid)obj); } if (obj is bool) { return new BsonValue((bool)obj); } if (obj is DateTime) { return new BsonValue((DateTime)obj); } if (obj is short || obj is ushort || obj is byte || obj is sbyte) { return new BsonValue(Convert.ToInt32(obj)); } if (obj is uint) { return new BsonValue(Convert.ToInt64(obj)); } if (obj is ulong) { return new BsonValue((long)(ulong)obj); } if (obj is float) { return new BsonValue(Convert.ToDouble(obj)); } if (obj is char) { return new BsonValue(obj.ToString()); } if (obj is Enum) { if (EnumAsInteger) { return new BsonValue((int)obj); } return new BsonValue(obj.ToString()); } if (obj is IDictionary dict) { if (type == typeof(object)) { type = obj.GetType(); } Type type2 = (type.GetTypeInfo().IsGenericType ? type.GetGenericArguments()[1] : typeof(object)); return SerializeDictionary(type2, dict, depth); } if (obj is IEnumerable) { return SerializeArray(Reflection.GetListItemType(type), obj as IEnumerable, depth); } return SerializeObject(type, obj, depth); } private BsonArray SerializeArray(Type type, IEnumerable array, int depth) { BsonArray bsonArray = new BsonArray(); foreach (object item in array) { bsonArray.Add(Serialize(type, item, depth)); } return bsonArray; } private BsonDocument SerializeDictionary(Type type, IDictionary dict, int depth) { BsonDocument bsonDocument = new BsonDocument(); foreach (object key in dict.Keys) { object obj = dict[key]; string name = key.ToString(); if (key is DateTime dateTime) { name = dateTime.ToString("o"); } bsonDocument[name] = Serialize(type, obj, depth); } return bsonDocument; } private BsonDocument SerializeObject(Type type, object obj, int depth) { Type type2 = obj.GetType(); BsonDocument bsonDocument = new BsonDocument(); EntityMapper entityMapper = GetEntityMapper(type2); if (type != type2) { bsonDocument["_type"] = new BsonValue(_typeNameBinder.GetName(type2)); } foreach (MemberMapper item in entityMapper.Members.Where((MemberMapper x) => x.Getter != null)) { object obj2 = item.Getter(obj); if (obj2 != null || SerializeNullValues || !(item.FieldName != "_id")) { if (item.Serialize != null) { bsonDocument[item.FieldName] = item.Serialize(obj2, this); } else { bsonDocument[item.FieldName] = Serialize(item.DataType, obj2, depth); } } } return bsonDocument; } } public class EntityBuilder<T> { private readonly BsonMapper _mapper; private readonly EntityMapper _entity; private readonly ITypeNameBinder _typeNameBinder; internal EntityBuilder(BsonMapper mapper, ITypeNameBinder typeNameBinder) { _mapper = mapper; _typeNameBinder = typeNameBinder; _entity = mapper.GetEntityMapper(typeof(T)); } public EntityBuilder<T> Ignore<K>(Expression<Func<T, K>> member) { return GetMember(member, delegate(MemberMapper p) { _entity.Members.Remove(p); }); } public EntityBuilder<T> Field<K>(Expression<Func<T, K>> member, string field) { if (field.IsNullOrWhiteSpace()) { throw new ArgumentNullException("field"); } return GetMember(member, delegate(MemberMapper p) { p.FieldName = field; }); } public EntityBuilder<T> Id<K>(Expression<Func<T, K>> member, bool autoId = true) { return GetMember(member, delegate(MemberMapper p) { MemberMapper memberMapper = _entity.Members.FirstOrDefault((MemberMapper x) => x.FieldName == "_id"); if (memberMapper != null) { memberMapper.FieldName = _mapper.ResolveFieldName(memberMapper.MemberName); memberMapper.AutoId = false; } p.FieldName = "_id"; p.AutoId = autoId; }); } public EntityBuilder<T> Ctor(Func<BsonDocument, T> createInstance) { _entity.CreateInstance = (BsonDocument v) => createInstance(v); return this; } public EntityBuilder<T> DbRef<K>(Expression<Func<T, K>> member, string collection = null) { return GetMember(member, delegate(MemberMapper p) { BsonMapper.RegisterDbRef(_mapper, p, _typeNameBinder, collection ?? _mapper.ResolveCollectionName(typeof(K))); }); } private EntityBuilder<T> GetMember<TK, K>(Expression<Func<TK, K>> member, Action<MemberMapper> action) { if (member == null) { throw new ArgumentNullException("member"); } MemberMapper member2 = _entity.GetMember(member); if (member2 == null) { throw new ArgumentNullException("Member '" + member.GetPath() + "' not found in type '" + _entity.ForType.Name + "' (use IncludeFields in BsonMapper)"); } action(member2); return this; } } public class EntityMapper { public Type ForType { get; } public List<MemberMapper> Members { get; } = new List<MemberMapper>(); public MemberMapper Id => Members.SingleOrDefault((MemberMapper x) => x.FieldName == "_id"); public CreateObject CreateInstance { get; set; } public EntityMapper(Type forType) { ForType = forType; } public MemberMapper GetMember(Expression expr) { return Members.FirstOrDefault((MemberMapper x) => x.MemberName == expr.GetPath()); } } internal class LinqExpressionVisitor : ExpressionVisitor { private static readonly Dictionary<Type, ITypeResolver> _resolver = new Dictionary<Type, ITypeResolver> { [typeof(BsonValue)] = new BsonValueResolver(), [typeof(BsonArray)] = new BsonValueResolver(), [typeof(BsonDocument)] = new BsonValueResolver(), [typeof(Convert)] = new ConvertResolver(), [typeof(DateTime)] = new DateTimeResolver(), [typeof(int)] = new NumberResolver("INT32"), [typeof(long)] = new NumberResolver("INT64"), [typeof(decimal)] = new NumberResolver("DECIMAL"), [typeof(double)] = new NumberResolver("DOUBLE"), [typeof(ICollection)] = new ICollectionResolver(), [typeof(Enumerable)] = new EnumerableResolver(), [typeof(Guid)] = new GuidResolver(), [typeof(Math)] = new MathResolver(), [typeof(Regex)] = new RegexResolver(), [typeof(ObjectId)] = new ObjectIdResolver(), [typeof(string)] = new StringResolver(), [typeof(Nullable)] = new NullableResolver() }; private readonly BsonMapper _mapper; private readonly Expression _expr; private readonly ParameterExpression _rootParameter; private readonly BsonDocument _parameters = new BsonDocument(); private int _paramIndex; private Type _dbRefType; private readonly StringBuilder _builder = new StringBuilder(); private readonly Stack<Expression> _nodes = new Stack<Expression>(); public LinqExpressionVisitor(BsonMapper mapper, Expression expr) { _mapper = mapper; _expr = expr; if (expr is LambdaExpression lambdaExpression) { _rootParameter = lambdaExpression.Parameters.First(); return; } throw new NotSupportedException("Expression " + expr.ToString() + " must be a lambda expression"); } public BsonExpression Resolve(bool predicate) { Visit(_expr); Constants.ENSURE(_nodes.Count == 0, "node stack must be empty when finish expression resolve"); string text = _builder.ToString(); try { BsonExpression bsonExpression = BsonExpression.Create(text, _parameters); if (predicate && (bsonExpression.Type == BsonExpressionType.Path || bsonExpression.Type == BsonExpressionType.Call || bsonExpression.Type == BsonExpressionType.Parameter)) { text = "(" + text + " = true)"; bsonExpression = BsonExpression.Create(text, _parameters); } return bsonExpression; } catch (Exception innerException) { throw new NotSupportedException("Invalid BsonExpression when converted from Linq expression: " + _expr.ToString() + " - `" + text + "`", innerException); } } protected override Expression VisitLambda<T>(Expression<T> node) { Expression result = base.VisitLambda(node); _builder.Length--; return result; } protected override Expression VisitInvocation(InvocationExpression node) { Expression result = base.VisitInvocation(node); _builder.Length--; return result; } protected override Expression VisitParameter(ParameterExpression node) { _builder.Append(_rootParameter.Equals(node) ? "$" : "@"); return base.VisitParameter(node); } protected override Expression VisitMember(MemberExpression node) { bool flag = ParameterExpressionVisitor.Test(node); MemberInfo member = node.Member; if (TryGetResolver(member.DeclaringType, out var typeResolver)) { string text = typeResolver.ResolveMember(member); if (text == null) { throw new NotSupportedException("Member " + member.Name + " are not support in " + member.DeclaringType.Name + " when convert to BsonExpression (" + node.ToString() + ")."); } ResolvePattern(text, node.Expression, new Expression[0]); } else if (node.Expression != null) { _nodes.Push(node); base.Visit(node.Expression); if (flag) { string value = ResolveMember(member); _builder.Append(value); } } else { object value2 = Evaluate(node); base.Visit(Expression.Constant(value2)); } if (_nodes.Count > 0) { _nodes.Pop(); } return node; } protected override Expression VisitMethodCall(MethodCallExpression node) { if (IsMethodIndexEval(node, out var obj, out var idx)) { Visit(obj); object obj2 = Evaluate(idx, typeof(string), typeof(int)); if (obj2 is string) { _builder.Append("."); _builder.Append($"['{obj2}']"); } else { _builder.Append($"[{obj2}]"); } return node; } if (!TryGetResolver(node.Method.DeclaringType, out var typeResolver)) { if (ParameterExpressionVisitor.Test(node)) { throw new NotSupportedException("Method " + node.Method.Name + " not available to convert to BsonExpression (" + node.ToString() + ")."); } object value = Evaluate(node); base.Visit(Expression.Constant(value)); return node; } string text = typeResolver.ResolveMethod(node.Method); if (text == null) { throw new NotSupportedException("Method " + Reflection.MethodName(node.Method) + " in " + node.Method.DeclaringType.Name + " are not supported when convert to BsonExpression (" + node.ToString() + ")."); } ResolvePattern(text, node.Object, node.Arguments); return node; } protected override Expression VisitConstant(ConstantExpression node) { object value = node.Value; while (_nodes.Count > 0 && _nodes.Peek() is MemberExpression memberExpression) { if (memberExpression.Member is FieldInfo fieldInfo) { value = fieldInfo.GetValue(value); } else if (memberExpression.Member is PropertyInfo propertyInfo) { value = propertyInfo.GetValue(value); } _nodes.Pop(); } Constants.ENSURE(_nodes.Count == 0, "counter stack must be zero to eval all properties/field over object"); string text = "p" + _paramIndex++; _builder.AppendFormat("@" + text); Type type = value?.GetType(); BsonValue value2 = ((type == null) ? BsonValue.Null : ((type == typeof(string)) ? new BsonValue((string)value) : _mapper.Serialize(value.GetType(), value))); _parameters[text] = value2; return node; } protected override Expression VisitUnary(UnaryExpression node) { if (node.NodeType == ExpressionType.Not) { if (node.Operand.NodeType == ExpressionType.MemberAccess) { _builder.Append("("); Visit(node.Operand); _builder.Append(" = false)"); } else { _builder.Append("("); Visit(node.Operand); _builder.Append(")"); _builder.Append(" = false"); } } else if (node.NodeType == ExpressionType.Convert) { Type fromType = node.Operand.Type; Type type = node.Type; if ((fromType == typeof(double) || fromType == typeof(decimal)) && (type == typeof(int) || type == typeof(long))) { string methodName = "To" + type.Name.ToString(); MethodInfo methodInfo = (from x in typeof(Convert).GetMethods() where x.Name == methodName where x.GetParameters().Length == 1 && x.GetParameters().Any((ParameterInfo z) => z.ParameterType == fromType) select x).FirstOrDefault(); if (methodInfo == null) { throw new NotSupportedException("Cast from " + fromType.Name + " are not supported when convert to BsonExpression"); } MethodCallExpression node2 = Expression.Call(null, methodInfo, node.Operand); VisitMethodCall(node2); } else { base.VisitUnary(node); } } else if (node.NodeType == ExpressionType.ArrayLength) { _builder.Append("LENGTH("); Visit(node.Operand); _builder.Append(")"); } else { base.VisitUnary(node); } return node; } protected override Expression VisitNew(NewExpression node) { if (node.Members == null) { if (!TryGetResolver(node.Type, out var typeResolver)) { throw new NotSupportedException($"New instance are not supported for {node.Type} when convert to BsonExpression ({node.ToString()})."); } string text = typeResolver.ResolveCtor(node.Constructor); if (text == null) { throw new NotSupportedException("Constructor for " + node.Type.Name + " are not supported when convert to BsonExpression (" + node.ToString() + ")."); } ResolvePattern(text, null, node.Arguments); } else { _builder.Append("{ "); for (int i = 0; i < node.Members.Count; i++) { MemberInfo memberInfo = node.Members[i]; _builder.Append((i > 0) ? ", " : ""); _builder.AppendFormat("'{0}': ", memberInfo.Name); Visit(node.Arguments[i]); } _builder.Append(" }"); } return node; } protected override Expression VisitMemberInit(MemberInitExpression node) { if (node.NewExpression.Constructor.GetParameters().Length != 0) { throw new NotSupportedException($"New instance of {node.Type} are not supported because contains ctor with parameter. Try use only property initializers: `new {node.Type.Name} {{ PropA = 1, PropB == \"John\" }}`."); } _builder.Append("{"); for (int i = 0; i < node.Bindings.Count; i++) { MemberAssignment memberAssignment = node.Bindings[i] as MemberAssignment; string text = ResolveMember(memberAssignment.Member); _builder.Append((i > 0) ? ", " : ""); _builder.Append(text.Substring(1)); _builder.Append(":"); Visit(memberAssignment.Expression); } _builder.Append("}"); return node; } protected override Expression VisitNewArray(NewArrayExpression node) { _builder.Append("[ "); for (int i = 0; i < node.Expressions.Count; i++) { _builder.Append((i > 0) ? ", " : ""); Visit(node.Expressions[i]); } _builder.Append(" ]"); return node; } protected override Expression VisitBinary(BinaryExpression node) { bool ensurePredicate = node.NodeType == ExpressionType.AndAlso || node.NodeType == ExpressionType.OrElse; if (node.NodeType == ExpressionType.Coalesce) { return VisitCoalesce(node); } if (node.NodeType == ExpressionType.ArrayIndex) { return VisitArrayIndex(node); } string @operator = GetOperator(node.NodeType); _builder.Append("("); VisitAsPredicate(node.Left, ensurePredicate); _builder.Append(@operator); if (!_mapper.EnumAsInteger && node.Left.NodeType == ExpressionType.Convert && node.Left is UnaryExpression unaryExpression && unaryExpression.Operand.Type.GetTypeInfo().IsEnum && unaryExpression.Type == typeof(int)) { VisitAsPredicate(Expression.Constant(Enum.GetName(unaryExpression.Operand.Type, Evaluate(node.Right))), ensurePredicate); } else { VisitAsPredicate(node.Right, ensurePredicate); } _builder.Append(")"); return node; } protected override Expression VisitConditional(ConditionalExpression node) { _builder.Append("IIF("); Visit(node.Test); _builder.Append(", "); Visit(node.IfTrue); _builder.Append(", "); Visit(node.IfFalse); _builder.Append(")"); return node; } private Expression VisitCoalesce(BinaryExpression node) { _builder.Append("COALESCE("); Visit(node.Left); _builder.Append(", "); Visit(node.Right); _builder.Append(")"); return node; } private Expression VisitArrayIndex(BinaryExpression node) { Visit(node.Left); _builder.Append("["); object value = Evaluate(node.Right, typeof(int)); _builder.Append(value); _builder.Append("]"); return node; } private void ResolvePattern(string pattern, Expression obj, IList<Expression> args) { Tokenizer tokenizer = new Tokenizer(pattern); while (!tokenizer.EOF) { Token token = tokenizer.ReadToken(eatWhitespace: false); if (token.Type == TokenType.Hashtag) { Visit(obj); } else if (token.Type == TokenType.At && tokenizer.LookAhead(eatWhitespace: false).Type == TokenType.Int) { int index = Convert.ToInt32(tokenizer.ReadToken(eatWhitespace: false).Expect(TokenType.Int).Value); Visit(args[index]); } else if (token.Type == TokenType.Percent) { VisitEnumerablePredicate(args[1] as LambdaExpression); } else { _builder.Append((token.Type == TokenType.String) ? ("'" + token.Value + "'") : token.Value); } } } private void VisitEnumerablePredicate(LambdaExpression lambda) { Expression body = lambda.Body; if (body is BinaryExpression binaryExpression) { if (binaryExpression.Left.NodeType != ExpressionType.Parameter) { throw new LiteException(0, "Any/All requires simple parameter on left side. Eg: `x => x.Phones.Select(p => p.Number).Any(n => n > 5)`"); } string @operator = GetOperator(binaryExpression.NodeType); _builder.Append(@operator); VisitAsPredicate(binaryExpression.Right, ensurePredicate: false); return; } if (body is MethodCallExpression methodCallExpression) { if (methodCallExpression.Object.NodeType != ExpressionType.Parameter) { throw new NotSupportedException("Any/All requires simple parameter on left side. Eg: `x.Customers.Select(c => c.Name).Any(n => n.StartsWith('J'))`"); } if (!TryGetResolver(methodCallExpression.Method.DeclaringType, out var typeResolver)) { throw new NotSupportedException("Method " + methodCallExpression.Method.Name + " not available to convert to BsonExpression inside Any/All call."); } string text = typeResolver.ResolveMethod(methodCallExpression.Method); if (text == null || !text.StartsWith("#")) { throw new NotSupportedException("Method " + methodCallExpression.Method.Name + " not available to convert to BsonExpression inside Any/All call."); } ResolvePattern(text.Substring(1), methodCallExpression.Object, methodCallExpression.Arguments); return; } throw new LiteException(0, "When using Any/All method test do only simple predicate variable. Eg: `x => x.Phones.Select(p => p.Number).Any(n => n > 5)`"); } private string GetOperator(ExpressionType nodeType) { return nodeType switch { ExpressionType.Add => " + ", ExpressionType.Multiply => " * ", ExpressionType.Subtract => " - ", ExpressionType.Divide => " / ", ExpressionType.Equal => " = ", ExpressionType.NotEqual => " != ", ExpressionType.GreaterThan => " > ", ExpressionType.GreaterThanOrEqual => " >= ", ExpressionType.LessThan => " < ", ExpressionType.LessThanOrEqual => " <= ", ExpressionType.And => " AND ", ExpressionType.AndAlso => " AND ", ExpressionType.Or => " OR ", ExpressionType.OrElse => " OR ", _ => throw new NotSupportedException($"Operator not supported {nodeType}"), }; } private string ResolveMember(MemberInfo member) { string name = member.Name; bool flag = _dbRefType != null && member.DeclaringType.IsAssignableFrom(_dbRefType); MemberMapper memberMapper = _mapper.GetEntityMapper(member.DeclaringType).Members.FirstOrDefault((MemberMapper x) => x.MemberName == name); if (memberMapper == null) { throw new NotSupportedException($"Member {name} not found on BsonMapper for type {member.DeclaringType}."); } _dbRefType = (memberMapper.IsDbRef ? memberMapper.UnderlyingType : null); return "." + ((flag && memberMapper.FieldName == "_id") ? "$id" : memberMapper.FieldName); } private bool IsMethodIndexEval(MethodCallExpression node, out Expression obj, out Expression idx) { MethodInfo method = node.Method; _ = method.DeclaringType; ParameterInfo[] parameters = method.GetParameters(); if (method.Name == "get_Item" && parameters.Length == 1 && (parameters[0].ParameterType == typeof(int) || parameters[0].ParameterType == typeof(string))) { obj = node.Object; idx = node.Arguments[0]; return true; } obj = null; idx = null; return false; } private void VisitAsPredicate(Expression expr, bool ensurePredicate) { ensurePredicate = ensurePredicate && (expr.NodeType == ExpressionType.MemberAccess || expr.NodeType == ExpressionType.Call || expr.NodeType == ExpressionType.Invoke || expr.NodeType == ExpressionType.Constant); if (ensurePredicate) { _builder.Append("("); _builder.Append("("); base.Visit(expr); _builder.Append(")"); _builder.Append(" = true)"); } else { base.Visit(expr); } } private object Evaluate(Expression expr, params Type[] validTypes) { object value = null; if (expr.NodeType == ExpressionType.Constant) { ConstantExpression constantExpression = (ConstantExpression)expr; value = constantExpression.Value; } else { Delegate @delegate = Expression.Lambda(expr).Compile(); value = @delegate.DynamicInvoke(); } if (validTypes.Length != 0 && value == null) { throw new NotSupportedException($"Expression {expr} can't return null value"); } if (validTypes.Length != 0 && !validTypes.Any((Type x) => x == value.GetType())) { throw new NotSupportedException(string.Format("Expression {0} must return on of this types: {1}", expr, string.Join(", ", validTypes.Select((Type x) => "`" + x.Name + "`")))); } return value; } private bool TryGetResolver(Type declaringType, out ITypeResolver typeResolver) { bool num = Reflection.IsCollection(declaringType); bool flag = Reflection.IsEnumerable(declaringType); bool flag2 = Reflection.IsNullable(declaringType); Type key = (num ? typeof(ICollection) : (flag ? typeof(Enumerable) : (flag2 ? typeof(Nullable) : declaringType))); return _resolver.TryGetValue(key, out typeResolver); } } internal class ParameterExpressionVisitor : ExpressionVisitor { public bool IsParameter { get; private set; } protected override Expression VisitParameter(ParameterExpression node) { IsParameter = true; return base.VisitParameter(node); } public static bool Test(Expression node) { ParameterExpressionVisitor parameterExpressionVisitor = new ParameterExpressionVisitor(); parameterExpressionVisitor.Visit(node); return parameterExpressionVisitor.IsParameter; } } internal class BsonValueResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { return null; } public string ResolveMember(MemberInfo member) { switch (member.Name) { case "AsInt32": case "AsInt64": case "AsArray": case "AsDateTime": case "AsDocument": case "AsObjectId": case "AsString": case "AsBinary": case "AsDouble": case "AsBoolean": case "AsDecimal": case "AsGuid": return "#"; case "IsNull": return "IS_NULL(#)"; case "IsArray": return "IS_ARRAY(#)"; case "IsDocument": return "IS_DOCUMENT(#)"; case "IsInt32": return "IS_INT32(#)"; case "IsInt64": return "IS_INT64(#)"; case "IsDouble": return "IS_DOUBLE(#)"; case "IsDecimal": return "IS_DECIMAL(#)"; case "IsNumber": return "IS_NUMBER(#)"; case "IsBinary": return "IS_BINARY(#)"; case "IsBoolean": return "IS_BOOLEAN(#)"; case "IsString": return "IS_STRING(#)"; case "IsObjectId": return "IS_OBJECTID(#)"; case "IsGuid": return "IS_GUID(#)"; case "IsDateTime": return "IS_DATETIME(#)"; case "IsMinValue": return "IS_MINVALUE(#)"; case "IsMaxValue": return "IS_MAXVALUE(#)"; default: return null; } } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class ConvertResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { return method.Name switch { "ToInt32" => "INT32(@0)", "ToInt64" => "INT64(@0)", "ToDouble" => "DOUBLE(@0)", "ToDecimal" => "DECIMAL(@0)", "ToDateTime" => "DATE(@0)", "FromBase64String" => "BINARY(@0)", "ToBoolean" => "BOOL(@0)", "ToString" => "STRING(@0)", _ => null, }; } public string ResolveMember(MemberInfo member) { return null; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class DateTimeResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { switch (method.Name) { case "AddYears": return "DATEADD('y', @0, #)"; case "AddMonths": return "DATEADD('M', @0, #)"; case "AddDays": return "DATEADD('d', @0, #)"; case "AddHours": return "DATEADD('h', @0, #)"; case "AddMinutes": return "DATEADD('m', @0, #)"; case "AddSeconds": return "DATEADD('s', @0, #)"; case "ToString": { ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length == 0) { return "STRING(#)"; } if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string)) { return "FORMAT(#, @0)"; } break; } case "ToUniversalTime": return "TO_UTC(#)"; case "Parse": return "DATETIME(@0)"; case "Equals": return "# = @0"; } return null; } public string ResolveMember(MemberInfo member) { return member.Name switch { "Now" => "NOW()", "UtcNow" => "NOW_UTC()", "Today" => "TODAY()", "Year" => "YEAR(#)", "Month" => "MONTH(#)", "Day" => "DAY(#)", "Hour" => "HOUR(#)", "Minute" => "MINUTE(#)", "Second" => "SECOND(#)", "Date" => "DATETIME(YEAR(#), MONTH(#), DAY(#))", "ToLocalTime" => "TO_LOCAL(#)", "ToUniversalTime" => "TO_UTC(#)", _ => null, }; } public string ResolveCtor(ConstructorInfo ctor) { ParameterInfo[] parameters = ctor.GetParameters(); if (parameters.Length == 3 && parameters[0].ParameterType == typeof(int) && parameters[1].ParameterType == typeof(int) && parameters[2].ParameterType == typeof(int)) { return "DATETIME(@0, @1, @2)"; } return null; } } internal class EnumerableResolver : ITypeResolver { public virtual string ResolveMethod(MethodInfo method) { switch (Reflection.MethodName(method, 1)) { case "AsEnumerable()": return "@0[*]"; case "get_Item(int)": return "#[@0]"; case "ElementAt(int)": return "@0[@1]"; case "Single()": case "First()": case "SingleOrDefault()": case "FirstOrDefault()": return "@0[0]"; case "Last()": case "LastOrDefault()": return "@0[-1]"; case "Single(Func<T,TResult>)": case "First(Func<T,TResult>)": case "SingleOrDefault(Func<T,TResult>)": case "FirstOrDefault(Func<T,TResult>)": return "FIRST(FILTER(@0 => @1))"; case "Last(Func<T,TResult>)": case "LastOrDefault(Func<T,TResult>)": return "LAST(FILTER(@0 => @1))"; case "Where(Func<T,TResult>)": return "FILTER(@0 => @1)"; case "Select(Func<T,TResult>)": return "MAP(@0 => @1)"; case "Count()": return "COUNT(@0)"; case "Sum()": return "SUM(@0)"; case "Average()": return "AVG(@0)"; case "Max()": return "MAX(@0)"; case "Min()": return "MIN(@0)"; case "Count(Func<T,TResult>)": return "COUNT(FILTER(@0 => @1))"; case "Sum(Func<T,TResult>)": return "SUM(MAP(@0 => @1))"; case "Average(Func<T,TResult>)": return "AVG(MAP(@0 => @1))"; case "Max(Func<T,TResult>)": return "MAX(MAP(@0 => @1))"; case "Min(Func<T,TResult>)": return "MIN(MAP(@0 => @1))"; case "ToList()": case "ToArray()": return "ARRAY(@0)"; case "Any(Func<T,TResult>)": return "@0 ANY %"; case "All(Func<T,TResult>)": return "@0 ALL %"; case "Any()": return "COUNT(@0) > 0"; default: if (method.Name == "Contains") { return "@0 ANY = @1"; } return null; } } public virtual string ResolveMember(MemberInfo member) { string name = member.Name; if (!(name == "Length")) { if (name == "Count") { return "COUNT(#)"; } return null; } return "LENGTH(#)"; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class GuidResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { return method.Name switch { "ToString" => "STRING(#)", "NewGuid" => "GUID()", "Parse" => "GUID(@0)", "TryParse" => throw new NotSupportedException("There is no TryParse translate. Use Guid.Parse()"), "Equals" => "# = @0", _ => null, }; } public string ResolveMember(MemberInfo member) { if (member.Name == "Empty") { return "GUID('00000000-0000-0000-0000-000000000000')"; } return null; } public string ResolveCtor(ConstructorInfo ctor) { ParameterInfo[] parameters = ctor.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string)) { return "GUID(@0)"; } return null; } } internal class ICollectionResolver : EnumerableResolver { public override string ResolveMethod(MethodInfo method) { if (method.Name == "Contains") { return "# ANY = @0"; } return base.ResolveMethod(method); } } internal interface ITypeResolver { string ResolveMethod(MethodInfo method); string ResolveMember(MemberInfo member); string ResolveCtor(ConstructorInfo ctor); } internal class MathResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { int num = method.GetParameters().Length; switch (method.Name) { case "Abs": return "ABS(@0)"; case "Pow": return "POW(@0, @1)"; case "Round": if (num != 2) { throw new ArgumentOutOfRangeException("Method Round need 2 arguments when convert to BsonExpression"); } return "ROUND(@0, @1)"; default: return null; } } public string ResolveMember(MemberInfo member) { return null; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class NullableResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { return null; } public string ResolveMember(MemberInfo member) { string name = member.Name; if (!(name == "HasValue")) { if (name == "Value") { return "#"; } return null; } return "(IS_NULL(#) = false)"; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class NumberResolver : ITypeResolver { private readonly string _parseMethod; public NumberResolver(string parseMethod) { _parseMethod = parseMethod; } public string ResolveMethod(MethodInfo method) { switch (method.Name) { case "ToString": { ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length == 0) { return "STRING(#)"; } if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string)) { return "FORMAT(#, @0)"; } break; } case "Parse": return _parseMethod + "(@0)"; case "Equals": return "# = @0"; } return null; } public string ResolveMember(MemberInfo member) { return null; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class ObjectIdResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { string name = method.Name; if (!(name == "ToString")) { if (name == "Equals") { return "# = @0"; } return null; } return "STRING(#)"; } public string ResolveMember(MemberInfo member) { string name = member.Name; if (!(name == "Empty")) { if (name == "CreationTime") { return "OID_CREATIONTIME(#)"; } return null; } return "OBJECTID('000000000000000000000000')"; } public string ResolveCtor(ConstructorInfo ctor) { ParameterInfo[] parameters = ctor.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string)) { return "OBJECTID(@0)"; } return null; } } internal class RegexResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { string name = method.Name; if (!(name == "Split")) { if (name == "IsMatch") { return "IS_MATCH(@0, @1)"; } return null; } return "SPLIT(@0, @1, true)"; } public string ResolveMember(MemberInfo member) { return null; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class StringResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { int num = method.GetParameters().Length; switch (method.Name) { case "Count": return "LENGTH(#)"; case "Trim": return "TRIM(#)"; case "TrimStart": return "LTRIM(#)"; case "TrimEnd": return "RTRIM(#)"; case "ToUpper": return "UPPER(#)"; case "ToUpperInvariant": return "UPPER(#)"; case "ToLower": return "LOWER(#)"; case "ToLowerInvariant": return "LOWER(#)"; case "Replace": return "REPLACE(#, @0, @1)"; case "PadLeft": return "LPAD(#, @0, @1)"; case "RightLeft": return "RPAD(#, @0, @1)"; case "IndexOf": if (num != 1) { return "INDEXOF(#, @0, @1)"; } return "INDEXOF(#, @0)"; case "Substring": if (num != 1) { return "SUBSTRING(#, @0, @1)"; } return "SUBSTRING(#, @0)"; case "StartsWith": return "# LIKE (@0 + '%')"; case "Contains": return "# LIKE ('%' + @0 + '%')"; case "EndsWith": return "# LIKE ('%' + @0)"; case "ToString": return "#"; case "Equals": return "# = @0"; case "IsNullOrEmpty": return "(LENGTH(@0) = 0)"; case "IsNullOrWhiteSpace": return "(LENGTH(TRIM(@0)) = 0)"; case "Format": throw new NotImplementedException(); case "Join": throw new NotImplementedException(); default: return null; } } public string ResolveMember(MemberInfo member) { string name = member.Name; if (!(name == "Length")) { if (name == "Empty") { return "''"; } return null; } return "LENGTH(#)"; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } public class MemberMapper { public bool AutoId { get; set; } public string MemberName { get; set; } public Type DataType { get; set; } public string FieldName { get; set; } public GenericGetter Getter { get; set; } public GenericSetter Setter { get; set; } public Func<object, BsonMapper, BsonValue> Serialize { get; set; } public Func<BsonValue, BsonMapper, object> Deserialize { get; set; } public bool IsDbRef { get; set; } public bool IsEnumerable { get; set; } public Type UnderlyingType { get; set; } public bool IsIgnore { get; set; } } public delegate object CreateObject(BsonDocument value); public delegate void GenericSetter(object target, object value); public delegate object GenericGetter(object obj); internal class Reflection { private static readonly Dictionary<Type, CreateObject> _cacheCtor = new Dictionary<Type, CreateObject>(); public static readonly Dictionary<Type, PropertyInfo> ConvertType = new Dictionary<Type, PropertyInfo> { [typeof(DateTime)] = typeof(BsonValue).GetProperty("AsDateTime"), [typeof(decimal)] = typeof(BsonValue).GetProperty("AsDecimal"), [typeof(double)] = typeof(BsonValue).GetProperty("AsDouble"), [typeof(long)] = typeof(BsonValue).GetProperty("AsInt64"), [typeof(int)] = typeof(BsonValue).GetProperty("AsInt32"), [typeof(bool)] = typeof(BsonValue).GetProperty("AsBoolean"), [typeof(byte[])] = typeof(BsonValue).GetProperty("AsBinary"), [typeof(BsonDocument)] = typeof(BsonValue).GetProperty("AsDocument"), [ty