removes WebsiteComposer, BackOfficeComposer
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -70,7 +70,6 @@ namespace Umbraco.Core.Composing
|
||||
|
||||
foreach (var composer in composers)
|
||||
{
|
||||
var componentType = composer.GetType();
|
||||
composer.Compose(_builder);
|
||||
}
|
||||
}
|
||||
|
||||
76
src/Umbraco.Tests.Integration/ComponentRuntimeTests.cs
Normal file
76
src/Umbraco.Tests.Integration/ComponentRuntimeTests.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Infrastructure.PublishedCache.DependencyInjection;
|
||||
using Umbraco.Tests.Integration.Extensions;
|
||||
using Umbraco.Tests.Integration.Implementations;
|
||||
using Umbraco.Tests.Integration.Testing;
|
||||
using Umbraco.Tests.Testing;
|
||||
using Umbraco.Web.Common.DependencyInjection;
|
||||
|
||||
namespace Umbraco.Tests.Integration
|
||||
{
|
||||
|
||||
[TestFixture]
|
||||
[UmbracoTest(Boot = true)]
|
||||
public class ComponentRuntimeTests : UmbracoIntegrationTest
|
||||
{
|
||||
// ensure composers are added
|
||||
protected override void CustomTestSetup(IUmbracoBuilder builder) => builder.AddComposers();
|
||||
|
||||
/// <summary>
|
||||
/// This will boot up umbraco with components enabled to show they initialize and shutdown
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Start_And_Stop_Umbraco_With_Components_Enabled()
|
||||
{
|
||||
IRuntime runtime = Services.GetRequiredService<IRuntime>();
|
||||
IRuntimeState runtimeState = Services.GetRequiredService<IRuntimeState>();
|
||||
IMainDom mainDom = Services.GetRequiredService<IMainDom>();
|
||||
ComponentCollection components = Services.GetRequiredService<ComponentCollection>();
|
||||
|
||||
MyComponent myComponent = components.OfType<MyComponent>().First();
|
||||
|
||||
Assert.IsTrue(mainDom.IsMainDom);
|
||||
Assert.IsNull(runtimeState.BootFailedException);
|
||||
Assert.IsTrue(myComponent.IsInit, "The component was not initialized");
|
||||
|
||||
// force stop now
|
||||
await runtime.StopAsync(CancellationToken.None);
|
||||
Assert.IsTrue(myComponent.IsTerminated, "The component was not terminated");
|
||||
}
|
||||
|
||||
public class MyComposer : IUserComposer
|
||||
{
|
||||
public void Compose(IUmbracoBuilder builder) => builder.Components().Append<MyComponent>();
|
||||
}
|
||||
|
||||
public class MyComponent : IComponent
|
||||
{
|
||||
public bool IsInit { get; private set; }
|
||||
|
||||
public bool IsTerminated { get; private set; }
|
||||
|
||||
private readonly ILogger<MyComponent> _logger;
|
||||
|
||||
public MyComponent(ILogger<MyComponent> logger) => _logger = logger;
|
||||
|
||||
public void Initialize() => IsInit = true;
|
||||
|
||||
public void Terminate() => IsTerminated = true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,9 +34,9 @@ namespace Umbraco.Tests.Integration.DependencyInjection
|
||||
/// <summary>
|
||||
/// Uses/Replaces services with testing services
|
||||
/// </summary>
|
||||
public static IUmbracoBuilder AddTestServices(this IUmbracoBuilder builder, TestHelper testHelper)
|
||||
public static IUmbracoBuilder AddTestServices(this IUmbracoBuilder builder, TestHelper testHelper, AppCaches appCaches = null)
|
||||
{
|
||||
builder.Services.AddUnique(AppCaches.NoCache);
|
||||
builder.Services.AddUnique(appCaches ?? AppCaches.NoCache);
|
||||
builder.Services.AddUnique(Mock.Of<IUmbracoBootPermissionChecker>());
|
||||
builder.Services.AddUnique(testHelper.MainDom);
|
||||
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Infrastructure.PublishedCache.DependencyInjection;
|
||||
using Umbraco.Tests.Integration.Extensions;
|
||||
using Umbraco.Tests.Integration.Implementations;
|
||||
using Umbraco.Web.Common.DependencyInjection;
|
||||
|
||||
namespace Umbraco.Tests.Integration
|
||||
{
|
||||
|
||||
[TestFixture]
|
||||
public class RuntimeTests
|
||||
{
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
MyComponent.Reset();
|
||||
MyComposer.Reset();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
MyComponent.Reset();
|
||||
MyComposer.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will boot up umbraco with components enabled to show they initialize and shutdown
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Start_And_Stop_Umbraco_With_Components_Enabled()
|
||||
{
|
||||
var testHelper = new TestHelper();
|
||||
|
||||
IHostBuilder hostBuilder = new HostBuilder()
|
||||
.ConfigureServices((hostContext, services) =>
|
||||
{
|
||||
IWebHostEnvironment webHostEnvironment = testHelper.GetWebHostEnvironment();
|
||||
services.AddSingleton(testHelper.DbProviderFactoryCreator);
|
||||
services.AddRequiredNetCoreServices(testHelper, webHostEnvironment);
|
||||
|
||||
// Add it!
|
||||
TypeLoader typeLoader = services.AddTypeLoader(
|
||||
GetType().Assembly,
|
||||
webHostEnvironment,
|
||||
testHelper.GetHostingEnvironment(),
|
||||
testHelper.ConsoleLoggerFactory,
|
||||
AppCaches.NoCache,
|
||||
hostContext.Configuration,
|
||||
testHelper.Profiler);
|
||||
|
||||
var builder = new UmbracoBuilder(
|
||||
services,
|
||||
hostContext.Configuration,
|
||||
typeLoader,
|
||||
testHelper.ConsoleLoggerFactory);
|
||||
|
||||
builder.Services.AddUnique(AppCaches.NoCache);
|
||||
builder.AddConfiguration()
|
||||
.AddUmbracoCore()
|
||||
.AddWebComponents()
|
||||
.AddComposers()
|
||||
.Build();
|
||||
|
||||
services.AddRouting(); // LinkGenerator
|
||||
});
|
||||
|
||||
IHost host = await hostBuilder.StartAsync();
|
||||
var app = new ApplicationBuilder(host.Services);
|
||||
|
||||
app.UseUmbracoCore();
|
||||
|
||||
// assert results
|
||||
IRuntimeState runtimeState = app.ApplicationServices.GetRequiredService<IRuntimeState>();
|
||||
IMainDom mainDom = app.ApplicationServices.GetRequiredService<IMainDom>();
|
||||
|
||||
Assert.IsTrue(mainDom.IsMainDom);
|
||||
Assert.IsNull(runtimeState.BootFailedException);
|
||||
Assert.IsTrue(MyComponent.IsInit);
|
||||
|
||||
await host.StopAsync();
|
||||
|
||||
Assert.IsTrue(MyComponent.IsTerminated);
|
||||
}
|
||||
|
||||
public class MyComposer : IUserComposer
|
||||
{
|
||||
public void Compose(IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Components().Append<MyComponent>();
|
||||
IsComposed = true;
|
||||
}
|
||||
|
||||
public static void Reset() => IsComposed = false;
|
||||
|
||||
public static bool IsComposed { get; private set; }
|
||||
}
|
||||
|
||||
public class MyComponent : IComponent
|
||||
{
|
||||
public static bool IsInit { get; private set; }
|
||||
|
||||
public static bool IsTerminated { get; private set; }
|
||||
|
||||
private readonly ILogger<MyComponent> _logger;
|
||||
|
||||
public MyComponent(ILogger<MyComponent> logger) => _logger = logger;
|
||||
|
||||
public void Initialize() => IsInit = true;
|
||||
|
||||
public void Terminate() => IsTerminated = true;
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
IsTerminated = false;
|
||||
IsInit = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,6 +139,7 @@ namespace Umbraco.Tests.Integration.TestServerTest
|
||||
.AddUmbracoCore()
|
||||
.AddWebComponents()
|
||||
.AddRuntimeMinifier()
|
||||
.AddBackOfficeCore()
|
||||
.AddBackOfficeAuthentication()
|
||||
.AddBackOfficeIdentity()
|
||||
.AddBackOfficeAuthorizationPolicies(TestAuthHandler.TestAuthenticationScheme)
|
||||
|
||||
@@ -217,25 +217,20 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
builder.Services.AddLogger(TestHelper.GetHostingEnvironment(), TestHelper.GetLoggingConfiguration(), Configuration);
|
||||
|
||||
builder.AddConfiguration()
|
||||
.AddUmbracoCore();
|
||||
|
||||
builder.Services.AddUnique<AppCaches>(GetAppCaches());
|
||||
builder.Services.AddUnique<IUmbracoBootPermissionChecker>(Mock.Of<IUmbracoBootPermissionChecker>());
|
||||
builder.Services.AddUnique<IMainDom>(TestHelper.MainDom);
|
||||
|
||||
//.AddTestServices(TestHelper)
|
||||
builder.AddWebComponents()
|
||||
.AddUmbracoCore()
|
||||
.AddWebComponents()
|
||||
.AddRuntimeMinifier()
|
||||
.AddBackOfficeAuthentication()
|
||||
.AddBackOfficeIdentity();
|
||||
.AddBackOfficeIdentity()
|
||||
.AddTestServices(TestHelper, GetAppCaches());
|
||||
//.AddComposers();
|
||||
|
||||
services.AddSignalR();
|
||||
services.AddMvc();
|
||||
|
||||
builder.Build();
|
||||
|
||||
CustomTestSetup(builder);
|
||||
|
||||
builder.Build();
|
||||
}
|
||||
|
||||
protected virtual AppCaches GetAppCaches()
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core.DependencyInjection;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Infrastructure.DependencyInjection;
|
||||
using Umbraco.Infrastructure.PublishedCache.DependencyInjection;
|
||||
using Umbraco.Web.BackOffice.Authorization;
|
||||
using Umbraco.Web.BackOffice.Controllers;
|
||||
using Umbraco.Web.BackOffice.Filters;
|
||||
using Umbraco.Web.BackOffice.Middleware;
|
||||
using Umbraco.Web.BackOffice.Routing;
|
||||
using Umbraco.Web.BackOffice.Security;
|
||||
using Umbraco.Web.BackOffice.Services;
|
||||
using Umbraco.Web.BackOffice.Trees;
|
||||
using Umbraco.Web.Common.Authorization;
|
||||
using Umbraco.Web.Common.DependencyInjection;
|
||||
@@ -25,6 +35,7 @@ namespace Umbraco.Web.BackOffice.DependencyInjection
|
||||
.AddUmbracoCore()
|
||||
.AddWebComponents()
|
||||
.AddRuntimeMinifier()
|
||||
.AddBackOfficeCore()
|
||||
.AddBackOfficeAuthentication()
|
||||
.AddBackOfficeIdentity()
|
||||
.AddBackOfficeAuthorizationPolicies()
|
||||
@@ -64,6 +75,11 @@ namespace Umbraco.Web.BackOffice.DependencyInjection
|
||||
});
|
||||
|
||||
builder.Services.ConfigureOptions<ConfigureBackOfficeCookieOptions>();
|
||||
|
||||
builder.Services.AddUnique<PreviewAuthenticationMiddleware>();
|
||||
builder.Services.AddUnique<BackOfficeExternalLoginProviderErrorMiddleware>();
|
||||
builder.Services.AddUnique<IBackOfficeAntiforgery, BackOfficeAntiforgery>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -117,5 +133,41 @@ namespace Umbraco.Web.BackOffice.DependencyInjection
|
||||
/// </summary>
|
||||
public static TreeCollectionBuilder Trees(this IUmbracoBuilder builder)
|
||||
=> builder.WithCollectionBuilder<TreeCollectionBuilder>();
|
||||
|
||||
public static IUmbracoBuilder AddBackOfficeCore(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.AddUnique<BackOfficeAreaRoutes>();
|
||||
builder.Services.AddUnique<PreviewRoutes>();
|
||||
builder.Services.AddUnique<BackOfficeServerVariables>();
|
||||
builder.Services.AddScoped<BackOfficeSessionIdValidator>();
|
||||
builder.Services.AddScoped<BackOfficeSecurityStampValidator>();
|
||||
|
||||
// register back office trees
|
||||
// the collection builder only accepts types inheriting from TreeControllerBase
|
||||
// and will filter out those that are not attributed with TreeAttribute
|
||||
var umbracoApiControllerTypes = builder.TypeLoader.GetUmbracoApiControllers().ToList();
|
||||
builder.Trees()
|
||||
.AddTreeControllers(umbracoApiControllerTypes.Where(x => typeof(TreeControllerBase).IsAssignableFrom(x)));
|
||||
|
||||
builder.ComposeWebMappingProfiles();
|
||||
|
||||
builder.Services.AddUnique<IPhysicalFileSystem>(factory =>
|
||||
{
|
||||
var path = "~/";
|
||||
var hostingEnvironment = factory.GetRequiredService<IHostingEnvironment>();
|
||||
return new PhysicalFileSystem(
|
||||
factory.GetRequiredService<IIOHelper>(),
|
||||
hostingEnvironment,
|
||||
factory.GetRequiredService<ILogger<PhysicalFileSystem>>(),
|
||||
hostingEnvironment.MapPathContentRoot(path),
|
||||
hostingEnvironment.ToAbsolute(path)
|
||||
);
|
||||
});
|
||||
|
||||
builder.Services.AddUnique<IIconService, IconService>();
|
||||
builder.Services.AddUnique<UnhandledExceptionLoggerMiddleware>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.DependencyInjection;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.BackOffice.Controllers;
|
||||
using Umbraco.Web.BackOffice.DependencyInjection;
|
||||
using Umbraco.Web.BackOffice.Filters;
|
||||
using Umbraco.Web.BackOffice.Middleware;
|
||||
using Umbraco.Web.BackOffice.Routing;
|
||||
using Umbraco.Web.BackOffice.Security;
|
||||
using Umbraco.Web.BackOffice.Services;
|
||||
using Umbraco.Web.BackOffice.Trees;
|
||||
using Umbraco.Web.Common.Runtime;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Runtime
|
||||
{
|
||||
[ComposeBefore(typeof(ICoreComposer))]
|
||||
[ComposeAfter(typeof(AspNetCoreComposer))]
|
||||
public class BackOfficeComposer : IComposer
|
||||
{
|
||||
public void Compose(IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.AddUnique<BackOfficeAreaRoutes>();
|
||||
builder.Services.AddUnique<PreviewRoutes>();
|
||||
builder.Services.AddUnique<BackOfficeServerVariables>();
|
||||
builder.Services.AddScoped<BackOfficeSessionIdValidator>();
|
||||
builder.Services.AddScoped<BackOfficeSecurityStampValidator>();
|
||||
|
||||
builder.Services.AddUnique<PreviewAuthenticationMiddleware>();
|
||||
builder.Services.AddUnique<BackOfficeExternalLoginProviderErrorMiddleware>();
|
||||
builder.Services.AddUnique<IBackOfficeAntiforgery, BackOfficeAntiforgery>();
|
||||
|
||||
// register back office trees
|
||||
// the collection builder only accepts types inheriting from TreeControllerBase
|
||||
// and will filter out those that are not attributed with TreeAttribute
|
||||
var umbracoApiControllerTypes = builder.TypeLoader.GetUmbracoApiControllers().ToList();
|
||||
builder.Trees()
|
||||
.AddTreeControllers(umbracoApiControllerTypes.Where(x => typeof(TreeControllerBase).IsAssignableFrom(x)));
|
||||
|
||||
builder.ComposeWebMappingProfiles();
|
||||
|
||||
builder.Services.AddUnique<IPhysicalFileSystem>(factory =>
|
||||
{
|
||||
var path = "~/";
|
||||
var hostingEnvironment = factory.GetRequiredService<IHostingEnvironment>();
|
||||
return new PhysicalFileSystem(
|
||||
factory.GetRequiredService<IIOHelper>(),
|
||||
hostingEnvironment,
|
||||
factory.GetRequiredService<ILogger<PhysicalFileSystem>>(),
|
||||
hostingEnvironment.MapPathContentRoot(path),
|
||||
hostingEnvironment.ToAbsolute(path)
|
||||
);
|
||||
});
|
||||
|
||||
builder.Services.AddUnique<IIconService, IconService>();
|
||||
builder.Services.AddUnique<UnhandledExceptionLoggerMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,4 @@
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.DependencyInjection;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Diagnostics;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Net;
|
||||
using Umbraco.Web.Common.AspNetCore;
|
||||
using Umbraco.Web.Common.Controllers;
|
||||
using Umbraco.Web.Common.Install;
|
||||
using Umbraco.Web.Common.Lifetime;
|
||||
using Umbraco.Web.Common.Macros;
|
||||
using Umbraco.Web.Common.Middleware;
|
||||
using Umbraco.Web.Common.Profiler;
|
||||
using Umbraco.Web.Common.Routing;
|
||||
using Umbraco.Web.Common.Security;
|
||||
using Umbraco.Web.Common.Templates;
|
||||
using Umbraco.Web.Macros;
|
||||
using Umbraco.Web.Security;
|
||||
using Umbraco.Web.Templates;
|
||||
using Umbraco.Web.Common.ModelBinders;
|
||||
using Umbraco.Infrastructure.DependencyInjection;
|
||||
|
||||
namespace Umbraco.Web.Common.Runtime
|
||||
{
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace Umbraco.Web.UI.NetCore
|
||||
services.AddUmbraco(_env, _config)
|
||||
.AddBackOffice()
|
||||
.AddWebsite()
|
||||
.AddComposers()
|
||||
.Build();
|
||||
#pragma warning restore IDE0022 // Use expression body for methods
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Infrastructure.DependencyInjection;
|
||||
using Umbraco.Infrastructure.PublishedCache.DependencyInjection;
|
||||
using Umbraco.Web.Website.Collections;
|
||||
using Umbraco.Web.Website.Controllers;
|
||||
using Umbraco.Web.Website.Routing;
|
||||
using Umbraco.Web.Website.ViewEngines;
|
||||
@@ -20,6 +22,11 @@ namespace Umbraco.Web.Website.DependencyInjection
|
||||
/// </summary>
|
||||
public static IUmbracoBuilder AddWebsite(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.AddUnique<NoContentRoutes>();
|
||||
|
||||
builder.WithCollectionBuilder<SurfaceControllerTypeCollectionBuilder>()
|
||||
.Add(builder.TypeLoader.GetSurfaceControllers());
|
||||
|
||||
// Set the render & plugin view engines (Super complicated, but this allows us to use the IServiceCollection
|
||||
// to inject dependencies into the viewEngines)
|
||||
builder.Services.AddTransient<IConfigureOptions<MvcViewOptions>, RenderMvcViewOptionsSetup>();
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
using Umbraco.Core.DependencyInjection;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.Website.Routing;
|
||||
using Umbraco.Web.Common.Runtime;
|
||||
using Umbraco.Web.Website.Collections;
|
||||
|
||||
namespace Umbraco.Web.Website.Runtime
|
||||
{
|
||||
// web's initial composer composes after core's, and before all core composers
|
||||
[ComposeBefore(typeof(ICoreComposer))]
|
||||
[ComposeAfter(typeof(AspNetCoreComposer))]
|
||||
public class WebsiteComposer : IComposer
|
||||
{
|
||||
public void Compose(IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.AddUnique<NoContentRoutes>();
|
||||
|
||||
builder.WithCollectionBuilder<SurfaceControllerTypeCollectionBuilder>()
|
||||
.Add(builder.TypeLoader.GetSurfaceControllers());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user