This commit is contained in:
zznty
2022-05-24 15:49:43 +07:00
commit c20e468b99
32 changed files with 1654 additions and 0 deletions

8
LightPerms/Config.cs Normal file
View File

@@ -0,0 +1,8 @@
using Torch;
namespace LightPerms;
public class Config : ViewModel
{
}

View File

@@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<PropertyChanged />
</Weavers>

47
LightPerms/Group.cs Normal file
View File

@@ -0,0 +1,47 @@
using PetaPoco;
namespace LightPerms;
[TableName("groups")]
[PrimaryKey(nameof(Uid), AutoIncrement = true)]
public class Group
{
[Column]
public long Uid { get; set; }
[Column]
public string Name { get; set; } = "group";
internal const string CreateTableSql = "create table if not exists groups (uid INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL);";
}
[TableName("permissions")]
[PrimaryKey(nameof(Uid), AutoIncrement = true)]
public class Permission
{
[Column]
public long Uid { get; set; }
[Column]
public string Value { get; set; } = "*";
[Column]
public long GroupUid { get; set; }
internal const string CreateTableSql = "create table if not exists permissions (uid INTEGER PRIMARY KEY AUTOINCREMENT, value TEXT NOT NULL, group_uid INTEGER, FOREIGN KEY(group_uid) REFERENCES groups(uid));";
}
[TableName("group_members")]
[PrimaryKey(nameof(Uid), AutoIncrement = true)]
public class GroupMember
{
[Column]
public long Uid { get; set; }
[Column]
public string Name { get; set; } = "no name";
[Column]
public string ClientId { get; set; } = "0";
[Column]
public long GroupUid { get; set; }
internal const string CreateTableSql = "create table if not exists group_members (uid INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, client_id INTEGER, group_uid INTEGER, FOREIGN KEY(group_uid) REFERENCES groups(uid));";
}

View File

@@ -0,0 +1,88 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>10</LangVersion>
<UseWpf>true</UseWpf>
<TorchDir>$(SolutionDir)TorchBinaries\</TorchDir>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugType>none</DebugType>
</PropertyGroup>
<ItemGroup>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c">
<HintPath>$(TorchDir)NLog.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Sandbox.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>$(TorchDir)DedicatedServer64\Sandbox.Common.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Sandbox.Game, Version=0.1.1.0, Culture=neutral, PublicKeyToken=null">
<HintPath>$(TorchDir)DedicatedServer64\Sandbox.Game.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Sandbox.Graphics, Version=0.1.1.0, Culture=neutral, PublicKeyToken=null">
<HintPath>$(TorchDir)DedicatedServer64\Sandbox.Graphics.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Net.Http" />
<Reference Include="Torch">
<HintPath>$(TorchDir)Torch.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Torch.API">
<HintPath>$(TorchDir)Torch.API.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Torch.Server">
<HintPath>$(TorchDir)Torch.Server.exe</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>$(TorchDir)DedicatedServer64\VRage.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Game, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>$(TorchDir)DedicatedServer64\VRage.Game.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Input, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>$(TorchDir)DedicatedServer64\VRage.Input.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Library, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>$(TorchDir)DedicatedServer64\VRage.Library.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Math, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>$(TorchDir)DedicatedServer64\VRage.Math.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Network, Version=1.0.53.0, Culture=neutral, PublicKeyToken=null">
<HintPath>$(TorchDir)DedicatedServer64\VRage.Network.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="heh" Version="1.0.4" />
<PackageReference Include="PropertyChanged.Fody" Version="4.0.0" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<None Update="manifest.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<Target Name="CopyToTorch" AfterTargets="Build">
<ZipDirectory DestinationFile="$(TorchDir)Plugins\$(AssemblyName).zip" SourceDirectory="$(TargetDir)" Overwrite="true" />
</Target>
</Project>

163
LightPerms/LpCommands.cs Normal file
View File

@@ -0,0 +1,163 @@
using PetaPoco;
using Torch.API.Managers;
using Torch.Commands;
using Torch.Commands.Permissions;
using VRage.Game.ModAPI;
namespace LightPerms;
[Category("lp")]
public class LpCommands : CommandModule
{
private IPermissionsManager Pm => Context.Torch.Managers.GetManager<IPermissionsManager>();
#if DEBUG
[Command("gen")]
public void GenTest()
{
var db = Pm.Db;
db.Execute("delete from groups");
db.Execute("delete from group_members");
db.Execute("delete from permissions");
var playerGroup = (long)db.Insert(new Group {Name = "player"});
db.Insert(new Permission {Value = "test.permission.player", GroupUid = playerGroup});
var adminGroup = (long)db.Insert(new Group {Name = "admin"});
db.Insert(new Permission {Value = "test.permission.admin", GroupUid = adminGroup});
db.Insert(new GroupMember {Name = Context.Player.DisplayName, ClientId = Context.Player.SteamUserId.ToString(), GroupUid = playerGroup});
}
#endif
[Command("has perm")]
[Permission(MyPromoteLevel.Admin)]
public void HasPermission(string perm, ulong clientId = 0)
{
clientId = Context.Player?.SteamUserId ?? clientId;
if (clientId == 0)
{
Context.Respond("Specify valid client id.");
return;
}
Context.Respond(Pm.HasPermission(clientId, perm) ? "Yes" : "No");
}
[Command("get groups")]
[Permission(MyPromoteLevel.Admin)]
public void GetGroups()
{
Context.Respond($"Available groups:\n {string.Join("\n ", Pm.Db.Query<Group>().Select(b => $"{b.Name} - id {b.Uid}"))}");
}
[Command("get perms")]
[Permission(MyPromoteLevel.Admin)]
public void GetPerms(string groupName)
{
if (Pm.Db.SingleOrDefault<Group>(Sql.Builder.Where("name = @0", groupName)) is not { } group)
{
Context.Respond("No group found with given name");
return;
}
Context.Respond($"Available perms:\n {string.Join("\n ", Pm.Db.Query<Permission>(Sql.Builder.Where("group_uid = @0", group.Uid)).Select(b => b.Value).OrderBy(b => b))}");
}
[Command("add group")]
[Permission(MyPromoteLevel.Admin)]
public void AddGroup(string groupName)
{
if (Pm.Db.Exists<Group>("name = @0", groupName))
{
Context.Respond("Group already exists");
return;
}
Pm.Db.Insert(new Group
{
Name = groupName
});
Context.Respond("Added");
}
[Command("add perm")]
[Permission(MyPromoteLevel.Admin)]
public void AddPerm(string groupName, string permission)
{
if (Pm.Db.SingleOrDefault<Group>(Sql.Builder.Where("name = @0", groupName)) is not { } group)
{
Context.Respond("No group found with given name");
return;
}
if (Pm.Db.Exists<Permission>("group_uid = @0 and value like @1", group.Uid, permission.Replace('*', '%')))
{
Context.Respond("Permission already exists");
return;
}
Pm.Db.Insert(new Permission
{
Value = permission,
GroupUid = group.Uid
});
Context.Respond("Added");
}
[Command("del group")]
[Permission(MyPromoteLevel.Admin)]
public void DelGroup(string groupName)
{
if (Pm.Db.Delete<Group>(Sql.Builder.Where("name = @0", groupName)) != 1)
{
Context.Respond("Group not exists");
return;
}
Context.Respond("Deleted");
}
[Command("del perm")]
[Permission(MyPromoteLevel.Admin)]
public void DelPerm(string groupName, string permission)
{
if (Pm.Db.SingleOrDefault<Group>(Sql.Builder.Where("name = @0", groupName)) is not { } group)
{
Context.Respond("No group found with given name");
return;
}
var count = Pm.Db.Delete<Permission>(Sql.Builder.Where("group_uid = @0 and value like @1", group.Uid, permission.Replace('*', '%')));
if (count > 0)
{
Context.Respond($"Deleted {count} permissions");
return;
}
Context.Respond("No permissions found");
}
[Command("assign group")]
[Permission(MyPromoteLevel.Admin)]
public void AssignGroup(string groupName, ulong clientId = 0)
{
if (clientId == 0)
clientId = Context.Player.SteamUserId;
if (Pm.Db.SingleOrDefault<Group>(Sql.Builder.Where("name = @0", groupName)) is not { } group)
{
Context.Respond("No group found with given name");
return;
}
if (Pm.Db.Exists<GroupMember>("group_uid = @0 and client_id = @1", group.Uid, clientId))
{
Context.Respond("User already assigned to this group");
return;
}
Pm.AssignGroup(clientId, groupName);
Context.Respond("User assigned to the group");
}
}

View File

@@ -0,0 +1,37 @@
using Torch.API;
using Torch.API.Managers;
using Torch.Managers;
namespace LightPerms;
public class MultiplayerMembersManager : Manager
{
[Dependency]
private readonly IPermissionsManager _permissionsManager = null!;
[Dependency]
private readonly IMultiplayerManagerServer _multiplayerManager = null!;
public MultiplayerMembersManager(ITorchBase torchInstance) : base(torchInstance)
{
}
public override void Attach()
{
base.Attach();
_multiplayerManager.PlayerJoined += MultiplayerManagerOnPlayerJoined;
}
private void MultiplayerManagerOnPlayerJoined(IPlayer player)
{
if (_permissionsManager.Db.Exists<GroupMember>("client_id = @0", player.SteamId))
return;
var groupMember = new GroupMember
{
Name = player.Name,
ClientId = player.SteamId.ToString(),
GroupUid = 0
};
_permissionsManager.Db.Insert(groupMember);
}
}

View File

@@ -0,0 +1,85 @@
using heh;
using NLog;
using PetaPoco;
using Sandbox.Game.Multiplayer;
using Torch.API;
using Torch.API.Managers;
using Torch.Managers;
namespace LightPerms;
public interface IPermissionsManager : IManager
{
bool HasPermission(ulong clientId, string permission);
void AssignGroup(ulong clientId, string groupName);
Group? GetGroup(string groupName);
IDatabase Db { get; }
}
public class PermissionsManager : Manager, IPermissionsManager
{
private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
private static readonly char[] InvalidPermissionChars = {'*', '?'};
[Dependency]
private readonly IDbManager _dbManager = null!;
public PermissionsManager(ITorchBase torchInstance) : base(torchInstance)
{
}
public override void Attach()
{
base.Attach();
Db = _dbManager.Create("light_perms");
Db.Execute(Group.CreateTableSql);
Db.Execute(Permission.CreateTableSql);
Db.Execute(GroupMember.CreateTableSql);
if (GetGroup("player") is null)
Db.Insert(new Group
{
Name = "player"
});
}
public bool HasPermission(ulong clientId, string permission)
{
if (permission.IndexOfAny(InvalidPermissionChars) > -1)
throw new InvalidOperationException("Permission should not contain any invalid characters");
var member = Db.SingleOrDefault<GroupMember>(Sql.Builder.Where("client_id = @0", clientId));
var result = member is not null && Db.Exists<Permission>("group_uid = @0 and @1 like replace(replace(value,'*','%'),'?','_')", member.GroupUid, permission);
if (!result)
Log.Info("User ({0}) has no permission '{1}'", clientId, permission);
return result;
}
public void AssignGroup(ulong clientId, string groupName)
{
if (Sync.Players.TryGetPlayerIdentity(new(clientId)) is not { } identity)
throw new InvalidOperationException($"Invalid client id {clientId}");
if (Db.SingleOrDefault<Group>(Sql.Builder.Where("name = @0", groupName)) is not { } group)
throw new InvalidOperationException($"Invalid group name {groupName}");
if (Db.SingleOrDefault<GroupMember>(Sql.Builder.Where("client_id = @0", clientId)) is { } groupMember)
Db.Delete(groupMember);
Db.Insert(new GroupMember
{
Name = identity.DisplayName,
ClientId = clientId.ToString(),
GroupUid = group.Uid
});
}
public Group? GetGroup(string groupName)
{
return Db.SingleOrDefault<Group>(Sql.Builder.Where("name = @0", groupName));
}
public IDatabase Db { get; private set; } = null!;
}

31
LightPerms/Plugin.cs Normal file
View File

@@ -0,0 +1,31 @@
using System.IO;
using System.Windows.Controls;
using heh;
using Torch;
using Torch.API;
using Torch.API.Managers;
using Torch.API.Plugins;
using Torch.API.Session;
using Torch.Views;
namespace LightPerms;
public class Plugin : TorchPluginBase, IWpfPlugin
{
private Persistent<Config> _config = null!;
public override void Init(ITorchBase torch)
{
base.Init(torch);
_config = Persistent<Config>.Load(Path.Combine(StoragePath, "LightPerms.cfg"));
Torch.Managers.AddManager(DbManager.Static);
Torch.Managers.AddManager(new PermissionsManager(Torch));
Torch.Managers.GetManager<ITorchSessionManager>().AddFactory(s => new MultiplayerMembersManager(s.Torch));
}
public UserControl GetControl() => new PropertyGrid
{
Margin = new(3),
DataContext = _config.Data
};
}

25
LightPerms/README.md Normal file
View File

@@ -0,0 +1,25 @@
Light plugin to add permission system (like minecraft's PermissionEx or LuckPerms) to your server.
In this plugins all permissions can be wildcards and belongs to groups
By default plugin creates and assigns `player` group to all players automatically (currently unchangeable)
### Commands
+ `!lp get groups` - Returns list of all groups
+ `!lp add group <group name>` - Creates a new group
+ `!lp del groups <group name>` - Deletes the group
+ `!lp get perms <group name>` - Returns list of permissions in the group
+ `!lp add perm <group name> <permission>` - Adds a new permission to the group
+ `!lp del perm <group name> <permission>` - Deletes a permission from the group
+ `!lp has perm <permission> <client id>` - Returns if player has a permission (client id can be removed to use the sender id)
+ `!lp assign group <group name> <client id>` - Assigns a group to the player (client id can be removed to use the sender id)
### Groups based on discord roles
To automatically assign a group to players if they have a specific role in discord server use [LightPerms.Discord](https://torchapi.com/plugins/view/?guid=d53cf5e6-27ea-491b-9579-8506d93f184b) plugin
### Support in plugins
+ [Kits](https://torchapi.com/plugins/view/?guid=d095391d-b5ec-43a9-8ba4-6c4909227e6e)

6
LightPerms/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>LightPerms</Name>
<Guid>5c3f35b3-ac9d-486f-8559-f931536c6700</Guid>
<Version>v1.0.1</Version>
</PluginManifest>

20
LightPerms/setup.bat Normal file
View File

@@ -0,0 +1,20 @@
:: This script creates a symlink to the game binaries to account for different installation directories on different systems.
@echo off
set /p path="Please enter the folder location of your Torch.Server.exe: "
cd %~dp0
rmdir TorchBinaries > nul 2>&1
mklink /J ..\TorchBinaries "%path%"
if errorlevel 1 goto Error
echo Done!
echo You can now open the plugin without issue.
goto EndFinal
:Error
echo An error occured creating the symlink.
goto EndFinal
:EndFinal
pause