Skip to content

Commit

Permalink
Implement Core & Plugin Service Collection (#129)
Browse files Browse the repository at this point in the history
  • Loading branch information
roflmuffin authored Nov 26, 2023
1 parent 8d1891a commit 4e8c18a
Show file tree
Hide file tree
Showing 35 changed files with 999 additions and 667 deletions.
71 changes: 71 additions & 0 deletions docs/src/content/docs/guides/dependency-injection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
title: Dependency Injection
description: How to make use of dependency injection in CounterStrikeSharp
sidebar:
order: 1
---

`CounterStrikeSharp` uses a standard <a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-8.0" target="_blank">`IServiceCollection`</a> to allow for dependency injection in plugins.

There are a handful of standard services that are predefined for you (`ILogger` for logging for instance), with more to come in the future. To add your own scoped & singleton services to the container, you can create a new class that implements the `IPluginServiceCollection<T>` interface for your plugin.

```csharp
public class TestPlugin : BasePlugin
{
// Plugin code...
}

public class TestPluginServiceCollection : IPluginServiceCollection<TestPlugin>
{
public void ConfigureServices(IServiceCollection serviceCollection)
{
serviceCollection.AddScoped<ExampleInjectedClass>();
serviceCollection.AddLogging(builder => ...);
}
}
```

CounterStrikeSharp will search your assembly for any implementations of `IPlugin` and then any implementations of `IPluginServiceCollection<T>` where `T` is your plugin. It will then configure the service provider and then request a singleton instance of your plugin before proceeding to the load step.

In this way, any dependencies that are listed in your plugin class constructor will automatically get injected at instantation time (before load).

### Example

```csharp
public class TestInjectedClass
{
private readonly ILogger<TestInjectedClass> _logger;

public TestInjectedClass(ILogger<TestInjectedClass> logger)
{
_logger = logger;
}

public void Hello()
{
_logger.LogInformation("Hello World from Test Injected Class");
}
}

public class TestPluginServiceCollection : IPluginServiceCollection<SamplePlugin>
{
public void ConfigureServices(IServiceCollection serviceCollection)
{
serviceCollection.AddScoped<TestInjectedClass>();
}
}

public class SamplePlugin : BasePlugin
{
private readonly TestInjectedClass _testInjectedClass;
public SamplePlugin(TestInjectedClass testInjectedClass)
{
_testInjectedClass = testInjectedClass;
}

public override void Load(bool hotReload)
{
_testInjectedClass.Hello();
}
}
```
2 changes: 2 additions & 0 deletions docs/src/content/docs/guides/getting-started.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
---
title: Getting Started
description: How to get started installing & using CounterStrikeSharp.
sidebar:
order: 0
---

# Installation
Expand Down
2 changes: 2 additions & 0 deletions docs/src/content/docs/guides/hello-world-plugin.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
---
title: Hello World Plugin
description: How to write your first plugin for CounterStrikeSharp
sidebar:
order: 0
---

## Creating a New Project
Expand Down
67 changes: 67 additions & 0 deletions managed/CounterStrikeSharp.API/Bootstrap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Hosting;
using CounterStrikeSharp.API.Core.Logging;
using CounterStrikeSharp.API.Core.Plugin;
using CounterStrikeSharp.API.Core.Plugin.Host;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;

namespace CounterStrikeSharp.API;

public static class Bootstrap
{
[UnmanagedCallersOnly]
// Used by .NET Host in C++ to initiate loading
public static int Run()
{
try
{
// Path to /game/csgo/addons/counterstrikesharp
var contentRoot = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.Parent.FullName;

using var host = Host.CreateDefaultBuilder()
.UseContentRoot(contentRoot)
.ConfigureServices(services =>
{
services.AddLogging(builder =>
{
builder.ClearProviders();
builder.AddCoreLogging(contentRoot);
});

services.AddSingleton<IScriptHostConfiguration, ScriptHostConfiguration>();
services.AddScoped<Application>();
services.AddSingleton<IPluginManager, PluginManager>();
services.AddScoped<IPluginContextQueryHandler, PluginContextQueryHandler>();

services.Scan(i => i.FromCallingAssembly()
.AddClasses(c => c.AssignableTo<IStartupService>())
.AsSelfWithInterfaces()
.WithSingletonLifetime());
})
.Build();

using IServiceScope scope = host.Services.CreateScope();

// TODO: Improve static singleton access
GameData.GameDataProvider = scope.ServiceProvider.GetRequiredService<GameDataProvider>();

var application = scope.ServiceProvider.GetRequiredService<Application>();
application.Start();

return 1;
}
catch (Exception e)
{
Console.Error.WriteLine(e);
Log.Fatal(e, "Failed to start application");
return 0;
}
}
}
Loading

0 comments on commit 4e8c18a

Please sign in to comment.