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"?>
|
||||
<PluginManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<Name>LuckPerms.Loader</Name>
|
||||
<Guid>7E4B3CC8-64FA-416E-8910-AACDF2DA5E2C</Guid>
|
||||
<Version>v5.4.106.3</Version>
|
||||
<Name>Kits</Name>
|
||||
<Guid>d095391d-b5ec-43a9-8ba4-6c4909227e6e</Guid>
|
||||
<Version>v1.0.6</Version>
|
||||
</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>
|
||||
|
||||
<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="PropertyChanged.Fody" Version="4.0.0" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
@@ -25,4 +24,8 @@
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\heh\heh.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@@ -8,18 +8,30 @@
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
</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' ">
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
<DebugType>none</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<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">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<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" />
|
||||
</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>
|
||||
<Reference Include="System.IO.Compression" />
|
||||
</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>
|
@@ -6,64 +6,102 @@ using Torch;
|
||||
using Torch.API;
|
||||
using Torch.API.Managers;
|
||||
using Torch.API.Plugins;
|
||||
using Torch.Collections;
|
||||
using Torch.Managers;
|
||||
using Torch.Utils;
|
||||
|
||||
namespace LuckPerms.Loader;
|
||||
|
||||
public class Plugin : TorchPluginBase
|
||||
{
|
||||
private static readonly ITorchPlugin MainPluginInstance;
|
||||
private static readonly ILogger Log = LogManager.GetLogger("LuckPerms.Loader");
|
||||
private static readonly ILogger Log = LogManager.GetLogger("Loader");
|
||||
|
||||
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
|
||||
var torch = (ITorchServer)TorchBase.Instance;
|
||||
#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 (File.Exists(hashPath))
|
||||
{
|
||||
Log.Info("Checking cache");
|
||||
|
||||
var hash = File.ReadAllBytes(hashPath);
|
||||
|
||||
if (hash.SequenceEqual(currentHash)) return;
|
||||
}
|
||||
|
||||
dir.Delete(true);
|
||||
}
|
||||
|
||||
Log.Info($"Extracting cache to {dir}");
|
||||
|
||||
using (var pluginStream = typeof(Plugin).Assembly.GetManifestResourceStream("plugin.zip")!)
|
||||
using (var archive = new ZipArchive(pluginStream, ZipArchiveMode.Read))
|
||||
archive.ExtractToDirectory(dir.FullName);
|
||||
using var pluginStream = typeof(Plugin).Assembly.GetManifestResourceStream("plugin.zip")!;
|
||||
using var archive = new ZipArchive(pluginStream, ZipArchiveMode.Read);
|
||||
|
||||
Log.Info("Injecting LuckPerms");
|
||||
archive.ExtractToDirectory(dir.FullName);
|
||||
File.WriteAllBytes(hashPath, currentHash);
|
||||
}
|
||||
|
||||
ExtractCache();
|
||||
|
||||
Log.Info($"Injecting {assemblyName}");
|
||||
|
||||
AppDomain.CurrentDomain.AssemblyResolve += (_, args) =>
|
||||
{
|
||||
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");
|
||||
|
||||
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);
|
||||
|
||||
TorchBase.RegisterAuxAssembly(mainAssembly);
|
||||
typeof(TorchBase).GetMethod("RegisterAuxAssembly", BindingFlags.NonPublic | BindingFlags.Static)!.Invoke(null, new object[] { mainAssembly });
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
pluginManager._plugins.Add(Manifest.Guid, MainPluginInstance);
|
||||
var pluginManager = torch.Managers.GetManager<PluginManager>();
|
||||
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);
|
||||
|
||||
|
@@ -2,12 +2,6 @@
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
".NETFramework,Version=v4.8": {
|
||||
"Krafs.Publicizer": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.2.1, )",
|
||||
"resolved": "2.2.1",
|
||||
"contentHash": "QGI4nMGQbKsuFUUboixVHu4mv3lHB5RejIa7toIlzTmwLkuCYYEpUBJjmy3OpXYyj5dVSZAXVbr4oeMSloE67Q=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.3, )",
|
||||
@@ -62,6 +56,7 @@
|
||||
"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()
|
||||
.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);
|
||||
|
||||
|
@@ -41,12 +41,12 @@ public class LpTorchPlugin(LuckPermsBootstrap bootstrap, ITorchBase torch) : Abs
|
||||
private LpContextManager? _contextManager;
|
||||
private LpSenderFactory? _senderFactory;
|
||||
private LpConnectionListener? _connectionListener;
|
||||
private global::Torch.Managers.DependencyManager? _dependencyManager;
|
||||
public override LuckPermsBootstrap getBootstrap() => bootstrap;
|
||||
|
||||
protected override void setupSenderFactory()
|
||||
{
|
||||
_senderFactory = new LpSenderFactory(this);
|
||||
torch.Managers.GetManager<ITorchSessionManager>().AddFactory(_ => _senderFactory);
|
||||
_senderFactory = new(this);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
_commandManager = new LpCommandManager(this, _senderFactory!);
|
||||
torch.Managers.GetManager<ITorchSessionManager>().AddFactory(_ => _commandManager);
|
||||
_commandManager = new(this, _senderFactory!);
|
||||
}
|
||||
|
||||
protected override void setupManagers()
|
||||
@@ -71,16 +70,29 @@ public class LpTorchPlugin(LuckPermsBootstrap bootstrap, ITorchBase torch) : Abs
|
||||
_groupManager = new(this);
|
||||
_trackManager = 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(_ => 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 void setupContextManager()
|
||||
{
|
||||
_contextManager = new LpContextManager(this);
|
||||
torch.Managers.GetManager<ITorchSessionManager>().AddFactory(_ => _contextManager);
|
||||
_contextManager = new(this);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
@@ -24,6 +24,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="torch.server.referenceassemblies" Version="1.3.1.260-master" PrivateAssets="all" IncludeAssets="compile" />
|
||||
<PackageReference Include="Torch.Loader" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -100,9 +101,15 @@
|
||||
|
||||
<MySqlPath>$(LibsPath)mysql-connector-j-8.0.33.jar</MySqlPath>
|
||||
<ProtobufPath>$(LibsPath)protobuf-java-3.21.9.jar</ProtobufPath>
|
||||
|
||||
<SqlitePath>$(LibsPath)sqlite-jdbc-3.43.2.2.jar</SqlitePath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<IkvmReference Include="$(SqlitePath)">
|
||||
<References>$(Slf4jApiPath)</References>
|
||||
</IkvmReference>
|
||||
|
||||
<IkvmReference Include="$(MySqlPath)">
|
||||
<References>$(ProtobufPath)</References>
|
||||
</IkvmReference>
|
||||
@@ -122,7 +129,7 @@
|
||||
</IkvmReference>
|
||||
<IkvmReference Include="$(JnaPath)" />
|
||||
|
||||
<IkvmReference Include="$(H2Path)" />
|
||||
<!--<IkvmReference Include="$(H2Path)" />-->
|
||||
<IkvmReference Include="$(PostgreSqlPath)" />
|
||||
|
||||
<IkvmReference Include="$(MongoDriverLegacyPath)">
|
||||
@@ -234,21 +241,17 @@
|
||||
<IkvmReference Include="$(SnakeYamlPath)" />
|
||||
<IkvmReference Include="$(CheckerQualPath)" />
|
||||
|
||||
<IkvmReference Include="$(ApiJarPath)" />
|
||||
<IkvmReference Include="$(ApiJarPath)">
|
||||
<AssemblyName>api</AssemblyName>
|
||||
<DisableAutoAssemblyName>true</DisableAutoAssemblyName>
|
||||
</IkvmReference>
|
||||
<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>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="manifest.xml">
|
||||
<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\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\mysql.sql" LogicalName="me.lucko.luckperms.schema.mysql.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(
|
||||
typeof(net.luckperms.api.LuckPerms).Assembly.Location, // net.luckperms.api.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();
|
||||
|
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.util;
|
||||
using LuckPerms.Torch.Impl;
|
||||
using LuckPerms.Torch.PlatformApi;
|
||||
using NLog;
|
||||
using Sandbox;
|
||||
using Torch;
|
||||
using Torch.API;
|
||||
using Torch.API.Managers;
|
||||
using Torch.API.Session;
|
||||
using Exception = System.Exception;
|
||||
using Object = java.lang.Object;
|
||||
|
||||
@@ -23,58 +26,14 @@ public class Plugin : TorchPluginBase
|
||||
}
|
||||
|
||||
public static readonly ILogger Log = LogManager.GetLogger("LuckPerms");
|
||||
private LpTorchBootstrap? _bootstrap;
|
||||
|
||||
|
||||
public override void Init(ITorchBase torch)
|
||||
{
|
||||
base.Init(torch);
|
||||
Torch.GameStateChanged += TorchOnGameStateChanged;
|
||||
_bootstrap = new((ITorchServer)Torch, this, Log, Path.Combine(StoragePath, "luckperms"));
|
||||
|
||||
try
|
||||
{
|
||||
Log.Info("Initializing LuckPerms");
|
||||
_bootstrap.Plugin.load();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Fatal(e);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_bootstrap.LoadLatch.countDown();
|
||||
}
|
||||
}
|
||||
var platformManager = new LuckPermsPlatformManager(this, (ITorchServer)torch, Log);
|
||||
|
||||
private void TorchOnGameStateChanged(MySandboxGame game, TorchGameState newState)
|
||||
{
|
||||
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;
|
||||
}
|
||||
Torch.Managers.GetManager<ITorchSessionManager>().AddFactory(_ => platformManager);
|
||||
}
|
||||
}
|
@@ -58,7 +58,6 @@ server: global
|
||||
# |=> MongoDB
|
||||
#
|
||||
# | Flatfile/local database - don't require any extra configuration
|
||||
# |=> H2 (preferred over SQLite)
|
||||
# |=> SQLite
|
||||
#
|
||||
# | 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.
|
||||
# | 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.
|
||||
storage-method: h2
|
||||
storage-method: sqlite
|
||||
|
||||
# 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",
|
||||
"contentHash": "XwNhfkr7IeUiH8AE4pzob8YioxfL6nxgAx+fHEeWCObY/NZuBMfWLh39FznXbneKvagiqeeI7quIvZ6P1eVaEA=="
|
||||
},
|
||||
"Torch.Loader": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.0, )",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "GAf9Mv1t1/qTGHSgDqkiKAc7Xbh36+U8Ce1PuSoJZNKxHVmzbKHc3nSVz0dIBHhLE7Op8k60NfmclDRAQAppbQ=="
|
||||
},
|
||||
"Torch.Server.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
"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
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuckPerms.Loader", "LuckPerms.Loader\LuckPerms.Loader.csproj", "{D1D4E971-39CE-482C-A56D-9448A77883BB}"
|
||||
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
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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}.Release|Any CPU.ActiveCfg = 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
|
||||
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