外观
第一个服务器插件
本页讲清楚插件项目的搭建、最小插件类、生命周期与事件注册。内容同时覆盖两种常见开发方式:引用社区源码(基于 sc2.2network)和引用发行 DLL(插件站/发行构建)。接口成员签名一致,仅命名空间与引用方式不同,详见下方说明。
构建与目标版本
- 社区源码构建:服务端来自
sc2.2network源码树,命令基类AbstractProcessCmd位于Game.Server命名空间,目标框架net10.0,使用项目引用(ProjectReference)。 - 发行 DLL 构建:服务端来自插件站/发行包,命令基类
AbstractProcessCmd位于Game.Server.EssentialCmd命名空间,目标框架.NET 8.0,引用Engine.dll、GameEntitySystem.dll等发行程序集。
开发时请锁定你服务端对应的提交或发行标签,并以 接口参考 为准。
插件是什么
插件是一个继承 ServerPlugin 的 C# 类库(.dll)。服务端启动时会扫描 Plugins 目录,实例化其中所有非抽象的 ServerPlugin 与 AbstractProcessCmd 子类并加载。
方式一:引用社区源码(推荐用于维护现有服)
目标框架 net10.0,直接引用同一源码树中的服务端项目:
xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\EngineServer\EngineServer.csproj" />
<ProjectReference Include="..\..\EntitySystemServer\EntitySystemServer.csproj" />
<ProjectReference Include="..\..\SurvivalcraftServer\SurvivalcraftServer.csproj" />
</ItemGroup>
</Project>路径要按你的项目位置调整。若使用发行 DLL 引用,也必须来自目标服务端同一构建。
最小插件类:
csharp
using Game.Server;
namespace Workshop.Example;
public sealed class HelloPlugin : ServerPlugin
{
public override int Version => 10000;
public override string Name => "Workshop.Hello";
public override void Initialize()
{
// 只执行一次:在这里注册事件处理器。
}
public override void Load()
{
// 世界加载后读取配置和持久化数据。
}
public override void Save()
{
// 随世界保存插件数据。
}
public override void Update(float dt)
{
// 每帧调用,避免阻塞 IO 和长耗时工作。
}
}方式二:引用发行 DLL(插件站 / 发行构建)
目标框架 .NET 8.0,引用游戏目录下的发行程序集:
xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<AssemblyName>MyPlugin</AssemblyName>
</PropertyGroup>
<ItemGroup>
<Reference Include="Engine">
<HintPath>游戏目录\Engine.dll</HintPath>
</Reference>
<Reference Include="GameEntitySystem">
<HintPath>游戏目录\GameEntitySystem.dll</HintPath>
</Reference>
<Reference Include="LiteNetLib">
<HintPath>游戏目录\LiteNetLib.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>游戏目录\Newtonsoft.Json.dll</HintPath>
</Reference>
</ItemGroup>
</Project>不需要引用
Game.dll,插件通过实体系统操作游戏。引用路径中的游戏目录请替换为你实际的服务端目录。
最简插件(发行构建写法):
csharp
using Game.Server;
public class MyPlugin : ServerPlugin
{
public override string Name => "我的插件";
public override int Version => 1;
public byte FirstLevel => 0;
public override void Initialize()
{
Console.WriteLine("插件加载成功!");
}
public override void Load() { }
public override void Save() { }
}必须实现的成员
| 成员 | 类型 | 说明 |
|---|---|---|
Name | string | 插件名称,显示在日志中 |
Version | int | 版本号,整数(例:10000 表示 1.0.0) |
FirstLevel | byte | 优先级,默认填 0 即可 |
Initialize() | 方法 | 插件加载时执行一次,用来注册事件 |
Load() | 方法 | 世界加载后调用,读取数据/配置 |
Save() | 方法 | 保存数据时调用(关闭或自动保存) |
Update(float dt) | 方法 | 每帧调用,可选重写,避免阻塞 |
插件的生命周期
服务器启动
↓
Initialize() 被调用 ← 在这里注册事件监听
↓
Load() 被调用 ← 在这里读取配置文件
↓
插件运行中(Update 每帧调用)...
↓
Save() 被调用 ← 服务器关闭或自动保存时
↓
服务器关闭服务端会扫描 Plugins 目录的 .dll,加载程序集,并实例化其中非抽象的 ServerPlugin 与 AbstractProcessCmd 子类。因此插件类必须公开、非抽象,并能由加载器正常构造。
注册事件
在 Initialize() 中注册你想监听的事件:
csharp
using Game.Server;
using Game.Server.PlayerEvent;
public class MyPlugin : ServerPlugin, IPlayerEnterGameEventHandle
{
public override string Name => "MyPlugin";
public override int Version => 1;
public byte FirstLevel => 0;
public override void Initialize()
{
// 注册玩家进入/离开事件
PlayerEnterGameEventManager.AddObject(this);
}
void IPlayerEnterGameEventHandle.PlayerEnter(PlayerData player)
{
Console.WriteLine($"欢迎 {player.Name}!");
}
void IPlayerEnterGameEventHandle.PlayerLeave(PlayerData player)
{
Console.WriteLine($"{player.Name} 离开了");
}
public override void Load() { }
public override void Save() { }
}大多数事件都有对应的 XxxEventManager.AddObject(this) 与 RemoveObject(this)。事件订阅建议放在 Initialize(),避免 Load() 多次执行导致重复订阅。
编译与安装
powershell
dotnet build -c Release把输出 DLL 及其独有依赖复制到测试服务端的 Plugins 目录,然后依次验证:加载、初始化、世界读取、玩家加入、save、exit 和重启恢复。不要把服务端已有的核心 DLL 再复制进插件目录。
可用事件接口(速览)
| 接口 | 说明 |
|---|---|
IPlayerEnterGameEventHandle | 玩家进入/离开 |
IPlayerMoveHandle | 玩家移动 |
IPlayerBreakAndPlaceHandle | 方块破坏/放置 |
IPlayerInteractEventHandle | 玩家交互(点击、攻击等) |
常见问题
Q: 插件没加载? A: 检查 .dll 是否在 Plugins 文件夹,检查是否有报错;确认插件类是 public 且非抽象。
Q: 报错找不到类型? A: 确认引用了正确的程序集(发行 DLL 方式检查 HintPath,源码方式检查 ProjectReference)。注意命令基类命名空间:源码构建为 Game.Server,发行构建为 Game.Server.EssentialCmd。
Q: 事件没触发? A: 确认在 Initialize() 中调用了对应的 EventManager.AddObject(this)。
IyDnrKzkuIDkuKrmnI3liqHlmajmj5Lku7YKCuacrOmhteiusua4healmuaPkuS7tumhueebrueahOaQreW7uuOAgeacgOWwj+aPkuS7tuexu+OAgeeUn+WRveWRqOacn+S4juS6i+S7tuazqOWGjOOAguWGheWuueWQjOaXtuimhuebluS4pOenjeW4uOingeW8gOWPkeaWueW8j++8mioq5byV55So56S+5Yy65rqQ56CBKirvvIjln7rkuo4gYHNjMi4ybmV0d29ya2DvvInlkowqKuW8leeUqOWPkeihjCBETEwqKu+8iOaPkuS7tuermS/lj5HooYzmnoTlu7rvvInjgILmjqXlj6PmiJDlkZjnrb7lkI3kuIDoh7TvvIzku4Xlkb3lkI3nqbrpl7TkuI7lvJXnlKjmlrnlvI/kuI3lkIzvvIzor6bop4HkuIvmlrnor7TmmI7jgIIKCjo6OiB3YXJuaW5nIOaehOW7uuS4juebruagh+eJiOacrAotICoq56S+5Yy65rqQ56CB5p6E5bu6KirvvJrmnI3liqHnq6/mnaXoh6ogYHNjMi4ybmV0d29ya2Ag5rqQ56CB5qCR77yM5ZG95Luk5Z+657G7IGBBYnN0cmFjdFByb2Nlc3NDbWRgIOS9jeS6jiBgR2FtZS5TZXJ2ZXJgIOWRveWQjeepuumXtO+8jOebruagh+ahhuaetiBgbmV0MTAuMGDvvIzkvb/nlKjpobnnm67lvJXnlKjvvIhgUHJvamVjdFJlZmVyZW5jZWDvvInjgIIKLSAqKuWPkeihjCBETEwg5p6E5bu6KirvvJrmnI3liqHnq6/mnaXoh6rmj5Lku7bnq5kv5Y+R6KGM5YyF77yM5ZG95Luk5Z+657G7IGBBYnN0cmFjdFByb2Nlc3NDbWRgIOS9jeS6jiBgR2FtZS5TZXJ2ZXIuRXNzZW50aWFsQ21kYCDlkb3lkI3nqbrpl7TvvIznm67moIfmoYbmnrYgYC5ORVQgOC4wYO+8jOW8leeUqCBgRW5naW5lLmRsbGDjgIFgR2FtZUVudGl0eVN5c3RlbS5kbGxgIOetieWPkeihjOeoi+W6j+mbhuOAggoK5byA5Y+R5pe26K+36ZSB5a6a5L2g5pyN5Yqh56uv5a+55bqU55qE5o+Q5Lqk5oiW5Y+R6KGM5qCH562+77yM5bm25LulIFvmjqXlj6Plj4LogINdKC9kZXZlbG9wZXIvc2VydmVyLXBsdWdpbi1hcGkpIOS4uuWHhuOAggo6OjoKCiMjIOaPkuS7tuaYr+S7gOS5iAoK5o+S5Lu25piv5LiA5Liq57un5om/IGBTZXJ2ZXJQbHVnaW5gIOeahCBDIyDnsbvlupPvvIhgLmRsbGDvvInjgILmnI3liqHnq6/lkK/liqjml7bkvJrmiavmj48gYFBsdWdpbnNgIOebruW9le+8jOWunuS+i+WMluWFtuS4reaJgOaciemdnuaKveixoeeahCBgU2VydmVyUGx1Z2luYCDkuI4gYEFic3RyYWN0UHJvY2Vzc0NtZGAg5a2Q57G75bm25Yqg6L2944CCCgojIyDmlrnlvI/kuIDvvJrlvJXnlKjnpL7ljLrmupDnoIHvvIjmjqjojZDnlKjkuo7nu7TmiqTnjrDmnInmnI3vvIkKCuebruagh+ahhuaetiBgbmV0MTAuMGDvvIznm7TmjqXlvJXnlKjlkIzkuIDmupDnoIHmoJHkuK3nmoTmnI3liqHnq6/pobnnm67vvJoKCmBgYHhtbAo8UHJvamVjdCBTZGs9Ik1pY3Jvc29mdC5ORVQuU2RrIj4KICA8UHJvcGVydHlHcm91cD4KICAgIDxUYXJnZXRGcmFtZXdvcms+bmV0MTAuMDwvVGFyZ2V0RnJhbWV3b3JrPgogICAgPEltcGxpY2l0VXNpbmdzPmVuYWJsZTwvSW1wbGljaXRVc2luZ3M+CiAgICA8TnVsbGFibGU+ZW5hYmxlPC9OdWxsYWJsZT4KICA8L1Byb3BlcnR5R3JvdXA+CiAgPEl0ZW1Hcm91cD4KICAgIDxQcm9qZWN0UmVmZXJlbmNlIEluY2x1ZGU9Ii4uXC4uXEVuZ2luZVNlcnZlclxFbmdpbmVTZXJ2ZXIuY3Nwcm9qIiAvPgogICAgPFByb2plY3RSZWZlcmVuY2UgSW5jbHVkZT0iLi5cLi5cRW50aXR5U3lzdGVtU2VydmVyXEVudGl0eVN5c3RlbVNlcnZlci5jc3Byb2oiIC8+CiAgICA8UHJvamVjdFJlZmVyZW5jZSBJbmNsdWRlPSIuLlwuLlxTdXJ2aXZhbGNyYWZ0U2VydmVyXFN1cnZpdmFsY3JhZnRTZXJ2ZXIuY3Nwcm9qIiAvPgogIDwvSXRlbUdyb3VwPgo8L1Byb2plY3Q+CmBgYAoK6Lev5b6E6KaB5oyJ5L2g55qE6aG555uu5L2N572u6LCD5pW044CC6Iul5L2/55So5Y+R6KGMIERMTCDlvJXnlKjvvIzkuZ/lv4XpobvmnaXoh6rnm67moIfmnI3liqHnq6/lkIzkuIDmnoTlu7rjgIIKCuacgOWwj+aPkuS7tuexu++8mgoKYGBgY3NoYXJwCnVzaW5nIEdhbWUuU2VydmVyOwoKbmFtZXNwYWNlIFdvcmtzaG9wLkV4YW1wbGU7CgpwdWJsaWMgc2VhbGVkIGNsYXNzIEhlbGxvUGx1Z2luIDogU2VydmVyUGx1Z2luCnsKICAgIHB1YmxpYyBvdmVycmlkZSBpbnQgVmVyc2lvbiA9PiAxMDAwMDsKICAgIHB1YmxpYyBvdmVycmlkZSBzdHJpbmcgTmFtZSA9PiAiV29ya3Nob3AuSGVsbG8iOwoKICAgIHB1YmxpYyBvdmVycmlkZSB2b2lkIEluaXRpYWxpemUoKQogICAgewogICAgICAgIC8vIOWPquaJp+ihjOS4gOasoe+8muWcqOi/memHjOazqOWGjOS6i+S7tuWkhOeQhuWZqOOAggogICAgfQoKICAgIHB1YmxpYyBvdmVycmlkZSB2b2lkIExvYWQoKQogICAgewogICAgICAgIC8vIOS4lueVjOWKoOi9veWQjuivu+WPlumFjee9ruWSjOaMgeS5heWMluaVsOaNruOAggogICAgfQoKICAgIHB1YmxpYyBvdmVycmlkZSB2b2lkIFNhdmUoKQogICAgewogICAgICAgIC8vIOmaj+S4lueVjOS/neWtmOaPkuS7tuaVsOaNruOAggogICAgfQoKICAgIHB1YmxpYyBvdmVycmlkZSB2b2lkIFVwZGF0ZShmbG9hdCBkdCkKICAgIHsKICAgICAgICAvLyDmr4/luKfosIPnlKjvvIzpgb/lhY3pmLvloZ4gSU8g5ZKM6ZW/6ICX5pe25bel5L2c44CCCiAgICB9Cn0KYGBgCgojIyDmlrnlvI/kuozvvJrlvJXnlKjlj5HooYwgRExM77yI5o+S5Lu256uZIC8g5Y+R6KGM5p6E5bu677yJCgrnm67moIfmoYbmnrYgYC5ORVQgOC4wYO+8jOW8leeUqOa4uOaIj+ebruW9leS4i+eahOWPkeihjOeoi+W6j+mbhu+8mgoKYGBgeG1sCjxQcm9qZWN0IFNkaz0iTWljcm9zb2Z0Lk5FVC5TZGsiPgogIDxQcm9wZXJ0eUdyb3VwPgogICAgPFRhcmdldEZyYW1ld29yaz5uZXQ4LjA8L1RhcmdldEZyYW1ld29yaz4KICAgIDxPdXRwdXRUeXBlPkxpYnJhcnk8L091dHB1dFR5cGU+CiAgICA8QXNzZW1ibHlOYW1lPk15UGx1Z2luPC9Bc3NlbWJseU5hbWU+CiAgPC9Qcm9wZXJ0eUdyb3VwPgogIDxJdGVtR3JvdXA+CiAgICA8UmVmZXJlbmNlIEluY2x1ZGU9IkVuZ2luZSI+CiAgICAgIDxIaW50UGF0aD7muLjmiI/nm67lvZVcRW5naW5lLmRsbDwvSGludFBhdGg+CiAgICA8L1JlZmVyZW5jZT4KICAgIDxSZWZlcmVuY2UgSW5jbHVkZT0iR2FtZUVudGl0eVN5c3RlbSI+CiAgICAgIDxIaW50UGF0aD7muLjmiI/nm67lvZVcR2FtZUVudGl0eVN5c3RlbS5kbGw8L0hpbnRQYXRoPgogICAgPC9SZWZlcmVuY2U+CiAgICA8UmVmZXJlbmNlIEluY2x1ZGU9IkxpdGVOZXRMaWIiPgogICAgICA8SGludFBhdGg+5ri45oiP55uu5b2VXExpdGVOZXRMaWIuZGxsPC9IaW50UGF0aD4KICAgIDwvUmVmZXJlbmNlPgogICAgPFJlZmVyZW5jZSBJbmNsdWRlPSJOZXd0b25zb2Z0Lkpzb24iPgogICAgICA8SGludFBhdGg+5ri45oiP55uu5b2VXE5ld3RvbnNvZnQuSnNvbi5kbGw8L0hpbnRQYXRoPgogICAgPC9SZWZlcmVuY2U+CiAgPC9JdGVtR3JvdXA+CjwvUHJvamVjdD4KYGBgCgo+IOS4jemcgOimgeW8leeUqCBgR2FtZS5kbGxg77yM5o+S5Lu26YCa6L+H5a6e5L2T57O757uf5pON5L2c5ri45oiP44CC5byV55So6Lev5b6E5Lit55qEIGDmuLjmiI/nm67lvZVgIOivt+abv+aNouS4uuS9oOWunumZheeahOacjeWKoeerr+ebruW9leOAggoK5pyA566A5o+S5Lu277yI5Y+R6KGM5p6E5bu65YaZ5rOV77yJ77yaCgpgYGBjc2hhcnAKdXNpbmcgR2FtZS5TZXJ2ZXI7CgpwdWJsaWMgY2xhc3MgTXlQbHVnaW4gOiBTZXJ2ZXJQbHVnaW4KewogICAgcHVibGljIG92ZXJyaWRlIHN0cmluZyBOYW1lID0+ICLmiJHnmoTmj5Lku7YiOwogICAgcHVibGljIG92ZXJyaWRlIGludCBWZXJzaW9uID0+IDE7CiAgICBwdWJsaWMgYnl0ZSBGaXJzdExldmVsID0+IDA7CgogICAgcHVibGljIG92ZXJyaWRlIHZvaWQgSW5pdGlhbGl6ZSgpCiAgICB7CiAgICAgICAgQ29uc29sZS5Xcml0ZUxpbmUoIuaPkuS7tuWKoOi9veaIkOWKn++8gSIpOwogICAgfQoKICAgIHB1YmxpYyBvdmVycmlkZSB2b2lkIExvYWQoKSB7IH0KICAgIHB1YmxpYyBvdmVycmlkZSB2b2lkIFNhdmUoKSB7IH0KfQpgYGAKCiMjIOW/hemhu+WunueOsOeahOaIkOWRmAoKfCDmiJDlkZggfCDnsbvlnosgfCDor7TmmI4gfAp8IC0tLSB8IC0tLSB8IC0tLSB8CnwgYE5hbWVgIHwgYHN0cmluZ2AgfCDmj5Lku7blkI3np7DvvIzmmL7npLrlnKjml6Xlv5fkuK0gfAp8IGBWZXJzaW9uYCB8IGBpbnRgIHwg54mI5pys5Y+377yM5pW05pWw77yI5L6L77yaYDEwMDAwYCDooajnpLogMS4wLjDvvIkgfAp8IGBGaXJzdExldmVsYCB8IGBieXRlYCB8IOS8mOWFiOe6p++8jOm7mOiupOWhqyBgMGAg5Y2z5Y+vIHwKfCBgSW5pdGlhbGl6ZSgpYCB8IOaWueazlSB8IOaPkuS7tuWKoOi9veaXtuaJp+ihjOS4gOasoe+8jOeUqOadpeazqOWGjOS6i+S7tiB8CnwgYExvYWQoKWAgfCDmlrnms5UgfCDkuJbnlYzliqDovb3lkI7osIPnlKjvvIzor7vlj5bmlbDmja4v6YWN572uIHwKfCBgU2F2ZSgpYCB8IOaWueazlSB8IOS/neWtmOaVsOaNruaXtuiwg+eUqO+8iOWFs+mXreaIluiHquWKqOS/neWtmO+8iSB8CnwgYFVwZGF0ZShmbG9hdCBkdClgIHwg5pa55rOVIHwg5q+P5bin6LCD55So77yM5Y+v6YCJ6YeN5YaZ77yM6YG/5YWN6Zi75aGeIHwKCiMjIOaPkuS7tueahOeUn+WRveWRqOacnwoKYGBgCuacjeWKoeWZqOWQr+WKqAogICAg4oaTCkluaXRpYWxpemUoKSDooqvosIPnlKgg4oaQIOWcqOi/memHjOazqOWGjOS6i+S7tuebkeWQrAogICAg4oaTCkxvYWQoKSDooqvosIPnlKgg4oaQIOWcqOi/memHjOivu+WPlumFjee9ruaWh+S7tgogICAg4oaTCuaPkuS7tui/kOihjOS4re+8iFVwZGF0ZSDmr4/luKfosIPnlKjvvIkuLi4KICAgIOKGkwpTYXZlKCkg6KKr6LCD55SoIOKGkCDmnI3liqHlmajlhbPpl63miJboh6rliqjkv53lrZjml7YKICAgIOKGkwrmnI3liqHlmajlhbPpl60KYGBgCgrmnI3liqHnq6/kvJrmiavmj48gYFBsdWdpbnNgIOebruW9leeahCBgLmRsbGDvvIzliqDovb3nqIvluo/pm4bvvIzlubblrp7kvovljJblhbbkuK3pnZ7mir3osaHnmoQgYFNlcnZlclBsdWdpbmAg5LiOIGBBYnN0cmFjdFByb2Nlc3NDbWRgIOWtkOexu+OAguWboOatpOaPkuS7tuexu+W/hemhu+WFrOW8gOOAgemdnuaKveixoe+8jOW5tuiDveeUseWKoOi9veWZqOato+W4uOaehOmAoOOAggoKIyMg5rOo5YaM5LqL5Lu2CgrlnKggYEluaXRpYWxpemUoKWAg5Lit5rOo5YaM5L2g5oOz55uR5ZCs55qE5LqL5Lu277yaCgpgYGBjc2hhcnAKdXNpbmcgR2FtZS5TZXJ2ZXI7CnVzaW5nIEdhbWUuU2VydmVyLlBsYXllckV2ZW50OwoKcHVibGljIGNsYXNzIE15UGx1Z2luIDogU2VydmVyUGx1Z2luLCBJUGxheWVyRW50ZXJHYW1lRXZlbnRIYW5kbGUKewogICAgcHVibGljIG92ZXJyaWRlIHN0cmluZyBOYW1lID0+ICJNeVBsdWdpbiI7CiAgICBwdWJsaWMgb3ZlcnJpZGUgaW50IFZlcnNpb24gPT4gMTsKICAgIHB1YmxpYyBieXRlIEZpcnN0TGV2ZWwgPT4gMDsKCiAgICBwdWJsaWMgb3ZlcnJpZGUgdm9pZCBJbml0aWFsaXplKCkKICAgIHsKICAgICAgICAvLyDms6jlhoznjqnlrrbov5vlhaUv56a75byA5LqL5Lu2CiAgICAgICAgUGxheWVyRW50ZXJHYW1lRXZlbnRNYW5hZ2VyLkFkZE9iamVjdCh0aGlzKTsKICAgIH0KCiAgICB2b2lkIElQbGF5ZXJFbnRlckdhbWVFdmVudEhhbmRsZS5QbGF5ZXJFbnRlcihQbGF5ZXJEYXRhIHBsYXllcikKICAgIHsKICAgICAgICBDb25zb2xlLldyaXRlTGluZSgkIuasoui/jiB7cGxheWVyLk5hbWV977yBIik7CiAgICB9CgogICAgdm9pZCBJUGxheWVyRW50ZXJHYW1lRXZlbnRIYW5kbGUuUGxheWVyTGVhdmUoUGxheWVyRGF0YSBwbGF5ZXIpCiAgICB7CiAgICAgICAgQ29uc29sZS5Xcml0ZUxpbmUoJCJ7cGxheWVyLk5hbWV9IOemu+W8gOS6hiIpOwogICAgfQoKICAgIHB1YmxpYyBvdmVycmlkZSB2b2lkIExvYWQoKSB7IH0KICAgIHB1YmxpYyBvdmVycmlkZSB2b2lkIFNhdmUoKSB7IH0KfQpgYGAKCuWkp+WkmuaVsOS6i+S7tumDveacieWvueW6lOeahCBgWHh4RXZlbnRNYW5hZ2VyLkFkZE9iamVjdCh0aGlzKWAg5LiOIGBSZW1vdmVPYmplY3QodGhpcylg44CC5LqL5Lu26K6i6ZiF5bu66K6u5pS+5ZyoIGBJbml0aWFsaXplKClg77yM6YG/5YWNIGBMb2FkKClgIOWkmuasoeaJp+ihjOWvvOiHtOmHjeWkjeiuoumYheOAggoKIyMg57yW6K+R5LiO5a6J6KOFCgpgYGBwb3dlcnNoZWxsCmRvdG5ldCBidWlsZCAtYyBSZWxlYXNlCmBgYAoK5oqK6L6T5Ye6IERMTCDlj4rlhbbni6zmnInkvp3otZblpI3liLbliLDmtYvor5XmnI3liqHnq6/nmoQgYFBsdWdpbnNgIOebruW9le+8jOeEtuWQjuS+neasoemqjOivge+8muWKoOi9veOAgeWIneWni+WMluOAgeS4lueVjOivu+WPluOAgeeOqeWutuWKoOWFpeOAgWBzYXZlYOOAgWBleGl0YCDlkozph43lkK/mgaLlpI3jgILkuI3opoHmiormnI3liqHnq6/lt7LmnInnmoTmoLjlv4MgRExMIOWGjeWkjeWItui/m+aPkuS7tuebruW9leOAggoKIyMg5Y+v55So5LqL5Lu25o6l5Y+j77yI6YCf6KeI77yJCgp8IOaOpeWPoyB8IOivtOaYjiB8CnwgLS0tIHwgLS0tIHwKfCBgSVBsYXllckVudGVyR2FtZUV2ZW50SGFuZGxlYCB8IOeOqeWutui/m+WFpS/nprvlvIAgfAp8IGBJUGxheWVyTW92ZUhhbmRsZWAgfCDnjqnlrrbnp7vliqggfAp8IGBJUGxheWVyQnJlYWtBbmRQbGFjZUhhbmRsZWAgfCDmlrnlnZfnoLTlnY8v5pS+572uIHwKfCBgSVBsYXllckludGVyYWN0RXZlbnRIYW5kbGVgIHwg546p5a625Lqk5LqS77yI54K55Ye744CB5pS75Ye7562J77yJIHwKCuWujOaVtOa4heWNleS4juetvuWQjeingSBb5o6l5Y+j5Y+C6ICDXSgvZGV2ZWxvcGVyL3NlcnZlci1wbHVnaW4tYXBpKSDkuI4gW+S6i+S7tuezu+e7n10oL2RldmVsb3Blci9zZXJ2ZXItcGx1Z2lucy9ldmVudHMp44CCCgojIyDluLjop4Hpl67popgKCioqUTog5o+S5Lu25rKh5Yqg6L2977yfKioKQTog5qOA5p+lIGAuZGxsYCDmmK/lkKblnKggYFBsdWdpbnNgIOaWh+S7tuWkue+8jOajgOafpeaYr+WQpuacieaKpemUme+8m+ehruiupOaPkuS7tuexu+aYryBgcHVibGljYCDkuJTpnZ7mir3osaHjgIIKCioqUTog5oql6ZSZ5om+5LiN5Yiw57G75Z6L77yfKioKQTog56Gu6K6k5byV55So5LqG5q2j56Gu55qE56iL5bqP6ZuG77yI5Y+R6KGMIERMTCDmlrnlvI/mo4Dmn6UgYEhpbnRQYXRoYO+8jOa6kOeggeaWueW8j+ajgOafpSBgUHJvamVjdFJlZmVyZW5jZWDvvInjgILms6jmhI/lkb3ku6Tln7rnsbvlkb3lkI3nqbrpl7TvvJrmupDnoIHmnoTlu7rkuLogYEdhbWUuU2VydmVyYO+8jOWPkeihjOaehOW7uuS4uiBgR2FtZS5TZXJ2ZXIuRXNzZW50aWFsQ21kYOOAggoKKipROiDkuovku7bmsqHop6blj5HvvJ8qKgpBOiDnoa7orqTlnKggYEluaXRpYWxpemUoKWAg5Lit6LCD55So5LqG5a+55bqU55qEIGBFdmVudE1hbmFnZXIuQWRkT2JqZWN0KHRoaXMpYOOAggoK5LiL5LiA5q2l6ZiF6K+7IFvlkb3ku6Tns7vnu59dKC9kZXZlbG9wZXIvc2VydmVyLXBsdWdpbnMvY29tbWFuZHMp44CBW+S6i+S7tuezu+e7n10oL2RldmVsb3Blci9zZXJ2ZXItcGx1Z2lucy9ldmVudHMp44CBW+eOqeWutueuoeeQhl0oL2RldmVsb3Blci9zZXJ2ZXItcGx1Z2lucy9wbGF5ZXItbWFuYWdlbWVudCkg5LiOIFvmjqXlj6Plj4LogINdKC9kZXZlbG9wZXIvc2VydmVyLXBsdWdpbi1hcGkp44CCCg==
