actually now its usable
This commit is contained in:
25
Kits/Commands.cs
Normal file
25
Kits/Commands.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Sandbox.Game.World;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.Commands;
|
||||||
|
using Torch.Commands.Permissions;
|
||||||
|
using VRage.Game.ModAPI;
|
||||||
|
namespace Kits;
|
||||||
|
|
||||||
|
public class Commands : CommandModule
|
||||||
|
{
|
||||||
|
[Command("kit")]
|
||||||
|
[Permission(MyPromoteLevel.None)]
|
||||||
|
public void GetKit(string name)
|
||||||
|
{
|
||||||
|
var manager = Context.Torch.CurrentSession.Managers.GetManager<IKitManager>();
|
||||||
|
var player = (MyPlayer)Context.Player;
|
||||||
|
|
||||||
|
if (!manager.CanGiveKit(player, name, out var reason))
|
||||||
|
{
|
||||||
|
Context.Respond(reason, "Error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
manager.GiveKit(player, player.Character.GetInventoryBase(), name);
|
||||||
|
Context.Respond($"You have got kit {name}");
|
||||||
|
}
|
||||||
|
}
|
20
Kits/Config.cs
Normal file
20
Kits/Config.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Xml.Schema;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
using Kits.Views;
|
||||||
|
using Torch;
|
||||||
|
using Torch.Views;
|
||||||
|
|
||||||
|
namespace Kits;
|
||||||
|
|
||||||
|
[XmlRoot()]
|
||||||
|
public class Config : ViewModel
|
||||||
|
{
|
||||||
|
[Display(Name = "Kits", EditorType = typeof(EditButton))]
|
||||||
|
[XmlArrayItem("Kit")]
|
||||||
|
public ObservableCollection<KitViewModel> Kits { get; set; } = new();
|
||||||
|
|
||||||
|
[XmlAttribute(Form = XmlSchemaForm.Qualified, Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
public string noNamespaceSchemaLocation = "Kits.v1.0.6.xsd";
|
||||||
|
}
|
3
Kits/FodyWeavers.xml
Normal file
3
Kits/FodyWeavers.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||||
|
<PropertyChanged />
|
||||||
|
</Weavers>
|
178
Kits/KitManager.cs
Normal file
178
Kits/KitManager.cs
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
using heh;
|
||||||
|
using net.luckperms.api;
|
||||||
|
using NLog;
|
||||||
|
using PetaPoco;
|
||||||
|
using Sandbox.Game;
|
||||||
|
using Sandbox.Game.Entities;
|
||||||
|
using Sandbox.Game.GameSystems.BankingAndCurrency;
|
||||||
|
using Sandbox.Game.Multiplayer;
|
||||||
|
using Sandbox.Game.World;
|
||||||
|
using Torch.API;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.Managers;
|
||||||
|
using Torch.Server.Managers;
|
||||||
|
using VRage;
|
||||||
|
using VRage.Game.Entity;
|
||||||
|
using VRage.Library.Utils;
|
||||||
|
using VRage.ObjectBuilders;
|
||||||
|
namespace Kits;
|
||||||
|
|
||||||
|
public class KitManager : Manager, IKitManager
|
||||||
|
{
|
||||||
|
private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private readonly Config _config;
|
||||||
|
|
||||||
|
[Dependency]
|
||||||
|
private readonly IDbManager _dbManager = null!;
|
||||||
|
|
||||||
|
[Dependency]
|
||||||
|
private readonly MultiplayerManagerDedicated _multiplayerManager = null!;
|
||||||
|
|
||||||
|
private IDatabase _db = null!;
|
||||||
|
|
||||||
|
public KitManager(ITorchBase torchInstance, Config config) : base(torchInstance)
|
||||||
|
{
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Attach()
|
||||||
|
{
|
||||||
|
base.Attach();
|
||||||
|
_db = _dbManager.Create("kits");
|
||||||
|
MyVisualScriptLogicProvider.RespawnShipSpawned += RespawnShipSpawned;
|
||||||
|
}
|
||||||
|
private void RespawnShipSpawned(long shipEntityId, long playerId, string respawnShipPrefabName)
|
||||||
|
{
|
||||||
|
if (!MyEntities.TryGetEntityById(shipEntityId, out MyCubeGrid grid) ||
|
||||||
|
!Sync.Players.TryGetPlayerId(playerId, out var playerClientId) ||
|
||||||
|
Sync.Players.GetPlayerById(playerClientId) is not { } player)
|
||||||
|
return;
|
||||||
|
foreach (var kit in _config.Kits.Where(b => CanGiveRespawnKit(player, b, respawnShipPrefabName, out _)))
|
||||||
|
{
|
||||||
|
GiveKit(player, grid.GetFatBlocks().First(b => b is MyCargoContainer or MyCockpit).GetInventoryBase(), kit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GiveKit(MyPlayer player, MyInventoryBase inventory, string kitName)
|
||||||
|
{
|
||||||
|
GiveKit(player, inventory, GetKit(kitName));
|
||||||
|
}
|
||||||
|
public bool CanGiveKit(MyPlayer player, string kitName, out string reason)
|
||||||
|
{
|
||||||
|
return CanGiveKit(player, GetKit(kitName), out reason);
|
||||||
|
}
|
||||||
|
public bool CanGiveRespawnKit(MyPlayer player, string kitName, string respawnName, out string reason)
|
||||||
|
{
|
||||||
|
return CanGiveRespawnKit(player, GetKit(kitName), respawnName, out reason);
|
||||||
|
}
|
||||||
|
public KitViewModel GetKit(string kitName)
|
||||||
|
{
|
||||||
|
return _config.Kits.First(b => b.Name == kitName);
|
||||||
|
}
|
||||||
|
public bool TryGetKit(string kitName, out KitViewModel? kit)
|
||||||
|
{
|
||||||
|
kit = _config.Kits.FirstOrDefault(b => b.Name == kitName);
|
||||||
|
return kit is not null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GiveKit(MyPlayer player, MyInventoryBase inventory, KitViewModel kit)
|
||||||
|
{
|
||||||
|
if (kit.UseCooldownMinutes > 0)
|
||||||
|
{
|
||||||
|
CheckTable();
|
||||||
|
_db.Insert(new PlayerCooldown {Id = player.Id.SteamId.ToString(), KitName = kit.Name, LastUsed = DateTime.Now});
|
||||||
|
}
|
||||||
|
|
||||||
|
MyBankingSystem.ChangeBalance(player.Identity.IdentityId, kit.UseCost);
|
||||||
|
|
||||||
|
foreach (var item in kit.Items.Where(b => b.Probability >= 1 || b.Probability < MyRandom.Instance.GetRandomFloat(0, 1)))
|
||||||
|
{
|
||||||
|
inventory.AddItems((MyFixedPoint)item.Amount, MyObjectBuilderSerializer.CreateNewObject(item.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Info($"Given kit {kit.Name} to {player.DisplayName} ({player.Id.SteamId})");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanGiveKit(MyPlayer player, KitViewModel kit, out string reason)
|
||||||
|
{
|
||||||
|
reason = string.Empty;
|
||||||
|
|
||||||
|
var level = MySession.Static.GetUserPromoteLevel(player.Id.SteamId);
|
||||||
|
if (level < kit.RequiredPromoteLevel ||
|
||||||
|
!string.IsNullOrEmpty(kit.LpPermission))
|
||||||
|
{
|
||||||
|
var api = LuckPermsProvider.get();
|
||||||
|
var torchPlayer = _multiplayerManager.Players[player.Id.SteamId];
|
||||||
|
|
||||||
|
if (!api.getPlayerAdapter(typeof(IPlayer)).getPermissionData(torchPlayer).checkPermission(kit.LpPermission).asBoolean())
|
||||||
|
{
|
||||||
|
reason = "Not enough rights to acquire this";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kit.UseCost > 0 && kit.UseCost > MyBankingSystem.GetBalance(player.Identity.IdentityId))
|
||||||
|
{
|
||||||
|
reason = "Not enough money to acquire this";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kit.UseCooldownMinutes <= 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var sql = Sql.Builder.Where("id = @0", player.Id.SteamId).Append("AND kit_name = @0", kit.Name);
|
||||||
|
CheckTable();
|
||||||
|
var playerCooldown = _db.SingleOrDefault<PlayerCooldown>(sql);
|
||||||
|
var cooldown = DateTime.Now - playerCooldown?.LastUsed;
|
||||||
|
|
||||||
|
if (cooldown is null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (cooldown > TimeSpan.FromMinutes(kit.UseCooldownMinutes))
|
||||||
|
{
|
||||||
|
_db.Delete<PlayerCooldown>(sql);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
reason = $"Next use available in {TimeSpan.FromMinutes(kit.UseCooldownMinutes) - cooldown:dd\\.hh\\:mm\\:ss}";
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
public bool CanGiveRespawnKit(MyPlayer player, KitViewModel kit, string respawnName, out string reason)
|
||||||
|
{
|
||||||
|
reason = "Invalid respawn name";
|
||||||
|
return kit.RespawnPodWildcards.Any(respawnName.Glob) && CanGiveKit(player, kit, out reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckTable()
|
||||||
|
{
|
||||||
|
_db.Execute("create table if not exists cooldown (uid INTEGER PRIMARY KEY AUTOINCREMENT, id TEXT NOT NULL, kit_name TEXT NOT NULL, last_used DATETIME NOT NULL)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TableName("cooldown")]
|
||||||
|
[PrimaryKey(nameof(Uid), AutoIncrement = true)]
|
||||||
|
public class PlayerCooldown
|
||||||
|
{
|
||||||
|
[Column]
|
||||||
|
public long Uid { get; set; }
|
||||||
|
[Column]
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
[Column]
|
||||||
|
public string KitName { get; set; } = string.Empty;
|
||||||
|
[Column]
|
||||||
|
public DateTime LastUsed { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IKitManager : IManager
|
||||||
|
{
|
||||||
|
void GiveKit(MyPlayer player, MyInventoryBase inventory, string kitName);
|
||||||
|
void GiveKit(MyPlayer player, MyInventoryBase inventory, KitViewModel kit);
|
||||||
|
bool CanGiveKit(MyPlayer player, string kitName, out string reason);
|
||||||
|
bool CanGiveKit(MyPlayer player, KitViewModel kit, out string reason);
|
||||||
|
bool CanGiveRespawnKit(MyPlayer player, KitViewModel kit, string respawnName, out string reason);
|
||||||
|
bool CanGiveRespawnKit(MyPlayer player, string kitName, string respawnName, out string reason);
|
||||||
|
KitViewModel GetKit(string kitName);
|
||||||
|
bool TryGetKit(string kitName, out KitViewModel? kit);
|
||||||
|
}
|
62
Kits/KitViewModel.cs
Normal file
62
Kits/KitViewModel.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
using Kits.Views;
|
||||||
|
using Torch;
|
||||||
|
using Torch.Views;
|
||||||
|
using VRage.Game.ModAPI;
|
||||||
|
using VRage.ObjectBuilders;
|
||||||
|
|
||||||
|
namespace Kits;
|
||||||
|
|
||||||
|
public class KitViewModel : ViewModel
|
||||||
|
{
|
||||||
|
[Display(Name = "Name", GroupName = "General")]
|
||||||
|
[XmlAttribute]
|
||||||
|
public string Name { get; set; } = "unnamed";
|
||||||
|
[Display(Name = "Cost", GroupName = "Usage", Description = "Credits cost to use this kit")]
|
||||||
|
public long UseCost { get; set; } = 0;
|
||||||
|
[Display(Name = "Cooldown Minutes", GroupName = "Usage", Description = "Cooldown to use this kit per player in minutes")]
|
||||||
|
public ulong UseCooldownMinutes { get; set; } = 0;
|
||||||
|
[Display(Name = "Required Promote Level", GroupName = "Conditions", Description = "Minimal Promote Level to use this kit")]
|
||||||
|
public MyPromoteLevel RequiredPromoteLevel { get; set; } = MyPromoteLevel.None;
|
||||||
|
[Display(Name = "Lp Permission", GroupName = "Conditions", Description = "Luck Perms permission to use this kit (leave empty to disable, example: kits.vip)")]
|
||||||
|
public string LpPermission { get; set; } = "";
|
||||||
|
[Display(Name = "Respawn Pod Wildcards", GroupName = "Usage", Description = "Respawn pod name wildcard to filter usage of kit, leave empty to disable")]
|
||||||
|
public ObservableCollection<string> RespawnPodWildcards { get; set; } = new();
|
||||||
|
[Display(Name = "Items", GroupName = "General", EditorType = typeof(EditButton))]
|
||||||
|
[XmlArrayItem("Item")]
|
||||||
|
public ObservableCollection<KitItemViewModel> Items { get; set; } = new();
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class KitItemViewModel : ViewModel
|
||||||
|
{
|
||||||
|
[Display(Name = "Id", EditorType = typeof(DefinitionIdEditor), Description = "TypeId/SubtypeId. Only items are allowed. for e.g Component/SteelPlate, Ore/Stone, PhysicalGunObject/RapidFireAutomaticRifleItem")]
|
||||||
|
public DefinitionId Id { get; set; } = new();
|
||||||
|
[Display(Name = "Probability", Description = "Probability of the item. 1 is 100%, 0 is 0%")]
|
||||||
|
public float Probability { get; set; } = 1;
|
||||||
|
[Display(Name = "Amount")]
|
||||||
|
public float Amount { get; set; } = 0;
|
||||||
|
|
||||||
|
[XmlIgnore]
|
||||||
|
public string Name => Id.ToString();
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Id.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DefinitionId : ViewModel
|
||||||
|
{
|
||||||
|
[XmlAttribute]
|
||||||
|
public string TypeId { get; set; } = "type";
|
||||||
|
[XmlAttribute]
|
||||||
|
public string SubtypeId { get; set; } = "subtype";
|
||||||
|
|
||||||
|
public override string ToString() => $"{TypeId}/{SubtypeId}";
|
||||||
|
public static implicit operator SerializableDefinitionId(DefinitionId id) => new(MyObjectBuilderType.ParseBackwardsCompatible(id.TypeId), id.SubtypeId);
|
||||||
|
}
|
47
Kits/Kits.csproj
Normal file
47
Kits/Kits.csproj
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net48</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<LangVersion>10</LangVersion>
|
||||||
|
<UseWpf>true</UseWpf>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="LuckPerms.Torch.Api" Version="5.4.0" />
|
||||||
|
<PackageReference Include="PetaPoco.Compiled" Version="6.0.480" />
|
||||||
|
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" PrivateAssets="all" />
|
||||||
|
<PackageReference Include="Torch.Server.ReferenceAssemblies" Version="1.3.1.207-master" PrivateAssets="all" IncludeAssets="compile" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="manifest.xml">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="schema.xsd" />
|
||||||
|
<EmbeddedResource Include="schema.xsd" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Update="Views\EditButton.cs">
|
||||||
|
<DependentUpon>EditButton.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Update="Views\ProperCollectionEditor.cs">
|
||||||
|
<DependentUpon>ProperCollectionEditor.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\heh\heh.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
50
Kits/Plugin.cs
Normal file
50
Kits/Plugin.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using heh;
|
||||||
|
using heh.Utils;
|
||||||
|
using Kits.Views;
|
||||||
|
using Torch;
|
||||||
|
using Torch.API;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.API.Plugins;
|
||||||
|
using Torch.API.Session;
|
||||||
|
using Torch.Views;
|
||||||
|
|
||||||
|
namespace Kits;
|
||||||
|
|
||||||
|
public class Plugin : TorchPluginBase, IWpfPlugin
|
||||||
|
{
|
||||||
|
private ProperPersistent<Config> _config = null!;
|
||||||
|
|
||||||
|
public override void Init(ITorchBase torch)
|
||||||
|
{
|
||||||
|
base.Init(torch);
|
||||||
|
CheckConfigSchema();
|
||||||
|
_config = new(Path.Combine(StoragePath, "Kits.xml"));
|
||||||
|
|
||||||
|
Torch.Managers.AddManager(DbManager.Static);
|
||||||
|
Torch.Managers.GetManager<ITorchSessionManager>().AddFactory(s => new KitManager(s.Torch, _config.Data));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckConfigSchema()
|
||||||
|
{
|
||||||
|
var files = Directory.EnumerateFiles(StoragePath, "Kits.*.xsd").ToList();
|
||||||
|
if (files.Any() && files[0].Substring(files[0].IndexOf('.') + 1, Manifest.Version.Length) == Manifest.Version)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
File.Delete(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var resource = typeof(Plugin).Assembly.GetManifestResourceStream("Kits.schema.xsd");
|
||||||
|
using var stream = File.Create(Path.Combine(StoragePath, $"Kits.{Manifest.Version}.xsd"));
|
||||||
|
resource?.CopyTo(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserControl GetControl() => new PropertyGrid
|
||||||
|
{
|
||||||
|
Margin = new(3),
|
||||||
|
DataContext = _config.Data
|
||||||
|
};
|
||||||
|
}
|
18
Kits/Views/DefinitionIdEditor.xaml
Normal file
18
Kits/Views/DefinitionIdEditor.xaml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<UserControl x:Class="Kits.Views.DefinitionIdEditor"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:kits="clr-namespace:Kits"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="300" d:DesignWidth="300">
|
||||||
|
|
||||||
|
<UserControl.DataContext>
|
||||||
|
<kits:DefinitionId/>
|
||||||
|
</UserControl.DataContext>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBox Text="{Binding TypeId}" Margin="0,0,3,0" />
|
||||||
|
<TextBlock Text="/" Margin="0,0,3,0" />
|
||||||
|
<TextBox Text="{Binding SubtypeId}" Margin="0,0,3,0" />
|
||||||
|
</StackPanel>
|
||||||
|
</UserControl>
|
12
Kits/Views/DefinitionIdEditor.xaml.cs
Normal file
12
Kits/Views/DefinitionIdEditor.xaml.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Kits.Views;
|
||||||
|
|
||||||
|
public partial class DefinitionIdEditor : UserControl
|
||||||
|
{
|
||||||
|
public DefinitionIdEditor()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
20
Kits/Views/EditButton.cs
Normal file
20
Kits/Views/EditButton.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
namespace Kits.Views;
|
||||||
|
|
||||||
|
public partial class EditButton : UserControl
|
||||||
|
{
|
||||||
|
public EditButton()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
new ProperCollectionEditor
|
||||||
|
{
|
||||||
|
DataContext = DataContext,
|
||||||
|
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||||
|
Owner = Window.GetWindow(this)
|
||||||
|
}.ShowDialog();
|
||||||
|
}
|
||||||
|
}
|
9
Kits/Views/EditButton.xaml
Normal file
9
Kits/Views/EditButton.xaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<UserControl x:Class="Kits.Views.EditButton"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="300" d:DesignWidth="300">
|
||||||
|
<Button Content="Edit" Click="ButtonBase_OnClick" />
|
||||||
|
</UserControl>
|
21
Kits/Views/ProperCollectionEditor.cs
Normal file
21
Kits/Views/ProperCollectionEditor.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Windows;
|
||||||
|
namespace Kits.Views;
|
||||||
|
|
||||||
|
public partial class ProperCollectionEditor : Window
|
||||||
|
{
|
||||||
|
public ProperCollectionEditor()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonAdd_OnClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
((IList)DataContext).Add(Activator.CreateInstance(DataContext.GetType().GenericTypeArguments[0]));
|
||||||
|
}
|
||||||
|
private void ButtonDelete_OnClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (ElementsGrid.SelectedItem is { } item)
|
||||||
|
((IList)DataContext).Remove(item);
|
||||||
|
}
|
||||||
|
}
|
35
Kits/Views/ProperCollectionEditor.xaml
Normal file
35
Kits/Views/ProperCollectionEditor.xaml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<Window x:Class="Kits.Views.ProperCollectionEditor"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:views="clr-namespace:Torch.Views;assembly=Torch"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="Proper Collection Editor" Height="450" Width="800">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="1*" />
|
||||||
|
<ColumnDefinition Width="2*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="10*" />
|
||||||
|
<RowDefinition Height="1*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<DataGrid Margin="3" Name="ElementsGrid" AutoGenerateColumns="False" ItemsSource="{Binding }" IsReadOnly="True">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Binding="{Binding Name}" Width="*" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
<Grid Grid.Row="1">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Button Margin="5" Content="Add" Click="ButtonAdd_OnClick" />
|
||||||
|
<Button Grid.Column="1" Margin="5" Content="Delete" Click="ButtonDelete_OnClick" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<views:PropertyGrid Grid.Column="1" DataContext="{Binding ElementName=ElementsGrid, Path=SelectedItem, Mode=OneWay}" Margin="3" />
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<PluginManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
<PluginManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<Name>LuckPerms.Loader</Name>
|
<Name>Kits</Name>
|
||||||
<Guid>7E4B3CC8-64FA-416E-8910-AACDF2DA5E2C</Guid>
|
<Guid>d095391d-b5ec-43a9-8ba4-6c4909227e6e</Guid>
|
||||||
<Version>v5.4.106.3</Version>
|
<Version>v1.0.6</Version>
|
||||||
</PluginManifest>
|
</PluginManifest>
|
73
Kits/schema.xsd
Normal file
73
Kits/schema.xsd
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xs:element name="Config" nillable="true" type="Config" />
|
||||||
|
<xs:complexType name="Config">
|
||||||
|
<xs:complexContent mixed="false">
|
||||||
|
<xs:extension base="ViewModel">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element minOccurs="1" maxOccurs="1" name="Kits" type="ArrayOfKitViewModel" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:extension>
|
||||||
|
</xs:complexContent>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="ViewModel" abstract="true" />
|
||||||
|
<xs:complexType name="DefinitionId">
|
||||||
|
<xs:complexContent mixed="false">
|
||||||
|
<xs:extension base="ViewModel">
|
||||||
|
<xs:attribute name="TypeId" type="xs:string" />
|
||||||
|
<xs:attribute name="SubtypeId" type="xs:string" />
|
||||||
|
</xs:extension>
|
||||||
|
</xs:complexContent>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="KitItemViewModel">
|
||||||
|
<xs:complexContent mixed="false">
|
||||||
|
<xs:extension base="ViewModel">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element minOccurs="1" maxOccurs="1" name="Id" type="DefinitionId" />
|
||||||
|
<xs:element minOccurs="1" maxOccurs="1" name="Probability" type="xs:float" />
|
||||||
|
<xs:element minOccurs="1" maxOccurs="1" name="Amount" type="xs:float" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:extension>
|
||||||
|
</xs:complexContent>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="KitViewModel">
|
||||||
|
<xs:complexContent mixed="false">
|
||||||
|
<xs:extension base="ViewModel">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element minOccurs="0" maxOccurs="1" name="UseCost" type="xs:long" />
|
||||||
|
<xs:element minOccurs="0" maxOccurs="1" name="UseCooldownMinutes" type="xs:unsignedLong" />
|
||||||
|
<xs:element minOccurs="0" maxOccurs="1" name="RequiredPromoteLevel" type="MyPromoteLevel" />
|
||||||
|
<xs:element minOccurs="0" maxOccurs="1" name="LpPermission" type="xs:string" />
|
||||||
|
<xs:element minOccurs="0" maxOccurs="1" name="RespawnPodWildcards" type="ArrayOfString" />
|
||||||
|
<xs:element minOccurs="1" maxOccurs="1" name="Items" type="ArrayOfKitItemViewModel" />
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="Name" type="xs:string" />
|
||||||
|
</xs:extension>
|
||||||
|
</xs:complexContent>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:simpleType name="MyPromoteLevel">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:enumeration value="None" />
|
||||||
|
<xs:enumeration value="Scripter" />
|
||||||
|
<xs:enumeration value="Moderator" />
|
||||||
|
<xs:enumeration value="SpaceMaster" />
|
||||||
|
<xs:enumeration value="Admin" />
|
||||||
|
<xs:enumeration value="Owner" />
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
<xs:complexType name="ArrayOfString">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element minOccurs="0" maxOccurs="unbounded" name="string" nillable="true" type="xs:string" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="ArrayOfKitItemViewModel">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element minOccurs="0" maxOccurs="unbounded" name="Item" nillable="true" type="KitItemViewModel" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="ArrayOfKitViewModel">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element minOccurs="0" maxOccurs="unbounded" name="Kit" nillable="true" type="KitViewModel" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:schema>
|
@@ -14,7 +14,6 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="heh" Version="1.0.12" />
|
|
||||||
<PackageReference Include="Torch.Server.ReferenceAssemblies" Version="1.3.1.207-master" PrivateAssets="all" IncludeAssets="compile" />
|
<PackageReference Include="Torch.Server.ReferenceAssemblies" Version="1.3.1.207-master" PrivateAssets="all" IncludeAssets="compile" />
|
||||||
<PackageReference Include="PropertyChanged.Fody" Version="4.0.0" PrivateAssets="all" />
|
<PackageReference Include="PropertyChanged.Fody" Version="4.0.0" PrivateAssets="all" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -25,4 +24,8 @@
|
|||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\heh\heh.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -8,18 +8,30 @@
|
|||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||||
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<PluginBasePath>$(MSBuildThisFileDirectory)$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework)\</PluginBasePath>
|
||||||
|
<PluginZipPath>$(PluginBasePath)plugin.zip</PluginZipPath>
|
||||||
|
<PluginZipHashPath>$(PluginBasePath)plugin.zip.sha256</PluginZipHashPath>
|
||||||
|
<PluginNamePath>$(PluginBasePath)name.txt</PluginNamePath>
|
||||||
|
<PluginManifestPath>$(PluginBasePath)manifest.xml</PluginManifestPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="$(PluginZipPath)" LogicalName="plugin.zip" />
|
||||||
|
<EmbeddedResource Include="$(PluginZipHashPath)" LogicalName="plugin.zip.sha256" />
|
||||||
|
<EmbeddedResource Include="$(PluginNamePath)" LogicalName="name.txt" />
|
||||||
|
<Content Include="$(PluginManifestPath)" Link="manifest.xml" CopyToOutputDirectory="PreserveNewest" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
<DebugSymbols>false</DebugSymbols>
|
<DebugSymbols>false</DebugSymbols>
|
||||||
<DebugType>none</DebugType>
|
<DebugType>none</DebugType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Krafs.Publicizer" Version="2.2.1">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="PolySharp" Version="1.13.2">
|
<PackageReference Include="PolySharp" Version="1.13.2">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
@@ -27,37 +39,8 @@
|
|||||||
<PackageReference Include="torch.server.referenceassemblies" Version="1.3.1.260-master" PrivateAssets="all" IncludeAssets="compile" />
|
<PackageReference Include="torch.server.referenceassemblies" Version="1.3.1.260-master" PrivateAssets="all" IncludeAssets="compile" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Publicize Include="Torch:Torch.TorchBase.RegisterAuxAssembly" />
|
|
||||||
<Publicize Include="Torch:Torch.Managers.PluginManager._plugins" />
|
|
||||||
<Publicize Include="Torch:Torch.TorchPluginBase.Manifest" />
|
|
||||||
<Publicize Include="Torch:Torch.TorchPluginBase.StoragePath" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="manifest.xml">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\LuckPerms.Torch\LuckPerms.Torch.csproj" ReferenceOutputAssembly="false" Private="false" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System.IO.Compression" />
|
<Reference Include="System.IO.Compression" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="BuildArchive" BeforeTargets="PreBuildEvent">
|
|
||||||
<PropertyGroup>
|
|
||||||
<PluginDir>..\LuckPerms.Torch\bin\$(Configuration)\$(TargetFramework)\win-x64\</PluginDir>
|
|
||||||
<PluginZipPath>$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework)\plugin.zip</PluginZipPath>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ZipDirectory DestinationFile="$(PluginZipPath)" SourceDirectory="$(PluginDir)" Overwrite="true" />
|
|
||||||
<ItemGroup>
|
|
||||||
<EmbeddedResource Include="$(PluginZipPath)" LogicalName="plugin.zip" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
@@ -6,64 +6,102 @@ using Torch;
|
|||||||
using Torch.API;
|
using Torch.API;
|
||||||
using Torch.API.Managers;
|
using Torch.API.Managers;
|
||||||
using Torch.API.Plugins;
|
using Torch.API.Plugins;
|
||||||
|
using Torch.Collections;
|
||||||
using Torch.Managers;
|
using Torch.Managers;
|
||||||
|
using Torch.Utils;
|
||||||
|
|
||||||
namespace LuckPerms.Loader;
|
namespace LuckPerms.Loader;
|
||||||
|
|
||||||
public class Plugin : TorchPluginBase
|
public class Plugin : TorchPluginBase
|
||||||
{
|
{
|
||||||
private static readonly ITorchPlugin MainPluginInstance;
|
private static readonly ITorchPlugin MainPluginInstance;
|
||||||
private static readonly ILogger Log = LogManager.GetLogger("LuckPerms.Loader");
|
private static readonly ILogger Log = LogManager.GetLogger("Loader");
|
||||||
|
|
||||||
static Plugin()
|
static Plugin()
|
||||||
{
|
{
|
||||||
|
string assemblyName;
|
||||||
|
using (var infoStream = typeof(Plugin).Assembly.GetManifestResourceStream("name.txt")!)
|
||||||
|
using (var infoStreamReader = new StreamReader(infoStream))
|
||||||
|
assemblyName = infoStreamReader.ReadLine()!.Trim();
|
||||||
|
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
var torch = (ITorchServer)TorchBase.Instance;
|
var torch = (ITorchServer)TorchBase.Instance;
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||||
var dir = new DirectoryInfo(Path.Combine(torch.InstancePath, "cache", "luckperms.loader"));
|
var dir = new DirectoryInfo(Path.Combine(torch.InstancePath, "cache", assemblyName));
|
||||||
|
|
||||||
|
void ExtractCache()
|
||||||
|
{
|
||||||
|
using var currentHashStream = typeof(Plugin).Assembly.GetManifestResourceStream("plugin.zip.sha256")!;
|
||||||
|
var currentHash = currentHashStream.ReadToEnd();
|
||||||
|
|
||||||
|
var hashPath = Path.Combine(dir.FullName, "plugin.zip.sha256");
|
||||||
if (dir.Exists)
|
if (dir.Exists)
|
||||||
|
{
|
||||||
|
if (File.Exists(hashPath))
|
||||||
|
{
|
||||||
|
Log.Info("Checking cache");
|
||||||
|
|
||||||
|
var hash = File.ReadAllBytes(hashPath);
|
||||||
|
|
||||||
|
if (hash.SequenceEqual(currentHash)) return;
|
||||||
|
}
|
||||||
|
|
||||||
dir.Delete(true);
|
dir.Delete(true);
|
||||||
|
}
|
||||||
|
|
||||||
Log.Info($"Extracting cache to {dir}");
|
Log.Info($"Extracting cache to {dir}");
|
||||||
|
|
||||||
using (var pluginStream = typeof(Plugin).Assembly.GetManifestResourceStream("plugin.zip")!)
|
using var pluginStream = typeof(Plugin).Assembly.GetManifestResourceStream("plugin.zip")!;
|
||||||
using (var archive = new ZipArchive(pluginStream, ZipArchiveMode.Read))
|
using var archive = new ZipArchive(pluginStream, ZipArchiveMode.Read);
|
||||||
archive.ExtractToDirectory(dir.FullName);
|
|
||||||
|
|
||||||
Log.Info("Injecting LuckPerms");
|
archive.ExtractToDirectory(dir.FullName);
|
||||||
|
File.WriteAllBytes(hashPath, currentHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExtractCache();
|
||||||
|
|
||||||
|
Log.Info($"Injecting {assemblyName}");
|
||||||
|
|
||||||
AppDomain.CurrentDomain.AssemblyResolve += (_, args) =>
|
AppDomain.CurrentDomain.AssemblyResolve += (_, args) =>
|
||||||
{
|
{
|
||||||
var fileName = args.Name[..args.Name.IndexOf(',')];
|
var fileName = args.Name[..args.Name.IndexOf(',')];
|
||||||
|
|
||||||
|
if (AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(b =>
|
||||||
|
b.GetName().Name?.Equals(fileName, StringComparison.OrdinalIgnoreCase) is true) is { } assembly)
|
||||||
|
return assembly;
|
||||||
|
|
||||||
var path = Path.Combine(dir.FullName, fileName + ".dll");
|
var path = Path.Combine(dir.FullName, fileName + ".dll");
|
||||||
|
|
||||||
return File.Exists(path) ? Assembly.LoadFile(path) : null;
|
return File.Exists(path) ? Assembly.LoadFile(path) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
var mainAssembly = Assembly.LoadFile(Path.Combine(dir.FullName, "LuckPerms.Torch.dll"));
|
var mainAssembly = Assembly.LoadFile(Path.Combine(dir.FullName, $"{assemblyName}.dll"));
|
||||||
|
|
||||||
var pluginType = mainAssembly.GetType("LuckPerms.Torch.Plugin", true)!;
|
var pluginType = mainAssembly.GetType($"{assemblyName}.Plugin", true)!;
|
||||||
|
|
||||||
// a hacky way to configure JVM
|
// a hacky way to configure the plugin
|
||||||
RuntimeHelpers.RunClassConstructor(pluginType.TypeHandle);
|
RuntimeHelpers.RunClassConstructor(pluginType.TypeHandle);
|
||||||
|
|
||||||
TorchBase.RegisterAuxAssembly(mainAssembly);
|
typeof(TorchBase).GetMethod("RegisterAuxAssembly", BindingFlags.NonPublic | BindingFlags.Static)!.Invoke(null, new object[] { mainAssembly });
|
||||||
|
|
||||||
MainPluginInstance = (ITorchPlugin)Activator.CreateInstance(pluginType)!;
|
MainPluginInstance = (ITorchPlugin)Activator.CreateInstance(pluginType)!;
|
||||||
|
|
||||||
if (MainPluginInstance is not TorchPluginBase pluginBase) return;
|
|
||||||
|
|
||||||
pluginBase.Manifest = PluginManifest.Load(Path.Combine(dir.FullName, "manifest.xml"));
|
|
||||||
pluginBase.StoragePath = torch.InstancePath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Init(ITorchBase torch)
|
public override void Init(ITorchBase torch)
|
||||||
{
|
{
|
||||||
var pluginManager = torch.Managers.GetManager<PluginManager>();
|
if (MainPluginInstance is TorchPluginBase pluginBase)
|
||||||
|
{
|
||||||
|
typeof(TorchPluginBase).GetProperty(nameof(Manifest))!.SetValue(pluginBase, Manifest);
|
||||||
|
typeof(TorchPluginBase).GetProperty(nameof(StoragePath))!.SetValue(pluginBase, StoragePath);
|
||||||
|
}
|
||||||
|
|
||||||
pluginManager._plugins.Remove(Manifest.Guid);
|
var pluginManager = torch.Managers.GetManager<PluginManager>();
|
||||||
pluginManager._plugins.Add(Manifest.Guid, MainPluginInstance);
|
var plugins =
|
||||||
|
(MtObservableSortedDictionary<Guid, ITorchPlugin>)typeof(PluginManager).GetField("_plugins",
|
||||||
|
BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(pluginManager);
|
||||||
|
|
||||||
|
plugins.Remove(Manifest.Guid);
|
||||||
|
plugins.Add(Manifest.Guid, MainPluginInstance);
|
||||||
|
|
||||||
MainPluginInstance.Init(torch);
|
MainPluginInstance.Init(torch);
|
||||||
|
|
||||||
|
@@ -2,12 +2,6 @@
|
|||||||
"version": 1,
|
"version": 1,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
".NETFramework,Version=v4.8": {
|
".NETFramework,Version=v4.8": {
|
||||||
"Krafs.Publicizer": {
|
|
||||||
"type": "Direct",
|
|
||||||
"requested": "[2.2.1, )",
|
|
||||||
"resolved": "2.2.1",
|
|
||||||
"contentHash": "QGI4nMGQbKsuFUUboixVHu4mv3lHB5RejIa7toIlzTmwLkuCYYEpUBJjmy3OpXYyj5dVSZAXVbr4oeMSloE67Q=="
|
|
||||||
},
|
|
||||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[1.0.3, )",
|
"requested": "[1.0.3, )",
|
||||||
@@ -62,6 +56,7 @@
|
|||||||
"protobuf-net": "1.0.0"
|
"protobuf-net": "1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
".NETFramework,Version=v4.8/win-x64": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
15
LuckPerms.Torch.Api/LuckPerms.Torch.Api.nuspec
Normal file
15
LuckPerms.Torch.Api/LuckPerms.Torch.Api.nuspec
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
|
||||||
|
<metadata>
|
||||||
|
<id>LuckPerms.Torch.Api</id>
|
||||||
|
<version>5.4</version>
|
||||||
|
<authors>LuckPerms.Torch.Api</authors>
|
||||||
|
<description>Package Description</description>
|
||||||
|
<dependencies>
|
||||||
|
<group targetFramework=".NETFramework4.8">
|
||||||
|
<dependency id="Torch.Loader" version="1.0.0" />
|
||||||
|
<dependency id="IKVM.Maven.Sdk" version="1.6.1" />
|
||||||
|
</group>
|
||||||
|
</dependencies>
|
||||||
|
</metadata>
|
||||||
|
</package>
|
@@ -0,0 +1,5 @@
|
|||||||
|
<Project>
|
||||||
|
<ItemGroup>
|
||||||
|
<MavenReference Include="net.luckperms:api" Version="5.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
@@ -52,7 +52,7 @@ public class LpTorchBootstrap : LuckPermsBootstrap
|
|||||||
public Collection getPlayerList() => Sync.Players?.GetAllPlayers()
|
public Collection getPlayerList() => Sync.Players?.GetAllPlayers()
|
||||||
.Select(b => Sync.Players.TryGetPlayerIdentity(b)?.DisplayName).Where(b => b is not null).ToCollection() ?? Collections.EMPTY_LIST;
|
.Select(b => Sync.Players.TryGetPlayerIdentity(b)?.DisplayName).Where(b => b is not null).ToCollection() ?? Collections.EMPTY_LIST;
|
||||||
|
|
||||||
public Platform.Type getType() => Platform.Type.STANDALONE; // meh
|
public Platform.Type getType() => Platform.Type.BUKKIT; // meh
|
||||||
|
|
||||||
public SchedulerAdapter getScheduler() => _schedulerAdapter ??= new LpSchedulerAdapter(this, _torch);
|
public SchedulerAdapter getScheduler() => _schedulerAdapter ??= new LpSchedulerAdapter(this, _torch);
|
||||||
|
|
||||||
|
@@ -41,12 +41,12 @@ public class LpTorchPlugin(LuckPermsBootstrap bootstrap, ITorchBase torch) : Abs
|
|||||||
private LpContextManager? _contextManager;
|
private LpContextManager? _contextManager;
|
||||||
private LpSenderFactory? _senderFactory;
|
private LpSenderFactory? _senderFactory;
|
||||||
private LpConnectionListener? _connectionListener;
|
private LpConnectionListener? _connectionListener;
|
||||||
|
private global::Torch.Managers.DependencyManager? _dependencyManager;
|
||||||
public override LuckPermsBootstrap getBootstrap() => bootstrap;
|
public override LuckPermsBootstrap getBootstrap() => bootstrap;
|
||||||
|
|
||||||
protected override void setupSenderFactory()
|
protected override void setupSenderFactory()
|
||||||
{
|
{
|
||||||
_senderFactory = new LpSenderFactory(this);
|
_senderFactory = new(this);
|
||||||
torch.Managers.GetManager<ITorchSessionManager>().AddFactory(_ => _senderFactory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Sender getConsoleSender() => _senderFactory?.wrap(torch) ?? throw new InvalidOperationException("call setupSenderFactory first");
|
public override Sender getConsoleSender() => _senderFactory?.wrap(torch) ?? throw new InvalidOperationException("call setupSenderFactory first");
|
||||||
@@ -61,8 +61,7 @@ public class LpTorchPlugin(LuckPermsBootstrap bootstrap, ITorchBase torch) : Abs
|
|||||||
|
|
||||||
protected override void registerCommands()
|
protected override void registerCommands()
|
||||||
{
|
{
|
||||||
_commandManager = new LpCommandManager(this, _senderFactory!);
|
_commandManager = new(this, _senderFactory!);
|
||||||
torch.Managers.GetManager<ITorchSessionManager>().AddFactory(_ => _commandManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void setupManagers()
|
protected override void setupManagers()
|
||||||
@@ -71,16 +70,29 @@ public class LpTorchPlugin(LuckPermsBootstrap bootstrap, ITorchBase torch) : Abs
|
|||||||
_groupManager = new(this);
|
_groupManager = new(this);
|
||||||
_trackManager = new(this);
|
_trackManager = new(this);
|
||||||
_connectionListener = new(this);
|
_connectionListener = new(this);
|
||||||
|
if (torch.CurrentSession is null)
|
||||||
|
{
|
||||||
|
torch.Managers.GetManager<ITorchSessionManager>().AddFactory(_ => _senderFactory);
|
||||||
|
torch.Managers.GetManager<ITorchSessionManager>().AddFactory(_ => _commandManager);
|
||||||
torch.Managers.GetManager<ITorchSessionManager>().AddFactory(_ => _connectionListener);
|
torch.Managers.GetManager<ITorchSessionManager>().AddFactory(_ => _connectionListener);
|
||||||
torch.Managers.GetManager<ITorchSessionManager>().AddFactory(_ => new ModApiManager());
|
torch.Managers.GetManager<ITorchSessionManager>().AddFactory(_ => new ModApiManager());
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_dependencyManager = new(torch.CurrentSession.Managers);
|
||||||
|
|
||||||
|
_dependencyManager.AddManager(_senderFactory);
|
||||||
|
_dependencyManager.AddManager(_commandManager);
|
||||||
|
_dependencyManager.AddManager(_connectionListener);
|
||||||
|
_dependencyManager.AddManager(new ModApiManager());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override CalculatorFactory provideCalculatorFactory() => new LpCalculatorFactory(this);
|
protected override CalculatorFactory provideCalculatorFactory() => new LpCalculatorFactory(this);
|
||||||
|
|
||||||
protected override void setupContextManager()
|
protected override void setupContextManager()
|
||||||
{
|
{
|
||||||
_contextManager = new LpContextManager(this);
|
_contextManager = new(this);
|
||||||
torch.Managers.GetManager<ITorchSessionManager>().AddFactory(_ => _contextManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override GroupManager getGroupManager() => _groupManager ?? throw new InvalidOperationException("call setupManagers first");
|
public override GroupManager getGroupManager() => _groupManager ?? throw new InvalidOperationException("call setupManagers first");
|
||||||
@@ -123,6 +135,23 @@ public class LpTorchPlugin(LuckPermsBootstrap bootstrap, ITorchBase torch) : Abs
|
|||||||
CommandPrefixPatch.Patch(context);
|
CommandPrefixPatch.Patch(context);
|
||||||
|
|
||||||
patchManager.Commit();
|
patchManager.Commit();
|
||||||
|
|
||||||
|
if (_dependencyManager is null)
|
||||||
|
{
|
||||||
|
torch.Managers.GetManager<ITorchSessionManager>().AddFactory(_ => _contextManager);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_dependencyManager.AddManager(_contextManager);
|
||||||
|
|
||||||
|
_dependencyManager.Attach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void removePlatformHooks()
|
||||||
|
{
|
||||||
|
base.removePlatformHooks();
|
||||||
|
_dependencyManager?.Detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override AbstractEventBus provideEventBus(LuckPermsApiProvider luckPermsApiProvider) => new LpEventBus(this, luckPermsApiProvider, torch);
|
protected override AbstractEventBus provideEventBus(LuckPermsApiProvider luckPermsApiProvider) => new LpEventBus(this, luckPermsApiProvider, torch);
|
||||||
|
@@ -24,6 +24,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="torch.server.referenceassemblies" Version="1.3.1.260-master" PrivateAssets="all" IncludeAssets="compile" />
|
<PackageReference Include="torch.server.referenceassemblies" Version="1.3.1.260-master" PrivateAssets="all" IncludeAssets="compile" />
|
||||||
|
<PackageReference Include="Torch.Loader" Version="1.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@@ -100,9 +101,15 @@
|
|||||||
|
|
||||||
<MySqlPath>$(LibsPath)mysql-connector-j-8.0.33.jar</MySqlPath>
|
<MySqlPath>$(LibsPath)mysql-connector-j-8.0.33.jar</MySqlPath>
|
||||||
<ProtobufPath>$(LibsPath)protobuf-java-3.21.9.jar</ProtobufPath>
|
<ProtobufPath>$(LibsPath)protobuf-java-3.21.9.jar</ProtobufPath>
|
||||||
|
|
||||||
|
<SqlitePath>$(LibsPath)sqlite-jdbc-3.43.2.2.jar</SqlitePath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<IkvmReference Include="$(SqlitePath)">
|
||||||
|
<References>$(Slf4jApiPath)</References>
|
||||||
|
</IkvmReference>
|
||||||
|
|
||||||
<IkvmReference Include="$(MySqlPath)">
|
<IkvmReference Include="$(MySqlPath)">
|
||||||
<References>$(ProtobufPath)</References>
|
<References>$(ProtobufPath)</References>
|
||||||
</IkvmReference>
|
</IkvmReference>
|
||||||
@@ -122,7 +129,7 @@
|
|||||||
</IkvmReference>
|
</IkvmReference>
|
||||||
<IkvmReference Include="$(JnaPath)" />
|
<IkvmReference Include="$(JnaPath)" />
|
||||||
|
|
||||||
<IkvmReference Include="$(H2Path)" />
|
<!--<IkvmReference Include="$(H2Path)" />-->
|
||||||
<IkvmReference Include="$(PostgreSqlPath)" />
|
<IkvmReference Include="$(PostgreSqlPath)" />
|
||||||
|
|
||||||
<IkvmReference Include="$(MongoDriverLegacyPath)">
|
<IkvmReference Include="$(MongoDriverLegacyPath)">
|
||||||
@@ -234,21 +241,17 @@
|
|||||||
<IkvmReference Include="$(SnakeYamlPath)" />
|
<IkvmReference Include="$(SnakeYamlPath)" />
|
||||||
<IkvmReference Include="$(CheckerQualPath)" />
|
<IkvmReference Include="$(CheckerQualPath)" />
|
||||||
|
|
||||||
<IkvmReference Include="$(ApiJarPath)" />
|
<IkvmReference Include="$(ApiJarPath)">
|
||||||
|
<AssemblyName>api</AssemblyName>
|
||||||
|
<DisableAutoAssemblyName>true</DisableAutoAssemblyName>
|
||||||
|
</IkvmReference>
|
||||||
<IkvmReference Include="$(CommonJarPath)">
|
<IkvmReference Include="$(CommonJarPath)">
|
||||||
<References>$(ApiJarPath);$(ConfigurateCorePath);$(ConfigurateGsonPath);$(ConfigurateHoconPath);$(ConfigurateYamlPath);$(SnakeYamlPath);$(CheckerQualPath);$(AdventureApiPath);$(AdventureKeyPath);$(AdventureTextPlainPath);$(AdventureTextLegacyPath);$(AdventureTextMinimessagePath);$(ExaminationApiPath);$(GuavaPath);$(GsonPath);$(ConfigurateTomlPath);$(BrigadierPath);$(EventApiPath);$(Slf4jApiPath);$(Log4jApiPath);$(CaffeinePath);$(OkHttpPath);$(OkioPath);$(ByteBuddyPath);$(JnatsPath);$(HikariCPPath);$(JedisPath);$(CommonsPoolPath);$(AmqpClientPath);$(BsonPath);$(MongoDriverCorePath);$(MongoDriverLegacyPath);$(MongoDriverSyncPath);$(PostgreSqlPath);$(H2Path);$(MariaDbPath);$(MySqlPath)</References>
|
<References>$(ApiJarPath);$(ConfigurateCorePath);$(ConfigurateGsonPath);$(ConfigurateHoconPath);$(ConfigurateYamlPath);$(SnakeYamlPath);$(CheckerQualPath);$(AdventureApiPath);$(AdventureKeyPath);$(AdventureTextPlainPath);$(AdventureTextLegacyPath);$(AdventureTextMinimessagePath);$(ExaminationApiPath);$(GuavaPath);$(GsonPath);$(ConfigurateTomlPath);$(BrigadierPath);$(EventApiPath);$(Slf4jApiPath);$(Log4jApiPath);$(CaffeinePath);$(OkHttpPath);$(OkioPath);$(ByteBuddyPath);$(JnatsPath);$(HikariCPPath);$(JedisPath);$(CommonsPoolPath);$(AmqpClientPath);$(BsonPath);$(MongoDriverCorePath);$(MongoDriverLegacyPath);$(MongoDriverSyncPath);$(PostgreSqlPath);$(MariaDbPath);$(MySqlPath);$(SqlitePath)</References>
|
||||||
</IkvmReference>
|
</IkvmReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="manifest.xml">
|
<!--<EmbeddedResource Include="Resources\schema\h2.sql" LogicalName="me.lucko.luckperms.schema.h2.sql" />-->
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<EmbeddedResource Include="Resources\schema\h2.sql" LogicalName="me.lucko.luckperms.schema.h2.sql" />
|
|
||||||
<EmbeddedResource Include="Resources\schema\mariadb.sql" LogicalName="me.lucko.luckperms.schema.mariadb.sql" />
|
<EmbeddedResource Include="Resources\schema\mariadb.sql" LogicalName="me.lucko.luckperms.schema.mariadb.sql" />
|
||||||
<EmbeddedResource Include="Resources\schema\mysql.sql" LogicalName="me.lucko.luckperms.schema.mysql.sql" />
|
<EmbeddedResource Include="Resources\schema\mysql.sql" LogicalName="me.lucko.luckperms.schema.mysql.sql" />
|
||||||
<EmbeddedResource Include="Resources\schema\postgresql.sql" LogicalName="me.lucko.luckperms.schema.postgresql.sql" />
|
<EmbeddedResource Include="Resources\schema\postgresql.sql" LogicalName="me.lucko.luckperms.schema.postgresql.sql" />
|
||||||
|
@@ -21,7 +21,8 @@ public class ModApiManager : IManager
|
|||||||
MyScriptCompiler.Static.AddReferencedAssemblies(
|
MyScriptCompiler.Static.AddReferencedAssemblies(
|
||||||
typeof(net.luckperms.api.LuckPerms).Assembly.Location, // net.luckperms.api.dll
|
typeof(net.luckperms.api.LuckPerms).Assembly.Location, // net.luckperms.api.dll
|
||||||
typeof(java.lang.Boolean).Assembly.Location, // IKVM.Java.dll
|
typeof(java.lang.Boolean).Assembly.Location, // IKVM.Java.dll
|
||||||
typeof(ModApiManager).Assembly.Location // LuckPerms.Torch.dll
|
typeof(ModApiManager).Assembly.Location, // LuckPerms.Torch.dll
|
||||||
|
typeof(ITorchBase).Assembly.Location // Torch.API.dll
|
||||||
);
|
);
|
||||||
|
|
||||||
using var whitelist = MyScriptCompiler.Static.Whitelist.OpenBatch();
|
using var whitelist = MyScriptCompiler.Static.Whitelist.OpenBatch();
|
||||||
|
61
LuckPerms.Torch/PlatformApi/LuckPermsPlatformManager.cs
Normal file
61
LuckPerms.Torch/PlatformApi/LuckPermsPlatformManager.cs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
using System.IO;
|
||||||
|
using java.lang;
|
||||||
|
using LuckPerms.Torch.Impl;
|
||||||
|
using NLog;
|
||||||
|
using Torch;
|
||||||
|
using Torch.API;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Exception = System.Exception;
|
||||||
|
|
||||||
|
namespace LuckPerms.Torch.PlatformApi;
|
||||||
|
|
||||||
|
public class LuckPermsPlatformManager : IManager
|
||||||
|
{
|
||||||
|
private readonly ILogger _log = LogManager.GetCurrentClassLogger();
|
||||||
|
private readonly LpTorchBootstrap _bootstrap;
|
||||||
|
|
||||||
|
public LuckPermsPlatformManager(TorchPluginBase plugin, ITorchServer server, ILogger log)
|
||||||
|
{
|
||||||
|
_bootstrap = new(server, plugin, log, Path.Combine(plugin.StoragePath, "luckperms"));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log.Info("Initializing LuckPerms");
|
||||||
|
_bootstrap.Plugin.load();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.Fatal(e);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_bootstrap.LoadLatch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Attach()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_log.Info("Loading LuckPerms");
|
||||||
|
Thread.currentThread().setContextClassLoader(LpDependencyManager.CurrentClassLoader);
|
||||||
|
_bootstrap.Plugin.enable();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_log.Fatal(e);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_bootstrap.EnableLatch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Detach()
|
||||||
|
{
|
||||||
|
_log.Info("Unloading LuckPerms");
|
||||||
|
_bootstrap.Plugin.disable();
|
||||||
|
}
|
||||||
|
}
|
@@ -3,10 +3,13 @@ using System.IO;
|
|||||||
using java.lang;
|
using java.lang;
|
||||||
using java.util;
|
using java.util;
|
||||||
using LuckPerms.Torch.Impl;
|
using LuckPerms.Torch.Impl;
|
||||||
|
using LuckPerms.Torch.PlatformApi;
|
||||||
using NLog;
|
using NLog;
|
||||||
using Sandbox;
|
using Sandbox;
|
||||||
using Torch;
|
using Torch;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.API.Session;
|
||||||
using Exception = System.Exception;
|
using Exception = System.Exception;
|
||||||
using Object = java.lang.Object;
|
using Object = java.lang.Object;
|
||||||
|
|
||||||
@@ -23,58 +26,14 @@ public class Plugin : TorchPluginBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static readonly ILogger Log = LogManager.GetLogger("LuckPerms");
|
public static readonly ILogger Log = LogManager.GetLogger("LuckPerms");
|
||||||
private LpTorchBootstrap? _bootstrap;
|
|
||||||
|
|
||||||
public override void Init(ITorchBase torch)
|
public override void Init(ITorchBase torch)
|
||||||
{
|
{
|
||||||
base.Init(torch);
|
base.Init(torch);
|
||||||
Torch.GameStateChanged += TorchOnGameStateChanged;
|
|
||||||
_bootstrap = new((ITorchServer)Torch, this, Log, Path.Combine(StoragePath, "luckperms"));
|
|
||||||
|
|
||||||
try
|
var platformManager = new LuckPermsPlatformManager(this, (ITorchServer)torch, Log);
|
||||||
{
|
|
||||||
Log.Info("Initializing LuckPerms");
|
|
||||||
_bootstrap.Plugin.load();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Fatal(e);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_bootstrap.LoadLatch.countDown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TorchOnGameStateChanged(MySandboxGame game, TorchGameState newState)
|
Torch.Managers.GetManager<ITorchSessionManager>().AddFactory(_ => platformManager);
|
||||||
{
|
|
||||||
if (_bootstrap is null)
|
|
||||||
throw new InvalidOperationException("Plugin is not initialized");
|
|
||||||
|
|
||||||
switch (newState)
|
|
||||||
{
|
|
||||||
case TorchGameState.Loading:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Log.Info("Loading LuckPerms");
|
|
||||||
Thread.currentThread().setContextClassLoader(LpDependencyManager.CurrentClassLoader);
|
|
||||||
_bootstrap.Plugin.enable();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Fatal(e);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_bootstrap.EnableLatch.countDown();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TorchGameState.Unloading:
|
|
||||||
Log.Info("Unloading LuckPerms");
|
|
||||||
_bootstrap.Plugin.disable();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -58,7 +58,6 @@ server: global
|
|||||||
# |=> MongoDB
|
# |=> MongoDB
|
||||||
#
|
#
|
||||||
# | Flatfile/local database - don't require any extra configuration
|
# | Flatfile/local database - don't require any extra configuration
|
||||||
# |=> H2 (preferred over SQLite)
|
|
||||||
# |=> SQLite
|
# |=> SQLite
|
||||||
#
|
#
|
||||||
# | Readable & editable text files - don't require any extra configuration
|
# | Readable & editable text files - don't require any extra configuration
|
||||||
@@ -71,9 +70,9 @@ server: global
|
|||||||
# | and all stored in the same file by switching to a combined storage variant.
|
# | and all stored in the same file by switching to a combined storage variant.
|
||||||
# | Just add '-combined' to the end of the storage-method, e.g. 'yaml-combined'
|
# | Just add '-combined' to the end of the storage-method, e.g. 'yaml-combined'
|
||||||
#
|
#
|
||||||
# - A H2 database is the default option.
|
# - A SQLite database is the default option.
|
||||||
# - If you want to edit data manually in "traditional" storage files, we suggest using YAML.
|
# - If you want to edit data manually in "traditional" storage files, we suggest using YAML.
|
||||||
storage-method: h2
|
storage-method: sqlite
|
||||||
|
|
||||||
# The following block defines the settings for remote database storage methods.
|
# The following block defines the settings for remote database storage methods.
|
||||||
#
|
#
|
||||||
|
BIN
LuckPerms.Torch/libs/sqlite-jdbc-3.43.2.2.jar
Normal file
BIN
LuckPerms.Torch/libs/sqlite-jdbc-3.43.2.2.jar
Normal file
Binary file not shown.
@@ -45,6 +45,12 @@
|
|||||||
"resolved": "1.13.2",
|
"resolved": "1.13.2",
|
||||||
"contentHash": "XwNhfkr7IeUiH8AE4pzob8YioxfL6nxgAx+fHEeWCObY/NZuBMfWLh39FznXbneKvagiqeeI7quIvZ6P1eVaEA=="
|
"contentHash": "XwNhfkr7IeUiH8AE4pzob8YioxfL6nxgAx+fHEeWCObY/NZuBMfWLh39FznXbneKvagiqeeI7quIvZ6P1eVaEA=="
|
||||||
},
|
},
|
||||||
|
"Torch.Loader": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[1.0.0, )",
|
||||||
|
"resolved": "1.0.0",
|
||||||
|
"contentHash": "GAf9Mv1t1/qTGHSgDqkiKAc7Xbh36+U8Ce1PuSoJZNKxHVmzbKHc3nSVz0dIBHhLE7Op8k60NfmclDRAQAppbQ=="
|
||||||
|
},
|
||||||
"Torch.Server.ReferenceAssemblies": {
|
"Torch.Server.ReferenceAssemblies": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[1.3.1.260-master, )",
|
"requested": "[1.3.1.260-master, )",
|
||||||
|
14
Torch.Loader/Torch.Loader.csproj
Normal file
14
Torch.Loader/Torch.Loader.csproj
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.Build.NoTargets/3.7.0">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net48</TargetFramework>
|
||||||
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
|
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||||
|
<Version>1.0.0</Version>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="buildTransitive\**\*" Pack="true" PackagePath="buildTransitive\%(RecursiveDir)%(Filename)%(Extension)" />
|
||||||
|
<None Include="..\LuckPerms.Loader\LuckPerms.Loader.csproj;..\LuckPerms.Loader\Plugin.cs" Pack="true" PackagePath="LuckPerms.Loader\%(RecursiveDir)%(Filename)%(Extension)" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
29
Torch.Loader/buildTransitive/Torch.Loader.targets
Normal file
29
Torch.Loader/buildTransitive/Torch.Loader.targets
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<Project>
|
||||||
|
<Target Name="BuildArchive" AfterTargets="AfterBuild"
|
||||||
|
Condition="$(DesignTimeBuild) != true Or $(BuildingForLiveUnitTesting) == true">
|
||||||
|
<PropertyGroup>
|
||||||
|
<PluginBasePath>$(MSBuildThisFileDirectory)..\LuckPerms.Loader\$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework)\</PluginBasePath>
|
||||||
|
<PluginZipPath>$(PluginBasePath)plugin.zip</PluginZipPath>
|
||||||
|
<PluginZipHashPath>$(PluginBasePath)plugin.zip.sha256</PluginZipHashPath>
|
||||||
|
<PluginNamePath>$(PluginBasePath)name.txt</PluginNamePath>
|
||||||
|
<PluginManifestPath>$(PluginBasePath)manifest.xml</PluginManifestPath>
|
||||||
|
<PluginOutputPath>$(ProjectDir)$(BaseOutputPath)$(Configuration)\plugin\</PluginOutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<MakeDir Directories="$(PluginBasePath)" ContinueOnError="true" />
|
||||||
|
<ZipDirectory DestinationFile="$(PluginZipPath)" SourceDirectory="$(OutputPath)" Overwrite="true" />
|
||||||
|
<GetFileHash Files="$(PluginZipPath)">
|
||||||
|
<Output TaskParameter="Hash"
|
||||||
|
ItemName="PluginZipHash" />
|
||||||
|
</GetFileHash>
|
||||||
|
<WriteLinesToFile File="$(PluginZipHashPath)" Lines="@(PluginZipHash)" Overwrite="true" />
|
||||||
|
<WriteLinesToFile File="$(PluginNamePath)" Lines="$(AssemblyName)" Overwrite="true" />
|
||||||
|
<Copy SourceFiles="$(ProjectDir)manifest.xml" DestinationFiles="$(PluginManifestPath)" SkipUnchangedFiles="true" />
|
||||||
|
|
||||||
|
<MSBuild Projects="$(MSBuildThisFileDirectory)..\LuckPerms.Loader\LuckPerms.Loader.csproj"
|
||||||
|
Properties="AssemblyName=$(AssemblyName).Loader;OutputPath=$(PluginOutputPath);Configuration=$(Configuration);TargetFrameworkRootPath=$(TargetFrameworkRootPath)"
|
||||||
|
Targets="Restore" />
|
||||||
|
<MSBuild Projects="$(MSBuildThisFileDirectory)..\LuckPerms.Loader\LuckPerms.Loader.csproj"
|
||||||
|
Properties="AssemblyName=$(AssemblyName).Loader;OutputPath=$(PluginOutputPath);Configuration=$(Configuration);TargetFrameworkRootPath=$(TargetFrameworkRootPath)" />
|
||||||
|
<RemoveDir Directories="$(PluginOutputPath)obj" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
@@ -10,6 +10,23 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuckPerms.Torch", "LuckPerm
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuckPerms.Loader", "LuckPerms.Loader\LuckPerms.Loader.csproj", "{D1D4E971-39CE-482C-A56D-9448A77883BB}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuckPerms.Loader", "LuckPerms.Loader\LuckPerms.Loader.csproj", "{D1D4E971-39CE-482C-A56D-9448A77883BB}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Torch.Loader", "Torch.Loader\Torch.Loader.csproj", "{AD9B7D1E-386A-4EF2-B475-BCB770537035}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kits", "Kits\Kits.csproj", "{DFEA7F04-6086-4D4B-A95E-100EEF793751}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{2C069BB5-B110-4024-93B7-28C6965AD21F} = {2C069BB5-B110-4024-93B7-28C6965AD21F}
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "plugins", "plugins", "{2C911BD8-8B11-460E-AB7E-16552949A6FC}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "loader", "loader", "{06CD2354-307D-4A1C-B46B-1D9EB3AAE742}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deprecated", "deprecated", "{557A4A51-B8ED-4CA0-866D-D18D219129F3}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "libs", "libs", "{862C7244-258E-4BFD-B271-9AA2D3FBE916}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "heh", "heh\heh.csproj", "{927CB303-E699-4716-A62E-232AE1125159}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -36,5 +53,27 @@ Global
|
|||||||
{D1D4E971-39CE-482C-A56D-9448A77883BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{D1D4E971-39CE-482C-A56D-9448A77883BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{D1D4E971-39CE-482C-A56D-9448A77883BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{D1D4E971-39CE-482C-A56D-9448A77883BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{D1D4E971-39CE-482C-A56D-9448A77883BB}.Release|Any CPU.Build.0 = Release|Any CPU
|
{D1D4E971-39CE-482C-A56D-9448A77883BB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{AD9B7D1E-386A-4EF2-B475-BCB770537035}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{AD9B7D1E-386A-4EF2-B475-BCB770537035}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{AD9B7D1E-386A-4EF2-B475-BCB770537035}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{AD9B7D1E-386A-4EF2-B475-BCB770537035}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{DFEA7F04-6086-4D4B-A95E-100EEF793751}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{DFEA7F04-6086-4D4B-A95E-100EEF793751}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{DFEA7F04-6086-4D4B-A95E-100EEF793751}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{DFEA7F04-6086-4D4B-A95E-100EEF793751}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{927CB303-E699-4716-A62E-232AE1125159}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{927CB303-E699-4716-A62E-232AE1125159}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{927CB303-E699-4716-A62E-232AE1125159}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{927CB303-E699-4716-A62E-232AE1125159}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(NestedProjects) = preSolution
|
||||||
|
{AD9B7D1E-386A-4EF2-B475-BCB770537035} = {06CD2354-307D-4A1C-B46B-1D9EB3AAE742}
|
||||||
|
{D1D4E971-39CE-482C-A56D-9448A77883BB} = {06CD2354-307D-4A1C-B46B-1D9EB3AAE742}
|
||||||
|
{DFEA7F04-6086-4D4B-A95E-100EEF793751} = {2C911BD8-8B11-460E-AB7E-16552949A6FC}
|
||||||
|
{2C069BB5-B110-4024-93B7-28C6965AD21F} = {2C911BD8-8B11-460E-AB7E-16552949A6FC}
|
||||||
|
{3963D8F4-CCB6-4305-8FEC-A19597404A19} = {557A4A51-B8ED-4CA0-866D-D18D219129F3}
|
||||||
|
{B1A35416-6CFB-4AE7-A2F2-818E8F7A8C13} = {557A4A51-B8ED-4CA0-866D-D18D219129F3}
|
||||||
|
{8F9D910F-FFE6-4010-921F-5872ACF638BB} = {557A4A51-B8ED-4CA0-866D-D18D219129F3}
|
||||||
|
{927CB303-E699-4716-A62E-232AE1125159} = {862C7244-258E-4BFD-B271-9AA2D3FBE916}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
43
heh/DbManager.cs
Normal file
43
heh/DbManager.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using System.IO;
|
||||||
|
using NLog;
|
||||||
|
using PetaPoco;
|
||||||
|
using PetaPoco.Core.Inflection;
|
||||||
|
using PetaPoco.Providers;
|
||||||
|
using Torch;
|
||||||
|
using Torch.API;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.Managers;
|
||||||
|
|
||||||
|
namespace heh;
|
||||||
|
|
||||||
|
public interface IDbManager : IManager
|
||||||
|
{
|
||||||
|
IDatabase Create(string name);
|
||||||
|
}
|
||||||
|
public class DbManager : Manager, IDbManager
|
||||||
|
{
|
||||||
|
private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
public static readonly IDbManager Static = new DbManager(TorchBase.Instance);
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
|
||||||
|
public DbManager(ITorchBase torchInstance) : base(torchInstance)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public IDatabase Create(string name)
|
||||||
|
{
|
||||||
|
return DatabaseConfiguration.Build()
|
||||||
|
.UsingProvider<SQLiteDatabaseProvider>()
|
||||||
|
.UsingExceptionThrown((_, args) => Log.Error(args.Exception))
|
||||||
|
.WithAutoSelect()
|
||||||
|
.UsingConnectionString($"Data Source={Path.Combine(Torch.Config.InstancePath, $"{name}.db")};Version=3;")
|
||||||
|
.UsingDefaultMapper<ConventionMapper>(mapper =>
|
||||||
|
{
|
||||||
|
string UnFuckIt(IInflector inflector, string s) => inflector.Underscore(s).ToLower();
|
||||||
|
mapper.InflectColumnName = UnFuckIt;
|
||||||
|
mapper.InflectTableName = UnFuckIt;
|
||||||
|
})
|
||||||
|
.Create();
|
||||||
|
}
|
||||||
|
}
|
3
heh/FodyWeavers.xml
Normal file
3
heh/FodyWeavers.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||||
|
<PropertyChanged />
|
||||||
|
</Weavers>
|
35
heh/StringExtensions.cs
Normal file
35
heh/StringExtensions.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
namespace heh;
|
||||||
|
|
||||||
|
// https://github.com/ServiceStack/ServiceStack.Text/blob/master/src/ServiceStack.Text/StringExtensions.cs
|
||||||
|
public static class StringExtensions
|
||||||
|
{
|
||||||
|
public static bool Glob(this string value, string pattern)
|
||||||
|
{
|
||||||
|
int pos;
|
||||||
|
for (pos = 0; pattern.Length != pos; pos++)
|
||||||
|
{
|
||||||
|
switch (pattern[pos])
|
||||||
|
{
|
||||||
|
case '?':
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '*':
|
||||||
|
for (var i = value.Length; i >= pos; i--)
|
||||||
|
{
|
||||||
|
if (Glob(value.Substring(i), pattern.Substring(pos + 1)))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (value.Length == pos || char.ToUpper(pattern[pos]) != char.ToUpper(value[pos]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.Length == pos;
|
||||||
|
}
|
||||||
|
}
|
71
heh/Utils/ChangeListener.cs
Normal file
71
heh/Utils/ChangeListener.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using NLog;
|
||||||
|
namespace heh.Utils;
|
||||||
|
|
||||||
|
public abstract class ChangeListener : INotifyPropertyChanged, IDisposable
|
||||||
|
{
|
||||||
|
#region *** Members ***
|
||||||
|
protected static readonly ILogger Log = LogManager.GetCurrentClassLogger();
|
||||||
|
protected string? PropertyName;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region *** Abstract Members ***
|
||||||
|
protected abstract void Unsubscribe();
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region *** INotifyPropertyChanged Members and Invoker ***
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
|
protected virtual void RaisePropertyChanged(string propertyName)
|
||||||
|
{
|
||||||
|
var temp = PropertyChanged;
|
||||||
|
temp?.Invoke(this, new(propertyName));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region *** Disposable Pattern ***
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
Unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~ChangeListener()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region *** Factory ***
|
||||||
|
public static ChangeListener? Create(object value, string? propertyName = null)
|
||||||
|
{
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case INotifyCollectionChanged collectionChanged and IEnumerable:
|
||||||
|
return new CollectionChangeListener(collectionChanged, propertyName);
|
||||||
|
case INotifyPropertyChanged propertyChanged:
|
||||||
|
return new ChildChangeListener(propertyChanged, propertyName);
|
||||||
|
default:
|
||||||
|
Log.Warn("changes in {0} type cannot be watched", value.GetType().FullName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
131
heh/Utils/ChildChangeListener.cs
Normal file
131
heh/Utils/ChildChangeListener.cs
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
|
namespace heh.Utils;
|
||||||
|
|
||||||
|
public class ChildChangeListener : ChangeListener
|
||||||
|
{
|
||||||
|
#region *** Members ***
|
||||||
|
protected static readonly Type InotifyType = typeof(INotifyPropertyChanged);
|
||||||
|
|
||||||
|
private readonly INotifyPropertyChanged _value;
|
||||||
|
private readonly Type _type;
|
||||||
|
private readonly Dictionary<string?, ChangeListener?> _childListeners = new();
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region *** Constructors ***
|
||||||
|
public ChildChangeListener(INotifyPropertyChanged instance)
|
||||||
|
{
|
||||||
|
_value = instance ?? throw new ArgumentNullException(nameof(instance));
|
||||||
|
_type = _value.GetType();
|
||||||
|
|
||||||
|
Subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChildChangeListener(INotifyPropertyChanged instance, string? propertyName)
|
||||||
|
: this(instance)
|
||||||
|
{
|
||||||
|
PropertyName = propertyName;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region *** Private Methods ***
|
||||||
|
private void Subscribe()
|
||||||
|
{
|
||||||
|
_value.PropertyChanged += value_PropertyChanged;
|
||||||
|
|
||||||
|
var query =
|
||||||
|
from property
|
||||||
|
in _type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||||
|
where InotifyType.IsAssignableFrom(property.PropertyType)
|
||||||
|
select property;
|
||||||
|
|
||||||
|
foreach (var property in query)
|
||||||
|
{
|
||||||
|
// Declare property as known "Child", then register it
|
||||||
|
_childListeners.Add(property.Name, null);
|
||||||
|
ResetChildListener(property.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets known (must exist in children collection) child event handlers
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyName">Name of known child property</param>
|
||||||
|
private void ResetChildListener(string? propertyName)
|
||||||
|
{
|
||||||
|
if (propertyName is null || !_childListeners.TryGetValue(propertyName, out var childListener))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Unsubscribe if existing
|
||||||
|
if (childListener != null)
|
||||||
|
{
|
||||||
|
childListener.PropertyChanged -= child_PropertyChanged;
|
||||||
|
|
||||||
|
// Should unsubscribe all events
|
||||||
|
childListener.Dispose();
|
||||||
|
_childListeners.Remove(propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
var property = _type.GetProperty(propertyName);
|
||||||
|
if (property == null)
|
||||||
|
throw new InvalidOperationException($"Was unable to get '{propertyName}' property information from Type '{_type.Name}'");
|
||||||
|
|
||||||
|
var newValue = property.GetValue(_value, null);
|
||||||
|
|
||||||
|
if (newValue is not null)
|
||||||
|
_childListeners[propertyName] = Create(newValue, propertyName);
|
||||||
|
|
||||||
|
if (_childListeners[propertyName] != null)
|
||||||
|
_childListeners[propertyName]!.PropertyChanged += child_PropertyChanged;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region *** Event Handler ***
|
||||||
|
private void child_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
RaisePropertyChanged(e.PropertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void value_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
// First, reset child on change, if required...
|
||||||
|
ResetChildListener(e.PropertyName);
|
||||||
|
|
||||||
|
// ...then, notify about it
|
||||||
|
RaisePropertyChanged(e.PropertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void RaisePropertyChanged(string propertyName)
|
||||||
|
{
|
||||||
|
// Special Formatting
|
||||||
|
base.RaisePropertyChanged($"{PropertyName}{(PropertyName != null ? "." : null)}{propertyName}");
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region *** Overrides ***
|
||||||
|
/// <summary>
|
||||||
|
/// Release all child handlers and self handler
|
||||||
|
/// </summary>
|
||||||
|
protected override void Unsubscribe()
|
||||||
|
{
|
||||||
|
_value.PropertyChanged -= value_PropertyChanged;
|
||||||
|
|
||||||
|
foreach (var kv in _childListeners)
|
||||||
|
{
|
||||||
|
kv.Value?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_childListeners.Clear();
|
||||||
|
|
||||||
|
Debug.WriteLine("ChildChangeListener '{0}' unsubscribed", PropertyName);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
123
heh/Utils/CollectionChangeListener.cs
Normal file
123
heh/Utils/CollectionChangeListener.cs
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
|
namespace heh.Utils;
|
||||||
|
|
||||||
|
public class CollectionChangeListener : ChangeListener
|
||||||
|
{
|
||||||
|
#region *** Members ***
|
||||||
|
private readonly INotifyCollectionChanged _value;
|
||||||
|
private readonly Dictionary<INotifyPropertyChanged, ChangeListener> _collectionListeners = new();
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region *** Constructors ***
|
||||||
|
public CollectionChangeListener(INotifyCollectionChanged collection, string? propertyName)
|
||||||
|
{
|
||||||
|
_value = collection;
|
||||||
|
PropertyName = propertyName;
|
||||||
|
|
||||||
|
if (_value.GetType().IsGenericType && !typeof(INotifyPropertyChanged).IsAssignableFrom(_value.GetType().GetGenericArguments()[0]))
|
||||||
|
return;
|
||||||
|
Subscribe();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region *** Private Methods ***
|
||||||
|
private void Subscribe()
|
||||||
|
{
|
||||||
|
_value.CollectionChanged += value_CollectionChanged;
|
||||||
|
|
||||||
|
foreach (INotifyPropertyChanged item in (IEnumerable)_value)
|
||||||
|
{
|
||||||
|
ResetChildListener(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetChildListener(INotifyPropertyChanged item)
|
||||||
|
{
|
||||||
|
if (item == null)
|
||||||
|
throw new ArgumentNullException(nameof(item));
|
||||||
|
|
||||||
|
RemoveItem(item);
|
||||||
|
|
||||||
|
var listener = Create(item)!;
|
||||||
|
|
||||||
|
listener.PropertyChanged += listener_PropertyChanged;
|
||||||
|
_collectionListeners.Add(item, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveItem(INotifyPropertyChanged item)
|
||||||
|
{
|
||||||
|
// Remove old
|
||||||
|
if (!_collectionListeners.ContainsKey(item))
|
||||||
|
return;
|
||||||
|
_collectionListeners[item].PropertyChanged -= listener_PropertyChanged;
|
||||||
|
|
||||||
|
_collectionListeners[item].Dispose();
|
||||||
|
_collectionListeners.Remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void ClearCollection()
|
||||||
|
{
|
||||||
|
foreach (var key in _collectionListeners.Keys)
|
||||||
|
{
|
||||||
|
_collectionListeners[key].Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_collectionListeners.Clear();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region *** Event handlers ***
|
||||||
|
private void value_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Action == NotifyCollectionChangedAction.Reset)
|
||||||
|
{
|
||||||
|
ClearCollection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't care about e.Action, if there are old items, Remove them...
|
||||||
|
if (e.OldItems != null)
|
||||||
|
{
|
||||||
|
foreach (INotifyPropertyChanged item in e.OldItems)
|
||||||
|
RemoveItem(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...add new items as well
|
||||||
|
if (e.NewItems != null)
|
||||||
|
{
|
||||||
|
foreach (INotifyPropertyChanged item in e.NewItems)
|
||||||
|
ResetChildListener(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void listener_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
// ...then, notify about it
|
||||||
|
RaisePropertyChanged($"{PropertyName}{(PropertyName != null ? "[]." : null)}{e.PropertyName}");
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region *** Overrides ***
|
||||||
|
/// <summary>
|
||||||
|
/// Releases all collection item handlers and self handler
|
||||||
|
/// </summary>
|
||||||
|
protected override void Unsubscribe()
|
||||||
|
{
|
||||||
|
ClearCollection();
|
||||||
|
|
||||||
|
_value.CollectionChanged -= value_CollectionChanged;
|
||||||
|
|
||||||
|
Debug.WriteLine("CollectionChangeListener unsubscribed");
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
84
heh/Utils/ProperPersistent.cs
Normal file
84
heh/Utils/ProperPersistent.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
using NLog;
|
||||||
|
using Torch;
|
||||||
|
namespace heh.Utils;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simple class that manages saving <see cref="Persistent{T}.Data"/> to disk using XML serialization.
|
||||||
|
/// Will automatically save on changes in the data class.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TViewModel">Data class type</typeparam>
|
||||||
|
public sealed class ProperPersistent<TViewModel> : IDisposable where TViewModel : class, INotifyPropertyChanged, new()
|
||||||
|
{
|
||||||
|
private static readonly XmlSerializer Serializer = new(typeof(TViewModel));
|
||||||
|
private static readonly ILogger Log = LogManager.GetLogger($"ProperPersistent_{typeof(TViewModel)}");
|
||||||
|
|
||||||
|
private readonly ChangeListener _listener;
|
||||||
|
private Timer? _saveConfigTimer;
|
||||||
|
|
||||||
|
public TViewModel Data { get; }
|
||||||
|
public string Path { get; set; }
|
||||||
|
|
||||||
|
public ProperPersistent(string path, TViewModel? defaultViewModel = default)
|
||||||
|
{
|
||||||
|
Path = path;
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var stream = File.OpenRead(path);
|
||||||
|
Data = (TViewModel) Serializer.Deserialize(stream);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e);
|
||||||
|
Data = defaultViewModel ?? new TViewModel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Data = defaultViewModel ?? new TViewModel();
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
_listener = ChangeListener.Create(Data)!;
|
||||||
|
_listener.PropertyChanged += ListenerOnPropertyChanged;
|
||||||
|
}
|
||||||
|
private void ListenerOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
SaveAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveAsync()
|
||||||
|
{
|
||||||
|
_saveConfigTimer ??= new(_ => Save());
|
||||||
|
_saveConfigTimer.Change(1000, -1);
|
||||||
|
}
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_listener.Dispose();
|
||||||
|
_saveConfigTimer?.Dispose();
|
||||||
|
_saveConfigTimer = null;
|
||||||
|
}
|
||||||
|
#region Backwards compatibility
|
||||||
|
public void Save(string? newPath = null)
|
||||||
|
{
|
||||||
|
if (newPath is not null)
|
||||||
|
Path = newPath;
|
||||||
|
|
||||||
|
using var stream = File.Create(Path);
|
||||||
|
using var writer = new XmlTextWriter(stream, Encoding.UTF8)
|
||||||
|
{
|
||||||
|
Formatting = Formatting.Indented
|
||||||
|
};
|
||||||
|
|
||||||
|
Serializer.Serialize(writer, Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ProperPersistent<TViewModel> Load(string path, bool saveIfNew = true) => new(path);
|
||||||
|
#endregion
|
||||||
|
}
|
21
heh/heh.csproj
Normal file
21
heh/heh.csproj
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net48</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<LangVersion>10</LangVersion>
|
||||||
|
<UseWpf>true</UseWpf>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="PetaPoco.Compiled" Version="6.0.480" />
|
||||||
|
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" PrivateAssets="all" />
|
||||||
|
<PackageReference Include="Torch.Server.ReferenceAssemblies" Version="1.3.1.207-master" PrivateAssets="all" IncludeAssets="compile" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
Reference in New Issue
Block a user