Compare commits

...

3 Commits
v74 ... v1.0.77

Author SHA1 Message Date
Roflmuffin
319b116c5f fix: bugs in config manager & plugin load, fixes #138 2023-11-27 11:42:34 +10:00
Michael Wilson
e0dc053d22 Config Example Parsing (#136) 2023-11-26 20:30:21 +10:00
Charles_
f2e0dac32d Feature: ProcessTargetString() & GetPlayerFromSteamId() (#121)
Co-authored-by: Roflmuffin <shortguy014@gmail.com>
2023-11-26 15:03:01 +10:00
17 changed files with 273 additions and 36 deletions

View File

@@ -152,17 +152,7 @@ namespace CounterStrikeSharp.API.Core
true);
break;
}
var plugin = _pluginContextQueryHandler.FindPluginByModulePath(info.GetArg(2));
if (plugin == null)
{
info.ReplyToCommand("Could not find plugin to load.");
break;
}
plugin.Load(false);
// If our arugment doesn't end in ".dll" - try and construct a path similar to PluginName/PluginName.dll.
// We'll assume we have a full path if we have ".dll".
var path = info.GetArg(2);
@@ -175,15 +165,23 @@ namespace CounterStrikeSharp.API.Core
path = Path.Combine(_scriptHostConfiguration.RootPath, path);
}
try
{
// LoadPlugin(path);
}
catch (Exception e)
{
Logger.LogError(e, "Failed to load plugin from {Path}", path);
}
var plugin = _pluginContextQueryHandler.FindPluginByModulePath(path);
if (plugin == null)
{
try
{
_pluginManager.LoadPlugin(path);
} catch (Exception e)
{
info.ReplyToCommand($"Could not load plugin \"{path}\")", true);
}
}
else
{
plugin.Load(false);
}
break;
}

View File

@@ -5,5 +5,6 @@ namespace CounterStrikeSharp.API.Core.Plugin.Host;
public interface IPluginManager
{
public void Load();
public void LoadPlugin(string path);
public IEnumerable<PluginContext> GetLoadedPlugins();
}

View File

@@ -45,7 +45,7 @@ public class PluginManager : IPluginManager
return _loadedPluginContexts;
}
private void LoadPlugin(string path)
public void LoadPlugin(string path)
{
var plugin = new PluginContext(_serviceProvider, _scriptHostConfiguration, path, _loadedPluginContexts.Select(x => x.PluginId).DefaultIfEmpty(0).Max() + 1);
_loadedPluginContexts.Add(plugin);

View File

@@ -0,0 +1,14 @@
using CounterStrikeSharp.API.Modules.Commands.Targeting;
namespace CounterStrikeSharp.API.Modules.Commands;
public static class CommandExtensions
{
/// <summary>
/// Treats the argument at the specified index as a target string (@all, @me etc.) and returns the result.
/// </summary>
public static TargetResult GetArgTargetResult(this CommandInfo commandInfo, int index)
{
return new Target(commandInfo.GetArg(index)).GetTarget(commandInfo.CallingPlayer);
}
}

View File

@@ -25,13 +25,14 @@ namespace CounterStrikeSharp.API.Modules.Commands
public delegate HookResult CommandListenerCallback(CCSPlayerController? player, CommandInfo commandInfo);
private CCSPlayerController _player;
public IntPtr Handle { get; private set; }
public CCSPlayerController? CallingPlayer { get; }
public IntPtr Handle { get; }
internal CommandInfo(IntPtr pointer, CCSPlayerController player)
{
Handle = pointer;
_player = player;
CallingPlayer = player;
}
public int ArgCount => NativeAPI.CommandGetArgCount(Handle);
@@ -42,12 +43,12 @@ namespace CounterStrikeSharp.API.Modules.Commands
public string ArgByIndex(int index) => NativeAPI.CommandGetArgByIndex(Handle, index);
public string GetArg(int index) => NativeAPI.CommandGetArgByIndex(Handle, index);
public void ReplyToCommand(string message, bool console = false) {
if (_player != null)
if (CallingPlayer != null)
{
if (console) { _player.PrintToConsole(message); }
else _player.PrintToChat(message);
if (console) { CallingPlayer.PrintToConsole(message); }
else CallingPlayer.PrintToChat(message);
}
else
{

View File

@@ -0,0 +1,126 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.Modules.Utils;
namespace CounterStrikeSharp.API.Modules.Commands.Targeting;
public class Target
{
private TargetType Type { get; }
private string Raw { get; }
private string Slug { get; }
private static readonly Dictionary<string, TargetType> TargetTypeMap = new(StringComparer.OrdinalIgnoreCase)
{
{ "@all", TargetType.GroupAll },
{ "@bots", TargetType.GroupBots },
{ "@human", TargetType.GroupHumans },
{ "@alive", TargetType.GroupAlive },
{ "@dead", TargetType.GroupDead },
{ "@!me", TargetType.GroupNotMe },
{ "@me", TargetType.PlayerMe },
{ "@ct", TargetType.TeamCt },
{ "@t", TargetType.TeamT },
{ "@spec", TargetType.TeamSpec }
};
private static bool ConstTargetType(string target, out TargetType targetType)
{
targetType = TargetType.Invalid;
if (!target.StartsWith("@"))
{
return false;
}
return TargetTypeMap.TryGetValue(target, out targetType);
}
private bool IdTargetType(string target,
out TargetType targetType,
[MaybeNullWhen(false)] out string slug)
{
targetType = TargetType.Invalid;
slug = null!;
if (!target.StartsWith("#"))
{
return false;
}
slug = target.TrimStart('#');
if (slug.StartsWith("STEAM")) targetType = TargetType.IdSteamEscaped;
else if (!ulong.TryParse(slug, out _)) targetType = TargetType.ExplicitName;
else if (slug.Length == 17) targetType = TargetType.IdSteam64;
else targetType = TargetType.IdUserid;
return true;
}
public Target(string target)
{
Raw = target.Trim();
if (ConstTargetType(Raw, out var targetType))
{
Type = targetType;
Slug = Raw;
}
else if (IdTargetType(Raw, out targetType, out var slug))
{
Type = targetType;
Slug = slug;
}
else
{
Type = TargetType.ImplicitName;
Slug = Raw;
}
}
private bool TargetPredicate(CCSPlayerController player, CCSPlayerController? caller)
{
switch (Type)
{
case TargetType.TeamCt:
return player.TeamNum == (byte)CsTeam.CounterTerrorist;
case TargetType.TeamT:
return player.TeamNum == (byte)CsTeam.Terrorist;
case TargetType.TeamSpec:
return player.TeamNum == (byte)CsTeam.Spectator;
case TargetType.GroupAll:
return true;
case TargetType.GroupBots:
return player.IsBot;
case TargetType.GroupHumans:
return !player.IsBot;
case TargetType.GroupAlive:
return player.PlayerPawn is { IsValid: true, Value.LifeState: (byte)LifeState_t.LIFE_ALIVE };
case TargetType.GroupDead:
return player.PlayerPawn is { IsValid: true, Value.LifeState: (byte)LifeState_t.LIFE_DEAD or (byte)LifeState_t.LIFE_DYING };
case TargetType.GroupNotMe:
return player.SteamID != caller?.SteamID;
case TargetType.PlayerMe:
return player.SteamID == caller?.SteamID;
case TargetType.IdUserid:
return player.UserId.ToString() == Slug;
case TargetType.IdSteamEscaped:
return ((SteamID)player.SteamID).SteamId2 == Slug;
case TargetType.IdSteam64:
return ((SteamID)player.SteamID).SteamId64.ToString() == Slug;
case TargetType.ExplicitName:
case TargetType.ImplicitName:
return player.PlayerName.Contains(Slug, StringComparison.OrdinalIgnoreCase);
default:
return false;
}
}
public TargetResult GetTarget(CCSPlayerController? caller)
{
var players = Utilities.GetPlayers().Where(player => TargetPredicate(player, caller)).ToList();
return new TargetResult() { Players = players };
}
}

View File

@@ -0,0 +1,20 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace CounterStrikeSharp.API.Modules.Commands.Targeting;
public class TargetResult : IEnumerable<CCSPlayerController>
{
public List<CCSPlayerController> Players { get; set; } = new();
public IEnumerator<CCSPlayerController> GetEnumerator()
{
return Players.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

View File

@@ -0,0 +1,25 @@
namespace CounterStrikeSharp.API.Modules.Commands.Targeting;
public enum TargetType
{
TeamCt, // @ct
TeamT, // @t
TeamSpec, // @spec
GroupAll, // @all
GroupBots, // @bots
GroupHumans, // @human
GroupAlive, // @alive
GroupDead, // @dead
GroupNotMe, // @!me
PlayerMe, // @me
IdUserid, // #4
IdSteamEscaped, // "#STEAM_0:1:8614"
IdSteam64, // #76561198116940237
ExplicitName, // #name
ImplicitName, // name
Invalid
}

View File

@@ -43,10 +43,11 @@ namespace CounterStrikeSharp.API.Modules.Config
{
string directoryPath = Path.Combine(_pluginConfigsFolderPath, pluginName);
string configPath = Path.Combine(directoryPath, $"{pluginName}.json");
string exampleConfigPath = Path.Combine(directoryPath, $"{pluginName}.example.json");
T config = (T)Activator.CreateInstance(typeof(T))!;
if (!File.Exists(configPath))
if (!File.Exists(configPath) && !File.Exists(exampleConfigPath))
{
try
{
@@ -56,8 +57,10 @@ namespace CounterStrikeSharp.API.Modules.Config
}
StringBuilder builder = new StringBuilder();
builder.Append($"// This configuration was automatically generated by CounterStrikeSharp for plugin '{pluginName}', at {DateTimeOffset.Now:yyyy/MM/dd hh:mm:ss}\n");
builder.Append(JsonSerializer.Serialize<T>(config, new JsonSerializerOptions { WriteIndented = true }));
builder.Append(
$"// This configuration was automatically generated by CounterStrikeSharp for plugin '{pluginName}', at {DateTimeOffset.Now:yyyy/MM/dd hh:mm:ss}\n");
builder.Append(JsonSerializer.Serialize<T>(config,
new JsonSerializerOptions { WriteIndented = true }));
File.WriteAllText(configPath, builder.ToString());
return config;
}
@@ -65,6 +68,17 @@ namespace CounterStrikeSharp.API.Modules.Config
{
_logger.LogError(ex, "Failed to generate configuration file for {PluginName}", pluginName);
}
} else if (File.Exists(exampleConfigPath) && !File.Exists(configPath))
{
try
{
_logger.LogInformation("Copying example configuration file for {PluginName}", pluginName);
File.Copy(exampleConfigPath, configPath);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to copy example configuration file for {PluginName}", pluginName);
}
}
try

View File

@@ -14,15 +14,14 @@
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
*/
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using CounterStrikeSharp.API.Modules.Commands.Targeting;
namespace CounterStrikeSharp.API
{
@@ -64,6 +63,16 @@ namespace CounterStrikeSharp.API
return Utilities.GetEntityFromIndex<CCSPlayerController>((userid & 0xFF) + 1);
}
public static CCSPlayerController? GetPlayerFromSteamId(ulong steamId)
{
return Utilities.GetPlayers().FirstOrDefault(player => player.SteamID == steamId);
}
public static TargetResult ProcessTargetString(string pattern, CCSPlayerController player)
{
return new Target(pattern).GetTarget(player);
}
public static IEnumerable<T> FindAllEntitiesByDesignerName<T>(string designerName) where T : CEntityInstance
{
var pEntity = new CEntityIdentity(NativeAPI.GetFirstActiveEntity());

View File

@@ -344,6 +344,24 @@ namespace TestPlugin
giveItemMenu.AddMenuOption("weapon_ak47", handleGive);
giveItemMenu.AddMenuOption("weapon_p250", handleGive);
AddCommand("css_target", "Target Test", (player, info) =>
{
if (player == null) return;
var targetResult = info.GetArgTargetResult(1);
if (!targetResult.Any())
{
player.PrintToChat("No players found.");
return;
}
foreach (var result in targetResult.Players)
{
player.PrintToChat($"Target found: {result?.PlayerName}");
}
});
AddCommand("css_menu", "Opens example menu", (player, info) => { ChatMenus.OpenMenu(player, largeMenu); });
AddCommand("css_gunmenu", "Gun Menu", (player, info) => { ChatMenus.OpenMenu(player, giveItemMenu); });

View File

@@ -26,11 +26,22 @@ CCoreConfig::~CCoreConfig() = default;
bool CCoreConfig::Init(char* conf_error, int conf_error_size)
{
std::ifstream ifs(m_sPath);
std::ifstream ifs(std::string(m_sPath + ".json"));
if (!ifs) {
V_snprintf(conf_error, conf_error_size, "CoreConfig file not found.");
return false;
std::ifstream exampleIfs(std::string(m_sPath + ".example.json"));
if (!exampleIfs) {
V_snprintf(conf_error, conf_error_size, "CoreConfig file not found.");
return false;
}
CSSHARP_CORE_INFO("CoreConfig file not found, creating one from example.");
std::ofstream ofs(std::string(m_sPath + ".json"));
ofs << exampleIfs.rdbuf();
ofs.close();
return Init(conf_error, conf_error_size);
}
m_json = json::parse(ifs);

View File

@@ -84,7 +84,7 @@ bool CounterStrikeSharpMMPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, s
GET_V_IFACE_ANY(GetEngineFactory, globals::gameEventSystem, IGameEventSystem,
GAMEEVENTSYSTEM_INTERFACE_VERSION);
auto coreconfig_path = std::string(utils::ConfigsDirectory() + "/core.json");
auto coreconfig_path = std::string(utils::ConfigsDirectory() + "/core");
globals::coreConfig = new CCoreConfig(coreconfig_path);
char coreconfig_error[255] = "";