actually now its usable

This commit is contained in:
zznty
2023-11-13 23:17:39 +07:00
parent aecc7ee66f
commit ce07a1e86a
41 changed files with 1401 additions and 138 deletions

25
Kits/Commands.cs Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
};
}

View 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>

View 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
View 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();
}
}

View 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>

View 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);
}
}

View 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>

6
Kits/manifest.xml Normal file
View File

@@ -0,0 +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>Kits</Name>
<Guid>d095391d-b5ec-43a9-8ba4-6c4909227e6e</Guid>
<Version>v1.0.6</Version>
</PluginManifest>

73
Kits/schema.xsd Normal file
View 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>