Update Plugins

This commit is contained in:
linmeideli committed 2026-10-06 19:11:21 +08:00
1 parent 8eaf33595b
commit 6fd771e0cf
21 files changed
+573 -6

No files matched your search

+1
View File
@@ -1,6 +1,7 @@
/.vscode
/.claude
/build
/release
/.vs
/tools
+94
View File
@@ -0,0 +1,94 @@
#!/usr/bin/env bash
# Builds every server RID, packs the NuGet packages and collects the results in ./release.
#
# Server archives follow the build.cake naming so the artifacts match what CI publishes:
# Empostor-Server_<version>_<rid>.zip (win-x64)
# Empostor-Server_<version>_<rid>.tar.gz (linux-*, osx-x64)
#
# Note: Git Bash here has no `zip` binary, only `unzip`, so the win-x64 zip is produced by the
# small dotnet helper in tools/zipdir. tar handles the .tar.gz side natively.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
OUT="$ROOT/release"
SERVER_PROJ="$ROOT/src/Empostor.Server/Empostor.Server.csproj"
API_PROJ="$ROOT/src/Empostor.Api/Empostor.Api.csproj"
SERVER_BIN="$ROOT/src/Empostor.Server/bin/Release/net8.0"
API_BIN="$ROOT/src/Empostor.Api/bin/Release"
# Version comes from the props file, the same source build.cake reads.
VERSION="$(sed -n 's:.*<VersionPrefix>\(.*\)</VersionPrefix>.*:\1:p' "$ROOT/src/Directory.Build.props" | head -1)"
if [ -z "$VERSION" ]; then
echo "error: could not read VersionPrefix from src/Directory.Build.props" >&2
exit 1
fi
# Windows is the only zip target; everything else is tar.gz.
RIDS_ZIP="win-x64"
RIDS_TGZ="linux-x64 linux-arm64 linux-arm osx-x64"
echo "==> Version $VERSION"
echo "==> Output $OUT"
rm -rf "$OUT"
mkdir -p "$OUT"
# ---------------------------------------------------------------- server
for rid in $RIDS_ZIP $RIDS_TGZ; do
echo "==> Publishing server ($rid)"
dotnet publish "$SERVER_PROJ" -c Release -r "$rid" --nologo -v minimal
done
# ---------------------------------------------------------------- api
echo "==> Packing Empostor.Api"
dotnet pack "$API_PROJ" -c Release --nologo -v minimal
# ---------------------------------------------------------------- collect
# Build the zip helper once up front: `dotnet run` swallows --nologo/-v as its own options, so
# the app arguments only survive when they follow a bare `--`.
echo "==> Building zip helper"
dotnet build "$ROOT/tools/zipdir/zipdir.csproj" -c Release --nologo -v quiet
# The publish step leaves a zip behind from an earlier build.cake run; use only the RID folders.
for rid in $RIDS_ZIP; do
dir="$SERVER_BIN/$rid/publish"
if [ ! -d "$dir" ]; then
echo "error: missing publish output $dir" >&2
exit 1
fi
echo "==> Zipping server ($rid)"
dotnet "$ROOT/tools/zipdir/bin/Release/net8.0/zipdir.dll" \
"$dir" "$OUT/Empostor-Server_${VERSION}_${rid}.zip"
done
for rid in $RIDS_TGZ; do
dir="$SERVER_BIN/$rid/publish"
if [ ! -d "$dir" ]; then
echo "error: missing publish output $dir" >&2
exit 1
fi
echo "==> Tarball server ($rid)"
# -C publishes the folder's contents, so the archive has no wrapping directory.
tar -czf "$OUT/Empostor-Server_${VERSION}_${rid}.tar.gz" -C "$dir" .
done
# Only the release build (no -dev / -ci suffix) is collected; the others are stale local output.
echo "==> Collecting NuGet packages"
shopt -s nullglob
found=0
for pkg in "$API_BIN"/Empostor.Api.*.nupkg "$API_BIN"/Empostor.Api.*.snupkg; do
name="$(basename "$pkg")"
case "$name" in
*-dev.nupkg|*-dev.snupkg|*-ci.*.nupkg|*-ci.*.snupkg) continue ;;
esac
cp -f "$pkg" "$OUT/"
found=1
done
shopt -u nullglob
if [ "$found" -eq 0 ]; then
echo "error: no release NuGet package found in $API_BIN" >&2
exit 1
fi
echo
echo "==> Done. Contents of release/:"
ls -lh "$OUT"
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+19 -6
View File
@@ -7,7 +7,7 @@
"versions": [
{
"version": "1.0.0",
"empostor_version": "2.0.0",
"empostor_version": "2.0.1",
"download_url": "https://raw.githubusercontent.com/Empostor/Empostor/master/marketplace/Empostor.Plugin.Chat.dll"
}
]
@@ -20,7 +20,7 @@
"versions": [
{
"version": "1.0.0",
"empostor_version": "2.0.0",
"empostor_version": "2.0.1",
"download_url": "https://raw.githubusercontent.com/Empostor/Empostor/master/marketplace/Empostor.Plugin.Code.dll"
}
]
@@ -33,7 +33,7 @@
"versions": [
{
"version": "1.0.0",
"empostor_version": "2.0.0",
"empostor_version": "2.0.1",
"download_url": "https://raw.githubusercontent.com/Empostor/Empostor/master/marketplace/Empostor.Plugins.Welcome.dll"
}
]
@@ -59,7 +59,7 @@
"versions": [
{
"version": "1.0.0",
"empostor_version": "2.0.0",
"empostor_version": "2.0.1",
"download_url": "https://raw.githubusercontent.com/Empostor/Empostor/master/marketplace/Empostor.Plugins.FixedCode.dll"
}
]
@@ -150,7 +150,7 @@
"versions": [
{
"version": "2.2.0",
"empostor_version": "2.0.0",
"empostor_version": "2.0.1",
"download_url": "https://raw.githubusercontent.com/Empostor/Empostor/main/marketplace/Reactor.Empostor.dll"
}
]
@@ -176,7 +176,7 @@
"versions": [
{
"version": "2.0.0",
"empostor_version": "2.0.0",
"empostor_version": "2.0.1",
"download_url": "https://raw.githubusercontent.com/Empostor/Empostor/main/marketplace/Empostor.Plugins.DiscordWebhook.dll"
}
]
@@ -232,5 +232,18 @@
"download_url": "https://raw.githubusercontent.com/Empostor/Empostor/main/marketplace/Empostor.Plugins.Privacy.dll"
}
]
},
{
"id": "cn.hayashiume.maintenance",
"name": "Maintenance Server",
"description": "Maintian server to refuse plater connect to the server",
"author": "Empostor",
"versions": [
{
"version": "1.0.0",
"empostor_version": "2.0.1",
"download_url": "https://raw.githubusercontent.com/Empostor/Empostor/main/marketplace/Empostor.Plugins.Maintenance.dll"
}
]
}
]
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>Empostor.Plugins.Maintenance</AssemblyName>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Empostor.Api\Empostor.Api.csproj" />
</ItemGroup>
<Target Name="CopyToMarketplace" AfterTargets="Build">
<Copy SourceFiles="$(TargetPath)" DestinationFolder="E:\WindowsFile\Empostor\Empostor\marketplace" />
</Target>
</Project>
@@ -0,0 +1,78 @@
using System;
using System.Threading.Tasks;
using Empostor.Api.Admin;
namespace Empostor.Plugins.Maintenance;
public sealed class MaintenanceAdminExtension : IAdminExtension
{
private readonly MaintenanceStore _store;
private readonly MaintenanceListener _listener;
public MaintenanceAdminExtension(MaintenanceStore store, MaintenanceListener listener)
{
_store = store;
_listener = listener;
}
public string Id => "maintenance";
public string Title => "Maintenance";
public string Icon => "shield";
public void Build(AdminPanelBuilder b)
{
b.RegisterText(
_store.Enabled
? "Maintenance mode: ON — every player is turned away."
: "Maintenance mode: OFF — the server is accepting players.",
_store.Enabled ? "warning" : "default");
b.RegisterButton(button =>
{
button.Label = _store.Enabled ? "Turn maintenance OFF" : "Turn maintenance ON";
button.Icon = _store.Enabled ? "refresh" : "shield";
button.Style = _store.Enabled ? "primary" : "danger";
button.OnClick(async ctx => await ToggleAsync(ctx));
});
b.RegisterDivider();
b.RegisterTextbox(textbox =>
{
textbox.Label = "Kick message (Chinese)";
textbox.Value = _store.MessageZh;
textbox.Multiline = true;
textbox.OnSubmit(async ctx => await SaveMessageAsync(ctx, zh: ctx.Value, en: null));
});
b.RegisterTextbox(textbox =>
{
textbox.Label = "Kick message (English)";
textbox.Value = _store.MessageEn;
textbox.Multiline = true;
textbox.OnSubmit(async ctx => await SaveMessageAsync(ctx, zh: null, en: ctx.Value));
});
}
private async ValueTask<AdminActionResult> ToggleAsync(AdminActionContext ctx)
{
var enabled = !_store.Enabled;
await _listener.SetAsync(enabled);
return AdminActionResult.Ok(enabled
? "Maintenance mode ON. Everyone in a lobby was disconnected; new players are refused."
: "Maintenance mode OFF. The server is accepting players again.");
}
private async ValueTask<AdminActionResult> SaveMessageAsync(AdminActionContext ctx, string? zh, string? en)
{
_store.SetMessages(
string.IsNullOrWhiteSpace(zh) ? _store.MessageZh : zh.Trim(),
string.IsNullOrWhiteSpace(en) ? _store.MessageEn : en.Trim());
await _store.SaveAsync();
return AdminActionResult.Ok("Kick message saved.");
}
}
@@ -0,0 +1,102 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Empostor.Api.Events;
using Empostor.Api.Events.Client;
using Empostor.Api.Games;
using Empostor.Api.Games.Managers;
using Empostor.Api.Innersloth;
using Empostor.Api.Net;
using Microsoft.Extensions.Logging;
namespace Empostor.Plugins.Maintenance;
/// <summary>
/// While maintenance mode is on, every player is turned away: the ones already in a lobby are
/// disconnected, and anyone connecting, creating a lobby or trying to join is rejected with the
/// same message.
/// </summary>
public sealed class MaintenanceListener : IEventListener
{
private readonly ILogger<MaintenanceListener> _logger;
private readonly MaintenanceStore _store;
private readonly IGameManager _games;
public MaintenanceListener(
ILogger<MaintenanceListener> logger,
MaintenanceStore store,
IGameManager games)
{
_logger = logger;
_store = store;
_games = games;
}
/// <summary>Turns maintenance on or off, disconnecting everyone currently in a lobby when on.</summary>
public async ValueTask SetAsync(bool enabled)
{
_store.SetEnabled(enabled);
await _store.SaveAsync();
if (!enabled)
{
_logger.LogInformation("MaintenanceMode disabled, the server is accepting players again");
return;
}
_logger.LogWarning("MaintenanceMode enabled, disconnecting every player currently in a lobby");
await KickAllAsync();
}
private async ValueTask KickAllAsync()
{
foreach (var game in _games.Games.ToList())
{
foreach (var player in game.Players.ToList())
{
var client = player.Client;
if (client == null)
{
continue;
}
try
{
await client.DisconnectAsync(DisconnectReason.Custom, _store.MessageFor(client.Language));
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Maintenance failed to disconnect client {Id}", client.Id);
}
}
}
}
[EventListener]
public async ValueTask OnClientConnected(IClientConnectedEvent e)
{
// A client that never reaches a lobby would otherwise sit connected forever.
if (_store.Enabled)
{
await e.Client.DisconnectAsync(DisconnectReason.Custom, _store.MessageFor(e.Client.Language));
}
}
[EventListener]
public async ValueTask OnGameCreating(IGameCreationEvent e)
{
if (_store.Enabled && e.Client != null)
{
await e.Client.DisconnectAsync(DisconnectReason.Custom, _store.MessageFor(e.Client.Language));
}
}
[EventListener]
public void OnPlayerJoining(IGamePlayerJoiningEvent e)
{
if (_store.Enabled)
{
e.JoinResult = GameJoinResult.CreateCustomError(_store.MessageFor(e.Player.Client.Language));
}
}
}
@@ -0,0 +1,24 @@
using System.Threading.Tasks;
using Empostor.Api.Plugins;
using Microsoft.Extensions.Logging;
namespace Empostor.Plugins.Maintenance;
[EmpostorPlugin("cn.hayashiume.maintenance")]
public sealed class MaintenancePlugin : PluginBase
{
private readonly ILogger<MaintenancePlugin> _logger;
public MaintenancePlugin(ILogger<MaintenancePlugin> logger)
{
_logger = logger;
}
public override ValueTask EnableAsync()
{
_logger.LogInformation("[Maintenance] Plugin enabled. Use the admin panel (Plugins → Maintenance) to toggle maintenance mode.");
return default;
}
public override ValueTask DisableAsync() => default;
}
@@ -0,0 +1,22 @@
using Empostor.Api.Admin;
using Empostor.Api.Events;
using Empostor.Api.Plugins;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Empostor.Plugins.Maintenance;
public sealed class MaintenanceStartup : IPluginStartup
{
public void ConfigureHost(IHostBuilder host) { }
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<MaintenanceStore>();
services.AddSingleton<MaintenanceListener>();
services.AddSingleton<IEventListener>(sp => sp.GetRequiredService<MaintenanceListener>());
services.AddSingleton<IAdminExtension>(sp => new MaintenanceAdminExtension(
sp.GetRequiredService<MaintenanceStore>(),
sp.GetRequiredService<MaintenanceListener>()));
}
}
@@ -0,0 +1,65 @@
using System.Threading.Tasks;
using Empostor.Api.Innersloth;
using Empostor.Api.Service;
using Microsoft.Extensions.Logging;
namespace Empostor.Plugins.Maintenance;
public sealed class MaintenanceConfig
{
public bool Enabled { get; set; }
public string MessageZh { get; set; } = "服务器正在维护中,请联系管理员获取支持。";
public string MessageEn { get; set; } = "The server is under maintenance. Please contact the administrator for support.";
}
/// <summary>
/// Holds the maintenance flag and the two kick messages. Persisted so a restart does not
/// silently drop the server back into service while an operator is mid-update.
/// </summary>
public sealed class MaintenanceStore : JsonDataStore<MaintenanceConfig>
{
public MaintenanceStore(ILogger<MaintenanceStore> logger)
: base(logger)
{
Load();
}
public bool Enabled { get; private set; }
public string MessageZh { get; private set; } = new MaintenanceConfig().MessageZh;
public string MessageEn { get; private set; } = new MaintenanceConfig().MessageEn;
/// <summary>The message shown to a client speaking the given language.</summary>
public string MessageFor(Language language) => language switch
{
Language.SChinese or Language.TChinese => MessageZh,
_ => MessageEn,
};
public void SetEnabled(bool enabled) => Enabled = enabled;
public void SetMessages(string zh, string en)
{
MessageZh = zh;
MessageEn = en;
}
public new async ValueTask SaveAsync() => await base.SaveAsync();
protected override MaintenanceConfig GetSnapshot() => new()
{
Enabled = Enabled,
MessageZh = MessageZh,
MessageEn = MessageEn,
};
protected override void ApplySnapshot(MaintenanceConfig data)
{
Enabled = data.Enabled;
MessageZh = data.MessageZh;
MessageEn = data.MessageEn;
}
}
@@ -0,0 +1,140 @@
{
"version": 1,
"dependencies": {
"net8.0": {
"BouncyCastle.Cryptography": {
"type": "Transitive",
"resolved": "2.6.1",
"contentHash": "vZsG2YILhthgRqO+ZVgRff4ZFKKTl0v7kqaVBLCtRvpREhfBP33pcWrdA3PRYgWuFL1RxiUFvjMUHTdBZlJcoA=="
},
"Empostor.Hazel": {
"type": "Transitive",
"resolved": "1.0.2",
"contentHash": "cblWCgvLcQ2xK6ztV04RVrghVEnJpCWe4N6G3LCNaJhuUq367a1/bbtQxUWAflqvgSvGwY3g3SClhh9eWTCBDA==",
"dependencies": {
"BouncyCastle.Cryptography": "2.6.1",
"Empostor.Hazel.Abstractions": "1.0.2",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6",
"Microsoft.Extensions.ObjectPool": "8.0.1",
"Serilog": "4.3.1"
}
},
"Empostor.Hazel.Abstractions": {
"type": "Transitive",
"resolved": "1.0.2",
"contentHash": "uJqGMASOGj+ELffmIMqubyvJ/FHjOXA1RtQdS38s1LRhP+Eu4zJMTTndr12+Q3vS0kHggFFyuExFmNxWyzWmig=="
},
"Microsoft.AspNetCore.Http.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "Nxs7Z1q3f1STfLYKJSVXCs1iBl+Ya6E8o4Oy1bCxJ/rNI44E/0f6tbsrVqAWfB7jlnJfyaAtIalBVxPKUPQb4Q==",
"dependencies": {
"Microsoft.AspNetCore.Http.Features": "2.2.0",
"System.Text.Encodings.Web": "4.5.0"
}
},
"Microsoft.AspNetCore.Http.Features": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "ziFz5zH8f33En4dX81LW84I6XrYXKf9jg6aM39cM+LffN9KJahViKZ61dGMSO2gd3e+qe5yBRwsesvyqlZaSMg==",
"dependencies": {
"Microsoft.Extensions.Primitives": "2.2.0"
}
},
"Microsoft.Extensions.Configuration.Abstractions": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
"dependencies": {
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "9.0.6",
"contentHash": "0Zn6nR/6g+90MxskZyOOMPQvnPnrrGu6bytPwkV+azDcTtCSuQ1+GJUrg8Klmnrjk1i6zMpw2lXijl+tw7Q3kA=="
},
"Microsoft.Extensions.Diagnostics.Abstractions": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "JHYCQG7HmugNYUhOl368g+NMxYE/N/AiclCYRNlgCY9eVyiBkOHMwK4x60RYMxv9EL3+rmj1mqHvdCiPpC+D4Q==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.Options": "8.0.0",
"System.Diagnostics.DiagnosticSource": "8.0.0"
}
},
"Microsoft.Extensions.FileProviders.Abstractions": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==",
"dependencies": {
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Hosting.Abstractions": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "AG7HWwVRdCHlaA++1oKDxLsXIBxmDpMPb3VoyOoAghEWnkUvEAdYQUwnV4jJbAaa/nMYNiEh5ByoLauZBEiovg==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0",
"Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
"Microsoft.Extensions.Logging.Abstractions": "8.0.0"
}
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
}
},
"Microsoft.Extensions.ObjectPool": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "X21b8D6z58EG34OMLH955750mFaEaGs5xLGe9Vyi3059Cgb3sk+p2k/JlQXaieEBRrk+SVlFzDei3Q6PdVzJOA=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g=="
},
"Serilog": {
"type": "Transitive",
"resolved": "4.3.1",
"contentHash": "savYe7h5yRlkqBVOwP8cIRDOdqKiPmYCU4W87JH38sBmcKD5EBoXvQIw6bNEvZ/pTe1gsiye3VFCzBsoppGkXQ=="
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g=="
},
"empostor.api": {
"type": "Project",
"dependencies": {
"Empostor.Hazel": "[1.0.2, )",
"Microsoft.AspNetCore.Http.Abstractions": "[2.2.0, )",
"Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )",
"Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )"
}
}
}
}
}
+15
View File
@@ -47,6 +47,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Empostor.Plugins.Monitor",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Empostor.Plugins.Privacy", "Empostor.Plugins.Privacy\Empostor.Plugins.Privacy.csproj", "{959879FE-2FE0-4FD8-A706-0FA1B3138F45}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Empostor.Plugins.Maintenance", "Empostor.Plugins.Maintenance\Empostor.Plugins.Maintenance.csproj", "{79F25D71-B209-4B6B-89FF-54999A93D9E2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -309,6 +311,18 @@ Global
{959879FE-2FE0-4FD8-A706-0FA1B3138F45}.Release|x64.Build.0 = Release|Any CPU
{959879FE-2FE0-4FD8-A706-0FA1B3138F45}.Release|x86.ActiveCfg = Release|Any CPU
{959879FE-2FE0-4FD8-A706-0FA1B3138F45}.Release|x86.Build.0 = Release|Any CPU
{79F25D71-B209-4B6B-89FF-54999A93D9E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{79F25D71-B209-4B6B-89FF-54999A93D9E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{79F25D71-B209-4B6B-89FF-54999A93D9E2}.Debug|x64.ActiveCfg = Debug|Any CPU
{79F25D71-B209-4B6B-89FF-54999A93D9E2}.Debug|x64.Build.0 = Debug|Any CPU
{79F25D71-B209-4B6B-89FF-54999A93D9E2}.Debug|x86.ActiveCfg = Debug|Any CPU
{79F25D71-B209-4B6B-89FF-54999A93D9E2}.Debug|x86.Build.0 = Debug|Any CPU
{79F25D71-B209-4B6B-89FF-54999A93D9E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{79F25D71-B209-4B6B-89FF-54999A93D9E2}.Release|Any CPU.Build.0 = Release|Any CPU
{79F25D71-B209-4B6B-89FF-54999A93D9E2}.Release|x64.ActiveCfg = Release|Any CPU
{79F25D71-B209-4B6B-89FF-54999A93D9E2}.Release|x64.Build.0 = Release|Any CPU
{79F25D71-B209-4B6B-89FF-54999A93D9E2}.Release|x86.ActiveCfg = Release|Any CPU
{79F25D71-B209-4B6B-89FF-54999A93D9E2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -332,6 +346,7 @@ Global
{135D8F7F-C330-444E-94D4-D2D3944F688A} = {E0009DCB-32A6-45C4-A06C-7F62D4091A29}
{B50665B5-AEDB-4F56-B54A-542863EDB009} = {E0009DCB-32A6-45C4-A06C-7F62D4091A29}
{959879FE-2FE0-4FD8-A706-0FA1B3138F45} = {E0009DCB-32A6-45C4-A06C-7F62D4091A29}
{79F25D71-B209-4B6B-89FF-54999A93D9E2} = {E0009DCB-32A6-45C4-A06C-7F62D4091A29}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DAF5625C-7E46-4DCC-AF2C-11D20D965454}