diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..7d9974e --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,31 @@ +name-template: 'v$RESOLVED_VERSION 🌈' +tag-template: 'v$RESOLVED_VERSION' +categories: + - title: '🚀 Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + label: 'chore' +change-template: '- $TITLE @$AUTHOR (#$NUMBER)' +change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. +version-resolver: + major: + labels: + - 'major' + minor: + labels: + - 'minor' + patch: + labels: + - 'patch' + default: patch +template: | + ## Changes + + $CHANGES diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..151f936 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,43 @@ +name: MSBuild + +on: + pull_request: + push: + branches: + - main + - develop + paths: + - '*/**' + - '!./*' + - './*.sln' + - '!.github/**' + +jobs: + build: + name: MSBuild + runs-on: windows-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4.0.0 + + - name: Restore nuget packages + run: dotnet restore AtsExCsTemplate.sln --locked-mode + + - name: MSBuild + run: dotnet publish .\AtsExCsTemplate.sln --configuration Release --no-restore /p:platform="Any CPU" /p:OutputPath="./out/" + + - name: Collect artifact + run: | + ls -alR + mkdir plugins/ + find . -type f -path '*/out/publish/*.dll' | xargs mv -t ./plugins/ + shell: bash + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: plugins + path: ./plugins/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..bcf757e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,81 @@ +name: Create release +on: + workflow_dispatch: + pull_request: + branches: + - main + types: + - closed + +jobs: + build: + name: Generate dll + runs-on: windows-latest + outputs: + name: ${{ steps.version.outputs.name }} + version: ${{ steps.version.outputs.version }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup .NET + uses: actions/setup-dotnet@v4.0.0 + - name: Restore nuget packages + run: dotnet restore AtsExCsTemplate.sln --locked-mode + - name: Build sln + run: dotnet publish .\AtsExCsTemplate.sln --configuration Release --no-restore /p:platform="Any CPU" /p:OutputPath="./out/" + - name: Collect artifact + run: | + mkdir plugins/ + find . -type f -path '*/out/publish/*.dll' | xargs mv -t ./plugins/ + shell: bash + - name: Check assembly version + id: version + run: | + Get-ChildItem plugins/ -Recurse -Filter "*.dll" -File | foreach { + Write-Output $_.FileName + $_.FileName + "name=$_" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + $ver=(Get-Item $_.FullName).VersionInfo.FileVersion + $ver + "version=v$ver" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + } + shell: pwsh + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: plugins + path: ./plugins/ + + release: + name: Create release + runs-on: ubuntu-latest + needs: build + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Create tag + run: | + git tag ${{needs.build.outputs.version}} + git push origin ${{needs.build.outputs.version}} + - name: Get repository name + run: | + echo "REPOSITORY_NAME=${GITHUB_REPOSITORY#${GITHUB_REPOSITORY_OWNER}/}" >> $GITHUB_ENV + echo "FILE_NAME=${GITHUB_REPOSITORY#${GITHUB_REPOSITORY_OWNER}/}_${{needs.build.outputs.version}}" >> $GITHUB_ENV + - name: Compress DLLs + run: | + cd plugins/ + zip ${{env.FILE_NAME}}.zip -r * + - name: Create Release Draft and Upload Release Asset + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{needs.build.outputs.version}} + name: Release ${{needs.build.outputs.version}} + body: TODO New Release. + draft: true + prerelease: false + files: | + ./plugins/${{env.FILE_NAME}}.zip + LICENSE diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a30d25 --- /dev/null +++ b/.gitignore @@ -0,0 +1,398 @@ +## 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/main/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/ +[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/ + +# 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 +*.tlog +*.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 +# 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 +*.pfx +*.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 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# 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/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# 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 + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml diff --git a/AtsExCsTemplate.sln b/AtsExCsTemplate.sln new file mode 100644 index 0000000..1e4e2ee --- /dev/null +++ b/AtsExCsTemplate.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33516.290 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VehiclePlugin", "VehiclePlugin\VehiclePlugin.csproj", "{B4C723A7-FD6A-4931-B3BF-A6635F44899B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapPlugin", "MapPlugin\MapPlugin.csproj", "{C7F17EA7-55CF-449F-9255-C3B1721A4DC9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Extension", "Extension\Extension.csproj", "{80D2BAF1-62D2-4538-8249-5F1C6D2B3DCE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B4C723A7-FD6A-4931-B3BF-A6635F44899B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B4C723A7-FD6A-4931-B3BF-A6635F44899B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B4C723A7-FD6A-4931-B3BF-A6635F44899B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B4C723A7-FD6A-4931-B3BF-A6635F44899B}.Release|Any CPU.Build.0 = Release|Any CPU + {C7F17EA7-55CF-449F-9255-C3B1721A4DC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7F17EA7-55CF-449F-9255-C3B1721A4DC9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7F17EA7-55CF-449F-9255-C3B1721A4DC9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7F17EA7-55CF-449F-9255-C3B1721A4DC9}.Release|Any CPU.Build.0 = Release|Any CPU + {80D2BAF1-62D2-4538-8249-5F1C6D2B3DCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {80D2BAF1-62D2-4538-8249-5F1C6D2B3DCE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {80D2BAF1-62D2-4538-8249-5F1C6D2B3DCE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {80D2BAF1-62D2-4538-8249-5F1C6D2B3DCE}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7A01144D-3066-4B8F-9C06-A9212986B216} + EndGlobalSection +EndGlobal diff --git a/Extension/Extension.cs b/Extension/Extension.cs new file mode 100644 index 0000000..8c60f0c --- /dev/null +++ b/Extension/Extension.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using AtsEx.PluginHost.Plugins; +using AtsEx.PluginHost.Plugins.Extensions; + +namespace AtsExCsTemplate.Extension +{ + /// + /// プラグインの本体 + /// Plugin() の第一引数でこのプラグインの仕様を指定 + /// Plugin() の第二引数でこのプラグインが必要とするAtsEX本体の最低バージョンを指定(オプション) + /// Togglable を付加するとユーザーがAtsEXのバージョン一覧から有効・無効を切換できる + /// + [Plugin(PluginType.Extension)] + [Togglable] + internal class ExtensionMain : AssemblyPluginBase, ITogglableExtension, IExtension + { + /// + /// プラグインの有効・無効状態 + /// + private bool status = true; + + /// + public bool IsEnabled + { + get { return status; } + set { status = value; } + } + /// + /// プラグインが読み込まれた時に呼ばれる + /// 初期化を実装する + /// + /// + public ExtensionMain(PluginBuilder builder) : base(builder) + { + } + + /// + /// プラグインが解放されたときに呼ばれる + /// 後処理を実装する + /// + public override void Dispose() + { + } + + /// + /// シナリオ読み込み中に毎フレーム呼び出される + /// + /// 前回フレームからの経過時間 + public override TickResult Tick(TimeSpan elapsed) + { + if (status) + { + // 処理を実装 + } + return new ExtensionTickResult(); + } + } +} diff --git a/Extension/Extension.csproj b/Extension/Extension.csproj new file mode 100644 index 0000000..bcd31e4 --- /dev/null +++ b/Extension/Extension.csproj @@ -0,0 +1,21 @@ + + + + + net48 + AtsExCsTemplate.Extension + false + false + true + + + + + all + + + all + + + + \ No newline at end of file diff --git a/Extension/Properties/AssemblyInfo.cs b/Extension/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..617da01 --- /dev/null +++ b/Extension/Properties/AssemblyInfo.cs @@ -0,0 +1,26 @@ +using System.Reflection; + +// アセンブリに関する一般情報は以下を通して制御されます +// 制御されます。アセンブリに関連付けられている情報を変更するには、 +// これらの属性値を変更してください。 +[assembly: AssemblyTitle("Extension")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Extension")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + + +// アセンブリのバージョン情報は、以下の 4 つの値で構成されています: +// +// メジャー バージョン +// マイナー バージョン +// ビルド番号 +// リビジョン +// +// すべての値を指定するか、次を使用してビルド番号とリビジョン番号を既定に設定できます +// 既定値にすることができます: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.11.*")] diff --git a/Extension/packages.lock.json b/Extension/packages.lock.json new file mode 100644 index 0000000..983fb07 --- /dev/null +++ b/Extension/packages.lock.json @@ -0,0 +1,50 @@ +{ + "version": 1, + "dependencies": { + ".NETFramework,Version=v4.8": { + "AtsEx.CoreExtensions": { + "type": "Direct", + "requested": "[1.0.0-rc9, )", + "resolved": "1.0.0-rc9", + "contentHash": "b9KGmP9t37+L+9xt2NnI7a/mCh6i8A2Vodx6w/CkwT08bPqe9IsfCt/nSIT0Q8DghTU2nykSN/BBP1TCSH/0ng==", + "dependencies": { + "AtsEx.PluginHost": "1.0.0-rc1", + "ObjectiveHarmonyPatch": "1.1.0" + } + }, + "AtsEx.PluginHost": { + "type": "Direct", + "requested": "[1.0.0-rc9, )", + "resolved": "1.0.0-rc9", + "contentHash": "UjhPUohYL8hiMCwvJGQ0IZ5ALcgesonhTGMPO9f2ZjbOMLyHtIdPZj3yeqWUxfX4RqkgKwRidvw9G9OQo61BQA==", + "dependencies": { + "SlimDX": "4.0.13.44", + "UnembeddedResources": "1.0.0" + } + }, + "Lib.Harmony": { + "type": "Transitive", + "resolved": "2.2.2", + "contentHash": "70KvWz+DiUELxafsYL/LHxA/jH3PDWeApLo/VwtnrpTvRWQ/eUdPfS/l5funmhZWOy41QXw6UjVv+6C57Nx77A==" + }, + "ObjectiveHarmonyPatch": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "tzKrRbZCURq3RpAGbTXZywNJ1sGI/4ia+8OJ6NiSPfhw0uCevXUcHGFlpIxgOQei/3JQKqUMH9aq+RvWUn/44g==", + "dependencies": { + "Lib.Harmony": "2.2.2" + } + }, + "SlimDX": { + "type": "Transitive", + "resolved": "4.0.13.44", + "contentHash": "Oj8ICZ3tIGvd93s5W6wSWXckDb3payQCo4fWp7GKPwnnGck7wEHHBZwnwfJJTdNb+t+IYr4HJCu07YhZ82xrIg==" + }, + "UnembeddedResources": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "cZJ8PtsQwQ4EzShUiUdKz2blvj/r6v0/Tg5+43SsWBTpHhX79P05Srtu6ypiSPgOKePnpB5D/SM5HCyoaMQN6g==" + } + } + } +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bffa0a3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 停P + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/MapPlugin/MapPlugin.cs b/MapPlugin/MapPlugin.cs new file mode 100644 index 0000000..44dcba3 --- /dev/null +++ b/MapPlugin/MapPlugin.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using AtsEx.PluginHost.Plugins; + +namespace AtsExCsTemplate.MapPlugin +{ + /// + /// プラグインの本体 + /// Plugin() の第一引数でこのプラグインの仕様を指定 + /// Plugin() の第二引数でこのプラグインが必要とするAtsEX本体の最低バージョンを指定(オプション) + /// + [Plugin(PluginType.MapPlugin)] + internal class MapPluginMain : AssemblyPluginBase + { + /// + /// プラグインが読み込まれた時に呼ばれる + /// 初期化を実装する + /// + /// + public MapPluginMain(PluginBuilder builder) : base(builder) + { + } + + /// + /// プラグインが解放されたときに呼ばれる + /// 後処理を実装する + /// + public override void Dispose() + { + } + + /// + /// シナリオ読み込み中に毎フレーム呼び出される + /// + /// 前回フレームからの経過時間 + public override TickResult Tick(TimeSpan elapsed) + { + return new MapPluginTickResult(); + } + } +} diff --git a/MapPlugin/MapPlugin.csproj b/MapPlugin/MapPlugin.csproj new file mode 100644 index 0000000..105182b --- /dev/null +++ b/MapPlugin/MapPlugin.csproj @@ -0,0 +1,21 @@ + + + + + net48 + AtsExCsTemplate.MapPlugin + false + false + true + + + + + all + + + all + + + + \ No newline at end of file diff --git a/MapPlugin/Properties/AssemblyInfo.cs b/MapPlugin/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e5e6628 --- /dev/null +++ b/MapPlugin/Properties/AssemblyInfo.cs @@ -0,0 +1,25 @@ +using System.Reflection; + +// アセンブリに関する一般情報は以下を通して制御されます +// 制御されます。アセンブリに関連付けられている情報を変更するには、 +// これらの属性値を変更してください。 +[assembly: AssemblyTitle("MapPlugin")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MapPlugin")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// アセンブリのバージョン情報は、以下の 4 つの値で構成されています: +// +// メジャー バージョン +// マイナー バージョン +// ビルド番号 +// リビジョン +// +// すべての値を指定するか、次を使用してビルド番号とリビジョン番号を既定に設定できます +// 既定値にすることができます: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.*")] diff --git a/MapPlugin/packages.lock.json b/MapPlugin/packages.lock.json new file mode 100644 index 0000000..983fb07 --- /dev/null +++ b/MapPlugin/packages.lock.json @@ -0,0 +1,50 @@ +{ + "version": 1, + "dependencies": { + ".NETFramework,Version=v4.8": { + "AtsEx.CoreExtensions": { + "type": "Direct", + "requested": "[1.0.0-rc9, )", + "resolved": "1.0.0-rc9", + "contentHash": "b9KGmP9t37+L+9xt2NnI7a/mCh6i8A2Vodx6w/CkwT08bPqe9IsfCt/nSIT0Q8DghTU2nykSN/BBP1TCSH/0ng==", + "dependencies": { + "AtsEx.PluginHost": "1.0.0-rc1", + "ObjectiveHarmonyPatch": "1.1.0" + } + }, + "AtsEx.PluginHost": { + "type": "Direct", + "requested": "[1.0.0-rc9, )", + "resolved": "1.0.0-rc9", + "contentHash": "UjhPUohYL8hiMCwvJGQ0IZ5ALcgesonhTGMPO9f2ZjbOMLyHtIdPZj3yeqWUxfX4RqkgKwRidvw9G9OQo61BQA==", + "dependencies": { + "SlimDX": "4.0.13.44", + "UnembeddedResources": "1.0.0" + } + }, + "Lib.Harmony": { + "type": "Transitive", + "resolved": "2.2.2", + "contentHash": "70KvWz+DiUELxafsYL/LHxA/jH3PDWeApLo/VwtnrpTvRWQ/eUdPfS/l5funmhZWOy41QXw6UjVv+6C57Nx77A==" + }, + "ObjectiveHarmonyPatch": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "tzKrRbZCURq3RpAGbTXZywNJ1sGI/4ia+8OJ6NiSPfhw0uCevXUcHGFlpIxgOQei/3JQKqUMH9aq+RvWUn/44g==", + "dependencies": { + "Lib.Harmony": "2.2.2" + } + }, + "SlimDX": { + "type": "Transitive", + "resolved": "4.0.13.44", + "contentHash": "Oj8ICZ3tIGvd93s5W6wSWXckDb3payQCo4fWp7GKPwnnGck7wEHHBZwnwfJJTdNb+t+IYr4HJCu07YhZ82xrIg==" + }, + "UnembeddedResources": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "cZJ8PtsQwQ4EzShUiUdKz2blvj/r6v0/Tg5+43SsWBTpHhX79P05Srtu6ypiSPgOKePnpB5D/SM5HCyoaMQN6g==" + } + } + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..fa1507b --- /dev/null +++ b/README.md @@ -0,0 +1,139 @@ +# AtsExCsTemplate +[AtsEX](https://github.com/automatic9045/AtsEX)を使ったBve5またはBve6用のプラグインのためのテンプレート + + +## プラグイン開発が初めての人へ +~全然クイックじゃない~[クイックスタート](../../wiki/クイックスタート/)から取り掛かるのがおすすめです + + +## ライセンス +[MIT](LICENSE) + +## このテンプレートの機能 +- 取っ掛かりやすいように3種類のプラグインのファイル + - マッププラグイン + - 車両プラグイン + - 拡張機能 +- Actionsでのdll自動生成 +- 頑張って書いた[wiki](../../wiki/) + + +## 動作環境 +- [AtsEX](https://github.com/automatic9045/AtsEX) + - [ver1.0-RC9 - v1.0.40627.1](https://github.com/automatic9045/AtsEX/releases/tag/v1.0.40627.1) or later +- Win10 22H2, Win11 22H2 or later + - Visual Studio 2022 + - Microsoft Visual Studio Community 2022 (64 ビット) - Current Version 17.5.3 +- [Bve](https://bvets.net/) + - BVE Trainsim Version 6.0.7554.619 + + +## 依存環境 +- [AtsEx.CoreExtensions](https://www.nuget.org/packages/AtsEx.CoreExtensions/) (1.0.0-rc9) +- [AtsEx.PluginHost](https://www.nuget.org/packages/AtsEx.PluginHost/) (1.0.0-rc9) + +間接参照を含めたすべての依存情報については、各プロジェクトのフォルダにある `packages.lock.json` をご確認ください。 + + +## 使い方 +1. Use this template から新しくリポジトリを作成する +1. githubリポジトリの詳細を設定する +2. LICENSEの著作権表記を変更する +1. 自分の作りたい機能に合わせて設定する +1. コードを書く +1. リリースする + +### 0. 下準備 +#### 0.1. テンプレートから新しくリポジトリを作成して設定する +1. `Use this template`ボタンから新しいリポジトリの作成画面に入る + - `Create a new repository`で新しくリポジトリを作成する + - リポジトリの名前はお好みで + - Description にプラグインの概要とかを書いておくといい +1. リポジトリの設定画面でDescriptionやTopicsを設定する +1. LICENSEの著作権表記を変更する +1. README.md を消した後 README_TEMPLATE.md を README.md にリネームしてtodoを埋める + +#### 0.2. ローカルにクローンする +1. `< > Code`からURLをコピーする +1. Visual Studio を開いて リポジトリのクローン からコピーしたURLでローカルにクローンする + +できないときとかは下のコマンドでできる +```bash +git clone https://github.com/USERNAME/REPONAME.git +``` + +#### 0.3. Visual Studio でビルドできる状態にする +1. AtsExCsTemplate.csproj を開いてすべて保存から適当な場所にslnを生成する +1. NuGetからAtsEx関連のライブラリを入れる(ビルドすれば勝手に入る) +1. 開発するプラグインの種類に応じて要らないファイルを削除する + - MapPlugin/ + - マッププラグイン用のプロジェクト + - VehiclePlugin/ + - 車両プラグイン用のプロジェクト + - Extension/ + - 拡張機能用のプロジェクト + +#### 0.4. プラグイン情報の設定 +**Properties/AssemblyInfo.cs** +BveからAtsExのバージョン情報を見たときに表示される内容を設定できます +AtsExのバージョン情報画面から見えるのはファイル名と下の3項目です + +- AssemblyTitle + - プラグインの名前 +- AssemblyDescription + - プラグインの説明 +- AssemblyVersion + - プラグインのバージョン + +### 1. コードを書く +頑張ってゴリゴリ書きましょう + +### 2. ドキュメントを書く +1. githubリポジトリの詳細を設定する +1. LICENSEの著作権表記を変更する +1. README.md を消した後 README_TEMPLATE.md を README.md にリネームしてtodoを埋める + +### 3. 公開する +公開ができる状態になったらmainにpushしてtag打ってreleaseを作りましょう + + + +## デバッグについて +※この項目に書いてあることは環境によって差異があるかもしれないので適宜自分の環境に合わせて読み替えること +### 1. 生成物がAtsExから読めるようにする +そのままの状態でビルドしてもデバッグできないのでBveからAtsEx経由でビルドしたプラグインが読み込めるようにする必要があります +そのためには大きく次のA,Bで2通りのやり方があります +おすすめはBのシンボリックリンク経由です +シンボリックリンク経由だとpdbなどのごみがBve側のディレクトリに散らばったりしなくて嬉しいです +#### 1.A. 生成物の出力パスをいじる +1. メニューバー > プロジェクト > (プロジェクト名)のプロパティ を選択しプロジェクトのプロパティ画面を開く +1. サイドバー > ビルド を選択しビルドの設定画面を開く +1. 出力セクションの出力パスをプラグインの出力先に設定する +1. 試しにビルドしてみて出力されるか確認する +#### 1.B.シンボリックリンクを張る +1. 生成物がない場合はビルドしてダミーのdllを生成する +1. 出力ディレクトリ(binの下)にある生成物へのシンボリックリンクをプラグインの配置場所に配置する + - winでシンボリックリンクを簡単に張るには[Link Shell Extension](https://www.gigafree.net/system/explorer/hardlinkshellextension.html)がおすすめ +1. 試しにビルドしてみて更新されるか確認する +### 2. Visual Studioでデバッグする +#### 2.1. デバッグの設定をする +1. メニューバー > デバッグ > (プロジェクト名)のデバッグプロパティ を選択しプロジェクトのデバッグプロパティ画面を開く +1. 開始動作を"外部プログラムの開始"を選択しBveのパスを設定する +1. 必要があればコマンドライン引数にシナリオファイルのパスを設定する + - ここでシナリオファイルのパスを設定したらそのシナリオが直接読み込まれる + - 何も指定しなければ普通にシナリオ選択画面が立ち上がる +#### 2.2.実際にデバッグする +1. 適当にブレークとかを張る +1. デバッグを始める + - F5キー + - メニューバーのデバッグとテストの下あたりの開始ボタン(緑の三角形) +1. 張ったブレークで止まるか見てみる + + +## 備考・その他 +- C#は初めてなのでお作法がわかりません + - ミスとか良くないところがあったらissue立てるなりしてくれればできる範囲で対応します + - PR大歓迎!!! +- 自分用に作ったので適当です、自分が欲しい機能をとりあえず入れてます +- AtsExとAtsEXがどっちもあったのでここでは引用を除いてコードに準じてAtsExとしています + - AtsEXが正式な表記っぽい? diff --git a/README_TEMPLATE.md b/README_TEMPLATE.md new file mode 100644 index 0000000..0fbaaee --- /dev/null +++ b/README_TEMPLATE.md @@ -0,0 +1,215 @@ +# READMEのテンプレート +## 使い方 +1. 既にある`README.md`を消す +2. このテンプレートを`README.md`にリネームする +3. 水平線より上(この使い方の部分)を消す +4. `$REPONAME$`をリポジトリの名前に置換する +5. Todoのところを書く +6. 保存してコミット&プッシュ +--- +# $REPONAME$ +[AtsEX](https://github.com/automatic9045/AtsEX)を使ったBve5またはBve6用のプラグイン +**Todo: プラグインの概要を書く** + + +## プラグインの機能 +- **Todo: 機能を列挙** + + +## 導入方法 +**Todo: 導入方法を必要に応じて変更** +### 1. AtsEXの導入 +[公式のダウンロードページ](https://automatic9045.github.io/AtsEX/download/)を参照してください +### 2. 本プラグインの導入 +1. [Releases](releases/)から最新版がダウンロードできます +2. **Todo: 配置場所をプラグインの種類に応じて下から選択して変更** +```md +# 拡張機能の場合 +2. AtsExの導入場所にある`Extensions`フォルダの中に本プラグインを配置します + - デフォルト: `C:\Users\Public\Documents\AtsEx\1.0\Extensions` + - プラグインはBveの起動と同時に読み込まれ、必要に応じて他のプラグインから利用されます + +# マッププラグインの場合 +2. シナリオフォルダの中に本プラグインを配置します + ディレクトリ構成例: + ```text + Scenarios + ├ MyScenario.txt + └ MyMaps + └ SampleMap + ├ MapPlugins + │ ├ 本プラグイン($REPONAME$.dll) + │ ├ OtherPlugin.dll + │ └ ... + ├ MapPluginUsing.xml + ├ Image.jpg + ├ Map.txt + ├ Signals.csv + ├ Sounds.csv + ├ Sounds3D.csv + ├ Stations.csv + └ Structures.csv + ``` +3. `MapPluginUsing.xml`を作成し本プラグインの情報を記入します + MapPluginUsing.xml(例): + ```xml + + + + + + ``` +4. マップファイルから参照します + Map.txt(例): + ```text + BveTs Map 2.02:utf-8 + + // AtsEX + include ''; // AtsEXのプラグインを使うという宣言 + include '1'; // AtsEXのプラグインを探すディレクトリの深さ + include 'MapPluginUsing.xml'; // 使うプラグインの情報を伝えるファイルの位置 + + Structure.Load('Structures.txt'); + Signal.Load('Signals.csv'); + Sound.Load('Sounds_e.txt'); + Sound3D.Load('Sounds3D.txt'); + Station.Load('Stations.csv'); + + 0; + ... + ``` + +# 車両プラグインの場合 +2. 車両アドオンの中に本プラグインを配置します + ディレクトリ構成例: + ```text + Scenarios + └ MyVehicles +   └ SampleVehicle +     ├ Vehicle.txt +     ├ Ats +     │ ├ AtsEx.Caller.txt +     │ ├ AtsEx.Caller.x64.dll +     │ ├ AtsEx.Caller.x86.dll +     │ ├ AtsEXPlugins +     │ │ ├ 本プラグイン($REPONAME$.dll) +     │ │ └ OtherPlugin.dll +     │ ├ DetailManager.x64.dll +     │ ├ DetailManager.x86.dll +     │ ├ detailmodules.txt +     │ ├ OtherNormalPlugin.dll +     │ └ ... +     ├ Notch +     │ ├ Notch.txt +     │ └ ... +     ├ Panel +     │ ├ Panel.txt +     │ └ ... +     ├ Sound +     │ ├ Sound.txt +     │ ├ Motor.txt +     │ └ ... +     ├ Parameters.txt +     └ ... + ``` +3. 設定ファイルを作成し本プラグインの情報を記入します + - AtsEx.Caller.txt + ```text + ..\..\..\AtsEx + ``` + - AtsEx.Caller.x86.VehiclePluginUsing.xml: x64と同じ + - AtsEx.Caller.x64.VehiclePluginUsing.xml(例): + ```xml + + + + + + ``` + - AtsEx.Caller.x86.VehicleConfig.xml: x64と同じ + - AtsEx.Caller.x64.VehicleConfig.xml(例): + ```xml + + + true + true + + ``` +4. ビークルファイルから参照します + Vehicle.txt(例): + ```text + BveTs Vehicle 2.00 + PerformanceCurve = Notch\Notch.txt + Parameters = Parameters.txt + Panel = Panel\Panel.txt + Sound = Sound\Sound.txt + MotorNoise = Sound\Motor.txt + Ats32 = Ats\AtsEx.Caller.x86.dll + Ats64 = Ats\AtsEx.Caller.x64.dll + ``` + + 非AtsEXなプラグインと両立する場合は次の通りです + 1. Ats32とAts64でDetailManagerを指定する + 2. detailmodules.txtでAtsEx.Callerを指定する +``` +> [!WARNING] +> この項目の内容はすべてが正しい保証がありません +> 正確な情報を得るには以下を参照してください +> - AtsEXの[公式リポジトリ](https://github.com/automatic9045/AtsEX/) +> - AtsEXの[公式サイト](https://automatic9045.github.io/AtsEX/) + + +## 使い方 +- **Todo: 必要に応じて書く** +### パネル +**Todo: 必要に応じて書く** +| index | 型 | 機能 | 備考 | +| ------ | ---- | ---------- | ----------------- | +| ats001 | uint | 速度絶対値 | 1km刻みで切り捨て | + + +## ライセンス +- **Todo: `LICENSE`の著作権表示を書き換える** +- **Todo: ライセンスを変更する場合には`LICENSE`を書き換えた後にここも変更する** +- [MIT](LICENSE) + - できること + - 商用利用 + - 修正 + - 配布 + - 私的使用 + - ダメなこと + - 著作者は一切責任を負わない + - 本プラグインは無保証で提供される + + +## 動作環境 +**Todo: 動作環境を必要に応じて変更** +- Windows + - Win10 22H2 + - Win11 23H2 or later +- [Bve](https://bvets.net/) + - BVE Trainsim Version 5.8.7554.391 or later + - BVE Trainsim Version 6.0.7554.619 or later +- [AtsEX](https://github.com/automatic9045/AtsEX) + - [ver1.0-RC5 - v1.0.40101.1](https://github.com/automatic9045/AtsEX/releases/tag/v1.0.40101.1) or later + + +## 開発環境 +**Todo: 開発環境を必要に応じて変更** +- [AtsEX](https://github.com/automatic9045/AtsEX) + - [ver1.0-RC5 - v1.0.40101.1](https://github.com/automatic9045/AtsEX/releases/tag/v1.0.40101.1) +- Win10 22H2 + - Visual Studio 2022 + - Microsoft Visual Studio Community 2022 (64 ビット) - Current Version 17.5.3 +- [Bve](https://bvets.net/) + - BVE Trainsim Version 5.8.7554.391 + - BVE Trainsim Version 6.0.7554.619 + + +## 依存環境 +**Todo: 依存環境を必要に応じて変更** +- AtsEx.CoreExtensions (1.0.0-rc1) +- AtsEx.PluginHost (1.0.0-rc5) + +(開発者向け) +間接参照を含めたすべての依存情報については、各プロジェクトのフォルダにある `packages.lock.json` をご確認ください。 diff --git a/VehiclePlugin/Properties/AssemblyInfo.cs b/VehiclePlugin/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a75c419 --- /dev/null +++ b/VehiclePlugin/Properties/AssemblyInfo.cs @@ -0,0 +1,25 @@ +using System.Reflection; + +// アセンブリに関する一般情報は以下を通して制御されます +// 制御されます。アセンブリに関連付けられている情報を変更するには、 +// これらの属性値を変更してください。 +[assembly: AssemblyTitle("VehiclePlugin")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("VehiclePlugin")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// アセンブリのバージョン情報は、以下の 4 つの値で構成されています: +// +// メジャー バージョン +// マイナー バージョン +// ビルド番号 +// リビジョン +// +// すべての値を指定するか、次を使用してビルド番号とリビジョン番号を既定に設定できます +// 既定値にすることができます: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.*")] diff --git a/VehiclePlugin/VehiclePlugin.cs b/VehiclePlugin/VehiclePlugin.cs new file mode 100644 index 0000000..b68b437 --- /dev/null +++ b/VehiclePlugin/VehiclePlugin.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using AtsEx.PluginHost.Plugins; + +namespace AtsExCsTemplate.VehiclePlugin +{ + /// + /// プラグインの本体 + /// Plugin() の第一引数でこのプラグインの仕様を指定 + /// Plugin() の第二引数でこのプラグインが必要とするAtsEX本体の最低バージョンを指定(オプション) + /// + [Plugin(PluginType.VehiclePlugin)] + internal class VehiclePluginMain : AssemblyPluginBase + { + /// + /// プラグインが読み込まれた時に呼ばれる + /// 初期化を実装する + /// + /// + public VehiclePluginMain(PluginBuilder builder) : base(builder) + { + } + + /// + /// プラグインが解放されたときに呼ばれる + /// 後処理を実装する + /// + public override void Dispose() + { + } + + /// + /// シナリオ読み込み中に毎フレーム呼び出される + /// + /// 前回フレームからの経過時間 + public override TickResult Tick(TimeSpan elapsed) + { + return new VehiclePluginTickResult(); + } + } +} diff --git a/VehiclePlugin/VehiclePlugin.csproj b/VehiclePlugin/VehiclePlugin.csproj new file mode 100644 index 0000000..136adae --- /dev/null +++ b/VehiclePlugin/VehiclePlugin.csproj @@ -0,0 +1,21 @@ + + + + + net48 + AtsExCsTemplate.VehiclePlugin + false + false + true + + + + + all + + + all + + + + \ No newline at end of file diff --git a/VehiclePlugin/packages.lock.json b/VehiclePlugin/packages.lock.json new file mode 100644 index 0000000..983fb07 --- /dev/null +++ b/VehiclePlugin/packages.lock.json @@ -0,0 +1,50 @@ +{ + "version": 1, + "dependencies": { + ".NETFramework,Version=v4.8": { + "AtsEx.CoreExtensions": { + "type": "Direct", + "requested": "[1.0.0-rc9, )", + "resolved": "1.0.0-rc9", + "contentHash": "b9KGmP9t37+L+9xt2NnI7a/mCh6i8A2Vodx6w/CkwT08bPqe9IsfCt/nSIT0Q8DghTU2nykSN/BBP1TCSH/0ng==", + "dependencies": { + "AtsEx.PluginHost": "1.0.0-rc1", + "ObjectiveHarmonyPatch": "1.1.0" + } + }, + "AtsEx.PluginHost": { + "type": "Direct", + "requested": "[1.0.0-rc9, )", + "resolved": "1.0.0-rc9", + "contentHash": "UjhPUohYL8hiMCwvJGQ0IZ5ALcgesonhTGMPO9f2ZjbOMLyHtIdPZj3yeqWUxfX4RqkgKwRidvw9G9OQo61BQA==", + "dependencies": { + "SlimDX": "4.0.13.44", + "UnembeddedResources": "1.0.0" + } + }, + "Lib.Harmony": { + "type": "Transitive", + "resolved": "2.2.2", + "contentHash": "70KvWz+DiUELxafsYL/LHxA/jH3PDWeApLo/VwtnrpTvRWQ/eUdPfS/l5funmhZWOy41QXw6UjVv+6C57Nx77A==" + }, + "ObjectiveHarmonyPatch": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "tzKrRbZCURq3RpAGbTXZywNJ1sGI/4ia+8OJ6NiSPfhw0uCevXUcHGFlpIxgOQei/3JQKqUMH9aq+RvWUn/44g==", + "dependencies": { + "Lib.Harmony": "2.2.2" + } + }, + "SlimDX": { + "type": "Transitive", + "resolved": "4.0.13.44", + "contentHash": "Oj8ICZ3tIGvd93s5W6wSWXckDb3payQCo4fWp7GKPwnnGck7wEHHBZwnwfJJTdNb+t+IYr4HJCu07YhZ82xrIg==" + }, + "UnembeddedResources": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "cZJ8PtsQwQ4EzShUiUdKz2blvj/r6v0/Tg5+43SsWBTpHhX79P05Srtu6ypiSPgOKePnpB5D/SM5HCyoaMQN6g==" + } + } + } +} \ No newline at end of file