From 279da58dc135435d4121cd371b36bcf69bdd3da3 Mon Sep 17 00:00:00 2001 From: Naruse <71993948+DevilProMT@users.noreply.github.com> Date: Mon, 20 Apr 2026 12:40:38 +0800 Subject: [PATCH] enter intro cutscene --- .editorconfig | 125 +++++ .gitignore | 370 ++++++++++++ Common/Common.csproj | 32 ++ Common/Configuration/ConfigContainer.cs | 76 +++ Common/Configuration/HotfixContainer.cs | 75 +++ Common/Data/Config/DictionaryConverter.cs | 43 ++ Common/Data/ExcelResource.cs | 18 + Common/Data/GameData.cs | 5 + Common/Data/ResourceEntity.cs | 33 ++ Common/Data/ResourceManager.cs | 169 ++++++ Common/Database/Account/AccountData.cs | 181 ++++++ Common/Database/BaseDatabaseDataHelper.cs | 8 + Common/Database/CustomSerializeService.cs | 32 ++ Common/Database/DatabaseHelper.cs | 305 ++++++++++ Common/Database/Player/PlayerGameData.cs | 23 + .../Enums/Language/ProgramLanguageTypeEnum.cs | 9 + Common/Enums/Packet/PacketFraming.cs | 9 + Common/Enums/Player/BanTypeEnum.cs | 13 + Common/Enums/Player/FriendEnum.cs | 7 + Common/Enums/Player/PermEnum.cs | 9 + Common/Internationalization/I18nManager.cs | 102 ++++ .../Message/LanguageCHS.cs | 527 +++++++++++++++++ .../Message/LanguageCHT.cs | 530 ++++++++++++++++++ .../Message/LanguageEN.cs | 277 +++++++++ .../PluginLanguageAttribute.cs | 9 + Common/Util/ConfigManager.cs | 99 ++++ Common/Util/Crpyto/DispatchEncryption.cs | 35 ++ Common/Util/DateTime.cs | 14 + Common/Util/Extensions/Extensions.cs | 232 ++++++++ Common/Util/GameConstants.cs | 11 + Common/Util/Guid64.cs | 11 + Common/Util/IConsole.cs | 185 ++++++ Common/Util/Logger.cs | 109 ++++ Common/Util/LoggingMiddleware.cs | 34 ++ Common/Util/Position.cs | 129 +++++ Common/Util/Security/Crypto.cs | 29 + Config/Config.json | 42 ++ Config/Hotfix.json | 51 ++ GameServer/Command/CommandArg.cs | 79 +++ GameServer/Command/CommandAttribute.cs | 25 + GameServer/Command/CommandExecutor.cs | 19 + GameServer/Command/CommandInterface.cs | 3 + GameServer/Command/CommandManager.cs | 126 +++++ GameServer/Command/CommandSender.cs | 41 ++ GameServer/Command/Commands/CommandHelp.cs | 52 ++ GameServer/Game/BasePlayerManager.cs | 8 + GameServer/Game/Player/PlayerInstance.cs | 98 ++++ GameServer/GameServer.csproj | 28 + GameServer/Server/Connection.cs | 142 +++++ GameServer/Server/Listener.cs | 13 + GameServer/Server/Packet/Handler.cs | 6 + GameServer/Server/Packet/HandlerManager.cs | 31 + GameServer/Server/Packet/Opcode.cs | 7 + .../Packet/Recv/Login/HandlerReqLogin.cs | 50 ++ .../Packet/Send/Login/PacketRspLogin.cs | 29 + MikuSB.sln | 60 ++ MikuSB/MikuSB.csproj | 21 + MikuSB/Program/LoaderManager.cs | 178 ++++++ MikuSB/Program/MikuSB.cs | 90 +++ .../PublishProfiles/MikuSB-Win64-Debug.pubxml | 18 + .../MikuSB-Win64-MultiFile.pubxml | 19 + .../MikuSB-Win64-OneFile.pubxml | 19 + MikuSB/Source/Kiana.ico | Bin 0 -> 432254 bytes MikuSB/Tool/AssemblyGenerater.cs | 38 ++ MikuSB/Tool/HandbookGenerator.cs | 102 ++++ Proto/CmdIds.cs | 136 +++++ Proto/Core.proto | 302 ++++++++++ Proto/Proto.csproj | 26 + Proto/Snowbreak.proto | 381 +++++++++++++ SdkServer/Handlers/RouteController.cs | 285 ++++++++++ SdkServer/Models/ResponseBase.cs | 9 + SdkServer/SdkServer.cs | 95 ++++ SdkServer/SdkServer.csproj | 19 + .../Utils/JsonStringToObjectConverter.cs | 24 + SdkServer/Utils/LoggingMiddleware.cs | 34 ++ TcpSharp/BasePacket.cs | 51 ++ TcpSharp/PacketCodec.cs | 308 ++++++++++ TcpSharp/SessionStateEnum.cs | 10 + TcpSharp/SocketConnection.cs | 203 +++++++ TcpSharp/SocketListener.cs | 105 ++++ TcpSharp/TcpSharp.csproj | 21 + 81 files changed, 7279 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 Common/Common.csproj create mode 100644 Common/Configuration/ConfigContainer.cs create mode 100644 Common/Configuration/HotfixContainer.cs create mode 100644 Common/Data/Config/DictionaryConverter.cs create mode 100644 Common/Data/ExcelResource.cs create mode 100644 Common/Data/GameData.cs create mode 100644 Common/Data/ResourceEntity.cs create mode 100644 Common/Data/ResourceManager.cs create mode 100644 Common/Database/Account/AccountData.cs create mode 100644 Common/Database/BaseDatabaseDataHelper.cs create mode 100644 Common/Database/CustomSerializeService.cs create mode 100644 Common/Database/DatabaseHelper.cs create mode 100644 Common/Database/Player/PlayerGameData.cs create mode 100644 Common/Enums/Language/ProgramLanguageTypeEnum.cs create mode 100644 Common/Enums/Packet/PacketFraming.cs create mode 100644 Common/Enums/Player/BanTypeEnum.cs create mode 100644 Common/Enums/Player/FriendEnum.cs create mode 100644 Common/Enums/Player/PermEnum.cs create mode 100644 Common/Internationalization/I18nManager.cs create mode 100644 Common/Internationalization/Message/LanguageCHS.cs create mode 100644 Common/Internationalization/Message/LanguageCHT.cs create mode 100644 Common/Internationalization/Message/LanguageEN.cs create mode 100644 Common/Internationalization/PluginLanguageAttribute.cs create mode 100644 Common/Util/ConfigManager.cs create mode 100644 Common/Util/Crpyto/DispatchEncryption.cs create mode 100644 Common/Util/DateTime.cs create mode 100644 Common/Util/Extensions/Extensions.cs create mode 100644 Common/Util/GameConstants.cs create mode 100644 Common/Util/Guid64.cs create mode 100644 Common/Util/IConsole.cs create mode 100644 Common/Util/Logger.cs create mode 100644 Common/Util/LoggingMiddleware.cs create mode 100644 Common/Util/Position.cs create mode 100644 Common/Util/Security/Crypto.cs create mode 100644 Config/Config.json create mode 100644 Config/Hotfix.json create mode 100644 GameServer/Command/CommandArg.cs create mode 100644 GameServer/Command/CommandAttribute.cs create mode 100644 GameServer/Command/CommandExecutor.cs create mode 100644 GameServer/Command/CommandInterface.cs create mode 100644 GameServer/Command/CommandManager.cs create mode 100644 GameServer/Command/CommandSender.cs create mode 100644 GameServer/Command/Commands/CommandHelp.cs create mode 100644 GameServer/Game/BasePlayerManager.cs create mode 100644 GameServer/Game/Player/PlayerInstance.cs create mode 100644 GameServer/GameServer.csproj create mode 100644 GameServer/Server/Connection.cs create mode 100644 GameServer/Server/Listener.cs create mode 100644 GameServer/Server/Packet/Handler.cs create mode 100644 GameServer/Server/Packet/HandlerManager.cs create mode 100644 GameServer/Server/Packet/Opcode.cs create mode 100644 GameServer/Server/Packet/Recv/Login/HandlerReqLogin.cs create mode 100644 GameServer/Server/Packet/Send/Login/PacketRspLogin.cs create mode 100644 MikuSB.sln create mode 100644 MikuSB/MikuSB.csproj create mode 100644 MikuSB/Program/LoaderManager.cs create mode 100644 MikuSB/Program/MikuSB.cs create mode 100644 MikuSB/Properties/PublishProfiles/MikuSB-Win64-Debug.pubxml create mode 100644 MikuSB/Properties/PublishProfiles/MikuSB-Win64-MultiFile.pubxml create mode 100644 MikuSB/Properties/PublishProfiles/MikuSB-Win64-OneFile.pubxml create mode 100644 MikuSB/Source/Kiana.ico create mode 100644 MikuSB/Tool/AssemblyGenerater.cs create mode 100644 MikuSB/Tool/HandbookGenerator.cs create mode 100644 Proto/CmdIds.cs create mode 100644 Proto/Core.proto create mode 100644 Proto/Proto.csproj create mode 100644 Proto/Snowbreak.proto create mode 100644 SdkServer/Handlers/RouteController.cs create mode 100644 SdkServer/Models/ResponseBase.cs create mode 100644 SdkServer/SdkServer.cs create mode 100644 SdkServer/SdkServer.csproj create mode 100644 SdkServer/Utils/JsonStringToObjectConverter.cs create mode 100644 SdkServer/Utils/LoggingMiddleware.cs create mode 100644 TcpSharp/BasePacket.cs create mode 100644 TcpSharp/PacketCodec.cs create mode 100644 TcpSharp/SessionStateEnum.cs create mode 100644 TcpSharp/SocketConnection.cs create mode 100644 TcpSharp/SocketListener.cs create mode 100644 TcpSharp/TcpSharp.csproj diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3d4d3e3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,125 @@ +[*.cs] + +# IDE0022: 使用方法的程序块主体 +csharp_style_expression_bodied_methods = false:silent + +[*.cs] +#### 命名样式 #### + +# 命名规则 + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# 符号规范 + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# 命名样式 + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +csharp_using_directive_placement = outside_namespace:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent +csharp_style_var_elsewhere = false:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_space_around_binary_operators = before_and_after +csharp_indent_labels = one_less_than_current + +[*.vb] +#### 命名样式 #### + +# 命名规则 + +dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion +dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface +dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始 + +dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion +dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型 +dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 + +dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion +dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员 +dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 + +# 符号规范 + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected +dotnet_naming_symbols.类型.required_modifiers = + +dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method +dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected +dotnet_naming_symbols.非字段成员.required_modifiers = + +# 命名样式 + +dotnet_naming_style.以_i_开始.required_prefix = I +dotnet_naming_style.以_i_开始.required_suffix = +dotnet_naming_style.以_i_开始.word_separator = +dotnet_naming_style.以_i_开始.capitalization = pascal_case + +dotnet_naming_style.帕斯卡拼写法.required_prefix = +dotnet_naming_style.帕斯卡拼写法.required_suffix = +dotnet_naming_style.帕斯卡拼写法.word_separator = +dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case + +dotnet_naming_style.帕斯卡拼写法.required_prefix = +dotnet_naming_style.帕斯卡拼写法.required_suffix = +dotnet_naming_style.帕斯卡拼写法.word_separator = +dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case + +[*.{cs,vb}] +end_of_line = crlf +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +tab_width = 4 +indent_size = 4 +dotnet_style_operator_placement_when_wrapping = beginning_of_line \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4207fbf --- /dev/null +++ b/.gitignore @@ -0,0 +1,370 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +.idea/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +[Ll]aunchSettings.json +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +# *.pubxml +# *.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# Special Files +/SdkServer/Properties +/GameServer/OriginalProto +*.rar \ No newline at end of file diff --git a/Common/Common.csproj b/Common/Common.csproj new file mode 100644 index 0000000..024f2ef --- /dev/null +++ b/Common/Common.csproj @@ -0,0 +1,32 @@ + + + + net9.0 + enable + enable + false + MikuSB + MikuCommon + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Common/Configuration/ConfigContainer.cs b/Common/Configuration/ConfigContainer.cs new file mode 100644 index 0000000..5472e47 --- /dev/null +++ b/Common/Configuration/ConfigContainer.cs @@ -0,0 +1,76 @@ +namespace MikuSB.Configuration; + +public class ConfigContainer +{ + public HttpServerConfig HttpServer { get; set; } = new(); + public GameServerConfig GameServer { get; set; } = new(); + public PathConfig Path { get; set; } = new(); + public ServerOption ServerOption { get; set; } = new(); +} + +public class HttpServerConfig +{ + public string BindAddress { get; set; } = "0.0.0.0"; + public string PublicAddress { get; set; } = "127.0.0.1"; + public int Port { get; set; } = 21500; + + public string GetDisplayAddress() + { + return "http" + "://" + PublicAddress + ":" + Port; + } + + public string GetBindDisplayAddress() + { + return "http" + "://" + BindAddress + ":" + Port; + } +} + +public class GameServerConfig +{ + public string BindAddress { get; set; } = "0.0.0.0"; + public string PublicAddress { get; set; } = "127.0.0.1"; + public int Port { get; set; } = 21000; + public int KcpAliveMs { get; set; } = 45000; + public string DatabaseName { get; set; } = "Miku.db"; + public string GameServerId { get; set; } = "MikuSB"; + public string GameServerName { get; set; } = "MikuSB"; + public string GetDisplayAddress() + { + return PublicAddress + ":" + Port; + } +} + +public class PathConfig +{ + public string ResourcePath { get; set; } = "Resources"; + public string ConfigPath { get; set; } = "Config"; + public string DatabasePath { get; set; } = "Config/Database"; + public string HandbookPath { get; set; } = "Config/Handbook"; + public string LogPath { get; set; } = "Config/Logs"; + public string DataPath { get; set; } = "Config/Data"; +} + +public class ServerOption +{ + public string Language { get; set; } = "EN"; + public string FallbackLanguage { get; set; } = "EN"; + public string[] DefaultPermissions { get; set; } = ["Admin"]; + public ServerProfile ServerProfile { get; set; } = new(); + public bool AutoCreateUser { get; set; } = true; + public bool SavePersonalDebugFile { get; set; } = false; + public bool AutoSendResponseWhenNoHandler { get; set; } = true; +#if DEBUG + public bool EnableDebug { get; set; } = true; +#else + public bool EnableDebug { get; set; } = false; +#endif + public bool DebugMessage { get; set; } = true; + public bool DebugDetailMessage { get; set; } = true; + public bool DebugNoHandlerPacket { get; set; } = true; +} + +public class ServerProfile +{ + public string Name { get; set; } = "Miku-chan"; + public int Uid { get; set; } = 80; +} \ No newline at end of file diff --git a/Common/Configuration/HotfixContainer.cs b/Common/Configuration/HotfixContainer.cs new file mode 100644 index 0000000..742f10c --- /dev/null +++ b/Common/Configuration/HotfixContainer.cs @@ -0,0 +1,75 @@ +using System.Text.Json.Serialization; +using Newtonsoft.Json; + +namespace MikuSB.Configuration; + +public class HotfixContainer +{ + public bool UseLocalCache { get; set; } = false; + public Dictionary Hotfixes { get; set; } = new(); + public Dictionary AesKeys { get; set; } = new (); + + public static string ExtractVersionNumber(string? version) + { + try + { + return version == null ? "" : version[..version.IndexOf('_')]; + } + catch + { + return ""; + } + } +} + +public class HotfixManfiset +{ + [JsonPropertyName("Asb")] public AsbData Asb { get; set; } = new(); + [JsonPropertyName("AsbPreDownload")] public AsbPreDownloadData AsbPreDownload { get; set; } = new(); + [JsonPropertyName("Audio")] public AudioData Audio { get; set; } = new(); + [JsonPropertyName("AudioPreDownload")] public AudioPreDownloadData AudioPreDownload { get; set; } = new(); + [JsonPropertyName("VideoEncrypt")] public VideoEncryptData VideoEncrypt { get; set; } = new(); +} + +public class AsbData +{ + [JsonPropertyName("android")] public PlatformInfo Android { get; set; } = new(); + [JsonPropertyName("iphone")] public PlatformInfo Iphone { get; set; } = new(); + [JsonPropertyName("pc")] public PlatformInfo Pc { get; set; } = new(); +} + +public class AsbPreDownloadData +{ + [JsonPropertyName("android")] public PlatformEncryptedInfo Android { get; set; } = new(); + [JsonPropertyName("iphone")] public PlatformEncryptedInfo Iphone { get; set; } = new(); +} + +public class AudioData +{ + [JsonPropertyName("platform")] public Dictionary Platform { get; set; } = new(); + [JsonPropertyName("revision")] public int Revision { get; set; } +} + +public class AudioPreDownloadData +{ + [JsonPropertyName("enable_time")] public long EnableTime { get; set; } + [JsonPropertyName("platform")] public Dictionary Platform { get; set; } = new(); + [JsonPropertyName("revision")] public int Revision { get; set; } +} + +public class VideoEncryptData +{ + [JsonPropertyName("filename")] public string FileName { get; set; } = ""; +} + +public class PlatformInfo +{ + [JsonPropertyName("enable_time")] public long EnableTime { get; set; } + [JsonPropertyName("revision")] public string Revision { get; set; } = ""; + [JsonPropertyName("suffix")] public string Suffix { get; set; } = ""; +} + +public class PlatformEncryptedInfo : PlatformInfo +{ + [JsonPropertyName("encrypt_key")] public string EncryptKey { get; set; } = ""; +} \ No newline at end of file diff --git a/Common/Data/Config/DictionaryConverter.cs b/Common/Data/Config/DictionaryConverter.cs new file mode 100644 index 0000000..cccb4e4 --- /dev/null +++ b/Common/Data/Config/DictionaryConverter.cs @@ -0,0 +1,43 @@ +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; + +namespace MikuSB.Data.Config; + +class IntDictionaryConverter : JsonConverter> +{ + public override Dictionary? ReadJson(JsonReader reader, Type objectType, Dictionary? existingValue, bool hasExistingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.StartArray) + { + JArray.Load(reader); + return new Dictionary(); + } + else if (reader.TokenType == JsonToken.StartObject) + { + var obj = JObject.Load(reader); + var dict = new Dictionary(); + + foreach (var prop in obj.Properties()) + { + if (int.TryParse(prop.Name, out var key)) + { + dict[key] = prop.Value.ToObject(); + } + } + return dict; + } + + return new Dictionary(); + } + + public override void WriteJson(JsonWriter writer, Dictionary? value, JsonSerializer serializer) + { + writer.WriteStartObject(); + foreach (var kv in value) + { + writer.WritePropertyName(kv.Key.ToString()); + writer.WriteValue(kv.Value); + } + writer.WriteEndObject(); + } +} diff --git a/Common/Data/ExcelResource.cs b/Common/Data/ExcelResource.cs new file mode 100644 index 0000000..506a51e --- /dev/null +++ b/Common/Data/ExcelResource.cs @@ -0,0 +1,18 @@ +namespace MikuSB.Data; + +public abstract class ExcelResource +{ + public abstract uint GetId(); + + public virtual void Loaded() + { + } + + public virtual void Finalized() + { + } + + public virtual void AfterAllDone() + { + } +} \ No newline at end of file diff --git a/Common/Data/GameData.cs b/Common/Data/GameData.cs new file mode 100644 index 0000000..a0fc47d --- /dev/null +++ b/Common/Data/GameData.cs @@ -0,0 +1,5 @@ +namespace MikuSB.Data; + +public static class GameData +{ +} \ No newline at end of file diff --git a/Common/Data/ResourceEntity.cs b/Common/Data/ResourceEntity.cs new file mode 100644 index 0000000..6ad6c87 --- /dev/null +++ b/Common/Data/ResourceEntity.cs @@ -0,0 +1,33 @@ +namespace MikuSB.Data; + +[AttributeUsage(AttributeTargets.Class, Inherited = false)] +public class ResourceEntity : Attribute +{ + [Obsolete("No effect")] + public ResourceEntity(string fileName, bool isCritical = false, bool isMultifile = false) + { + if (isMultifile) + FileName = new List(fileName.Split(',')); + else + FileName = [fileName]; + IsCritical = isCritical; + } + + + public ResourceEntity(string fileName, bool isMultifile = false) + { + if (isMultifile) + FileName = new List(fileName.Split(',')); + else + FileName = [fileName]; + } + + public ResourceEntity(string fileName) + { + FileName = [fileName]; + } + + public List FileName { get; private set; } + + [Obsolete("No effect")] public bool IsCritical { get; private set; } // deprecated +} \ No newline at end of file diff --git a/Common/Data/ResourceManager.cs b/Common/Data/ResourceManager.cs new file mode 100644 index 0000000..c2c8f30 --- /dev/null +++ b/Common/Data/ResourceManager.cs @@ -0,0 +1,169 @@ +using MikuSB.Internationalization; +using MikuSB.Util; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Reflection; + +namespace MikuSB.Data; + +public class ResourceManager +{ + public static Logger Logger { get; } = new("ResourceManager"); + public static bool IsLoaded { get; set; } + + public static void LoadGameData() + { + LoadExcel(); + } + + public static void LoadExcel() + { + var classes = Assembly.GetExecutingAssembly().GetTypes(); // Get all classes in the assembly + List resList = []; + + foreach (var cls in classes.Where(x => x.IsSubclassOf(typeof(ExcelResource)))) + { + var res = LoadSingleExcelResource(cls); + if (res != null) resList.AddRange(res); + } + + foreach (var cls in resList) cls.AfterAllDone(); + } + + public static List? LoadSingleExcel(Type cls) where T : ExcelResource, new() + { + return LoadSingleExcelResource(cls) as List; + } + + public static List? LoadSingleExcelResource(Type cls) + { + var attribute = (ResourceEntity?)Attribute.GetCustomAttribute(cls, typeof(ResourceEntity)); + + if (attribute == null) return null; + var resource = (ExcelResource)Activator.CreateInstance(cls)!; + var count = 0; + List resList = []; + foreach (var fileName in attribute.FileName) + try + { + var path = ConfigManager.Config.Path.ResourcePath + "/ExcelOutput/" + fileName; + var file = new FileInfo(path); + if (!file.Exists) + { + Logger.Error(I18NManager.Translate("Server.ServerInfo.FailedToReadItem", fileName, + I18NManager.Translate("Word.NotFound"))); + continue; + } + + var json = file.OpenText().ReadToEnd(); + using (var reader = new JsonTextReader(new StringReader(json))) + { + reader.Read(); + switch (reader.TokenType) + { + case JsonToken.StartArray: + { + // array + var jArray = JArray.Parse(json); + foreach (var item in jArray) + { + var res = JsonConvert.DeserializeObject(item.ToString(), cls); + resList.Add((ExcelResource)res!); + ((ExcelResource?)res)?.Loaded(); + count++; + } + + break; + } + case JsonToken.StartObject: + { + // dictionary + var jObject = JObject.Parse(json); + foreach (var (_, obj) in jObject) + { + var instance = JsonConvert.DeserializeObject(obj!.ToString(), cls); + + if (((ExcelResource?)instance)?.GetId() == 0 || (ExcelResource?)instance == null) + { + // Deserialize as JObject to handle nested dictionaries + var nestedObject = JsonConvert.DeserializeObject(obj.ToString()); + + foreach (var nestedItem in nestedObject ?? []) + { + var nestedInstance = + JsonConvert.DeserializeObject(nestedItem.Value!.ToString(), cls); + resList.Add((ExcelResource)nestedInstance!); + ((ExcelResource?)nestedInstance)?.Loaded(); + count++; + } + } + else + { + resList.Add((ExcelResource)instance); + ((ExcelResource)instance).Loaded(); + } + + count++; + } + + break; + } + } + } + + resource.Finalized(); + } + catch (Exception ex) + { + Logger.Error( + I18NManager.Translate("Server.ServerInfo.FailedToReadItem", fileName, + I18NManager.Translate("Word.Error")), ex); + } + + Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadedItems", count.ToString(), cls.Name)); + + return resList; + } + + public static T? LoadCustomFile(string filetype, string filename) + { + var type = I18NManager.Translate("Word." + filetype); + Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadingItem", type)); + FileInfo file = new(ConfigManager.Config.Path.DataPath + $"/{filename}.json"); + T? customFile = default; + if (!file.Exists) + { + Logger.Warn(I18NManager.Translate("Server.ServerInfo.ConfigMissing", type, + $"{ConfigManager.Config.Path.DataPath}/{filename}.json", type)); + return customFile; + } + + try + { + using var reader = file.OpenRead(); + using StreamReader reader2 = new(reader); + var text = reader2.ReadToEnd(); + var json = JsonConvert.DeserializeObject(text); + customFile = json; + } + catch (Exception ex) + { + Logger.Error("Error in reading " + file.Name, ex); + } + + switch (customFile) + { + case Dictionary d: + Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadedItems", d.Count.ToString(), type)); + break; + case Dictionary> di: + Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadedItems", di.Count.ToString(), type)); + break; + default: + Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadedItem", filetype)); + break; + } + + return customFile; + } +} \ No newline at end of file diff --git a/Common/Database/Account/AccountData.cs b/Common/Database/Account/AccountData.cs new file mode 100644 index 0000000..48d43e7 --- /dev/null +++ b/Common/Database/Account/AccountData.cs @@ -0,0 +1,181 @@ +using MikuSB.Enums.Player; +using MikuSB.Util; +using MikuSB.Util.Extensions; +using MikuSB.Util.Security; +using SqlSugar; + +namespace MikuSB.Database.Account; + +[SugarTable("Account")] +public class AccountData : BaseDatabaseDataHelper +{ + public string Username { get; set; } = ""; + public string Password { get; set; } = ""; + public BanTypeEnum BanType { get; set; } + public string Phone { get; set; } = ""; + + [SugarColumn(IsJson = true)] public List Permissions { get; set; } = []; + + [SugarColumn(IsNullable = true)] public string? ComboToken { get; set; } + [SugarColumn(IsNullable = true)] public string? DispatchToken { get; set; } + + #region GetAccount + + public static AccountData? GetAccountByUserName(string username) + { + AccountData? result = null; + DatabaseHelper.GetAllInstance()?.ForEach(account => + { + if (account.Username == username) result = account; + }); + return result; + } + + public static AccountData? GetAccountByUid(int uid, bool force = false) + { + var result = DatabaseHelper.GetInstance(uid, force); + return result; + } + + public static AccountData? GetAccountByDispatchToken(string dispatchToken) + { + AccountData? result = null; + DatabaseHelper.GetAllInstance()?.ForEach(account => + { + if (account.DispatchToken == dispatchToken) result = account; + }); + return result; + } + + public static AccountData? GetAccountByComboToken(string comboToken) + { + AccountData? result = null; + DatabaseHelper.GetAllInstance()?.ForEach(account => + { + if (account.ComboToken == comboToken) result = account; + }); + return result; + } + + #endregion + + #region Account + + public static void CreateAccount(string username, int uid, string password) + { + var newUid = uid; + if (uid == 0) + { + newUid = 1; + while (GetAccountByUid(newUid) != null) newUid++; + } + + var account = new AccountData + { + Uid = newUid, + Username = username, + Password = password, + Phone = "123456", + Permissions = [.. ConfigManager.Config.ServerOption.DefaultPermissions + .Select(perm => Enum.TryParse(perm, out PermEnum result) ? result : (PermEnum?)null) + .Where(result => result.HasValue).Select(result => result!.Value)] + + }; + SetPassword(account, password); + + DatabaseHelper.CreateInstance(account); + } + + public static void DeleteAccount(int uid) + { + if (GetAccountByUid(uid) == null) return; + DatabaseHelper.DeleteAllInstance(uid); + } + + public static void SetPassword(AccountData account, string password) + { + if (password.Length > 0) + account.Password = Extensions.GetSha256Hash(password); + else + account.Password = ""; + } + + public static bool VerifyPassword(AccountData account, string password) + => account.Password == Extensions.GetSha256Hash(password); + + + #endregion + + #region Permission + + public static bool HasPerm(PermEnum[] perms, int uid) + { + if (uid == (int)ServerEnum.Console) return true; + var account = GetAccountByUid(uid); + if (account?.Permissions == null) return false; + if (account.Permissions.Contains(PermEnum.Admin)) return true; + + return perms.Any(account.Permissions.Contains); + } + + public static void AddPerm(PermEnum[] perms, int uid) + { + if (uid == (int)ServerEnum.Console) return; + var account = GetAccountByUid(uid); + if (account == null) return; + + account.Permissions ??= []; + foreach (var perm in perms) + { + if (!account.Permissions.Contains(perm)) + { + account.Permissions = [.. account.Permissions, perm]; + } + } + } + + public static void RemovePerm(PermEnum[] perms, int uid) + { + if (uid == (int)ServerEnum.Console) return; + var account = GetAccountByUid(uid); + if (account == null) return; + if (account.Permissions == null) return; + + foreach (var perm in perms) + { + if (account.Permissions.Contains(perm)) + { + account.Permissions = account.Permissions.Except([perm]).ToList(); + } + } + } + + public static void CleanPerm(int uid) + { + if (uid == (int)ServerEnum.Console) return; + var account = GetAccountByUid(uid); + if (account == null) return; + + account.Permissions = []; + } + + #endregion + + #region Token + + public string GenerateDispatchToken() + { + DispatchToken = Crypto.CreateSessionKey(Uid.ToString()); + DatabaseHelper.UpdateInstance(this); + return DispatchToken; + } + + public string GenerateComboToken() + { + ComboToken = Crypto.CreateSessionKey(Uid.ToString()); + DatabaseHelper.UpdateInstance(this); + return ComboToken; + } + + #endregion +} \ No newline at end of file diff --git a/Common/Database/BaseDatabaseDataHelper.cs b/Common/Database/BaseDatabaseDataHelper.cs new file mode 100644 index 0000000..36d53fa --- /dev/null +++ b/Common/Database/BaseDatabaseDataHelper.cs @@ -0,0 +1,8 @@ +using SqlSugar; + +namespace MikuSB.Database; + +public abstract class BaseDatabaseDataHelper +{ + [SugarColumn(IsPrimaryKey = true)] public int Uid { get; set; } +} \ No newline at end of file diff --git a/Common/Database/CustomSerializeService.cs b/Common/Database/CustomSerializeService.cs new file mode 100644 index 0000000..75d52f2 --- /dev/null +++ b/Common/Database/CustomSerializeService.cs @@ -0,0 +1,32 @@ +using Newtonsoft.Json; +using SqlSugar; + +namespace MikuSB.Database; + +public class CustomSerializeService : ISerializeService +{ + private readonly JsonSerializerSettings _jsonSettings; + + public CustomSerializeService() + { + _jsonSettings = new JsonSerializerSettings + { + DefaultValueHandling = DefaultValueHandling.Ignore // ignore default values + }; + } + + public string SerializeObject(object value) + { + return JsonConvert.SerializeObject(value, _jsonSettings); + } + + public T DeserializeObject(string value) + { + return JsonConvert.DeserializeObject(value)!; + } + + public string SugarSerializeObject(object value) + { + return JsonConvert.SerializeObject(value, _jsonSettings); + } +} \ No newline at end of file diff --git a/Common/Database/DatabaseHelper.cs b/Common/Database/DatabaseHelper.cs new file mode 100644 index 0000000..1a9dc5f --- /dev/null +++ b/Common/Database/DatabaseHelper.cs @@ -0,0 +1,305 @@ +using MikuSB.Database.Account; +using MikuSB.Internationalization; +using MikuSB.Util; +using SqlSugar; +using System.Collections.Concurrent; +using System.Globalization; + +namespace MikuSB.Database; + +public class DatabaseHelper +{ + public static Logger logger = new("Database"); + public static SqlSugarScope? sqlSugarScope; + public static readonly ConcurrentDictionary> UidInstanceMap = []; + public static readonly List ToSaveUidList = []; + public static long LastSaveTick = DateTime.UtcNow.Ticks; + public static Thread? SaveThread; + public static bool LoadAccount; + public static bool LoadAllData; + + public void Initialize() + { + logger.Info(I18NManager.Translate("Server.ServerInfo.LoadingItem", I18NManager.Translate("Word.Database"))); + var f = new FileInfo(ConfigManager.Config.Path.DatabasePath + "/" + ConfigManager.Config.GameServer.DatabaseName); + if (!f.Exists && f.Directory != null) f.Directory.Create(); + + sqlSugarScope = new SqlSugarScope(new ConnectionConfig + { + ConnectionString = $"Data Source={f.FullName};", + DbType = DbType.Sqlite, + IsAutoCloseConnection = true, + ConfigureExternalServices = new ConfigureExternalServices + { + SerializeService = new CustomSerializeService() + } + }); + + InitializeSqlite(); + + var baseType = typeof(BaseDatabaseDataHelper); + var assembly = typeof(BaseDatabaseDataHelper).Assembly; + var types = assembly.GetTypes().Where(t => t.IsSubclassOf(baseType)); + + var list = sqlSugarScope.Queryable().ToList(); + foreach (var inst in list) + { + if (!UidInstanceMap.TryGetValue(inst.Uid, out var value)) + { + value = []; + UidInstanceMap[inst.Uid] = value; + } + + value.Add(inst); // add to the map + } + + // start dispatch server + LoadAccount = true; + + var res = Parallel.ForEach(list, account => + { + Parallel.ForEach(types, t => + { + if (t == typeof(AccountData)) return; // skip the account data + + try + { + typeof(DatabaseHelper).GetMethod(nameof(InitializeTable))?.MakeGenericMethod(t) + .Invoke(null, [account.Uid]); + } + catch (Exception e) + { + logger.Error("Database initialization error: ", e); + } + + }); // cache the data + }); + + while (!res.IsCompleted) + { + } + + LastSaveTick = DateTime.UtcNow.Ticks; + + SaveThread = new Thread(() => + { + while (true) CalcSaveDatabase(); + }); + SaveThread.Start(); + + LoadAllData = true; + } + + public static void InitializeTable(int uid) where T : BaseDatabaseDataHelper, new() + { + var list = sqlSugarScope?.Queryable() + .Select(x => x) + .Select() + .Where(x => x.Uid == uid) + .ToList(); + + foreach (var inst in list!.Select(instance => (instance as BaseDatabaseDataHelper)!)) + { + if (!UidInstanceMap.TryGetValue(inst.Uid, out var value)) + { + value = []; + UidInstanceMap[inst.Uid] = value; + } + + value.Add(inst); // add to the map + } + } + + public static void InitializeSqlite() + { + var baseType = typeof(BaseDatabaseDataHelper); + var assembly = typeof(BaseDatabaseDataHelper).Assembly; + var types = assembly.GetTypes().Where(t => t.IsSubclassOf(baseType)); + foreach (var type in types) + typeof(DatabaseHelper).GetMethod("InitializeSqliteTable")?.MakeGenericMethod(type).Invoke(null, null); + } + + // DO NOT DEL ReSharper disable once UnusedMember.Global + public static void InitializeSqliteTable() where T : BaseDatabaseDataHelper, new() + { + try + { + sqlSugarScope?.CodeFirst.InitTables(); + } + catch + { + // ignored + } + } + + public static T? GetInstance(int uid, bool forceReload = false) where T : BaseDatabaseDataHelper, new() + { + try + { + if (!forceReload && UidInstanceMap.TryGetValue(uid, out var value)) + { + var instance = value.OfType().FirstOrDefault(); + if (instance != null) return instance; + } + var t = sqlSugarScope?.Queryable() + .Where(x => x.Uid == uid) + .ToList(); + + if (t is { Count: > 0 }) + { + var instance = t[0]; + + if (!UidInstanceMap.TryGetValue(uid, out var list)) + { + list = new List(); + UidInstanceMap[uid] = list; + } + else + { + list.RemoveAll(i => i is T); + } + + list.Add(instance); + return instance; + } + + return null; + } + catch (Exception e) + { + logger.Error("Unsupported type", e); + return null; + } + } + + public static T GetInstanceOrCreateNew(int uid) where T : BaseDatabaseDataHelper, new() + { + var instance = GetInstance(uid); + if (instance != null) return instance; + + instance = new T + { + Uid = uid + }; + CreateInstance(instance); + + return instance; + } + + public static List? GetAllInstance() where T : BaseDatabaseDataHelper, new() + { + try + { + return sqlSugarScope?.Queryable() + .Select(x => x) + .ToList(); + } + catch (Exception e) + { + logger.Error("Unsupported type", e); + return null; + } + } + + public static void UpdateInstance(T instance) where T : BaseDatabaseDataHelper, new() + { + sqlSugarScope?.Updateable(instance).ExecuteCommand(); + } + + public static void CreateInstance(T instance) where T : BaseDatabaseDataHelper, new() + { + sqlSugarScope?.Insertable(instance).ExecuteCommand(); + if (!UidInstanceMap.TryGetValue(instance.Uid, out var value)) + { + value = []; + UidInstanceMap[instance.Uid] = value; + } + value.Add(instance); + } + + public static void DeleteInstance(int key) where T : BaseDatabaseDataHelper, new() + { + try + { + sqlSugarScope?.Deleteable().Where(x => x.Uid == key).ExecuteCommand(); + } + catch (Exception e) + { + logger.Error("An error occurred while delete the database", e); + } + } + + public static void DeleteAllInstance(int key) + { + + var value = UidInstanceMap[key]; + var baseType = typeof(BaseDatabaseDataHelper); + var assembly = typeof(BaseDatabaseDataHelper).Assembly; + var types = assembly.GetTypes().Where(t => t.IsSubclassOf(baseType)); + foreach (var type in types) + { + var instance = value.Find(x => x.GetType() == type); + if (instance != null) + typeof(DatabaseHelper).GetMethod("DeleteInstance")?.MakeGenericMethod(type) + .Invoke(null, [key]); + } + + if (UidInstanceMap.TryRemove(key, out var instances)) + ToSaveUidList.RemoveAll(x => x == key); + } + + // Auto save per 5 min + public static void CalcSaveDatabase() + { + if (LastSaveTick + TimeSpan.TicksPerMinute * 5 > DateTime.UtcNow.Ticks) return; + SaveDatabase(); + } + + public static void SaveDatabase() + { + try + { + var prev = DateTime.Now; + var list = ToSaveUidList.ToList(); // copy the list to avoid the exception + foreach (var uid in list) + { + var value = UidInstanceMap[uid]; + var baseType = typeof(BaseDatabaseDataHelper); + var assembly = typeof(BaseDatabaseDataHelper).Assembly; + var types = assembly.GetTypes().Where(t => t.IsSubclassOf(baseType)); + foreach (var type in types) + { + var instance = value.Find(x => x.GetType() == type); + if (instance != null) + typeof(DatabaseHelper).GetMethod("SaveDatabaseType")?.MakeGenericMethod(type) + .Invoke(null, [instance]); + } + } + + var t = (DateTime.Now - prev).TotalSeconds; + logger.Info(I18NManager.Translate("Server.ServerInfo.SaveDatabase", + Math.Round(t, 2).ToString(CultureInfo.InvariantCulture))); + + ToSaveUidList.Clear(); + } + catch (Exception e) + { + logger.Error("An error occurred while saving the database", e); + } + + LastSaveTick = DateTime.UtcNow.Ticks; + } + + // DO NOT DEL ReSharper save database from cache + public static void SaveDatabaseType(T instance) where T : BaseDatabaseDataHelper, new() + { + try + { + sqlSugarScope?.Updateable(instance).ExecuteCommand(); + } + catch (Exception e) + { + logger.Error("An error occurred while saving the database", e); + } + } +} \ No newline at end of file diff --git a/Common/Database/Player/PlayerGameData.cs b/Common/Database/Player/PlayerGameData.cs new file mode 100644 index 0000000..c3c39e9 --- /dev/null +++ b/Common/Database/Player/PlayerGameData.cs @@ -0,0 +1,23 @@ +using MikuSB.Common.Util; +using MikuSB.Proto; +using MikuSB.Util.Extensions; +using SqlSugar; + +namespace MikuSB.Database.Player; + +[SugarTable("Player")] +public class PlayerGameData : BaseDatabaseDataHelper +{ + public string? Name { get; set; } = ""; + public string? Signature { get; set; } = "MikuPS"; + public uint Level { get; set; } = 1; + public int Exp { get; set; } = 0; + public long RegisterTime { get; set; } = Extensions.GetUnixSec(); + public long LastActiveTime { get; set; } + + public static PlayerGameData? GetPlayerByUid(long uid) + { + var result = DatabaseHelper.GetInstance((int)uid); + return result; + } +} \ No newline at end of file diff --git a/Common/Enums/Language/ProgramLanguageTypeEnum.cs b/Common/Enums/Language/ProgramLanguageTypeEnum.cs new file mode 100644 index 0000000..623675c --- /dev/null +++ b/Common/Enums/Language/ProgramLanguageTypeEnum.cs @@ -0,0 +1,9 @@ +namespace MikuSB.Enums.Language; + +public enum ProgramLanguageTypeEnum +{ + EN = 0, + CHS = 1, + CHT = 2, + JP = 3 +} \ No newline at end of file diff --git a/Common/Enums/Packet/PacketFraming.cs b/Common/Enums/Packet/PacketFraming.cs new file mode 100644 index 0000000..2215458 --- /dev/null +++ b/Common/Enums/Packet/PacketFraming.cs @@ -0,0 +1,9 @@ +namespace MikuSB.Enums.Packet; + +public enum PacketFraming +{ + FourByteLittleEndianLength, + TwoByteBigEndianLength, + Control, + Unknown +} \ No newline at end of file diff --git a/Common/Enums/Player/BanTypeEnum.cs b/Common/Enums/Player/BanTypeEnum.cs new file mode 100644 index 0000000..e0ddc54 --- /dev/null +++ b/Common/Enums/Player/BanTypeEnum.cs @@ -0,0 +1,13 @@ +namespace MikuSB.Enums.Player; + +public enum BanTypeEnum +{ + None = 0, + UseThirdPartySoftware = 1, + ThirdPartySoftware = 2, + AbnormalLogin = 4, + AbnormalAccount = 5, + ViolationTermsService = 6, + AccountRisk = 7, + Unknown = 8 +} \ No newline at end of file diff --git a/Common/Enums/Player/FriendEnum.cs b/Common/Enums/Player/FriendEnum.cs new file mode 100644 index 0000000..c4e5c79 --- /dev/null +++ b/Common/Enums/Player/FriendEnum.cs @@ -0,0 +1,7 @@ +namespace MikuSB.Enums.Player; + +public enum ServerEnum +{ + Console = 0, + Chat = 1 +} \ No newline at end of file diff --git a/Common/Enums/Player/PermEnum.cs b/Common/Enums/Player/PermEnum.cs new file mode 100644 index 0000000..fe48bbc --- /dev/null +++ b/Common/Enums/Player/PermEnum.cs @@ -0,0 +1,9 @@ +namespace MikuSB.Enums.Player; + +public enum PermEnum +{ + Trial = 0, + Support = 1, + Admin = 2, + Other = 10 +} \ No newline at end of file diff --git a/Common/Internationalization/I18nManager.cs b/Common/Internationalization/I18nManager.cs new file mode 100644 index 0000000..e6ab84c --- /dev/null +++ b/Common/Internationalization/I18nManager.cs @@ -0,0 +1,102 @@ +using MikuSB.Enums.Language; +using MikuSB.Internationalization.Message; +using MikuSB.Util; +using System.Reflection; + +namespace MikuSB.Internationalization; + +public static class I18NManager +{ + public static Logger Logger = new("I18nManager"); + + public static object Language { get; set; } = new LanguageEN(); + public static Dictionary> PluginLanguages { get; } = []; + + public static void LoadLanguage() + { + var languageStr = "MikuSB.Internationalization.Message.Language" + + ConfigManager.Config.ServerOption.Language; + var languageType = Type.GetType(languageStr); + if (languageType == null) + { + Logger.Warn("Language not found, fallback to EN"); + // fallback to English + languageType = Type.GetType("MikuSB.Internationalization.Message.LanguageEN")!; + } + + var language = Activator.CreateInstance(languageType) ?? throw new Exception("Language not found"); + Language = language; + + Logger.Info(Translate("Server.ServerInfo.LoadedItem", Translate("Word.Language"))); + } + + public static void LoadPluginLanguage(Dictionary> pluginAssemblies) + { + foreach (var (pluginName, types) in pluginAssemblies) + { + var languageType = types.FindAll(x => x.GetCustomAttribute() != null); + if (languageType.Count == 0) // no language to use + continue; + + PluginLanguages.Add(pluginName, []); + foreach (var type in languageType) + { + var attr = type.GetCustomAttribute(); + if (attr == null) continue; + + var language = Activator.CreateInstance(type); + if (language == null) continue; + PluginLanguages[pluginName].Add(attr.LanguageType, language); + } + } + } + + public static string Translate(string key, params string[] args) + { + var pluginLangs = PluginLanguages.Values; + var langs = (from pluginLang in pluginLangs + from o in pluginLang + where o.Key == Enum.Parse(ConfigManager.Config.ServerOption.Language) + select o.Value).ToList(); // get all plugin languages + langs.Add(Language); // add server language + + var result = langs.Select(lang => GetNestedPropertyValue(lang, key)).OfType().FirstOrDefault() ?? key; + + var index = 0; + + return args.Aggregate(result, (current, arg) => current.Replace("{" + index++ + "}", arg)); + } + + public static string TranslateAsCertainLang(string langStr, string key, params string[] args) + { + var languageStr = "MikuSB.Internationalization.Message.Language" + + langStr; + var languageType = Type.GetType(languageStr) ?? + Type.GetType("MikuSB.Internationalization.Message.LanguageEN")!; + var language = Activator.CreateInstance(languageType) ?? throw new Exception("Language not found"); + + List langs = [language]; + + var result = langs.Select(lang => GetNestedPropertyValue(lang, key)).OfType().FirstOrDefault() ?? key; + + var index = 0; + + return args.Aggregate(result, (current, arg) => current.Replace("{" + index++ + "}", arg)); + } + + public static string? GetNestedPropertyValue(object? obj, string propertyName) + { + foreach (var part in propertyName.Split('.')) + { + if (obj == null) return null; + + var type = obj.GetType(); + var property = type.GetProperty(part); + if (property == null) return null; + + obj = property.GetValue(obj, null); + } + + return (string?)obj; + } +} \ No newline at end of file diff --git a/Common/Internationalization/Message/LanguageCHS.cs b/Common/Internationalization/Message/LanguageCHS.cs new file mode 100644 index 0000000..10b9c6d --- /dev/null +++ b/Common/Internationalization/Message/LanguageCHS.cs @@ -0,0 +1,527 @@ +namespace MikuSB.Internationalization.Message; + +#region Root + +public class LanguageCHS +{ + public GameTextCHS Game { get; } = new(); + public ServerTextCHS Server { get; } = new(); + public WordTextCHS Word { get; } = new(); // a placeholder for the actual word text +} + +#endregion + +#region Layer 1 + +/// +/// path: Game +/// +public class GameTextCHS +{ + public CommandTextCHS Command { get; } = new(); +} + +/// +/// path: Server +/// +public class ServerTextCHS +{ + public WebTextCHS Web { get; } = new(); + public ServerInfoTextCHS ServerInfo { get; } = new(); +} + +/// +/// path: Word +/// +public class WordTextCHS +{ + public string Rank => "星魂"; + public string Avatar => "角色"; + public string Material => "材料"; + public string Pet => "宠物"; + public string Relic => "遗器"; + public string Equipment => "光锥"; + public string Talent => "行迹"; + public string Banner => "卡池"; + public string Activity => "活动"; + public string CdKey => "兑换码"; + public string VideoKey => "过场动画密钥"; + public string Buff => "祝福"; + public string Miracle => "奇物"; + public string Unlock => "奢侈品"; + public string TrainParty => "派对车厢"; + + // server info + public string Config => "配置文件"; + public string Language => "语言"; + public string Log => "日志"; + public string GameData => "游戏数据"; + public string Cache => "资源缓存"; + public string CustomData => "自定义数据"; + public string Database => "数据库"; + public string Command => "命令"; + public string SSL => "SSL"; + public string Ec2b => "Ec2b"; + public string SdkServer => "Web服务器"; + public string Handler => "包处理器"; + public string Dispatch => "全局分发"; + public string Game => "游戏"; + public string Handbook => "手册"; + public string NotFound => "未找到"; + public string Error => "错误"; + public string FloorInfo => "区域文件"; + public string FloorGroupInfo => "区域组文件"; + public string FloorMissingResult => "传送与世界生成"; + public string FloorGroupMissingResult => "传送、怪物战斗与世界生成"; + public string Mission => "任务"; + public string MissionInfo => "任务文件"; + public string SubMission => "子任务"; + public string SubMissionInfo => "子任务文件"; + public string MazeSkill => "角色秘技"; + public string MazeSkillInfo => "角色秘技文件"; + public string Dialogue => "模拟宇宙事件"; + public string DialogueInfo => "模拟宇宙事件文件"; + public string Performance => "剧情操作"; + public string PerformanceInfo => "剧情操作文件"; + public string RogueChestMap => "模拟宇宙地图"; + public string RogueChestMapInfo => "模拟宇宙地图文件"; + public string ChessRogueRoom => "模拟宇宙DLC"; + public string ChessRogueRoomInfo => "模拟宇宙DLC文件"; + public string SummonUnit => "秘技生成"; + public string SummonUnitInfo => "秘技生成文件"; + public string RogueTournRoom => "差分宇宙"; + public string RogueTournRoomInfo => "差分宇宙房间文件"; + public string TypesOfRogue => "类型的模拟宇宙"; + public string RogueMagicRoom => "不可知域"; + public string RogueMagicRoomInfo => "不可知域房间文件"; + public string RogueDiceSurface => "骰面效果"; + public string RogueDiceSurfaceInfo => "骰面效果文件"; + public string AdventureModifier => "AdventureModifier"; + public string AdventureModifierInfo => "AdventureModifier文件"; + public string RogueMapGen => "RogueMapGen文件"; + public string RogueMiracleGroup => "RogueMiracleGroup文件"; + public string RogueMiracleEffectGen => "RogueMiracleEffectGen文件"; + + public string DatabaseAccount => "数据库账号"; + public string Tutorial => "教程"; +} + +#endregion + +#region Layer 2 + +#region GameText + +/// +/// path: Game.Command +/// +public class CommandTextCHS +{ + public NoticeTextCHS Notice { get; } = new(); + + public GenderTextCHS Gender { get; } = new(); + public AvatarTextCHS Avatar { get; } = new(); + public AnnounceTextCHS Announce { get; } = new(); + public BanTextCHS Ban { get; } = new(); + public GiveTextCHS Give { get; } = new(); + public GiveAllTextCHS GiveAll { get; } = new(); + public LineupTextCHS Lineup { get; } = new(); + public HelpTextCHS Help { get; } = new(); + public KickTextCHS Kick { get; } = new(); + public MissionTextCHS Mission { get; } = new(); + public RelicTextCHS Relic { get; } = new(); + public ReloadTextCHS Reload { get; } = new(); + public RogueTextCHS Rogue { get; } = new(); + public SceneTextCHS Scene { get; } = new(); + public UnlockAllTextCHS UnlockAll { get; } = new(); + public MailTextCHS Mail { get; } = new(); + public RaidTextCHS Raid { get; } = new(); + public AccountTextCHS Account { get; } = new(); + public UnstuckTextCHS Unstuck { get; } = new(); + public SetlevelTextCHS Setlevel { get; } = new(); + public PermissionTextCHS Permission { get; } = new(); +} + +#endregion + +#region ServerText + +/// +/// path: Server.Web +/// +public class WebTextCHS +{ + public string Maintain => "服务器正在维修, 请稍后尝试。"; +} + +/// +/// path: Server.ServerInfo +/// +public class ServerInfoTextCHS +{ + public string Shutdown => "关闭中…"; + public string CancelKeyPressed => "已按下取消键 (Ctrl + C), 服务器即将关闭…"; + public string StartingServer => "正在启动 MikuSB"; + public string CurrentVersion => "当前服务端支持的版本: {0}"; + public string InvalidVersion => "当前为不受支持的游戏版本 {0}\n请更新游戏到 {1}"; + public string LoadingItem => "正在加载 {0}…"; + public string GeneratingItem => "正在生成 {0}…"; + public string WaitingItem => "正在等待进程 {0} 完成…"; + public string RegisterItem => "注册了 {0} 个 {1}。"; + public string FailedToLoadItem => "加载 {0} 失败。"; + public string NewClientSecretKey => "客户端密钥不存在, 正在生成新的客户端密钥。"; + public string FailedToInitializeItem => "初始化 {0} 失败。"; + public string FailedToReadItem => "读取 {0} 失败, 文件{1}"; + public string GeneratedItem => "已生成 {0}。"; + public string LoadedItem => "已加载 {0}。"; + public string LoadedItems => "已加载 {0} 个 {1}。"; + public string ServerRunning => "{0} 服务器正在监听 {1}"; + public string ServerStarted => "启动完成!用时 {0}s, 击败了99%的用户, 输入 ‘help’ 来获取命令帮助"; // 玩梗, 考虑英语版本将其本土化 + public string MissionEnabled => "任务系统已启用, 此功能仍在开发中, 且可能不会按预期工作, 如果遇见任何bug, 请汇报给开发者。"; + public string KeyStoreError => "SSL证书不存在, 已关闭SSL功能。"; + public string CacheLoadSkip => "已跳过缓存加载。"; + + public string ConfigMissing => "{0} 缺失, 请检查你的资源文件夹: {1}, {2} 可能不能使用。"; + public string UnloadedItems => "卸载了所有 {0}。"; + public string SaveDatabase => "已保存数据库, 用时 {0}s"; + public string WaitForAllDone => "现在还不可以进入游戏, 请等待所有项目加载完成后再试"; + + public string UnhandledException => "发生未经处理的异常: {0}"; +} + +#endregion + +#endregion + +#region Layer 3 + +#region CommandText + +/// +/// path: Game.Command.Notice +/// +public class NoticeTextCHS +{ + public string PlayerNotFound => "未找到玩家!"; + public string InvalidArguments => "无效的参数!"; + public string NoPermission => "你没有权限这么做!"; + public string CommandNotFound => "未找到命令! 输入 '/help' 来获取帮助"; + public string TargetOffline => "目标 {0}({1}) 离线了!清除当前目标"; + public string TargetFound => "找到目标 {0}({1}), 下一次命令将默认对其执行"; + public string TargetNotFound => "未找到目标 {0}!"; + public string InternalError => "在处理命令时发生了内部错误: {0}!"; +} + +/// +/// path: Game.Command.Gender +/// +public class GenderTextCHS +{ + public string Desc => "切换主角的性别"; + public string Usage => "用法: /gender [man/woman]"; + public string GenderNotSpecified => "性别不存在!"; + public string GenderChanged => "性别已更改!"; +} + +/// +/// path: Game.Command.UnlockAll +/// +public class UnlockAllTextCHS +{ + public string Desc => + "解锁所有在类别内的对象\n" + + "使用 /unlockall mission 以完成所有任务, 使用后会被踢出, 重新登录后可能会被教程卡住, 请谨慎使用\n" + + "使用 /unlockall tutorial 以解锁所有教程, 使用后会被踢出, 用于部分界面卡住无法行动的情况\n" + + "使用 /unlockall rogue 以解锁所有类型模拟宇宙, 使用后会被踢出, 建议与 /unlockall tutorial 搭配使用以获取更好效果"; + public string Usage => "用法: /unlockall [mission/tutorial/rogue]"; + public string UnlockedAll => "已解锁/完成所有{0}!"; +} + +/// +/// path: Game.Command.Avatar +/// +public class AvatarTextCHS +{ + public string Desc => "设定玩家已有角色的属性, -1为所有已拥有角色"; + public string Usage => + "用法: /avatar talent [角色ID/-1] [行迹等级]\n" + + "用法: /avatar rank [角色ID/-1] [星魂]\n" + + "用法: /avatar level [角色ID/-1] [角色等级]"; + public string InvalidLevel => "{0} 等级无效!"; + public string AllAvatarsLevelSet => "已将全部角色 {0} 等级设置为 {1}."; + public string AvatarLevelSet => "已将 {0} 角色 {1} 等级设置为 {2}."; + public string AvatarNotFound => "角色不存在!"; +} + +/// +/// path: Game.Command.Give +/// +public class GiveTextCHS +{ + public string Desc => "给予玩家物品"; + public string Usage => "用法: /give [物品ID] l[等级] x[数量] r[叠影]"; + public string ItemNotFound => "未找到物品!"; + public string GiveItem => "已给予 {0} {1} 个物品 {2}."; +} + +/// +/// path: Game.Command.GiveAll +/// +public class GiveAllTextCHS +{ + public string Desc => "给予玩家全部指定类型的物品"; + public string Usage => + "用法: /giveall avatar r[星魂] l[等级]\n" + + "用法: /giveall material x[数量]\n" + + "用法: /giveall equipment r[叠影] l[等级] x[数量]\n" + + "用法: /giveall relic x[数量]\n" + + "用法: /giveall unlock\n" + + "用法: /giveall train\n" + + "用法: /giveall path"; + public string GiveAllItems => "已给予所有 {0}, 各 {1} 个."; +} + +/// +/// path: Game.Command.Lineup +/// +public class LineupTextCHS +{ + public string Desc => "管理玩家的队伍信息"; + public string Usage => + "用法: /lineup mp\n" + + "用法: /lineup sp\n" + + "用法: /lineup heal"; + public string GainedMp => "成功恢复秘技点!"; + public string GainedSp => "成功恢复能量!"; + public string HealedAllAvatars => "成功治愈当前队伍中的所有角色!"; +} + +/// +/// path: Game.Command.Help +/// +public class HelpTextCHS +{ + public string Desc => "显示帮助信息"; + public string Usage => + "用法: /help\n" + + "用法: /help [命令]"; + public string Commands => "命令: "; + public string CommandPermission => "所需权限: "; + public string CommandAlias => "命令别名: "; +} + +/// +/// path: Game.Command.Kick +/// +public class KickTextCHS +{ + public string Desc => "踢出玩家"; + public string Usage => "用法: /kick"; + public string PlayerKicked => "玩家 {0} 已被踢出!"; +} + +/// +/// path: Game.Command.Mission +/// +public class MissionTextCHS +{ + public string Desc => + "管理玩家的任务\n" + + "使用 pass 完成当前正在进行的所有任务, 此命令易造成严重卡顿, 请尽量使用 /mission finish 替代\n" + + "使用 finish [子任务ID] 完成指定子任务, 请浏览 handbook 来获取子任务ID\n" + + "使用 finishmain [主任务ID] 完成指定主任务, 请浏览 handbook 来获取主任务ID\n" + + "使用 running [-all] 获取正在追踪的任务, 增加'-all'则显示所有正在进行的任务以及可能卡住的任务, 使用后可能会出现较长任务列表, 请注意甄别\n" + + "使用 reaccept [主任务ID] 可重新进行指定主任务, 请浏览 handbook 来获取主任务ID"; + public string Usage => + "用法: /mission pass\n" + + "用法: /mission finish [子任务ID]\n" + + "用法: /mission running [-all]\n" + + "用法: /mission reaccept [主任务ID]\n" + + "用法: /mission finishmain [主任务ID]"; + public string AllMissionsFinished => "所有任务已完成!"; + public string AllRunningMissionsFinished => "共 {0} 个进行中的任务已完成!"; + public string MissionFinished => "任务 {0} 已完成!"; + public string InvalidMissionId => "无效的任务ID!"; + public string NoRunningMissions => "没有正在进行的任务!"; + public string RunningMissions => "正在进行的任务: "; + public string PossibleStuckMissions => "可能卡住的任务: "; + public string MainMission => "主任务"; + public string MissionReAccepted => "重新接受任务 {0}."; +} + +/// +/// path: Game.Command.Relic +/// +public class RelicTextCHS +{ + public string Desc => "管理玩家的遗器, 等级限制: 1 ≤ 等级 ≤ 9999"; + public string Usage => "用法: /relic [遗器ID] [主词条ID] [ID1:等级] [ID2:等级] l[等级] x[数量]"; + public string RelicNotFound => "遗器不存在!"; + public string InvalidMainAffixId => "主词条ID无效!"; + public string InvalidSubAffixId => "副词条ID无效!"; + public string RelicGiven => "给予玩家 {0} {1} 个遗器 {2}."; +} + +/// +/// path: Game.Command.Reload +/// +public class ReloadTextCHS +{ + public string Desc => "重新加载指定的配置"; + public string Usage => "用法: /reload [banner/activity]"; + public string ConfigReloaded => "配置 {0} 已重新加载!"; +} + +/// +/// path: Game.Command.Rogue +/// +public class RogueTextCHS +{ + public string Desc => "管理模拟宇宙数据, -1意为所有已拥有祝福, buff获取祝福, enhance强化祝福"; + public string Usage => + "用法: /rogue money [宇宙碎片数量]\n" + + "用法: /rogue buff [祝福ID/-1]\n" + + "用法: /rogue miracle [奇物ID]\n" + + "用法: /rogue enhance [祝福ID/-1]\n" + + "用法: /rogue unstuck - 脱离事件"; + public string PlayerGainedMoney => "已获得 {0} 宇宙碎片."; + public string PlayerGainedAllItems => "已获得所有{0}."; + public string PlayerGainedItem => "已获得{0} {1}."; + public string PlayerEnhancedBuff => "已强化祝福 {0}."; + public string PlayerEnhancedAllBuffs => "已强化所有祝福."; + public string PlayerUnstuck => "已脱离事件."; + public string NotFoundItem => "未找到 {0}!"; + public string PlayerNotInRogue => "玩家不在模拟宇宙中!"; +} + +/// +/// path: Game.Command.Scene +/// +public class SceneTextCHS +{ + public string Desc => + "管理玩家场景\n" + + "使用 PlaneId 默认进入指定场景\n" + + "使用 group 来获取组, 使用 prop 来设置道具状态, 在 PropStateEnum 获取状态列表\n" + + "使用 unlockall 来解锁场景内所有道具(open状态), 可能导致游戏加载卡条, 使用 /scene reset 解决\n" + + "使用 reload 来重新加载当前场景, 并回到初始位置\n" + + "使用 reset 来重置指定场景所有道具状态"; + public string Usage => + "用法: /scene [PlaneId]\n" + + "用法: /scene cur\n" + + "用法: /scene reload\n" + + "用法: /scene group\n" + + "用法: /scene unlockall\n" + + "用法: /scene reset [PlaneId]" + + "用法: /scene prop [组ID] [道具ID] [状态]\n" + + "用法: /scene remove [实体ID]\n"; + + public string LoadedGroups => "已加载组: {0}."; + public string PropStateChanged => "道具: {0} 的状态已设置为 {1}."; + public string PropNotFound => "未找到道具!"; + public string EntityRemoved => "实体 {0} 已被移除."; + public string EntityNotFound => "未找到实体!"; + public string AllPropsUnlocked => "所有道具已解锁!"; + public string SceneChanged => "已进入场景 {0}."; + public string SceneReloaded => "场景已重新加载!"; + public string SceneReset => "已重置场景 {0} 中所有道具状态!"; + public string CurrentScene => "当前场景 EntryId: {0}, PlaneId: {1}, FloorId: {2}."; +} + +/// +/// path: Game.Command.Mail +/// +public class MailTextCHS +{ + public string Desc => "发送邮件"; + public string Usage => "用法: /mail [发送名称] [标题] [内容] [ID1:数量,ID2:数量]"; + public string MailSent => "邮件已发送!"; +} + +/// +/// path: Game.Command.Raid +/// +public class RaidTextCHS +{ + public string Desc => "管理玩家的任务临时场景"; + public string Usage => "用法: /raid leave"; + public string Leaved => "已离开临时场景!"; +} + +/// +/// path: Game.Command.Account +/// +public class AccountTextCHS +{ + public string Desc => "管理数据库账号"; + public string Usage => + "用法: /account create [用户名] [UID] [密码]\n" + + "用法: /account delete [UID]"; + public string InvalidUid => "UID无效!"; + public string InvalidAccount => "账号 {0} 无效!"; + public string CreateSuccess => "账号 {0} 创建成功!"; + public string DeleteSuccess => "账号 {0} 删除成功!"; +} + +/// +/// path: Game.Command.Announce +/// +public class AnnounceTextCHS +{ + public string Desc => "发送弹窗公告"; + public string Usage => "用法: /announce [Text] [Color]"; + public string SendSuccess => "发送成功!"; +} + +/// +/// path: Game.Command.Ban +/// +public class BanTextCHS +{ + public string Desc => "封禁或解封用户"; + public string Usage => "用法: /ban [add/delete]"; + public string BanSuccess => "账号已封禁!"; + public string UnBanSuccess => "账号已解封!"; +} + +/// +/// path: Game.Command.Unstuck +/// +public class UnstuckTextCHS +{ + public string Desc => "将玩家传送回默认场景"; + public string Usage => "用法: /unstuck [UID]"; + public string UnstuckSuccess => "已成功将该玩家传送回默认场景."; + public string UidNotExist => "该UID不存在!"; + public string PlayerIsOnline => "该玩家目前在线上!"; +} + +/// +/// path: Game.Command.Setlevel +/// +public class SetlevelTextCHS +{ + public string Desc => "设定玩家等级"; + public string Usage => "用法: /setlevel [等级]"; + public string SetlevelSuccess => "等级设定成功!"; +} + +/// +/// path: Game.Command.Permission +/// +public class PermissionTextCHS +{ + public string Desc => "管理玩家权限"; + public string Usage => + "用法: /permission add [权限]\n" + + "用法: /permission remove [权限]\n" + + "用法: /permission clean [权限]"; + public string InvalidPerm => "权限 {0} 不存在!"; + public string Added => "已添加权限 {0} 到玩家 {1}!"; + public string Removed => "已移除玩家 {0} 的权限 {1}!"; + public string Cleaned => "已清除玩家 {0} 的所有权限!"; +} + +#endregion + +#endregion \ No newline at end of file diff --git a/Common/Internationalization/Message/LanguageCHT.cs b/Common/Internationalization/Message/LanguageCHT.cs new file mode 100644 index 0000000..18617f9 --- /dev/null +++ b/Common/Internationalization/Message/LanguageCHT.cs @@ -0,0 +1,530 @@ +namespace MikuSB.Internationalization.Message; + +#region Root + +public class LanguageCHT +{ + public GameTextCHT Game { get; } = new(); + public ServerTextCHT Server { get; } = new(); + public WordTextCHT Word { get; } = new(); // a placeholder for the actual word text +} + +#endregion + +#region Layer 1 + +/// +/// path: Game +/// +public class GameTextCHT +{ + public CommandTextCHT Command { get; } = new(); +} + +/// +/// path: Server +/// +public class ServerTextCHT +{ + public WebTextCHT Web { get; } = new(); + public ServerInfoTextCHT ServerInfo { get; } = new(); +} + +/// +/// path: Word +/// +public class WordTextCHT +{ + public string Rank => "星魂"; + public string Avatar => "角色"; + public string Material => "材料"; + public string Pet => "寵物"; + public string Relic => "遺器"; + public string Equipment => "光錐"; + public string Talent => "行跡"; + public string Banner => "卡池"; + public string Activity => "活動"; + public string CdKey => "兌換碼"; + public string VideoKey => "過場動畫金鑰"; + public string Buff => "祝福"; + public string Miracle => "奇物"; + public string Unlock => "奢侈品"; + public string TrainParty => "派對車廂"; + + // server info + public string Config => "配置文件"; + public string Language => "語言"; + public string Log => "日誌"; + public string GameData => "遊戲數據"; + public string Cache => "資源緩存"; + public string CustomData => "自定義數據"; + public string Database => "數據庫"; + public string Command => "命令"; + public string SSL => "SSL"; + public string Ec2b => "Ec2b"; + public string SdkServer => "Web服務器"; + public string Handler => "包處理器"; + public string Dispatch => "全局分發"; + public string Game => "遊戲"; + public string Handbook => "手冊"; + public string NotFound => "未找到"; + public string Error => "錯誤"; + public string FloorInfo => "區域文件"; + public string FloorGroupInfo => "區域組文件"; + public string FloorMissingResult => "傳送與世界生成"; + public string FloorGroupMissingResult => "傳送、怪物戰鬥與世界生成"; + public string Mission => "任務"; + public string MissionInfo => "任務文件"; + public string SubMission => "子任務"; + public string SubMissionInfo => "子任務文件"; + public string MazeSkill => "角色秘技"; + public string MazeSkillInfo => "角色秘技文件"; + public string Dialogue => "模擬宇宙事件"; + public string DialogueInfo => "模擬宇宙事件文件"; + public string Performance => "劇情操作"; + public string PerformanceInfo => "劇情操作文件"; + public string RogueChestMap => "模擬宇宙地圖"; + public string RogueChestMapInfo => "模擬宇宙地圖文件"; + public string ChessRogueRoom => "模擬宇宙DLC"; + public string ChessRogueRoomInfo => "模擬宇宙DLC文件"; + public string SummonUnit => "秘技生成"; + public string SummonUnitInfo => "秘技生成文件"; + public string RogueTournRoom => "差分宇宙"; + public string RogueTournRoomInfo => "差分宇宙房間文件"; + public string TypesOfRogue => "類型的模擬宇宙"; + public string RogueMagicRoom => "不可知域"; + public string RogueMagicRoomInfo => "不可知域房間文件"; + public string RogueDiceSurface => "骰面效果"; + public string RogueDiceSurfaceInfo => "骰面效果文件"; + public string AdventureModifier => "AdventureModifier"; + public string AdventureModifierInfo => "AdventureModifier文件"; + public string RogueMapGen => "RogueMapGen文件"; + public string RogueMiracleGroup => "RogueMiracleGroup文件"; + public string RogueMiracleEffectGen => "RogueMiracleEffectGen文件"; + + public string DatabaseAccount => "數據庫賬號"; + public string Tutorial => "教程"; +} + +#endregion + +#region Layer 2 + +#region GameText + +/// +/// path: Game.Command +/// +public class CommandTextCHT +{ + public NoticeTextCHT Notice { get; } = new(); + + public GenderTextCHT Gender { get; } = new(); + public AvatarTextCHT Avatar { get; } = new(); + public AnnounceTextCHT Announce { get; } = new(); + public BanTextCHT Ban { get; } = new(); + public GiveTextCHT Give { get; } = new(); + public GiveAllTextCHT GiveAll { get; } = new(); + public LineupTextCHT Lineup { get; } = new(); + public HelpTextCHT Help { get; } = new(); + public KickTextCHT Kick { get; } = new(); + public MissionTextCHT Mission { get; } = new(); + public RelicTextCHT Relic { get; } = new(); + public ReloadTextCHT Reload { get; } = new(); + public RogueTextCHT Rogue { get; } = new(); + public SceneTextCHT Scene { get; } = new(); + public UnlockAllTextCHT UnlockAll { get; } = new(); + public MailTextCHT Mail { get; } = new(); + public RaidTextCHT Raid { get; } = new(); + public AccountTextCHT Account { get; } = new(); + public UnstuckTextCHT Unstuck { get; } = new(); + public SetlevelTextCHT Setlevel { get; } = new(); + public PermissionTextCHT Permission { get; } = new(); +} + +#endregion + +#region ServerText + +/// +/// path: Server.Web +/// +public class WebTextCHT +{ + public string Maintain => "服務器正在維修, 請稍後嘗試。"; +} + +/// +/// path: Server.ServerInfo +/// +public class ServerInfoTextCHT +{ + public string Shutdown => "關閉中…"; + public string CancelKeyPressed => "已按下取消鍵 (Ctrl + C), 服務器即將關閉…"; + public string StartingServer => "正在啟動 MikuSB"; + public string CurrentVersion => "當前服務端支援的版本: {0}"; + public string InvalidVersion => "目前為不受支援的遊戲版本 {0}\n請更新遊戲到 {1}"; + public string LoadingItem => "正在加載 {0}…"; + public string GeneratingItem => "正在生成 {0}…"; + public string WaitingItem => "正在等待進程 {0} 完成…"; + public string RegisterItem => "註冊了 {0} 個 {1}。"; + public string FailedToLoadItem => "加載 {0} 失敗。"; + public string NewClientSecretKey => "客戶端密鑰不存在, 正在生成新的客戶端密鑰。"; + public string FailedToInitializeItem => "初始化 {0} 失敗。"; + public string FailedToReadItem => "讀取 {0} 失敗, 文件{1}"; + public string GeneratedItem => "已生成 {0}。"; + public string LoadedItem => "已加載 {0}。"; + public string LoadedItems => "已加載 {0} 個 {1}。"; + public string ServerRunning => "{0} 服務器正在監聽 {1}"; + public string ServerStarted => "啟動完成!用時 {0}s, 擊敗了99%的用戶, 輸入 『help』 來獲取命令幫助"; // 玩梗, 考慮英語版本將其本土化 + public string MissionEnabled => "任務系統已啟用, 此功能仍在開發中, 且可能不會按預期工作, 如果遇見任何bug, 請匯報給開發者。"; + public string KeyStoreError => "SSL證書不存在, 已關閉SSL功能。"; + public string CacheLoadSkip => "已跳過緩存加載。"; + + public string ConfigMissing => "{0} 缺失, 請檢查你的資源文件夾: {1}, {2} 可能不能使用。"; + public string UnloadedItems => "卸載了所有 {0}。"; + public string SaveDatabase => "已保存數據庫, 用時 {0}s"; + public string WaitForAllDone => "現在還不可以進入遊戲, 請等待所有項目加載完成後再試"; + + public string UnhandledException => "發生未經處理的異常: {0}"; +} + +#endregion + +#endregion + +#region Layer 3 + +#region CommandText + +/// +/// path: Game.Command.Notice +/// +public class NoticeTextCHT +{ + public string PlayerNotFound => "未找到玩家!"; + public string InvalidArguments => "無效的參數!"; + public string NoPermission => "你沒有權限這麽做!"; + public string CommandNotFound => "未找到命令! 輸入 '/help' 來獲取幫助"; + public string TargetOffline => "目標 {0}({1}) 離線了!清除當前目標"; + public string TargetFound => "找到目標 {0}({1}), 下一次命令將默認對其執行"; + public string TargetNotFound => "未找到目標 {0}!"; + public string InternalError => "在處理命令時發生了內部錯誤: {0}!"; +} + +/// +/// path: Game.Command.Gender +/// +public class GenderTextCHT +{ + public string Desc => "切換主角的性別"; + public string Usage => "用法: /gender [man/woman]"; + public string GenderNotSpecified => "性別不存在!"; + public string GenderChanged => "性別已更改!"; +} + +/// +/// path: Game.Command.UnlockAll +/// +public class UnlockAllTextCHT +{ + public string Desc => + "解鎖所有在類別內的對象\n" + + "使用 /unlockall mission 以完成所有任務, 使用後會被踢出, 重新登錄後可能會被教程卡住, 請謹慎使用\n" + + "使用 /unlockall tutorial 以解鎖所有教程, 使用後會被踢出, 用於部分界面卡住無法行動的情況\n" + + "使用 /unlockall rogue 以解鎖所有類型模擬宇宙, 使用後會被踢出, 建議與 /unlockall tutorial 搭配使用以獲取更好效果"; + + public string Usage => "用法: /unlockall [mission/tutorial/rogue]"; + public string UnlockedAll => "已解鎖/完成所有{0}!"; +} + +/// +/// path: Game.Command.Avatar +/// +public class AvatarTextCHT +{ + public string Desc => "設定玩家已有角色的屬性, -1意為所有已擁有角色"; + + public string Usage => + "用法: /avatar talent [角色ID/-1] [行跡等級]\n" + + "用法: /avatar rank [角色ID/-1] [星魂]\n" + + "用法: /avatar level [角色ID/-1] [角色等級]"; + public string InvalidLevel => "{0}等級無效!"; + public string AllAvatarsLevelSet => "已將全部角色 {0}等級設置為 {1}."; + public string AvatarLevelSet => "已將 {0} 角色 {1}等級設置為 {2}."; + public string AvatarNotFound => "角色不存在!"; +} + +/// +/// path: Game.Command.Give +/// +public class GiveTextCHT +{ + public string Desc => "給予玩家物品"; + public string Usage => "用法: /give [物品ID] l[等級] x[數量] r[疊影]"; + public string ItemNotFound => "未找到物品!"; + public string GiveItem => "給予 @{0} {1} 個物品 {2}."; +} + +/// +/// path: Game.Command.GiveAll +/// +public class GiveAllTextCHT +{ + public string Desc => "給予玩家全部指定類型的物品"; + public string Usage => + "用法: /giveall avatar r[星魂] l[等級]\n" + + "用法: /giveall material x[數量]\n" + + "用法: /giveall equipment r[叠影] l[等級] x[數量]\n" + + "用法: /giveall relic l<等級> x<數量>\n" + + "用法: /giveall unlock\n" + + "用法: /giveall train\n" + + "用法: /giveall path"; + public string GiveAllItems => "已給予所有 {0}, 各 {1} 個."; +} + +/// +/// path: Game.Command.Lineup +/// +public class LineupTextCHT +{ + public string Desc => "管理玩家的隊伍資訊"; + public string Usage => + "用法: /lineup mp\n" + + "用法: /lineup sp\n" + + "用法: /lineup heal"; + public string GainedMp => "成功恢復秘技點!"; + public string GainedSp => "成功恢復能量!"; + public string HealedAllAvatars => "成功治愈當前隊伍中的所有角色!"; +} + +/// +/// path: Game.Command.Help +/// +public class HelpTextCHT +{ + public string Desc => "顯示幫助信息"; + public string Usage => + "用法: /help\n" + + "用法: /help [命令]"; + public string Commands => "命令: "; + public string CommandPermission => "所需權限: "; + public string CommandAlias => "命令別名: "; +} + +/// +/// path: Game.Command.Kick +/// +public class KickTextCHT +{ + public string Desc => "踢出玩家"; + public string Usage => "用法: /kick"; + public string PlayerKicked => "玩家 {0} 已被踢出!"; +} + +/// +/// path: Game.Command.Mission +/// +public class MissionTextCHT +{ + public string Desc => + "管理玩家的任務\n" + + "使用 pass 完成當前正在進行的所有任務, 此命令易造成嚴重卡頓, 請盡量使用 /mission finish 替代\n" + + "使用 finish [子任務ID] 完成指定子任務, 請流覽 handbook 來獲取子任務ID\n" + + "使用 finishmain [主任務ID] 完成指定主任務, 請流覽 handbook 來獲取主任務ID\n" + + "使用 running [-all] 獲取正在追蹤的任務, 增加'-all'則顯示所有正在進行的任務以及可能卡住的任務, 使用後可能會出現較長任務清單, 請注意甄別\n" + + "使用 reaccept [主任務ID] 可重新進行指定主任務, 請流覽 handbook 來獲取主任務ID"; + + public string Usage => + "用法: /mission pass\n" + + "用法: /mission finish [子任務]\n" + + "用法: /mission running [-all]\n" + + "用法: /mission reaccept [主任務ID]\n" + + "用法: /mission finishmain [主任務ID]"; + public string AllMissionsFinished => "所有任務已完成!"; + public string AllRunningMissionsFinished => "共 {0} 個進行中的任務已完成!"; + public string MissionFinished => "任務 {0} 已完成!"; + public string InvalidMissionId => "無效的任務ID!"; + public string NoRunningMissions => "沒有正在進行的任務!"; + public string RunningMissions => "正在進行的任務: "; + public string PossibleStuckMissions => "可能卡住的任務: "; + public string MainMission => "主任務"; + public string MissionReAccepted => "重新接受任務 {0}."; +} + +/// +/// path: Game.Command.Relic +/// +public class RelicTextCHT +{ + public string Desc => "管理玩家的遺器, 等級限製: 1 ≤ 等級 ≤ 9999"; + public string Usage => "用法: /relic [遺器ID] [主詞條ID] [ID1:等級] [ID2:等級] l[等級] x[數量]"; + public string RelicNotFound => "遺器不存在!"; + public string InvalidMainAffixId => "主詞條ID無效!"; + public string InvalidSubAffixId => "副詞條ID無效!"; + public string RelicGiven => "給予玩家 @{0} {1} 個遺器 {2}, 主詞條 {3}."; +} + +/// +/// path: Game.Command.Reload +/// +public class ReloadTextCHT +{ + public string Desc => "重新加載指定的配置"; + public string Usage => "用法: /reload [banner/activity]"; + public string ConfigReloaded => "配置 {0} 已重新加載!"; +} + +/// +/// path: Game.Command.Rogue +/// +public class RogueTextCHT +{ + public string Desc => "管理模擬宇宙數據, -1意為所有已擁有祝福, buff來獲取祝福, enhance強化祝福"; + + public string Usage => + "用法: /rogue money [宇宙碎片數量]\n" + + "用法: /rogue buff [祝福ID/-1]\n" + + "用法: /rogue miracle [奇物ID]\n" + + "用法: /rogue enhance [祝福ID/-1]\n" + + "用法: /rogue unstuck - 脫離事件"; + public string PlayerGainedMoney => "已獲得 {0} 宇宙碎片."; + public string PlayerGainedAllItems => "已獲得所有{0}."; + public string PlayerGainedItem => "已獲得{0} {1}."; + public string PlayerEnhancedBuff => "已強化祝福 {0}."; + public string PlayerEnhancedAllBuffs => "已強化所有祝福."; + public string PlayerUnstuck => "已脫離事件."; + public string NotFoundItem => "未找到 {0}!"; + public string PlayerNotInRogue => "玩家不在模擬宇宙中!"; +} + +/// +/// path: Game.Command.Scene +/// +public class SceneTextCHT +{ + public string Desc => + "管理玩家場景\n" + + "使用 PlaneId 預設進入指定場景\n" + + "使用 group 來獲取組, 使用 prop 來設置道具狀態, 在 PropStateEnum 獲取狀態列表\n" + + "使用 unlockall 來解鎖場景內所有道具(open狀態), 可能導致遊戲加載卡條, 使用 /scene reset 解決\n" + + "使用 reload 來重新加載當前場景, 並回到初始位置\n" + + "使用 reset 來重置指定場景所有道具狀態"; + public string Usage => + "用法: /scene [entryId]\n" + + "用法: /scene cur\n" + + "用法: /scene reload\n" + + "用法: /scene group\n" + + "用法: /scene unlockall\n" + + "用法: /scene reset [floorId]" + + "用法: /scene prop [組ID] [道具ID] [狀態]\n" + + "用法: /scene remove [實體ID]\n"; + public string LoadedGroups => "已加載組: {0}."; + public string PropStateChanged => "道具: {0} 的狀態已設置為 {1}."; + public string PropNotFound => "未找到道具!"; + public string EntityRemoved => "實體 {0} 已被移除."; + public string EntityNotFound => "未找到實體!"; + public string AllPropsUnlocked => "所有道具已解鎖!"; + public string SceneChanged => "已進入場景 {0}."; + public string SceneReloaded => "場景已重新加載!"; + public string SceneReset => "已重置場景 {0} 中所有道具狀態!"; + public string CurrentScene => "當前場景Entry Id: {0}, Plane Id: {1}, Floor Id: {2}."; +} + +/// +/// path: Game.Command.Mail +/// +public class MailTextCHT +{ + public string Desc => "發送郵件"; + public string Usage => "用法: /mail [發送名稱] [標題] [內容] [ID1:數量,ID2:數量]"; + public string MailSent => "郵件已發送!"; +} + +/// +/// path: Game.Command.Raid +/// +public class RaidTextCHT +{ + public string Desc => "管理玩家的任務臨時場景"; + public string Usage => "用法: /raid leave"; + public string Leaved => "已離開臨時場景!"; +} + +/// +/// path: Game.Command.Account +/// +public class AccountTextCHT +{ + public string Desc => "管理資料庫帳號"; + public string Usage => + "用法: /account create [用户名] [UID] [密碼]\n" + + "用法: /account delete [UID]"; + public string InvalidUid => "UID無效!"; + public string InvalidAccount => "帳號 {0} 無效!"; + public string CreateSuccess => "賬號 {0} 創建成功!"; + public string DeleteSuccess => "賬號 {0} 刪除成功!"; +} + +/// +/// path: Game.Command.Announce +/// +public class AnnounceTextCHT +{ + public string Desc => "發送彈窗公告"; + public string Usage => "用法: /announce [Text] [Color]"; + public string SendSuccess => "發送成功!"; +} + +/// +/// path: Game.Command.Ban +/// +public class BanTextCHT +{ + public string Desc => "封禁或解封用户"; + public string Usage => "用法: /ban [add/delete]"; + public string BanSuccess => "帳號已封禁!"; + public string UnBanSuccess => "帳號已解封!"; +} + +/// +/// path: Game.Command.Unstuck +/// +public class UnstuckTextCHT +{ + public string Desc => "將玩家傳送回默認場景"; + public string Usage => "用法: /unstuck [UID]"; + public string UnstuckSuccess => "已成功將該玩家傳送回默認場景"; + public string UidNotExist => "該UID不存在!"; + public string PlayerIsOnline => "該玩家目前在線上!"; +} + +/// +/// path: Game.Command.Setlevel +/// +public class SetlevelTextCHT +{ + public string Desc => "設定玩家等級"; + public string Usage => "用法: /setlevel [等級]"; + public string SetlevelSuccess => "等級設定成功!"; +} + +/// +/// path: Game.Command.Permission +/// +public class PermissionTextCHT +{ + public string Desc => "管理玩家權限"; + public string Usage => + "用法: /permission add [權限]\n" + + "用法: /permission remove [權限]\n" + + "用法: /permission clean [權限]"; + public string InvalidPerm => "權限 {0} 不存在!"; + public string Added => "已添加權限 {0} 到玩家 {1}!"; + public string Removed => "已移除玩家 {0} 的權限 {1}!"; + public string Cleaned => "已清除玩家 {0} 的所有權限!"; +} + +#endregion + +#endregion \ No newline at end of file diff --git a/Common/Internationalization/Message/LanguageEN.cs b/Common/Internationalization/Message/LanguageEN.cs new file mode 100644 index 0000000..38087f6 --- /dev/null +++ b/Common/Internationalization/Message/LanguageEN.cs @@ -0,0 +1,277 @@ +namespace MikuSB.Internationalization.Message; + +#region Root + +public class LanguageEN +{ + public GameTextEN Game { get; } = new(); + public ServerTextEN Server { get; } = new(); + public WordTextEN Word { get; } = new(); // a placeholder for the actual word text +} + +#endregion + +#region Layer 1 + +/// +/// path: Game +/// +public class GameTextEN +{ + public CommandTextEN Command { get; } = new(); +} + +/// +/// path: Server +/// +public class ServerTextEN +{ + public WebTextEN Web { get; } = new(); + public ServerInfoTextEN ServerInfo { get; } = new(); +} + +/// +/// path: Word +/// +public class WordTextEN +{ + public string Star => "Star"; + public string Valk => "Valkyrie"; + public string Material => "Material"; + public string Stigmata => "Stigmata"; + public string Weapon => "Weapon"; + public string Banner => "Gacha"; + public string Activity => "Activity"; + public string Elf => "Elf"; + public string Dress => "Outfit"; + public string Bracket => "Bracket"; + public string Disturbance => "Disturbance"; + public string Site => "Site"; + + // server info + public string Config => "Config File"; + public string Language => "Language"; + public string Log => "Log"; + public string GameData => "Game Data"; + public string Cache => "Resource Cache"; + public string CustomData => "Custom Data"; + public string Database => "Database"; + public string Command => "Command"; + public string SdkServer => "Web Server"; + public string Handler => "Packet Handler"; + public string Dispatch => "Global Dispatch"; + public string Game => "Game"; + public string Handbook => "Handbook"; + public string NotFound => "Not Found"; + public string Error => "Error"; + public string DatabaseAccount => "Database Account"; + public string Tutorial => "Tutorial"; +} + +#endregion + +#region Layer 2 + +#region GameText + +/// +/// path: Game.Command +/// +public class CommandTextEN +{ + public NoticeTextEN Notice { get; } = new(); + public HelpTextEN Help { get; } = new(); + public ValkTextEN Valk { get; } = new(); + public GiveAllTextEN GiveAll { get; } = new(); + public ElfTextEN Elf { get; } = new(); + public AbyssTextEN Abyss { get; } = new(); + public EndlessTextEN Endless { get; } = new(); +} + +#endregion + +#region ServerTextEN + +/// +/// path: Server.Web +/// +public class WebTextEN +{ + public string Maintain => "The server is undergoing maintenance, please try again later."; +} + +/// +/// path: Server.ServerInfo +/// +public class ServerInfoTextEN +{ + public string Shutdown => "Shutting down..."; + public string CancelKeyPressed => "Cancel key pressed (Ctrl + C), server shutting down..."; + public string StartingServer => "Starting MikuSB"; + public string CurrentVersion => "Server supported versions: {0}"; + public string InvalidVersion => "Unsupported game version {0}\nPlease update game to {1}"; + public string LoadingItem => "Loading {0}..."; + public string GeneratingItem => "Building {0}..."; + public string WaitingItem => "Waiting for process {0} to complete..."; + public string RegisterItem => "Registered {0} {1}(s)."; + public string FailedToLoadItem => "Failed to load {0}."; + public string NewClientSecretKey => "Client Secret Key does not exist and a new Client Secret Key is being generated."; + public string FailedToInitializeItem => "Failed to initialize {0}."; + public string FailedToReadItem => "Failed to read {0}, file {1}"; + public string GeneratedItem => "Generated {0}."; + public string LoadedItem => "Loaded {0}."; + public string LoadedItems => "Loaded {0} {1}(s)."; + public string ServerRunning => "{0} server listening on {1}"; + + public string ServerStarted => + "Startup complete! Took {0}s, better than 99% of users. Type 'help' for command help"; // This is a meme, consider localpermissiong in English + + public string MissionEnabled => + "Mission system enabled. This feature is still in development and may not work as expected. Please report any bugs to the developers."; + public string KeyStoreError => "The SSL certificate does not exist, SSL functionality has been disabled."; + public string CacheLoadSkip => "Skipped cache loading."; + + public string ConfigMissing => "{0} is missing. Please check your resource folder: {1}, {2} may not be available."; + public string UnloadedItems => "Unloaded all {0}."; + public string SaveDatabase => "Database saved in {0}s"; + + public string WaitForAllDone => + "You cannot enter the game yet. Please wait for all items to load before trying again"; + + public string UnhandledException => "An unhandled exception occurred: {0}"; +} + +#endregion + +#endregion + +#region Layer 3 + +#region CommandText + +/// +/// path: Game.Command.Notice +/// +public class NoticeTextEN +{ + public string PlayerNotFound => "Player not found!"; + public string InvalidArguments => "Invalid arguments!"; + public string NoPermission => "You don't have permission!"; + public string CommandNotFound => "Command not found! Type '/help' for assistance"; + public string TargetOffline => "Target {0}({1}) is offline! Clearing current target"; + public string TargetFound => "Target {0}({1}) found. Next command will default to this target"; + public string TargetNotFound => "Target {0} not found!"; + public string InternalError => "Internal error occurred while processing command!"; +} + +/// +/// path: Game.Command.Help +/// +public class HelpTextEN +{ + public string Desc => "Show help information"; + public string Usage => + "Usage: /help\n" + + "Usage: /help [cmd]"; + public string Commands => "Commands: "; + public string CommandUsage => "Usage: "; + public string CommandPermission => "Level Permission For Access: "; + public string CommandAlias => "Command Alias:"; +} + +/// +/// path: Game.Command.Valk +/// +public class ValkTextEN +{ + public string Desc => "Set attributes for owned characters\n" + + "Note: -1 means all owned characters\n"; + + public string Usage => + "Usage: /valk add [ValkID/-1] l s\n\n" + + "Usage: /valk level [ValkID/-1] [Level]\n\n" + + "Usage: /valk star [ValkID/-1] [Star]\n\n" + + "Usage: /valk skill [ValkID/-1] for max skill level"; + + public string ValkNotFound => "Character does not exist!"; + public string ValkAddedAll => "Granted all characters to player!"; + public string ValkAdded => "Granted character {0} to player!"; + public string ValkSetLevelAll => "Set all characters to level {0}!"; + public string ValkSetLevel => "Set character {0} to level {1}!"; + public string ValkSetStarAll => "Set all characters' Resonance to {0}!"; + public string ValkSetStar => "Set character {0}'s Resonance to {1}!"; + public string ValkSetSkillLevelAll => "Set all characters' skill levels to max!"; + public string ValkSetSkillLevel => "Set character {0}'s skill levels to max!"; +} + +/// +/// path: Game.Command.GiveAll +/// +public class GiveAllTextEN +{ + public string Desc => "Give all items of specified type\n" + + "weapon,stigmata"; + + public string Usage => + "Usage: /giveall weapon\n\n" + + "Usage: /giveall stigmata\n\n" + + "Usage: /giveall material\n\n" + + "Usage: /giveall dress\n"; + + public string GiveAllItems => "Granted all {0}"; +} + +/// +/// path: Game.Command.Elf +/// +public class ElfTextEN +{ + public string Desc => "Set attributes for owned elfs\n" + + "Note: -1 means all owned elfs\n"; + + public string Usage => + "Usage: /elf add [ElfID/-1] l s\n\n"; + + public string ElfNotFound => "Elf does not exist!"; + public string ElfAddedAll => "Granted all Elfs to player!"; + public string ElfAdded => "Granted Elf {0} to player!"; + public string ElfSetLevelAll => "Set all Elfs to level {0}!"; + public string ElfSetLevel => "Set Elf {0} to level {1}!"; + public string ElfSetStarAll => "Set all Elf's Resonance to {0}!"; + public string ElfSetStar => "Set Elf {0}'s Resonance to {1}!"; +} + +/// +/// path: Game.Command.Abyss +/// +public class AbyssTextEN +{ + public string Desc => "Set abyss disturbance,bracket,site \n"; + + public string Usage => + "Usage: /abyss bracket [1-9]\n\n" + + "Usage: /abyss temp [value]\n\n" + + "Usage: /abyss site [siteId]\n"; + + public string Success => "Success set {0}"; + public string AreaNotFound => "SiteId Not Found"; +} + +/// +/// path: Game.Command.Endless +/// +public class EndlessTextEN +{ + public string Desc => "Set Memorial Arena boss \n"; + + public string Usage => + "Usage: /endless [bossid1] [bossid2] [bossid3]\n\n" + + "/endless 1001 1002 1003"; + + public string Success => "Success set Memorial Arena Boss"; + public string NotFound => "BossId Not Found"; +} + +#endregion + +#endregion \ No newline at end of file diff --git a/Common/Internationalization/PluginLanguageAttribute.cs b/Common/Internationalization/PluginLanguageAttribute.cs new file mode 100644 index 0000000..cde6528 --- /dev/null +++ b/Common/Internationalization/PluginLanguageAttribute.cs @@ -0,0 +1,9 @@ +using MikuSB.Enums.Language; + +namespace MikuSB.Internationalization; + +[AttributeUsage(AttributeTargets.Class)] +public class PluginLanguageAttribute(ProgramLanguageTypeEnum languageType) : Attribute +{ + public ProgramLanguageTypeEnum LanguageType { get; } = languageType; +} \ No newline at end of file diff --git a/Common/Util/ConfigManager.cs b/Common/Util/ConfigManager.cs new file mode 100644 index 0000000..70e72bf --- /dev/null +++ b/Common/Util/ConfigManager.cs @@ -0,0 +1,99 @@ +using MikuSB.Configuration; +using MikuSB.Internationalization; +using Newtonsoft.Json; +using MikuSB.Util.Extensions; + +namespace MikuSB.Util; + +public static class ConfigManager +{ + public static readonly Logger Logger = new("ConfigManager"); + public static ConfigContainer Config { get; private set; } = new(); + private static readonly string ConfigFilePath = Config.Path.ConfigPath + "/Config.json"; + public static HotfixContainer Hotfix { get; private set; } = new(); + private static readonly string HotfixFilePath = Config.Path.ConfigPath + "/Hotfix.json"; + + public static void LoadConfig() + { + LoadConfigData(); + //LoadHotfixData(); + } + + private static void LoadConfigData() + { + var file = new FileInfo(ConfigFilePath); + if (!file.Exists) + { + Config = new() + { + ServerOption = + { + Language = Extensions.Extensions.GetCurrentLanguage() + } + }; + + Logger.Info("Current Language is " + Config.ServerOption.Language); + SaveData(Config, ConfigFilePath); + } + + using (var stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + using (var reader = new StreamReader(stream)) + { + var json = reader.ReadToEnd(); + Config = JsonConvert.DeserializeObject(json)!; + } + + SaveData(Config, ConfigFilePath); + } + + private static void LoadHotfixData() + { + var file = new FileInfo(HotfixFilePath); + + // Generate all necessary versions + var verList = Extensions.Extensions.GetSupportVersions(); + + Logger.Info(I18NManager.Translate("Server.ServerInfo.CurrentVersion", + verList.Aggregate((current, next) => $"{current}, {next}"))); + + if (!file.Exists) + { + Hotfix = new HotfixContainer(); + SaveData(Hotfix, HotfixFilePath); + file.Refresh(); + } + + using (var stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + using (var reader = new StreamReader(stream)) + { + var json = reader.ReadToEnd(); + Hotfix = JsonConvert.DeserializeObject(json)!; + } + + foreach (var version in verList) + if (!Hotfix.Hotfixes.TryGetValue(version, out var _)) + Hotfix.Hotfixes[version] = new(); + + SaveData(Hotfix, HotfixFilePath); + } + + private static void SaveData(object data, string path) + { + var json = JsonConvert.SerializeObject(data, Formatting.Indented); + using var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite); + using var writer = new StreamWriter(stream); + writer.Write(json); + } + + public static void InitDirectories() + { + foreach (var property in Config.Path.GetType().GetProperties()) + { + var dir = property.GetValue(Config.Path)?.ToString(); + + if (!string.IsNullOrEmpty(dir)) + if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) + Directory.CreateDirectory(dir); + } + } +} \ No newline at end of file diff --git a/Common/Util/Crpyto/DispatchEncryption.cs b/Common/Util/Crpyto/DispatchEncryption.cs new file mode 100644 index 0000000..466b95c --- /dev/null +++ b/Common/Util/Crpyto/DispatchEncryption.cs @@ -0,0 +1,35 @@ +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; + +namespace MikuSB.Util.Crypto; + +public static class DispatchEncryption +{ + private static readonly JsonSerializerOptions JsonSerializerOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower + }; + + public static string? EncryptDispatchContent(string version, object? data) + { + if (!ConfigManager.Hotfix.AesKeys.TryGetValue(version, out var aesKey)) + return null; + + var serializedData = JsonSerializer.Serialize(data, JsonSerializerOptions); + var keyBytes = aesKey.Split(' ') + .Select(b => Convert.ToByte(b, 16)) + .ToArray(); + + using var aes = Aes.Create(); + aes.Mode = CipherMode.ECB; + aes.Padding = PaddingMode.PKCS7; + aes.Key = keyBytes; + + var encryptor = aes.CreateEncryptor(); + var dataBytes = Encoding.UTF8.GetBytes(serializedData); + var encryptedBytes = encryptor.TransformFinalBlock(dataBytes, 0, dataBytes.Length); + + return Convert.ToBase64String(encryptedBytes); + } +} \ No newline at end of file diff --git a/Common/Util/DateTime.cs b/Common/Util/DateTime.cs new file mode 100644 index 0000000..61308d0 --- /dev/null +++ b/Common/Util/DateTime.cs @@ -0,0 +1,14 @@ + +namespace MikuSB.Util; + + +public static class DateTimeExtensions +{ + private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + public static long ToUnixTimestampMilliseconds(this DateTime dateTime) + { + return (long)(dateTime - UnixEpoch).TotalMilliseconds; + } + +} diff --git a/Common/Util/Extensions/Extensions.cs b/Common/Util/Extensions/Extensions.cs new file mode 100644 index 0000000..a7ed27b --- /dev/null +++ b/Common/Util/Extensions/Extensions.cs @@ -0,0 +1,232 @@ +using MikuSB.Proto; +using Newtonsoft.Json; +using System.Buffers.Binary; +using System.Globalization; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; + +namespace MikuSB.Util.Extensions; + +public static partial class Extensions +{ + #region Regex + + [GeneratedRegex(@"CN|OS|BETA|PROD|CECREATION|Android|Win|iOS")] + public static partial Regex VersionRegex(); + + [GeneratedRegex(@"(?<=Avatar_)(.*?)(?=_Config)")] + public static partial Regex AvatarConfigRegex(); + + [GeneratedRegex(@"(?<=Avatar_RogueBattleevent)(.*?)(?=_Config.json)")] + public static partial Regex BattleEventDataRegex(); + + [GeneratedRegex(@"coin(\d+)tier")] + public static partial Regex ProductRegex(); + + #endregion + + public static string GetCurrentLanguage() + { + var uiCulture = CultureInfo.CurrentUICulture; + return uiCulture.Name switch + { + "zh-CN" => "CHS", + "zh-TW" => "CHT", + "ja-JP" => "JP", + _ => "EN" + }; + } + + public static List GetSupportVersions() + { + var verList = new List(); + if (GameConstants.GAME_VERSION[^1] == '5') + for (var i = 1; i < 6; i++) + verList.Add(GameConstants.GAME_VERSION + i.ToString()); + else + verList.Add(GameConstants.GAME_VERSION); + + return verList; + } + + public static T RandomElement(this List values) + { + var index = new Random().Next(values.Count); + return values[index]; + } + + public static string RandomKey(int length) + { + const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + var random = new Random(); + return new string(Enumerable.Repeat(chars, length) + .Select(s => s[random.Next(s.Length)]).ToArray()); + } + + public static ICollection Clone(this ICollection values) + { + List list = [.. values]; + + return list; + } + + public static int RandomInt(int from, int to) + { + return new Random().Next(from, to); + } + + public static string GetSha256Hash(string input) + { + byte[] bytes = SHA256.HashData(Encoding.UTF8.GetBytes(input)); + var builder = new StringBuilder(); + for (int i = 0; i < bytes.Length; i++) builder.Append(bytes[i].ToString("x2")); + return builder.ToString(); + } + + public static void SafeAdd(this List list, T item) + { + if (!list.Contains(item)) list.Add(item); + } + + public static void SafeAddRange(this List list, List item) + { + foreach (var i in item) list.SafeAdd(i); + } + + public static long GetUnixSec() + { + return DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + } + + public static long ToUnixSec(this DateTime dt) + { + return new DateTimeOffset(dt).ToUnixTimeSeconds(); + } + + public static long GetUnixMs() + { + return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + } + + public static string ToArrayString(this List list) + { + return list.JoinFormat(", ", ""); + } + + public static string ToJsonString(this Dictionary dic) where TK : notnull + { + return JsonConvert.SerializeObject(dic); + } + + public static byte[] StringToByteArray(string hex) + { + if (hex.Length % 2 == 1) + throw new Exception("The binary key cannot have an odd number of digits"); + + byte[] arr = new byte[hex.Length >> 1]; + + for (int i = 0; i < hex.Length >> 1; ++i) + { + arr[i] = (byte)((GetHexVal(hex[i << 1]) << 4) + (GetHexVal(hex[(i << 1) + 1]))); + } + + return arr; + } + + public static int GetHexVal(char hex) + { + int val = (int)hex; + //For uppercase A-F letters: + //return val - (val < 58 ? 48 : 55); + //For lowercase a-f letters: + //return val - (val < 58 ? 48 : 87); + //Or the two combined, but a bit slower: + return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); + } + + #region Kcp Utils + + public static string JoinFormat(this IEnumerable list, string separator, + string formatString) + { + formatString = string.IsNullOrWhiteSpace(formatString) ? "{0}" : formatString; + return string.Join(separator, + list.Select(item => string.Format(formatString, item))); + } + + public static void WriteConvID(this BinaryWriter bw, long convId) + { + //bw.Write(convId); + bw.Write((int)(convId >> 32)); + bw.Write((int)(convId & 0xFFFFFFFF)); + } + + public static long GetNextAvailableIndex(this SortedList sortedList) + { + long key = 1; + long count = sortedList.Count; + long counter = 0; + do + { + if (count == 0) break; + var nextKeyInList = sortedList.Keys.ElementAt((Index)counter++); + if (key != nextKeyInList) break; + key = nextKeyInList + 1; + } while (count != 1 && counter != count && key == sortedList.Keys.ElementAt((Index)counter)); + + return key; + } + + public static long AddNext(this SortedList sortedList, T item) + { + var key = sortedList.GetNextAvailableIndex(); + sortedList.Add(key, item); + return key; + } + + public static int ReadInt32BE(this BinaryReader br) + { + return BinaryPrimitives.ReadInt32BigEndian(br.ReadBytes(sizeof(int))); + } + + public static uint ReadUInt32BE(this BinaryReader br) + { + return BinaryPrimitives.ReadUInt32BigEndian(br.ReadBytes(sizeof(uint))); + } + + public static ushort ReadUInt16BE(this BinaryReader br) + { + return BinaryPrimitives.ReadUInt16BigEndian(br.ReadBytes(sizeof(ushort))); + } + + public static void WriteUInt16BE(this BinaryWriter bw, ushort value) + { + Span data = stackalloc byte[sizeof(ushort)]; + BinaryPrimitives.WriteUInt16BigEndian(data, value); + bw.Write(data); + } + + public static void WriteInt32BE(this BinaryWriter bw, int value) + { + Span data = stackalloc byte[sizeof(int)]; + BinaryPrimitives.WriteInt32BigEndian(data, value); + bw.Write(data); + } + + public static void WriteUInt32BE(this BinaryWriter bw, uint value) + { + Span data = stackalloc byte[sizeof(uint)]; + BinaryPrimitives.WriteUInt32BigEndian(data, value); + bw.Write(data); + } + + public static void WriteUInt64BE(this BinaryWriter bw, ulong value) + { + Span data = stackalloc byte[sizeof(ulong)]; + BinaryPrimitives.WriteUInt64BigEndian(data, value); + bw.Write(data); + } + + #endregion +} \ No newline at end of file diff --git a/Common/Util/GameConstants.cs b/Common/Util/GameConstants.cs new file mode 100644 index 0000000..726a724 --- /dev/null +++ b/Common/Util/GameConstants.cs @@ -0,0 +1,11 @@ +namespace MikuSB.Util; + +public static class GameConstants +{ + public const string GAME_VERSION = "BETA V1.0"; + public const int MAX_STAMINA = 300; + public const int STAMINA_RECOVERY_TIME = 360; // 6 minutes + public const int STAMINA_RESERVE_RECOVERY_TIME = 1080; // 18 minutes + public const int INVENTORY_MAX_EQUIPMENT = 1000; + public const int MAX_LINEUP_COUNT = 9; +} \ No newline at end of file diff --git a/Common/Util/Guid64.cs b/Common/Util/Guid64.cs new file mode 100644 index 0000000..062a1ea --- /dev/null +++ b/Common/Util/Guid64.cs @@ -0,0 +1,11 @@ +namespace MikuSB.Util; + +public static class Guid64 +{ + public static ulong NewGuid64() + { + byte[] guidBytes = Guid.NewGuid().ToByteArray(); + return (ulong)BitConverter.ToUInt32(guidBytes, 0); + } +} + diff --git a/Common/Util/IConsole.cs b/Common/Util/IConsole.cs new file mode 100644 index 0000000..ac0c0e9 --- /dev/null +++ b/Common/Util/IConsole.cs @@ -0,0 +1,185 @@ +using Kodnix.Character; + +namespace MikuSB.Util; + +public class IConsole +{ + public static readonly string PrefixContent = "[MikuSB]> "; + public static readonly string Prefix = $"\u001b[38;2;255;192;203m{PrefixContent}\u001b[0m"; + private static readonly int HistoryMaxCount = 10; + + public static List Input { get; set; } = []; + private static int CursorIndex { get; set; } = 0; + private static readonly List InputHistory = []; + private static int HistoryIndex = -1; + + public static event Action? OnConsoleExcuteCommand; + + public static void InitConsole() + { + Console.Title = ConfigManager.Config.GameServer.GameServerName; + } + + public static int GetWidth(string str) + => str.ToCharArray().Sum(EastAsianWidth.GetLength); + + public static void RedrawInput(List input, bool hasPrefix = true) + => RedrawInput(new string([.. input]), hasPrefix); + + public static void RedrawInput(string input, bool hasPrefix = true) + { + var length = GetWidth(input); + if (hasPrefix) + { + input = Prefix + input; + length += GetWidth(PrefixContent); + } + + if (Console.GetCursorPosition().Left > 0) + Console.SetCursorPosition(0, Console.CursorTop); + + Console.Write(input + new string(' ', Console.BufferWidth - length)); + Console.SetCursorPosition(length, Console.CursorTop); + } + + #region Handlers + + public static void HandleEnter() + { + var input = new string([.. Input]); + if (string.IsNullOrWhiteSpace(input)) return; + + // New line + Console.WriteLine(); + Input = []; + CursorIndex = 0; + if (InputHistory.Count >= HistoryMaxCount) + InputHistory.RemoveAt(0); + InputHistory.Add(input); + HistoryIndex = InputHistory.Count; + + // Handle command + if (input.StartsWith('/')) input = input[1..].Trim(); + OnConsoleExcuteCommand?.Invoke(input); + } + + public static void HandleBackspace() + { + if (CursorIndex <= 0) return; + CursorIndex--; + var targetWidth = GetWidth(Input[CursorIndex].ToString()); + Input.RemoveAt(CursorIndex); + + var (left, _) = Console.GetCursorPosition(); + Console.SetCursorPosition(left - targetWidth, Console.CursorTop); + var remain = new string([.. Input.Skip(CursorIndex)]); + Console.Write(remain + new string(' ', targetWidth)); + Console.SetCursorPosition(left - targetWidth, Console.CursorTop); + } + + public static void HandleUpArrow() + { + if (InputHistory.Count == 0) return; + + if (HistoryIndex > 0) + { + HistoryIndex--; + var history = InputHistory[HistoryIndex]; + Input = [.. history]; + CursorIndex = Input.Count; + RedrawInput(Input); + } + } + + public static void HandleDownArrow() + { + if (HistoryIndex >= InputHistory.Count) return; + + HistoryIndex++; + if (HistoryIndex >= InputHistory.Count) + { + HistoryIndex = InputHistory.Count; + Input = []; + CursorIndex = 0; + } + else + { + var history = InputHistory[HistoryIndex]; + Input = [.. history]; + CursorIndex = Input.Count; + } + RedrawInput(Input); + } + + public static void HandleLeftArrow() + { + if (CursorIndex <= 0) return; + + var (left, _) = Console.GetCursorPosition(); + CursorIndex--; + Console.SetCursorPosition(left - GetWidth(Input[CursorIndex].ToString()), Console.CursorTop); + } + + public static void HandleRightArrow() + { + if (CursorIndex >= Input.Count) return; + + var (left, _) = Console.GetCursorPosition(); + CursorIndex++; + Console.SetCursorPosition(left + GetWidth(Input[CursorIndex - 1].ToString()), Console.CursorTop); + } + + public static void HandleInput(ConsoleKeyInfo keyInfo) + { + if (char.IsControl(keyInfo.KeyChar)) return; + if (Input.Count >= (Console.BufferWidth - PrefixContent.Length)) return; + HandleInput(keyInfo.KeyChar); + } + + public static void HandleInput(char keyChar) + { + Input.Insert(CursorIndex, keyChar); + CursorIndex++; + + var (left, _) = Console.GetCursorPosition(); + Console.Write(new string([.. Input.Skip(CursorIndex - 1)])); + Console.SetCursorPosition(left + GetWidth(keyChar.ToString()), Console.CursorTop); + } + + #endregion + + public static string ListenConsole() + { + while (true) + { + ConsoleKeyInfo keyInfo; + try { keyInfo = Console.ReadKey(true); } + catch (InvalidOperationException) { continue; } + + switch (keyInfo.Key) + { + case ConsoleKey.Enter: + HandleEnter(); + break; + case ConsoleKey.Backspace: + HandleBackspace(); + break; + case ConsoleKey.LeftArrow: + HandleLeftArrow(); + break; + case ConsoleKey.RightArrow: + HandleRightArrow(); + break; + case ConsoleKey.UpArrow: + HandleUpArrow(); + break; + case ConsoleKey.DownArrow: + HandleDownArrow(); + break; + default: + HandleInput(keyInfo); + break; + } + } + } +} \ No newline at end of file diff --git a/Common/Util/Logger.cs b/Common/Util/Logger.cs new file mode 100644 index 0000000..5a60b49 --- /dev/null +++ b/Common/Util/Logger.cs @@ -0,0 +1,109 @@ +using Spectre.Console; +using System.Diagnostics; + +namespace MikuSB.Util; + +public class Logger(string moduleName) +{ + private static FileInfo? LogFile; + private static readonly object _lock = new(); + private readonly string ModuleName = moduleName; + + public void Log(string message, LoggerLevel level) + { + lock (_lock) + { + var savedInput = IConsole.Input.ToList(); // Copy + IConsole.RedrawInput("", false); + AnsiConsole.MarkupLine($"[[[bold deepskyblue3_1]{DateTime.Now:HH:mm:ss}[/]]] " + + $"[[[gray]{ModuleName}[/]]] [[[{(ConsoleColor)level}]{level}[/]]] " + + $"{message.Replace("[", "[[").Replace("]", "]]")}"); + IConsole.RedrawInput(savedInput); + + var logMessage = $"[{DateTime.Now:HH:mm:ss}] [{ModuleName}] [{level}] {message}"; + WriteToFile(logMessage); + } + } + + public void Info(string message, Exception? e = null) + { + Log(message, LoggerLevel.INFO); + if (e != null) + { + Log(e.Message, LoggerLevel.INFO); + Log(e.StackTrace!, LoggerLevel.INFO); + } + } + + public void Warn(string message, Exception? e = null) + { + Log(message, LoggerLevel.WARN); + if (e != null) + { + Log(e.Message, LoggerLevel.WARN); + Log(e.StackTrace!, LoggerLevel.WARN); + } + } + + public void Error(string message, Exception? e = null) + { + Log(message, LoggerLevel.ERROR); + if (e != null) + { + Log(e.Message, LoggerLevel.ERROR); + Log(e.StackTrace!, LoggerLevel.ERROR); + } + } + + public void Fatal(string message, Exception? e = null) + { + Log(message, LoggerLevel.FATAL); + if (e != null) + { + Log(e.Message, LoggerLevel.FATAL); + Log(e.StackTrace!, LoggerLevel.FATAL); + } + } + + public void Debug(string message, Exception? e = null) + { + Log(message, LoggerLevel.DEBUG); + if (e != null) + { + Log(e.Message, LoggerLevel.DEBUG); + Log(e.StackTrace!, LoggerLevel.DEBUG); + } + } + + public static void SetLogFile(FileInfo file) + { + LogFile = file; + } + + public static void WriteToFile(string message) + { + try + { + if (LogFile == null) throw new Exception("LogFile is not set"); + using var sw = LogFile.AppendText(); + sw.WriteLine(message); + } + catch + { + } + } + + public static Logger GetByClassName() + { + return new Logger(new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? ""); + } +} + +public enum LoggerLevel +{ + INFO = ConsoleColor.Cyan, + WARN = ConsoleColor.Yellow, + ERROR = ConsoleColor.Red, + FATAL = ConsoleColor.DarkRed, + DEBUG = ConsoleColor.Blue +} diff --git a/Common/Util/LoggingMiddleware.cs b/Common/Util/LoggingMiddleware.cs new file mode 100644 index 0000000..5e5563f --- /dev/null +++ b/Common/Util/LoggingMiddleware.cs @@ -0,0 +1,34 @@ +using MikuSB.Util; +using Microsoft.AspNetCore.Http; + +namespace MikuSB.SdkServer.Utils; + +public class RequestLoggingMiddleware(RequestDelegate next) +{ + public async Task InvokeAsync(HttpContext context, Logger logger) + { + var request = context.Request; + var method = request.Method; + var path = request.Path + request.QueryString; + + await next(context); + + var statusCode = context.Response.StatusCode; + + if (path.StartsWith("/report") || path.Contains("/log/") || path == "/alive") + return; + + if (statusCode == 200) + { + logger.Info($"{method} {path} => {statusCode}"); + } + else if (statusCode == 404) + { + logger.Warn($"{method} {path} => {statusCode}"); + } + else + { + logger.Error($"{method} {path} => {statusCode}"); + } + } +} \ No newline at end of file diff --git a/Common/Util/Position.cs b/Common/Util/Position.cs new file mode 100644 index 0000000..dbfe891 --- /dev/null +++ b/Common/Util/Position.cs @@ -0,0 +1,129 @@ +namespace MikuSB.Common.Util; + +public class Position +{ + public Position(float x, float y, float z) + { + X = x; + Y = y; + Z = z; + } + + //public Position(Proto.Position position) + //{ + // X = position.X; + // Y = position.Y; + // Z = position.Z; + //} + + public Position() + { + X = 0; + Y = 0; + Z = 0; + } + + public Position(Position position) + { + X = position.X; + Y = position.Y; + Z = position.Z; + } + + public float X { get; set; } + public float Y { get; set; } + public float Z { get; set; } + + public void Set(float x, float y, float z) + { + X = x; + Y = y; + Z = z; + } + + public void Set(Position position) + { + X = position.X; + Y = position.Y; + Z = position.Z; + } + + //public void Set(Vector vector) + //{ + // X = vector.X; + // Y = vector.Y; + // Z = vector.Z; + //} + + public void Add(float x, float y, float z) + { + X += x; + Y += y; + Z += z; + } + + public void Add(Position position) + { + X += position.X; + Y += position.Y; + Z += position.Z; + } + + public void Sub(float x, float y, float z) + { + X -= x; + Y -= y; + Z -= z; + } + + public void Sub(Position position) + { + X -= position.X; + Y -= position.Y; + Z -= position.Z; + } + + public void Mul(float x, float y, float z) + { + X *= x; + Y *= y; + Z *= z; + } + + public void Mul(Position position) + { + X *= position.X; + Y *= position.Y; + Z *= position.Z; + } + + public void Div(float x, float y, float z) + { + X /= x; + Y /= y; + Z /= z; + } + + public void Div(Position position) + { + X /= position.X; + Y /= position.Y; + Z /= position.Z; + } + + public double Distance(Position position) + { + return Math.Sqrt((X - position.X) * (X - position.X) + (Y - position.Y) * (Y - position.Y) + + (Z - position.Z) * (Z - position.Z)); + } + + //public Proto.Position ToProto() + //{ + // return new Proto.Position + // { + // X = (int)X, + // Y = (int)Y, + // Z = (int)Z + // }; + //} +} \ No newline at end of file diff --git a/Common/Util/Security/Crypto.cs b/Common/Util/Security/Crypto.cs new file mode 100644 index 0000000..4328b40 --- /dev/null +++ b/Common/Util/Security/Crypto.cs @@ -0,0 +1,29 @@ +using System.Security.Cryptography; +using System.Text; + +namespace MikuSB.Util.Security; + +public class Crypto +{ + private static readonly Random SecureRandom = new(); + + // Simple way to create a unique session key + public static string CreateSessionKey(string accountUid) + { + var random = new byte[64]; + SecureRandom.NextBytes(random); + + var temp = accountUid + "." + DateTime.Now.Ticks + "." + SecureRandom; + + try + { + var bytes = SHA512.HashData(Encoding.UTF8.GetBytes(temp)); + return Convert.ToBase64String(bytes); + } + catch + { + var bytes = SHA512.HashData(Encoding.UTF8.GetBytes(temp)); + return Convert.ToBase64String(bytes); + } + } +} \ No newline at end of file diff --git a/Config/Config.json b/Config/Config.json new file mode 100644 index 0000000..b601499 --- /dev/null +++ b/Config/Config.json @@ -0,0 +1,42 @@ +{ + "HttpServer": { + "BindAddress": "0.0.0.0", + "PublicAddress": "127.0.0.1", + "Port": 21500 + }, + "GameServer": { + "BindAddress": "0.0.0.0", + "PublicAddress": "127.0.0.1", + "Port": 21000, + "KcpAliveMs": 45000, + "DatabaseName": "Miku.db", + "GameServerId": "MikuSB", + "GameServerName": "MikuSB" + }, + "Path": { + "ResourcePath": "Resources", + "ConfigPath": "Config", + "DatabasePath": "Config/Database", + "HandbookPath": "Config/Handbook", + "LogPath": "Config/Logs", + "DataPath": "Config/Data" + }, + "ServerOption": { + "Language": "EN", + "FallbackLanguage": "EN", + "DefaultPermissions": [ + "Admin" + ], + "ServerProfile": { + "Name": "Miku-chan", + "Uid": 80 + }, + "AutoCreateUser": true, + "SavePersonalDebugFile": false, + "AutoSendResponseWhenNoHandler": true, + "EnableDebug": true, + "DebugMessage": true, + "DebugDetailMessage": true, + "DebugNoHandlerPacket": true + } +} \ No newline at end of file diff --git a/Config/Hotfix.json b/Config/Hotfix.json new file mode 100644 index 0000000..00f3859 --- /dev/null +++ b/Config/Hotfix.json @@ -0,0 +1,51 @@ +{ + "UseLocalCache": false, + "Hotfixes": { + "BETA V1.0": { + "Asb": { + "Android": { + "EnableTime": 0, + "Revision": "", + "Suffix": "" + }, + "Iphone": { + "EnableTime": 0, + "Revision": "", + "Suffix": "" + }, + "Pc": { + "EnableTime": 0, + "Revision": "", + "Suffix": "" + } + }, + "AsbPreDownload": { + "Android": { + "EncryptKey": "", + "EnableTime": 0, + "Revision": "", + "Suffix": "" + }, + "Iphone": { + "EncryptKey": "", + "EnableTime": 0, + "Revision": "", + "Suffix": "" + } + }, + "Audio": { + "Platform": {}, + "Revision": 0 + }, + "AudioPreDownload": { + "EnableTime": 0, + "Platform": {}, + "Revision": 0 + }, + "VideoEncrypt": { + "FileName": "" + } + } + }, + "AesKeys": {} +} \ No newline at end of file diff --git a/GameServer/Command/CommandArg.cs b/GameServer/Command/CommandArg.cs new file mode 100644 index 0000000..e9cd9c1 --- /dev/null +++ b/GameServer/Command/CommandArg.cs @@ -0,0 +1,79 @@ +using MikuSB.Database.Account; +using MikuSB.GameServer.Server; +using MikuSB.Internationalization; + +namespace MikuSB.GameServer.Command; + +public class CommandArg +{ + public string RawArg { get; } = ""; + public List Args { get; } = []; + public List Attributes { get; } = []; + public ICommandSender Sender { get; } + public int TargetUid { get; set; } = 0; + public Connection? Target { get; set; } + + public CommandArg(string rawArg, ICommandSender sender) + { + Sender = sender; + RawArg = rawArg; + foreach (var arg in rawArg.Split(' ')) + { + if (string.IsNullOrEmpty(arg)) continue; + Args.Add(arg); + } + } + + public async ValueTask SendMsg(string msg) + { + await Sender.SendMsg(msg); + } + + public int GetInt(int index) + { + if (Args.Count <= index) return 0; + if (int.TryParse(Args[index], out var res)) + return res; + return 0; + } + + public async ValueTask GetOption(char pre, string def = "1") + { + var opStr = Args.FirstOrDefault(x => x[0] == pre)?[1..] ?? def; + if (!int.TryParse(opStr, out var op)) + { + await SendMsg(I18NManager.Translate("Game.Command.Notice.InvalidArguments")); + return null; + } + return op; + } + + public async ValueTask CheckArgCnt(int start, int? end = null) + { + end ??= start; + if (Args.Count >= start && Args.Count <= end) return true; + await SendMsg(I18NManager.Translate("Game.Command.Notice.InvalidArguments")); + return false; + } + + public async ValueTask CheckTarget() + { + if (AccountData.GetAccountByUid(TargetUid) == null) + { + await SendMsg(I18NManager.Translate("Game.Command.Notice.PlayerNotFound")); + return false; + } + return true; + } + + public async ValueTask CheckOnlineTarget(bool sendMsg = true) + { + if (Target == null) + { + if (sendMsg) + await SendMsg(I18NManager.Translate("Game.Command.Notice.PlayerNotFound")); + return false; + } + return true; + } +} \ No newline at end of file diff --git a/GameServer/Command/CommandAttribute.cs b/GameServer/Command/CommandAttribute.cs new file mode 100644 index 0000000..6d2411f --- /dev/null +++ b/GameServer/Command/CommandAttribute.cs @@ -0,0 +1,25 @@ +using MikuSB.Enums.Player; + +namespace MikuSB.GameServer.Command; + +[AttributeUsage(AttributeTargets.Class)] +public class CommandInfoAttribute( + string name, string desc, string usage, string[] alias, PermEnum[] perm) : Attribute +{ + public string Name { get; } = name; + public string Description { get; } = desc; + public string Usage { get; } = usage; + public PermEnum[] Perm { get; } = perm; + public string[] Alias { get; } = alias; +} + +[AttributeUsage(AttributeTargets.Method)] +public class CommandDefaultAttribute : Attribute +{ +} + +[AttributeUsage(AttributeTargets.Method)] +public class CommandMethodAttribute(string method) : Attribute +{ + public string MethodName { get; } = method; +} \ No newline at end of file diff --git a/GameServer/Command/CommandExecutor.cs b/GameServer/Command/CommandExecutor.cs new file mode 100644 index 0000000..81e7f5e --- /dev/null +++ b/GameServer/Command/CommandExecutor.cs @@ -0,0 +1,19 @@ + +namespace MikuSB.GameServer.Command; + +public static class CommandExecutor +{ + public delegate void RunCommand(ICommandSender sender, string cmd); + + public static event RunCommand? OnRunCommand; + + public static void ExecuteCommand(ICommandSender sender, string cmd) + { + OnRunCommand?.Invoke(sender, cmd); + } + + public static void ConsoleExcuteCommand(string input) + { + CommandManager.HandleCommand(input, new ConsoleCommandSender(CommandManager.Logger)); + } +} \ No newline at end of file diff --git a/GameServer/Command/CommandInterface.cs b/GameServer/Command/CommandInterface.cs new file mode 100644 index 0000000..35d325a --- /dev/null +++ b/GameServer/Command/CommandInterface.cs @@ -0,0 +1,3 @@ +namespace MikuSB.GameServer.Command; + +public interface ICommands; \ No newline at end of file diff --git a/GameServer/Command/CommandManager.cs b/GameServer/Command/CommandManager.cs new file mode 100644 index 0000000..4d9fc34 --- /dev/null +++ b/GameServer/Command/CommandManager.cs @@ -0,0 +1,126 @@ +using MikuSB.Database.Account; +using MikuSB.Enums.Player; +using MikuSB.GameServer.Server; +using MikuSB.Internationalization; +using MikuSB.TcpSharp; +using MikuSB.Util; +using System.Reflection; + +namespace MikuSB.GameServer.Command; + +public class CommandManager +{ + public static Logger Logger { get; } = new("CommandManager"); + + public static Dictionary Commands { get; } = []; + public static Dictionary CommandInfo { get; } = []; + public static Dictionary CommandAlias { get; } = []; // + + public static void RegisterCommands() + { + foreach (var type in Assembly.GetExecutingAssembly().GetTypes()) + if (typeof(ICommands).IsAssignableFrom(type) && !type.IsAbstract) + RegisterCommand(type); + + Logger.Info(I18NManager.Translate("Server.ServerInfo.RegisterItem", Commands.Count.ToString(), + I18NManager.Translate("Word.Command"))); + } + + public static void RegisterCommand(Type type) + { + var attr = type.GetCustomAttribute(); + if (attr == null) return; + var instance = Activator.CreateInstance(type); + if (instance is not ICommands command) return; + Commands.Add(attr.Name, command); + CommandInfo.Add(attr.Name, attr); + + // register alias + foreach (var alias in attr.Alias) // add alias + CommandAlias.Add(alias, attr.Name); + } + + public static async void HandleCommand(string input, ICommandSender sender) + { + try + { + var argInfo = new CommandArg(input, sender); + var target = sender.GetSender(); + + foreach (var arg in argInfo.Args.ToList()) // Copy + { + switch (arg[0]) + { + case '-': + argInfo.Attributes.Add(arg[1..]); + break; + case '@': + _ = int.TryParse(arg[1..], out target); + argInfo.Args.Remove(arg); + break; + } + } + argInfo.TargetUid = target; + if (SocketListener.Connections.Values.ToList().Find(item => + (item as Connection)?.Player?.Uid == target) is Connection con) + argInfo.Target = con; + + // find register cmd + var cmdName = argInfo.Args[0]; + if (CommandAlias.TryGetValue(cmdName, out var fullName)) cmdName = fullName; + if (!Commands.TryGetValue(cmdName, out var command)) + { + await sender.SendMsg(I18NManager.Translate("Game.Command.Notice.CommandNotFound")); + return; + } + argInfo.Args.RemoveAt(0); + var cmdInfo = CommandInfo[cmdName]; + + // Check cmd perms + if (!AccountData.HasPerm(cmdInfo.Perm, sender.GetSender())) + { + await sender.SendMsg(I18NManager.Translate("Game.Command.Notice.NoPermission")); + return; + } + if (argInfo.Target?.Player?.Uid != sender.GetSender() && !AccountData.HasPerm([PermEnum.Other], sender.GetSender())) + { + await sender.SendMsg(I18NManager.Translate("Game.Command.Notice.NoPermission")); + return; + } + + // find CommandMethodAttribute + var isFound = false; + foreach (var methodInfo in command.GetType().GetMethods()) + { + var attr = methodInfo.GetCustomAttribute(); + if (attr == null) continue; + if (argInfo.Args.Count > 0 && attr.MethodName == argInfo.Args[0]) + { + argInfo.Args.RemoveAt(0); + isFound = true; + methodInfo.Invoke(command, [argInfo]); + break; + } + } + if (isFound) return; + + // find CommandDefaultAttribute + foreach (var methodInfo in command.GetType().GetMethods()) + { + var attr = methodInfo.GetCustomAttribute(); + if (attr == null) continue; + isFound = true; + methodInfo.Invoke(command, [argInfo]); + break; + } + if (isFound) return; + + // failed to find method + await sender.SendMsg(I18NManager.Translate(cmdInfo.Usage)); + } + catch (Exception ex) + { + Logger.Error(I18NManager.Translate("Game.Command.Notice.InternalError", ex.ToString())); + } + } +} \ No newline at end of file diff --git a/GameServer/Command/CommandSender.cs b/GameServer/Command/CommandSender.cs new file mode 100644 index 0000000..eba40ac --- /dev/null +++ b/GameServer/Command/CommandSender.cs @@ -0,0 +1,41 @@ +using MikuSB.Enums.Player; +using MikuSB.GameServer.Game.Player; +using MikuSB.Util; + +namespace MikuSB.GameServer.Command; + +public interface ICommandSender +{ + public ValueTask SendMsg(string msg); + + public int GetSender(); +} + +public class ConsoleCommandSender(Logger logger) : ICommandSender +{ + public async ValueTask SendMsg(string msg) + { + logger.Info(msg); + await Task.CompletedTask; + } + + public int GetSender() + { + return (int)ServerEnum.Console; + } +} + +public class PlayerCommandSender(PlayerInstance player) : ICommandSender +{ + public PlayerInstance Player = player; + + public async ValueTask SendMsg(string msg) + { + // TODO + } + + public int GetSender() + { + return Player.Uid; + } +} \ No newline at end of file diff --git a/GameServer/Command/Commands/CommandHelp.cs b/GameServer/Command/Commands/CommandHelp.cs new file mode 100644 index 0000000..24951b6 --- /dev/null +++ b/GameServer/Command/Commands/CommandHelp.cs @@ -0,0 +1,52 @@ +using MikuSB.Enums.Player; +using MikuSB.Internationalization; +using MikuSB.Util.Extensions; + +namespace MikuSB.GameServer.Command.Commands; + +[CommandInfo("help", "Game.Command.Help.Desc", "Game.Command.Help.Usage", ["h"], [PermEnum.Support, PermEnum.Trial])] +public class CommandHelp : ICommands +{ + [CommandDefault] + public async static ValueTask Help(CommandArg arg) + { + if (arg.Args.Count == 1) + { + var cmd = arg.Args[0]; + if (CommandManager.CommandInfo == null || !CommandManager.CommandInfo.TryGetValue(cmd, out var command)) + { + await arg.SendMsg(I18NManager.Translate("Game.Command.Notice.CommandNotFound")); + return; + } + + var msg = + $"/{command.Name} - {I18NManager.Translate(command.Description)}\n{I18NManager.Translate(command.Usage)}"; + if (command.Alias.Length > 0) + msg += + $"\n{I18NManager.Translate("Game.Command.Help.CommandAlias")} {command.Alias.ToList().ToArrayString()}"; + if (command.Perm != null) + msg += $"\n{I18NManager.Translate("Game.Command.Help.CommandPermission")} {string.Join(", ", command.Perm.Select(perm => perm.ToString()))}"; + + await arg.SendMsg(msg + "\n"); + return; + } + else + { + await arg.SendMsg(I18NManager.Translate("Game.Command.Help.Commands")); + if (CommandManager.CommandInfo == null) return; + + foreach (var command in CommandManager.CommandInfo.Values) + { + var msg = + $"/{command.Name} - {I18NManager.Translate(command.Description)}\n{I18NManager.Translate(command.Usage)}"; + if (command.Alias.Length > 0) + msg += + $"\n{I18NManager.Translate("Game.Command.Help.CommandAlias")} {command.Alias.ToList().ToArrayString()}"; + + if (command.Perm != null) + msg += $"\n{I18NManager.Translate("Game.Command.Help.CommandPermission")} {string.Join(", ", command.Perm.Select(perm => perm.ToString()))}"; + await arg.SendMsg(msg + "\n"); + } + } + } +} \ No newline at end of file diff --git a/GameServer/Game/BasePlayerManager.cs b/GameServer/Game/BasePlayerManager.cs new file mode 100644 index 0000000..0b45023 --- /dev/null +++ b/GameServer/Game/BasePlayerManager.cs @@ -0,0 +1,8 @@ +using MikuSB.GameServer.Game.Player; + +namespace MikuSB.GameServer.Game; + +public class BasePlayerManager(PlayerInstance player) +{ + public PlayerInstance Player { get; private set; } = player; +} \ No newline at end of file diff --git a/GameServer/Game/Player/PlayerInstance.cs b/GameServer/Game/Player/PlayerInstance.cs new file mode 100644 index 0000000..30d3613 --- /dev/null +++ b/GameServer/Game/Player/PlayerInstance.cs @@ -0,0 +1,98 @@ +using MikuSB.Database; +using MikuSB.Database.Account; +using MikuSB.Database.Player; +using MikuSB.GameServer.Server; +using MikuSB.TcpSharp; +using MikuSB.Util.Extensions; + +namespace MikuSB.GameServer.Game.Player; + +public class PlayerInstance(PlayerGameData data) +{ + #region Property + public Connection? Connection { get; set; } + + public static readonly List _playerInstances = []; + public int Uid { get; set; } + public bool Initialized { get; set; } + public bool IsNewPlayer { get; set; } + + #endregion + + #region Data & Manager + + public PlayerGameData Data { get; set; } = data; + + #endregion + + #region Initializers + public PlayerInstance(int uid) : this(new PlayerGameData { Uid = uid }) + { + // new player + IsNewPlayer = true; + Data.Name = AccountData.GetAccountByUid(uid)?.Username; + + DatabaseHelper.CreateInstance(Data); + + var t = Task.Run(async () => + { + await InitialPlayerManager(); + }); + t.Wait(); + + Initialized = true; + + } + private async ValueTask InitialPlayerManager() + { + Uid = Data.Uid; + Data.LastActiveTime = Extensions.GetUnixSec(); + + await Task.CompletedTask; + } + public T InitializeDatabase() where T : BaseDatabaseDataHelper, new() + { + var instance = DatabaseHelper.GetInstanceOrCreateNew(Uid); + return instance!; + } + + #endregion + + #region Network + public async ValueTask OnEnterGame() + { + if (!Initialized) await InitialPlayerManager(); + } + + public async ValueTask OnLogin() + { + _playerInstances.Add(this); + await Task.CompletedTask; + } + + public static PlayerInstance? GetPlayerInstanceByUid(long uid) + => _playerInstances.FirstOrDefault(player => player.Uid == uid); + public void OnLogoutAsync() + { + _playerInstances.Remove(this); + } + public async ValueTask SendPacket(BasePacket packet) + { + if (Connection?.IsOnline == true) await Connection.SendPacket(packet); + } + + #endregion + + #region Actions + public async ValueTask OnHeartBeat() + { + DatabaseHelper.ToSaveUidList.SafeAdd(Uid); + await Task.CompletedTask; + } + + #endregion + + #region Serialization + + #endregion +} \ No newline at end of file diff --git a/GameServer/GameServer.csproj b/GameServer/GameServer.csproj new file mode 100644 index 0000000..6600304 --- /dev/null +++ b/GameServer/GameServer.csproj @@ -0,0 +1,28 @@ + + + + Library + net9.0 + enable + enable + false + MikuSB.GameServer + + true + MikuGameServer + + + + + + + + + + + + + + + + diff --git a/GameServer/Server/Connection.cs b/GameServer/Server/Connection.cs new file mode 100644 index 0000000..b97bf4f --- /dev/null +++ b/GameServer/Server/Connection.cs @@ -0,0 +1,142 @@ +using MikuSB.Enums.Packet; +using MikuSB.GameServer.Game.Player; +using MikuSB.GameServer.Server.Packet; +using MikuSB.TcpSharp; +using MikuSB.Util; +using System.Buffers; +using System.Net; +using System.Net.Sockets; + +namespace MikuSB.GameServer.Server; + +public class Connection(Socket socket, IPEndPoint remote) : SocketConnection(socket, remote) +{ + private static readonly Logger Logger = new("GameServer"); + + public PlayerInstance? Player { get; set; } + + private static readonly HashSet DummyPacketNames = + [ + + ]; + + public override async void Start() + { + Logger.Info($"New connection from {RemoteEndPoint}."); + State = SessionStateEnum.WAITING_FOR_TOKEN; + await ReceiveLoop(); + } + + public override void Stop(bool isServerStop = false) + { + Player?.OnLogoutAsync(); + SocketListener.UnregisterConnection(this); + base.Stop(isServerStop); + } + + public static int GetInt32(byte[] buf, int index) + { + int networkValue = BitConverter.ToInt32(buf, index); + return IPAddress.NetworkToHostOrder((int)networkValue); + } + + protected async Task ReceiveLoop() + { + try + { + var stream = new NetworkStream(Socket, ownsSocket: false); + + while (SocketConnected()) + { + var decodedPacket = await new PacketCodec().ReadPacketAsync(stream, CancelToken.Token); + + if (decodedPacket == null) + { + Logger.Info("Client disconnected"); + break; + } + + switch (decodedPacket.Framing) + { + case PacketFraming.FourByteLittleEndianLength: + case PacketFraming.TwoByteBigEndianLength: + Framing = decodedPacket.Framing; + LogPacket("Recv", decodedPacket.CmdId, decodedPacket.Body.ToArray(),Framing); + await HandlePacket(decodedPacket.CmdId, decodedPacket.Body.ToArray()); + break; + + case PacketFraming.Control: + Logger.Info("Control packet received"); + // Handle control packet if needed + break; + + case PacketFraming.Unknown: + Logger.Warn("Unknown packet format received"); + break; + } + } + } + catch (OperationCanceledException) + { + Logger.Info("ReceiveLoop cancelled"); + } + catch (Exception ex) + { + Logger.Info($"ReceiveLoop error: {ex}"); + } + finally + { + Socket.Close(); + } + Stop(); + } + + private async Task HandlePacket(ushort opcode, byte[] payload) + { + var packetName = LogMap.GetValueOrDefault(opcode); + if (DummyPacketNames.Contains(packetName!)) + { + await SendDummy(packetName!); + Logger.Info($"[Dummy] Send Dummy {packetName}"); + return; + } + + // Find the Handler for this opcode + var handler = HandlerManager.GetHandler(opcode); + if (handler != null) + { + // Handle + // Make sure session is ready for packets + var state = State; + try + { + await handler.OnHandle(this, payload, (ushort)DownStreamSeqNo); + } + catch (Exception e) + { + Logger.Error(e.Message, e); + } + return; + } + + if (ConfigManager.Config.ServerOption.EnableDebug && + ConfigManager.Config.ServerOption.DebugNoHandlerPacket && !IgnoreLog.Contains(opcode)) + Logger.Error($"No handler found for {packetName}({opcode})"); + + //if (ConfigManager.Config.ServerOption.AutoSendResponseWhenNoHandler) + //{ + // await SendDummy(packetName); + //} + + } + + private async Task SendDummy(string packetName) + { + var respName = packetName.Replace("Req", "Rsp"); // Get the response packet name + if (respName == packetName) return; // do not send rsp when resp name = recv name + var respOpcode = LogMap.FirstOrDefault(x => x.Value == respName).Key; // Get the response opcode + + // Send Rsp + await SendPacket(respOpcode); + } +} \ No newline at end of file diff --git a/GameServer/Server/Listener.cs b/GameServer/Server/Listener.cs new file mode 100644 index 0000000..59f00aa --- /dev/null +++ b/GameServer/Server/Listener.cs @@ -0,0 +1,13 @@ +using MikuSB.TcpSharp; + +namespace MikuSB.GameServer.Server; + +public class Listener : SocketListener +{ + public static Connection? GetActiveConnection(int uid) + { + var con = Connections.Values.FirstOrDefault(c => + (c as Connection)?.Player?.Uid == uid && c.State == SessionStateEnum.ACTIVE) as Connection; + return con; + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Handler.cs b/GameServer/Server/Packet/Handler.cs new file mode 100644 index 0000000..0f8dc58 --- /dev/null +++ b/GameServer/Server/Packet/Handler.cs @@ -0,0 +1,6 @@ +namespace MikuSB.GameServer.Server.Packet; + +public abstract class Handler +{ + public abstract Task OnHandle(Connection connection, byte[] data, ushort SeqNo = 0); +} \ No newline at end of file diff --git a/GameServer/Server/Packet/HandlerManager.cs b/GameServer/Server/Packet/HandlerManager.cs new file mode 100644 index 0000000..f76600b --- /dev/null +++ b/GameServer/Server/Packet/HandlerManager.cs @@ -0,0 +1,31 @@ +using System.Reflection; + +namespace MikuSB.GameServer.Server.Packet; + +public static class HandlerManager +{ + public static Dictionary handlers = []; + + public static void Init() + { + var classes = Assembly.GetExecutingAssembly().GetTypes(); // Get all classes in the assembly + foreach (var cls in classes) + { + var attribute = (Opcode?)Attribute.GetCustomAttribute(cls, typeof(Opcode)); + + if (attribute != null) handlers.Add(attribute.CmdId, (Handler)Activator.CreateInstance(cls)!); + } + } + + public static Handler? GetHandler(int cmdId) + { + try + { + return handlers[cmdId]; + } + catch + { + return null; + } + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Opcode.cs b/GameServer/Server/Packet/Opcode.cs new file mode 100644 index 0000000..00b91eb --- /dev/null +++ b/GameServer/Server/Packet/Opcode.cs @@ -0,0 +1,7 @@ +namespace MikuSB.GameServer.Server.Packet; + +[AttributeUsage(AttributeTargets.Class)] +public class Opcode(int cmdId) : Attribute +{ + public int CmdId = cmdId; +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Login/HandlerReqLogin.cs b/GameServer/Server/Packet/Recv/Login/HandlerReqLogin.cs new file mode 100644 index 0000000..723d75e --- /dev/null +++ b/GameServer/Server/Packet/Recv/Login/HandlerReqLogin.cs @@ -0,0 +1,50 @@ +using MikuSB.Data; +using MikuSB.Database; +using MikuSB.Database.Account; +using MikuSB.Database.Player; +using MikuSB.GameServer.Game.Player; +using MikuSB.GameServer.Server.Packet.Send.Login; +using MikuSB.Proto; +using MikuSB.TcpSharp; +using MikuSB.Util; + +namespace MikuSB.GameServer.Server.Packet.Recv.Login; + +[Opcode(CmdIds.ReqLogin)] +public class HandlerReqLogin : Handler +{ + public override async Task OnHandle(Connection connection, byte[] data, ushort seqNo) + { + var req = ReqLogin.Parser.ParseFrom(data); + var account = AccountData.GetAccountByUid(1); + if (account == null) + { + AccountData.CreateAccount("MIKU", 0, ""); + account = AccountData.GetAccountByUid(1); + if (account == null) + { + await connection.SendPacket(CmdIds.NtfLogout); + return; + } + } + if (!ResourceManager.IsLoaded) + // resource manager not loaded, return + return; + var prev = Listener.GetActiveConnection(account.Uid); + if (prev != null) + { + await connection.SendPacket(CmdIds.NtfLogout); + prev.Stop(); + } + + connection.State = SessionStateEnum.WAITING_FOR_LOGIN; + var pd = DatabaseHelper.GetInstance(account.Uid); + connection.Player = pd == null ? new PlayerInstance(account.Uid) : new PlayerInstance(pd); + + connection.DebugFile = Path.Combine(ConfigManager.Config.Path.LogPath, "Debug/", $"{account.Uid}/", + $"Debug-{DateTime.Now:yyyy-MM-dd HH-mm-ss}.log"); + await connection.Player.OnEnterGame(); + connection.Player.Connection = connection; + await connection.SendPacket(new PacketRspLogin(connection.Player!)); + } +} diff --git a/GameServer/Server/Packet/Send/Login/PacketRspLogin.cs b/GameServer/Server/Packet/Send/Login/PacketRspLogin.cs new file mode 100644 index 0000000..4524bba --- /dev/null +++ b/GameServer/Server/Packet/Send/Login/PacketRspLogin.cs @@ -0,0 +1,29 @@ +using MikuSB.GameServer.Game.Player; +using MikuSB.TcpSharp; +using MikuSB.Proto; +using MikuSB.Util.Extensions; + +namespace MikuSB.GameServer.Server.Packet.Send.Login; + +public class PacketRspLogin : BasePacket +{ + public PacketRspLogin(PlayerInstance player) : base(CmdIds.RspLogin) + { + var proto = new RspLogin + { + Timestamp = (uint)Extensions.GetUnixSec(), + WorldChannel = 1, + AreaId = 1, + Data = new Player + { + Pid = (ulong)player.Data.Uid, + Account = player.Data.Name, + Name = player.Data.Name, + Level = 80 + }, + NeedRename = false + }; + + SetData(proto); + } +} diff --git a/MikuSB.sln b/MikuSB.sln new file mode 100644 index 0000000..43ce1f1 --- /dev/null +++ b/MikuSB.sln @@ -0,0 +1,60 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34616.47 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SdkServer", "SdkServer\SdkServer.csproj", "{A84C0D8D-BF2E-449A-A46C-F5BE6FD2F42F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common", "Common\Common.csproj", "{0690883A-D749-42F3-88CB-41D2F627C862}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GameServer", "GameServer\GameServer.csproj", "{8E3A0EA5-F4BC-4478-AEB9-CAAC07F10BD3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0C822679-4BCC-497A-AF15-F441EC750CCE}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MikuSB", "MikuSB\MikuSB.csproj", "{71D8488F-CAED-48EE-BD5C-F325FBAB991F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Proto", "Proto\Proto.csproj", "{8A0ECA1A-167B-4B97-BF79-3665AF654A52}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TcpSharp", "TcpSharp\TcpSharp.csproj", "{CD7EFAA3-C655-40EE-8F6A-A8E2DA3B0FCB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A84C0D8D-BF2E-449A-A46C-F5BE6FD2F42F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A84C0D8D-BF2E-449A-A46C-F5BE6FD2F42F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A84C0D8D-BF2E-449A-A46C-F5BE6FD2F42F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A84C0D8D-BF2E-449A-A46C-F5BE6FD2F42F}.Release|Any CPU.Build.0 = Release|Any CPU + {0690883A-D749-42F3-88CB-41D2F627C862}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0690883A-D749-42F3-88CB-41D2F627C862}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0690883A-D749-42F3-88CB-41D2F627C862}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0690883A-D749-42F3-88CB-41D2F627C862}.Release|Any CPU.Build.0 = Release|Any CPU + {8E3A0EA5-F4BC-4478-AEB9-CAAC07F10BD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E3A0EA5-F4BC-4478-AEB9-CAAC07F10BD3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E3A0EA5-F4BC-4478-AEB9-CAAC07F10BD3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E3A0EA5-F4BC-4478-AEB9-CAAC07F10BD3}.Release|Any CPU.Build.0 = Release|Any CPU + {71D8488F-CAED-48EE-BD5C-F325FBAB991F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71D8488F-CAED-48EE-BD5C-F325FBAB991F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71D8488F-CAED-48EE-BD5C-F325FBAB991F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71D8488F-CAED-48EE-BD5C-F325FBAB991F}.Release|Any CPU.Build.0 = Release|Any CPU + {8A0ECA1A-167B-4B97-BF79-3665AF654A52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A0ECA1A-167B-4B97-BF79-3665AF654A52}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A0ECA1A-167B-4B97-BF79-3665AF654A52}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A0ECA1A-167B-4B97-BF79-3665AF654A52}.Release|Any CPU.Build.0 = Release|Any CPU + {CD7EFAA3-C655-40EE-8F6A-A8E2DA3B0FCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CD7EFAA3-C655-40EE-8F6A-A8E2DA3B0FCB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CD7EFAA3-C655-40EE-8F6A-A8E2DA3B0FCB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CD7EFAA3-C655-40EE-8F6A-A8E2DA3B0FCB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {05A94C2B-B569-45D2-AB39-3F26D02E421A} + EndGlobalSection +EndGlobal diff --git a/MikuSB/MikuSB.csproj b/MikuSB/MikuSB.csproj new file mode 100644 index 0000000..c438f00 --- /dev/null +++ b/MikuSB/MikuSB.csproj @@ -0,0 +1,21 @@ + + + + Exe + net9.0 + enable + enable + false + MikuSB.MikuSB + MikuSB + Source\Kiana.ico + false + + + + + + + + + diff --git a/MikuSB/Program/LoaderManager.cs b/MikuSB/Program/LoaderManager.cs new file mode 100644 index 0000000..f0ac644 --- /dev/null +++ b/MikuSB/Program/LoaderManager.cs @@ -0,0 +1,178 @@ +using Microsoft.AspNetCore.Components; +using MikuSB.Data; +using MikuSB.Database; +using MikuSB.GameServer.Command; +using MikuSB.GameServer.Server; +using MikuSB.GameServer.Server.Packet; +using MikuSB.Internationalization; +using MikuSB.MikuSB.Tool; +using MikuSB.Proto; +using MikuSB.SdkServer; +using MikuSB.TcpSharp; +using MikuSB.Util; +using MikuSB.Util.Security; +using System.Reflection; + +namespace MikuSB.MikuSB.Program; + +public class LoaderManager : MikuSB +{ + public static void InitConfig() + { + // Initialize log + var counter = 0; + FileInfo file; + while (true) + { + file = new FileInfo(ConfigManager.Config.Path.LogPath + $"/{DateTime.Now:yyyy-MM-dd}-{++counter}.log"); + if (file is not { Exists: false, Directory: not null }) continue; + file.Directory.Create(); + break; + } + Logger.SetLogFile(file); + + // Init all directories + try + { + ConfigManager.InitDirectories(); + } + catch (Exception e) + { + Logger.Error(I18NManager.Translate("Server.ServerInfo.FailedToLoadItem", I18NManager.Translate("Word.Config")), e); + Console.ReadLine(); + return; + } + + // Starting the server + Logger.Info(I18NManager.Translate("Server.ServerInfo.StartingServer")); + + // Load the config + Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadingItem", I18NManager.Translate("Word.Config"))); + try + { + ConfigManager.LoadConfig(); + } + catch (Exception e) + { + Logger.Error( + I18NManager.Translate("Server.ServerInfo.FailedToLoadItem", I18NManager.Translate("Word.Config")), e); + Console.ReadLine(); + return; + } + + // Load the language + Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadingItem", I18NManager.Translate("Word.Language"))); + try + { + I18NManager.LoadLanguage(); + } + catch (Exception e) + { + Logger.Error( + I18NManager.Translate("Server.ServerInfo.FailedToLoadItem", I18NManager.Translate("Word.Language")), e); + Console.ReadLine(); + return; + } + } + + public static void InitDatabase() + { + // Initialize the database + try + { + _ = Task.Run(DatabaseHelper.Initialize); // do not wait + + while (!DatabaseHelper.LoadAccount) Thread.Sleep(100); + + Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadedItem", + I18NManager.Translate("Word.DatabaseAccount"))); + } + catch (Exception e) + { + Logger.Error( + I18NManager.Translate("Server.ServerInfo.FailedToLoadItem", I18NManager.Translate("Word.Database")), e); + Console.ReadLine(); + return; + } + } + + public static async Task InitSdkServer() + { + SdkServer.SdkServer.Start([]); + Logger.Info(I18NManager.Translate("Server.ServerInfo.ServerRunning", I18NManager.Translate("Word.Dispatch"), + ConfigManager.Config.HttpServer.GetDisplayAddress())); + + //KcpListener.BaseConnection = typeof(Connection); + //KcpListener.StartListener(); + SocketListener.BaseConnection = typeof(Connection); + SocketListener.StartListener(); + + await Task.CompletedTask; + } + + public static void InitPacket() + { + // get opcode from CmdIds + var opcodes = typeof(CmdIds).GetFields().Where(x => x.FieldType == typeof(int)).ToList(); + foreach (var opcode in opcodes) + { + var name = opcode.Name; + var value = (int)opcode.GetValue(null)!; + SocketConnection.LogMap.TryAdd(value, name); + } + + HandlerManager.Init(); + } + + public static async Task InitResource() + { + // Init custom files + Logger.Info(I18NManager.Translate("Server.ServerInfo.GeneratingItem", I18NManager.Translate("Word.CustomData"))); + try + { + await AssemblyGenerater.LoadCustomData(Assembly.GetExecutingAssembly()); + } + catch (Exception e) + { + Logger.Error( + I18NManager.Translate("Server.ServerInfo.FailedToLoadItem", I18NManager.Translate("Word.CustomData")), e); + Console.ReadLine(); + return; + } + + // Load the game data + try + { + Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadingItem", I18NManager.Translate("Word.GameData"))); + ResourceManager.LoadGameData(); + } + catch (Exception e) + { + Logger.Error( + I18NManager.Translate("Server.ServerInfo.FailedToLoadItem", I18NManager.Translate("Word.GameData")), e); + Console.ReadLine(); + return; + } + } + + public static void InitCommand() + { + // Register the command handlers + try + { + CommandManager.RegisterCommands(); + } + catch (Exception e) + { + Logger.Error( + I18NManager.Translate("Server.ServerInfo.FailedToInitializeItem", + I18NManager.Translate("Word.Command")), e); + Console.ReadLine(); + return; + } + IConsole.OnConsoleExcuteCommand += CommandExecutor.ConsoleExcuteCommand; + CommandExecutor.OnRunCommand += (sender, e) => { CommandManager.HandleCommand(e, sender); }; + + IConsole.ListenConsole(); + } +} \ No newline at end of file diff --git a/MikuSB/Program/MikuSB.cs b/MikuSB/Program/MikuSB.cs new file mode 100644 index 0000000..dd39b04 --- /dev/null +++ b/MikuSB/Program/MikuSB.cs @@ -0,0 +1,90 @@ +using MikuSB.Data; +using MikuSB.Database; +using MikuSB.MikuSB.Tool; +using MikuSB.GameServer.Command; +using MikuSB.GameServer.Server; +using MikuSB.Internationalization; +using MikuSB.TcpSharp; +using MikuSB.Util; +using System.Globalization; + +namespace MikuSB.MikuSB.Program; + +public class MikuSB +{ + public static readonly Logger Logger = new("MikuSB"); + public static readonly DatabaseHelper DatabaseHelper = new(); + public static readonly Listener Listener = new(); + public static readonly CommandManager CommandManager = new(); + + public static async Task Main() + { + var time = DateTime.Now; + RegisterExitEvent(); + IConsole.InitConsole(); + LoaderManager.InitConfig(); + await LoaderManager.InitSdkServer(); + LoaderManager.InitPacket(); + + LoaderManager.InitDatabase(); + if (!DatabaseHelper.LoadAllData) + { + var t = Task.Run(() => + { + while (!DatabaseHelper.LoadAllData) // wait for all data to be loaded + Thread.Sleep(100); + }); + + await t.WaitAsync(new CancellationToken()); + + Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadedItem", I18NManager.Translate("Word.Database"))); + } + + Logger.Warn(I18NManager.Translate("Server.ServerInfo.WaitForAllDone")); + + await LoaderManager.InitResource(); + ResourceManager.IsLoaded = true; + + HandbookGenerator.GenerateAll(); + LoaderManager.InitCommand(); + + var elapsed = DateTime.Now - time; + Logger.Info(I18NManager.Translate("Server.ServerInfo.ServerStarted", + Math.Round(elapsed.TotalSeconds, 2).ToString(CultureInfo.InvariantCulture))); + } + + # region Exit + + private static void RegisterExitEvent() + { + AppDomain.CurrentDomain.ProcessExit += (_, _) => + { + Logger.Info(I18NManager.Translate("Server.ServerInfo.Shutdown")); + ProcessExit(); + }; + AppDomain.CurrentDomain.UnhandledException += (obj, arg) => + { + Logger.Error(I18NManager.Translate("Server.ServerInfo.UnhandledException", obj.GetType().Name), + (Exception)arg.ExceptionObject); + Logger.Info(I18NManager.Translate("Server.ServerInfo.Shutdown")); + ProcessExit(); + Environment.Exit(1); + }; + + Console.CancelKeyPress += (_, eventArgs) => + { + Logger.Info(I18NManager.Translate("Server.ServerInfo.CancelKeyPressed")); + eventArgs.Cancel = true; + Environment.Exit(0); + }; + } + + private static void ProcessExit() + { + SocketListener.Connections.Values.ToList().ForEach(x => x.Stop(true)); + DatabaseHelper.SaveThread?.Interrupt(); + DatabaseHelper.SaveDatabase(); + } + + # endregion +} \ No newline at end of file diff --git a/MikuSB/Properties/PublishProfiles/MikuSB-Win64-Debug.pubxml b/MikuSB/Properties/PublishProfiles/MikuSB-Win64-Debug.pubxml new file mode 100644 index 0000000..b46a1e0 --- /dev/null +++ b/MikuSB/Properties/PublishProfiles/MikuSB-Win64-Debug.pubxml @@ -0,0 +1,18 @@ + + + + + Debug + Any CPU + bin\MikuSB-Win64-Debug + FileSystem + <_TargetId>Folder + net9.0 + win-x64 + false + false + false + + \ No newline at end of file diff --git a/MikuSB/Properties/PublishProfiles/MikuSB-Win64-MultiFile.pubxml b/MikuSB/Properties/PublishProfiles/MikuSB-Win64-MultiFile.pubxml new file mode 100644 index 0000000..3ca89f3 --- /dev/null +++ b/MikuSB/Properties/PublishProfiles/MikuSB-Win64-MultiFile.pubxml @@ -0,0 +1,19 @@ + + + + + Release + Any CPU + bin\MikuSB-MultiFile\ + FileSystem + <_TargetId>Folder + net9.0 + win-x64 + true + false + true + false + + \ No newline at end of file diff --git a/MikuSB/Properties/PublishProfiles/MikuSB-Win64-OneFile.pubxml b/MikuSB/Properties/PublishProfiles/MikuSB-Win64-OneFile.pubxml new file mode 100644 index 0000000..9ac6ab9 --- /dev/null +++ b/MikuSB/Properties/PublishProfiles/MikuSB-Win64-OneFile.pubxml @@ -0,0 +1,19 @@ + + + + + Release + Any CPU + bin\MikuSB-OneFile\ + FileSystem + <_TargetId>Folder + net9.0 + win-x64 + true + true + true + false + + \ No newline at end of file diff --git a/MikuSB/Source/Kiana.ico b/MikuSB/Source/Kiana.ico new file mode 100644 index 0000000000000000000000000000000000000000..c518cc2b66782dd590fcfad47bd6504e3f49a29e GIT binary patch literal 432254 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 z zG3SzlmJXGSBbC$OsDFL}8m}qKsU27P*F6-mmJAXjD-*Q%0MS z=k^-W1{UyqA_bRQckuG=19(b4fjjqbpmhLCqvRh4r?D}+hOUMVm{9ZCSx=2UZS%?u z^_X1yY4Y7$j2VQr7r48mfHb5fR$_{=%g^<@SR>CjyYY_v;V)RBFIr$8DeS`n?}YFO z_hOFxUOBOe{#x#5a|gAE(-@;BR)}@#Q?t+*os2HlptL1sqJ>YZ@R^W-I@U$hgeIUo zFbWwC)EsagUeeD+Hg%DDjm7arER8Q=WpasUWQ01&4y=*S67;rXoJW6Uorq^$<9*#E zudpyZjaKGCZss2FerkaMhX&NMmSK!s>BQ_7`i7RdAM``yL4`X#!#ICx zQsOFzb$cT*I1LljjdxbHqrJ2ft%b}9sTY}Ij6XihSZ(%zci}w-dwIt==B2rL3^le9 z18SsA&%!vd)YkkG1e;mH`rZR-K5Nu|&H!IZI7kwrI>t zm2m#&$H?0fdgBJvpFV*m*WC09 zIYs(lBWg&^uil34PiM)axQg~*f!vj74*Po?&ngzFqs#Njr?#pZE98&_u5@=}Tcq!e zuymw$vJ7v@uL@^pjj`=J@@f;zF;?bI!uv69H9<=hTjL{tVnqE#W|aWl0UH&BYt!SHE)0!lxO>Zgie=VBBcR_;$3b zhwtA<3v-2{&@zM@#i6ULgEp9ZL7VKVq=^@@swjwzLz<^AIrCWZdOJK%yd$*#Lfz6P ze^%gse-SW3;-13VzyH9ToOflIK6#Sb!`{X&=ClLc-+A7*Io_>$k@jk3Wf`+PbA7aJ zx{NR7&z^=RYvt^&GhSn?V|SHt<0a04xQM`yTV9~fnd3LRcnkWcE{o#$p|(yD<{E36 z!dkg(>Zhl$v$RD{$z8OUBZC9Vc&30Y3y**c z3j49mJ$+A1RM-{>p8eoo4RIyvUSsrv900dcAFh=uB2x(?No zXQ|sHpD{3m16X)gz)o;IrG3yL1hye<`zA%0}Vw%EUSd!;9KYy9Jf(y{%7_?53^Cq`1=*)Gv zzJ|>A;vJGj8ke#5Vd}Wkh!K?_)W#YX#6h$C1E6>9I^@WGCcXB-47sir^8Vg7_V9Fg zA*%iRW1aWO$LwwI;DBQkaRS_neSR(I_66S~XaNL%UeG11@r-2$2Sfe4Z>d4Jz;RxN z88rwNTytxV--eGV|4taYDdT_LbIbv$4^TOA`d{?{>eL9Ra~=gDg^Z)-$s;tAPs&2< zixf1{pN#i(P&eXE&HFJCw{FFFcAR-iKXJVX4{zAH%NFBj=Jg^`q?48<=EbT_#pZe&iL~Dyhxw(`=9?T;w%nm zqc-RZvx34nj$_nX9D&Y>W6(YJ6ZEN#&_8|xdek5YHIw>h&O@Iy3g&|CIZqwx>9e9a z7*HP}=tNYHebs0PS`9%XA!xL+gK`jI8BhIwCToLPH>?-Hy#6-xrEhuuk3sAo{{bGX zq1zDycdl{CA5o-ZRlHf=qm(n?*nb>=-?pgsPRbfV0{K{ zqoCQhzRx;52|3n$D8Nlh6>d*es2`Gp?R`n&8`r7#I0fA!oY&D4ytAyc;2F{v>0G$( ztmhCk9)iwfkXZfP2(i)dVgwp-OiKcmDxSTACssF*`BA zJ)0tqxPl#GiEF%zf-Y;3;}-n-_U@(#2i&0kq@DR%X>2-U{4|7F1W{`djs&|HM3}H1 zOve>|8g}p}4*159S~P1Pq`F2S%PSQbFB1`K;mi6c9T-ukq;`aR#z#ZYhX{K>UPjml zLDRlO+^#XBiFKr`X||>|f^&#-Orvfj8PU`!1epZG;;}Xy6twYy=Q`TQpS-%KXq}gY zbHaNn#23QZ7j#SSKCr%LVvKob5jEGD$n?%cf^#xr?2-{;8bO_~D_o?_MOdKGm3zdL zu0Z?LN!I!BOr1Q%@zRGql0|?X`FD?G6o-}|&5c}_tS;-guT$T067q+SvNq>30&Sfb zcWzK;NiNMVAF1w{h_Xpw4w}w*DTVWi7imu795WF2f?8(QX--d0BHGU%jvAVX@eM$I zaS7|~$nW;{u`YTXwWWn9PRK#BXDYQQ)Sd7?33`-ZlZdZc6jRnon8(52;0?SqJm4&2 z4r_5mnBC#oVZ5Nh`=NT|2$X;Pkul71Sn{mJJ4d2Au2D1(7a7*T+><1(c~qoRS*G5w zvvdF%foasx2O{lNKG%wx9oAkYavs7QLd+t`kB702xE}9!SWg=o!8$A{6vrpRNmY$C zlBUF4cz(6@;9_VBZ+#!pIz-vB#?mZ;TAEOfHyDDRMmVQ|)bs>%FG8tH5_C2GdcoAt zIKoa+59W7eXm3sv+ds$pW!jx%hoMPbkC%osa(r`<@I5h4mwx zLl^X(JcZ%I=SW~pPO5tz?{k)DFXFftZ_Hy5Y#u?Zss#fr&8RCbg6(r9`0E5Tt)+O6Fhp|=>N+T*ndYtlzO6FMQC<-cN-D455Sx=Jb zna4dB)+(Ghg^|ckX`DC??K7uo2am#9LWOlfj-p!gKm%8Js9E6p-~UIHQa7?X#k{Xt zh^cysBOb-g|MbtWmeFRdb~yDkdDQ&yJh;+7*ria*m_eR_n$WQkM3Ad3jb@Jno+Ckv z9_td$dz8WYp?r~+r#^x8L&;xtJdLSMU$s05wWx|A{uM_2H%QPF@!xh!r9LYTk+#u@ zV10O$eH@&n^{6d6M*Dq=^3>;LiZh!e6#mvyxxZBx{G zE>Y7w1f}b@asF@r7GmEY=6+CDU@e@hY$VgZ3g;@$HHCMRIwEg><}2hN^4ROZFI$vD z)TcD?vkq0%8wsC+9!Yp=6YEfyL_8_EUNnye{+vjoB7pX-z;Px}A$A3>)j!N-r!zv{I37yq`N zERPy3L8q0kYmY+qBq$(vmP0NiQ_yQE+aM{f8^a3+B5qXR6$Lz5&}|8I8*L+R5tq=7 z!J1aqJQN`(A{m3kG&+fev{6&sPb^iay{%_WY(8Z`_!wQK^Owf`G3HH?wYI{pj| zGuG8vvB!Z8wO>|9Q?)`A`P}3BwZZH-wK42XQI?g5>Vg6|YZy@vXNNb`gaz|cSced< zgS(17V&hp~UbiKhe@id*#?*%inCVx{?z3nO)<674zV#F5Nu3+DVTr5@O5r+Ws#_sh z#hiO@im>Ob^NVTayxA9Fk(|O`HE-kOrKX9YWo8Vpjv)i3mGg|Po2ZRh!rPs<*k(P2 z5L1p3J82_E*FzqzG?sOjX;r8xZbu8TMSq6?guPBfUVJuvbqIo}RSI$S74^d?icLe5 zUnop8jbNx{0yXNt%JTB5_3J=(e2S=6QA1J@;nc)M3TJ>%AoXPdB5hm(3d*U?tRVhS zu}Yp_s4Jnqj5w>X4okZqSx@*oI{SB#7F0%^oUW**F`invI7I{M<*0k*dPFIaCx51e zgs@ub9X=3CC)UGSp;77t#)N0(6QX>J;HOEAV-{=X+XoqwQg6-NP0#{;p~mSK>b8U& zVT!qb1N8~jnN^4kF6Ww7Gsf+uj;;y~1y#iLsGDkG%{jT&-pWQaWfh|;DU11U4swY3 zT56ae+&36I#5gC%Mvl`boOM~k{s*Y?>?>Fi%cpLza!aHQEaDopbbr+c zismoE%|9Twcma_PNyvN=Ez;Q~5Z8!NWd9MabGSUeR@FzUfhjdnYKRM}!1x07f-}VN z=C(z;!4cvK-4lBVVgHeuxH$GQ$Ru{sFPgi6dHkVW6KUVM7Qg)V8+Mrkc2%{q=BJ8J zHTAoN=&d1!OYL1%MiJ_BE6`p}UZ9THTWt%^Oarx(aO9M3BCmLb+QL=jl~SWuvmx3)fg`AI=l?5OCTEz8 z6w5$lIK?4dQ5^~LtS6OGrdCghbCyTAk{S|p4dKl*78l66G}^ym#-hXQK{Uj8VuI(O zv2B&TGy*W*$asgnZ(3>R1fAg@xPy@E{_*^=4rAS{(`JqT0P8_ki79TcQ#-@^@_z4t zzIqe06U5Cs`&paXPHdR#(7<(Io*?Solbiv2YEi>^FLE>UkeD%oaJMXk^2~>lZ}(9q zuN2zAxzC}jkacC%qs072&|1f*d7KaHOzK!CAI3a@eL0e>m|IcHlq#c&BnFb!G^fA(@(X{SJP9#i zp%=%{A8SBfVYs=CxlA#<-6LRVPF!55A@i+7kQ{aMtj+T$KU$jCMtjqQ0&))iue{)B zVGUao3s@49H#0QDGiiCo$I9pmeSvUl4C7jlyH64HT#Y?7d_`K?yrd#5@Lc~a!rJzUeX?iB^dfs{bW(d)i3$3! z9@ZVr_mAQ;@vH-$jjy^l)&cVjh%iTC9e7qgaBaSDjRY>Tn|P>5)5@GPA!QN;#cRke zT0&AnA6`nCu%5^jvB9CZNIdKCcRx`RdLEaECkoH4t9M{e?!%H8gToUgwAkpQ?TrJf zsga4c4T9%GN%EKM+jCz+q_s3BHfO`{gEiFQ$aP4KDr7C_TlPnpr8a1t`T%0jr8BJ4 z^Mmv4$MB+N@U@r$!{v`li`4WS_N_zhZ2Tbuavx zUE$t~G?>)=@tlbCu)>|-*CM;YwfU;Y{79^9S;RM`#MdC6n#`;MYV(TMk)1OSEpZiy z-Muf;m7XLHaFn^{IpQ&w&#>;5x>$emo@0&7%cu!1^a(+=kv0asbWk7SLY?Uwa%~T( z>lK3?>l`gPccHG^?2b4w6lwOFhSxK9ycaKp2H%tC=k}*^tdk6KhHm;X8{%ajJwZPnS zLHqcJj`Kag7yRgZ;-3ffpZo0hG1T5iJuS7sxx|#xn%LK_kbQ85aP7=3*y^(nL3%d* zNH&ah^>9L1gA>&6Ft@GZS#P4&v4uEMc~~Mcq}9-Dt&2WyJyb+Tz>?YlD{6@?sUtAq z*Me46@7i6~cZ)-tU%zonrj7oLZ6OA1SweT-IGWN}ldS9r|7RMg4^3hp0)6%Za%If5 zh}tIhkg4b16b-P}b&7h2U;kR?Ea)BB&n=^X*is*B0coF^+wJ{T$11FW=%Fq6V;%N+ z2lhGt1J1vRJz+A-){&7#n?QWBEh&sOG5WYd{<^2K27T7;f7Y95a{ju=aeqxrlDrPFXai!CTEx$EIcG!meKEN65X$GT zA@XGk>#1s}d-sLCoE`B;TfEZrqHWMdxlbhO-Xy@|zASlLQZRjI?{8-Sc6)Fj_B7Id}RTn8;_t(-`&kMs7XpfmWDnxZ`V2h6V$aRpj4Ip|2$0D zi*Q<0o14hKQkBjY)XB2;O35C&JR{oYS!Z^RwelBO2S=^1A?L11JW-jLkNmaguwng0 zh9C7UNg33u`Qyp?htRwF2qT3xsG@Gm|Dg(PfGV}W?I>?pM`_gp&jK;k{9eQ*Rbz-c zixuja1>_6t&p>Br!jNFx{2zU2G zCTlxN*tcVfy^%s(UPJ4WIA$B@&zbiGJYa9N2db>cPNjC3c~{9CN-CET8d?lv9Vh50 z+EBAug7)q?^p7nw5Bbc`Rq8uf3rm|gzr)&(@nv$x^VC1Ho`F2;{Ng(6BbG%Prnyb> z^GhG8)1v-6g!Zhx9bMtC(B@!?eD=IZW?fKkLlb)v4xqcR0Kr-Yu%m|4&%_Fz)c4pw zroNbd!kv#d`zUx(YaC_Ecd18*fQ{B*e9&zJtBrVj6MsACBS_ zc`Es%lEitQL5{qqBlW@k?CaPzuz|!_>Jl4AP~A>0GrIywnPuo3TcH-2Ip#8ROzIP6 z1bqePKREvmd70Dz6c%B2dX6zcCEDXckm~4!!uT}W)m~(#Wxzw|H^n}m8O7{Nn8rRZ zwEeMe!Eof*JmiTVGFI|rJy|p{cq)gkwesHe?txSvedgO!8;%c*^3XTSH2G= zYJD}YC_?ix>+&u>=g-Jdllp*Ms1d576Y1aQ(41XD?Tiv@#Gk?Pu@)kND3*D2nOt@X_1+Jled!*1BCsaqAHQYa ziIcc>_#&Jx7-HXH0vHUyO12oD{dk7l!DHm;vQF}zIeXnnVznH1ct)M)E0Ww1DTjwB0fAE z?NycRyHZ2^upO^h`{T{ti-D}g3u3Musm)$Hy6g!=eY6qzW9@U))t+Xp!<{F%c0$mI zUg!6cBE7WEdG;7NMZNKlR%g>PV%mZsd)UeyKXT;&#>_26WHk&q-TITVNU4#ntpfWckU}^S(T0hnT z4$X_^E#NKF>)dPR;0c+N#1E)z$t!04%Ydjy*GO+a_3hN5@cj#n6{pDE&Wukm|DiU# zyN!Bc_O2xlZSzPCfy|S`Y5(5PX2fXNAXd~*+5d(<)T>|eNro7}s*KagWS`4IBd zS}UKXPMkeXo*sVyrPI%#eqIK0?6qRYdfgZYFV-_qlf-;QxCeqZ=@oU$Pskr#CBG!} z{SZHS1rq1(;Q5hH|3NvEcLI$OT;b_OwXv`Aq1oLany228!JK zXD5VSGf&y)MUwqqp2C_KiQP@!TlPj3Vss%dU!ev_&@&rSr>S)I3Z9?22pRf`yT`6m zgMJb>ej=}U{{qCQ#khX#3Z7lQFY5bh^h^?u$fw&`d9Z%*cj64}`N+9XuF~%FYhnL8 z`Zmxr_Kq>sd-ihZVP7U{rDoRg%kT6blR^))F}#0A{Rw?QM?)j?>2*>4^Z58U^RG|D z3!NF`kr$+$4xkMTXDz6pQ-4K&XLVg zI!!J2;k&RReqY4i=nG$qn}F3-#%&DEQ~N1p%s1Kg)B{O~u2aeR4J^5ILkMqR%c&&7S}%gI!#q)z-X>m?+9y1}{gUY@uI$z$Yf z8D~8E@h0n@^cfG310`=^aYLM(pAu^*n~C34iF$zuy(~=0gV|nx2-}+yxOwC(j(vX| zC)tDZ64&4+d${O4*M$Xpv?ad|hunQ>6eeV$o4B#7vp@0~9}2qnmS*-KW{puNYh3e_ zE2$|dLOajb4DrWt;&Hit>~#=F?1}n`z5RV^Q{H2ZXRxD@cY&N=2YKUEmscnaOQqj8 zK)RhTN_}JD^Gp+OXb(e)8DD3w&HJ>`k|)^1nY9ICKe0xI{h*~d--rBKl4n7dKI{p5 zeoC`{ro{JG5Wz8)#U#Rn{hv&z>(yWnh9dH<lJ%*uzF#Op3kFSfk3gu#GWOL0UaV zo0=F0Q=>>d{XFC7S;htIDY zU40efx2%`scohxV=S*KzC!kKBXG&d)<3nloewD!8A1_0a^B3p5AF;+kjP(NIhp%uQ zZi#gG55BvE^x$Zud-#YnM<(nMB-97E%PSMpY(k)^4LUON$X9#8K}?$cOYg9k5H;}B z$|!LSWLQI>C8mh@j2Wa6r;E*+A(k_bK))R;~`K zn`?}P$*0pV3N-~Yj1Po&2Li2%X9gXd~>0fKlZ5Q z_}~8Pe-r83dx;|k^9+YFh6#E47%mUkla@LpQLO>@{62e`3N;@R>}~Xz&pobz@clj3 zBHaAbS!5EE4JD>zM1QHvxvO#iP1z@>jT}hwD<3p*5C3=e-aD$xEY0_wb?&-r-8tt@ zpP3HR-IYvLhO!K0&N-DBK_w_kl&As*j36QsCFhJJB@2>+B*_8_0_Iq$tGa5sXL`c9 zbMA9L&x=;~J?H#;|GHCaz3YdTB747kKYQGjS?*3bd*2~8B;s9kSq=9W?~$KR zp5&Hso-4R7*~P<>oO4gouRoNe+y@dCm_;rt1K(Eze(fCeccn%?P?;SqgWaa8QSL-7#(&s z_f%D3aZ<%+*4Y55<-Vtp*q4Yd`zHC?QFI@J-96&Qd$(MLcKl3)+$d}@{2N_EXf??P z==$E$`<#6%V+|)pgqY|C`yj^iq8wVWUFQ9As-y+Sp~ESWS-+etvE-2&U^i6-M@t#Lk89q( zQh@)zi`;z*-XZnTYdr40a|i9hJ_){TX&1=zb(Y;U+W!q&)EE7nHTwqOr@>q1{hEA^$8^me zS@O&2a&XgLsY*n@7@iK>V}-Fc<^;#f?YvSkU9(+$kDf-C6)#Qblxx$n<=RF3kGI>< zO{bxCt~d5%!)V)d9G4QG@4}|kF?-kG-RWK0S5gJ{;8qLyFwTO?q`F5utGNA?Gjo~Y zpWKFqYU|`r#A~~$q1AuFeW+@SJJ|CKaep;HyQ|Uhp0kIihfd%yUGbu28FpEl@oX#d{%FeLA0OxUpw5l>qpPv3%DmUe>p|W=CQ9|xm#xc*U7Yfk5s4K zkp7}tG;}et`2A1Cf_-Tjth3u0Xkyt%?%!!C2NteIn;lK8Dn+I)eefB%v7khZ-t9np|h+mln0}_%*y$HPV*N6jM!|>TS;|;(1gPbFeXpZOC znmpteGv-Tch#n`n*Pv>uGEpeVFCBdDCTyVUbm&6`E zBGFEslHwPD25htVt=%Ng*rWE9R!GWzd$~-0AfNly6?n01@XXEm^$c;@VCr?~S}*E3vPYv%TI;_dkSB_3giim!r2_u*8=Y9Kl&@nly9HeT)YA z33<>{+;3QutIP-pmcBckXkEhCgDo)hlVa`6P!&UA1@Ld>Z<&9EV_rT!6wsBY3Got&I@wHmpukU%_;WtJ; zZ-jmreLc>^yZ;O?4Sh8FAC#yn!f{Q303ayheNt(+v^e|*t0 zIkj}9_;~tCeK8DK{0r*M&^~z7HzEn4ak7;>*cJGr&xn-+_&xeL>vxyKwBkNrJs-*u z8$si!{u@1KR9}Z7k7lT?6|Nnc><7)g+*c3bvElCHiOzjE!qh0}(>{4fUoaLl7dWm< zc{LS9N0d(0bt!3J-=5q(J&Ui^ws zn3}9fH(|F_vL4;yDY+aW$;VEK-;O=Bf0cOC?(^$m>r;!wlRaBsOCNPp24T44Kf$Ln z+=DOW!6S(dj1v2OcGAEd?4zL(7%OP?(fHm&AJ&4$N#Dm~?(}qxf5p!;fIijNIfxoJ zH{cSrNZ-|Z`9tlG^0*PN4)-_1&wg*r?=jjp^6LAyZT_<+!+8J1K;6%E4nCIat+?>>!ooQ{1Bf?1?QlmzzAtb&N4hcm7GN@@G0G7Jum0$^6)tJC!9z8!lm$1 zgp{z4xXBqHi*@?)4zzOkP%fD4U|uXm>po<7m6T6!w7(0_&cFk>zOb>mkADXHQ)^)K zamG~7m3o_1ul@`@sCs79Z>XI4_PPeSl~siAf_)yk+na~t<#pr}X$~c3_6{5Gzi;Rz z_vlaXEIl*oE{y1208NJG`qU$*93A7#k5@}QLw8|2j__}HRyUB>Vedn~YK@oz;zA4e zD7E-xbq($;EN5*ll6uwJuz&8ZYl6Q9-)?AB23eO{i_5ul%ab<7Qf+dkB(gr7S-lAl z;Buqw!}Ni+{immQkazw-o{(3$+lV&2q*7W7(SZ~*whL~eTfc9-f7P%n>ri7~cWb-h zAG?up6aU#IIcDM_-s=yGx22bq6~R7cd^J=wQzzp#pM6qUM6D&xcK6XC-KFilm6o!wMe8(NeL0@|(TD@dxNyT%`)5%<{=FAc4azsw8-;C~dIUd^e z;<950Yzzl!L<6pOs=60@@d$lEXD{Qtn|VxpW1NILxc#}!+Wu}G|HI_5Mi}Sa`0Fb0 zwN*A0;iSyP2VGF@1f39AGzL6?mL=0@M58*Mql5?xa;Ix zSi!q%NX(S>ykhtE5c>fmIn{|6pVdk;E~9+*OC z-ifZZ;!je(=rHr|K|c&_a?I-Kx=+4cwe0%6^!upqPrairi3j&P`lKME zPD-wo5m(92vIlNv4eO!L9??I~9`pAe;FlO8A&J!WFJmme?Fvm-x6Qv|Zx$$}8xNRr7gc)MB2CmAwwX%2J8nL7w9A_!V5ns+{ zpObgMD?(n+k-Lq<3sw-rdJV71&;a!;7zgTGd`V0l`y2M^%A;$ntmXed z8220TZ#|)&)oXILeeLLc&=Te0<#b)UUov)wNsdJT9FUt*bOY}*jLRX$ntDsd*iY7E zU6+=kG8yEI(Z+c=JNT;XVgF{an0hYD$-gY+zMB2M;mt!wZ%^B8xVt^{Df5Ed{(kb! zz7~h%>o;E;wX}MPi7)thUVr)8&>iXhMOjR#MA*kl(4F=o8q} zUp;wROkn|Ny$=`O_judi`r`OK2Xam}GZ+5U2g-2GxEw-`^bc@xVR;c#Mh9pgaiJpq zri9zXN*KAZ+udy4G*%@i1t7mS#yUYc+5pxo!a;Yaj3 z8VA+?r=QB*)i$edLj8Y+fAQrj`r=D+bzM@F(=4^rglenrmBuPOjfHn48vT9Lxpc|G zXVu6WqvyxxuV1kaG@@arZV>vP-FVj5Ec#qF@OjA@_SAg%2Y6bv-ii7G)f4D4YrGF! zW~nc5!MI10IiMavjRj?BB)(i9@^F${Zh&hpc^rj6c`) zCa_hfbBB6R!rhPK^@qD??_!*D4Aw3=3H9ZuU+^p5v)0j4|BLE>w6@hx9x(dh>)(AN zZLB*DrA@3u=s(c?)fd;I>%d1$OdRcH-@%wuA9Wj?F4d5Z5nGi1qjx)L0im$I=Tkq5 znk6&It+RJKN_=zUp57gP!jaG6ooGKW?u|ase&E-(wB24G?zs-l;GP$cURe@-K(1VU zXfa2UCCVv5GW@f7_qzVwlSh0WLl@_{eGl(J_x4XYe>}u{^ce4L1G?50WB!lfk?H3i zO6P&r*%@FD@D2T-`mOKL_Nb0W^^p3gKUZU@@`#_mct#w4!TrQAn!jG^s^B|h%xKJS zXHD0dE-!fZz3j6LUJ&bk;wfL|AT>Aa9gW!UK#oJ}&^YNk$CvAdKh%YJ?>GZ??+o}p zwAGPM2W>ZTzwS@negHMurLc~Yq7~j3!YRvI&_WQaQR;{0*fu!x)e&tAN9W|}tqdqjl zM@JtiPe}iqa{qMgRA#ouTIFVHuHy;!Hy!MGo;^qVl5vf?lqJm7r__+3R#8-}_?wy2 zepn{VbywQ0?_9m5hc%x)9iJVne|y@h-d=69so&I;aeZcol|(wnOT1Sy z-g(9#KYuslG2kHe(enbCByR{Gg@P|Jb8c&@M`sXw@ z8hydnTHDR=veG}gpU}S0GiEP-z&=9TQMIt&F(0%)bWg6I%Eeai`v7Z*vLDn7tG_l_?(D7l`MrC@29J%@tNP%5 zqs@-^FAsBnYR^4_3pq}G|3~q~8op}UfA~}SfKR*6#{IG8v|W9Hd#3Qo7*=nizE=zU zzItyJjQ{NpKIO;v;N76ti}OsEtE>f;*DK{(7<_26z2xB0x_WrB7EC}V08bB3adknF zQRkz(wq8<#Ld8qt60DW3w)UUae66GLoc*@?;nhd0^}@7Ie)!=BV_vAQe1tjofHAN0 zM)&0-?5ni?qWTX9Xn&xSvzUMM0X+KIc$e`6^Ijdu`Kd415kIpd?_bA(dV~+5JJA29 z`#|+{?w`TD{bU;NY%1->Cp>Z7Tf2wc>R$D#(hsTU)0y)y6Z+9?#T3DpPa__1H+SSJ z^Sp^U*bkpK8@NQI*7j>G&@t0m zQ;)X)2tV@_yoxjM98(*Om}keQzIWxs+tGIQP}?)kZ5i)2d>+((q3zDZDLd8m;aRq% z57eK$e=6htW5xn));aL*vjjFse;f0xqhI1qtCuwao^6WZZ4Bq@bNf!CjhijoC%#AR zpO5i5e=NH{LD%$me=A{U&lx;~&W1+mZoG{~y+&&2i-fEAE;*YjhrJZ9Y)5?~{?tBV z8eHi14#Qvjns@xo+qFUaM)$`9tf!5|)D>{^WWMt6On47J@&x)}65N1~WbcFzVbV{Q z1N4jeBz|uK^KmjhcK!c+S}@ja`B}&L1loT5+0221#0)EKKfgAcqCV|Ex4$T{&}jc4 zI)e!O5W$Bd@PVembWg_bj9(L<@I@;hv0#6(?W6Z($HWiCgnw_*&wn8?cx^Qgs@%Z= z?zxn6-^W@t&_KQdJ$ykNEID*Z30H#14WRR6t}7!+eZ}w2209nCwp)@{zvB_H zLo;w>jfr?uyZIuz#lVB7Sf?&X7#h?F7e8t+T#_hfUlS&CloK7=5uZf$6OE#CqW4i-;aQ5BXOpUW&H!OY0`sg;i2G{l!J=ig`+TJw;*6v9zbvMH z%$o4Nn1A>I>liU&8k$0M2%cNaB$zRgjc27irb-Gz3Sd86q5h#c+AjR%+?kltA7&Z{ zmF%!3|h0*R%eDops^1}>IdNBPE>=c7$>n#({DEgx06N!hS*PEJV0#4T)%Ku(lDqb#MQTgm_ao zVdY9WdC&%pL@2%tcj}WTNn&_}WGBM`D=ihDWA3p0PfDa`ENw)CK)XYnB8WSYv_F#e zqCL!V{wjp2#3XG-5%J>+o%_@g?k=2%^xcdW4u;}Iqlcl z5&9g)BOQJu6wh{nWp9gXd*E&)nHiY`wy_Ep0^$CI}{8H=o&mV_W#xO5_|d_-u+E*lZg9?sp7DH zgPep7ana$hY#;`^aGp5h=pv^NoiMlq$}NcTq~3#b1nmnJ--DOM*V-RH|7E$z8N~0< z73LfB-{uPa5XirWzn?w{unnh1Ww3bfJdU=+T8?ez?tkHWgHK?|cu;S@uG9PBgY5Zu zvbYhqd@N2&hF_NC`d>pEZ6*m{n-NPpm=yDF7 zPX<3D{&?o4j)NG^O1eIV^ZvCT&Rbogwr)AOZ|cLd*0tBypm9d?vSkPF&`x}9ZKwyl z58dA~IeoxIV!V^kiN@jo#~07{2yw#0=MpMWp3z3^$@I%KxDeNgWkpv@q$C25`rD@{ ztdtm6EV%*su>Le=zy(N{Yw9V-x4YuW zPm{usLbSdMnA^cJ>#zS>;{C$N?~%W&#w(ZANPVd>F<-t?KKW07CHt1F6n_V_#Ai}D zgJ0wCC`%^E=!4kf^n)91I1-6RsTPkxFKgjF_Kse1evc*p)&*)B?IH)B1D`twt>aN* zDY1kCRXAtsX|9k3A3!6b{E4EVBGpsyVxfiZ3RC66U;?o zMzkj^46j(uEBK|o8LRwWsA~i@ST_;}KA_zn8|~LvxtG1#sh!UJOrCHOT=9dHlE*nK zj;l@JB>W7W=BKp%4tc*P?B!rB*P}CPyC?fsZJ-bSlg#`1&(Vb}7H{;jG5p+#XW&1a zNQYsed&M+~VU0{Umx>;_#>gG!qPtGIbP0`ejoix2K`(|b4(&kniD=0S$)`qVvC-aJ z@zg+x!zUdx&LSv{8D|l^;}Mj{(Jygu3Fr%5n~EY!@jetv&gDGbJ$n@T;L713xp>Hr zIpPbK{S-6C#PO{*;%iR5w7s6}uR@4%X>tL-`?6pETIR5p zo5D|QEro~Jj>o6CRYtn0&vj=AmWZ{?|M@Rj@Bdal|Ji><2Rjo!H-2fjCCRYs<|F1a_nO1I+MJ>*p z18^dkJJ&3aNgDitw70B?=rEY({pwj5_bnLub=7gB|4|Kl4L;KFs7CJpD&-FO?9`xW zYU30UZ<<*{3Z=df9~&A{Wkyt`T;n_%ArWY8&m3}<3icxbYwYC&8b?QXK`B?0jQiae zqmQ|BqTVcV#vgvbjnr&;!#-O*b0hc3<&sa(yMt}-OO>&{P$mA7 z?D%=oxHs%=?=tSAddVYy#o0^wjERZKa@Y=Ew6%l5Y1SNDK~@gj^lZsXNRs2|`-9;t z1is}X#LK2{i8akVSEe^zod`opCmI| zI17g)j_;eW*M>1eod(W!vH14&2_-&9hg6~k!|#W-uNzIT_L26LK6*!Rx=|)Ueph72mR(|PVJnuV2PDitfSe@S#OP%5 zy+v}>@3L5}-62;ks8w!wiN^U80gNS|ozw|BUn2P>&kY7eZqb-j(m$$ORt=c;k7_+0 zyr9;{mp^jn|2xTytCeI^mvjQtPvyMX7DKRQfoaPy`$3n%k9cK?t=2De_bx= zv88e~q>Or5_~+qW#s_D!UKbiQvU{p;OM6)@^+H&y;<%r>5+Nx8;gTK{BfdvYi52WH zkHggcWv*p$uhLpyFTUt)+^y*wIA&V+=?Y`Vk2&TK@8q1tS>EH2L{a z5_&OTI`6~kKtI!Y|Czz-=p2SyGV&b%=$8`U87Fb>UXl}=11F$%{A^C{MRQb!*P7g< zk)QoNwZp&ro}5OX!PqWHET&G^4atrvWd4=N?ZQfO-j&oULvxgVU78ASac@KIuTnG( z{C+)SupF&IHQK=Juvqc1b|hDJM}DCHpHiDO#y^;PxOozE5Pk=|6@%ZxTsUU}bHh8A zK7rkmKPq{Qo!p{_)WdmZ=)QIA48UxuZF?yh_`fe}t`7El+KCuR+PzO=cJ7n#?R(*P z?1AM#?Isi07M6Hd?WtP@2QTb=Hhv1mPd_XV>bG>D!)eDyqWyET?xFbDaBd~ndowMa zcIF!_RLxzyyUTdTv(WoK?WZ6A@DJRPKBV?1`Wo`Zwdgx4(a7E*R`uMzgYKafuNwD! z=ti1hgLPCkNE;fF#*%V$?cDXF4ZoRw&A5*{d-%9qy%Huh-Qw@`Vb7^r|1Zu;XfviTlzp^uN1;s_mL!b z7#BycN;3Tt$9>*alid<(vP%NCP%CB&{H$GjB-D($f@`-+=(!BMwy-W>uN-+bgS0jC1@St<}zwwp+BpHrJ8an1eUF*q{b)9CI47S z%o>&;FLFK93<)9z`R=fh%g5s+6AgK8W}{RW;V~!X-M)!msI*qf3u>ecEpmBLr6iyu ziSuxhG`Hgt?dS&wBnS=&_ZQpDB+=5Gd9{=IgnyRj!WI*`_yyk1mDHLbH`LAg-U$Pw ztD8RRMzhoT5Dm|l@QaG6%L;FeJm8(;n{ZolVcgy^bSIkM{AuiH-|(o%U-c)W4`7qW zqSuQ~;lAS@{0yy6KmPxfC1U(N`bgOz-_mCzXpU~90c*ci!SoCx~N-~;ff%#3&as()W=UulfjdnRRvG!&Jy z4ieMpBhArAT&Au(^Cv024_3oNcrlgmL!9sg+es4l>zaG^IdNJRaGyS5!W5azJ@M2j zv*2mWk%@f$5&DlwAAKel$orMJJ8ag`6qd0m$C%QvA-W(*$n<_uPjZxen- z!7bkX_skoeXRr{yM8h&d4z0NzT_kr<)#&ojE;VFVkZ*-Q!=F`#z&oype(L?o@4n@e z-yi2njI%{X?+r@>*NIb zTlZzF4WE+p($$7$|LQ>)oU7NvT1vyq`-(mAeRvya5AQHH3Y4weCueuNqt{(8K5KW$ zxkXzg!1*#P1Q^{Zj3>M@V=&G8JM_=OAWXh4_00X=yXYU$+iOktx}r8}^wpx>flEzn z`s!O{42;*|)B11U>X@T{^wF_otcQG#!K!JwRR@nXOCsXiB?+%qM&1|?HB^#%#eRdO zY+bui)~;MFzxvx>$Vc3bO(WMlbJ{GK%f0(2+@HGR&+b6e(Oz0^)HqDBIwY;9tflD! z434Y*)NBnh>fL!QUoFRYT*d789pw4eOSFF~cV=G`6IHLv{WE=3 zlXXYRQ`_$4cKhywQ{qFdXLom%1LI-^gpGB!4F1V z8r_rKWq;Y$^@{qVcnewcwU54{kH+h=>F3+=#HYslcm4Av`vv8=Jm~4At|q>q{zJWk^L=N&PcUm*ZLC@koei3&OIadPM&r?S;HN#-K zr;;ZtCJr~9+%FBjc9I?tLVQ^!u8WpYyJf{Vt7IWH>E`RRoP6*)IY?h!_KISkN>U-E`jFoq+lAk{H_l)NoqweE3%-L@C zxg-}n{ACJy3gxyaN2{4WP+h0>&Rz2HhZE%!?kmj}E|ki1 zE>aR3$Jvd!%K96$#!|BJos~P8OPh~1d5}nPAZ8uraa^!O4$WUAR(ySczOi1oLhP2U zkv-fU_;bJT7!KI|-ZA_vHRM2wsaXRvi+S(8#!3<$PfA@hJPUje#~1F9jHKIAUi*ae z#skiRV^UCl51t^NF81>3zZ~}=a_+&CT*2S#=oped7+J+p`RF7^V7s9AAns_rMg9D# zj~GW6L<-M-rbkA|>$gS}QB0F2Y9&k66f3@=gB7&lrrU>fC%O2@4~p zej(E)PNjc7!RxR=iVs>#p{F%E+g?MD8nMGxGInuqe|*1`owVmn>u%ID)O+In+@I{5 zH5;A-9x1$m%6YLwOSO}`qH_*@a^CK|Sg+WJj?{ws%BJ|A;2*$IIksY_G$rLpSwNKd zZrCf{i%lf@LKb_|=P=fWR?`z6=xpxpA@52GQ9xL7S& zv?0pzD82yJcS|^u+^_AOidKLey}^Nj1!IY(O8dteFX=wU((c*wW!0o5;%V;0_${Dr zO#|BeI!O(VloeAJ!q?a(1EqCR5y-v9+I`};Y=?x}`blB=V^}Cdas!`ZK`BhS{7$$< zx1_idrt1?JlwbZq`bX5y%wDRO`Bm4A_xVS|-`?9zEm5>_1NR@W)}y1MkCYYlhPX2{ zLS0_!g6P>#y;G&NoEiMXrOek?$|9~x=LL5up@vP?Hg`EQ-&FivJq)cv#>wLngWu^q z_5MQETSyYQlxw!;oYfA<(M<=%V}sUWw8vZI!aXC5nB7O@JZsM`#-Zsa=tj6-Hs{{m zdfrmB*K6>3Z=ij`S?8)%i z1;Ggk*hD>L_9EAdIp~bt-UbR)g`3~)b=ykAr9Mi%X#+5dT-&r{-B(K8*^qG{FrU9 zM#-a}rsiYD=}74;ZYSS7&XKXfyS9H8u?L=lJ)Tk9rJG^ttdzA=7s@)A5^E1z z3GO+x&(ude@L0WBJdB@w=CAVQ+R@i;mMv$_T@>eI zCnc14ccZdh5|VS^Y&6O>&ca8yi#kmWKyS@8?{bneaAx%W^;UENoEd5)s1^@DxCMGL zi&@KIH?0@Tc`z05n5|XT%*=7tjK$|WWDjx2V(t#=9B!2LGuBYc&6%8c3c9NSc+ORL z_$}ldK2hfd>%`m6Un0V9$=an`<>U80mSxl%ytv|o4BB_gEvHjb;D85gIh>Pi2V~vM zWwK}1PBC4$P3-UmdRwj&r;XdiYL`8+tDO0y+zs{Dj{DX$#`p2-+#+gbu`f(b=6;Bu z;mu3Drycky$UWI}zFxYJnv9x@NX(JC)zrUND610yLVdfi9Dh<<}vQ&EyR19 zEpv-=Ew$_Q@qn-x}Ky6d2{-hGUZ zOMN=;`Uyr4{gYSvP!f{R!v#g4;iS$HXR?|id;sVehvB1md!Lt`FbT}asqHd@e{F6h z5my4?mXsNcnvuJ`++kfZ>X2R}k9GkzLkKM55O_5q2ToES1nn-GZY%cNW@tF~a(`#J zbd$`QJPVILteWY{s@Vw7d<%WE1$M_;`q4!8&)G?>(k_e*~CPZ+ti2=XG~ zp@F>7IP0g2JTHD4v{ogsu=;yC(AT^)e78w3 z4cu3m$+<5MNx%-RrRpMK|}7;VyZvoIS9`cuy4#o%q1N zC8tXr9p+OXV@5v*9>0ct#lDKQv57NEBmU!(D}Hd3b7X+E?ztarimtO-#|}uA06`W>5Hxny8cEp=dms1Sbq8($u-K=;P02&1ZNO z)@oVwC-S#lNaYJYAviVmD8J* zk3#=&=Mlj@(P_?CHnU(spc`KTD`5jn4CMqV8)G?jNw>lWGJ%Vx^=&s!Uc$IqiAHj* zY{4X`^_}jvtgG+`2|aI8nr9$6N}k%D=)2< zy>=5mqap5LlpU0v%~>X=RLZmQFrjg8$B&o>Z>XsZUpGED)vYO~S@Qs$t!?n5Dh*xV z?|%FNmiGfm502nm4(9{bVGDcw2zzg8II~6=ap(xVtk2;jC`)Iytfqff!%tF%%*MAY zp;gpPR+f+nETIkb&t_u94udB&UxGc(N$dqbcuBBVmu`UDus~`ua-EPNzqB*4s3^3I3DCP!zq^?(Gz zo6~W$fH=I2+}WB*)D-3&Y8_i;$|zrGv%%R>?$82a?NZsyeADsu1^bxI#HX8SaEwm- z;$>Mx?cAm4dgfB6;~;rY>RNKwqW<7|yuj}3S?{LK6A$V!xWb;=GJP(rrCE%V3GAnr zOG-$VoH~~#;gK-6Gn*tMxrMtQa%{}cma-Z-?{!92tlYvG7rzl+#d>^r*HUZn)E44p z9OS-Wj62ZZ6O)w5_YHZ?5veXCUf-z1w+X)`f_fLnj!Ri~j--c0iVHkp7xsGR&|2k& z#TyKwlJF!c=3X=2<%C=|u@?XB2dNotM?Ar^Fn4P<`Xw=UOnfiqNG3I7YVjA>;z7A^<}9_} z(QCp;D7eu;ZRvbGZTOT}yZeYuhL@TCdB-QxwN7gv4{--t4$sPU!%phaZHBi>EjQYF zax-;`(5|K&IU#o&n&e;q<=^DL{ri8%Q}%;ooxTj;8nz^yqJWL-zWGX$;1;ElKTazc zlSt0wkF_5PmxO$GBP;z}qr6!|q?Va?IMcm+S2e6-fA{HcGmkKT@>UiJj{$O|OL zUN<}!_Gpdm*^_y!*&=7ShxJ;tTEe(P>n7*_6=%Hfzy4PKZxMO(kFn!z*1&|3n`-3BT|J=j;$E!iQZ8pD6C=DRExCjy?DyiHK~5 zV>BvB+`(vl=>&3t83oLX{1Hh`#ka(LYkfWpiCeYQgP?A5DLVPCUc=|up?l^B&*`7H zd8N1gqr9)XW88o9`(AtO(YenOr-fR-8O8^DPq%sVVH~ZHGvtpiZ-CQsDOA3F^QHXr zzx^vcBrq$uuQs)TbIo~_`L&$!p`5o(FrMtU!1rL>tfDWLeK?c7B6S)0^>X6)!R0Vy z*z@d%2jlDP0WXTNa`~zh`h~##4U%NfbCQ1AUvA^gt>LaS+1m%6N|-dEXV!gVP40CV zG1M@e2_wzKMuMmVR?&o)x9tfmL+)d#-+Qb1p`>IDN_No`@d+pqEB3Lek!jq4HOTdt zR4I;48?T-9{JHd@jc?^#uW?7u8@j(%u1YWY53Lb>Ew37lFFpwJh01_(S%fzbO_w{5 zCoGf`D^|&Q^3=KKedS;2n}7dzrC^D1*OK9WN{%mIPON3kVlFL#ab~;4Ld@oGkfp!D zKe2L~xR{uW1=@Y6rnCZG#&%e-UvQ4S5eXw12GFg*NU02pf~_4PnP)DshQYq# z4kiH>T1gyd*PcEj=a&)^Dcj&8240Ak2DtJAPku0X2&w@w>XbZFw$&RM=ABo=JxtBz z98Zqh#XSrrg)?l18g!+05`FwEzA!iiPhkV$mubbrp=WvJ?WhK)dze}Uk6ue7etO4E zX2d=A;q-?xqV&^#-Xdb<0;3kVGQiJqN0a2>B7bBbp#OdM)z|XJfBKU&=M<3hStv`H zH%s0}OTTJ2Il^7C;1|Qt z_p{PlP$^eA1MJKK{;NP$b6{$>G)8sqr_Rx_T?+^wymcV9t7E0XNRf zQruUpf!ljba_|bC+qpx$S(^$2gN(j;F?df3Tuw@=og2R1ewp#h$+CU+8gazKFz;8> zW#yFll7POeoBP;abfZb=aTdHkL$=Ld%iPbDs-zrgxK@N8{D@egXRyT+a+5kBjfG|G zhjI+BXh${PVD@8=pK?dfy>m-r2WLyIw>>_8G`FRBt_Lr_XYcZgHN8v1Q`ys?%}FnM zL>yvWe#RX={nWu32S$;T}56T zFNxzq81aW3*<)Og1m0n+(=mxTd_-cL+|gznlW<$kpT|#=6Wbxj@haEGaenL{kbM4Z zH2DzUbz3Ckg0C!Q?RMf0e=h7blLeb3J1Skqy5N$u56DR8J#pXfM2>-c{Z?~n%!GBq zp0K%yd+?fOm|Q;aNaN9=j~eTe@+f-nC@Y^v{iRwLyS0fLXq}JH#Sj}t$7ARC*ijDK zIG0N6V3$zmA^paX#H6F=;qw8pRk=)KU;U#*gk_U+S|r)51*Y6-eZl?1^%&a2{CY2mh73(zJN8H}QV+da}-dq>)DHp;<`W}GV* z!=?+B9`p`(@=ByWBOmr4>=<$icgPp?a;|Ab_oTjBXz$x%Hvk?s`pq z7tW9m;0vNR)Be%%qh~1P)s4OSUdG@x>F>9~t!jYNq4D}2u}F1GJuu3*@s4yKm2vS3 zx!I3kvZvttcqmV~%c#REsI^Mn)~+||{HY(TBqfcSs>|`C!ZKhjcU^#wnLcu!ryM)H zZL{aH7sBI796Gs_z1fNttYhot9DSk;s}oC^m&`*43rE?wbPMO5Kv*RM(q7dfkeq&7XrsJ*NAVL37JXt&qGRBte94(CS2%CQY5Xn&~7(c&wz`65^ zZ!{WLOL^2Y08cX8;Ej!vLs9=|8vFIuvI@g1t_L0 zPmn5}GWH|c-WTPnHU6=68(=;ymeX*8&eA`pVW)VnUTd(xstT%Pu&p0Ie~&SK9(2J) z>487P8QJ%^kC?MgX>GQb?7xS5iAe*n0?6xj);7zXM)Bb38dN6K?`25)hdDO-?0d%2cLpzT2%W9!N8k04)(6nN z-Vm&$B4S>Ir?*iHy^Fcr%HBlJ` z<&PJLd`SWOwL*6wYCJNSqX2#{1|*eS|H<>iG-gvYDv_T5>PO{og(M<35ja zvVLM^(LZDTLlSZ!L_*jTE5G1D{~#O*>f3kX^=xe6XDTzsjrw|(l`~3CPPsAn`}&C4 zsp9Pb%j9x`2D{ zStDbu3Ef@Gb$Ic`w~U-ZD|?MPYLQjNXGjVAwNmPGH}L05&{0P@ILk@m&{5`|2ik=D zaDcjWn zK^&;QUIE)@KXoa#NZM}h3tWmM%QZ;qYsi;!SJQ!aueo_ps;m3RM?9s@4zZXRq#D}+ z;?uxG?kYy8-SU8(A$MPGoe$6aT40aq5A}vCYe;!g>Kj)s zLVc{L zMOkaTaD#h>l{rU^fqtHjQ8&t5HC|6$ z_oRha>sq}xcc!X!h&l;``&}bwyUhp=$l@0UUyixdYamdbzIk2eFy34CCV{e*DsqaXso|d7ZXTBE@+KLAd8ACcj(WyRA>21U2hm{k(pSS>1FVJcj&@)#e6@_ZVYPnt(jHgb1Wz1&Q~8_6DL z?<_po>(}wD74y~PU0}J{E+;p)h8i>*I4i87CKWjgYx3ezr_Q2%_}19Zs-IRlOWH@; z9@R~b^V?Wc=$q005gENhJyG=9H)9G6j%5GsHetATPZd9bX}9PCslU>Ys5>-_h8{y~+sxK7V%@4%`^?atOU=!=OY3 zB}r}U4SAjNQobwwgLJ3Y!02z6;xg)$@ble;jrNfJ#G~iWq=EcQX*4-e*4A=zaarLp z)XSJpEt=28ay9iwV4LlQ*SL@TpAGpQr_b0E&iV_++3bbXub_{n&N1o)s@73C!H;>@ zdS=nRxpEbh^LUT(G76LKZd1468NY@f^gjC)&99HrAKEWEH=jOWtS}B5SWk!b9+2cc`4RI>IW|xDx-9b+_ZZYSr?2i%lccun7PU(IjWPV{6@0Nrc)-b}*%>SsLZTzJG>sAv7EVcfLyeS3+8jhVS} z+Rjn7&7)q=iWRbN2*O+mPo}?vc%B3t{2F%i)}%49_<& z+2gmi8(+Ut9ux0j6#k1ct?o9V7d%U@&z)LF4d|8FqmK}yb!~sd`t<|`UL`++vXvdV z^VdE6b7GsW>#8kP2Et3mYIm$|VF~<8H8IxB}$#d7=#t&=@UqZP#GsJ;h z@*wyB+P=r^VO7UF(9t13pgUFG(IeI|)#N@Wj`qRDP~ML+Qj`Vqg8lUi=GH6rE-!gs zb+x@bLsC=C9;Sv|VABv<_Ez!pijpH1zT#~kDapaqqhYVE_ny!B9(Oy~Luk&J?-Ak6 z-Nwx6vUJvG;%-HK*ORAZA1oVJ=9|B4%(=X^R|!dxy#T#O1~Ur)Ol3i zadUA6+?Z6_+0Qxtn$b7uc&6`nvEN}HDZA_ieOVBf%-L_YeDSME+)bc^nTd{J;-_eR zZy7wnXT*cA$ba4CjQV@#u#TT^I1{}hHYrc;OZxCjw5PfzXn($AC#i<8mbYp&YJeo z4dU3hXhfCyr&|3i|9m*5@GP8SVE9%t9>!S5?iy#m%;0d@^vjQo8VNVzlkowTP-ie7 zzxhciO-?g%JP#i*_ZT0_8#9Q1(Ob+83 z`j73hU70K&<0~h}p;}5e&IPXARgClJs5dZ&SUeLZ74u2Qls?YHAmz{LT+==}#IKb} zW-)0dzTFUGZhiN|?Q`)DzC9Nl_pYPaH$|ZX|3SX0WsO$!v zZ#wT(+pJ$dU=2|oO%L}Ht=!>s*R{ZUXf;?Cnm5tCr{)8L`yO%kw2i%3FIg#8tO5IZ7aluIjao>` ztGiErP+5Cj zOxegfhBUXYapxI*r}?Ej{5voB9(8#)7|YjT9l;0ZXD&`nr!K|?xqz3%nH;mr9LABZ zTf}CaPuf???XzROG9LQGhR0yovBq0*FQv~x`tBh8WctB$^xzwe@e|=3L!OJVz!`LG z;GPsk72@~y=gfDLebrhq;jVno^f~Ym=JL#uZGS&OT-I+j&KAS;Q72=mFe3}*Ct6f= z*vfCxw!Ukx_KiL|cGM^EkN^0OhA&6!F6o~!<{f>d zre=c&c8@c#)~fpQ%`3?XzbZDL^WNZ5*iD;CPG|N{d^-=cxURg@!{ox0^QU|x8}7j0 zG43pyj?kSu zt|N0`ywN`{+*dgBwes&=->&Hf|BV>r_~~r=NEvwW@8G={3_LVk@3?QuKGQzZ8a6tX zf^8zH=WqkoC}*y__vBW3rGy-gk!wNMCCEk1 z_oA^s)cF+7{hH=qJPz85my?U!Cngy&nRu+9Kd}+%4~<1Xa@V9jxOcu2{r_(mL$6r- zl?~U$n0&%nMfXt88JBwQ)$@_o7STJM2u}~$1;fGNGi8IpMC0D)@TbZF`%GLvf6K?C zf86J=R_S_0JaVFcl#yrq*=&OkWlp~;8*4B9q>Ma+lZP*3AAMv=A1NpA^1%?iCa|yB zr{3pWof(iTamUi(KExRsz7xB4qfKO-xg9~Do+4&2Sd^1z3M=cF$#z2rsqeYvCjN8I zKc4UekMR^-%Q41<^6y@2|7e_{uMAEz?bS6-V~+0Ym7n+}vFkZ|dfkKf6QA{NLg$;F zUGyxY%&7-M#7EY;g7|pZH{m1Luhc=9IfJ!moEYHN!YvklMicyxA#w)o5`QX9l22yCAYu$f zXH%<>8mCPS@^oZKLOpnAA5ElQ4|7!e2tHv=Q8AumJbgR`3CZH^gdg^phnzp=iARuH z>9kpW%esa?B)&c!QcfV}3XRkBnSL7MhQ84J;|Sx<=u`UDINz~%P_F1R;&5|KofIaf zFu!4gO`F1=WhP^TcF!8;>y0z!;0x+nML#$&)}2_p>=;)z^p6AYS=n>e+z;)Conj7W zP8o}4_@4GKPn4^rd^yv<_&O&rPuiJFH@*nJJGmj+B21}Kba(( zKKOt!R7efo*XReTU`W7M;cg`veU|^Z^Jq)R@8;zh@#8hI{ZbLHbI0oysQD`L%}_^sayM@r(D8n;)Tn ziW7?rc2?YR*gGD|=Sr4{BMETCBE@x$shE89Tl$FpnFjmi7_?w1Wd7vouTga;W9mc)~Nli*3R zr``)&a?eEgl9Q+pNgp`Om}78UmFH>2pH+sTHDgczpZ1TL&NXG&8FP$Z(^tj#`g%CK zKjr;3{sJ1kdmnzrfEQZt$!% zj%r<#{!YFhoXRfZ#USrqJwNq%aQcWH*P>(bRhJoi8_gf+nyz=P8f$bO=^nbZwoa15 zt}<6IqC@kSqj+20R_~AtW_IF_w*9KZMTxkPf?#724(u$1j#rPC#l}i z66*mY&HfDa1nuR>Y7?{xi{udNxa9=;Zt`2kq4tliWsD&UWuI}!xSP*CoWqQV_}tsf zUcM2|C%VsDX_c^&LL|T;)Zke~!T8fLq^!RP`Y6OTk~%}jsei7zuL-j5gWt%4zxx>; zW-ocoT6~{9i{24F;29+cq-QIwub{cJ7py-Thcq7PdiIQX^yC>_)2Ez++2iP#(L1z? z>qU}&C6>G?8i#A;lIWdG`%^hX=fFO^CJFGtLh%Iq>^UYU&YM` zkC`p}1W(rB0Lk)2D|8iYYjm{~hm}Ywedw<`Q2NV?c%%%(eZ(@2Jyui6ITDAA@k2iu z9K?w;B%C|j!L|X;@p$~m5i1Km&^gp#;3?-S>gYQbBJ2?NSeSBg^kIaYwKyq<*YA~0 z6Q{|soo4bSu|?OaBNnhLk9r#S7@9lN{EupEb)D8e(Xpam>wft8b3R{iuf}<_x)`Pt zaq9+m57()2pYEfat62DJaj?elme~f$ncbf7kPo5BFvX{_8C~%zvDI6VKKfd@u@8=w(ufMFjHjJfV5GQQG8l>bIj`=aZmlVLY-MGd zG1qoao@Q_o53O5IUim(KG)PUq2FVS)F4uz!=$~*2ca3KprH+pwk0ka$ZnA#+jGOkqhO|Y0VpLv(A&!{Oi>9Gn4%*SBtyl0Z9r9mCUFp>I*fH z^SNbsM0&fsrJ<@)Qo=(eEhIsb_!)x^(;v+3D?EYl8iO1-liTZa6}}?>&o0bhEM9)w zUzcoxVN70<^LtOi5Mz^}1Ikw@qtOpAg8H~{Z?pAJ7`=&VV7XH9QIE#sr za54@4V-%k4Sc5mI%t`I5sAGu+V^Dv-5a%e~DgMT^5_!cwPU{q?cH$WOZtFE`q_3_) zQdxg&*RI72Whvg|Aa*XM<`?HRcS|cdxOoe@Fub1jFh!ih##y6BVfQ&lGj`Y`*anO8 z75%IO#dm*z@%Ze&3?q&hWCf4$KoHzC`qVC50uI4mG=p)omKv+8_lO5|%dKZEfH^q% z?S4nO%Q~*~fj7(iJBFXY-}Zv!UdfXi#1=1;1LA85lfoxnTsQBb=IRtUjLodgV|ZWi zt)(__7my-*7cXbO><_1!`Vg#RVa~Cv3G@#!=^dvy%rypnLMYs~D-v)xNbc0&abk`Y zN3)KEg^@3}6@PcUX0AuYn>(p9^iLvtqHXN&Z8mKYpTpjgaWz#gI|Z>Hg(-79l^7QT z<1$b#9)d?{iw@a=yG-X0xq38`GgmD9zXWPwB%@1DF`oESFcFU@(66z~$w=NO{mR%o zv*)C^Z*hdFl5>$b|#4JNR^kIRWY)C#k=6%TWBId5$% zmx(R9cZlGu7e>E?kvE7u7EQaU^Xi|42S6E)WyA=4S_#Vd!$%)i34`_)HG3+EweSnW zVH{rN35R1C2G^0;nR_{(`()w-eRn&wnk4O?J!EK{SEH>BfR)bl==FaaZR6!)4-M{S|dR zextSQ^s4MV-Vk)xq6g+5NsP|PX^fs8Tma^);3G> zM3&R1;+v#@7*}rV%&2p764oc4<$a!VGpbtriA8SAuO)x~OPTkxpGm@{FnM~PK5D)X zKdlkYC0?OBL*j(SaOVHDUX*ZDO-}@b%9A>hW;NiAERgn;`ii*QF%tE%TD`q0Gxtn4ikDEa7>_ zy?n=eG&qk5^k0IGCvr_ZMj!I`%A#&cA>&6Gn8{~Td6(gGiG9+A0~eV8=On-}fSlhs z&M3rf&bC%FiQAt}B@git^@!otm{VKP`~=$AWl-}L|U#G((4;WL^%3*V=h@0kbhHTHN69xIp=7H8zl zK5uw=XNcXq*xP)<9&sxB9CD%T&3)lcU$pR+%eKC(oBr@wZx}pU@QDBM-+%PLfAqlr zM?Ih%QX8ITo}W0RKuLf8U&bGP+xYJqUfwLLBa&(9F4qnmHCUuM2RtR)3MQ!)bx`rL zrdq%x#X}xx>WVI^QcCNoCpfCi&3B&laqsavKB-Zk7*9z>%L@sMX_Nf;0vT(@1F3BO z&=_iQrlLV=B-YnTUVIvUIXHSX4e+vVQjZQ!LRvO=dGOoaPs7YUEk~)Vo^&4XcxZ!! zE_agCUs#IQ7AvveW+m5?b7iy#{U|zuA?}D{0;41@ELzG7OVPR?lPfSxwO0LO>b^ZE z-}eKWm+!y%f7p5tuPnDM%lj|r@pXOOx9Xl~PM9-d#4JV>6^SZI6wHbk5fciS6(xz} zBsmC@bIw6PL`)}#dvDdP*tPn%9^~9p-F?P*#`_YEz}kDSy~12`ew1IRMQIN`2MqC3 zaE%YI2GT#Rk&L()@jrM1jzx45;Oo51Ot>#Iz4}~$W8mB-XE_hQ=EZGp#0~Cp{dyr> zHUkp(tedAtQgY$U=uwO=&%n^@5O=5IREIcDyWai`UhEg}mp{uBYi9}L3B~7!Tkm5X z_DdA&5Md2BENc@AkKto0clbW{Nx&vox#5FQ)g5DW_#5~<^jK$oYyON zf@&o)4E{QL*;&zOX&a!8njaV|zaP{m}bl0>Njc#8*d!YjE(}*aE1K0MR z`moN_d>DKu)nFBFT=kRV2akdqiTN0sA4<+wg$>Ei)QTL zkIcjTDk0nVNHEW1YDkZn2MgWlA`v!jtP9-cJKQD6(iPrNH+iJ|32j^b_&g8v6>S87BoAEUK!@g6)RQDV2sQM`8_rOy!~2l@SU4$l(&<`K^9HuU6Y zGK01fzIqp1C%GSZk6COq?i6>%zE^zKhd1vel5@3V$1d5m-B#>&IEvM#?bPkj;_Ymg ztY^ebQE~DtIvQSt1aaO(P6K~O0Q=t$yI)h=^XNKU;yhfn^56`>Te~k{w$^72p=U-mBCfk zMbEmk3C&6{B=6Xhil5XTNDUaojF=?wS25JPnehjk^Xx$+YXBZRxe%_bB76fg4}~@2 z31`s(xOARANQK)sTKv2($|>el4l&-jiw6haO zTW4{Hx6p2zz1+Fxhi>X?NqQP5Apu~4v$7=Q)@^j6R}fRVp)c+MCxwUjeX_gqD*N(W zfdkoV+i|&ht5Ra0!KuvLsB-?qV*eD~|Gh)pxH{XXdw&SqtQc@`;ctJ0EB7CnorNFb zY>WhNb1-;vA7S@<*#Dk#3UA#Dx3Dvxhhy1tj|9R~a+BJ_wRN^~h*<6dBz1o}Ob`0xy=&aQ+%Hd+FF(AKyePLCd(Ci4$X z`4!TTTf!U@FYD04=Sv!Yl(0~K&Y_1Aaf15vRvT<>BM&X@vCr8HVM8KA>ohFQcB8WzYU1;>}iirYs%Vp&{yLDyP>+asvp(U*2PSFoWuo1(m%=+ z{|gcF@E$w`m!rsGin$~5m_vdifwS@iuA=m?1ZGs9amQyeC-Z_eDwLY+0`xDkBt0aG zna)R&12#E7lD-CWug+T?#Lap)ILRiX=H1Rbd@op>tH;lY&q*J-dH#u9L*L;BT>SL~j?Du1c2!S|ezqU;xv8%y3AL7Z|kMlvFpH@F`uaSy=KgT*dGCnGgH z4$ZV+$$k0^jA zJmNcQ=wO_}n00x|xeN5m!tWe4_P_FQU%?MuCKkB__reKkcHuES@-((fqL{^wOX|V* zsQ>Aa_+)r&n?8J+H-R@5EPPhMfILm?mHT&V(O<=H~sYe&qF-S z(ahU2SE-!L`3cIK$o^(dr=hF{JU6707e$a^+{P!ymPmF;hV8A{*@8bK=oz z@SG*7nP@ao+kG4gPCiu*GiSC3AAH@{Pg3IHPvo9{5`u2vg9h=n^@eZ9eJJ)}|6fBZ z;OrK6@q=eJHu<$g#=Vrt7}g-VQzE08`;Kjb!?_h6`F1I)9e{U&dpWsLo<`S861=DP z=zZLPTlRtbRe7`@9_K@W5<(mhw9!iLZTiIJY_UcBEH=xfHS6W_x{Y#U%QiS|ti^9L z{9w!JwYdet6NB!2W3N;-V0Y?=)dsio(EFMu`UGtub0#G8B40TckA#y%;!|s4?R`NA6@rKY8qa_ z@%UDJsUNDIeJeQqW^C|+zc=|Ic^AAr-(lBxV9XSY_y4p#=Rv=w{rdLi4k^cXWL>){ z#mrcjp-*M}Kl~ft@_oO5bp4Do^OJJp|156@-oj;9DrxNh@~r2~-=alMZjr_wZ(D1A?K~nkgUaMdTt6IIx8dt>mh0I58o8%8^SpPjJ>f1Zl*BMJ zyffhR&ddX=8!ZoS+!lXy1g@R)5nu1~a^Vzsj$?2r9ReF;xkI*^FO<56$0YBPC;VrJ z+C!e9b-Z-4QOtIhM?1uUjt{*KCxtD>sPus&xh*_4zgMX3*z4#e7*= zCBDA$rPL^IH%~S5ES2zfm+L$*>yn+{i^lIku=QZt;MQu$$%hXojya}0{`O?B$KY$e z8M6O>+4I=X(0=AsXEVP20d0Xk@X9Z8;8#2+Im}SCu^#War zk7AsD_*vIs5Z^=%9c+68cCW_|J!6izg5OJL6=DM(ix>DJrvvmCe4-=>4#_96ujTRy z^!qgDA?7~|@6L73Nc>~8gP&!VegEOZH`3M9C(YGW(w-N?{HMQE zB&B21pWx~p;(1p45A$LV<6nl?Vfa;X!p6VLTC~U#9zvdi>Y#9y)$vp0cyfd#9|=^f58L)+i){3m62bMlF_5b$t1Ya zrc9a*Z}uG4V!liV|2q-9>qKyfCKKn0)%<0W;N&W?`{0Lu_>ft@g zC}fVgkRCMLBPH1_Qu@3@!tTG26L8-8uG+-Exmg13T;K-|W#5rU5f27)-uwDqg9|41 z$JRe$PkmJ`^49~3f$5cwO7>}Hl9VNevBo8EXrYT58V^R8+(GRRcK`SGA6^H8o1A~^ zH~Z^ZB92mAvid!pttjFwWPv+T&hw0?QS9?T3A|G!5z*b)zf(ft-?{SQnDn&e;-Pq5gW$cKtGI2b-(-Yub0|Pu|(kxN#^2ykII(`53Yb<0veWKZ5 zmCi2TA_?{mQgLawB>0{*IMPEd-;-;0r{$74{X%B0y%w2c_Z4z@p>mong}-#UVecc% zrC-^(S%UW4iTi?Oa>2t-nw#H)TWe>Y6We3&+EVNfFaC?fI^yzXa@Ke_S}f!|{5$8D zbC;W2%0)Q*?!o6D9F&4qO{T<9|9NlBjP!9|GNaDCQWG;5g(=S^1K*k-3Flo=ku;Oj zHB&2TMh_+JK|CBI)@j9zIq-gzXK#qp#|R^iI%+_xk9)-`mOWTU-gR zRiYG82MMBQ6L`NKzR(T{k8H#Kt;QXD7yh5~R-UqU8a>-f*Qjqe$mf6f2l(?R;A_U? zUnaqe&fW)mJ)QkO8GN!S^8-=$9&pd3Vhi$fa92K?Z6)2+K`MRhrNDwFwnwdS;+#PN(=DC}C(Jz1f1N>-Bl1sg_r=u5J|Hz)!Tw!Pp ze!C0sNvdO@S`c~{sO`VwtQhvk_L}?YEJs@d^9aO6kFVa9yAKQHW=I1uPdk1eEg+t7 zVw^{h8|Z=Vktx{P6po~kUyYDiVDQF{7-jI%PXf1K0uFHsSn(MX;HLj-jGT7a2TxcA zGhI>K?a|Ehq{-707bJeCwGKIh<4eA3!2@UpLv)A+1D>Z`S7-Q@ku;8pd|%{8l|2DmUP9 zSR*HvY=*O7z4%+Z!_!Xx37y*~Pb(!f3SG))EpVs5kmzvcchjpS9Zs3J80J`B5p%v# zPBzUS;jD#6pq1RcwuRiT{u^=ta$jQkm;HnC&G)LU@#!33@8LBV_DlL|tih+fuWK-@ zW3Ka~9bioopJYi8{{CKYgFJ#KE$DuZ1o_2EIGkWlqI<*~?vgM6=Vu1ze>}51$-!Z8 zRo<6fR@-F^8pV@13sc8UhPPEYMW?|3a~f?5)zyhH*5G+!5*$0kI$-6}ciT$NK^v(& zXD`{lp40_y!!fx~4lr-NA6$g{f<>~=bdk95e)k+RxO&WFH|wwm&hGuo*1%h~RE|0N zFrxy#v$YreQV%uv9yklYSw-MWgNh|9q)IN+cRvf(<>aExa>ve7vaW-Lq^5Let&;=? z7o#-;=VnG1T<3if9oOI zX9{!X`SR)wSY`Z=!3Y28F8=)*81LEt#$Cbdd+h%XEUK=-dvLr}%#cPzR!9gng!`fR z9xx)$qoO47>IDh*K^ygSu>A4=`wUF5o76GCQ5?_j$h}d*Jy1yeV-Dxj1n~M(u>Y8^ z$BCWU0?9q?LXY&8p~aI$-r3QNKXy4Sw-;}fxb4=G=4vg)r*=y5Wi;4`eO+eF6PMZZ zWZ!J&dsqvnIrHJ|gRgUzDUTT(ji%zr8th%LM674Q4Pbi#e&0rL1cR&rnEq<&aQOU^ zsCweVa(dzA;%j{xZlw+4Po44wb%Ds^7v+TMI^x%z5*}QF-Cv^X0|$R*m%+myp9;TP zWVYm&x6`xuLF!(@d8zi-hx%3HOJ@Bu%cSJ?9cBf=6vNLS9}od|2J;lJ!P)D+#uxp@ z%dckw|Nr~7c>g`$qw{uZ`}c~;AC$~Y^1|Fc389YgAhby`V$re7qZjcsT%P+-=d`zy z&;RZZa_;a6L*FioJ*{(Wxv}JKemBIHy*iG2c*^+6GXCq);<;#rWFOco35RVZ^-ciu z#Po&0NM76GBDc-9O4L?sN#DCeD$ZHUvlHZXyS>DP`@(f5YcP#DvYF^z&zu8KHZ%7# zSqJ=&{XF~-|E?WpVjD9dUSI=iUcBV41T(|g^*VA3Dr%GpY8vhICk!nF zts`;<)b}@az^m#1SW3afb;E1YNZuEHB@AE7d}b%J?H}>&*j;)5jk}ri@O%4z!2WOX z{co9f&{(UYq8`p2wB|}*%j1|%)}P-eCY^c4EbujcQgzu~O1#{~4Go#ee;mymim!40 z3gf|n-@7X@;O9>*v5-l`_9nzZ^G8jQYYVL;6mI&Q{n+`88+G+|`Tmz5Bp*(@!1Xo~ z^-}IIiY9R%8pU4gRw^x=?yJnp(kryW=wqx@S9ys!! zF;|n)O}(^5Zi6+s3+5!0+1uv6AJIVME_ex#H}`NU=dGR^YaMm(q-TX_JitXp54E76 zkv>)hb34Sua4NJBTdKB$o(Dq%Lt`ZF2-SIbNzRd1QZ3~TUDSl(z~SG{D}PU2o1XR= zcx#cH#({G42ww1}?I72jM|qD0j|Zq_z!5?X3WLu)|vJnA^$W*&E>) z-Xx*iZ4s8vlBxd3(@wlM+Zg%}M^?gjvmB1Ib$jIWM%9L*j%{^VE>PRPxc#tf9%CYw z+#i-dQ3o51ZX%hTj^llcRvjtcFT)(r3g83Bv6ic21-hNJPLB zsSos)jB8ine4%c3-b3mx9g!4gum|*7LRRmStE=|X6OE(JmO(E&5ey@B^&`jSv7;;g zeTSST*Ycu8N-!YvaFew_IAf1KbpxpDFn7cr^Fo^c-|! zZ-1Hk1@)8vF-pe#_bVZ$`uYH5IApVH6QQkO!h6S}}jZcY@ z+TZ;muYdSw$ti5$PJxT73A`Dz88KqI;5~ZCQurSMTB4gpU;Lw#GJ9@ObGQ zk@A?Q1W~8C9RMcrwD=u40Y)9Hepa?T2@8{htjQ|smOJd6ou4u4?K*hlFzw$IlOb+9n}UEfVsO{z6Ha ze2d?D^BQai_3F3pKg#hFCuOnOGFiJ~ldPqmdI4UuTw;WFa>YhCvjV^gSZvrT&tjvc z`~^Jt{nUdCir}W+f!}wKljsASAqG8zFZ03wT(dq(KkYEjA!hBSFjF=~c2PUBn`S1~ z_#V|N*)eqinn26o{9P&LCZ>i4iv|4ms;97Zl7(1JMy~|j5}WC&?Sjq=YhuNEEE%x? z4PNjp$I&JCJO1 z-1m^bmio%a*jQ%qkD%MPO)g&x!r#C#OAMu4)v7n3ce%bAnlNhr)K21`Zfa5`5^@{- zct5;9a9LCrlu9PN72RzeaCW?vBZrTRDRs;>8_@+kLVn}pF9*SPZLqYKRcqGCy&C~= zcRr{8l1@LaLSA=v$r*4GhgR&8BP(qTjfV5|asup+iyyd_YrBrhb-Rb3UQe9F{vW?utW3BgSO=RK zo5>wE%eu+SgB)9{c$!46NMY0WDhJ_=>qp>pr+mDrT z*mX9%(JMyI74PYLrPHBM{_-GO{=M2+{#v|FPOhGb76LpctoKnfPsw(JyKmb~{w*^! zBiBmCxt&sXmw5~aZ&^HLp)6zWBtDI~MD~0O{=Pxi;nSn<_3nr#|9BkPB_aNgn58&x z^v4RR-;`(O(s!d@LA_kHpuZ!xNM%0n5bLmYn~iK(zfG*HY~;+T(`cc>3l$kDc_~R! zl#b?jPA0vbeDOy6+f#M1_MD{e2#y@=oMKr5z*hN#DfG8F$}Hy`_<+f~u=y?%W3?**BwYU^(HFCJkp`mkDb?4zPOEVt8fO^8M?{A2zTK@U9Rq zKR$n*I^ZKI&S1tIy%SxBUT{9DqvN!AwQT0+XgoE4%mf+nN9_N{afVjSjISojysxHE z3vrO(wWp*Bto*yX_EH;cE*s&u*)(B^cq{-XW*s7}XVC>Zk|V1|n8_;i8rDu^?%QIk zY=iTE!OX?-;9-&SZ2rvf2M=0cWI2$Tfd) z2CH|J{r_OUkKA!RD;Kx}Zqm!VMIZk*xQbik0j})xeKXC(3BRveH?}6TiAkCFU$If< zjGHb~{|G0wYDtabt{tbj)e11)+zZ%$BlqvhkqeFeuV+BdgcZJN%j~t1a5s$H<0>0>*4UBwx3HM-fG5t^r>dcVrDPD00TejGd%-i&{h~J^WfuHId2v72rDFD z(LsrIphx669j@?k@WPwP`pGLeLzeVQZ%W6F8hNzOLk?LkXTQMZKhabcOkN<@u04>x z@BRt@0~a6o48`aTYvkw|7~-q`C>dZAF5j%6_E*T9P_ZQXUX(I?*!T2@KEP$w$)4%$ z*7`bpeF@L7aiKA9r?adz<>lZra=^(%8f(zk)4~4(H(YNQebx%OxciWJF0+#3D{STb z8uGQx;5WCr(@#fZX!`-VLvDAN*zyRE+w=wG!1Kt3Sp#0De2ymSAI6|rf%eImh3HtQ zp3YL)LhPgGU^}`)%SSInTW1M&w;=vmBip!tx3EU`%Xi2lW)Aj)4J-unL82=XO~0r^248`jC@V;NQ#FsGRME%F zN=gPpnMA$l1>8gE)TgCOVQvn57|fr)8JlQ~PV;#MuE$EwWo@F7bxiIsKu`=sR^l?UxfG>&J?ZtCsK5I1_eY!;x=gaD; zrgCHNK6I?Ocb3f&ht;c%yXxw-dti%y{B7SWCbgdUr;&R=^$qL6=P5US4!t|Afz-6Y z=Sh93sHA~f0X@ci=H=jatfOD~)1Q7tyA|H2@=B>=E~kt8Shat3|L3P?kbCFBlhI;e zm#WIj=*gDLAm`@WUw(l1Ab^_YdO5b*R!-7?QjMg`#GuzX0|Dp&sV_P_W1(UHLo?7l zVjUJ>d-XZX#?0c*<$fGF3*8cQNwELW!#Q9LR9ng%`|Ekw!WwL0pKcmUtU7D0B-7hD zzR8yU@iyw(YpKbd1@qY?%}tF4_eEPf+_R0XaDK0fLynPKQDEu26fb>tKo z|Jf)pp`S7J>xnY;^UrS?~B>22a5Z~9y(=Pjhhgucla<7N8iCg|l*XCT*7Jtox~TJY5j^kL>O zdx{@IlW7Av*haJ$SB{#;D_Ui&h3Z$W{n}K#xZ5khIe4tx0vGveG)Jh9P$w)*NJhuI z%-{*{B!6t<95jIczkl!~+Kcctkb4|pHdeKoM*RKPGVY7f_#|rUUrmsS#7DE4=QR1F ziI|Q@AA00unKK5R7X18D{JrTI)@(E}!XkQ}9w#O6#$$O-Z=&V(&*1XF42=Qj5$?5Rth z=fsK`fU$oZ4;D9+xG&ex#5#I}{@!D_6sb+9@2~B6E9psj@V>TiHol|Y{YFB=3&3D; zcRq=f^l-E?OKag}{s=$wcaj>DArD;8JMaxOW(#V;(fs`9KgrL({vyA?C8B3Q?XUN5 zMIn4oec%A|^5m(HuS8zDAw|Sfr_dHSO5UI38zg-tt#W2P_Fs=a(nj=CxCi`LgQZ`p z#@2LnT4thEf?nDmCyE)GB&vy|Ii~87tsOnz&~-8UdM0+~E*NXdy{wv2rpEVeAU;yv zyS1a{gONESrC|IHgV*vjU5S43QZ$s%pk%J}d3ZGOXs!gEx*+Xf4{E?a72$(U+w3L} zpAN?xJy7OV_rpK1o;YmeXQSzFOhiX<1m|Fs;d`96I>}j&3$ow&0a@Yh*z^E)i|1 z;>1+^-AXwB7a8%aGiS_>So6f@9rC8L2c3{p5_aygG=d3y@i1H-ojhl7Z0;rJK8z1i ztuMD(3-LuWh>fPR_f2HnA3m22W~-Rb$duD(LgehVbb9$Ua@Q|GLLZmP!}}!?|0E5( ze-wOOsnjWR){~1apn0tykNN;1#|G^ zN}yyAW5-{BInP@RW@uMV6y6XXqKR4u&=#zGyLPu;|OFvf8*C<{SDxD_Nrz_^fqP7D@J#ICMbo8yvB& z&@1wd8Xia_@&qjPG^gw3T4(witSJws5E^ucI^P zX7>zJv4VSWBWtmF@+{ejj_=I})e=n4HVC|e>S_dme|`9*RlHB$li0^m)Y1~bccROe zoF<1o;e`o=t1gT^T*ZufTDb(@L^CQWjs7tltBnmtowSwv!RWOSBQ@zmy-qc8s)=7K zxyPGdeb0TOweK!4Xh+0r<96{}wcgOP*+a~JaPcaz3uua=dv%@liRFx|ANcF9e~}+Q zekXtZw||ol;PAqZoWuV!1HTDBwAoQkFI^_*R)BG!_wGusWy@q!ng7Qr_#^IL_PXk_ zsTSE{o*_-LIkJcv&HOJW8~#c6|2*yn)o$BFovt7*nVH{P23POCIg8P^qmN9C?+Ewm zj)_y?A~+?z#C0dO+rgV~6aDjhk`Qo*8SK-toAa`heRPP~eLFCHJIQA^kDfHtqu~7- zdfi(W@074u)pTo@N8oidb`OefldyztIqj1SHaQk-RjOoDH^>2ppx95}Gw0-@_jSoi z&W0Pi3_Sc}I49vahTEs40zcRchC}U--T%+W(5J#D)zO1)K*OmByyfmC)LP)q*fVtw z=X8PCQNO(DaTFbCD>-JmTrRBJDw*W|Khpad{_kJ^<3EhIuUk9i0ds+YR<3e|vv7SS zddEu_qi?0PH&dC*9jsb!E7j)coGro+sn1!8eKjUBAH|xWduGP#BEGI#ai*Myd7q&T z2j-`k9LCSZgMKo4S##;(u#b0uQBr+6Ta%g8LiXWv^Wl6r#rgD?+=pSzY=_WiSS2p_ ztVf4Vf$_E$SFNemuSde7n&vg&KJpxkasT-Kn3#)Gp1qZS_DizXU=Ar4K+ z#6Gzn-YCJ3Qn5WXXlD9e#3o59ID~|-r;-vL3r}+u*n~twe^GhLs%mPC-i_YVTALfL zk^X)i>NRK6dIfWf4{yc75ion+3IsIJ;le9>~AwPWVU8tSCGEgj{UtEadUlb>F>QM|yvc`PuO zz3PXkuUdkUx#fC@S@y>g3BIE^ELw6Ngwor%0dLfCNkGpc>I$<${&&E8ypW36XA*H1 zE&$Go?c90XO-tqO{YnXE=HcP0&cS<(6 zg}mz5Qdsv!UNGwr7S|}@QJvs6scpn{h{s_xxAz>AC)A+|@HUa3%e zUk_dK%@i$;S5ud;3V{<8bCH$_(!G4{zVemHh{0 z;r~BEY{(x2uc0xQ(MXLzXPJLAYL+_B*2bg#!^T&tL8*Em-$;0DBQvir>48%eSMsu~C+?=NF^pXtmHnF1j8U zUq??l#+{)WvvcwHC!G#UPYvseTR9}CIi z{i?su&`fyM|4PbV&a;Rw;1u2scFL7C@ie2RMm*!Qd^I_N4ZWC7!~XyApZ_U8(p&3bHb?(cEg}19 z3%Eb0P;Z=%Z=n}&Y9Ys&v0Q@Q-@-4^k)5$rE_--OJiVl6JUh^pS_;0w?CWXpIGvZ5 zRn6RQ$zsZ!f#sq#=%)os8L?eWVw$1hlyo@&t-yN{dh84wP?7LB)l1r~hj8Lulsfjj zYJI;R956Uu15ce3kF9$o<_Y-?`Zg`(tw!yV*rS=Aq1KXH$W7H(H);=&c%_jTv9X_C zqGA?_5%Vfu$;0p(Nz8dCM;y+{YI3;kvUZMez-hI9lygU}Q13=>0NXoZXD9e~oq3E`uxfjFZ#RvHJJ_B$ z&6)e*D45!FOP0%hS9b$z_rLyqDE?_lhX>%?bz)MulIbzb$LFgrS&GkJ!v5dC#Yrrv z?V6+6xeOeS7qd~hAqi3+3trjxj%=B>M3#Itjb8i=xdq0dDke>`A4Q6@rGq$ZbCiHn zzH;T@aYGNSlo-7H8JsfQw_&F*!S&lBmE?|z0e6j_fO0$PJ=mo)xYUR8I2*NSLY3E{ zAqQqbISe%ZGjby0@eXR56=;4vd+|!*vtEH6M3<3S{Dxj~r`N0n=R!3;<1$q%wppyv z)LcUiZ!lf2`u(fG25!dKly8=Id_3&Br ze6ZouRH=zhlL()iN_1}yKlZ}q2?*lnxwb$sw?wVi%CLe9J4O=9@ z#a+UQEh4?)Q$2G9$MdwLFo&B;eDNYKO_~ZSW#)Mg*SIVkn=ujpHUJ8?l9CI4u{QFa)C4B zZN7@0@Lrk1J$eLxZwX#`%zqmp>!>XTUA`v`1vS#&h`w7*JNYVeUAd)VVM=|qe?JZMWxJ=HNBx;uiOPgao=zZu6ufD|D(hB4lEC~%33&2 z$Nb%BSvbiIP5Vcj3v?N4J4E$B>z>nt=1C2Xl`ZpE8JeJb=&fWuj+N@<9I0gHzvO9x z6f#d#nvND|Sv~w?sxu8( z6-5iPu0!*k;X1&O?{DY6HTYXN2c2)ehr{9r_*;hdy85K-f^KU0+#_$fo3X!fPje?% zHohk&R=xG5(vXoa=cu!q|NR%lKu&Pf(UZgW!}j;{1hc~0*v*{H+&Ea}n{XPkZ#vN9 zxW*o~1>@{8c{({R`p&b_-_VD3IKX-xm`@%+?&U&E>P*h!MxE;rnB@~@3&BXElf7ak zy0ok0>{6XIT1uTA-O&Y$nLRR>tt+>Q%Pu#WN=@ga^D(Js-#3?`y_Zua)mf#?_@kYf zSBWOTO?qO~ddRto zC@*UlXR)7NiZ^&>E50un3~p^lKgs8hqhqD(;H7(>=lFsp#&gn)ud@b+_8u0?m0QIb zUY!bTt^6+K**dHee$Sscw@@g|0UF1 ziqdnbjpMuUtNGOb-n>RjlV0gdVqjzMvo6)d-<8=#lAYTs&obc-FYl$^-U}|dU+UV~ z|K0cy;(E3JkpEZBO4Wh=-uQZDEB=Ku(7_rE-3|Jx8jU(L_Ji^NXwRkd-OmydoFOZ} zo&q;Teb6hBLqOS@g+GCm}6 z7p{`WUzcapl~t=UZH=Tth$h z6!nNx3%SF2PV(MsAw06gOlp58w8DK)Tq6Ihhcf}a>F4EAOOK&0zlwR$8njaz;i1n) zM`^t{5u01XVcM9FCJQ~e)}nH0$}dL8@HrZrXlrs8yukjdW1zaJs$rvli|T*q-tQ;Z z>Fj8OBO^_sAEXd#JZGMouh%F}w1C$JX=rA)Lt}rf#S>Sm-fRc?R~IO@;M|l#$62Lq*a5pk<%ieD(Lo2Bg zO_w?}FY3_!>gbbakxAmP#axtaPe)~=8q$^q`atVdzsm)qbLEthQa zkxuHMee4N+GQy&`>&gu+xoTnrtwp7gzh`6feCkEoBg$b7)V>+)`*ildu0ty8koxI+ z)35R|vy`cR_a)={14$2fDDfBk;d?kD7w}mp=P!~|_@eV`)(}gdOwZxK?)}VN7a5#hEw!vu1Kc^-U$Ir)V5^GZ z6)VGc4$#-oy`{R2s>?mVt7=$uQ|pSl87xQb;if(e-_#2<%yZySIF%wFqCUtk1wYEW z0`xFzE2V{cZyWh+8@^m|FRYNxg`zNthiI>sa@x3YWK5%Y<4Nbn}FIlWv>a7Q?13Uw6aM-R| zEr+n&J9cK9S4k@ecLURHPAwIMfeb~X?SH_)HTUd(y-_zqIMxYn1_@oH`KuM(^RLbwD zKI((h9FaLmrwu^k3EArQLbN z-Iy#DQHf|B-;)?>UjB|w+`r4@&`eW$UJKzw?l7L;>^{5(*uN7Wq<*Hm9z0788eHt1 zm)O0B&(&}aismXgv+bow?mh{ z8IGYg>N6S#=|0zZNqO%Y_f9?&(s;7 zLFVvP3pn}uEh&$PlVWg_h1mZ&=e{gHMH;D>Rq^*F`rk&^0{)DBU}ve(-aK(ip4_;F z7In4^zItuovefsg2F*Y}y1?Kri?K~5d_=AAZYA_E=eqaxQ9V1= zvQW)MaM+klcbS6r z41E2&rlOr$9`>(l zi{^zgW9%CsA@l(Mi_O{ls@1GdC;6A|`_6`T^uxFd=uwmsx0J-GZZkF{*J{A-we%aS zQsDezJ?e;u`iU$m6(oR8ulPHJ91GTr|7MMN*2SEEz7bggpApFQgc4hVH>%;2eU3h5M@s zoJnm9++6&-74>L4H++!7GO%9UW7S#t_?Ql3f3%Q$mGiOsi6-KpXnaEn{-un)uYSKa zojDBb^_X11dHw=sL1&61eUg3XXu1;1JFZ zjIQ$_wY`4+c0K=t#Nl1koKyq#hi`}xN-LRH4d}DADFM5a z$QjO-jzV~gm~ktAmMyiMB2H;+=B8Dp|>y$F`>C(FQQdpwe)-CYsl7AE1_r30yR`z>d zXtdO^chZS3wI5nZewH5+X~fedDQV)h)si`oiDHX>^sZIQWjmVE_6yPTGiN4c%}Uw3 z4xJPWeO8I{hBf35OX=a78a=!48`QhN$i4jJhYgJu)?rX>gI{>V>yN+uY;X`7I^67a zy??uSI(Urx82fjYH%SHDTA2@{rHT5F>KIqEHyY{XwGjX7J={b5)8B@68+)pqyJ&#A z&QPq=Zu~5@e=9L)H+O;RFSg>twC*1TKBF>IgVM|!yb+pFNB$>HaLD{tUjb|uxx+nhle$e<3&zHQ=O%*ttyVlZwc*k9wc zYI@+k+`Ya0e$C+TNxH9I`6PSnQB|U&CbBsOq4KuHLAE-ajze{y= zhSy?%8jNan4f%R-$?SW5R4=TD+Iu^*=s6*&66F&r&G_LC;x*Mm?O^X{9l4Xo(6}ZB zQfzrmDgKRkXIO`)i`j2o2erRy4EDe5F@Cn{&bE~^C;Alb=O;OESwu3gQ!C%W381_% zrNkB`)T--Q-!|5>zkh&zqq?Bn!eMj{_YI|L8tv@K=#Q=BlAbX%P zr&wZw(J-dg)KZO3FF9KQclnDnxI6hdsz=q&o-uT{u)S)0_x8MkC-Qr0sqjU=sFzx5 zYo6AJnXTF`Mf;iCy8K#Rdbdc#9(bIoc{eon$m5`FaK2r}xoy`v6n}F+xk)walYT!; z3bDEF`yBEweS*&T$PQ|po5_uKlCy5d_S>+#KHHZs6I<3{H~lU{D;Zpw^Hg+zdEG<3 zZ8v*fHG%bU1;=AM8V$umC*)iBCe+@Vht`3UR^C9hk=6s=gHu-xcYWHqqr2f&tAN|| z$@vIF-?Sv50H4%o+}(Y|orcamzEth5d;bmoRL?^!{j3mXz20%AUgBT%o>g6Y#R>PQ z?`QviORTNy@ecdvfsZQAt&n%bXz#h3`g{7MhCHYo+xK+5q+j;|+rJgPt6uRny}JhA zyyyH5fVEAf2a@E7oai8y;Vbe)QE4*4tavznPtGwIMCm<#jn`&?465tH@0`1 zI;457Yv2NAd@p<7(L^75z+hI5hc(y>-p_{F-t0(bX})0|pT9xp5G!-?iBHjpf6L!Z z-=~NB&bXKG9hGTi66O_#=68@Jg3;^c4rnCL81^%2cis28-`lI}$rb(4D6tUB&&QER zIT~?qA8W0CNNWy1f)#FKUbzR~|J{cn9aNpItpR`V8b7Q#LO=B(jg6Wb>ft3pQx%`6 zy0IEF46sjL6Pv$g9dzF`)PkL@=#&=v?e$>yi>aHYJgXNE+mo`(%u<||S;)zQG4Y+>}QXZ60hfzcWHd^fjcMk_HA+Az5{OJJw|^n?6Mzq zMtl(VR$t?;kJ?=y`n>z546T71_E!yGC)Q&36y~R~yXrn04RLx|VumA~%d?%?bju^Vii7WD&gDJtfBEyTQV-^T5F7vLmtT!KL>sxp2ln;{ z&VzE{>g+-@_WuUEtLC4c5q;GDy8j38KdOJ%+tW|IqF(A7-Vv{MN_v{+5H(Wx9PXQf z7D?hRIkp$QpH=(e$ng^o>nq^V@k2$_63Nl^%xgWQhx70}DxTP(5uf;0lJ7r~)#$Y@ zp99tx?bcoNw6s5LOU>SH>NMtp*z=$4ty;;y*RvmbkNz!wNb!DqKCOXbS5-?|_3a(N z41-U(vtS1@7p4`L4s~_PgAH@Ez zStGp%eD}Je)j0)?N7Z_TtAKO2i=S}}e#$r9J*@LPL$~&O_WKXagH+|`%lFv&*I#~N zt=}54rNJ|dZ_<6=$$EUl_Zixz*jaVb)ZT`sGT*Of_2c`G@;s+bB0~!#;$DhmB^FTQ zM)SD>Ja|@xJd4R=2CfjD2jGlwz6S?saPyAmGw&&F}poW&bYWUw{D64@KiZKA9vrh+3bIMEi;I3cs2b0r&rZQ z)z7(4o5ML6@;}4c@O!xvTv-FX3obc0%SX;~KhGe4Q+FL&5nksF4aZ>bCqtTh9%#rO ze@uRa&mhk0*SR_Td^hW$T$#>Gn2q@RQDF5sn_%_b!Ezijw_tXdwV$_y^HMDX?BjOs znr}I81Nhp?+&ts`M?SBQ=E8&QYrT{8j`;JR{$$vHSj&3Y{;G|xYpJ}3FLI0I>3y)9 z1#s8bGPgudraXOl3FJ`lGSwKh@@i?Op4U*o{XjlY1NUnY_iI^3wh@Q*X`h9kUBYZ{ zD{Ii-*N-l6m>l0}E0)AGn@r)?q2If04tJ>5#ul2(e(df!8$8dfdE!A|%X21JH?Y|I z@izzb(KXOBfnPFo#QE6pLF|7Q&VuUD>wRF{1^9|9^b_9qqvy!JQa_>l^6AAmx#b>& z9(^dhZFk@bPbUt1NlZzcL%pht*hBjzj@Z}YA4bU;{CrXn7$5A~P*Q4WiROeq#bnn#>7NX3`eEK}wf*oq7@C`K z^|UlJ7_&idUlI?!e@o4#mHep*{>5T=B&j8_jxRe~iLn(+!d=bZrFtVZ^dt(fzw%{h z+@o<{dvh~2suUwHX`>dDNIm&9`+p<2pdI7K8ydw9lhC}Kj(>;mWj~L*+M8FkzsJmZ z*dN>*_kr6aY>sXBvFF`*j90ZgIM?C*zlX1@{Z;GUnYGw4Vv?M(vXj^CT?Pl5{;qfT zgS+sAw_<-Ve0)LJ6iwVF;!*Zl5BKlO>K5@_w@o&FH6FdV`*6Czd-ub)^7{Q7a!K?z z-=bmr-ACg4ZbL^|W9_y^I6}DFda1c7*2%Cvw(Zk?H@$*-;*X!0S5uvGk2Uz=3FabKp_@2L8@4!%S0{5t=J@h_{zP2~oTJCaq?HbPWH2gVu zEB3f6m~A)iTzC45`=_%eJo{K5XYB7Xkr;F$wgo@vf{mSc@2b9ldsuC*&#?U+OnA@d zw|zN5T)8`I`8|Jxm*}1c+FqW)a?_PM^{qJk4t*EuCGBAJ`$`NW#C%5mCfa52nBrngG%w?gWp-(t)DSACqN zHnh~}k8u}$#QtjI-}B$9{dG;0A58N_jZbv;b5MN|Hq(4X&xqd7dOnoL?K^5I-?3)j zen9Jkyrld&H9*d9Eq7c;Gr76O!vo|7e627wlYS#T7tUou;9cgA9vSgqAAj>Z^1N>H zy(;`u@L{wd=Pvk7S6=tKJ2{!gKKkm8{au&^ahWv7u>W4>)to1Qhr`}#`(gXrvo7|` zlGyR+t3BUiKM8&Z>}fk`F5F$LKlaxraNj+&42rS8`W@=p^h%1ULHb(5owv)8ebgt{ zj+|i5ZmMh;1(uAt2-Rv=P4Z1&jg>8YF7{!t;bR-vi<;*)5Zh!j|L`O8vZ}$Vdi70Z z6$ZDq?q{{FVmQT6o$ImQ8^i zI;iGLeFZftAV-mtgY94sQA+eK`+3qI)K$APaqaz^a& zNp@r5U-)8@+}V$&Eqg!MJJj$w?cB?X4Qa1#l#p{_a?#os4c{~gyY4S*KS$4+9D676 zmNh@$mNT`BbExwzmdr-%TVY|;Iy9zHjGNAd2b}jY@-nr1QCbFdar%(#-COP2ak&!cei!W6J-BWQ;F4Hw#G4M>>vo(mdtRO3 zWZJ>c(K#U-azJa&X(==K1K2}nVtdE~4qDq$=ZTQ!0=VGdbJ5sGbE)Aq7(N5TcK`i3 zP(SnobDp{u`l{cnYoNYo_!;&^@8C6jNzJL4`$l^{ebgDW-t!t?SNS~O;Ba_{{Y!b@ zf?s^adA{WAB3r-u65CB7UIACd@9!}Ut@P={{WIb6ARlm>&gXazO((|G`vBW34oL3> zSN>)flj*F%G(&IR7L4OAuygthY5byFORTQ^GRiZv4g2dFIFJ|b`Q)2!yQ2^8bW2L9 z{c6mwb&{?c_>ZqfqEG!$ZXbJ0%{~P#CNRX*H5}mw+D%Mr$Gpbr9X1Ac-!`y%HdCjg zojgwNojYgbQl-R4x))D5!Jl^P4*a;!9WR&MZy4dU;b>cF~i zHw_N-%Xxcy^7b!?*(b~1$x~$S#Hq56XFs-enmidhPvZ>ez011b?+>!?_v3@q{;sT* zo8HU3y7K+bCd^k&X3v9f+%-gYp2Xx?%A6!BkEjd-fuKA0*jlCSOLvQKsJ%bnc4LPadhd%jsT?gaA z_Iei^`=3{hpY;3G_PPeTF8Z<7D)cVbKBwYHbziIP`@p%V?K`RCX}&$c&(Skcn3r$d z1rNNvWb2pgdy`4@v&OTBO^9VCiwwpGVWC6tl-Z5hPzgM z)Nl>2k2;dpu{DO$_)G7EpLkXO)r${TK6#Dn+lUu*O>|B5PFAgT)iiobjqfeFNF{TQ zebfarBct&Fqw(WY+4m;wbrbe6d_&~AF8C4M&rbS&%5-s2TWkGc24`Ug@hI>2^7-BP zD<{5JZSTxF>E5@)_O{#~JH|~A%Q5Kpj#ZuPiL!Onq@f4f58Hn`pHn}i{wTl=F4ybP z(udD)t!yB_Y?i1SF>?Pk9e1nNo4Sc4ht54H-=q0&> z`u_nw*UJ6-JSENGD77Px+BJ40cAR9`-j#JwfA7lQ;LQG4UovceSM2M8eVvVbfOSy& zPsZ0zz+Yj1XI(4S!-2UNTh5H3dq18%&l(u^2hV6le5jAH4&=ZZFIr*$Z9JB&N%+}F zW`>f$ii5W`ut0U-m#M)WiJ;fdb36h*#T#<%upjk$dU)O35sfu+`?QyA96gHJ(h2-r z)kUAg?=Xe6A1l`L<`Ks-Yf)GvNx}4QsCNc>osmnNf%8X?fq8V12jGeOxu>;H)5+dd z4Ln_g0rsli&xU`;_Jh3FK7iWQh-a{^o&}9%w6FGxobK0Oe>HgjjhY?br~M|iz1Eca z$c58m(EKHL+z0-3%U2`BjvUtxjcN5iuJqDfrocDF9iVpK%il2kIAd$|Lr(hO_tpM- zE}U2+XZ(*dcdsk;AN|-^1ALh3#Ous}HNJlbetJ7``3_?B;d5X)ycXDBKNla6L>(oE z9sv3W%o0`QR?;^>i(GBbbN>iB)kh!6B}YHB>o3CVVF_Q)H2lsec$mhphG6E$j^$_K zd&YC_&_7=G4}XxLix=eAAHnT3*3plG7d`ALF>HYNvIZxZ$vKXW#IxAAAzzL?)wjP@ zzk@&5*s+)RMeX^Hyw$*TP}k7CueobWV-xl22IF4Pd->--{c7lissAzhd!KBt^@v>d zee`uW0#>XgM*iB^CpOr~p5J#j>#%pyB<@}lUb%T#p_Ipbt2uat zkB$3S?^%4e)=L$`rG8(r9r`u3zupJBeuLUSraocZC*W~ZZz?r7LQZb7lr8*DwkGUN z?WwcJ?U{vf#P$w+yqm`v|D$$v!Uw63aWL$x>wv$}2b-&pR~y?*m`sgd;~wr`9=!`} zxNnWUuYQ>C+pRytus`=NXF=^h90RG{^<#aFy&lJ0N(nvg7Gh7Zv2e8b?!F>-4^x*o z8e-UA-|OS=eowBr-jq}DxNaKFT-B(rScfsPooC0mv0^!P6zAe=S^f2w%&mSY@nK=q zz3>ItOXJZh&VlBrs>{&^W}>dBM4oboU*JwSve#M89y)@qV}N8tGh09%qZNBMlvgk( zz$`ZRh~{*9Mn0-fVvnoFk>&-O!>W&Z&E4Bg-9x!Ib)Dk^@5%Wcj@)(QjXkm*O?xZO z>vn$k9bFajRw>7baJ-*y-0=;ly%rT=5P1$un$uEKlXVfB^gD(**_1wgO9@{ zY<&mc!x=anYQ#c!4i2rsjs15y2e-xB(gV(`iLznjXU5-VId-&c963VPsO`yVmVWgW zIX}28Y_7F|H^d>;W%Tj!*&3_oVV~lJXVO~8+83cy7K1M1mFw_U9uqGxdS2uW5jXD` zGc(=9k&4|=f3Ce$jdAo|)*AHy?=@bkEmfVbSh;ofviMO;yW@RMT$nFe|K&)ymC1Sj zH+%0H6=a&G>(2hQL(lZ|gs$$cWSMi$8O4N%AUPvO%m@YyfC3VgC_z9ZgJcjD17HHg zEaq5cm6fZyx@XUxz4qE?o%>wxhjjJy%sS!M`7^!xT^}HY_&v}2*L9Cimqo1eo`YtucPs+!HGv;$shDAa~9R;vr`flM;J6Q~xpV-rR@Q zI>X!6&aMachdXet{G6f#BII= zmp8Qn)31DuUp2Ph^r(ysP3fCs@ngx#-<_!wdkRz*QK`!4YEF&H4%WaIQmNv|GM=R>IasFZgc{W+gWr(> zc78UyqM0pfNIj*>gp*+AD$cn zm{PVJRouG6;10!VtNm)QfWe@_=FEHlH(*kJsOi7`4g4mF^yt}d<6Ymz2bkLQD{76V zKVWL1rXFDQ*yI3~?Kba~_i18AqvzHfFTU99NWV0-Zq6%Y@EN^plam?j%{n+d@?tYo z8keVg^snzWv@2&%4wym(?AL?;x%&jQaIx2E;~XdEe8^|UgK@*m zPQXZdCDgGDR?+%tbHMFdq@C=##dvQ3w?9e+;m5%8sRLK(qRt*`1K;F~N~25BP2v); zOg78{|C-w5ijM}!o%`XSH)?U%NF@h2M>?G zXY$|tQjx?sZ;5A&pS+3fS&bh&M4hqqYzsM)+0VF5PQe@nalX|Td-vY^=41Z!M9*KD zcmFAK?4P|xme*BtqD1+L$;7t>ss&%5F0GOsicF=zDVnw|lU;jsgxXco`Xf5%y@z?6 zRSEif*GjiQD^JbNt7$S8TuUhI|&x#)N~ecYeee;_t!D89M3E@AF~ z6Yt-dnAG6T+w}j0nmqRB8gnT0Q`?xsERU-M8{@RHcOTP%fN1nRhksAL(zY1gfA{{k zW-BR(`rMXmrEblkcaf$9{;f$HQ@J;Z3UPp=*nbPNaS!2S{1)!(JZ*Gw2TOMleCxzQ zt!ybzvt@1I^ela`<#No zW!|8j;A4k?kGf?p-0~B^UB=J)VBZ6sj8JtO`<;el=Ai#=n(&obbLU@_FnxrF{z1%QnqIC?@Y1Yz4_eS z3nPa(?n4rF)wIwIJ*3xYxNaIMt1RCeLhiL^T@by>N^m9Sz&#hHmik7kR%&uEW2a2d z@$Mb&BXxy`J@X$uoZKVK7(|tP{5; zN%3orC~j>E`-{ocQI7ESB*k!yOu}6ED`eR=ZL|&0D(0>Or`Xf?wPT*fS=->5UJsT_ z0CgQ7Vkc8qFts&LWY8Pqn^`7+ZK9iBiM>w)I~i^Y--|3Y?ZkmCf4^rmpyooo#eiQ-nX=YH@697y%ZpiD_?!?kw>obEdgDFOTBF>V@Iksrv8yEOg1U#kg> zzaBVDZ(U)Bs~N7~^d@F}8da56t~}(BOw4KCYeG;eeiz&NPXDdkjJXw)Cmi0G!CrYh zxdSmIu~7>C;Rs)=%B;|5*b_6$WAf9s#%AsC^iqU>fIMc+VxNGxhg@LYLUz@c8l06i z?1D4rO>c7!@m(-^L1sdt#{cHG@}&N^70kv&bUk`?oDT8cj%-Th_u;Og8%gvlj4mAF z{Th8RwZmB6`Jt8QJ*W4XNNmU9_lyZJCy_tn`&OmkD>B%vNYg?8C~aN55?jQa^E5Dg zm#t!sYN7Rhy{MOZy~}mI`TabJDLn=c#*Yy9P|I`1U#th8Y?t#^CBX4peYjSq($DJX z<`^*LXK+Whr~#aV{k~hln;owSfBQ?7rsq@le98L<1ElpH^Qfnon`zVjpzR9tS)=l# zLS@hgNXDiZn}1|eCb>bLl_Q&{dH?35;uB)I^F|KlLZDX9 zhwju?S68{uoT<#ngYdMU(|Y^mie9%)zRYpgW~76k9;wCnMqgXz>m6L#Ga<&w&R4L@ z_Fg^MnxX7ndCFo|?8qi!0PIaPwkIku7M~TPNOsI3eGe!C+rQt(oP&zsX+95scOr=| zBK^SE@`>ipczT(0NRd89CfL!ycy(O9K0@t2ahXzX8|%G*4}^&h=nqF{~7P{W#p0(FBfQz2TP6T`%Q3n;cz)qnaaCbX$|*TgDiSF(-}-{df_lpR_?JdlIVqwmm-w186gTKNf0&Kc1==e}_G2S=t-{`)vJ)T3UJK{^t{cs^}3Y@u!oNl*vFW;&i zE8y|jdyJU%EW5`iw74G_;d7VjH2!+rFMc8ad3Nl)JZDCm*_y5gU?O#>CWAR3zE+x5 zsGro|bOx5~oiNN9gMqTfE03O8E(e zR@_*akx5^k{?`|usw^*0+nLSV%-WDV`p_HmwDP8c$c%zFz?(< z9@S{cz@YYL70|E2aF= z>tAcqKmHoL%zEnX%);V>Z@1qghiqma{swjUPjwi)mQla>&l>luzt-4a{jFT+MaFH2 zP(kDgV(tQBP-0U01Zn2|TehfI21X7?nJG;tJ~g~+7i-V4FGz3f+7E+MQ*hc)8K~2C(Q5%B5g?Xg^`7eJ^s0&!7==TxsRKkYCO5BpH^qtu%iYn%PSFvZv z`_4YYJLdf!ZQ^IZFa8&&E*+UI&^FWGiRl|i$djw9jC`_k%4txMGt+ zm#wD$6sr9$@XPc55BlsTXQ;#eH^P-OK~VuA<cCAAxf%BNT3{ z+%|0?#*JRHN0a~YA2sv6_cZoz{+o6Mtkbg_%&2vOJ;e^ysp50&yxb;+XP*4dOLFCd zn#jGG^!NX$sl+2=fB8$!-@yGbLR-LcO$kZRaqMUA9{L8>yEpn(z`Ms+bIjLbH)F$h zq^dlb-RANprG!N&EhL0J$5#Cx|L6Z=2LUWwa_^%%bCnyD36^Uq@9PZv>&@2tG&*kS zIq%xY!kpgsZr*+0`SW-Gmmj~oADKVjlRi0_yVQ(qPaQqYPS`1Os%qtjqf^_-*@B2+ z;a-pS57%zj5bbu~!JWa^dv2B=ePMU(zxPOd!U%jb_rQr->(+(5e~&P1P_Ux>$pL)# zSkLC{7UEDao5J%n9osVn8#L{=?`gy@{);v{dC{-!WM_iCSz@6J)tA5{pf^T-_LBX= zs_bGdpig0BF#Ej^;7R+PCjRX|XzVZk7R*rkdSHGgZ4Ot#0d{%!pi4Wd9Q{1nz`JO~SDJU;tJ}52)a8kl z)~TjaU97 zN-Fl=sv=II*A^A~Zc~}xc9r^USD9~!O1y*lI7G!>+jJamiUPQO^7(J_+&3$Qn!(|4 zu$-%&TO8vC%lgJ+ur$CZv3Tgfsxo+0|N0o-Suh+Py;b$O7s@H_Q37$e!Loh?c2aX; zsZQmWC}~Hy%2RUK<*8@4;*@IHW6z0CQDaeswKLmw=DZsC-kO}_s?P!w11yar!SL6C z0U1UedAG*~_SPL0!%VDt$1WxlscEGPRU0?_3mW509 z<+D$zi=;EpM_!th&u-&&`0^U%K7WP6)&(<5$j%!xF;5>oWY*~>bHQ)*FYsslGdpoV z(A&Owt;5=pON%=~Ew@RL+jg@P1K!)^HZ@Y~If-ClWFiw8b!#BOB$aB3;a0<*_TSm=~h}IG1u`LcrDp~V%MO6{f*k=U=~!L*3GsKW_Z|{xXCQug)8u^ z6j=MjW*+r+>t)?K4~8WBm0h*X>a1#pW2^}by)t%3iATcXwc5^8r5mYPO{WjO$XB8C zM%OLz0ta)ooMza`*TGxmd1cJ?JkjURUb9E>fH_@uCeL5di7euo1BbN92cC0yFMs&* zYkl`6duGJpN4AwErQkf&fQ#@--!bQA<^#(MiY?5ny<0-i1NgKqUSKCX z9lSYeGA^qv`Nwjf%rScK=el5Yz?_3FL2w$w=M7fs7QaNDD0ybdyrTB$JNds*c|Dwt z;FcNr8=N2`|6aT@gB|oUj?+W1=8XJ%4?6J1oF7%-v5NZ~s63u|+=qPd+6vHzLiC`- zcQemT=*LFB51I4(9CO{E9Ow1Q;`d8f2G0EM3UHBM=^DGUHy^*@UV|+S)~3NZHs>#O z;LZ!fv&*bJd!8i?Pjon=Q&lxj!G!J9$%1pL%qiAka7c6GGL#pe$(?784!oSJU=#Fk zM-4`e!EI@&s8$m_LhBT>>um7in$9zi0?#^_x~c52uUhJ_MI#o-W8!iJBWF)~X!hgh zDR7aewz;j>R`($GvzBPin3?i)^im4;rStMNefpH%+RHckivH#c>e~02n=_b~&$#oZ zr}G1Qw@;W)+_x`WZuEXUS9r>KnTz&@?zZMx3_eD4eS;dA4Q{Dx)B$Fb9?=^;8WU^n zI&56z3Z`eMVv)O%e;oI#PX~IR#wKh8OJ<9r!SD=$+jK_=v2oSsDzAPlBY$&F*1b?! z{S$S94bt#r=i_u)tVYzKap{}XJ%)6e0$`sxqd=|Ju@$2CwnE~}95YULU| z@LY@R*CBgs0yZFz-gKtZI!o?Z=wlXqWARG^6}z)UCspQ@9N{T!V)@_kor@3hV>YZSFkar!g z(mJ?7=7Gg*2OjN;Y4+OgvqfIEF7S=b(Y$dsvYiMIDsvQ@S7YbE?r1yHO0Ev>l-XCj z%Pb$WoPYe`C-}BH6u>NpySukmtz4@${_C{{{989_UMCArfVlv!`sH?27L@2Zx*D~M zIw)AkMd?}ioB%MZos>)uEgr761au$)zN#4PYRo%3aLD)s?tLsc?}yOSjV}8XpK#6M zwk|vQ%(DNM4XA$s&nxl=lgHTnUY;BFg}C>9=TGvtd;qv_Z@_%}0z8Me{{qkTePq58 z+0(-SXFAi#5Bd5j(C(ah@#S`n#|<2O}P4`+ROmyRZ$pkLcf z9TyA*X7-=zWEPU9E4E`asW+O3sKy|rSdGxM^CRdI|NzN5upJBMhEtDn|T6IMymtXYn^|P*80(h<7XV)wHsUU zLU)M`iosXA-F{t14#r>?o`7QDaR9QMX@pD8Y?L7Qy6lom@3p!vCqPqe8d zua^1#TkKRBYz?^IzG9yaz8SLv^wqcDTAYTXGJ3i$|2nPFvcZD5dU$UjV$$=Y;e>DW1bOzg~-`+c2jZ0$=!gi>J}j0c0PC4n)0+1*5t9 zF&-O{|7L}**sZj*YpSe)Q@sK|2@iyc|12MX{jVSn?7G+M|NHL0u>}UV&R_;wu^{%~ z5&r)U9P5|6zGqLOU5A6I^-_DyN9T{B^Eo_?9$2zBdf*(Wba+ZKkn>UGp5YXT?EQ)P z{B+on!=J_4d1)WL*{UXH9qzw{4~=%8Gq$%RWVvp^Ndv;>TS({N#)&M#BWfkbV`26!xW zl^^;LfPKGo<}8?y%;8YyZD1DgO4~)f0YfI#f0MlJU6d4cR0qAnxcgfy`=5y1BQyw_$q4Ye%m%7pC+z%FKgR+@#FgqzPu<>Rm!OZ*qD|sXTKi{P?`tT)o z;mhyPgYSP}KO78@GZ&Q=pUNKc8C|Trz+8A5Ket&~Y0Wx9zp~*(9k?t#YRs=f7cPT^ z4tD@KWm$TmP8U_N17E0G?)`}2|XHV74p4`93JQf_Oo4nR4Zf~TrkAeZbI$nwR|3vP6 zJaUgE1~9Ro$pa(cP&|lz2wuKjDG6{nR=-qn`D0{{{HwSp?_$8pdiI#{372ocQ^I|> zuvx(fy=Tduco1yMOI>eNcNQ!ZxWx{HRWq}YsNAhd%4c>lk9mt+XHQ}|cO3=G^(b;r zM-P&hdnm=hL&?~-Bg?&!w~rH ziTs(1eP;u@9`Nx)>;lhj?7?L)vEyUUfg5gm_8BUsPg08?KUdB@155E4vGtc<_wHi# z?Zy4+&LeZP!)4C*9D_0NHOKgd@2~|1kH+8$m4NYhnfdoKC8w3VHv^8S67Y#HG9PqC z#}c#P769+<9Q*A};5o3@e&PsmXIeh^)90`qt;7z^^oip)9k)6}57=R9E-F?@D)^|? z)adhy$g`eWI|62|<^N9peR|OMG`7L)_I>>oc`tXoHW%DUV!+$yuOs^wRS_GWNI0gG zeN!%pbOLuF4n@IS5H5gd7aAa64jl6-whex0pIm$H@vEs;N;|cYr)s8FDPOk z^Bgf~2X!fWqtqkHQjTIjvSsl7E|ynmr_Tm$;gnIcy2Y%$i3Pg&Sp|Gg`SE&X9lH*O zRJ{%n=NYcrI1~FLdlLi3U<(q-xpz5jSInU^Dye#*LU@b?243?&1e{cHTBmaIx;81Dq~w!}a@MvR?R1o5^XS+*g950534Llg?9T zl^>g^;>2_-&ocM@|Ev6s9+-W#?|%QIzJ2pGww|4o0uH#qjYZ(Sw%-7kkC{L2;!|*F zUOsxpo+sFCuh>_7$$8E^?BnOmr`^&;W_KDZYS5Ef-MVl^FXPZTFHIK?wh+`Qq-fe!dskcDlfjKic+4X zohmNsR7n~5t*7p&vigQfN-wLlv>my(g0I`D<42CeQ@m3tYY!?TC`LIO4uMM&hy1}x zb#T)$=8&>D8IG<>M$Sn~9nk?NcrA#{7TGI$iM?W(V@hOpEPe$xf$xpMKkT=4RIuwF z)isiX(o65S^_;`K=XBl1=5spey_?UCE_CrdH;EG~D%oFm3AMNaJGplc*^{_j)1aEn zV=6nG0WZfT;{0!|{Sy-d8bAN%e!uU&|K-zskJ(!>I~5mCUr-0}{`0#}!RWoA#?l6I zd2m;V!_3Z|;rljx>&9mon_-^c^Rx~>!~B`q6X@x_4NfliyyPS{qFFD9P2b#Rp6^tP zH6wkx?4%xC?b7|rU?)cD?~I&UC*P8XPj5TrwuQ-@d=N+;k>0Uaf-Un`Qi%YA4t)Ov|?hNRILu&re);vDLI#vU-m?~ z`FEMmgR^!yGRF_ZBl|e`yXWNGA|$-Pf=LKl2JmF|Z9cUVq-XRoNm%XE;lpE;WSi|skf6tQHv4lG%roi_H& z`-ZCV)I)V4`_4}6JJ0Jz-q+EGcjp?<4!+*eO>A)Mt)lmrGq=7)_sQM6*g?B{_B=SW z7cBYT;{8=*7J?)9z~Y4eg8Z*f{(Z9k_nu#o1H2^{c+2x^?)Jkw;QSWBAr%M5MnpXO zk8m0>uW9|f@4wSm$kyZy=9vH1V7m7G+&=l6*wW~Q(S`4R_>m|46g>~s%ssElEubI4 z-tfH#7G}D^s5yi0e};XyN!{`s7*&PQ>Dsz3Uh|i(mG8c`C5 zM%lyNgxq5}G2{a~$-4^<=jnJxiE8M9Ts}o#qve9C$T9Zq+$$G+^~@=AGy$xZu_MQ8 z^l;9wi5dY`pgF?^jnxovF@}I2GJt)o)r%ME+L4_)A5A@fFLox7dXxWd9oyU+3uck8 zWT69@-1l@xN1fcVPDj05-pPI`KHfp$3&C=qzf@rh?Kp-5X_@vda8mG0INBWHeQg9^ zsN)&)+)vciMJ$NCyE@_9;B@f$)y`+S2EO~Xjwfn`7ia&bqY7pI`Z{`SxQx2+wbvS% z!{MDiY`a3N#ystbO8xl#kNV8mfOq+RpM3hBeeu7k4Sk7>%)S2ZNBUlm!F+wluEiDR z%~N7@@<1H(r`N&lp_cF?SQ|$EoId$~`R?<+pW9~_%;$X=W|kfp`+{%y9-DOQa)+wv zKQxq8vtQPwC*Zr(VZX}3aO}Buof^Vb=W?;q0}bR_D89Pyz*>mrN!r6e!N)z9{w6RaZ$jTt#f zyp2WH9mG1D{wF2sprXIak)?BR^1ragK17-g^W*Tt}H%3SHg%yDTQ~I)H-20`(ddraeQbjDVS0sPF&vu!1&b8An+a<^!4&X&R`P!E0 zixlLvRi{tE^L6E^u3n;V#%gJYCHV0=bmdHIb-mYg(}@2 z%{(Vq{P0*@sb?-8dv*sbjr;6Y7H4EpN4pOu+7Ehz4p{FWJ@}K1kj1yu1&yEn9zFT) zTm1fOaO55^V|E9eqSmwE=fm-GEKBu?nR@yIme_6Uc>H zcmKYMsOiLnl`9hY$0Cm?YCa*Jfoh2{V9g7qIQ^$p7LK_Mf=(t;oEM&s(3c_wfR5m4|9SeHU!r`>L*jBYoL+ zMZg0awZvHw=vT}#Pm3d|rL+Rx5Aa>ctuCIV9+Z?#4nPf%Sis~1CKog^F|{7E8~)vQ z@T&0V&!2*$3ijS(aL4tTS_l^GSM2=eTt)N49k z45xU}qj%?_@{5r_9OJPE>J`3hy;8t?UTNc~gs^b-hlwZfB`%ID^x>;sd9;!9I~y=`@E8pqI1D>59(yo>kB4Y7`zuGC0(6|5t#$7jU5XB% z&JwLwcJ=G&VMGV+Q_AWn#o8=U?0h>#%vqp4b8Ho6vrs$vIC$nf?)N{_r;;SP(59hvcTbx`o!McV8d49^z1-(~K23p?WQD0ZBGg3Pf6?d%@m11_Bcqps$b zb;|OtGQYT52WInk&cnXUcTl9wavh{6dTd)bezj8h^jh+cu!EUYq#At056J3!?)UfH z-*3R9dh;oLdfxlJJ8+LM7jWa+O)%!b-9CN-eu-Rm+QB0v-@FTl#iP%DBCenoSWpJ$ zF1zhNU;~Wou^Z-?IN&Q{f9t%{1xx;X-ROXo51@138GFOi@(p}!@=RlszQPwgrpEUO zxnCeps313dcnLlYYJi6iRVXo~i~HWA6Xfs(B@fVr2iE;BB%eDHf0jKxYQ4n%(+7`d z4(rak4FAukpH+*Q#5SgNZBt~q+>@? zthw*n!V=zHs@6CPuL} zOvVIuWwstxKKx+?MHi`Wbn9?Z9^6Zp!R7$x?4#~(cgGwX2w)%S2ZztKJF;_YXt1*PQSTP~CPVMnkVU*bD@b%6c>_Qc5lPd?$D z5BL`OTe%@}H<$>0_Q1%!?-;vedK5j#{~@~A)_h*&^o~n2*iX)B)8Wh;)L`zbu#EeU z?2n@h#{Qq6rk9a=MPc-KJ!UM{WH4THj%I1<_=)QO{zn=$WS9msgFR&6U=15QR3oqh zBk={Ju>WK5|6@5uCnke!QJ>Fz1o)2Ui5bkf#+^BJqF6EXY8uv1V+r-qm2h8m#I92L zj@7D6KZ1=pp+nxx8_${tcj7#S&Y~|h+uU~>ZJsel8SYi(QIdISInYm2i^nmu+IO(9RC;M(OI+9eaoV>Geo?m4yq(f;br8iuYZ6`@ki>Qa3oR(?d1+)4(;Vza?`JMw)i}J%Vied z!ROQmzeL7;I?#Ln|8-1g`2+flUt<%#;d_n0|2Og{&-fC1^d&m*#cTT79XHrVGCX+o zir5|xZ+nm8vOASiaF_n=V@u|R70)a^D4?EyH0y@8FxxS4@Nj(i7-ILK`rx+Is<9kM6|3?lPr7?rYTJj&tPI|DHuiEG_ohsrTl`scG{9w2)3*rv~ zQ-`Sn+p6?Bvl>@)q5LL04`3tDa6tacv|}c`%ABw{j!F&KrjucD>}-bV#PKUCD(lek zvdh#vZ>hA5ovyS}Rh6DmT_xP6r!T9fuwGS_=XK*5_7~aTdj?O`JGtMZ2V(Jxzy6vT zryq3t!T0Lue5>YM_Ot6w>0Zwxz4@kBu3zB;-uC$dWZh>2`ksApf%)9neq#sv?tZWR zr%pki%EW~xH|*)|Ru{dZtn^blPH!ePhnWU?UAfF{WM;K0JGn_ECHIwI{!Ax|9?NN- zyZZgMpFaA{M+#zJ=R`_6Gj93JL4jMIkfh~yOZ7>=e#8JnH5{E7Ie3I7kOPbwFh(;5 zjbx`RnD=^;8hWq%&k{qO;o~zWtKn|mtz0`d)%*D9^m-qi+wQGv;Q?xkSP2#ZwLoUt zc5&Ygr~DS{%+aRlvpL50&(&JwzKWfsm6LkUYWBL#*@zwlFLcnl*|u8E-r8mE{EKJU zTJrnu3y;}t`iy@16L2SM5i;)=dlz{vQ|<=v+Pe|j2! z&oQ>ZJk7m-OH68N3I@~2VB0;oNzWII&+Oy){gbco0e5w*@R9PtZLdtt(uuu$nE^dW zz4n$;Q!i@xCu7y`AAZLi)jr)IXShQC&`y1!jatIxYCdjimM1%<13vf=pD-L97@@Jq zeF(U$e$%F_e)k%7O6Yg;-<%-^J;U8UT~@9;H1%9QFW%(1UeTXK39N_*VGZ%1P7ZKVgRaCQMfVw!oa#*oSrK zfbSIa!eJ9VJL>B9z&CjE3cQiWaLIj2Z|y!c%3C^9z)Ww>Rd#4v>7jSB#~iP{-2KRT zu5cwgQI`zY*`!<*Y(I!Ca?qA(PD+WUc2Yz?zv`wMPCZdIr}i|nk2TDRHQrZxcCC)) zfkg

z!xt^8JPUO&`>7>$b6TS{0kAS9KNi0^q}W3D14oT`*U2*xjXO`{=peVh4;L zcx&W|{QsX$U*2HVn#cn`H+4$xzli}14^>C|Rh5@tRtY_^Tx@f+Lvg;)c3>QV(yv{(GLEH#{arF!ZL_03?x@T_fq zqN;|+I$8gKd83=kEpCPf?FBOq->LiQTm1dcci-3nqXWkFUvGt$z0&06AoUg@OIakb)ITR{_Rl#N@W&h?}`mtJ8m*O=TqQ$<~f#{ z0eTQP37LZ{fIs)%8$DP%XCb&2HuA!5c%d7s>^Ct()y16MLonXpXt@4V z4O#TKqDz&vJ5Ra03v`U#?i2e;*o|sXv~Q&L&vnzjMZ~1}C)JV!pDXp*J>&tAHZBUA zOg!iw#+`qr+NLL}e|KuJ0X6iQPND-%^tG?SlT_V&MYotIy2tE;#jX8kI$&ae8_bzL z`}}{XBD+}mhqCmDxd`)pFUbj8%39%$Pt%K(e)Z|+`i9)_&0p$(i4*BX{iR(n z^8bPy@N?onBY#t?Y;W&WYsXz(z5A7l8sM=f=gUFjhxjtX56sPYo(sg2K=3w=y~`?0Eyn<5ZTMKKLFKmRXHCWy8o7Ay?lTHP#Q!z2ms!5A9==m~P4;l>QLua@+mt%dz( z$#e{s5&Dl307wo(Zf5y41?7jS?3g*C@&%rPF>5sbp^h>>X^9L0tmnkdkh_1K8cX92O zu3Wng*9kVgPySYHfDXLN5sXe4{rJq}edG&lwH*jcI1P#ml=~t>a~a5Llo!5{5iYZuGAF_zL`69)%+^`jnqsPky9hm*`bj=(vRqoT5svtg1T}@|ohn<{T7h0{; z4KDn7W^FdJQ!r!TR4p9|wv>$nyrd`LSp|QRXMPZMsM$`6pXaGV)ByIw9h@GVMPHfy zrkV%LIWikw`v_ey&xh#3bLAFwD3#s!tUTs;!G)-5>L%y=!Qu%v`vqps{4TRl9XH@g zxqjc8i@yKtEpaz-12M`2lQ-ZGt`n{QF1Q20g?*->Mk zZU07f9}FcY4^|C3HTg@{D%&=QKI|sB44J0GVf(FGj=_~LKTLnt&qvwJ;HHC%5ISkO zLe0Hj;I9I^K$Up~>Fm}ZoeEv4Brj(1?0gkMKVg^sD(mdzdv>v-5eCjqq!Y7cV7x}U zZdH`Wb{%jFrv9^2j>Hoy$R}6eSDc1|sfm4Y9|49q#}k{dk~q(w8qiAe!Ijhme6R)X z_Vgn)%Tbxe>>oVrI&4zg~qG=~~knxLujMI@b!P>UrXO!k4_wYZR58jfyJWwwE-@7?6YTCUW9MB444R#iH2kA~yh|X-?tZM&_ z*!DHbTEd>i;?3GS*Gr}J@h`!rafTT1965Y-JeaWoE0yZ(sXQ-y0lR3~)Cp4Ey|~Lx z;J_?V$Rco&kadXN8sv@a?N)(>xK{g@Ge@{Q2-%yN(hd0g4T^T&!v5J-#jf5@EWRE5 zPGnC$>CEw9|KE#xq6cw+2fbxq!-+5zjL^x%e>~UBScLrNXvNsca=`Z64V$cm!)>(5 zX{`d6c*qUE?nljeEq%=G;Eg5(hcg?O3m#nU*x9QAC0BwY015e1y!=0pwoFF?Jymj04*#%QWhvaT@Rs z12yQE12pni!}T;=zY+u@T?sCUdDp%z?Id!y$?6 zjNESX=iSfPS;8;Wo$1ht;wD|Ze3zMp9^InPaO(~;F3bX6zDlp51HSGD@WyfH%`T(a zaWgxucX%54ckuTVlKa%A<>*QNA(e(~)%~kiE&R*kgL`y6X&W)XE?wA#4OqKPC7#=K zWC1hwo5C#|%{$lNtUPx?o!px*UciBJ>jpL6d~Naemd)fznlpKZ+*f#O^QsVS^I}JU zIq|Iv0>GU!_^#alC9AEz!XEH2_kb@Hv3w&3`J)42OIE2gB3u9R=3mqb4u=Ol1vhlS zJbjoY2%wMX%WQ!+d@#QBmHj5pQNT1?t)pJMVeT??!3N(yS<5*#1E$rPYmiql_fJVGGP!m2L zsfhzeX+}SIMP~SG$pz{U>!gFR^3mq%{}m#QxELA`vvx; z#hP2X$L#E#7vHLf`ti$e|5>lU{Udu7ZNwS$8JJhL<{IxZyVgUE3;lS+o@I~uZ}2ll z?q;9SI?uo0>tE?I@n2raZTk47YRibCw_O9yKJ_8;vO68j1?=9UGm%?$K4Oy^L)p0w z3RY^+4*G`JCH7EmbaqlpdS>xkmySxVmMLj34NJOP*J*u@H5?1#;mN!`am&1t#df`d_e7L3SCU0|{9 z(Jp+z;YEImw)Il%0xx)amMA#zAT#byxc?8CfrbD1;+OLC4%3i-7@)}m$7uW~qsWcN zYj!_sRU^C=w=fLMV7S^QOi=KI@ml!zL)ime1*bjuSNDI^skRp?E^Al9p#mKYO;&Pr zu3D*8Hn-enHltCy_k;Nro227~RrDZE;>W?ue)g@Ny!w&&u6M@%HgYz*R=xE`Q+w>K zKN?-Q|N2W^=zLADkNhva4qPDWcrDl90EV}Y8JAlfhCibXEX*X;WgJyee3CYM_~=X} zoUvEBbdgz~^0G4R3FF_faH%FzgST^VQv`8g^I3Kg9+SIJ+uE}~R(aXSbom_fLG7)2 z%Iv{z&n*fjHrUM!*=~D$zx_(>aagT=j_i};10u=yjQ@{x+oq&7;fi4<)Qwpo4|KqA zJ^3N;0Jw1cI3A7QoABOWbcGxAAZWPnfGY11D0}F(UtW}e05-%mrjPqvCGT+AHBRzdhAi#O%JHdrpsu5fy{z)yDuj_@}9_`_Gs zAXI1_wl&Pb;J*6uT%|qB!C7Nxb3gmK5svJX68k0k>}1X~M2X%ztk}<$nF3dAfX8rh z{V_A-#Y~YKy5Kr;s$7So4-@8FI^c~i*pHg2DW8njykWDoc&Lq>$?ctnFC|_izaNF{ zN7>6~tfO`Ro%l14G0S<+U>6UckB@+(3|!gDLn-Vx`>U9JSi?bVcKFPm?Nwrdox})x znGM-7V=1-Z#oEMiA2U~5+;=M~EKi@jH%K4+-Fq7J!C(#g-B6ABc(j%dw1vZzzLrCl zz6iRnpWw{Cv}>EbEOF4){Zm!sYdDt2fXDU`zG{e7OYxb&opRVGXY_I*{j?+gK59u^ zuV*!3`l*H5)vCR+?GHD+y&tobNy<7=t7~_uZ&O$7-Dx+r-}Ib%kHNgSg>Se^4eYFu zKRw=K)PIWE*UZoFQcWd%rPL9>dP~jt`Dbb;M`|R`d-a06gM2fEp8a_0Z*ylZ)`}JW z3fc}HA9HYx=R2r-%<89r4gB=jgY*8e8MTdQNuwwvb5jv1ybO8K4EA}5i-bY?(?!NEn>D+s;g7E<^ zqrjgsUmHIMIa2SPzF2ccPScFRV1f^xrRBrsqc@ARV%QS747WpH?5P_$P(N~jOKzFm zx%XaUms2Oi226A!XSCD!567`Tbwb*)k_JYyJ|=o=d-VHMphu$8`?fxqPNst&Q}-PBp?)&o0~D2k>=o);8oHy2MYx z^F4_L{jvMpeRLpgRhSaI*f#;cCBY|@`495{fpB{f`+4I7{PFp|$li761nNH%Ei66n z@iUPvoa4-TE*dvWqdyp?+5N}Mp198iYz3F0)b@rfMi1=B8||_6%PoCy;7+=YTuK}P z&MwD1eI`0<@!;87Lp?h8AUjCp#P>S8>6tOF$L#by^uX{wl@d4m;bZ*9QEM1AofvYe zRuGfUr0+5Cw;v&U;{D$a(%|0?)VL3ZYv#w}WKX^E;KYsU@jt6?_g&IIS7zydTyd9f zP$ST`!$qCi(j=$B3&291NnK>VN`hDD!XbL9aj9VE z`pR~~9E}??SYdicA z8Of1p3D`xpaV|KCHXU}xnj0Ged+<4 zh`(Jt7o<;J|Y^xyd;2_dlUmJ{Z40 z=r_#8{BDTGeKcHC`i<25ev`Ft&IX;ZJD@9TQ}hLWzfX_a>BsZV;MI=i&&SGnq?u<5 zP~x&Uo!@#A|NT#Jv4G)&E_8Qwu}2CA9rxqq6XvH|z+o>dLFNtMo5QhG%f3-% zId-59Ea6o8GbbR!k=ET*d6D;Zt9O zoezX#*N>SpAN0VTcwjm4wI5sx>z8|KApG1TKOC-w;Ac1uodZXTtsFVdGk<0xBV#sh1%Gow(Y<{EbGYfsUugd6!bds(;O zDC6|pWw(NO;NZ&j@C}XU?o)5(jAst+1ML66_xo!IIxzGD=Hfmaq>;bvFPm|5iqCT3@Aew&q|H;;Fmo0GrqK!Y#NShj*M2SNKTh-E zoS993-3DK?n3!kw$myELjPs3quk`ZEf1*wRZprmWy22itiS_@j4)o3&bv*cz*(K(C zQ`%LKf10`&y@8@yl^sgZeP-FdWscF})`6G7@CTZ?fg2snM^P_pu5Hxys~yCK4=t|X zujnZne(gsOnX77RQ{mA}W{pq5&3plVhbQovFzbH1n;rA>%8p6Y#)a^SN{@8;-Gx+)}ISjHv{_u`r1Kft&;xmZ#Ml9x@+G_TP#MbDAEAn?9hHoG? zF!6yWc3@urY3OODiV}`0IV4=4g0<95&)md;M*a_Z-oJAXj?Kr^%i6VVxtr|qO^(DO z^Qcw#fA4qtm^0vapWp}jYdCShB>Exqs8vlLI*H$FmVzhwYR5!x=8C7Q>9CFF4f#Me z1IB9h;0c;JguG=SK4s8SWqKXeotUe7fi0>zv|eS={?x^mfe$lLQ~6nT^BmZ1W;coa z?uyx6rk7;;hd;-Fe~E2I4EX8`cAdXfGT0_5$L^@$#6{*}*#}`Zt0_E`-hT+Soi_d9 zzyFiP17T*>A0pc?KgV~_Lpop6$lY!M^QKW}up6hy|7*aPI$K+3@$8y;kduW)I@{Es zXHOn7he{9XYx>VT|HmKyJF!&(*y?uLNuIT5Nq`Q5l^X+2Q|$7!mJY;|2Oh=`q_A(8 z4yIS|gjouiG>6$s=03*2nKF{x868;1@i6nH?|q{Fzxs&$dK_`~blDG>iG82V{hk9~ z*j(gHJqR6eLf&rJfQ5r*QwM_Y9$Vlte2FE0=i$@`IBxg|yV17ro#$zXgC8>|cFd#Z zf#3b2_wFP62jJ{HymuddP56|^ky=a1_h!t~GIYQd87~|$R+Hf>8crNAkr|jp!zXDe z@yXKRT2+k+2vIocF@y~cRCeHiwyWeLE4CdLrPrp{p znTLu!!VYF5^KVz4;omQ?4+K`x{;lj!gXy*PfV$aj`2I)cPU$6n%bcN^Gwxw#&g2n%Iy%j&fZp7WLZfphlL`4Pm;vBr1U(oM=19#D<`i4IBb?Par z>8}~=xZQSs)Pe%EpBg}vW1swu9q6?I8T4yIC(K6vv*96~NgsHI#f7^XJy_ zOdtIn@A<=#R^QHU&@?UK{x3$x_Sk@BJRJt31A}ex2fgyQ9WY(C17~pe;l&$DZeZd= zo~}mzBNk~cKVum+#cjmY`@!N1;@M8!>jiP2;kbQ_&wKcQoS+9icmRjuwQg|96UiNj z1@KjF*rpZS&n5J%?1qgdHXx>BX24;bou+<7J?Q;GJV$8a2SYWp|A%08kJhHe6SZRc zWcsE9HU1-Ng+r%lHZ_1{#FvNsiKln&k>kYC>@wO=qnfV;V`pky|Iy0MtFUkt432@p zv9Yj=kg<`ynJu_w<$}GvCiAs>FPV2`_QlM8HJ*P#-p(#Q_2rK4C(MM^sqjFgn#;N0 zo%DyQ=taMHp>OFOn;G=a>DON;7PvzX#pFV-Uc9n6;P2hI35HlbSX3?4dd}-kcek~d z`1<8bc6*zt7hbVuS6+PjQeXY_9T-@ZT1zi(J2N&W{@+O~u#Z~M0s0KF>=+u%0kdyr zd_giX!8YRk?GtBf3-aGc?!R%|Osz&flRqB8Tv|V3tI^oqNyu~xHP-R6C3iA5z<$82 zULC*|^!kE%$l6x(2B0Iv_^!jL3t$%-3W85mEy-^8a&8%Ny8$2gLg4=pAbdz6}2GvKc3PjFgXMBQO~~oz3#sLQ8({W18BR(j)B=(1DB6~^VRe1a8-0! zcj4P_nLVc8^!oX8_J_}?m2(AK(8a#oH((5!9V9bi}a=|@z0gAv6L>nJ~4jd*1F!E0&{tLnG@8tdmkDp=rfK|MciR9Iz-y2L! zG*Jr%Vmk-mdy)T~{*yHI{V{wEKY*WIh7I`H4&V<4+c5V5z8d+s%P?Cl$2Tk=LX8N$ zSkix{)-3i`5B-y*(0$r8b%{dfgCAh)tPRtbFvFk13?Z{KmoHg7ZjbI02Ve(!m}R(m z25zp*;|gRSWjTIeIrS=!krS{n<27x-Xbt^vs0I@g4*sA&9}m#z5BdE+9;V^%eWY1~ zhid4rsT1_0{xe{l#(XkDGlubRAs4hkckHQYIZ@dWX+$lg!xl) zHSTaHSSCgXdND0~u`PR;J8Qpk7aXm#>iLxUBXU3U=RNchy6$|Tx^s`gID4hr)C_Oj zV;}B%k1q55SD6pG_2?zL&Ht<>`jF2afsf7pK^t}7uJ#TKd*C~Ij9;30L}YCEc{|!K zvBS`!s~0X>=k2HPq#^syzoJLYZrr7dt>jSb6vZ83c8eKp`mx(>T;V!%)mCEz(18Q= zfMb|7h&Od0J_Y=%y90fr@tBgXE%K7_}}!`u-^_PPMfR+{fs}IsJR0sTk>Db z-JkUd@9Bfl_VsFo|4ZK( zqDB3|qbFYpGCpLgEjcB=n%{W<{(1;!#0LX43i*#DHW>8Y2gHN}iD3q7EPcmm?8HqR zG#1?$VSPN0`nK(u*_uab1?jn}DX!JoGgox2 zs|W0WN=14E!I9OX`}ZHRw@+S(d_Q~j%Gx`!=8Vw+vx{W-B`-G98)|LSCGvm6!EEME zzowq}9p{H{zt-`TG-_Nn+BSEEcH>t!(NC>TI-$?5zho{Xm>pg(#nGpYr4}4dK45ep ziTHm9a~=NVRc7YaY8?~iCVvh9f zyYvn9(5jp~avWj;a-crm>Q?X<&SmB^cgTzpwdr-(>K**|d*|3uo1CtZ?+?@f?*1_3 zZ*s!n)O$Yp^@o}`fWGi=s1p#wOv8_j`Vj6{-hn@LvBl#SvM04tz6)G6oPNUK_lGMt zF`J&$S$5Ux*r%%0j-4?|NG%0l1spqkK-WWdU0!{ycKUC1W#sl9w=CO!m)d`87ky=B zmh&^JiEB@Q5mpE`RI!@D4!Zl`IWuS9>G@ZG(4|Yaloy$-?3LT$0iX_b`U1Tn>^pm} zW(LvBqMBNIi1Y~qKeJ~~eGh>aH- z`F}Er&$0VFZLs^s_Rlx+=YB6h##6aZw#eB5nJ*nQ{oTPA;~VDon?!vOeds&r$s%+p zlD@^2hGwPjkEFIYgBpcB^%(k#_?(q|9>AQ*_3HnNueV^YGR^irzrkEH^Pac86sLfK z!rh9(-QzAKxLW~*2Z;iK1W1681VY^1-Q5K(udePs=X6(fpEbXAC%c-{XJ)FeyPut% zoz33Q^8c~~b~e?^S?_(!^-YmFeBn0Y!OZZ-QWzGAFK>sxIbJ-$B|N6hmwn`;J@Hwr zz$PcYGeSQ2J$e9M9`nH{as~9#y!$?J!Wfy%_nbP6UE|C%ymlf`95?Ke!*(a7H6P3& zuhPusT87^od@fkBz}*ucu!oO)L;$tPGx#?gT=p~D3Vq%N-Ver`mQzcwS3dM-uN?W{ zyt0?nDO50P<-Vjr_xxwi=u^sKHc}FPg3e^6Ck-BH;FW%mwjtX5YA@GrifXq&*WK zfY~}n*Ez^l=l$4vCpkquK%gtT$-yqqQbTxQrJY<{Z4Vz16T$=M$qmf;!#J4>EO8KqxKE$_Zj_AvKi-k)zZnZXr~N|zB}#wJGR`9SVhTL-8FpaBh?&@f>BL+!IkODU zdxw6jEn{!{A|Tm<}qbR%Zu;mNhzi;u~;&*i^&OmMU7gG+`0BZ zGO-&a^w{cNyewk28s^|Nff;MAu#tMihM|+2Nk1e!Pz^Se6O$p2Z%0U0M!qB_WXgj_ z>@VSS-XuQ~BM?7!T08A zJ_U?q`>dt%DRyx>`f`EVf8_sDuAf5tCCKt;=z-bqeZt>-j4ufNq4!d-on_E}5x-yb zF7-k90m>Wm{xDfw@%<9+(0^A@DhJkVksZ*>1?7{upY4lUta<9kHZ*2{K)-bz1fx4nHaJu+-W-R6OGG~(gLpQ_Nv63#m%q6W< zorN#o#N1s=e~R`b)e;ZZaaCQAky9brSrrm~CsH2VjmA&p@9LSM+5A#2A7(a>le^sT z-YpM&eZh_ssHr{9KC_k7=+7pHu|V9Z#o9}}a&ylysbqeBbzU*MvBKFoe2HDB{^+1& z<`R|3gD)-bR@dp-Q7HW3@H2 zv6zoWPm|;H`P6n$Q%tSNcK%(*iO4dv*X#Dt6WBSpU;4m)GN@}=L(CQza9eW19!tX2 zAW03nL#@+Fc7;!qL-daC#RqT%v(g>E_T(!Z@eLjDXRIcIQ-c|wq@%xAGLijie1&>^1xtO1=}S=_ zKo{s~==qwt$SYJ|0sX^=_koTfBM;a zGiwO!TWx_JY=slGe1{e;lB0_j$(~uz1-;@mi+skFZKD1v1Jr^qN)fd1o zP(DzQA8IW@-?RFk`F~KcSZyZH{x7ut)5Gh!;fu3!{?rvYO73*Ps5*|BL+e z_rJ@3{$ucQN^qF?+3b)jn_T5QGsezg3og(@c5>l-`oG~T>I&_s?cD@@CZiXWhKt_+ zgqWTf5Lz0#57B=vSEaw^1Qvr2%p)f}8C@}x*DZ!8<`E-|`_*V{$t;=8^Lw{AncDCS z1xpXJm5zeX-Q0hSz3F!(;+)n@G4mT9 z@MLztQfff9q3_Ra(^{tu;MJ22E}-?SE8iVKd^!gF35*cDXd#%8r_Cwm<-ahrza7yk zwdp7CWqY^L<|_DCnWoHpnm4~E~U^ab}Br2m)yg9p?eDE-wwDDA6>DYZtimi~lG z^d@Zu?{b}vZ$w|7+oT!d%2A!^J?B$)czV#gIfuH*#d353Gd!%F%x8Gr(HlBw!OuU-fBgKD{QV#QkngBl4?pq+Gro4>6JQV4F~fWD0_Jecr{`i0{kaR0YGP^zmaKpVgb*o9$56jC&>TcW7QLL=tHJ z_A93b_@eu6zQ=5Oju**J^p^`Y@W*J%5Qjxp);kAYZQSJ|uS!pOOT> zTgLy(z`mWfa+Edijgb?ZY{+q5VXoy4>Jt~qZsro6wqwRF_`+q*=UW}bjk<~T)G9Bc z?!@OzoIHx}lDpB&VTf)uC+rFHH97aAJLUFc__FPdEg z>?Da}XIaz}_LDH5i@cG}s(pIno|yyL)kh5u`o9}&HkF)i6<9$Xu|gg6RT^95-6H>% ztM)5Zb!vS&7$UW#S(Uxw0cK~rOKk)!rTKAq72yLr4HKj6r`CCuYu zUK29lYvU?W^cQDdzl{z}mG1U-`OAO)CpO@($_~Sv!acVQ7d zIpF{FbFLuwKjpV$WiEPLY5W;}zWR8xkn`!()G8k=;9e?UEI{Vx5jW`T^cZ^pPiXz^ z>M^sh{ZnNYwG*qU_dG!C_r<{zvTf2F**$Zy>;=p7ChzDBKii_eHxHu@k^Iai>X6*g z`Efq}5{s?BgTJ4GKUVl4N|LAxdBXF7yAH{ny+iG^hG14QM1l0Q^&osl334WvkkkV zchIkVT*A=l_jVs)7WH8X^4KTW9d!SJqwK_|J4C%f^3|KvDPJ^tzlizkN$CGaXMO0~ zJ;&V8OXO;O*cC`@1TIiUp18ZQRcf;H#n+3P7Vyk9;Dk%a4KE@`JmuG8<(BUs`d=?L>$W$$cdb3-@Z6bln7`kR9$APC&Ov{F z3hkA~bC7f0k#x%KkgWM=zRbrasI8xm+)w-6XmSCgW#Ye46NLY-zMlFD^N1xE5}O6? zJSJAm2R?|dSukpv>{w6z6d1nW-XoIhOAoA1fFxZBl%%VHl5&N3^$IifuioIfo6PIG zBH^blv48fS)Y2c2dMAt>(HA9@I6Un57wlfYCK<$twdB6q!A8EOwmyq~DQEnKCBJ(| z7GvvIjGHfZ^gf(8Ko4zbI(4(u&QvgmxDgzWxdGK(Iv#y*Nd`lEjw6T4>FGr@YI;Wzdxm#-czmeN_lc=Md2EIK-TgjHM_@nPU-Kz@2F&64wQro z>~b4AiCmK|`bpv?KZ!g=+gdDNJve_hyROWBEb?`A_*rUa>$&fB_}UfJ;#SkM zS;Nk%hOY1A(X(3gejR(YI>rB1fq3rnmyPVY-MG<9cDtRBVDP;scj?WKj1xPsXWzZY zB^$fY2K~Ewy72!7?SEragQTYC8f;kUZ>blkrFX#cSaSxtGpW3ZxSzN$ucSw;mswGN zH4*yb?;HA0!!IY_q4b|lT?TquZGz5gaGQ)=O`RdTnAe~)T$BfnV+Z`!Y?Kf;FEEt8 zU+Di6H2=$A|5LspZ}c~wZ7eC5U}&-*pU-(ZwYA{m^Nn6dhSBF5`&m(ouf1IJ-X-V9 zb92%63+dnc!sCb>*ybv`S6MN`*j-kQnPcKU^&1>lZj-dAME3a=%SrDeG8LLAPkiBd z0Q~2H+&O+mGJK%_`RkGl&Gn?hD{*J9%Kby1%gtTP|KWG-MODl{iIUx{!bM|2vVp&aO_$O3yd+wa{CyN^|WM)LH_ieOXht6j5JL z#14;x_6Lc7K8D8Vd-_*g(E)DIUg>WRdO+uOI83BhpW_7mU3kuOI`K691N)%=N$7ut z8sZ>#FLoikGCrTmzs7_=fGgKffB4gX{g?dk?e`LaKHWECKKXj`;>h(}fej=zdVbPhP`DhsrKy3@*aQUr0a540z~->wak{td=jk zpEC>Zk$g;@=Ewj01NNBgW;PA@Wgu7%K71-W{t}1iAC0bmbo?B(il^y$Kg-UI0&rsH z1%db8+e#d?kcCl9t@%@G!xrEN&OtvM1&7XI)`4P=kBEzw&s-uau>;;VZW0|B!i=Iw>7mZ45L~qo ze@cD2jC<@)#IFzDb42o@o=GdUz$GzYaHlWI!wXlLCsV_G4tDV&TN-2cLjQ;x%!@j7 zN+N^8<>@14$Hrt!4!N9q=>9g|rnJ{MTTg?|h@j`Utm#X7v|mekDtT)=*97jD9y zjw5e76vJJEa^p5r=PgmD}g8i`&}Gat}PZkzFb|#M4D!Dz)i3;_JCjA}{-c`LSaWzb@nM z1Bv##A?;v5eca3LPV6c=U+-}`dqRqcZFQb|VR5xoQh!s`(nqZYdY)KHV=N2J4c)Mx{^>g- z$!(3H28sC?)E#XEr`*82gcTo*kaN!N>Uo$xyHZ7$JRbvwe4D;qx96a+uLC!0 zA-3umtjSdW>Yw!g2KqA#9-Uu}U4NQLUnscW=U4KX%fk$bB>a>3K6#kfCm~N-nIFUK zK3=c-Lg}x)-Hq7og7Q9DGmpMyu;vS3t_RRxi+}e4dSEPd#^`@$bK2mK=dz=rrM`ZU z{(R=vcJ_?Cc_~*89mj7NFE%rmf#)wGpEZS^sgKB4j1z}R$UT1BN#rreW0wT)J}7sO zon-C{J371%vZrCE_&QRnX;009y{q^-d63Wdk~`E!gdIF7clRC>A3l@&!llFn-~mJW z|Lnrgq={OQuU@cclOD^+9s4EBnVR4A%-2}Tj9q+}BgcL-A#SRSL~hYmZvE#a_v@$_=eE$9n~TC*`E{^^Bi5@#NYEb zyx!Oj^%?OcS_l3pH*O_^eQ3?;MB?T<;L3K`gZIfRF*8Md0QC10FtsM-Sw63R?hfeR zPkmm<`SY@2*oWx$3FvlYot|_%=BnAkGly5P2i9Y!_`(07=Pt2R?4I2H{Bt?LjAc(| zJ9^{u?Z5}zfo)K{XUEhj1{Y8q*poc@!Nn`-<+cXvW_QdcW^T+TA2=Hy03Yx&7|;z{ z7rAZY0^aY!4!5oB@mOKv!^86zQG2^oPA*#^!~gGL;=IgS&UhZDmu@_@Qsm5kGggjL zHxdIan~I2Qs8>;%cT_f`SJ=gk-VMDLEYpWEBZR!}Ui+Q0fIhw8v)85aDRoQ_pECE4 zIcC_4jK}1BU(?0@rIwj#?Izv%ujqN)$~UflQ$XY?KAgL z=NRg&?`C4FLio9~xR#oVMq<1HDXC?@AT?Q4#B9dTS08TZ4BGrb`s@B%wFAxYKtn$> zAc(UIDhHSy2gb{c2LF%>@d+xCOTNYUgv@Y#j(yggEj*;}R}FSo#ylD4HSV&IdZNQ? zZKMH=dFM)c#_2=Y@DW(|$d4rQdLT2vYfV2-E3sc2y;{$Sze1qvn&I@IgX1_rcbl=` z+cW3MIdT{`;QPmZfz*H$No`><*cf|g!E{{l{dP{93br#%_Rav~``g&#g1)-EV!fOpCO87!4}cLJoTFIb zLiRU(CX>MHw^A=Q;@?J4|Hqu+UyYJOE(aur`ozkdGU=?MPrbN~Is)?DnWa*mlF#m= zTnRksBcG0)A)m5mV&x2K_^jOV=LT(m5w#O#oa}p%X0}IqM66WjG9xgf5F9r|GM~lC zb9xpurVGRG@4|0VIWNSvcT@AH`ID|rW(wm&^>uVfEhEesz!8 zjoirJ8#{mwQ0%4&9T0LWN#t^Ldh@549H=LXp4 z!)uz<@5kS30IO}}v8mBgUr>EV{7JOm+}8cQg=I5 zvhF`3_g>13@^)zhqiE%-yKAl;JSrQB5v+;tKDV-yCw@U@woey6O&2(P@R{?>U847T z)Hq@}tzQ81fu4&0d7$szr-BDep>7cy;0`9F$7Av*;)xDW{Lf&3@WAfL;JD;H4np69 z=!yfgWcas-+mowoqHFdw?lll?RFHPC;E2ez)DM}V0!Tk-M76Te?Re2)HPYMKjc zu-ygBb7w~rr-D&VHF>*|T~*b1~;h1$CZv^loXt zMmzG~s^5X0tmC#$vi6-};^Vr5`kYJ5RS8b3z?CJ2t2XI0EyK=ml)v$LebM>arkwfGI z_M;~bk{3KYi=UzU5oUZJLf)1Bhi2g;@c0n)*R#iYuefe?mHGIlr}mwa%8X+4a;X%j zLGN_t`Xc*E|MFybAt{F(exdk(ewE&t1+oO}XCAqLi@QIU?C3aXA7}6z#c&F#c`oLZ zMbW!Nudm`kx_6?IzP4BRbGolNi#o{{&zYx0zh4DEE6vr{SJ~*r2goGXR*}X&yUYgW z0M}Drn@!DKhg4R9o0YL=g8hP(%yX(~AzmW})Ev8^zv_SdxMpbB0xebcTe1IYGxF%o z)|$Lu?1G{Hpp8)aH}&cMbmfg9`l}Dn&Q6JeALM3Wj7hMvV3)1rDY5r=PJSV0Y@8$-Ts!s> zHD~m{Bw`B_t^`o$7D(S}5V^4+FxJ6q0{O9oD?t+P6DTp{Lmm zH+D{$A_uS!2k;#aBL7F`%w@j<@(<0AVD}Hr<>&d>etkSd|GmiW>IG})D-LHy8NF7S zCCm_I29;i+e`zYci0LKp1&3PK`l4!TX>DaKHOD(`yy=-*fGxWxoy_v8&taDzH5=7w z=L2na&5p=Eq<7uj`mx1T_?)dgC$VCsazWRK91N7S! z7g2i`O}%)LTs#*}on;CA(9p4#U6a{)^vF^NU(4>bTKa@rp*wP2hYqjr#LvTz*I2%} z7nz5SZF>5@#nzMWVfI(+^Ka4r{QfQ4>tm&VBQ{`=4|qR|KZ*V?2CEGXO`;BtSuyT= zr3PKrQc;7wi<8sHhe~&^y5=fSK67^sbU4b}5 zW-<3H8-I;Hg%xjyl$wLulY=!S+&v*-<2R+)%9J(>tMW{$TrxW_Fd|HzLp;7-TSSv z{pIXIuP4XXsX$$mM5{<^qI~OU-WSud~W8J@NXxB z{}+TsGT*7pV3ex+`_Nwz*Mm%MP-mL7HPg#T{F)NV9(m~d6d8^o-)BCzP`rDs_&bsYfSH2KabVtf6lA}-L35Z`AzxU!=LxRCwb7h zfu6YvVx+he!SXV)Q(h3W_do2Gfs8t^*9>;fl_I<7Y~;Tgnk)TvPG2j}Sxy5wpb=fr zthfNQx6t1r@0M#LHlTrgfF+NhSdjMpSD^zg9Xw9G(dWo?q104XqvKoHD-$kRx5G@m zqS~)+cwBL;xLbFngL~52!i=S+H;a`25ag6~QC&AXD$&&dI|sdc)A zT;CYv0r(_NkA)AE_Q<}{TTkr8AbEE2hQyu6Ch(py@WeCag-d}FMJ=!oG3KGU@BupE z*evQi7A}++@&ZSQ3HDB%N$nW@Hu(3|1y%SXIxo6K+BDuH&TmG(nk)72n5)viyNOxK zv1#H)tatUyHF4OCZYxDcpvN1S<=063Yv#Dnv)fVEC|$Mm@s?E)OO(=6!TyE1TI54x zJ1{-$qxLX2R8$(DNgwO!fd^u#5xsem-I=zh#oO+@L_SEB3~;2vge1v28Z8~?8|9CM zKgyr-+4+~sy!#qvKci0?kkv+FHpOrh|IweDR1Z*RZD@`U*o0jF^9=HUh3SMM?#@W*t7<0P$%FF-$V_>q5VG@cH}kXA{9(bq;Pf zdcOhuB0Vx%ZXaP+`n4d*0qazL$+&e-GK24%V;&P5s}9gQ_h;;B#9>oXd5@=8{mH9ch7bJY!Lifqmu4Q+Y|Rl8 zJCZw6UvV$K;vQ^719WeK=FM->UFom9pw|}im*!g8U}kjG6jsS$uOo8d*g1G1g?PQq z%t^mnV=55{(y%ju-|wE_88-LE=d_cm8C z2Sx3E{7v#dx1+^r)edoE$DIGEE8yd+B+BZHbRMpeH;?F%i)23Y{Wi&E2UL1|CVn3B zkDO|6ruN#j5bw40e=lwLZY}VH;<@VIseJ#d?5p3eJoC@EUwz8KxKi_Xnit9|tjBMy zq|T6D%;YSi>-yT+b5vL+@zkfa@!fmSUk&Jm*xUDv>^GCYDo9P0>xYg?8nnp|WyU|W z&l;k;d3@`>r1QHp)=s*v7%Y)beR34^rtSR1U#cKDU(?x zd~a-DPkpn*-=GGdsz&->uxBcY{XNjH7v0c`UA%ehb4j_u9Axs{YU}ejxwpe58$QTH z2V~$A6vrg8`?g3LIA!?BMNi4GL_U>l;^ll|>JsqsHtzrHzURz^pf?ok{5g8&U$NJxrTzH}bUa3vZq-QEfV!&o&sjCUuv5#FF?Ck|(@}ll{P`|5= z|Np0dEA(%zrXRbC90$H$8+qDhqvxr6!#~iSR~htZXFX=e7T=|c{A?9-J!-gSJfvn0 zAFl`b*4SR-JJs_I*hI~fs^70W*&CRJqdl+#%!7HtJ?W@zL663X=axNUyVyZCPTwFl z>>D_`$Wv}@I4yx&jxaOSL+;b}niN|k)yz~VDQ=SFSauCP%cM`Tm^nHHl9qtXf*TYU z_efq|hZHdHHLr*`4D7HG9ijc4n)}!JPTJpD1ML+XtYD6J1+$#W*kf1T_=;RTv&D;R zh==IIPtK&~lkblI+DpDC8BDOLvW~sOE%NkMsF`awK+gREIG^hBBIuC~%?s{4lsvWj zJWc~MDMiMcm~m2?kV?Jlc{#IjyX>5^2tQr>n5gpr;9X}tIhu(I$%itTk-wdkXPNY+FZjdM3s5;d>u6|S;XlXkMQsKH!7I_Ujf}Kkb6DK z2buh?5g(yjeLVfF&|7=hHOHrTpXz^&3#v=W@o;noTnaj)qpl6QW=Lix{qoVJ;&N6 zM0QeB2a}K@LAwu#2Y$LUc_g=wsRIG)bDl(we-62T&lZxOSu9RVKNY*hi^U4LwW9vu zz{btgygrrhU%WCi1N5G%4?VzVdEV0pzW$Quxu$n#=D;(6cxwjN21&1AnQ zcwg0k8irQ`|ukIv{B>n(fETcZClv={#>X%Yf z^@4pv^kwAK!&}YlHsiZepVk4E(E-le2k+*Bb=0t*RQIseB~haQ&UG7kF1Q~l`N*O6 zuID26`RIXKYQt*_OC|j5MP^VfpigZgd0=`v=Fb*4t?dTSbp*S$hvp8;mXL>~$8q^0 zaam3c{gOrEJcm6@^V!9LKY#1|MRN~TxAv+WasQMD42H(fF9u!=>RRMaYv#VEH%Mo$ zYQI{<1)u*W?-u!2+H35m$JmAHW@)c(VMbpIb)(f%m69vz_vwj&Ms?_!hAi?z31B|R zUKKP~xi13`Rr>3`#aw>wrFKt0lOCmaBiL>qx?k^|=69Rxn5j<aM4*psYzsE1Ahx`x@H6 z0yn~!fDh7%Lrr~3dXCiRF{cN;-wyqI`TT`3NxVO^ov?w`mNPcXb9No5^d3j^mr$-KN(LC9{XdyLL%;JFVF0*FKHt25i*%EPB%G~tj z%fx9pd$-^NCwRbl342+mlLH3Jv!;IA|MXdTiLL4A>ovMn`9Sr7x#Cy#alci+8Xud_ z`U5?o-!NaIf!R_`Lpon&+|YeU?whzO?Hg+F>)`oz?11iTD1L^Hy+c1_lww8|(n2pp zDe_+s!(J8cVFk9a9{Fo6As1F&EzRV_O0o-(`F?|qn$HCfDE(i6k?3da$8M?KZ)fIH zKc83ek{tAFGWsn#IFkKb)JSWt7p%Xqs7)g7W6L72F~p?G_iFcg+BzjCHk~;UiSS$& z^2p4jm~_cWVtya_gHqzQZ2b17BKFb7!yEb9ukl6_z$c5N6XnZl>NV0^q%Q#+H`h2ko5#^f9`Gjuj-hZLp|Ym~wZ_%Q9N&$y=r&^`;>UWOjXLDxS-?-xJB zcjIvuzD#~>qI8s1%hSLc%x9y2cPw=y}XL2jtUyZX>&#G;G`zZF- z%RSTiGT(pyy`g!F@_@m526cT4vaa!hq`^YxC?#t`I4hp1z z`n{TzZ18O`TrdEQ|4a@07Qnkw`+@pwa zQ@g9FOX8{ZPRnYQ;>s876R9%3UN&*Pv4haQL+>|vp8V)|?P{E$1rD;Tpu~SEwh)ETNhyq-~%giz0My~+skWgr%^M& z>^cW@z7sm%Wx-tdX9l$d7mg;`R|JuRB^yp*t`}J`Z`JWf?MsF{^A$gNd zo>QKRyd5VAPm3hP@3B0-1@^;?+%Kv7*O}Qp#8>%zj#O$+D)PaxhMn_BTJda;AA z@Qs@BTWhFY80dXve4>K59Ofss!$(Q-=w=l1OxzROC>_^Y_gq{ZD^P}$S8a+ zs2}=B_q2UXE$~EYJ|>IHgsI>;(`Cmb`gTW6lw;fN7#yDt1a{S5k9>xl!Kum`$7Reb}UiPq1( z&0}8j(=cY^#FR<`J7SXYt+W@rg8SKoUMWb=lyb1*I{vK+YMEQmDgEFt{n$eFdz!$W z8{y5D{ja$P4=9jM^@xzsgv5UoX8aVe$mLB@^F2A0I$5nKb%B67JlW zuuIov6aM}3DeP6j-`hf-e;Yb}BedTL?KjQG20;HU$o@9`JKH(f0BG(>{HMo#0`b6j zYQo3S69)g7TJZ5x`I-6;WZy-3fSC|h!^g8v=m_l`X6ZBQg zPme|Jo6*|_7vMfU$0nDiW$}KshSnX>Uv*9kzFyxDFT5mArn$NRs4?)qNpOAPc47(cC-fB!i+ zNiTZ;H9U}u-&#`Gh&`v?fL!nayCckfUoQch_DP-xe%tO+sq{#bXxr;@+4+E!L)QOH@QL+?CtfLRgr2!mf|ghz1yh(S2{#D+;X z?ugs^P0XN}0gf?Cw$Ghw=&g2tquTz3gY;M0JEHsD#?u2fezJIsLl1Cugbx%Cupc*x zIVU{+XbL+ICYz&=9hDb2Hq-(XC#Nz$3LXFx(itE{*}3Fi@`#OILwo!{__dFtHI@yP zjY9|dg)VKi&G^9;5*HLhecEmDb-N)^SE8h^fjNxS=hRZ;pnC=^^tN1ecC(&t=&m*V zMcBxogD2$BMr-Lt2Gvh%!zbzI-`Bd1e)RK8-Jglf>x|nM{H$}X)gEXLs0-PRC59`^ zC?%dDrcvJjzR;LU^MsY;4a&*4^>_8r1IR8;{?6DF{1Uq%l&ggtsj_3GE(4JbKq2r>q0G;6Q(ZpY9ZZ`&g zc#8*|)DIXvN%pVWD1BgPFM40#PjO$L(_0A6PzX)bCaS;Ehn-bCwiTV&25mJ@q4f$4 zd9@Pf7b8Jl!E(#%p4>QeTbl8iTgqzayDXAU^u1y~ZD1XS_Rw5qUiD%RrvU#~=P%ez zVgCoZT1rfKQXS zPuwrQi>23Bb5E-4GoHj6|KugS@f=>)S?#+WoaCU3y9Av&3oe!l{3m38zzH1<%sPk`?KLVxuIR42F~{|?Cc+w!kGVENgVIsVr4!Upa?Mt%P`+_zqI zZLfTZETl6}DHZ#=Ox{*_pc$Lrf*sKL-DSi?=iGPF$HeT%cSf;S3?F6eRPmbonW4AJ zrp8H{`)k3k|CTv--~RD?-ix|Y>S2Ds7JZ40(tBJ7f0XAJfNdko(7lJxuX?=;{?s{` zJ?Mb1-mrs}_cL@KqQBB!pX-DEg9rZtn@~-SKw$y-NpRgtV#o^ivsF~|OL|J9L_8{# z)Wll3aXDPhc%K7nal%I1$i~@r%#5H9f5j1T-FQ-BAEp={&nC`)0j{R~of=Q;cPh&* zpw1Ee7rbIX|7~nn$c0PP#?UYO$;UDuJZ}*=?%ADtr79*tT-I-t)%f=I_;E^C5A^yk z2l`rM{=Z#$jXS?{#}>GZXC@4?t~9qCoqc7;QQmN8cKeo*A4~MjJIn_CnivIrsQMN? zpmAtqz-{U~xaVJfC(oZl6a3{);=iX?nGxj~Bq2M)bZofa{=-Urq9p%<>OFKC9W`NK%&6@h1rWtZ4TQ?XOnBW`0Ewe zrur|>k$v60tyhcutGxH~IS0s-sBP(N?<4n9B3YUEZJF&77ga7{A;}UEo+24Z#3m(8 z;5{u;lu!K&JvyPj&%}MpQTDKg$<6Z#a^=_q_9}!ipSsc1saHblUi7E>{TgR0?w*O< z7suff!lym>wK@0zUaQv0QsVp7^O%W9?6z#y9GOr5UbxRSxw7{Vy#r Y4_X<4=%l zXpT)+dTSl8r?LD0)cwEkfq4x+0kn6W07i&EXwSX2$iC47Jf}XP8+>3ze2~w)wJ*7c zZG6@)c;HKTpn`b)I(_Rmj-4e|qK>ArO%ej)#NVA=XWn<|FA9?Y?*LQ#ss5JY+KMY` z?&$@zZ$;+2@vSP6`Tf)_uKIwPylRW!J8NieGX`E{cYq_un$J-juQk4d^w*i*TKE6w zKmAF*gs%DIM!Sen{`}*g@JGLqm(cTTL-W^W&iR-0D7C^5#rOy&wjpL2AjY-O-a>!F z1L%tZX#Nr#r?&q!*Nl{MiFjBDMjX%lhH&O=rlWsUXR$N9szWlthMzo0lkngK3G0k>`UoE_SI)f2G4INzJG~4)D#xe zBYRA2n8mRYoM-uL=3&p8F3Z5_=1;}OuiYSlyAO)(=&`aBoY$Ma9S>^vEOhs#pVtdq z=NFp83l@HG|499U$>y~h6FA`;T4?Tw4RA&uICH<9xGE;#q;}zxS#?0J;9@yv&Jll4fK)g?l=c#NI4LXY#lj~+V^q1|9xYld$5a5;QoE+j_K}#?>d$xyWCkKNPRhS?L-xD&7C}sIJ#IX8=F#6*Kj7 zG7IF^^>AnkR!Kc`TLm%T}RuZG_{fN$i)O16Gp`=KC!%D?ETKe}f#oL0@IU z<9@quNq|?V`0u=p?Ps2_r$67Hepd4P?f8f4=c#Y6wC{o!lENeCeVr^D-ybWRu`_N9 zmdT|Zdx<@%LAi08T8LnYJby*}z4yx=`pxd0yKHKFx{(+4l~n#;efh@tby-m{+`At3 zpS?14*Zif^tuKk8n_cK2R%#8B_-KZF$Vvs%5A-=+g ztIMT_pZsR#<7ndAnn9k_95()+?(Ru>#2#B}z0?GCxBjn~5__9mWsr73fqRz_0oW8M%EhoZYLz614LUJ%=Ia zfg8+QxKAA?SOE7+bAC$urpg-p1Zr(!;@R;wO*SxJ-SLcV!#0rJ7P;&x!MEcc>OAM?-0vPnVH|&(nKyGkvO!&_z^we#0`z)EE}kwfWN+e zO)U^HNoq24o@1U#X0q8KIb34?YY0TOVUzq*`^ixG1 zdwn9G7@tJxUq!6<%$K+c-+K!(ZO6GR5AkOmyzx6<_-Bp&6 zZ7=kJ;Q^!n2m5*4!TQ{wzuRc~{@?*OJ;=HTbav0B-h6KFP`T;t>1$YSqqQK@{3AE-1}O#4gd{{wuW^jBM;&zTwN z=me#|@`vtCuE@`qH{_dM@jXq@3$>O-^?ww)FPNU~Q2eTh3s)uj zdH{16eA)YQPA++Piua<$%qks&Uo?SO585Hq=BUq)ALxmHxC1`091rw`>sVsG(MoIV zJGxwt3qH0UCyp!jR{cE--EE<p3_O`SJ?@I&x zksD7=PHh1){uOl6{n<({rMF(cMtAA;JN$ufnVGMTl|Jf^y@u~z^143b3*raz9>z96 zcjbADu27#*^^Wq5^42%##+Uewx{t2}yRM(B9=}d`LUR+E>rub|CGSzqtT~ORJF#7g z1vFPy$-OfdB+mEd;7qzyb~P0i<1;n#KE$wK294w@qUiV7Lr;hsIF38V4f^V1W7pw{ z!RsV=K=}bI2p&=TyW!^>n&VSBam-_QLT$bJd={Orn2^$4kIKB|YR%tR!8>}^zcX5P zf3{BYm|b3_lRMOEFuSUWT%PKEt?BJ1_Dc#$6(6Td^t()63GfjJ1HW!?MqLVhqtHo#+R-W@?)FbW+wTb>4om^fMMyQ+|}8nAl3_chp! zXSYJYX2EB%2et4+2mYSUa_i>$61nbUhKcHUrMrdp|H=o-3rgQU=>H@9pmIJyUECY| zTz&rSqqKkf=;u%@PjP9TEmZ{Fo5?MxkF51Gx~J7(j^NKa-%aB^%~PuEt6$WC-1iZ8 zWMT(m{clhgmWmA_PYh4I<}>JAn8=`;a_zuDxoD4ESlP3SX$`a6@agEqP#M>&7yg|3 zcoyGI^@9gEu*Unp&>q@34$)re@5oWPci`9!(cEgp1ldj=K(CggJm91;Ie)*M<1l8L zY-LX0reTwe{x>#YN3dM?@&~&t<36CLp}*pK?Z{J2PO13sy&*of7uk_z1)kV14+Czn z??dMw;QtecY$w*V#vir8-dKu1o@hivPvF|9>bJ^qGR<12Je0scoVxf8|uHFv34l;Vx*%V@8%u?OI&o!GU&|$H|zu)x1FtHgi0$q;%|7a{VUhE_T|M6m$lb)UQbnb=+c4_R- zy)-xvvaf!j#({45fzCrTwnL^Jm7e-DkFC|W8@lRaL;n%u4G(M^K2Ek0e{F{sY(~*z zsys3Vy)Y76z%Cr?QIo-YZi)YnTXM}cK<=LoGkM=ebe{Tqjr8Pp)KF{V9|;y1$UM~x z@`T(*O;MqlsjYiLHQ%rq9i@GYTX}CQ&Nlun%Taz<_4{FR>-1S8Ctc9FG(FSAPihmq zt!#-i$R%;_wKgUpoLQ>ypWbJszuIdHz5Z1XysZ;d2dFLh6TaeK{_+>Y3;KHt-{{|3 z_(}P|LVLYxUbF|FUvUN1H)?lE={weaTZ%uMo(_!(c-+nB&n54qF|5`zXq+1V=rMbl za!fw(1-)x|>|=GCH%HdJ^9SU06#m^rc5Eu0(d$FU3z{qKci;zlLF+wScWXTd_sb1` zz?EKc2;0|9njsg20JwY6pIpxSbwjP1(A%j-TB(g#dt+`tBJLLn_0oyoR;Dycb z!RBEfnV;>If6xK;?7GtUz}GX#(BH?Iel+T-+Ul99Qw}ZQfmU#za{B%P4iX>2BbS|h z$R&asz}M^EA0u|m>$b+{wB1>=krT{O9?;p|n}&~-eQP%m(~;LjhKi9L zwFQdj?%d2?q7$DR?6fL1gFexC(fwCyv-RG-K!<8Co$`Q%-g>pj|Jzr6{u^|O9`y~C zAM~;Qt>qY=g0C#}fBWbDA-*U_2j#>i7@N?p@~!;A(OMJDOEi>Ir=~ySLzfp8vA>5t z56x|%M;>0gE*nP-gEk`#uID(0T{YOpo%r(J6DBabpZO0TGqaBRPEXbE=>Hv@T|C}{ zyn7<=8t1zh-(P(M{D1Wk#jU6wU@v#QS z(YQhJeZ}!F;3sbRY&mt+CDaK=vKwIaLhu;>#D?e(hqL38G{0s#eweDBav;cdbVV5>M3c=R^(Rp6l`?CE3wJMy<0-eSJ%kC zgB*6UF@yck0}0%FPtH4DCN9cnHcW`DMgM3$f+PCMn)qeYh_UP+vL^Nk69;k~o6vt7 z$Bbsy)hPDAkC%1BM#!GE>&(tFo%56)9mCA07J1+oXzEvc@Hb9*dW#oyKjyWQzCQrJ>fiXCx=Z&fe%3icnh#X_p}$i-pmsrZ zzrL4ZjvD{#j@4FtxUwSV@L-qp-|FRWyU_inP7d2&UQlRyEi5%m%EKAx{g>#4yx15p z9%^}^z3nJwy^I=xzcvba#n*%WF3`yp+PfmRPCQl__Qc=w{zNfA#eF7$`$2zrLE}Ef z0#yE8jLsj758ag?^f|8f;0G4^57M0bJYuwLP?_UwQkp~iO~c>;===-)$qR4gY=`b! z&;=VgK5p0LoV|}Eh9*iU`d;Z@5B;^@$w! z4(1@DXZSu2)S{ksaAfA&ZE`&c}2M~z1I(LLyZEn`Q4J%NGoozo(t$obMYPn@Om zUbLq(;Q_N#(5riG9OSUa4ms-0URnoslpQ%{@ErBC4IQDG(y|}=s9*R7OyDc9VMBYR zCA3zZ+{x$D8p1wgRpa`vu?Jf7{Ns;58rz^cKy!$SGiu%0bLd+`4ziy4ZE73(&^IQJ zJanxA7u5JfYgGDpt=4>JWMr6H2E~NksNu97Ih@)JzFp3DbRonZ2T(Ah!d92@G4-EdW`fY%z@^SsjDX6!inKvedX z_9M-Vp^ZxW;qU>pU(eC2h4#u5TZVLk(GBp$29B>+kX&@4R+*aE_G)VRsP%0s1@A3k zKH6F8Kal&|2f`#6ocOvYKESRkrrxfjv6eiUFZSVtL|?roiTCdEcO{bY@R6*4e;Ba< zJ_(<3JD+hoy3h_Cu=Ee_$=&l8snMhco_@B1#AND;*zE-mlq4j}UT~v>?w)cO9O=jo zZ`n!hM3B!F(_^PJQQS`HqgbBi3Dy7VLzlD?Q#PZ!8=;}j5>f0}W!qqU=mFIg-+nh3 z8z}7+ztbJV+P9^7=_>rWdVW?NVetzs^jAEpgBlgJ`HJ}|4`{tw5%EELO`V)^bfezk z4`49p`*G-cWZrfBI1}T!W9vPjmy6Qh$iM1;birHncgELq9uJLqt*Hf2I;(z1wha9Z z@1O(V7e}Q%e{Y3cS#y;BTcH02@(em-O6k9Wc~q+RHxYMh?5#Xt&d|^1cSC%!9)7q> ze*3cfRWsYGwTe2<5^DAEaQ+uBy1@x6 zrLMG)xVe@3pL%gywhY~a|Er%59D3sDoL=1h2WH-%;$#KP zjydA&CgL>!82E4rot=_8qkGdA|AnJ7Z6j?#cstRjgN!@?MwHpXV!i zebl`$NqYEL{0|(K6~BLv?>dT{_i(WT<5&A{=x^w$vdbO^VgMKDuXuX*rvP+ zKg|E#Z?P*kz+7L`7m_3Qu3k60UMq?Hj=Oov7yH1;j-8Nw=z#;Avj+|tT&NAYHA8ou zqtT8$_9^BLO;jf+eT_c{rrZaO^f^5S>*r^+*_y8%;Pu}k|9VvZE6dA`kMKRZ;g8q` z^&j;;2kk%k4C4Df_@GChL*HnxUjli+$NtoKpZY@9eK;H+iuqq7M~cm8@;lgl2cS=rD>~2lBXk0^RC=oH|8kHA!voM?^|}*BYyaE~{m}`^C&u67_waz`0Tc&PJEC!g zozVm6Sma;%Kx4n{_+VSn|C>MLd@vgN56S+J4zSn+3s2}ZHh?<#YvEuzRp7C8QdOk# z-^@HWYF|9A%bg+m-`W=@fqU*s(7s@B;%n^YJTFJBPtc39ff+}0*zpcO4I3sK#*C2l z!#{w}-Z%F~d0@Np1NUabsNu5Yo!`;Rx&Ys=k-BVp!djW(Q;;vZ`}rC9le<3Gu6~}*R8(3hKJ)_GY8|0sx7}d4J^18W|Dn&D-wpAC(sK~s z|H|~878e#$7hf-5LvNEy!Z-NtM|z@$umY8LjrUatXsxX3675Y&Auf#cy&=JSj~U-* z>xaV(?YAR)HuyPqd`|~_UbX$|=UaSzrMcrrpJg8l1%%9?-aO3pQaBxA$F7fWsXvs+Jy z>ub!ZDJJ%>Y?3IyNV(+7-i-tIp*b-iHL!tuLwI~g0;m@_>+C~5z>mFa?y~$n=#Bo{ z1g+N&e;>JcPu6|#E-?xBhkLSd_=or?ACeD$AG~;&Ecn&0GTq?|$i-Rg5{-5FqMhDOfqW>QLzW$r)1E_x0_i*6#`aAtR zTloD(d;yEBYaC#S1@t+~^9jg*>@Cf6(#y^4pW31t=-((0e8TYQFA@9SB?cfrNF1m~ z9|!CWmTNn~gYB<~kH=Nn#eU&c@4rj^4|9=+L1XB@p2wS^^)_&C#ltp0|8?kvwfz3G z-~LWEQvX^>eBVP}MQea{PEdN(Q|1U{V2h}yjCvw9;BaLbSrY6YARbQ6;=Faccv#!Y z;T_%*4nOEFQ@4xVcES%ZJ{fEAqBwMTAW^pUu5=8R_NN+bSRO(DJB@KI)Lym?QZz4TIL zS?P|B(B2)z6%{8`+N+LGJFj*{ZAA;dhWdFK&)EHTHynTEj)a~#OTXN*8Lw|U{72IFt-+74ss{d8?E%I+(&wE+(oWTL1{U-QCV*+!? z&l;ak{k|c4p!&f7us?IwqKTd8d4%?L@IWINKHC<}DMi`74l#RkCX_JBk;tkv;6S+qGb&?4HZ-XD(*15BpD(0 z$8BU!?WT7>lx@7fHTuH=z2KtQ!Dz69(e!`72dd+3p{=!rPk4>$A3e$khW_}0TR7`A zCWOy6q60R5z`sGw-o_6GaUrFDR&=&J34Tl;xP|`AairfPiM|Fce)#CO+CWBZi{ z{CC}9A1^jQ?EuFgf6#}R;0uSVa>4bo99-`%Ysf>bz|UK+wg6dJg)A&bXD)eHceA}G zvw!m&SwCf}=?f~s?&p&a)VZkYvvuQRXT>MdlhSDPvEnX@xoci19p51J0euz^Bk84& zkwTs;fG-L;ns=|lZs`2z*T}cl6bz7u?Zv+I^ir>bAJ@zCN`J)=RM&SC&np(7^`==* znKyXvzTEZqmp%4Qa%>0lS1*8#xicGg61$3LEn{B0t(@8FDc4`QZr*_L#vnyGaWJ^7^#~$0VWP3acR_vU~W(JWnKmr642oeAjKoB|SoO8}O zlLP@0naCMn4$auzmc91Oe%Pw5v%m9RT$0^x$@c8ld|9#T)O+v6CBl9G&k4^-TlD@D z18##W8`C@>{Eybw43TS{z#q_g*vsRSa=EA1$jNKYO?9`vN4Z*^cXMNn9s=}*`VAV> zE6->6``pLd+;gq@()+48xm$PnI`wGxI0tw69;)ZH$5`hK>zPnLpq1HdwbaGsGCwgr zG+wUOHOpep2z(~7v45(RhZIXqWDR>KtEDWoiu%q9DLPxmOs#S$4Xl>(bJbEBz-y=Y z9w##CmrRrdk0^5O!q5lyg4l8P4jIzPt3qPDkSni4E<2C z)CFpNI5om2xc`d%=Vw86LOwg&)gI(Z{GmjN@{E%huQ&;?J;99h580LQU3m|m??ZIL z*1u34@Tcqy{7dOXx35zVrhKl}{3@R_q5e4-zDIstpKI^e_gd@5)sE}^ zRNbwyn#TBMG4lDw>(pIw?@gjJ5wr(_zBqoK#st^l5*iB2q%<*?8s%JRN+^??%y_te zgkN?#ko*9W%b)*dnLckr{4(NRkhQNCCFEHkw^nDV@@p z(a&D7KIY}Khn(|JpUBLj&_eb_gvq(R>}_Y}uQfQ{`zM?Q_*Pf^K{xm!m+xub-}Lhg zE?`p+C@-wGz>>3I1uvk!pgne=ASO|+c7T6o|LPpbd368Gp!R_^N)tb`h_fH&9x1V2 z=m5n%c+d3Ht63MA&O`c%JnTXaujfjdPo_lS6Gmb?V!aY2#3euuvP*C`x#ze4=npvy ze<*K!=M^c5O_T@3R$5b~IktJ~s1MmXNbq5AX(}l*?!WT*8n-Ra&l~+mS|6-uLC=BO zbG_H*c+Gr${u<}ReE+p3_1--_L(IaWudtFHsN}ODQjwM|l_>?%RnQ3jTG+vmLakGV zr23|F--&zpTeJN&{-I75JC}4InjE*c>}MyC4SNr*zV{<+Dt7hH_+0<&dz<)#1H~E1s%9A&iH$tKYa_I!BWDU=|?>o z$;=351*b3rA-$J#fE__Pu_?_7_2?dQckU;}=e^xxk8iLCoLS+Ys9kWyPc+?s;zZ7& z`TiUJ0KTB!e@kM6AAaNaq>7rw^=abpEBM{q|Jgx$Z?ih24*S%V+esg9u7vMnp6JmO z<8v_l0P4jvRTt>#gbw{<)7K6>PG{{?2r zb@lBu=2yF}+@0zKy_z1QH}0#?M?d+4yYR*fQb~@gC?!?u@(Zv({c^SIqO_H?NLgID zq@B#5)`8#uRIW*Usg2+)C*lJ~xW~zPr!f4R)7*bQ3AGE580RoPe?FJ}F_PdOA@Qyu z673Mc&aM*@u)|$WyuX(nzUTz}J3D+kdt!xs#D&aBU= zVn11;#2rnRq+^-<&g7zfvnACh!^nw5W5=RAV9N*$5CwCo`AS+)9xAT{vy(Uf9oW}%Wkv4S>y7v2XW#fbbt{R~ z{b~QgH1#iY%=Va-_JSU%O>U7CbYX1_es|7A=GPX3gIF-0YjA?<2bg&wy_(NJ3OU7S zPvwR(jMr3$s?A6_24}=Mi15HJ@L6V@qMm5*s?mF++)QI-6?HcBDR*>=)!Xlhw}S(- zRfn0=u~j}G20jTV++NGf7W{)xnPs3cF-a#z&~uQB521cS!r=^wIT$B{%KrJ3Mx4Kh6WB8{ZKa!{T zcI)VaCGPj)^dx=5*s%CGb`~Y0-@z|-EDioLjo%~D8y^lICCUT+M16za*VqHxbFVZ* z;tr%5&tp8f_ed=FJ5JAlC+7j&M|&j0g(sV?d_9VD5_d42zd4;f0m;Mw5pvGz7;_M? z(bU`8(j)0cYMUboJql*bRNHd3}Qi#Fm@vIQ2c;J4fO@M{I}# z`a}Pp$7@?ScW0$Aun@a|4&WT&;#4h+aoD-j|aCFpv>?9uFL#}2w z9K(;l`X40L_bjzP%vZ~hnw3BDqIr^XF;LaV?T_=mjo}FU1h52~a_Hx|CL)_`T zEG;OM^89@H$)Eg*SWs89lUkb-%=*ZpmN5ye#~w^H;u_WQ%3mgu>(Vou#_R*-HTBHK zdZogFb2g}tiQb=NoDDrAk?!%lZaOQ`{9UT+^zSns^SZvr5Y-c$m4t)*-Pnpmk4Ooy zI>P<`h>7aC>C(42|~UpDl34c$KasOk$zW$Dp`}KE? zH*A60gjj4rtl}Ts$AbA7L%8qUW7GTjKgNB>E^t<2;KO3}>w8mquj&xhC-@ZyHAc?D zcZ`4!bS5WC4drXQCCd4voZaqDoK9cHUw;=}uo=f2dO&$zt=ChYS$#TU1*4_{j_+^a zo6!+x_P9x)6H{zZ531?jHtl(+C5g7+PgF80C|g%%sSQ2qRjIG;gQ_#kpH;*@6NW-u!hW0`-Me)^O+zx|d3xVy?N?0%rDyX>NNVn6=E&Yuxq zTRW3y%#^#!8*DvE}?QBfB&3Qs02EMFuyZIf*g5`m>`JPgLqx9!Or2}Fx>RIV;EN+J4Q&bW2A&Q zMN6nt3>=^O4(V_zX+}Khvvr?1{cJnF;wcGt3>1eSzDcYA|BD{b`|ZH}c2d31Reb@+ zO`kw>tIp&g?Z5Yz;tZb<%pUaf)+f;^N9DW=yME)Cp^tyqkvf?AFQ z=8;~M*0fgn`8U2H7StZh3=;c~;ZJe@=SCN0hI#7kdA-s|et2ziL$X6+u;&>bpOTmp#Ap6-lIq8O52BV0?k)>IB<+}T6fHESOTx;f9Db%IzFr!M!&=nmPG^QdhH|{rF(U2Q z%r^99b4sf;;*Zr4LsdoBkUxS)B8E!F-%xv_u~ZOeC5ZD8wV$8s9xMkr2LU$JKcS!Y z{rGKo8e(~Dy~c)m@AWL$ajzZV4NSR5^$owzd%jOz^?TyI)sDFIjD%R9Vo&XHIc4L^ z9MW=Xd|KcRy5KA`*jM*o1ldd+~U4 z3o+0RcAg%lcbz(s-MhtW*AB5m4_NR!z5d64B4@bwQAdu7uhntv2mBfk%I=oF0(My!nxr@X0vwS^`ndMO z+v>Hy;G%Ku&1W_)x>c_|@VI@sm!v;$fLZU%1TWl_{ENiX@OqhWya_E*7*Z}t$MHeY zJ)!8C6MOt5g4&vRuPA2fyn}964oP!*igR*9-zBg5ee&wuck?;WT(sue9Dn}0gxLGa zIZJ=^fIk>HE&t>AN_B?E8*TJ+1GMg zrVabQL>`ryE1i@00Bz)1hN%hASq#In1&->M-}(l;!Pn&nU;SONVYWo-$#AKTuadkV z`p1vsUxTSM;wt0*`%*uEJ5HDb5fq1)TZ zK7+bi3FLmCz;6k)b(KI1c2ow3FlRGd&Y~C2d3Z>5Q64qCqf(v6{S7!PemiZ6>l3B$ zT%}=;(%~f&;d5iDSBO7?Po}u`XD70s zpQQQc?-~A}{a^Dn&dFK!W1P1+i7h!ntm!9#*7!Y6r=>owo-@$FJ+J3ny(wF$#Vrpl zkk`q@|NNWZk`IYJX6f}>Vz=@fbC2gomzdkMNNq?rvklpGLrk#FyqvzOX7)b*iTvyj zeotQiqgUhyzxy@hcg6M>?{e=Wr8>G&3ix|d!B^Tbjrq9$nm^tY^ZhsL1&tp}=cG_# zyvgIEhyL*)GudaRn8|R3eBhcqgxk-cM=Zx5-R9&jK{gK3mdReE>@0FCF2quMB`q|B zy6iPWUz8>#i`QHBl6ATiy`IkfR*nZBADn4TtLkILcXwVFSY{a#yMz^x;S)v6lXS!UGG6<*cUzT*h3$+EBF!z)t87hB+?@q zKO-8yA`%-DA?p8xx`pG9#=<*i@jWV}KDL1~QVd6uA_05+@e%2#RV6R*;`u|1! ziOB|Nt%VKt!4DrY%;B`0S6=}AA7+mqZRJD!5rZA*L2<5#CWHmh++KA;^hdDBCmm1?PxS6VWFnoTqXLusme&wzk}RSMmui)QPA#@z{xa zORYsQbi-f%g&bpkVL185U|T;4;tT{ioRn}Uz9)~%BP;nE`{9SOWe2&ya&r7X|IQ!C ztKa^%y!oAP%isO}A4mnckXxLAnX6h0PA?eqFWXD{mgC+b<)_SgRv|HDIa12c$8sUJoUreOy%;DEHBEEmyu!N9-9 z3%s69eMv6oBheRb=wu3eIx5*eImGWVNv}Ys#2h~%dFRhdGxz<*2D7Q=XXO8&ZtvQ_ zHOUIhlSr=^_6EU!hNiH0qmugv#^JHM&|#{}_5Lcp+qmED;9bvy%IEL|j z5n_81{eKdU@GN)?H~hhLU-m=syVORNiTmq2q#nOe@BORa{*JtXANcw!-;$qU4@%?H z<=!m$lHpZqLaDzPT;vR}Q|baU)rkevKit4q(0Ti|ufHol{@T|$NBEJ*8?XG4yr#P0 zo8OcleC_w-jX(Y_^}f3$@mPpdMi%3XfP=FpUm)jXDfe4-gvJb<3)K;tGdRsG0(@oV zX%fAuv4|*?k~r+cu``k%7|4vJMEO7eum5i7`r!*#B?{e^&-toPY$cCdEVW4$_+%HT z*8}6+hgNW|_r$oL+3+HqgBD(EPKPT)TJ%hO4*TXaU^*Ya{tTGU!mqn-`VRX2;baV5 zgWl=N?!jNgN5t+ZXIBbK+<-~5ka`J-RRd1mVthvkr8 zuffLAQv&aja|-=W9IjWj37ML2<#`smKy5}cdG|yl`p|iJNPqUa)XD$N-Jhp6pqM&E z{X9Dh(6{;2Qee-!$yusy@7%<%o&mkTP1x+JxEiU5sgl~ndgI=!KG1Wa^5tj1bPxZ> z&j7jn9Bd4`YT7c@XK2HBY?f;7K_z=_TbVW3TSyF1GKdZ8HsX(X&U371q{MqhOAK}) z%;~(G+~Xs@dya7q&dXVgBjQLefD>oH@o!9GhwZnd_VV}^JMbT9!oRh@ggXafU(+Pm z{xouin1fsqv1?;O9e;1HeC4vl?NcwXt`XL`+e>?;+HzEb9JTuK~|OSv=gDN@SgBKu=fYIjVEY>!Kt zgO6l8!r_xU%gmjW%7z@eY=lqvz$oyL;I+_8xmk>f|Z@{Krkq^9`lX%BxzM9TMd9YYyU1 zeP+;BM+y4SUQT^L9>MkyIj$pO|ITj7iAD+vqRrkO#|aGSJaRTr9m z{LBnHf4Ea}%-Q1x{wwO1+26v>F!pjQ{uTRQhW~#R^Dpa#FW`UnI{QD_|Jrg9{C8fL z>h?R5nLjRP;41RzjhJMY&@%Hpm%zfp#Ub|K62BGGE0)ck=jJX+ICY*JW)i(ib?gLc zC7<3eGnaTDwa4>A^f+A_mdh>dF=PjH4!uU1kqOkp*Bba=-P8l)J@l|O!ZkBei3Itc6HkZz;!Vv! zLRhr4)ir_P8M)MTLCzdKDf{-gh}(8YIqh*=`WxHi-pYnNd%&C?YPX(%@0;X)CPznP zVP=}zuG?T<>$=ztu!(uSs_5MAXZ#=S8yTk_^xDX%Q776`(;)8b9$KHK&wpT4_P)1G z(!%5A&H{V9u3VOm##(uBeNCE5nQxPrAobus!i#=$oBfjN<|_%4EpR4HO(7oUs9iUg0aO(pSX3cB7HpA}GT7C`wyA=QIHM_~4dv+Bo_7`t|5&w_>P558E{~6f+>~r`x>wy(8|3#^EJPLlj z!TnJ~%ACE$5MC=Ygx8(X11>)J2>1u+gGzKmHU5A~1=ub_kCuaPvu-F?+s|u-cHV}~ z*Q-i_KIii+uepfa=Zz>t5F9{2+O z=fMBy{KwMVzXtwqNLAY%iB9O3gxC&=I+IKOf%AM}SQe2va+cRFT!b$hlBS{xY7;}5 zk6I-CbGgh6H}H&lPpJ9(6byfc zZU2~Fz3GWjdcj7Rx5IoM=7gv(@DK3(zxsq3e%N;H<-bLrpg#WO(PIPeI>+ZJzJu0= z8++yHD>&rpNxg8hJic*H{K2;K9vd0$?PsR^lC;Aij$WeIs;W{lsXJHvr=M1C%|#Mi zjvMx0v9ErB+JE%}6#x3ztOv~3mvw>q00!>Oz5&?R{;hEIMX2fs+aqvuNAWdIi;pLD z)VVX#pxAF#+i$Y}Uxxp>cD~k0FZ#rNS-Sa&f&Y1S7cbs^_67U@0{@0zaQE5g?EmL| z0Tce!4|pNJdddF3cwFvqP%4}bf%QZ9=m)XYDu#jr?MAHN zk<^!CbiXz z)TvKMUMjtS=@sl5zQpX3Vd|x>&^LEgijuRaJFhI`#)Jaag z>nJDRhcCuQ4Yu_XPq<^t*S5gy}Ku>XoW&a(h8ha?bm- zWJc3-a%~mtKVeog*njqzIUb8LHZ({az%Ed*{1{CC{imN9SFP>O&&ieGfOB&Eh_{?P z?kgF|smuf!H1>piiXKqW`q7K@4L!d1K+@vr>p=f}djAQt%4=lnYwt*YQVKN=?0p&? zWv6HzGd;?TTJqMSQc3m?5TEU~^zkU}eV>bB-|PpN;{nwJW>Gz$I>0PuUqJnX2y5a6 za38^$4nq|C0ob*pcERF*oS3oV2KN7!)HZE^|LcbDZ}$H+254x#Vfg=bZ8xO0!;Jq= zW%1?{A z6FcpJkIvN)FCHJ_@j>INxX<^@sL#q8eQy7UuBp$@JgyaF)Bk_@YUqY_Y8eo-4*0cx zz)Scyy+`B!$8u%*zSMWGN>%$!NzK0|G4U6rv4ME3VM$tArlh*`0{g<6q$an4d6pgQ zR%1qfV1yJUWJ^|fGD5#xM5^RQ(`OQuD*43g?N#iD#8$3MvX2hzFM^f%k!$z_#6<7cz8gFSWU{ZGqb=7#h(HXHLqF1B|t-=j>!2e^J$406DukrrU9UkBLm$u)m1HOp; zUyJ_-e=i&G1M7%huYYWMUIv#{3g(M9@n7tyI^Y2M!0WjbqX*O{C`AvL@b3**pgKS~ zgJXtIP(~b4#+lM;+5Rj3XExvqHy+ES@!L|~BbQ;|lj{PFha>1Q07l=;iUGS+>8`5ff2%c}8D zi=-?iQ;L$(iC3vdPDqu~^jz#$jr7#EGMBYX^5SzPf!T~vA?cDBl_$0zF_-D*%nkU^ zQS9Edl-)ntDLemmD|_FfUx697AcgQw+O?Q2`TRfdTB{eRYUfoN~-n=b;|A_g! z_Z}I0<5rlzbPWuT4GqaCHhgl7ec;6O>&whozQNAfRrb4q*K4EW@OwIs89wi0_SfIJ zFNKB0;_Biqwl;R+=C)4`?DrBcw}ay8;wcAQ5AfJa?5PPpeKf~0-*yYJ`{n!8 zPaOs8KJ4o7lQ_k_`v04veu4S|igm@gUd_7UW&A()1>mrvkr@2maO~YV+hgKqe^$H? zPE>(38q_+M#9N_w<4&d6n{#*|z{_CIPpT3-NcBox{w(0*rk$L=p`J3!ezx4=R zz>YI)|ChtdJw0pcw)N`fL+tIka$t07!FnA zgVN1-p~CqvHvcI4173eqOt$|pKEB%d!-nJ$Gv%Q-b9pTv{O5xI1mdS8>K%feW8nX= z|4r*s)3PkXGmpXj$Ke0&bNs6wFvtG?(ic#@ps~Ov&u@-8w5Rm6s&sc$gH#=fLT~(^oL3 zrkVZZ9epy=b5Tb7*=xeJUwQpX=Dy^WNL6mJacwHAkuKuYf#z;@G+$&6Kl2d#nKj={ zKWp1X8LZVFyc%iBDV6e+Tq!`0)HD@S-xeE? zE!MBw(i5{?_WpD?{c`&x1D~L?xLHyIsWtKqkwfqT?|%Q6vg^n1qaWUroj(BIKiMK5 zadmvxQXJq94`TmMxl(_EZ)g3Eg=|F+?0$2XSi%!nzGnfqzDst$hkvolMot|&DaFjo z8W~_-_WCWuFHqm$A^8EF`>p+**G9*TUViQEzf3$n4~OvJ)*WL1d&bV5b@t4Kg++-q z{d{h29zd)vkugKp&W`5rd_S6yEYwd6d zy5SP{;FG%#rI-1Qw%c||GkRcSQEOa>r4jod?Cl4)S|Qti@&-lyKiTjJrck~9VY z;|cc1B@s!m^N}QSR`KYB7+yEyKW5W6cp3ltHA4s3z$H+Fok*M;K|FPmnJC8`*`woK zCxt~z@P63#Mr^*}1E2@M{>%8U2mf_#@Powv_3iA-1OM~apJMy5{|5FI|2)3+1Re0? zOR;bE|CRfHSr3>wK;!lMtdx-WE~8UOmY5I;W``<@Nnv+?)U?yF?; zn%Vv*g8yWT!_=`xNLnUyI~v!dwrxqq=AVLnb~3EI!2jp%zu6D?{B;KW&k(nMUerGT z^Kb=f`={3)$+gAD(%g4ls_@Aws@BBoKoPmW25G8WMF%MUm%u-9K=YjRv@b|jVztDb zOJbIJi-C9DBe96@yNvBw94Bt)j4vRw@PAWqde?@=m`gY${Vmjkme(?u0)IavPYRMU zB|AFBm_w8kl_u3`h4kXq%VoI4L1K)7#!eaSx=dhQmhgSV5ad8Js3q-c=#-g}X=W*2m#24$={aM+qQ?KpZF+t0!s&m^ z?0&far}XsC(fbnsW*r?|WS=`2-w)pRd6^!&A2cw(-^I(g?sGmMo^FT4&VsxJyud(b zJ3E*+&>{Dk2XLAGku^yOkCNJ=O8M;WV;Su15l4C*b(ZG_^C0FY$D{%KpBxk}ne?}9 z{kzv?|2A7W3m1~;<}Zm3CnV0s+rYgU|EdGx)aI-0-_!xo>I)!>ce7uhkIi|3IO}5) z&+`zwBjRg&T8_JCNpNt#R8*5!GVK0!scC=%AP@MP@ZSLb2gmP&|1a8q#r{jVz<;9; zunOi^!MciKf5p%N@CI-J%lH0?7~`L%wQE6=lN%)8dY^&)Vz90F*XOxb?uO)8nyz`g zme1F6Z4vx~0{8;M55N~FrvJFe7OoIJUv)qcu}}fI&}>UD$wabLHg$nsbIF%x(;uH` zO+MCwT(n)Nq@)f>Rl~Y8b+V6s@fp}B23UT9f3^RgxBD-!|KfGtvrgRbl9;%HhcXRs zt$e}sDzmBaFX}tir5bJ~FMCq4?GslN@Jt?0NDTLMQLi9kLY35KYGE<&Bw8{-^onBYSed~M2;m|I%^^ljM zJ~la9(#csB#-~X~S)E+#ACl|W7TDL#+*@M*l`*)(%i}V{F5|xP7AcRr50_(V%}b`yDw0b-hTIpK0t?6+96*PNMNKYv#~!dKq$+K249-fEIJ>AQVn zi)?!h{{h+Z%Mauu-fQ`Rh3vOt9?Ajm6c9zpKIXk1l_MTU#cSUo@xagDk9gwyAHe2&xiUAw?J#iw&k=WfS2^PDA4260z$^YWMNk6VL<6Jl5xN;NHN$VqWdM>H;3Cyo`H;|Fb{~`GQMdJSjwfWEO|G$p^FRcjx z^9KG6zu;3@#pW;H`v;l1`77}CiL|xPOC~*vN#`rUqnm+y^#PvapVx9MUC{w<;MmoW zTx)m9#y7}D2jpptKrEo~fbxWe;9u>&>Hx*RN)9?8!}5S+>^&fvRwf;g3FZyGz|5Qk zYfnkBI4I|AsAowa&aPjRW;nTPOHX8K=>c{c{KEm7IRNu@a)sSon__;=q#smAd|otv zFoVxOy|JkmR^a}|K9-thxWC5R#MIry!~V>V2$HPWUTG!sB3csd=B!~0YZ95H-<#r{FCuUEysr?VH&4~i?XLVju*zSULoi1@_B8JY*IqD~@* z`iOsi^bay|@v3;*IWyz6b<_TjOC!4+cD(wIZ28ISVnL0-8Ge7)_burw1oPPXWcB%x zM07wZzQ0NWdO>3VZ-_IjWQj$icq;eJ8 ze-rzn*vHSLuC2CN`TvdQ{=ZTC^OFDn9RHgC`*-61i=2SQ0j9XXg!{F-|4d!MKgrC> zuVi5Kq105f_b;Y_o;&)K&Q@(=-emXltxeZFu%2t}W=M{eE4si1|J_wGc|CJe2jt@e zXe^-k*O*`NZ}$BS{F8G|v-FTuW*X|%cx<^}GAvX)!9QOE{{dEk5*O1CU$QQ(Ju6`U z5pqwa7s38ZST|$-<*NzzFY1I|_yU_8Aw0sg#uDr3lC?WBwfsabT)qqbm!-6BL(cjX z%ZVLc^qP~$@hg)$>W|d_Z^Hj?ZJn38%BvC)lpz;8sqeuiFN~328XIP>)Fd(Jb$na) zlY;rj@Y7EoKQ;DJKjMt4=#EOAr9AUI&=ReZUm)1RBg z{DQEv?Dj~nl$qgK;^s*i>Ak`n?LzT&IYs|NMXDgc7GAUA?Og~Usz7!@fe-(cx`gE}P?e`Hka`C&EZMgIO-Qr;FDu?znU(JJA zO7swU8Mfa`4k-S4>_|^|96jN~7nm1_j$Osanub&CAwD_;*IAQSDdm}k;%V#5ywfgx zgL%XMuPZ8+ZNGd=w*2gMvE6PdA^yR#kGjNQ>K_xqeHt-9is}F`tk)Fv2Y4;PTo;56 zFk{|)RlG;@UeyVa)KW#k_lH@ZkyG|T5`3yk%F4kz=eUMkQVqDThW}Hk!LHPP&i9x(jIeSSj_=+-%F3;Ol-cE9$@uhV($jxO z^0TiF(@&i+2p?2BSl@$68K-lx4g%! zQQb3U*mrFFBW(O5u8*If1E^blSsue%KjpP2Pj$~ZIdSxX?kCaxW>45je)sxKIO}ER zvy93hdBAEo#>}`>qt;*;J#cfB{CjhsR8jwPwXsh&M&L->hRGE$vpv38vXW}qqtGFR znf-8s7bG{eLt;Ycjn2gnAa6K3NF7hdD0zz#2|OABpHVLrg_oqZ(p6e(=m6CV-NY{XybG?ftFTHs z^2(Vll+P>`eDmNK$qbB?Y_1jf={kG8F}F-AQ<>8l$846MSa!i3BbQ+%d+8%}*lh>i zz3}&s7&<`h{{egf7i(uZdBjKh$oV~FUa-!F9=SY7>@~^jzTyBkY4PDs@@^h-6&1}NzZPg2ASWCnB{k_HXZWUZj;mD%DDKTdY^%Ag zmpK5V76AUg9v#ro#@?*)2eNqUi}v5F1JDEH02Xd-)&|~sX5j4zI!${J$o-#BnPbLkYCP4^s#;|9qusI3hW~(@CYi& zdIkTnZhwgkYn3Xk{qJ4J_CLe-$M)j`j4$7lF?fKNWt`^|n_|`jdVPWU zFUAbi60c}I@qHOvcpwe!8^(P}BjrEKPPQ%3sPG? zASv{r&Rk({9{67d|93Z5!8!gs^{KjF!n}L-F}VKX&eGrbT>S$4fv4yW-9f9n3U%+z zwJXD${s8r>@fpm}Dwj)D)RL!_V0Y_f1D=KhX$4wJ(umh;DBBqO>^5||?xaUxD)PsU3|Xeu>|$;?VG zVUHs<^q0qFeSBWlC)mG7e6cV%0iQtLvWr|tORrpnv+S*ElCJUwa+vk-5ULAm;VQ{l zq6hj4Dw&l~X6S;7xO8L2RCZ`A`?U&;d`Ej;1@mVL@f~vL^JNy_@w3eMb0*)wj9cmt zJhAJC+>gpW=R@LVw_gr8?l-vo+iNSvEUBvl7xBN=Wt#g@S6&526GhEX3NyvWWR<>x z4RVtesrj<^jUC{h`J5hyB`!2Uf_$QwS$&Qe-;dZI+i!nDlGX1A*DA^ZsBfSgfZ{!Z zTw$cf0^r~90kCh8+|e1q>sI1PAb8D6@I0P+JCcD8~E3JlSv1d zdjRTMz<&$%|7{!i<`3ZdKQVFs|H21Q{A(_dD>1^N#tA&uyx<}_VF`V(bo*242S4Mi zKbC>Xd(w548rQx@Qe3_$NzrW*ccz&9FumB!&L}&RCROJ$rJVf)iv1!mU!cO{d=5srxYsMfHI@IP zxR2K7)TE!@VFh@UOTxiy8kfU%wRpYj6e1 z9WI;jKQenC-+xW2Yd0k9bOkkG)OYW6m9Skd^lkV^d>}pN^w2alF2MgUNOjp&$w{s; zW+yLFkFz#4EzjVfALGv}_RaX$og*s05$}c{puT`fbf2;M1}et?*LZ>2INgt;x%F}K z0Npj{#q=U}l`vZ~BvFbJ^JEEKG>SdXk0~T(Ns`j69`N2TIhol0oQsl^jV{RPl_F+g zsbzMxz)=pP6VMUYdYR2jt>Pek5f^IccgG*-EYuoi`WJ{3deIB2 z6I%1I`*5B`%yOxLW9Y?q7^rS$7BzYRouIFk5+lU0e{H{oqwIcbyEs#ic+d@Q0G`0z z+KHTMA@zn+!~pl?QeUsEEiIx8*dGk{a$|L!9fEK%>nrSyye2o<^PEdvmL)aLJ73=_ z2_e{Le2yq)QbsY8CD!4zBsusR*jN5PUh$7zH*x{s-LU_LkB^_v*CMI)3nzaS4yUDc z{t?8sTKgAheUe(rNJ)$7lN!!cMJ>G2zYG85{=k2I3wvj;vL^xl&&c_G8UDe!fqx?( zVA2Om)CH}8{pDMq!I?iZYKS`rZb<9EJ!$NFAlcNmMV)V!XkX^}`I3L~%alT929%y= z_XRWNivqC!Hm=~`1^l~!c^7QIipdAim|uPW4C4QEIGaqaDj8rpo&0h-dsEVxPnofa zf4!zzxEYcHZ&Dck(`z=4-wEbVi|G!5EDE=Q&|Naos>*Ht7 z5Ojma5yZK-*OnKldWo z#|OwCkn+3>lFbai__K-h9b5qaQ?h(zS}xRfvx~6++&7{}24r@Cnc9~|q>Vb-%n0q5 zspdUB=$%5N9-%lPQ>rrxr3Jg*k3Jg52T<&} zVth|IXzxb-L#9j(T_I+|zoX7T_sZy1=ZovU-@D8byMLEi_Y-4sb74tRf@5Xxn>)m9 zkG<5;UzL%}47=zWY9n&s?#^stpLid8pFl5KBDt%0#Xh)KzF%v9%p9Qd{aX94Jpf_o zfl#jJs9p854v@2lvZb_ePHN!%t7<=Q|7+m^4IhA9pxXY=+kaDEfEoY&L-(jn`^>2Q zeVP9^KQ{9Kssps*^}E9f;9-Jp^J_EXU#Y4{U*P2qbbBdKcl(HT*Doh1Rk+kfXV z2?}I(3-$M{@c)zZ^ykgv<1ez0d4auQDm+$yKxJZ6Czy4B`TAw{U-3V&qH(_o|NU3U z%b=gL(uU=@eE>7q+_}e|a-QESf}C@8JhRBcvm__6ObRpV;nbK}6P*M8sVSkBSLpLqY)U0I)B65VT}`GWE+W@*Ky z%awNelko#C)?SeKGb#A=iRh_Txc@#W&+U_pga&LL^VgWKRGnE(uHXv!gn5}H#;+!h zxLDJPF2J9mPI+yd+Qe(@h#DN0PV`I(wT*4m6U_8eqts3x4R*XCn;A^_12yP@IvzKx ze~<64`d|=#V7&J-z0G694AdCI3G$*>>zJXFU4#zEks3I~Z1M-5oVz`5ZIuI@$%2Fw_6hgF0k9hwSzBH)b^&PZ z;qcHP^~BU6j^K|B4N!}`E-`1rWG{1&6V8Xj6}C%!4D+W_x+FfJf?2BAe|&%S0g~VY z6#HubpVtIx4gfKDKJ2{W-_!$$4`4;Tzoj?xn@-6w`*4Yi=#)zG{gpLfzm}eoP5W=~ ze&!f}T;S*NZ|VoA(>$Qse|m(vF0iY9{a4igPy@7S|6jf?+@v-DTW{zA9xvYfdl{d5 zB>h)zOGp1rY3>65-H)ZT=9VO-j7capEn$b5Nq0C!a+$waa6CZ@Pk?{F6e-3BC^|{a z>FGS;em+|(2RJ(i@NSR2M=a3;eD>K^&gAZ$@#_`;j`;2lh7L&Eqq+du?aXV6X#{(3 zmUMVS#l1>8{DO+=fF!0tS%jP0kcl}H{>SHUqX%w1 zmr2zDsuK|P3Cy@RU%%)J=yS#X_=^5N_(W<7x-P9rW$kr|3hp59cUaP?>1~Q3_mAIO z7`Rm0Dud*5WEDNy=e=#w5J|HDfQ4`s*KKqjfj6v9lgsVEFHwF~FDM z{}~wnqI|+@pMmvH>3!3$8G1l<0IxlM_NhGO`V2l%cbaPNi`Kdh!^zdqS6m1eIMTuV zJo5a#Ro&DE5!)V(mFSZh5_dXNl9{(!kzOYQ4Fkmg^R21jlx-P{Xr0JRuW&|F%-{fC)K#4rOw!>oR;JwFnp4!_mxav#Mt4scbT0evSK8 zTJiPkvE|Agw3Jjc@41Va)1z<))I;#Sr^(G-rT($6ydIm+ZnWHDbU-OOfc;0=1@Ic# z!~nT+sk&aK$$ws~YLxmkdiRn1=oIlM2e^m)eQI!|4ACojdzGH#N6!%S@Ll#f(@VHU z-`@=TBF5QQzrcLad*o)$`<#`7#HxMtUbWRWOL%Cmfq~0I=*Xa3L zH$?S-SqEqj&W@$$4UONSjqE=1OF*fh`(6CJmf;Z zbSXJS3}EAg@9${fKil#J{#5*B7GEL;{o%8nT{?c`kt!FZyO+;k_AxpD?9PMpxf|f$ zBohla@dc1YbiyY7&9Q-A$Cht@sf;h9E662GEHm??cU8)(mnGmtu^ipz!A#!>`t8e@ zrAur!M$UYi&l#;_&`~MKmPWKT1zrnv^UUy@u z9#BzV!0Zn^MJMQEeUI@qxPkjO?-2hI`(L;!wamgSNhzexZ$wtEE=YeZ@n>c&@qDS2 zCGq#vU%;oQ9=CVGi0|{FnaO=R0)K$H1?BV_YyVQDS7PG} z;@IyK!hX>6ang`OZlH5e*2pKWQd^|)!dUlZ;*VY#LMIGR1K3@|%yh8dfF7vEzpu+K z#y_YJx`nu8bWVXxQp+=ev==k~ z0KHI_m@0wP3mwKsYsn`Tqvql6^?UFz`Q}^F+`b}%W4Gx6z^C6(`>vRW-@8fuKi6gA{v~vS#>f5CueZUKxAfre_dKN5 z=%!>BOiNM09J!!ziSa9A{(b_pCZi?iNVMd6$AN!lW*<+HBKF@EGMluJYcX7)fq%9C zihWDFP5bX4SztPQ6aVR8Kbbfxl^l&q3XhYxCL#%j*mJd)M0A4cf;4P>2DU$oIT4x8 z@P+%x6}ZABu&*Nxz9AWIFbSU_0{nY_g8hkw>xf=A zWfT7|W8aMb(dAq6vWzXEEASV_;QvMz9!nGaVo~um@o|q6pDnJ;uZ=hDoTa9Jv=uu+ z%}9DkICGGTM0@`?s9S5uEs}xOZdtfC3;yq!_;pV#F@Viyu;(8Fhc2yF)qZ?X@0je@e;T*~DC*?~WdZ3S)2U=^hf{(v3$*iH# zS@yh6%Jcxcx%vi;d||)xdd18i!p_%aiX-ma+wR8A%XjS*azS&iD#mvzO-IOE)J9HUuB0Z zeC&h!^6lK8l;*ypj!J}l!I>4IwAT4z-|LNpb>r0(Wm?uIX`rO+Wom( z4`u%LLnHU6y#ZIR-IIOYF@QJz|A|!apDG1<#Rn)d`~Q3G=*zLg??(^NdzA^6Gb|j4_0`_n8~9HH z>uIV7jOXBg7qj@uzs7NmM^qOish@zZHsPPV0DGG*$0tgiuP^$*3G8zZ$pb3>!*@Bc z+kzf-AL7)C1@MpmKmAa~XYkvZM?SG|i_c@z?mxfYB9Diy-;{ClIBD_0Y72vYMNzUfy&Gfa(Mz=kIOK9 z1wG6;Xo9n^Eo2Wi@w@glU2gBC?s!6OGKc5(?21vpybMP=&-a|@9)K6e<17ZuqUlU6YbzqyAld~k|ZB4u)sGJE)X98+h2&}gZsQq9guBdCmFkK zB@JvRQ$L-AB<{As_Vd~vTkO9r_TNV0cUnp84oivMX^jq0v5^G*+Fp19c*P9-{tRlq z+s*|`>A@qCNeqyPPZ4Y3Dskj@!rZ_Fg14|z!ioTz9# zu{yg%Z9RKdv8C4+*SI1JtJEp8BVc-g-9Zbo$p4ucX66XGvbVKMTCmRz)Gjxb@v|G+ zs1-8xFWg~v*v%QfAF=-;dSn_KuJeu5*6X}OosZa5Rw+Y##@aiziXND|I!fI$dZ3n? zL%yHRQLKj-$d8VvRwzRzx_V@upW9#2AeD(Z;<3}7n%;NB;+-8vUy?0$$JxeNjvau% z3y+Z!{IzWUUqLproX`VR%(qX&_Lt#bmXxkYN#T-&`&Y0x@2tW5CFAqQGq)oF?8lM^ zFk{}Z`fM+el^E%6#T<(gT(Zj##i8RCNl-nUHe*@`qatr&4Y_oW*$H1R;LPJ#R3+tPWF z+F1H^n%d{!3TBBJX3;lOQdW9ZDoQU(ef0pnScB9&F#p|$oy-TrCB=)rnggMdiTyWX zfTQfFKS~_n9WS}Z&;`E9Qs|#5#n}2HuwS6~x3HC5BnwI3V*}O||5otymSBCaBy-j0 zNxLlJ?kpva*P^#uNYpk9;~Kqvub~g((FMuqk~BMf0Y?`}LDGo*)8HCn(G8K<{pcNb z61JVaj$1t7u5!TttX#YVR+-H)I&%|jtMGjG7O{W|c;1xpS-1fejS&#Nsva2I)DMO( zKp#xvKTLuD(fLPmVdP_G{LIO@V}){npLyne3rX?uH)39`RT*k-hN~KYQyZ2MYGA9= zGNmddgW7v`b&$87yGrfP&D+G9HoleEzqhu98bb0C%ssia!0vZ+$U6T1GCe?Zmzf^~7jUtz$(VQ8fG%jS ztfhDQk}+p=d3*|g1D+6F(Oc6X-Na+9*!2o#rj?_Id&{e3sGfO=@C)gIk>X7q+9B#w zEZ*70?4PZ&oj&D2zd*@L$9Krgmcrb8>bT%I=&_C^2U=1#Zq&gPf&b#tWqLXq<+LL^ z1F`4H#PvokkoNwAcT=6;=1f5K{m}{0PQ1?iyD0jGk8Y#xH=rDjZ&?a!ma+M(QV;Iy zz<4D-el78SEjppPVqU6>nQL3f?y4eqTlOv1mVniAW)oBnNK^G?WI!6ZYMogXIIb+c zB;_RoQo;_u%8JWU#h$i`^2^wE`s4V2HB}d+rm|ORDtf7Vx*#nzm!-Xdo!Pxtr49Xf z#`OrhDNjq9SCC{J0{>oNl6ELka*jsf`$tPIIv_{II~M#WNr8W|6k+RC3J^2?v%!BR zb=MhORR9>rfpCfA9Zi)P8mvN2SzDHtq?8OhTlEl5}4jXs^TjomH zzzYy(B%u>xEbPI*qeL=~DTMv!$G7a4z$5Ijt(cL4%h>g)JLEHO$k?n3&u0*>ss~Jh zjW^?;tNIFt=zCRESHK;rTtgpB@qBFVq4W*jm$IS>Il+7u&ktS6=fZo^o1%HFsn$MO zV$SFMmR55Z9s{6=CJb(weLod_D5_=`7 z!ySgV@2Z9~fb;99ZZ!HFuhH*#eQHkb<5%38))__ze z2mHVldoP$5)PWu!qNifu!mtDe#7ST11tdYCVJsqF%O)KwWLP8;s)mjEvh@wde8(o>Z{ z-DIL0Gj>%E91bT35CQh15cohOmwZ5hFMNO>d;l?f;U1IZw=fBzX{j> z>en{E=V#2xoFxa?Osq0UZvHwwf*-F_`!-LnF*SKl$$u;jO;g9yOq@>b|L81sAKWuf z^Zxz+hqCuiHcULs$oO8}u5k*uKF{7x6C@3n5IbcE%Q9yE#BpDnAVOOsr6W{m;ctr{WK!5Yy*k%j*hC z9Q?nz`+(j-J`;Js2Dk)oX67i}s2U%+CA zc=7^Q(E+j43bLpI?V}nuX^DbE6BK>z zjxLa2iXi^KboGHw9xYM;HGsX$?jA-ClG6_Z^VSPw=Rx}YLgDm1fP}(bv!7o7poOd8 zT3xEWe&N_@`isf=+stYlzI;qni6YNt(T5HH>iJ~F(Nmcgm#G`c*$x+UDf#14Fl%R7 zMxK)=Ezit#<~_=?a;a;Y+@MAaZwhv%IEOR0(2>%-BKD_JZ^>sKgTG}}DLS;QT(`=Y zC(kX?75;Wxy_ad(xPeOE?WgRZZA#hhtH`xJIs%_k_)34B@+J--7jSm9#RBMowd~cR z9vF!ZxJdjIVFKMH;d@wX`DwnJ$>HbW`MyHmS`>Nk)6Ck|VL$Pc;kA5=5Ae?HVH^CL9%zeu z*Y)pV+`*FB)jtvQ`#*t4>lOUv3Zo-Az(IJc4z$XC zPUHCV#m@*{9jLR%_`S&M#m6#3Nb&9>xlyc2c#>}Bl9$cPr_ZyX75_KDe?Hi65xdT3E-X9ax-yt=PEMd# zmO9lDa_Xyn)@v&9_ux+7vU_xfD$njz?aA$`KfOa`hqmd`+La1h=%v#ucj+Ygz%xF? z0)~HlfC%t^3HyHmTwWl5cLCg=H|!&4vD4vTHhdO705<##JZ&du&8ByECb@sZ|4i}$ z*m)=RZ}I>45(n&?4DL<9e#n&R$PD`HXDWo4U=K5xd&w2=n;&-c#oZ$3aV{G5gOw8qbsORuWM!#`n5-(vgTk_UK4|IhE) z|3AZ(>4iVSzk~T!jJd8qI_h;biUm@N~ zCN8ML_BZ1TG!t7`Oi;`JOJQQFviLvA!rtZ4mz8rqR>3Q_((iN#{?CJ2yJm;xF)K8A z@&e760e9Gv9a`WKtiz|7i;TYu{_pAH6?(s~J|cH#HGq=}oJr0P>>rp#?=N=V!9KR% zvHSG<>_aEm+|QON%!o4Yf9VMG(@}Z48e5|aC$aa;US2(GF%KMX>6zI2EPAc+ACq$( zUg&c0UJ9;_3o4zyrkJyl^p>8~acW9o!TXrg+@n1KyZBzR<6%pn0yg?<>-sI)g7~fT z)26kXwZUhDyq7Wu#eAbXe1@~htWzu2IQtGxt=O&bRTc+WKR_tG0AY&4{u}s!2Voj{^!uI z#~$XnGtmLF=mRqUe+IcfV*N>A---V@iTy)(ynEtQ1#>T$>tN!6VD1Mwk6ZMB>404m z=V;f2S@Iu0TdPJhd*gkKTt}VCZesgyf&W|70d9eR(*dng3#Lu%aSb}+OQ|-!!E-Kg z=>mIvv+9+@?d88VTt4GHlzcc`_o#o};`>=sR6?z}M9o|`@V&c>k7d3<7T?3`7o#10 zP|5#mCiSn#qvxnC-*1OW+Q8LW~S&c}S3M9Nedh%N4K@?~lDy&l#=Fc_@}4;5ZE{0{{UirKUN3aO@46YxVgF*a!N6$*ad$!N#{-{=+enJ`bAQ) z^VurR$fWNP9gt?ckjz67i)0Z88DHW)W{(WRR->3Qc8W#~9;IOehG|frK^o9|fcp3B zuYNrSAOqFAOF#AO+*dtXrF&L(RS5Vt*-b34Yy1@L7&}>k z6DDgXF~Lrr-${OO=Y;9#g&Ep|1cLj(aWk}q9Q=wA3$az3cg=fAt>H+#l7qL_weuJ2Kj6*Kf>i7 z{D=;CPweu}W|_zpK7b$XCO&N{@zhn~sw{FWwfy}bPy>8KPSbX$)RPx5-H=RP@Cthp za(Io^1s>oFm>+P5-{+>qX7nEvL^Ip6CP-%vUZb}sM{^ekYteFePx)T2r@!4d;DT29 z!q*;IsYrUuFT^|m|4(!&=CPtKKh$1kPPWbVRY)uT4}t%~*!(d3|4?kc+5J#z0=`og zDR9{ur*HBUT&$7DBb3N2Q+_g>jo5{P6#6UDGr&AtZK?1xVl(yzg~-d(OB2RURNr3x z)wL7&{jQ5Twdf#$Df8R-6yYx`c?)}uO z`v7o_jqd@@Is5b+q&~g4=4_Ex-1h|6J%{o9(9hBvUEt`4ZiCSkgVn9e0O$SseAiyS zlg2B>-&c9t)~YmUld`sx!*nNa=jX>PW-2ol7qowcFLUu+I`q0M@_Gg;kZ`jA?gX=wxVE4z*RPcnE+Bt5z zc8r-S|Iz4xaVxv3^Rd~Pa`7dbO^bsKxsh~&pppE?%-|85+!FZc%Z2U?!cc6lDz zeNZ{*{-59Tod$<*!TxLHEq&+~2e|D2zrw#^=I=;b`{`icd}r+DC(GNJ4k7RV>6cG> z{R2Kf96|N;*!)6_U6Yxp`zh!2Q*n~61(566fd2<4-t#8>X|VF+J7SSveg*ryU#s2! z$b0=O{M++?WdFa^2Y>q5K09K+gNgl~pYWHA1MwbMx7}iy@$f#Ji>9|Q*69~dh}NB zUfA`X{Tv(Ly-Q#0J-$7*-X%S|_18bk0PYP^FQjMJfxLDgKQ};KJM(j$2B}-8q4@sj zimtsixqA(?sJZ-de|u2Re@=5oC};a)kdqV(%3BQKH8j*6}5w|z?js0r@Y zS$YC4FfV5PKxc`=&w$swfA?H^ILN_^n14RJUT?@P4Zj`(J_0=d4DmTBU;5+s{>2JVu8+LO9Fe zRW5r%@6;1GyyLZToR0#q|2v334#F+f!2jg~YX0fu&~4vVGr6fo@`0Jra8)q_kmKOL zgt|380KKS<_@@cP1t;lGeN1l6=Fr}P#n*hV-%uC)68|n;@OwBh{JZjJ_Pp85kCy8P z|5juByXDh9{;XH@;WXWE)=fA^Z&OEq{RAGv!aP-_Md^7ldCU~%6?gAZeBgc+f~8k? z>GQ##{^b+cXO8#-_W#dpf7|C=*B{XV7N0mV${)l6zxO+}z30Ncqr*%Wpd0OXw*H78 z=%Y05)Z67XD$UHt-;ZI=>H!=-yM^yAq#w?yk9ib?MmaAEX;GzDqaQ*$wGR z9>68ty7rezXYO_HJc!)DVD;@jR0G>})h2KlC|au zC$nDyF2@SK+h^c=*-o$X5&8m8t=yyYt6Ta3!?E3`!SG4^_v6^}6U6H$iR(`xC#Fs% z-#;Cm-s$xATfEXE~28Zs6xCN*{89eRc@nEPkM}qGlBrGbfR94?TWC8%NS>jPJ3VzJmaK zfJ|o2s;Ms@^jfR*uyEbQ#x#)ww4Kq(5mESh@ze(C3n(gd&W3^_c6b!%RLFj~=2Pj9 zBma*dV0-{?*=_p)9pJ*eea~IJKy7j1T;tz6r_K2jN0|J?^)F!l*T4UYz5KZ)hkg$} z#M(w;o_gKAe@D-n=$p?@Q*Cl2JBnjeodJJO)Ol4TU033M;$`&wi@JLHc<~WF{vA7R zzJE*1Z`l4a-nYkg-{lvy#lQ6$nmDtFyoZw$m(CPt}7d%ZrcGlGd)ly4~ zV4i7>`wksE2;Wgu12!Gs-u!zLa{MjV54mmz|JZ&LyG9ROW(M@^*=lWDc2v7&fOl-; zeqw-7X8Vo*WB0<h(28hXh6q4n$@L0^E6(%3U7Lx$1?VF&R6CpFWo$}RneQ4>1tT0a#P5c z@|kZ?x5|!5SI8=M!+QD?2fzdAy`Q-tcJGjfJBjT+hHritn|*lvBpseGNnsNvD2!O< z;AHCh=z@Lt{CkM!gYoNwEWd}155(8s3GNO1b{hV-f&Z=SWZyb^g0_!0>`&Cr$&<8m z>O}3DI)QxPcyvRHkKj9UGXDQu^8fhM8SIEGe~Auw)PnsIeEAaaUkd)qu=TA{#aUU3 z-AAgCTVTKLCOy8z;J%QZQw7Z`&%aN<#61<~-BbPz<}ZjXcC9!n-=T}Oo4q%C#?8?V z@()KAz;EWYhR;KdVv3t${ezTwEL;gE!kL|ka(2fV<{Q`n+=w1%z&E%{e3};rzs9bE z)Y|Lx_6=MB!~yUA1^&OpO^dw;V?X^JyJ(mPt8dAf)0f+P2mbC`%+l1d|F5A*ui0tw zsHQ+wsh4ys;hgSd#i$~k_$2iTf2&G8rSI_?wW$oazO9b=lKQ}V-s>a!O8*3Hn&hhPT%sCmyVqx*Pp5L z_|rw z5%P9nVelpdHf*!jKHzv^RelHqb4W-d~X{*L0b{?_jk04U7P*i zYCgai!$0?ZcoHmUGBJAAGa??ser z`y%*X2h(>vdagpo%u+D5hhXv)fg>hrH*sDN`>uk>P3(pX>FVAvxap!)coqMeUI5zx zYSXlE<- zomvFg|A72R4~6A8-n@FlT*6&u_n1*a8nE}-@$|S5^Jnq*zfH{b(E0?pZ?-s&eAcnR zy*jWZ6pouq@Lg2tC|pqo=AZ~c9CI@gB%f5QJ=2mco$8?=?E7UH}9y+Sg!$#^afBhS|{7&it?z@0hllI?r)OX*s z1F!9z_`eJOf7i|y2lV`x`0v@x@ZZnz5juTuzC=%Igo8C~;1CV%KS)CdkSpwt4neQ< zK&JE>pyP9wD+V5o)8y@A7cW#vz$#VjS+2UUWvUAEX2y4!Qcj&wG2e%R#B1=N&_flM zs?fE;+UL1b$GpHkJpqTX<6$P)5bVAObUjf@zpt;5D> z^UyK!9XwjTL&jT zKld*4t>B*-sgm?Y>c3!8;ijgs&MMsj77PurdqTEkXZvOe!<?G6Z23cl4-Z1($i zZ=D$T6F#nScUkOiHG-ehduD82zId%>^Y59%t0=ElDSkyA*N?&U z5BTp7sY_H{iB?%Oyx;MW+kj-?G&_W8$A}8>}pSg2QEwj8|X0ri{Dy) zuVW{5A;;Gl9nc9o-wxb=^VPR%2R6+I=!`GW#fkg7bM_!-*V_@R=bMjj^}Zff>o+mm z-?b0#)mcOO4bsvD?(_!j(WP)WoX$tU=XSxlKDO_G)-7445j}db&v~p)vU}^)1m^YW z)5}`FQpNtuRJnVJYWI8U7WmKHMZePaO)89zVm3UHIZg8cvUKTSgaX)CbOP+3WJYM; zsEOKd0`~VIyTSR+VPh2l&UXywg|N0?g9PvdTY~p8p zO-wiNvs=gTa}&m6^C!cbIvHPMj?(E-I^Z@|W7gMY(*Qav@I zdgk8i@O>Jn+2v^MG+#a&bxLN%f{8nJjhceZ=NjLC_n4W;EPNh%l_xAf7tGb3DIN-4 zvQgpWO-{2vBWz!|_QRubXjeFM1oXHkRV(6rs&)q-B5zra-~P!QZA}w7TlCENXyqIy=Y8=K zJh@j?aGe}Z8MX2UFZBF}|8h8>Zol|Z4KLt_;Jo()&SYvQ2}k0T4{z5^YMW1)|9O7z zng0IE{{q`!4KZ%9-;*B=^Ir;B|56Y9)F%FS`2o%e&MlYeSI$aL?rf zwLR@Qm!I_ZHT!QH*;jCjI$tfkWQgq=zjB^lfbaP7v(6TZWk!9#T_G?cyq_NS4zFQT?sNN<6iMbsLtAF3R= z5PCpfnpix?btRBeq;8EHL_BVq2we}o8yJ;lvf&CAfHVxl@3iAfbb@S{29bQcD4>`rn z#1iYl;o_llv~|@%m4NMs%|BufpMn4T#PRs|#O~FOfc<>1pO4K)Dht>tS880(Kh)~G~7)=o;&FIPJlZugFHfklCPD)=TON^89Reh`0gFN zpyNl+5WCbleJ(a*_w?a2_Fz9Dj&D{2HtkkrC0M5ylzG=mzSFhv={MKj*9-dfAKhZ- zX+|+TSXY#A;EZBJ!{Nn*>mUA|Jmc)Br~}_G_3Z6OJ;v9+5BFo^(;wCNlD)>@zv($M zfpEjrSG}f3rBF#nBUDHa;9dHEn&IYs`>9%-uselJ#VgCwfEx= zkKnDWLPf)Ia0E_|_{4|w_ukThW0lPI+;j5({}%qq173l*>u_+IHqYFsUF@g~U~kQW zVN=xouN~;C=}n*SWoE4swAO2-I)B?voscfr`p)e-fNNs=HrQ`(K0o&@Cg|Lrm>>J! zl|CQm#1H6B{NJ5Ez8;;xKhmRBA9Tm=cWKu}z0e!S>ESBI*SNvIc_Xexa<9Un?vg9l zoY{wR^uc-Z&+FKqbe5fc$zCgzwPvY`{TA!yj>W17TB3>&biw}RDh*w$cz9= zeF9s^OeOy6kuB_#b>E~DUV+%c@eb~Tz`oi0?O=G@kdfMo_#5sKKWzTyVWYKi=t%7S z2(UjwYr(z`*k3hZgnaBVI%6$AYuBsM0UJh)(w4EjCpCjz)3Cda^1!_#51B20s9WfW8n9noc%OY-&*0GKiDq>_r4{aNU?=g%V#c=Wy;6Gr*6#M}4 z0HbCR1K|IUWo8ll?;E{9!6V$YWzc->oWG5}?OZrknEAX~irpwy6gnU}p_*D#B|F~B z6tepS-m5$dRz5w1g*Ll|Z%|i-jmNKjeveta`!BG~_%2!H$~q6%=H62} zyX&Yf>^`nIxO_4$qwnGLsw1a&|HUt0p52X4f70!z;N95?1^%)5hJWLSYI@G!?D;49 z9$pax-BT?5{x>3GRTz`#aB$wOyGvd2zv#uI9{2}3z;JK)cZofA^&Ym>15R%+nE!-t z&>}7M0n-Pcv5qbt*g_i%kbBp>Pb%93oz+LJe4b%za&%kFK z6bcVH92a}e=`{WKSCby;$oYEhJ9bm&qwhHQclrLV)9k)oo6UFm08u=SiGKV7oA1DA2X({W@A7>Ib;S=b=>X=x{hA!0 z`2v>fGk?HN{P->%y5c9G16rj!K0uESy?&GS=mWb)ov_1SztNJZv*<6P-xRDDfPI@k zE#xeS<=TYnB5IGt)IM&+#;GJR1-|;t^x?WIY3XuZU$s<)8$49HWwFWv&;h}VRgDg) z#1FW6aD$Tk*}I94kaP|mkem!p~G;TyZ;&c zgNI}H!TXTWV11NUg8vl*hifJGyurO)o2(f+LcXKMY1?=(M~)(JDm_B<6ot&0r5*Ga z`;M8SHQ4_pqZY`2%@Od=u3Yx?+-Hup2EV>64_lv$50H!9M_l;7RoJZhGWe$Muy-C_ zpL%iyB^ls9jkrJQwuAjbFkgHX{BveTS5x;mu2qBPYWq;IKWvixs5@=}`~LU>JLp5& zIh@|b;pG3&1$&7Bg7N4)fenJ+ z0SPPr0Uy<8_r!y%>SFq1#cOKD6@>gdA4l8os zSzS6F4JX8P`W?uFJ^rBk>~FgRw|fJ2{XV|^Blbcxf^oxtlkvhmw-cT?xFMTfyh9fd zH#L4hZ@!?nu0k27;pb*P#%yIXy?_s!pVJHQtJ9BbJ-J`%0T=#%v;TiwEBrDhuo}VV z8lm~~Cg9)VewPmT67#OdF5lm<|IYd+KHvwCOEq3U(*X_OzY#rf52-CEXP4kP1+NdJ z-{u6n_TcnLe4u00{zBLvefCn*@8aKZ@ACg0oq#@ww0Hpgp9m|`I_CMeu*+@+JCD13 z+g|Oz{#Fx4kCiue{~P>$!?$6*9eSVx(&?M;$@_n+Z~pR?zUQ&oeV3R|&=&t)EdP&m zBM#`^o;aY5^z6uEes)mDE;{JD#ld-AEO|Pxp6?Q{Z(I~61z^93d}!X)SbW<=UE39= zuu1cEZP8MtFZEQOw}%SXFIMqp4|Kp1Rqk3sEPxK!OFr=6I;91y&~b8t8JFQ(gzI(x z@@+cIOn~|MX79Iwa|ipJe#2VvkI(PNW5fOiWF6RFYxoD}tMU20vHynk<;*iI8!%K} zhJDimyx$t$V4xSyE6;OZ?E zUb&^BEBOCN+Qo8w_)x7GfDVEWejE0GGv`)%kGBk?S8)hE%ESdbhtb1;53q|kVAr^L z%q_xs&W@+>6#+WHo~(m=E;0APENMgm{Uhb@JR;!#>a}Y21>eR$U{~ONc&lU6z&||r z&5zho^#eP>=~H0VxSYB{QbfEi;p0aJGXu6QOjph^Gn#Nir6q7*JmU9x3GSbL1ovRu zY(2R>+xccOe#>4MJK2G2^6aM;{J&(!G0)dG|Dv)o=Az+H$ctuX_F^)9O_$;1zs(+Q zW^Jf381CPJe^)GEJ^Ekj0_U-l6Ksk9+uHxO*VZ%O62rgw2G%!h__sX&e+U2XKhQsc zSU*ynhe z`2P~|e^e|d7`Is9GH0|?_iyn7?D0kVe=f#8(dlF5?CuNDvI+Cl|9fmU`0YqMZ_@tj z@2K&8t@d1Zp`PEXOE>kSx2He3`97WOwbbOk{jNm^m=9pscZ8fE&viC`p#!x6q#Ln7 z53t`8vD2hC_P;mp?T)Uw5_*U{4}9uZEp2}XjL^oC~QNcP76>VOsQh(xro$lxWd;$D`s<8FC|6@F)la&h3O=c-E ze?2w~4jeej-#&b;XZ3KmMy7y$cIREMgqNsE^$*_>!+%8gvC9qo+YVTln4LF&|DW;y zNljqC@dbP1i4Pt={V(Pw-_ZZ~R=233W?V>B;l%`%UrS>ih}vjHjb6T`Ux|K$x2?Fh z-u&Oy0fyyPe4D-hy;#7d3+#DUpMc{Vpabl={|^4m{AeEv!!{(q^$!G9#YTW9G5I!FIsWIX(vad(;1 zXD{E?J6t!T3tIHSMfAeOt50;1y5agIff`A_edoWK?e3z#{l{1O%m4U))e}E{GI&_$ zxtv`p^voWDFYD-WW`2+96uJ04+qP>lbMXUu_SSb_eXUL%@B8b^ zx*mBEp5^n}#LQE`@X?O=^Sf>Z`opvH9o# z!@ubPuK~lgin_q+p=0s?$C48mtM$|beMe2w7VQ0Y_=2`inN1(=46PkGL90hil)K^I z=MXa#4e-W+e|Gjgc*HJ5=K3pgsf`)_vHeX|Uk6u-YX zz7D)sEBA8cXURhcB%dzQ_PK%7FBg*Mn}N>{?y>hMEq@4eQU12jM2Pvjr`f5-k?3}Abe(FdHp2sX?7Z3j&B0v%<+Tw=-9hDOb@`BOLraop9klc5~%TCzNyoP64-eV$)3~% zxcF|uyJ#iHv^PJXr8eN?3CRa`MW=M7CfJL-U|)QUZr{@* z{CyAg`CDhr={rED*R9YMID1OtY=>%`Zd@e>Xcf}~H(JH=05`}FWXACtK0^v~XuHS2 z+c9IYVrDH?!n}n_TkNju%iNW-+Ff}Y+*Po7kxI7{2LuxXge+I(fpvA zBo07dtfn5g%6@Ld1Z^5S#Sz25FSEjH=s8$8Y__(nKEfp9#1`ha{vVOBL9 zH1q^k-cu#~$+b=R63ri}U1hK<7*1Vg?H&>XG*sPI9kKLVbb!qUd?L?hyN3P=|E3dK z?fCCu+x6U^oz~-P^?i#4Y_7rl0TZVOz_~{~(w;}0`2REh%?BVBcuNf6^aauTgZ*!+ zZeqV#GP*Qd(eUk^i+@HQ_9;18asVglbT$e1K)w`<1zLfnmS zZc+di3&5r$jxWwVbV4fFUB1MVc;H)dh3#8~Isy3aV);Pu--CYrUf{pW_g(NU@CE+! zJGd`LD`f}$`o7EQ+lWwp4BQ6HoLJwv<^B!(E?>aT>?^V4?~~}2#1GwhSb;;C)tkIX zk??6=g;z6i{sN^gc2mX*H)XGOLkBFRF1SeLJE;rq_EgP&Z>8;6rMSbP4!=^+T5=y? z-VfPGuh9l%{UAN@@V}2G7ve)yI@h%znG*0i2Sr!2e5j zxuOT~0d7*)e^~cgukZ<;qZ2A}>X{QIhu#RMe>VC4gj+4S{>!)Eg{V+A7|pqO6QNHx zs#=%#WoXlsP2iloKDGWGEPb5pG+?P#_N_vGa|8V(VR)x6_FY&=)ne@XVgMrtqx? zs2v_t+{v@l&)K2e^afu7{&=_#NXmKQ*5F4!eDH|@+$+t~*HAKt%rxK5uolLxE=|I7iD z+^EvolPU0QH|jz>{k!bkiem5HmDoo*e6pVSA5Jdv0hcahF`sc1?uM)Ix?-;*&MP)3 zKo_>51J-TT#pBnMm{5xjZj#UPP<{8G)akxsZtws4PxbnyBYQ(buN-_GBB>*V#iQVTHrTVGFSe1LA>by4^4TY83i zp$~?9)s9)CSt<-#qlAr%6iZxQcI65^r=|mb!@pDi1N+zG*tu~1I(+R-%y>P~9`==l zjD%;OTJ`w}3w3#_o8sZwOmuTo+A=q40&dD(w@^h}+*L+huqxO?wR@KVk*gSC-9!wtxKWF7clO|RjafrGVn;83oI;Oh_58nEw!U*N-IM+OZA|AzfRV4t2P z@NJl1(tD6|+O?#B=&1YS=qnL#?Y;`B62>xxfy~ zbLHf|Z&F)B4_v5H#I9s*n81$FF>nv#cLa^Z=8quG2mjkg((^ObLkU|$m9_V@(n4Bn zfApq3@D{k?E0D7U|0{;f*Zytr(p;(m)6AYk7AQLArjp1xW@J5b<_puZo+_EWEXg_S zWx9?o$S|&n8l}*GoR&kb>TYu?X0gMJdfwBY)dc<&)zS%pCPjnl9>A|c2MNi>V>OSn>J;OfUv$5>z zctYP)JzPrk2K;Co9W6W^Ru}jzhSOI2Z>LKSw7u8%+La$@d)m*q9{rysgW(UGa}=-06$<1zS$&x`zh6gB*4{P_dn^*Rf$cWhjp zqR!AGNsYWVCyN;gI33vKkbN?YowqBMvUi8}t?*7XgH*f%+vPhGdWX2*i@d;i@&Xadm#8Sfi@AL-MH91M*|}APQIRUZ56H(4Fznk-imdnq zl`@0&?9oH^o_%1qYB95Ob9H!(r;d$gSMRt*x-@yAV%fbDKaY9+C2q=Gxe)xjtI*F~ zr8^d3{}-z^WSMdT*D4`2R6jH@14TV`H8IFqaJ&W_8}@zr57erD1LecxRdx?OVEA7N z-o1J32)be=vYgj0?TZco=OzyBO&1_b5X1j+d;pgi{#Q{?SW1q-i*q@#!3^-f3I30y zh)VwE)$G}QpvRA}|M1P*p0~y-YUQPOnQ37UCi#JK`dk}}s0Y&Tn@|3&;A$1K;5E8& zv0B;ZZqi3nri^fA%Q>%~AqL!bRU5{yCa1pu{FC1wIUDT5Jq`~*&_r@CetVS{dPeCX zCzQGWw9@ySU^ko(IRfUHhD;|fIENUe}U6>_43Bag|C|5_n6Zw9V{|^4| zy+m)^dG74`DP^zc|E{Ufi(I(&4(wwmNQ@eqo;$w9qZj{0oYbI%W9-^Gbp<|%SoVj! zR5d!FuJOL!eFUc;$q9UfX9#(ZeRc%@-SFGeEBuf8;4_|E^@K}5*y(z%?X~Nkoi3fw z_H^ba!M?+%M*Q#WRR;gB=mogXEOQ>b3l}2b*D7O&OU6@0uty_;Io~LJ_)FmbAUyyP z%=blKDq;^t4s|2Ab~EXtfS0R~p1JCzIQHEwRqlR2<%9-k=c0uQTC+t9==J`NeBU?! z`B&!nzQ+!&p+`2I`7ZFCkfa;n|Ay&-Ye{@J5peF{9y}WEZNHJ}isFQ1`Zx|y%Nj== zp@VvXakKw67wFjk@5uvz{m%6HS&g6vb;B9mdg;=NWyhe*HQMzR>JjM0f+Q$V*elXXN_xpM_a0ZuffLFmr+RgBFkB`Jv~>_Qf2_6${@@HQFzo2b?pzVFT{!{(X z#)oj}fVQ}IU0d$o;ZucM+3^F=0q=i&&)$(2^iI{1ca4R^go>_a|tLhx>9lDE5_LO*qiZl;kp*t$`vL2%=pfG27HW@cdA zG<#rwb@+e&LtVk__tf+k&2fXHl>G(R`eMYepNABIeG{|&uG4ySa;eFizmRbJlnyLe zrd@F64gQ*$K;Ex2(t}vPE7I+I`~ka1z2AEM`u*p(U}(6aJeKI%GEZf!UaqW7%ayZp zg|7K6Wj~j_>jyHM4|nD%@>&pt7l)W?A>49 z;CKbPz;u8Q_+Qg|fYyM2pZ?f-`~z=vzzXyC`}EgRWC`NYr=J$_v+m?f4ELVcd(#7p zhyz?=_;+Fe>IF;b5%9!saOd7KbbTwWlZpKkQppSACuGpSTwL`+H_C}!FJ-dR ziQ3i+>*f7`|NjA7{XzAJ>4QJR{Xaa%YoB4?8_OOkW*_?N*a_ty4TlRhTm@MbYJ9?b zkso~g1Y7#-6LVn2irjr(m+75PgTwM2`ILHg`c~o#ym||7FMhyVM+da{0xkZ)Z*hU? z0qYTRolfqMT7e7O|IX>cyq%x@f3x}6|M%eE#EAjW2|qmlLD%Bh8FamvU06RTrwks~ zychp~|48Qj&s}O_uWJQ-Vi~H=$^-w`9sHMHH(uT}_=1uZ>Azk%dp4-{$VT1Vzg`)E z>$Q2(81VXydSRD4f73zznAJPBYaiH8p&zFO`+2R{cj4Zp3-YPc6;X>zI(}Sx7kemj zC4BpH7i!eEoz$JVUE>1k2KFuX?+pH}htSSGJU{v`9qB)v%C1f9_WZ?4^7K^ZY7gaX z#$KZXQn#||<$>D^Brt+J?hU$Ea#3*ja({4Xb-HyvR5z?~Y38~2yu z|F6K$U&eFGu=$Je85W`=Jjo+2p?_%Zpc(N09cIRwzS*lc>Enm*xwJvgsR2BBLf`x2 zH`K4+ksE*yy7;N?VoM9D!Ie_mE9U>ah@4yjv3_27iL$|e=CORG9nMuMdH=+NSqk$x zNeyoU{G?0i@pYr-N6v5T0;S;Z-$~5Tb;C{QDP5$&ZyAPvF#UymiT{*L_CKc)59Htvm&UBpH#tjIVvtkWwnp+ z|MB~uy#K5NoEX6TfPX^|SkAEF33mJC|4&Zjjt=>5hwF_z(zP3K?EK>R5*An7L7I`b zPydb|TcV3W=z!hmy%TYIckeZI0eYwkDzGv1D-sX9{qZ;ayL5n^ZT*0Mxc(hG|3|vu zOZ*#e+B@)X@xJXh`jPn7_K*Ga<4EF%s6EAsd;k-D_mOI3&b)Np9Ms)M~%Ku>HUeeoN| zP9PTt_P_0_?tkl`QOw|<+PN1y&)huNzwtZxcX9ynapY4@Db$19o6jnxtX>Ca?n2Fg zo1^D9=o4aotNmF|fyMmR8{GA~&g%cyPI7BEKnLi1J2{TMo9x27I&YCuJQq^8TcDz? zi&PxUXY;2nxW`{_n%Tj~+}ML>PxTae`TUvQKYgKNK3f$?ZtB>?MGD9FkK}tBGiRaV z7P%>P#R7bPH~N5D>VGA>mg`24kD}Ljv+Kx*9p=6Yp0l30hjm)nqp!T-G4a9Po2)W@ z0QOgL->Z8cEyq7t+N-aYfm=^({^Fi}w1~%xvHc5s^+C`FhG%f^MUH!29RSf>`vTKvs zmS+uce&*a(Y*@UGZak*FEBDFYZM(KD*sd*ewz0=?r}lV+C}hb#g{?TGQyan+x&0E{ z*a?c;3s1-Dqx2lDg1f+tnVyBz`k3!=Tcf7r8>+sVu8e)~N*xT>jT0B(L-vQels+J8 zgu&zqgGbKQE_A@oA#>zAkR0IZ!%8!s9=o2N^@QBtKT3L*5tuc6`H6pv83E@#i5G|ie&Dq^v4uLni&%#~`ov?G^@jb4cc~5JWwVd({uAnQ z^a+A@lea&TqienX()V{gcE$gHg!#7je;5Dc|Evelk@xif{7COJ^UIg6rqb6;9}s>~ zHTlvTl`oZE^jwk2_Z8Xd|6d^QXZVkat7R`2IdW$9OEci%habH>Jyn${sfyXPMQMR6 z)o|2LjR)`nz(VonL}pV+WQ7gsJ-j_*b#dU&xnYccWkNcE$(--8*z9`yWlXBNom0mAR^^<7sD`_C@& z?K4Hkm@hdwoL$@F!7f~5*A`N{U$IDqzV0gCu|)a7t96#1{f44^J;M)r^yI0YJ$b?& zw?}&a>Xj-}vzeoJqo;-ayz~K`Wv5m&Jpgf@#Q$p+Dg$34XS)ZuU#Xnnb&B7zR>wTp zWyHRN{c|@fWbOt9v;SlT7+(Q*k>TFKzk_|^0PZdA(nFr~J1&9C(8F-gxey+Lh4ehR zf&KZ^JKWF@=Iguse{XvKR$}j$q6?Oi?{kR<`oJAuVm^9f4*r1~90rT}4AH#))3kL3 z@hY{u#uE6vsP9$f*1-E1t)p9+bDHI?`2%NbTGvUM)@6dGb(^56-6v^E*NK|cX}qR( zny9HACTimMV;q^Uwc)3Q^aw{@Vv5__yKMKN}{JYITf6P?es4s?K*t`Sr3uN zO1(~>UncoLbVV}wPrm+?eeF+`RrFMK58?5zdrU4cR_8CLD*q-OEfY2 z|CXG?E8_hh@B=Il(RBYQJpIWkK_@lf8`V8}r(5(&u*Szq6Ov75BUFZ~4CT^AQSLM(@4v25{@Gn7K>f z&vYkeJVV33?ylbS`t_#HZ+$;K$OHD|?DbV=P475Fdqz-C9)jH-u~28mEoSGQhhi2i zQSwsyc=@bF0n1bhX3wmmcAuK8=g(d_`S91zEC=vJFMc2o_~Nyi;H9_{5DMQfdwyoS z>-6G9ie9^1Nn2JccaNV+4+koH?{-D3gX7Eteq;9y^tAX9`)*b!eBk>R_=5l5;J+6( zzpvx>d-v!gFCH8AmoT5S1pF_e?|DJj?wa2!^Eel9&kZgDcYFc!_e}?w-8cN3cw+lK z@bTTS_4C2{Jba7=#1?aT{VcBMqi-w^xRCfi(B}x;O3dyi+*H8Q04*KssY%_(X>!Lg zn%-ri=JlGPh5cs9t>1Lb={-d=d$Ieu*A&g^GfngR&Cr5=Gvzj5mUBJ7?+o65GP;7j zu>Ab!uLo;ZhavR!vU6-Kd&tOnltd)xX%V%f%ZkGBqhdY}(WgFAN*Z?EB;9&{z`6-|Pr1%L^nfEgws;|#T0jf_ zsSDD}e2dNjpa6C#!=m$J@K3<9BV{55#HnsW!EqG_X=b6p@S*_3| zKf71(ztT{C>GADlFHlSH$D^;jgjDrU%|Rxq;vE0iSgMb%6J+;^Gzg zjcdf}ix%sLP<&x2l{3-s|`ieu(6k^Bd@qw;TFC+Iy3{aJt=*+(6gsfA;u}!KAHQ#@| zO1G||Hs+)B6+VjhSfdDh;-fQG$fL(-4K+UiJKgQ;4w^KO{v0?}ixT0^AP#Wq0P*wy zB+`d)&@t|s)1JK^-_rBP>`(7b*!}jr=l9(;^6MV- z(9hJq5gs}W{!a`C|GYkO@?yo#TdXwje}fsFVtkeSp!M+cxT~JuE~xuF-5UWj|Q znTc#@Ah z-@@Zf_`tsKoNfW<-5psD&b_ejb}j?kp51$BF*sk$>5k0r+D-Gibalk8-MF`~2fD#> z0r>xIV*oGugBSN3g0DYFZsY+LfPXvP!2DeNh?zb5X&ygkF~HnD6SaBqF8RYrI+I%9 zRBZaZ?la`xcNUnRquJf3ac>;;l5yw?_=3RXGUE3o^zOI~ouQe1$HRj>O4ECeqo2}r zg6W4zn%Q%bX7re-@$H6cT8BaG9Ke^yS30rWPj%SFCq?|-sGFr9IIXhNm#G1UYu_wS z=6~jC|9ImG!(X8Gx1Bs-5IsUWi5pgrgj+c%1{(>!Qy+kRu#J5;A3xE=u%6i>hJU+G zB3EE>K#HRStY*-xBKS$e!>=hMBwS&Km@PaKr9;OrlgrL=c9>iKzXk7a!FOAs-{15A z`8>ma9pcmvny59zrmFTf`2=u&4}Up5vskB2TvA!ZJ#@$?HE_QP+~4NfW(mz_c=O^{ zdIR8uVn^%+^uWGMt2g_Ai-iDJ+8NqVa<#xpktZ)f-kg)eLvixo~xdU?h|rO>;Zvw8{rdR{6HTCLd4D-<|ymX2=NsxazQA#3~< zynTgr@rDpdLPd>K?I$%*Zu-~n_+`#(+6Ru4R`)%>> zo(%U7_}@+LFPIp>dWM3@9s2SAwQtoSI92b_)BAup0BO|$mJ=}iC$);%eY>{$Uy8*5 z_yC&ckIFs}f>&i|@_$H$8*@`*|DT zapTh#JzzD1d(;3ctM4%5lB_!q=$SXa0Q@#_eVP11#Kk1ttbL45pw9pd-9`skzhNWi zfsE&v@1w)aADs$x=l^{H>h%l zuZlNqRDsVrWiMT$6n7uR!uuaJb+s-`S_2P0Jb)9uv3H|1gubA@e{CmU_oWWrt=?aV z-*0|^;Xmc%Q5{*fP^shq5*Nd_Ic2eqj`o00Zy6l39-7f{ntt@(i4=kYWG;^M|F`MjX{3J71 zD`1+BwohBd{5*a8(^osP9laGWbqyRF>)_g1>+Cbx%|7$s`5WjFq|UcslS1bCY9F`{ z#oixWv`vRR=!stJ&tAZ-S_nUhBk-8Icj}=*L~pRg0ZS|n!2ZuA)|icdF})l7#az!N_b~R`0UG|@7>(~b z9$P+Ev%&6^&LfEd*h{$k5WSXZs?985H&+pRxeCbz<}(wRi+;}~S9k--*R31uOHRyG zROo4W!Q(Kd%TSH$I#T1ijMV6M1BoF9!n8@Cz(2b{{=aMjbydzg*4Wz5(1cpf~RG`uaQ6$|95LRm^}h@D()x z>oo%ZE*(Jqv4K9MtW4@Dxz&1)?s&`l{lHwohaZ2X7XJ%#MK6?{%e(^lriQ!C|BwrC z;of|L-xHUfaGkaf$ohR>ljDE;!TO7SRb%rL=BSbsOZ;*hezUviwmXjSyE;0+@c#z+ zk$TBnW!!iH{^@x@2OkcgZzOPs%ELlb6cVh$5Zi6LQE`V(s6Ok4E^P5vrvC=rJF-AggSTfI~kv`W{v6N@ZbpdHg@%b)k$HWOPugS{0q zyqVWsqaB=qGs$PqLKjT;MpmH{&=1H?UblPpT6UL#d3GX(%=KmW-zEi->kGm6-|Ob5 z5FYR2Jb)eu1^=_DQ_P`8F^~Mje00Hl@|APoB$(Z~i{=o!n9uLvAMCe{0Za!N{w@A@ z`TmCc74U*MH2{2prOW~w{w)rePt0$4pNVd9;(u}hc5me0`n2MItS0sVw_tS#z4v*s zDY^yLYqH7HQ`4=ar=vOx+#{7)_{r3HYI1IHovR9VFjeuITKIN~60gf+;(QHh-yc0N zTqCKqOz$=X9ungEDGQWw;FRv=mFrnK`$mY73(j8AeReiy>^ZJbv;W|HKY9N!{QW~? z7V02#gL}E&%`8CJDDXdFHM_vG9REL&Jakg44lw*DfOW%rqH}_Obc8)lcJ4h!M?6&` zaX=b%fRyZeO1OSU@o<*KQunyZ9Pu@H1X8XyDhFQULUx>#-J(aQ2~3kOY_uF6;`9l& z<^s$|wz^&+aerwgu_3s*O|Gno=M6{q(GN9s%(=3UJ~^#ecbK1QMyFWpU@<@g(nP-C zF7I1ZQqSyCxf+|8wR&TDe>i|X!5#SN|D!+Xzf@P-4BuG>xrWCM*4yR<+hX6P6MpY} zi|^pf_!H+pXLd2S;3oAuc+TKaze|4duK7EB?iT-s-vj(NQTucJe?DVr?HhWjp24{Q ze})93#cf-49h-mmn7`^nH_`|12mXC^ z!+WFBJ=W7Jw_4HD;L643pC9j|bK_R)JQB|Jz6r~y$f$+z>f-N@S)`-%=vb~lViNiO8J@bdz)OkC z*+uQUj(p!n-3VB($Yp%b;KtlGiMSIkLcb|1v<-aw&seFg)0UwJytww_-g5cT%i>SY z!hgD#0%m!^W4r=E2h3rQ(cF#j>}({zzaHM*jgFWfVEEsM4`Anh!#_U2EbwnK8yzs$ zuusimR)@~`FP$_S{Qn=$-a0tX1WEJ!<1Q|4_qK0lx~mJ#Ah0d6Y+20AjFx0(S!9`+ zWoCAnnIzd3*cLNbXqnk%tm^K&or&Fxy_n7WC>#`-&A)5Zb5Us(tUz#%x2FA@#oxyZaE7V zt#SUG$-ung-el+02N)ld`(Jq^XRkh#bJu$43HHzr?3KcjM^a2pt2YX7-F_w|@LM<8 zcUnxJprrgU_E5^}o=XE7h_?IG?&$gYd;wfO{8$edDI)e)Hn2m4n17d#JIMq3jQjws zS2d6;qX)cFP>BzCW8?vSx?}hNa)Ghww=I{-bxY*tie<8G z_FUus{Ci%tj9| zpTCpK&y>y2G%rMB`$T-gM42DNYi3dB2TeAf%fe>LvXE)An!5$7xLvv&P0xymS>PVy zbGp-XB|W}1_<%KV{wu-#a{Rzj>Vf6d1FP@_5kp7(YSHW|QHvrMh&BiNjbpd$*y@B|VwrByypzR0u@O`Q1`^IBwXnH21>zn9E?~m81_=Xf}fp~mCEPA3S zIKXHumYzYRm6Ny+8iNMQQ37myWIZ)p5qmG2Zd4G@%cQ0NTw}G@@B;-U?9A({16ppB zNi)`T6MukzXt-Ho#Q&NC@&fXJ67tDNCqHpFbAX4j6EuBm!cy3)dqBF2E7_~x z$_>3zxv~2wy7?+8+j)c=5b3g>T0ft};`YM?+aj{IBv|O@hZRU>bF>3fb;JZVvm$UbSh>LP=(TznDHi@j3Dpo~jK}ZBcs(@3#Zo2ADI8-L~+9QO@u$;f6x6BNm09UXgihbr_e-*Yib}qfX zEbai$l`V6)`MYSRteCJsmL_IPeyu;;=;O_Y`7x zx@!K||2GjG{{%3v_)q0y^#PNdeDDE2_y#X}@gDH~)c@c=0-q6r&N7laN5Pik#GU%z z&(celPgx{uXS0uRQnt)bpTl0J$ucW$qRhrJ$d&Tw(=45wCHYxPO(ea4vtxd9a-k|AXNFa@q5-5q#g=#*G~C{{Z~AX?)vvOrB5= zG@ZI4Tj7J&()Zs0@4uEDU=8@+4F1=%3wSLW%U$$pw|bM8!4vIVaYe3O0MqyY#rQe! zeiqZ$=Xo0V|NQaUi|7C^k^iFwzQ#Pz;X@^oIdiQ9qu;c%a2JaK_A=}{D;eC{s3&=mXDboFtI{R~95d5n~(9j{_4_e8^@6sz&&O!MH z-Az{Pe{C||v$EcL@TKZ7TYCn7V6E{%k;l24FFJ8T)KmD3{l^4?0!~SMC z#5;G`%RoQj_8o2*;#cm$U34p_$4S>(Bie|N3VJ51_RQ|1YiZ9_1~s;85_f zry69(O7^24*ezWLR?FQztGIi;S`N>eA;pIe%ZF#r(K|n8Uc6Iw&dZit>*h<>&OB+x z2h^<2lJZ5_Qj(J?g|jos!85p{lOadB{dtW0e@Eca_J+Wnfw^@PGv#4@GqdI$>?7v} zTU~>+R8(=F48Dn)v+N3bL+0{cw6pK6qFinsJb<=xsqC4TE88+MWL8)x`gRA2c6E_x zH#Z3$=Oo@}?A)!XN!jU{5 zKaVNyFC^wK37;to@dJx_y%b(>31-B8;{P&y!Lo3C0RKN~tI_(Y-tXMHqw=Qtg}m22 z`|OEa&;I`ybV||a6e5{V)~8x5{vZw?kjVX#6m&qz#Co-OxWNQ{P5&;*ioPE)Je@g) z^l{$k09D(M{tw(waAXfKJ9ekJ2GR#~r|<71QT!d{{X_YFq4$)eK3=q9m^)=8oP4~ z_n$@gS|s&Xs1+{Vlug+y#E1OfW7t>;wQ!Ow_Wf*%X8sxcw=+N4%Px%Wx)v$rj%)|B zGxzZc`=-q!*2hw>p%tX>u$(!;jb8M11IhV=;CI0PCZBX}8%>gBsT<|;A#wnE&!^6F zW9$O?J)HFEi|B)Sp2O51oWUQQCMP(n{lY69vT)umaYCP9@g27yzeRKU13Iz(R%k=? z$)2ch&BgHtJMkXmA>+Prl%fCLT%66_W$~O zvy0|-JMr_AG!WODiT$nA0lHgAO?zi;_qASlC#B$c-_gr*zMx#%*te)V1~tAKIzc1O zvQw2hyjl4}{!HhRbk<0Dg)28|xUG4U+6F9Fc9It~;Rl$rrjFNIz66}Bm14!WYKimJ zQbwPpu8ID2&kOX2Z<$$QX9?e1YZar`*P37b%xCNCdGI+t--mzIJLu2xR_-*v!gsOP z>tvnm%l}d8jvtbagKMSpz?`pJlV#0%G^$4~aerWoY~sGe#+-GsEPbA25&LKRr4jejB`YW${8R61{vQMf z7&wWzKgF;mU|V0$=W`436^p3}mPe2SV2XErE`irnAFz~MK)-(d`1#z6?UDcV{9oiR zPyTQD%Y#43rLAWq-pm@j;{(8d4Ess+<2djikEM(pEos)w`i!zSp2^@h2^^=`pxwiV zrC5(+7Qn^8f9hCxe5JsdCho|xfA0|U{4vb$x(*&K(W7+7NvLduPrbfrue27j7Z?2N zUfGHZ+*LSp6+WO<&aK+YeZx4g&y8@j)hm411?Zc~J;XHnLaFou65#^0M@T;6KU;R^ z9g{0(y1@QJIjwlV(6@u)6i>C&`mw%-Be-z2TXHhD$;d%YV(~3IpnhNnOuv!rfx-rk zlri*+?FWq%=RsEDGsuCN$yjQz46Gpd=&Q7E1A7 zTK^oLvPMfPu_$A9LO+ H{RbOmH}|irE?tthPoJT0d?dZh%RGF>%*;dbt{t1; z1ybe4=7rL-XN}z5rhXuYTp(M@(FPaK%aMYN962>TPqrp<12kn0J0wm^)16xOj@;tr zQpvAY4X@aRA9!;AfxLtZdG_iRzlVGHzuU5H$1a&UYp%paCrBXs?%dt{Wh}UN^9++I zeDCeMPm=%DNmc!Q*?;hYY+AEJimw(kqj&>ep@6#&hh!mri5Y<@k_q;+!FC=oe!gMU z{M0DQ11!KN%n!mJV2j`g^|=)8e|5|p@UMOV+|wgms(yfvm%sUsmK-0O>;mrL*`{$*zD21lFfYOTKL*c{;BwY6l#DJ?jd4myw<}9E_P3r+^{U!xxRq- z-ylA-Xa8$|v|$5A$mjuRL1^;$K z$N|_5=P=Zk-^CaS8^T_8|4jA|O%|_zw2=V%n11*&zX7%q45tw`#7;uUkpjq34&KbUI<|5e;oE8{-M&8o*zR{dD2>Ym88f;!o;{RBHrOXw4l z2heY9g%4=MjGctwR(o!`Yp91gf}Uq^gil^e0dalbk!y0`$LsVkYU$a5bN)N+_#3U& zwN_Bm*Qz`0#=5|-a{FCix`U4mJd9v+39(q zQ?_k5$^19^jUB7y6ni~xaU1OoG59sN#9m^rUcaOsVdpA$h2C_-x9;A7b|Z~FAhQf@ z>@9MDvbFQ2c;!MVTCzYcYzzUPB`?0PFHm90B>%j6leB|S5SQO%SHX=!Y6|*V@ZH7CJS-p=$q1eZ2cY<$CVAxe^WXvI^EtJ7 zd}8BCUC^aNMM5c}y1E+^ivBoA1IEy5QpqwZfyUUP2sA^EA{nf%NQ zTGPcE`j^WM%&QMb2Jd~?w*E@P{y&h{fz$#4=zhG=0eGSPR}O$406L*iTUW99qd7XOF-DB{8Rm+X- zljcfZN|ucMj{c<`cMRC)q5HiruG&KFbKTe{*-CBKgAXV>gLba0Qtnl^$Uf%(=Qswl zA23SR`Xn0ISNyLd?yn&SSmU2Ue=v^SYte8YQ8GPlKDVTA$tmK}Y3&DS+`rgE&foVu zb&fs(8lc5Xj)>V04q`WOG}!Ocg4=ZP~Pi7tox zjO=CVWT*M*^QE%)z$Mvt_yYfLa4*adcIk}*YK?ZRgF2&wew}I!Onc0K)i8m7)g$)J zG4pl5V!!XTvF{xILGz6c;(v$M2fS{^A6>ik5RFi`arbBY=F4&#?MCgTi_%?DDX;n2 zo#XVI1kA!uM@N&8Br~~h zy?5~nX`+|(i2Dr>+qp-GzVS8H=oW?3h@wfxA_ua_2fX zMz3F!@*DK9Z{3mS-4EC+bWJj&rcwvc-wT;8+0_0y0cpg2Vt(i(@Slc1NC*Gq{~;5( zr`3o5Ws%e1{=q*ne--uq3T!F--*R+7YHQeIyE9|G99XbjHceY$v%zEPWE)#}_OThOI62tEK z6#>q&EtuWRHX|hkU3oP5kuP=9MEH=ar;4Tg&OP)4FO6B*g4@sKN>Q5}J9=HV z-+4?5$mee0({B;K)ep2aus^)*t~_e*koG$ja`n(rxqGKdTDjHKM0_u%rUtM6rU6lsJ_W5k z^Oxu7QRLLRk=aPdi1osQ*1F9{e@1GY8{^|YeYtwT{q-uov@_wT~0QU8% z+P}5*`qn1p(EDfiNuufiawI=(ff4^T?~kSS*F0c6F+cS){wI>Jrz`e(swGqJC)+xK zd*U#?yYaU4`$l`g0s3(JgZW+uKl}l`Kc0zp?Dnu?7l46(>I3S4iS8ldYc@ul2il0I zxr_La;|GrLBj$r~Ypv&lC5C4#7H7k34>*s-2gF(jOPu-!{Dk5^c&M8Z^MA!ZK43hS zNO$(U!x`wsj-HWjVnllbx)XeW-kHA*{_obp z^)_%b98J(-?tiRcK6?Z4el>icVqWI~)pRa+iCZK&MTksimv|a`Sn{TB;x1?s7n*Z#j9`wQY$(5_g5eg+|#&Y7ktql2!yD>j7Bi@f4G0dDg4nqe$$_Fh)9QO%k zP;)GiBjG#cTFhy=6JIEIr&Y+cw4;*LFF?i*vy$Tzf|wHtmo0&Il7SCM!w1ArJA_lW zWv~l+>Fg!!D`qzp8lyEUk4RGFWC`KUiJ!GMzjt@>xAej1YJSD<$Oc}8|DQQ%t|l`R z5=!sGX`~}JZY<>c-+w2+|KEQn|M0*3Z}RoO`#^aD;ta3ON@UJsSsy8-tM%4bw-}UkP8p}<%)_DIL?!SEf<<}Vaf6A_BxQ0tb z_u2QYw>{t?&T#LOov}xbaQkOpDL$)04(uwD!#mD!GqRc)(p%D1TQAS=Ka|(cUdU@| z^`Ed$XcnqVZ_7RQioJWmzJup4*)#N9ZlWJaj7t!24=)K1j*!^MIEjx+kjRi|?g0gf zCs_CO2^Ke=j`X_xxM!0#ff&1Xha5kDLvHZ@Ua2_0OYVi$5c8{<@oZ$zQX~1%MdrI^ zWi2*xg|t-eb&*H>hJAY2m3P?1QY9~+J>&)od`oqM%%kTuJ1B)cB`F5R75lmPguGz- zg5ZBKeardu2u%3bkJSe#7q9~CuSEZ&bG{psaxv?$e0*`b@I* zlz3~jfA(Hz{+!YLc!)oopEGs87red?eJ_9dexa7W{CN<%U}8NMV+G!s`H90!c|aWg zAOU}nh^Y_o8{j12X5L25ukk;@I!t0l;6u#O{SQO;JIqZ2_}&qtgT$BrPX3&w%t9^T z?nj{PBBoW)^Xs`?&3#Gk$|(LD(E^ft)e;kW*){nHF7Pb%*#bXi2ALOL#=OxgG{MWL z36?Mqq`QF@yRsjQ`X<9Uh<-OpH?DIr+3Itv*DMQ zw9Dz3?J|?zNNHBIJlqbKG22ZyG2zh>g?xdDCnCy!s2n2f!;BvyapP^<8i1{juLy?{}TPkMa7f?f~PS z?`ggLd5;+gG!FZ&Np(pLdyebm&7%kM?&WKFi~Ypjtk*o%YN-bl?*@N|Z+M1Z=!G*p zaOkiE1&4^Mo2R&N^KKm47RRyf;!1t%?-L>Z{?P{JSFPK@&v8u(*hze=rW0Kf`F$I^ zs$1b!zvYZ^FkUO{NR;$TFs>+E+ zwQvh?7_Xo5Gry1n8@I`HX8dzQ$@js$Vm~Kfykz+$lm9cT#@%AYzT)19`+RKV01h zcLkRo5@hKuk<9i*jR=&;5y3o(|06^A-k}(A$?yeyj4wz8Q zu#x}A;x{6g`wuZgLk#}C2fK*ZQ2sl^UB!`}!di4iRmU#E*+=nvNt04${f}?lDmBD` z9_qUu{6JenlhoX<;O-ndh`Hn0eVZOr&T{S*urHXNz%uFqy%Dy6K0+?_L=M=`a`a~p zBX>;rc>EZDNnl1;`G76#9=UL$S$1x|B93;!(*G;&!+$+khO%2?D3~Amy_pRDmYs9o z4`=VZh1m5YALFvXK)*0Bi`Jb@ELAH&t;qhQ%A+y z#KP0iOA5=AnVyrxf4DQf7<+0kXUPf-XI>sXd{(*~ou9-`vFXxwVud`tyhl2ZA7OrS z4nEjZM*YrQ`v3cZ;^hz^i|4JA^=o#+Y3+~|>-Wm$z3i^6fM10JZ2PPDuUcMHKA`pe zzm5MU{QvfKXoLO@{(IT?$=)AKZ}}WKP0occIC-K)wro7fE)<<%B#(aeLS7N$e`1dL zEgA)VwzqajO;s(kjn|~Hz7edwL7Vj4u#S!{*|lr81XI(yu&c)1%}3nf@dAA!B{E{X z1O&#(c;d&tAJP9;w-M(bN(=mF3q6n)wAC$eCe5AC49?_YVL7D`e#WQr`qeY}@Y8FlM9Y>DsWrg(UvaP4 zS5sf0xxeDwi1}cDc??`1`Tt6^KWpd(EGG|GXUzZU%wLw_12hLPjGQ1JGwFX!+P_!} z8;J$~36}8u#QJ2gKHi#LzIJZJb98&`1xmJemKbt_D11Sbx$*$)^t46q3+6Q!(D

  • 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�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

      u@CkH*a)+1T25x+OoSjw76Fzs*zC8Dd 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 G@V@|`t%~yi literal 0 HcmV?d00001 diff --git a/MikuSB/Tool/AssemblyGenerater.cs b/MikuSB/Tool/AssemblyGenerater.cs new file mode 100644 index 0000000..fbc93aa --- /dev/null +++ b/MikuSB/Tool/AssemblyGenerater.cs @@ -0,0 +1,38 @@ +using MikuSB.Util; +using System.Reflection; + +namespace MikuSB.MikuSB.Tool; + +public class AssemblyGenerater +{ + private static readonly string SourceSpace = "MikuSB.MikuSB.Source."; + + public static async ValueTask LoadCustomData(Assembly assembly) + { + string[] embededRes = assembly.GetManifestResourceNames(); + foreach (var res in embededRes) + { + var stream = assembly.GetManifestResourceStream(res); + if (stream != null && res.Contains(ConfigManager.Config.Path.DataPath.Split("/").Last())) + await WriteOutputFiles(stream, res); + } + } + + private async static ValueTask WriteOutputFiles(Stream stream, string resSpace) + { + if (stream == null) return; + + string relativePath = resSpace.Replace(SourceSpace, ""); + int lastDotIndex = relativePath.LastIndexOf('.'); + string outputPath = string.Concat( + ConfigManager.Config.Path.ConfigPath, "/", + relativePath[..lastDotIndex].Replace('.', '/'), + relativePath.AsSpan(lastDotIndex)); + + if (File.Exists(outputPath)) return; // Check if file exist + + using var fileStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write); + stream.Position = 0; + await stream.CopyToAsync(fileStream); + } +} \ No newline at end of file diff --git a/MikuSB/Tool/HandbookGenerator.cs b/MikuSB/Tool/HandbookGenerator.cs new file mode 100644 index 0000000..bd1b147 --- /dev/null +++ b/MikuSB/Tool/HandbookGenerator.cs @@ -0,0 +1,102 @@ +using MikuSB.Data; +using MikuSB.GameServer.Command; +using MikuSB.Internationalization; +using MikuSB.Util; +using Newtonsoft.Json; +using System.Text; +using System.Text.Json.Serialization; + +namespace MikuSB.MikuSB.Tool; + +public static class HandbookGenerator +{ + public static void GenerateAll() + { + var directory = new DirectoryInfo(ConfigManager.Config.Path.ResourcePath + "/TextMap"); + var handbook = new DirectoryInfo(ConfigManager.Config.Path.HandbookPath); + if (!handbook.Exists) handbook.Create(); + if (!directory.Exists) return; + + foreach (var langFile in directory.GetFiles()) + { + if (langFile.Extension != ".json") continue; + var lang = langFile.Name.Replace("TextMap", "").Replace(".json", ""); + + // Check if handbook needs to regenerate + var handbookPath = $"{ConfigManager.Config.Path.HandbookPath}/Handbook{lang}.txt"; + if (File.Exists(handbookPath)) + { + var handbookInfo = new FileInfo(handbookPath); + if (handbookInfo.LastWriteTime >= langFile.LastWriteTime) + continue; // Skip if handbook is newer than language file + } + + Generate(lang); + } + + Logger.GetByClassName() + .Info(I18NManager.Translate("Server.ServerInfo.GeneratedItem", I18NManager.Translate("Word.Handbook"))); + } + + public static void Generate(string lang) + { + var textMapPath = ConfigManager.Config.Path.ResourcePath + "/TextMap/TextMap" + lang + ".json"; + + if (!File.Exists(textMapPath)) + { + Logger.GetByClassName().Error(I18NManager.Translate("Server.ServerInfo.FailedToReadItem", textMapPath, + I18NManager.Translate("Word.NotFound"))); + return; + } + + List textMapList = JsonConvert.DeserializeObject>(File.ReadAllText(textMapPath))!; + + if (textMapList == null) + { + Logger.GetByClassName().Error(I18NManager.Translate("Server.ServerInfo.FailedToReadItem", textMapPath, + I18NManager.Translate("Word.Error"))); + return; + } + + Dictionary textMap = []; + + foreach (var map in textMapList) textMap.Add(map.Value!.Hash, map.Text!); + + var builder = new StringBuilder(); + builder.AppendLine("#Handbook generated in " + DateTime.Now.ToString("yyyy/MM/dd HH:mm")); + builder.AppendLine(); + builder.AppendLine("#Command"); + builder.AppendLine(); + GenerateCmd(builder, lang); + + builder.AppendLine(); + WriteToFile(lang, builder.ToString()); + } + + public static void GenerateCmd(StringBuilder builder, string lang) + { + foreach (var cmd in CommandManager.CommandInfo) + { + builder.Append("\t" + cmd.Key); + var desc = I18NManager.TranslateAsCertainLang(lang, cmd.Value.Description).Replace("\n", "\n\t\t"); + builder.AppendLine(": " + desc); + } + } + + public static void WriteToFile(string lang, string content) + { + File.WriteAllText($"{ConfigManager.Config.Path.HandbookPath}/Handbook{lang}.txt", content); + } + +} + +public class TextMapEntry +{ + [JsonPropertyName("value")] public ValueEntry? Value { get; set; } + [JsonPropertyName("text")] public string? Text { get; set; } +} + +public class ValueEntry +{ + [JsonPropertyName("hash")] public int Hash { get; set; } +} \ No newline at end of file diff --git a/Proto/CmdIds.cs b/Proto/CmdIds.cs new file mode 100644 index 0000000..c613a23 --- /dev/null +++ b/Proto/CmdIds.cs @@ -0,0 +1,136 @@ +namespace MikuSB.Proto; + +public class CmdIds +{ + public const int None = 0; + public const int ReqLogin = 1; + public const int RspLogin = 2; + public const int ReqReconnect = 3; + public const int RspReconnect = 4; + public const int ReqRename = 5; + public const int RspRename = 6; + public const int ReqCallGs = 7; + public const int RspCallGs = 8; + public const int ReqUseItem = 9; + public const int RspUseItem = 10; + public const int ReqReadMail = 11; + public const int RspReadMail = 12; + public const int ReqMailAttachment = 13; + public const int RspMailAttachment = 14; + public const int ReqDelMail = 15; + public const int RspDelMail = 16; + public const int ReqSetNewGuide = 17; + public const int RspSetNewGuide = 18; + public const int ReqAccountInfo = 19; + public const int RspAccountInfo = 20; + public const int ReqResign = 23; + public const int RspResign = 24; + public const int ReqRecord = 25; + public const int RspRecord = 26; + public const int ReqAddFriendReq = 27; + public const int RspAddFriendReq = 28; + public const int ReqAgreeFriendReq = 29; + public const int RspAgreeFriendReq = 30; + public const int ReqRefuseFriendReq = 31; + public const int RspRefuseFriendReq = 32; + public const int ReqRemoveFriend = 33; + public const int RspRemoveFriend = 34; + public const int ReqGiveFriendVigor = 35; + public const int RspGiveFriendVigor = 36; + public const int ReqRecvFriendVigor = 37; + public const int RspRecvFriendVigor = 38; + public const int ReqPlayerRecommend = 39; + public const int RspPlayerRecommend = 40; + public const int ReqAddBlockList = 41; + public const int RspAddBlockList = 42; + public const int ReqDelBlockList = 43; + public const int RspDelBlockList = 44; + public const int ReqFindPlayer = 45; + public const int RspFindPlayer = 46; + public const int ReqPlayerProfile = 47; + public const int RspPlayerProfile = 48; + public const int ReqGetVersion = 49; + public const int RspGetVersion = 50; + public const int ReqRankList = 51; + public const int RspRankList = 52; + public const int ReqRank = 53; + public const int RspRank = 54; + public const int ReqBlockFriendReq = 55; + public const int RspBlockFriendReq = 56; + public const int ReqWordFilter = 57; + public const int RspWordFilter = 58; + public const int ReqSetCustomRoster = 59; + public const int RspSetCustomRoster = 60; + public const int ReqGlobalCounter = 61; + public const int RspGlobalCounter = 62; + public const int ReqMatch = 301; + public const int RspMatch = 302; + public const int ReqOnlineRoom = 303; + public const int RspOnlineRoom = 304; + public const int ReqOnlineRoomStart = 305; + public const int RspOnlineRoomStart = 306; + public const int ReqOnlineRoomExit = 307; + public const int RspOnlineRoomExit = 308; + public const int ReqOnlineRoomInvite = 309; + public const int RspOnlineRoomInvite = 310; + public const int ReqOnlineRoomAccept = 311; + public const int RspOnlineRoomAccept = 312; + public const int ReqOnlineRoomUpdate = 313; + public const int RspOnlineRoomUpdate = 314; + public const int ReqOnlineRoomReconnect = 315; + public const int RspOnlineRoomReconnect = 316; + public const int ReqOnlineRoomChatAccept = 317; + public const int RspOnlineRoomChatAccept = 318; + public const int ReqOnlineRoomUpdateMap = 319; + public const int RspOnlineRoomUpdateMap = 320; + public const int ReqChangeWorldChannel = 321; + public const int RspChangeWorldChannel = 322; + public const int ReqWorldChat = 323; + public const int RspWorldChat = 324; + public const int ReqFriendChat = 325; + public const int RspFriendChat = 326; + public const int ReqOnlineChat = 327; + public const int RspOnlineChat = 328; + public const int ReqOnlineRecruit = 329; + public const int RspOnlineRecruit = 330; + public const int NtfLog = 1001; + public const int NtfKickout = 1002; + public const int NtfBroadcast = 1003; + public const int NtfSyncAttr = 1004; + public const int NtfSyncLineup = 1005; + public const int NtfSyncNewMail = 1006; + public const int NtfSyncDelMail = 1007; + public const int NtfPlayerMsg = 1008; + public const int NtfLogout = 1009; + public const int NtfScript = 1010; + public const int NtfSetAttr = 1011; + public const int NtfSetStrAttr = 1012; + public const int NtfOnlineStart = 1013; + public const int NtfOnlineOver = 1014; + public const int NtfReadItem = 1015; + public const int NtfUpdateFriend = 1016; + public const int NtfDelFriend = 1017; + public const int NtfFriendReq = 1018; + public const int NtfFriendVigor = 1019; + public const int NtfBlacklist = 1020; + public const int NtfGlobalAttrs = 1021; + public const int NtfAntiData = 1022; + public const int NtfBlockFriendReq = 1023; + public const int NtfCustomRoster = 1024; + public const int NtfOnlineRoomInfo = 1031; + public const int NtfOnlineLoad = 1032; + public const int NtfOnlineKickout = 1033; + public const int NtfOnlineInvite = 1034; + public const int NtfOnlineState = 1035; + public const int NtfWorldChat = 1041; + public const int NtfFriendChat = 1042; + public const int NtfOnlineChat = 1043; + public const int NtfOnlineRecruit = 1044; + public const int NtfOnlinePlayerCheat = 1045; + public const int ReqRoomStart = 2001; + public const int RspRoomStart = 2002; + public const int NtfRoomReady = 2003; + public const int NtfRoomOver = 2004; + public const int NtfStopRoom = 2005; + public const int NtfRoomPlayerExit = 2006; +} diff --git a/Proto/Core.proto b/Proto/Core.proto new file mode 100644 index 0000000..1b3044f --- /dev/null +++ b/Proto/Core.proto @@ -0,0 +1,302 @@ +syntax = "proto3"; + +package core; + +option csharp_namespace = "MikuSB.Proto"; + +enum Sex { + MALE = 0; + FEMALE = 1; +} + +enum PlayerCoreAttribute { + LEVEL = 0; + EXP = 1; + VIGOR = 2; + CHARGED = 3; + VIGOR_TIME = 4; +} + +enum MailStat { + New = 0; + Readed = 1; + Geted = 2; + Removed = 3; +} + +enum GlobalMailStat { + Default = 0; + Banned = 1; + Deleted = 2; +} + +enum ChatType { + SYSTEM = 0; + WORLD = 1; + FRIEND = 2; + ONLINE = 3; +} + +message Empty { +} + +message SimpleBoolean { + bool data = 1; +} + +message SimpleUint { + uint64 data = 1; +} + +message SimpleString { + string data = 1; +} + +message IDArray { + repeated uint64 ids = 1; +} + +message StringArray { + repeated string list = 1; +} + +message PlayerProfileArray { + repeated PlayerProfile list = 1; +} + +message ChannelOpt { + string channel = 1; + repeated string subchannels = 2; +} + +message SimpleItem { + uint32 G = 1; + uint32 D = 2; + uint32 P = 3; + uint32 L = 4; + uint32 Count = 5; +} + +message Enhance { + uint32 level = 1; + uint32 exp = 2; + uint32 break = 3; + uint32 evolue = 4; + uint32 trust = 5; + uint32 pro_level = 6; + repeated uint64 spines = 11; + repeated uint32 affixs = 12; +} + +message Item { + uint64 id = 1; + uint64 template = 2; + uint32 count = 3; + uint32 flag = 4; + uint32 userdata = 5; + uint32 expiration = 6; + Enhance enhance = 7; + map slots = 8; +} + +message Lineup { + uint32 index = 1; + string name = 2; + uint64 member1 = 3; + uint64 member2 = 4; + uint64 member3 = 5; +} + +message Player { + uint64 pid = 1; + string account = 2; + string provider = 3; + string channel = 4; + string subchannel = 5; + string name = 11; + string sign = 12; + Sex sex = 13; + uint32 level = 14; + uint32 exp = 15; + uint32 vigor = 16; + map money = 17; + uint32 charged = 18; + uint32 create_time = 31; + uint32 last_login_time = 32; + uint32 last_vigor_time = 33; + uint64 item_id_alloc = 41; + repeated Item items = 42; + repeated Lineup solutions = 43; + map attrs = 44; + map str_attrs = 45; + repeated uint64 show_items = 46; + repeated uint32 show_attrs = 47; + map friend_pieces = 48; + uint64 last_pieces = 49; + map mail_box = 50; + uint32 last_global_mail_time = 51; + uint64 last_person_mid = 52; + repeated uint64 badges = 53; + map order_box = 60; + repeated uint64 tags = 96; + uint64 serial = 97; + uint32 ban_type = 98; + uint32 ban_expr = 99; +} + +message FriendPieces { + uint64 index = 1; + uint64 pid = 2; + uint32 shape = 3; + uint32 expr = 4; + bool deleted = 5; +} + +message PlayerMail { + uint64 mid = 1; + MailStat stat = 2; + uint32 time = 3; + uint32 expiration = 4; +} + +message Mail { + uint64 mid = 1; + string title = 2; + string message = 3; + repeated SimpleItem attachments = 4; + string sender = 5; + uint64 pid = 6; + uint32 time = 7; + uint32 expiration = 8; + uint32 life = 9; + MailStat stat = 10; + bool is_deleted = 99; +} + +message GlobalMail { + uint64 mid = 1; + string sender = 2; + string title = 3; + string message = 4; + repeated SimpleItem attachments = 5; + uint32 start_time = 6; + uint32 end_time = 7; + uint32 life = 8; + uint32 expiration = 9; + uint32 min_level = 10; + uint32 max_level = 12; + uint32 create_begin = 13; + uint32 create_end = 14; + repeated ChannelOpt channels = 15; + GlobalMailStat stat = 99; +} + +message Order { + string trade_no = 1; + string third_trade_no = 2; + uint64 pid = 3; + string product_id = 4; + uint32 product_quantity = 5; + uint32 total_price = 6; + uint32 paid_price = 7; + string finish_time = 8; + bool status = 9; + bool refund_status = 10; + string refund_time = 11; + string subchannel = 12; + string priceunit = 13; + bool supplement_status = 14; + string extendinfo = 15; +} + +message PlayerOrder { + string trade_no = 1; + string subchannel = 2; + uint32 done_time = 3; + uint32 refund_time = 4; + bool is_unreal = 5; + uint32 supplement = 6; +} + +message PlayerProfile { + uint64 pid = 1; + string account = 2; + uint32 create_time = 3; + string name = 4; + string sign = 5; + Sex sex = 6; + uint32 level = 7; + uint32 logout_time = 8; + uint32 friend_count = 9; + repeated Item show_items = 10; + repeated uint32 show_attrs = 11; + repeated Item badges = 12; + repeated uint64 tags = 13; +} + +message ClientProfile { + uint32 plat_id = 1; + string version = 2; + string os_version = 3; + string os_hardware = 4; + string telecom_oper = 5; + string network = 6; + uint32 screen_width = 7; + uint32 screen_height = 8; + float density = 9; + string cpu_profile = 10; + uint32 ram = 11; + string gl_render = 12; + string gl_version = 13; + string device_id = 14; + string resource_version = 15; + string language = 16; +} + +message OnlinePlayer { + uint64 pid = 1; + string name = 2; + uint64 face = 3; + uint64 faceframe = 4; + uint32 level = 5; + Lineup lineup = 6; + repeated Item items = 7; + bool captain = 8; + uint32 stateflag = 9; + repeated uint32 girllovelevel = 10; + map attrs = 11; +} + +message OnlineEndData { + uint64 pid = 1; + string infodata = 2; + uint32 status = 3; +} + +message AccountInfo { + string account = 1; + uint64 pid = 2; + uint32 new_guide = 3; + repeated uint32 error_info = 4; +} + +message ChatMsg { + ChatType type = 1; + uint32 channel_id = 2; + uint64 sender = 3; + uint64 recver = 4; + uint32 time_stamp = 5; + uint64 emoji = 11; + string text = 12; + PlayerProfile profile = 21; +} + +message CustomRoster { + uint64 pid = 1; + map roster = 2; +} + +message GlobalAttrs { + map attrs = 1; + map str_attrs = 2; +} diff --git a/Proto/Proto.csproj b/Proto/Proto.csproj new file mode 100644 index 0000000..4fe010f --- /dev/null +++ b/Proto/Proto.csproj @@ -0,0 +1,26 @@ + + + + net9.0 + enable + enable + false + MikuProto + MikuSB.Proto + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/Proto/Snowbreak.proto b/Proto/Snowbreak.proto new file mode 100644 index 0000000..c013009 --- /dev/null +++ b/Proto/Snowbreak.proto @@ -0,0 +1,381 @@ +syntax = "proto3"; + +package snowbreak; + +import "core.proto"; + +option csharp_namespace = "MikuSB.Proto"; + +enum PF { + NONE = 0; + REQ_LOGIN = 1; + RSP_LOGIN = 2; + REQ_RECONNECT = 3; + RSP_RECONNECT = 4; + REQ_RENAME = 5; + RSP_RENAME = 6; + REQ_CALLGS = 7; + RSP_CALLGS = 8; + REQ_USEITEM = 9; + RSP_USEITEM = 10; + REQ_READMAIL = 11; + RSP_READMAIL = 12; + REQ_MAIL_ATTACHMENT = 13; + RSP_MAIL_ATTACHMENT = 14; + REQ_DELMAIL = 15; + RSP_DELMAIL = 16; + REQ_SET_NEWGUIDE = 17; + RSP_SET_NEWGUIDE = 18; + REQ_ACCOUNTINFO = 19; + RSP_ACCOUNTINFO = 20; + REQ_RESIGN = 23; + RSP_RESIGN = 24; + REQ_RECORD = 25; + RSP_RECORD = 26; + REQ_ADD_FRIENDREQ = 27; + RSP_ADD_FRIENDREQ = 28; + REQ_AGREE_FRIENDREQ = 29; + RSP_AGREE_FRIENDREQ = 30; + REQ_REFUSE_FRIENDREQ = 31; + RSP_REFUSE_FRIENDREQ = 32; + REQ_REMOVE_FRIEND = 33; + RSP_REMOVE_FRIEND = 34; + REQ_GIVE_FRIENDVIGOR = 35; + RSP_GIVE_FRIENDVIGOR = 36; + REQ_RECV_FRIENDVIGOR = 37; + RSP_RECV_FRIENDVIGOR = 38; + REQ_PLAYER_RECOMMEND = 39; + RSP_PLAYER_RECOMMEND = 40; + REQ_ADD_BLOCKLIST = 41; + RSP_ADD_BLOCKLIST = 42; + REQ_DEL_BLOCKLIST = 43; + RSP_DEL_BLOCKLIST = 44; + REQ_FIND_PLAYER = 45; + RSP_FIND_PLAYER = 46; + REQ_PLAYER_PROFILE = 47; + RSP_PLAYER_PROFILE = 48; + REQ_GET_VERSION = 49; + RSP_GET_VERSION = 50; + REQ_RANKLIST = 51; + RSP_RANKLIST = 52; + REQ_RANK = 53; + RSP_RANK = 54; + REQ_BLOCK_FRIENDREQ = 55; + RSP_BLOCK_FRIENDREQ = 56; + REQ_WORD_FILTER = 57; + RSP_WORD_FILTER = 58; + REQ_SET_CUSTOMROSTER = 59; + RSP_SET_CUSTOMROSTER = 60; + REQ_GLOBALCOUNTER = 61; + RSP_GLOBALCOUNTER = 62; + REQ_MATCH = 301; + RSP_MATCH = 302; + REQ_ONLINE_ROOM = 303; + RSP_ONLINE_ROOM = 304; + REQ_ONLINE_ROOM_START = 305; + RSP_ONLINE_ROOM_START = 306; + REQ_ONLINE_ROOM_EXIT = 307; + RSP_ONLINE_ROOM_EXIT = 308; + REQ_ONLINE_ROOM_INVITE = 309; + RSP_ONLINE_ROOM_INVITE = 310; + REQ_ONLINE_ROOM_ACCEPT = 311; + RSP_ONLINE_ROOM_ACCEPT = 312; + REQ_ONLINE_ROOM_UPDATE = 313; + RSP_ONLINE_ROOM_UPDATE = 314; + REQ_ONLINE_ROOM_RECONNECT = 315; + RSP_ONLINE_ROOM_RECONNECT = 316; + REQ_ONLINE_ROOM_CHATACCEPT = 317; + RSP_ONLINE_ROOM_CHATACCEPT = 318; + REQ_ONLINE_ROOM_UPDATEMAP = 319; + RSP_ONLINE_ROOM_UPDATEMAP = 320; + REQ_CHANGE_WORLD_CHANNEL = 321; + RSP_CHANGE_WORLD_CHANNEL = 322; + REQ_WORLD_CHAT = 323; + RSP_WORLD_CHAT = 324; + REQ_FRIEND_CHAT = 325; + RSP_FRIEND_CHAT = 326; + REQ_ONLINE_CHAT = 327; + RSP_ONLINE_CHAT = 328; + REQ_ONLINE_RECRUIT = 329; + RSP_ONLINE_RECRUIT = 330; + NTF_LOG = 1001; + NTF_KICKOUT = 1002; + NTF_BROADCAST = 1003; + NTF_SYNCATTR = 1004; + NTF_SYNCLINEUP = 1005; + NTF_SYNC_NEW_MAIL = 1006; + NTF_SYNC_DEL_MAIL = 1007; + NTF_PLAYERMSG = 1008; + NTF_LOGOUT = 1009; + NTF_SCRIPT = 1010; + NTF_SETATTR = 1011; + NTF_SETSTRATTR = 1012; + NTF_ONLINE_START = 1013; + NTF_ONLINE_OVER = 1014; + NTF_READITEM = 1015; + NTF_UPDATE_FRIEND = 1016; + NTF_DEL_FRIEND = 1017; + NTF_FRIEND_REQ = 1018; + NTF_FRIEND_VIGOR = 1019; + NTF_BLACKLIST = 1020; + NTF_GLOBALATTRS = 1021; + NTF_ANTI_DATA = 1022; + NTF_BLOCK_FRIENDREQ = 1023; + NTF_CUSTOMROSTER = 1024; + NTF_ONLINE_ROOMINFO = 1031; + NTF_ONLINE_LOAD = 1032; + NTF_ONLINE_KICKOUT = 1033; + NTF_ONLINE_INVITE = 1034; + NTF_ONLINE_STATE = 1035; + NTF_WORLD_CHAT = 1041; + NTF_FRIEND_CHAT = 1042; + NTF_ONLINE_CHAT = 1043; + NTF_ONLINE_RECRUIT = 1044; + NTF_ONLINE_PLAYERCHEAT = 1045; + REQ_ROOM_START = 2001; + RSP_ROOM_START = 2002; + NTF_ROOM_READY = 2003; + NTF_ROOM_OVER = 2004; + NTF_STOP_ROOM = 2005; + NTF_ROOM_PLAYEREXIT = 2006; + NTF_ROOM_PLAYERCHEAT = 2007; + NTF_ROOM_PLAYERFINAL = 2008; +} + +message ReqLogin { + string provider = 1; + string token = 2; + core.ClientProfile client_profile = 3; +} + +message RspLogin { + string session_id = 1; + core.Player data = 2; + bool need_rename = 3; + uint32 area_id = 4; + int32 time_zone = 5; + uint32 timestamp = 6; + int32 certification = 7; + map global_attrs = 8; + uint32 world_channel = 9; + map global_str_attrs = 10; + uint32 error_code = 98; + repeated uint32 error_info = 99; +} + +message ReqReconnect { + uint64 pid = 1; + string session_id = 2; + uint32 world_channel = 3; + string language = 4; +} + +message RspReconnect { + string session_id = 1; + core.Player data = 2; + bool need_rename = 3; + int32 time_zone = 4; + uint32 timestamp = 5; + uint32 world_channel = 6; +} + +message ReqAccountInfo { + string provider = 1; + string token = 2; +} + +message ReqCallGS { + string api = 1; + string param = 2; + uint32 clicknum = 3; + repeated string dependent_params = 4; +} + +message ReqUseItem { + uint64 id = 1; + uint32 count = 2; +} + +message ReqOnlineCreateRoom { + uint32 onlineid = 1; + uint32 lineup_index = 2; +} + +message RspOnlineCreateRoom { + uint32 onlineid = 1; + uint32 lineup_index = 2; + uint64 roomid = 3; + repeated uint32 buffinfo = 4; +} + +message ReqOnlineAccept { + uint64 otherid = 1; + uint32 onlineid = 2; +} + +message ReqOnlineChatAccept { + uint64 otherid = 1; + uint32 onlineid = 2; + uint64 roomid = 3; +} + +message ReqOnlineRecruit { + uint64 room_id = 1; + uint32 online_id = 2; +} + +message FriendVigor { + uint64 pid = 1; + bool have_vigor = 2; + bool vigor_got = 3; + bool return_vigor = 4; +} + +message FriendVigorList { + repeated FriendVigor list = 1; +} + +message RankList { + message ListItem { + string member_name = 1; + uint32 score = 2; + string info = 3; + } + string rank_name = 1; + repeated RankList.ListItem list = 2; +} + +message RankInfo { + uint32 score = 1; + uint32 rank = 2; + uint32 sum = 3; + string info = 4; +} + +message GlobalCounterInfo { + string counter_name = 1; + uint32 value = 2; +} + +message NtfLog { + string action = 1; + string detail = 2; +} + +message NtfBroadcast { + string msg = 1; + uint32 duration = 2; + uint32 start_time = 3; + uint32 end_time = 4; + bool clean = 5; + repeated core.ChannelOpt channels = 6; +} + +message NtfSyncPlayer { + string sign = 1; + map core = 2; + map custom = 3; + map custom_str = 4; + repeated core.Item items = 5; + repeated uint64 show_items = 6; + repeated uint32 show_attrs = 7; + map money = 8; + repeated core.FriendPieces pieces = 9; + repeated uint64 badges = 10; + repeated uint64 tags = 11; +} + +message NtfSyncLineup { + core.Lineup lineup = 1; + bool remove = 2; +} + +message NtfCallScript { + string api = 1; + string arg = 2; + NtfSyncPlayer extra_sync = 3; +} + +message NtfSetAttr { + uint32 gid = 1; + uint32 sid = 2; + uint32 val = 3; +} + +message NtfSetStrAttr { + uint32 gid = 1; + uint32 sid = 2; + string val = 3; +} + +message NtfOnlineStart { + uint64 room_id = 1; + string room_addr = 2; +} + +message NtfOnlineRoom { + uint64 room_id = 1; + uint32 onlineid = 2; + repeated core.OnlinePlayer players = 3; + bool bmatch = 4; + repeated uint32 buffinfo = 5; +} + +message NtfOnlineInvite { + uint64 room_id = 1; + uint32 onlineid = 2; + uint64 playerid = 3; + string name = 4; + uint64 face = 5; + uint64 faceframe = 6; + uint32 level = 7; +} + +message NtfOnlineRecruit { + uint64 room_id = 1; + uint32 online_id = 2; + core.PlayerProfile sender_profile = 3; +} + +message NtfOnlineState { + uint64 room_id = 1; + uint32 onlineid = 2; + uint32 matchflag = 3; + repeated uint64 players = 4; + repeated uint32 stateflag = 5; + uint64 nowtime = 6; + uint64 levelid = 7; +} + +message ReqRoomStart { + uint64 room_id = 1; + bool is_reday = 2; + string error = 3; +} + +message RspRoomStart { + repeated core.OnlinePlayer players = 1; + repeated uint32 buffinfo = 2; + uint32 pollingweek = 3; +} + +message NtfStopRoom { + uint64 room_id = 1; + string reason = 2; +} + +message NtfRoomOver { + uint64 room_id = 1; + repeated core.OnlineEndData playerinfo = 2; +} + +message ReqAntiData { + uint32 data_type = 1; + bytes mtpData = 2; + int32 plat_id = 4; +} + +message NtfRoomPlayerCheat { + uint64 room_id = 1; + uint64 playerid = 2; +} diff --git a/SdkServer/Handlers/RouteController.cs b/SdkServer/Handlers/RouteController.cs new file mode 100644 index 0000000..b8d1f76 --- /dev/null +++ b/SdkServer/Handlers/RouteController.cs @@ -0,0 +1,285 @@ +using Microsoft.AspNetCore.Mvc; +using MikuSB.Configuration; +using MikuSB.SdkServer.Models; +using MikuSB.Util; +using System.Text; +using System.Text.Json; + +namespace MikuSB.SdkServer.Handlers; + +[ApiController] +public class RouteController : ControllerBase +{ + public static ConfigContainer Config = ConfigManager.Config; + + public static object BuildServerList(string version = "") + { + return new + { + code = 0, + ret = 0, + msg = "ok", + message = "ok", + version, + server_time = DateTimeOffset.UtcNow.ToUnixTimeSeconds(), + servers = new[] + { + new + { + id = 1, + server_id = 1, + name = Config.GameServer.GameServerName, + title = Config.GameServer.GameServerName, + host = Config.GameServer.PublicAddress, + ip = Config.GameServer.PublicAddress, + port = Config.GameServer.Port, + status = 1, + state = 1, + is_open = true, + open = true, + recommend = true + } + }, + game_server = new + { + host = Config.GameServer.PublicAddress, + ip = Config.GameServer.PublicAddress, + port = Config.GameServer.Port + }, + http_server = new + { + host = Config.HttpServer.PublicAddress, + port = Config.HttpServer.Port + } + }; + } + + private static string? ExtractUid(string? authInfo) + { + if (string.IsNullOrWhiteSpace(authInfo)) + return null; + + try + { + var normalized = Uri.UnescapeDataString(authInfo).Trim(); + var padding = normalized.Length % 4; + if (padding > 0) + normalized = normalized.PadRight(normalized.Length + (4 - padding), '='); + + var json = Encoding.UTF8.GetString(Convert.FromBase64String(normalized)); + using var document = JsonDocument.Parse(json); + return document.RootElement.TryGetProperty("uid", out var uid) ? uid.GetString() : null; + } + catch + { + return null; + } + } + + [HttpGet("/getGameConfig")] + [HttpPost("/getGameConfig")] + public IActionResult GetGameConfig() + { + object rsp = new + { + code = "0", + data = new + { + agreementUpdateTime = "1728552600000", + appDownLoadUrl = "", + enableReportDataToDouyin = false, + loginType = new[] { "channel" }, + openActivationCode = false, + qqGroup = (string?)null + }, + msg = "success" + }; + + return Ok(rsp); + } + + [HttpGet("/seasun/config")] + [HttpPost("/seasun/config")] + public IActionResult GetSeasunConfig() + { + object rsp = new + { + code = 0, + data = new + { + agreementUpdateTime = "1728552600000", + appDownLoadUrl = "", + enableReportDataToDouyin = false, + loginType = new[] { "channel" }, + openActivationCode = false, + qqGroup = (string?)null, + privacyUpdateTime = "1728552600000", + realNameAuth = false + }, + msg = "success" + }; + + return Ok(rsp); + } + + [HttpGet("/seasun/loginByToken")] + [HttpPost("/seasun/loginByToken")] + public IActionResult LoginByToken( + [FromQuery] string? uid, + [FromQuery] string? token, + [FromForm] string? form_uid, + [FromForm] string? form_token + ) + { + string finalUid = uid ?? form_uid ?? "10001"; + string finalToken = token ?? form_token ?? Guid.NewGuid().ToString("N"); + + object rsp = new + { + code = 0, + data = new + { + associatedAccounts = new[] + { + new { bindStatus = false, nickname = "", thirdPartyType = "mail" }, + new { bindStatus = true, nickname = Config.GameServer.GameServerName, thirdPartyType = "google" }, + new { bindStatus = false, nickname = "", thirdPartyType = "twitter" }, + new { bindStatus = false, nickname = "", thirdPartyType = "guest" }, + new { bindStatus = false, nickname = "", thirdPartyType = "steam" } + }, + isFirstLogin = false, + isNeedKoreaSciAuth = false, + ksOpenId = $"ks_{finalUid}", + nickname = Config.GameServer.GameServerName, + passportId = finalUid.Length > 10 ? finalUid[^10..] : finalUid, + playerFillAgeUrl = "", + status = 0, + thirdPartyUid = "", + finalToken, + type = "google", + uid = finalUid + }, + msg = "操作成功" + }; + + return Ok(rsp); + } + + [HttpGet("/seasun/getAccountInfoForGame")] + [HttpPost("/seasun/getAccountInfoForGame")] + public IActionResult GetAccountInfoForGame( + [FromQuery] string? uid, + [FromForm] string? form_uid + ) + { + string uidString = uid ?? form_uid ?? "10001"; + var finalUid = int.TryParse(uidString, out int parsedUid) ? parsedUid : 10001; + + object rsp = new + { + code = 0, + data = new + { + bindAccountTypes = new[] { "google" }, + channelUid = uidString, + loginAccountType = "google", + nickName = Config.GameServer.GameServerName, + passportId = uidString.Length > 10 ? uidString[^10..] : uidString, + uid = $"seasun__{uid}" + }, + msg = "操作成功" + }; + + return Ok(rsp); + } + + [HttpPost("/bisdk/batchpush")] + public IActionResult GetBatchPush() + { + object rsp = new + { + code = 0, + ret = 0, + msg = "ok", + message = "ok" + }; + + return Ok(rsp); + } + + [HttpGet("/query")] + public IActionResult GetQuery([FromQuery] string? version, [FromQuery] string? platform) + { + object rsp = new + { + platform, + version, + host = Config.GameServer.PublicAddress, + port = Config.GameServer.Port + }; + + return Ok(rsp); + } + + [HttpGet("/query_version={version}")] + public IActionResult GetQueryVersionV1(string version) + { + return Ok(BuildServerList(version)); + } + + [HttpGet("/query_version")] + public IActionResult GetQueryVersionV2([FromQuery] string version) + { + return Ok(BuildServerList(version)); + } + + [HttpGet("/api/serverlist")] + public IActionResult GetServerList() + { + return Ok(BuildServerList()); + } + + [HttpGet("/account/query-uid/{appId}")] + public IActionResult QueryUid(string appId, [FromQuery] string authInfo) + { + var uid = ExtractUid(authInfo) ?? "10001"; + + object rsp = new + { + code = "0", + msg = "success", + data = new + { + uid = $"seasun__{uid}" + } + }; + + return Ok(rsp); + } + + [HttpGet("/health")] + public IActionResult HealthCheck() + { + object rsp = new + { + status = "ok", + service = Config.GameServer.GameServerName + }; + + return Ok(rsp); + } + + [HttpPost("/api/auth/guest")] + public IActionResult AuthGuest([FromQuery] string? Token) + { + object rsp = new + { + Provider = "Guest", + Token = Token, + Account = "Account", + Pid = "123813131321312" + }; + + return Ok(rsp); + } +} diff --git a/SdkServer/Models/ResponseBase.cs b/SdkServer/Models/ResponseBase.cs new file mode 100644 index 0000000..b0a3dab --- /dev/null +++ b/SdkServer/Models/ResponseBase.cs @@ -0,0 +1,9 @@ +namespace MikuSB.SdkServer.Models; + +public class ResponseBase +{ + public string Msg { get; set; } = "OK"; + public bool Success { get; set; } = true; + public int Code { get; set; } + public object? Data { get; set; } +} diff --git a/SdkServer/SdkServer.cs b/SdkServer/SdkServer.cs new file mode 100644 index 0000000..68c74cc --- /dev/null +++ b/SdkServer/SdkServer.cs @@ -0,0 +1,95 @@ +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using MikuSB.SdkServer.Handlers; +using MikuSB.SdkServer.Utils; +using MikuSB.Util; +using System.Text.Json; + +namespace MikuSB.SdkServer; + +public static class SdkServer +{ + public static void Start(string[] args) + { + BuildWebHost(args).RunAsync(); + } + + private static IWebHost BuildWebHost(string[] args) + { + var builder = WebHost.CreateDefaultBuilder(args) + .UseStartup() + .ConfigureLogging((_, logging) => { logging.ClearProviders(); }) + .UseUrls(ConfigManager.Config.HttpServer.GetDisplayAddress()); + + return builder.Build(); + } +} + +public class Startup +{ + private static bool LooksLikeServerListRequest(string path, string? query) + { + var value = $"{path}?{query}".ToLowerInvariant(); + return value.Contains("server") + || value.Contains("version") + || value.Contains("query_version") + || value.Contains("serverlist"); + } + + public static void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); + + app.UseRouting(); + app.UseCors("AllowAll"); + app.UseAuthorization(); + app.UseMiddleware(); + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapFallback(async context => + { + var path = context.Request.Path.Value ?? ""; + if (LooksLikeServerListRequest(path, context.Request.QueryString.Value)) + { + var response = RouteController.BuildServerList(""); + context.Response.ContentType = "application/json"; + await context.Response.WriteAsync(JsonSerializer.Serialize(response)); + return; + } + var fallbackResponse = new + { + code = 0, + message = "ok", + service = ConfigManager.Config.GameServer.GameServerName, + path = path, + query = context.Request.QueryString.Value ?? "" + }; + + context.Response.ContentType = "application/json"; + await context.Response.WriteAsync(JsonSerializer.Serialize(fallbackResponse)); + }); + }); + } + + public static void ConfigureServices(IServiceCollection services) + { + services.AddCors(options => + { + options.AddPolicy("AllowAll", + builder => { builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); }); + }); + services.AddControllers() + .AddJsonOptions(options => + { + options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower; + }); + services.AddSingleton(_ => new Logger("HttpServer")); + } +} diff --git a/SdkServer/SdkServer.csproj b/SdkServer/SdkServer.csproj new file mode 100644 index 0000000..b69edd1 --- /dev/null +++ b/SdkServer/SdkServer.csproj @@ -0,0 +1,19 @@ + + + + net9.0 + enable + enable + MikuSB.SdkServer + + + + + + + + + + + + diff --git a/SdkServer/Utils/JsonStringToObjectConverter.cs b/SdkServer/Utils/JsonStringToObjectConverter.cs new file mode 100644 index 0000000..bf009d8 --- /dev/null +++ b/SdkServer/Utils/JsonStringToObjectConverter.cs @@ -0,0 +1,24 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MikuSB.SdkServer.Utils; + +public class JsonStringToObjectConverter : JsonConverter where T : class +{ + public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.String) + return JsonSerializer.Deserialize(ref reader, options); + + var jsonString = reader.GetString(); + return !string.IsNullOrEmpty(jsonString) + ? JsonSerializer.Deserialize(jsonString, options) + : null; + } + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + var json = JsonSerializer.Serialize(value, options); + writer.WriteStringValue(json); + } +} diff --git a/SdkServer/Utils/LoggingMiddleware.cs b/SdkServer/Utils/LoggingMiddleware.cs new file mode 100644 index 0000000..922b7ee --- /dev/null +++ b/SdkServer/Utils/LoggingMiddleware.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Http; +using MikuSB.Util; + +namespace MikuSB.SdkServer.Utils; + +public class RequestLoggingMiddleware(RequestDelegate next) +{ + public async Task InvokeAsync(HttpContext context, Logger logger) + { + var request = context.Request; + var method = request.Method; + var path = request.Path + request.QueryString; + + await next(context); + + var statusCode = context.Response.StatusCode; + + if (path.StartsWith("/report") || path.Contains("/log/") || path == "/alive") + return; + + if (statusCode == 200) + { + logger.Info($"{method} {path} => {statusCode}"); + } + else if (statusCode == 404) + { + logger.Warn($"{method} {path} => {statusCode}"); + } + else + { + logger.Error($"{method} {path} => {statusCode}"); + } + } +} diff --git a/TcpSharp/BasePacket.cs b/TcpSharp/BasePacket.cs new file mode 100644 index 0000000..f0241e5 --- /dev/null +++ b/TcpSharp/BasePacket.cs @@ -0,0 +1,51 @@ +using Google.Protobuf; +using MikuSB.Enums.Packet; + +namespace MikuSB.TcpSharp; + +public class BasePacket +{ + public ushort CmdId { get; set; } + public byte[] Body { get; set; } + public ushort SeqNo { get; set; } + public ushort PushSeq { get; set; } + public long Timestamp { get; set; } + public IMessage? Message { get; set; } + public PacketFraming Framing { get; set; } + + public BasePacket(ushort cmdId) + { + CmdId = cmdId; + Body = Array.Empty(); + SeqNo = 0; + PushSeq = 0; + Timestamp = 0; + Framing = PacketFraming.FourByteLittleEndianLength; + } + + public BasePacket(ushort cmdId, byte[] body, PacketFraming framing = PacketFraming.FourByteLittleEndianLength) + { + CmdId = cmdId; + Body = body ?? Array.Empty(); + Framing = framing; + SeqNo = 0; + PushSeq = 0; + Timestamp = 0; + } + + public void SetData(byte[] data) + { + Body = data; + } + + public void SetData(IMessage message) + { + Body = message.ToByteArray(); + Message = message; + } + + public void SetData(string base64) + { + SetData(Convert.FromBase64String(base64)); + } +} \ No newline at end of file diff --git a/TcpSharp/PacketCodec.cs b/TcpSharp/PacketCodec.cs new file mode 100644 index 0000000..99c17aa --- /dev/null +++ b/TcpSharp/PacketCodec.cs @@ -0,0 +1,308 @@ +using Google.Protobuf; +using MikuSB.Enums.Packet; +using MikuSB.Util; +using System.Buffers.Binary; +using System.Net.Sockets; + +namespace MikuSB.TcpSharp +{ + public class PacketCodec + { + private const int HeaderSize4Byte = 4; + private const int HeaderSize2Byte = 2; + private const int MaxPacketLength = 1024 * 1024; + private const ushort ClientMagic = 0x011F; + private const int ControlPacketSize = 35; + + private static readonly Logger Logger = new("PacketCodec"); + + public PacketCodec() + { + + } + + public async Task ReadPacketAsync( + Stream stream, + CancellationToken cancellationToken = default) + { + try + { + var lengthBuffer = new byte[HeaderSize4Byte]; + if (!await ReadExactAsync(stream, lengthBuffer, cancellationToken)) + { + Logger.Debug("Connection closed before packet header"); + return null; + } + + var framing = DetectFraming(lengthBuffer); + + switch (framing) + { + case PacketFraming.Control: + return await HandleControlPacket(stream, cancellationToken); + + case PacketFraming.TwoByteBigEndianLength: + return await HandleTwoBytePacket(stream, lengthBuffer, cancellationToken); + + case PacketFraming.FourByteLittleEndianLength: + return await HandleFourBytePacket(stream, lengthBuffer, cancellationToken); + + default: + return await HandleUnknownPacket(stream, lengthBuffer, cancellationToken); + } + } + catch (OperationCanceledException) + { + Logger.Debug("Packet read cancelled"); + return null; + } + catch (Exception ex) + { + Logger.Error($"Error reading packet {ex}"); + return null; + } + } + + public byte[] Encode(ushort packetId, byte[] payload, PacketFraming framing = PacketFraming.FourByteLittleEndianLength) + { + return framing switch + { + PacketFraming.TwoByteBigEndianLength => EncodeTwoByteFrame(packetId, payload), + PacketFraming.FourByteLittleEndianLength => EncodeFourByteFrame(packetId, payload), + _ => EncodeFourByteFrame(packetId, payload) + }; + } + + public byte[] EncodeRaw(ushort packetId, byte[] payload, PacketFraming framing = PacketFraming.FourByteLittleEndianLength) + { + return framing switch + { + PacketFraming.TwoByteBigEndianLength => EncodeTwoByteFrame(packetId, payload), + PacketFraming.FourByteLittleEndianLength => EncodeFourByteFrame(packetId, payload), + _ => EncodeFourByteFrame(packetId, payload) + }; + } + + #region Private Methods + + private PacketFraming DetectFraming(byte[] header) + { + var firstTwoBytes = BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(0, 2)); + var nextTwoBytes = BinaryPrimitives.ReadUInt16LittleEndian(header.AsSpan(2, 2)); + + if (firstTwoBytes == ClientMagic && nextTwoBytes == 0) + return PacketFraming.Control; + + if (firstTwoBytes == ClientMagic && IsValidPacketId(nextTwoBytes)) + return PacketFraming.TwoByteBigEndianLength; + + if (IsValidTwoByteHeader(firstTwoBytes, (ushort)nextTwoBytes)) + return PacketFraming.TwoByteBigEndianLength; + + return PacketFraming.FourByteLittleEndianLength; + } + + private async Task HandleControlPacket(Stream stream, CancellationToken cancellationToken) + { + var controlData = new byte[ControlPacketSize]; + if (!await ReadExactAsync(stream, controlData, cancellationToken)) + { + Logger.Debug("Connection closed during control packet read"); + return null; + } + + Logger.Debug("Control packet received"); + return new BasePacket(0) + { + Framing = PacketFraming.Control, + Body = Array.Empty() + }; + } + + private async Task HandleTwoBytePacket( + Stream stream, + byte[] header, + CancellationToken cancellationToken) + { + var packetId = BinaryPrimitives.ReadUInt16LittleEndian(header.AsSpan(2, 2)); + + var wrapper = new byte[ControlPacketSize]; + if (!await ReadExactAsync(stream, wrapper, cancellationToken)) + { + Logger.Debug($"Connection closed during wrapper read for packet {packetId}"); + return null; + } + + var payloadLength = BinaryPrimitives.ReadUInt16LittleEndian(wrapper.AsSpan(6, 2)); + var payload = await ReadPayloadAsync(stream, payloadLength, cancellationToken); + + if (payload == null) + return null; + + //Logger.Debug($"Packet received (2-byte framing): ID={packetId}, PayloadSize={payload.Length}"); + + return new BasePacket(packetId) + { + Framing = PacketFraming.TwoByteBigEndianLength, + Body = payload + }; + } + + private async Task HandleFourBytePacket( + Stream stream, + byte[] header, + CancellationToken cancellationToken) + { + var length = BinaryPrimitives.ReadUInt32LittleEndian(header); + + if (length < 2 || length > MaxPacketLength) + { + Logger.Warn($"Invalid packet length: {length}"); + return null; + } + + var frame = new byte[length]; + if (!await ReadExactAsync(stream, frame, cancellationToken)) + { + Logger.Debug("Connection closed during packet body read"); + return null; + } + + var packetId = BinaryPrimitives.ReadUInt16LittleEndian(frame.AsSpan(0, 2)); + var payload = frame[2..]; + + //Logger.Debug($"Packet received (4-byte framing): ID={packetId}, PayloadSize={payload.Length}"); + + return new BasePacket(packetId) + { + Framing = PacketFraming.FourByteLittleEndianLength, + Body = payload + }; + } + + private async Task HandleUnknownPacket( + Stream stream, + byte[] header, + CancellationToken cancellationToken) + { + var extraData = await ReadAvailableBytesAsync(stream, cancellationToken); + var combinedData = new byte[header.Length + extraData.Length]; + header.CopyTo(combinedData, 0); + extraData.CopyTo(combinedData, header.Length); + + Logger.Warn($"Unknown packet format detected, captured {combinedData.Length} bytes"); + + return new BasePacket(0) + { + Framing = PacketFraming.Unknown, + Body = combinedData + }; + } + + private async Task ReadPayloadAsync( + Stream stream, + int length, + CancellationToken cancellationToken) + { + if (length <= 0) + return Array.Empty(); + + if (length > MaxPacketLength) + { + Logger.Warn($"Payload too large: {length}"); + return null; + } + + var payload = new byte[length]; + if (!await ReadExactAsync(stream, payload, cancellationToken)) + return null; + + return payload; + } + + private byte[] EncodeTwoByteFrame(ushort packetId, byte[] payload) + { + var wrappedPayload = WrapPayload(payload); + var buffer = new byte[HeaderSize4Byte + wrappedPayload.Length]; + + BinaryPrimitives.WriteUInt16BigEndian(buffer.AsSpan(0, 2), ClientMagic); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.AsSpan(2, 2), packetId); + wrappedPayload.CopyTo(buffer.AsSpan(HeaderSize4Byte)); + + return buffer; + } + + private byte[] EncodeFourByteFrame(ushort packetId, byte[] payload) + { + var buffer = new byte[HeaderSize4Byte + HeaderSize2Byte + payload.Length]; + BinaryPrimitives.WriteUInt32LittleEndian(buffer.AsSpan(0, 4), (uint)(HeaderSize2Byte + payload.Length)); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.AsSpan(4, 2), packetId); + payload.CopyTo(buffer.AsSpan(HeaderSize4Byte + HeaderSize2Byte)); + return buffer; + } + + private byte[] WrapPayload(byte[] payload) + { + const int wrapperHeaderSize = 35; + var wrapped = new byte[wrapperHeaderSize + payload.Length]; + BinaryPrimitives.WriteUInt16LittleEndian(wrapped.AsSpan(6, 2), (ushort)payload.Length); + wrapped[11] = 1; + payload.CopyTo(wrapped.AsSpan(wrapperHeaderSize)); + + return wrapped; + } + + private static async Task ReadExactAsync( + Stream stream, + byte[] buffer, + CancellationToken cancellationToken) + { + var offset = 0; + while (offset < buffer.Length) + { + var read = await stream.ReadAsync(buffer.AsMemory(offset), cancellationToken); + if (read == 0) + return false; + + offset += read; + } + return true; + } + + private static async Task ReadAvailableBytesAsync( + Stream stream, + CancellationToken cancellationToken) + { + if (stream is not NetworkStream networkStream || !networkStream.DataAvailable) + return Array.Empty(); + + using var ms = new MemoryStream(); + var buffer = new byte[4096]; + + while (networkStream.DataAvailable && ms.Length < 16384) + { + var read = await networkStream.ReadAsync(buffer, cancellationToken); + if (read <= 0) + break; + + ms.Write(buffer, 0, read); + } + + return ms.ToArray(); + } + + private static bool IsValidTwoByteHeader(int firstTwoBytes, ushort packetId) + { + return firstTwoBytes >= 2 + && firstTwoBytes <= ushort.MaxValue + && IsValidPacketId(packetId); + } + + private static bool IsValidPacketId(ushort packetId) + { + return packetId != 0; + } + + #endregion + } +} \ No newline at end of file diff --git a/TcpSharp/SessionStateEnum.cs b/TcpSharp/SessionStateEnum.cs new file mode 100644 index 0000000..f89496a --- /dev/null +++ b/TcpSharp/SessionStateEnum.cs @@ -0,0 +1,10 @@ +namespace MikuSB.TcpSharp; + +public enum SessionStateEnum +{ + INACTIVE, + WAITING_FOR_TOKEN, + WAITING_FOR_LOGIN, + PICKING_CHARACTER, + ACTIVE +} \ No newline at end of file diff --git a/TcpSharp/SocketConnection.cs b/TcpSharp/SocketConnection.cs new file mode 100644 index 0000000..97a7ea2 --- /dev/null +++ b/TcpSharp/SocketConnection.cs @@ -0,0 +1,203 @@ +using Google.Protobuf; +using Google.Protobuf.Reflection; +using MikuSB.Enums.Packet; +using MikuSB.Proto; +using MikuSB.Util; +using System.Collections.Concurrent; +using System.Net; +using System.Net.Sockets; +using System.Reflection; + +namespace MikuSB.TcpSharp; + +public class SocketConnection +{ + public static readonly ConcurrentBag BannedPackets = []; + private static readonly Logger Logger = new("GameServer"); + public static readonly ConcurrentDictionary LogMap = []; + + public static readonly ConcurrentBag IgnoreLog = + [ + + ]; + protected readonly CancellationTokenSource CancelToken; + protected readonly Socket Socket; + public readonly IPEndPoint RemoteEndPoint; + + public string DebugFile = ""; + public bool IsOnline = true; + public StreamWriter? Writer; + + public int DownStreamSeqNo; + public int UpStreamSeqNo; + + public PacketFraming Framing; + + public SocketConnection(Socket socket, IPEndPoint remote) + { + Socket = socket; + RemoteEndPoint = remote; + CancelToken = new CancellationTokenSource(); + + Start(); + } + public SessionStateEnum State { get; set; } = SessionStateEnum.INACTIVE; + internal long ConnectionId { get; set; } + + public virtual void Start() + { + Logger.Info($"New connection from {RemoteEndPoint}."); + State = SessionStateEnum.WAITING_FOR_TOKEN; + } + + public virtual void Stop(bool isServerStop = false) + { + try + { + Socket?.Shutdown(SocketShutdown.Both); + } + catch { } + finally + { + Socket?.Close(); + Socket?.Dispose(); + } + try + { + CancelToken.Cancel(); + CancelToken.Dispose(); + } + catch + { + } + + IsOnline = false; + } + + public bool SocketConnected() + { + try + { + return !((Socket.Poll(1000, SelectMode.SelectRead) && (Socket.Available == 0)) || !Socket.Connected); + } + catch (Exception e) + { + return false; + } + } + + public void LogPacket(string sendOrRecv, ushort opcode, byte[] payload, PacketFraming framing) + { + if (!ConfigManager.Config.ServerOption.EnableDebug) return; + try + { + //Logger.DebugWriteLine($"{sendOrRecv}: {Enum.GetName(typeof(OpCode), opcode)}({opcode})\r\n{Convert.ToHexString(payload)}"); + if (IgnoreLog.Contains(opcode)) return; + if (!ConfigManager.Config.ServerOption.DebugDetailMessage) throw new Exception(); // go to catch block + var typ = AppDomain.CurrentDomain.GetAssemblies() + .SingleOrDefault(assembly => assembly.GetName().Name == "MikuProto")!.GetTypes() + .First(t => t.Name == $"{LogMap[opcode]}"); //get the type using the packet name + var descriptor = + typ.GetProperty("Descriptor", BindingFlags.Public | BindingFlags.Static)?.GetValue( + null, null) as MessageDescriptor; // get the static property Descriptor + var packet = descriptor?.Parser.ParseFrom(payload); + var formatter = JsonFormatter.Default; + var asJson = formatter.Format(packet); + var output = $"{sendOrRecv}: {LogMap[opcode]}({opcode}) ({framing})\r\n{asJson}"; + if (ConfigManager.Config.ServerOption.DebugMessage) + Logger.Debug(output); + if (DebugFile == "" || !ConfigManager.Config.ServerOption.SavePersonalDebugFile) return; + var sw = GetWriter(); + sw.WriteLine($"[{DateTime.Now:HH:mm:ss}] [GameServer] [DEBUG] " + output); + sw.Flush(); + } + catch + { + var output = $"{sendOrRecv}: {LogMap.GetValueOrDefault(opcode, "UnknownPacket")}({opcode})"; + if (ConfigManager.Config.ServerOption.DebugMessage) + Logger.Debug(output); + if (DebugFile != "" && ConfigManager.Config.ServerOption.SavePersonalDebugFile) + { + var sw = GetWriter(); + sw.WriteLine($"[{DateTime.Now:HH:mm:ss}] [GameServer] [DEBUG] " + output); + sw.Flush(); + } + } + } + + private StreamWriter GetWriter() + { + // Create the file if it doesn't exist + var file = new FileInfo(DebugFile); + if (!file.Exists) + { + Directory.CreateDirectory(file.DirectoryName!); + File.Create(DebugFile).Dispose(); + } + + Writer ??= new StreamWriter(DebugFile, true); + return Writer; + } + + public async Task SendPacket(byte[] packet) + { + try + { + if (Socket.Connected) + { + await Socket.SendAsync( + new ArraySegment(packet), + SocketFlags.None, + CancelToken.Token + ); + } + } + catch + { + // ignore + } + } + + public async Task SendPacket(BasePacket packet, ushort seqNo = 0) + { + // Test + if (packet.CmdId <= 0) + { + Logger.Debug("Tried to send packet with missing cmd id!"); + return; + } + + // DO NOT REMOVE (unless we find a way to validate code before sending to client which I don't think we can) + if (BannedPackets.Contains(packet.CmdId)) return; + LogPacket("Send", packet.CmdId, packet.Body,Framing); + byte[] packetBytes = new PacketCodec().Encode(packet.CmdId, packet.Body,Framing); + try + { + await SendPacket(packetBytes); + } + catch + { + // ignore + } + } + + public async Task SendPacket(int cmdId) + { + await SendPacket(new BasePacket((ushort)cmdId)); + } + + public async Task SendPacket(int cmdId, ushort seqNo) + { + var packet = new BasePacket((ushort)cmdId); + packet.SeqNo = seqNo; + await SendPacket(packet); + } + + public async Task SendPacket(int cmdId, IMessage msg, ushort seqNo = 0) + { + var packet = new BasePacket((ushort)cmdId); + packet.SetData(msg); + packet.SeqNo = seqNo; + await SendPacket(packet); + } +} \ No newline at end of file diff --git a/TcpSharp/SocketListener.cs b/TcpSharp/SocketListener.cs new file mode 100644 index 0000000..668cc41 --- /dev/null +++ b/TcpSharp/SocketListener.cs @@ -0,0 +1,105 @@ +using System.Net.Sockets; +using System.Net; +using MikuSB.Util; +using MikuSB.Internationalization; + +namespace MikuSB.TcpSharp; + +public class SocketListener +{ + private static IPEndPoint? ListenAddress; + private static readonly Logger Logger = new("GameServer"); + + private static Socket? serverSocket; + + public static readonly SortedList Connections = []; + + public static Type BaseConnection { get; set; } = typeof(SocketConnection); + + private static int PORT => ConfigManager.Config.GameServer.Port; + + private static long _nextId = 0; + + public static void StartListener() + { + if (serverSocket != null) + throw new InvalidOperationException("SocketListener already started."); + + ListenAddress = new IPEndPoint(IPAddress.Parse(ConfigManager.Config.GameServer.BindAddress), PORT); + + serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + serverSocket.Bind(ListenAddress); + serverSocket.Listen(100); + + Logger.Info(I18NManager.Translate("Server.ServerInfo.ServerRunning", + I18NManager.Translate("Word.Game"), + ConfigManager.Config.GameServer.GetDisplayAddress())); + + _ = Task.Run(AcceptLoop); + } + + private static async Task AcceptLoop() + { + if (serverSocket == null) + throw new InvalidOperationException("Server socket not initialized."); + + try + { + while (true) + { + Socket clientSocket = await serverSocket.AcceptAsync(); + var remote = clientSocket.RemoteEndPoint as IPEndPoint; + + if (remote == null) + { + clientSocket.Close(); + continue; + } + + try + { + var connection = (SocketConnection?)Activator.CreateInstance(BaseConnection, clientSocket, remote); + + if (connection == null) + { + Logger.Error($"Failed to create connection instance from {BaseConnection.Name}"); + clientSocket.Close(); + continue; + } + + var id = Interlocked.Increment(ref _nextId); + connection.ConnectionId = id; + + Connections[id] = connection; + Logger.Info($"Accepted connection #{id} from {remote}"); + } + catch (Exception ex) + { + Logger.Error($"Error creating connection: {ex}"); + clientSocket.Close(); + } + } + } + catch (ObjectDisposedException) + { + Logger.Info("Server stopped listening."); + } + } + + public static SocketConnection? GetConnectionByEndPoint(IPEndPoint ep) + { + Connections.TryGetValue(ep.GetHashCode(), out var conn); + return conn; + } + + public static void UnregisterConnection(SocketConnection socket) + { + if (socket == null) return; + + if (Connections.Remove(socket.ConnectionId)) + { + Logger.Info($"Connection #{socket.ConnectionId} with {socket.RemoteEndPoint} has been closed"); + } + } +} diff --git a/TcpSharp/TcpSharp.csproj b/TcpSharp/TcpSharp.csproj new file mode 100644 index 0000000..68eb1cf --- /dev/null +++ b/TcpSharp/TcpSharp.csproj @@ -0,0 +1,21 @@ + + + + net9.0 + enable + enable + false + TcpSharp + MikuSB.TcpSharp + + + + + + + + + + + +