Compare commits

..

2 Commits

Author SHA1 Message Date
Pieter-Jan Briers
91d4b32c66 Version: 167.0.1 2024-03-10 20:50:30 +01:00
Pieter-Jan Briers
e788f03a28 Backport 859f150404
(cherry picked from commit 24d5c26fa6)
(cherry picked from commit 688efac67b634c613539b783a9fb6e679948cd53)
2024-03-10 20:50:30 +01:00
1615 changed files with 51646 additions and 109501 deletions

View File

@@ -7,23 +7,8 @@ indent_size = 4
trim_trailing_whitespace = true
charset = utf-8
max_line_length = 120
# ReSharper properties
resharper_csharp_max_line_length = 120
resharper_csharp_wrap_after_declaration_lpar = true
resharper_csharp_wrap_arguments_style = chop_if_long
resharper_csharp_wrap_parameters_style = chop_if_long
resharper_keep_existing_attribute_arrangement = true
resharper_place_field_attribute_on_same_line = if_owner_is_single_line
resharper_wrap_chained_binary_patterns = chop_if_long
resharper_wrap_chained_method_calls = chop_if_long
[*.{csproj,xml,yml,dll.config,targets,props}]
indent_size = 2
[nuget.config]
indent_size = 2
[*.gdsl]
indent_style = tab

3
.github/CODEOWNERS vendored
View File

@@ -7,6 +7,3 @@
**/Toolshed/** @moonheart08
*Command.cs @moonheart08
*Commands.cs @moonheart08
# Physics
**/Robust.Shared/Physics/** @metalgearsloth

View File

@@ -1,34 +0,0 @@
name: Build All Configurations
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
build:
strategy:
matrix:
targetOS: [Windows, Linux, MacOS]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.2.2
with:
submodules: true
- name: Setup .NET
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 9.0.x
- name: Install dependencies
run: dotnet restore
- name: Build Debug
run: dotnet build --no-restore --configuration Debug /p:WarningsAsErrors=nullable /p:TargetOS=${{ matrix.targetOS }}
- name: Build Tools
run: dotnet build --no-restore --configuration Tools /p:WarningsAsErrors=nullable /p:TargetOS=${{ matrix.targetOS }}
- name: Build Release
run: dotnet build --no-restore --configuration Release /p:WarningsAsErrors=nullable /p:TargetOS=${{ matrix.targetOS }}

View File

@@ -5,30 +5,30 @@ on:
- cron: "0 0 * * 0"
jobs:
docfx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.2.2
with:
submodules: true
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.6.0
with:
submodules: true
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 9.0.x
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0
with:
dotnet-version: 7.0.x
- name: Install dependencies
run: dotnet restore
- name: Install dependencies
run: dotnet restore
- name: Build Project
run: dotnet build --no-restore /p:WarningsAsErrors=nullable
- name: Build Project
run: dotnet build --no-restore /p:WarningsAsErrors=nullable
- name: Build DocFX
uses: nikeee/docfx-action@v1.0.0
with:
args: Robust.Docfx/docfx.json
- name: Build DocFX
uses: nikeee/docfx-action@v1.0.0
with:
args: Robust.Docfx/docfx.json
- name: Publish Docfx Documentation on GitHub Pages
uses: maxheld83/ghpages@master
env:
BUILD_DIR: Robust.Docfx/_robust-site
GH_PAT: ${{ secrets.GH_PAT }}
- name: Publish Docfx Documentation on GitHub Pages
uses: maxheld83/ghpages@master
env:
BUILD_DIR: Robust.Docfx/_robust-site
GH_PAT: ${{ secrets.GH_PAT }}

View File

@@ -2,9 +2,9 @@ name: Build & Test
on:
push:
branches: [master]
branches: [ master ]
pull_request:
branches: [master]
branches: [ master ]
jobs:
build:
@@ -15,19 +15,19 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4.2.2
with:
submodules: true
- uses: actions/checkout@v3.6.0
with:
submodules: true
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0
with:
dotnet-version: 7.0.x
- name: Install dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore /p:WarningsAsErrors=nullable
- name: Test Engine
run: dotnet test --no-build Robust.UnitTesting/Robust.UnitTesting.csproj -- NUnit.ConsoleOut=0
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 9.0.x
- name: Install dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore /p:WarningsAsErrors=nullable
- name: Robust.UnitTesting
run: dotnet test --no-build Robust.UnitTesting/Robust.UnitTesting.csproj -- NUnit.ConsoleOut=0
- name: Robust.Analyzers.Tests
run: dotnet test --no-build Robust.Analyzers.Tests/Robust.Analyzers.Tests.csproj -- NUnit.ConsoleOut=0

View File

@@ -11,8 +11,14 @@
#
name: "CodeQL"
on:
workflow_dispatch
#on:
# push:
# branches: [ master ]
# pull_request:
# # The branches below must be a subset of the branches above
# branches: [ master ]
# schedule:
# - cron: '30 18 * * 6'
jobs:
analyze:
@@ -22,50 +28,50 @@ jobs:
strategy:
fail-fast: false
matrix:
language: ["csharp"]
language: [ 'csharp' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v4.2.2
with:
submodules: true
- name: Checkout repository
uses: actions/checkout@v3.6.0
with:
submodules: true
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 7.0.x
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0
with:
dotnet-version: 7.0.x
- name: Build
run: dotnet build
- name: Build
run: dotnet build
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -3,50 +3,51 @@
on:
push:
tags:
- "v*"
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Parse version
id: parse_version
shell: pwsh
run: |
$ver = [regex]::Match($env:GITHUB_REF, "refs/tags/v?(.+)").Groups[1].Value
echo ("::set-output name=version::{0}" -f $ver)
- name: Parse version
id: parse_version
shell: pwsh
run: |
$ver = [regex]::Match($env:GITHUB_REF, "refs/tags/v?(.+)").Groups[1].Value
echo ("::set-output name=version::{0}" -f $ver)
- uses: actions/checkout@v4.2.2
with:
submodules: true
- uses: actions/checkout@v3.6.0
with:
submodules: true
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 9.0.x
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0
with:
dotnet-version: 7.0.x
- name: Package client
run: Tools/package_client_build.py
- name: Package client
run: Tools/package_client_build.py -p windows mac linux
- name: Shuffle files around
run: |
mkdir "release/${{ steps.parse_version.outputs.version }}"
mv release/*.zip "release/${{ steps.parse_version.outputs.version }}"
- name: Shuffle files around
run: |
mkdir "release/${{ steps.parse_version.outputs.version }}"
mv release/*.zip "release/${{ steps.parse_version.outputs.version }}"
- name: Upload files to Suns
uses: appleboy/scp-action@master
with:
host: suns.spacestation14.com
username: robust-build-push
key: ${{ secrets.CENTCOMM_ROBUST_BUILDS_PUSH_KEY }}
source: "release/${{ steps.parse_version.outputs.version }}"
target: "/var/lib/robust-builds/builds/"
strip_components: 1
- name: Upload files to centcomm
uses: appleboy/scp-action@master
with:
host: centcomm.spacestation14.io
username: robust-build-push
key: ${{ secrets.CENTCOMM_ROBUST_BUILDS_PUSH_KEY }}
source: "release/${{ steps.parse_version.outputs.version }}"
target: "/var/lib/robust-builds/builds/"
strip_components: 1
- name: Update manifest JSON
uses: appleboy/ssh-action@master
with:
host: centcomm.spacestation14.io
username: robust-build-push
key: ${{ secrets.CENTCOMM_ROBUST_BUILDS_PUSH_KEY }}
script: /home/robust-build-push/push.ps1 ${{ steps.parse_version.outputs.version }}
- name: Update manifest JSON
uses: appleboy/ssh-action@master
with:
host: suns.spacestation14.com
username: robust-build-push
key: ${{ secrets.CENTCOMM_ROBUST_BUILDS_PUSH_KEY }}
script: /home/robust-build-push/push.ps1 ${{ steps.parse_version.outputs.version }}

View File

@@ -2,39 +2,40 @@ name: Test content master against engine
on:
push:
branches: [master]
branches: [ master ]
pull_request:
branches: [master]
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out content
uses: actions/checkout@v4.2.2
with:
repository: space-wizards/space-station-14
submodules: recursive
- name: Check out content
uses: actions/checkout@v3.6.0
with:
repository: space-wizards/space-station-14
submodules: recursive
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 9.0.x
- name: Disable submodule autoupdate
run: touch BuildChecker/DISABLE_SUBMODULE_AUTOUPDATE
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0
with:
dotnet-version: 7.0.x
- name: Disable submodule autoupdate
run: touch BuildChecker/DISABLE_SUBMODULE_AUTOUPDATE
- name: Check out engine version
run: |
cd RobustToolbox
git fetch origin ${{ github.sha }}
git checkout FETCH_HEAD
git submodule update --init --recursive
- name: Install dependencies
run: dotnet restore
- name: Build
run: dotnet build --configuration Tools --no-restore
- name: Content.Tests
run: dotnet test --no-build Content.Tests/Content.Tests.csproj -v n
- name: Content.IntegrationTests
run: COMPlus_gcServer=1 dotnet test --no-build Content.IntegrationTests/Content.IntegrationTests.csproj -v n
- name: Check out engine version
run: |
cd RobustToolbox
git fetch origin ${{ github.sha }}
git checkout FETCH_HEAD
git submodule update --init --recursive
- name: Install dependencies
run: dotnet restore
- name: Build
run: dotnet build --configuration Tools --no-restore
- name: Content.Tests
run: dotnet test --no-build Content.Tests/Content.Tests.csproj -v n
- name: Content.IntegrationTests
run: COMPlus_gcServer=1 dotnet test --no-build Content.IntegrationTests/Content.IntegrationTests.csproj -v n

View File

@@ -1,83 +0,0 @@
<Project>
<PropertyGroup>
<!--
We actually set ManagePackageVersionsCentrally manually in another import file.
Since .NET SDK 8.0.300, ManagePackageVersionsCentrally is automatically set if Directory.Packages.props exists.
https://github.com/NuGet/NuGet.Client/pull/5572
We actively negate this here, as we have some packages in tree we don't want such automatic behavior for.
We use Directory.Build.props to get copy the state *after* our MSBuild config but before Nuget's config.
-->
<ManagePackageVersionsCentrally />
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
<PackageVersion Include="JetBrains.Annotations" Version="2024.3.0" />
<PackageVersion Include="JetBrains.Profiler.Api" Version="1.4.8" />
<PackageVersion Include="Linguini.Bundle" Version="0.8.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzer.Testing" Version="1.1.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" Version="1.1.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" Version="1.1.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.12.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.12.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.12.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.12.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.12.0" />
<PackageVersion Include="Microsoft.CodeCoverage" Version="17.12.0" />
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="9.0.0" />
<PackageVersion Include="Microsoft.DotNet.RemoteExecutor" Version="8.0.0-beta.24059.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.ObjectPool" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="9.0.0" />
<PackageVersion Include="Microsoft.ILVerification" Version="9.0.0" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageVersion Include="Microsoft.NET.ILLink.Tasks" Version="9.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageVersion Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="NUnit" Version="4.3.2" />
<PackageVersion Include="NUnit.Analyzers" Version="4.5.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageVersion Include="Nett" Version="0.15.0" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.2" />
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
<PackageVersion Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" />
<PackageVersion Include="Pidgin" Version="3.3.0" />
<PackageVersion Include="Robust.Natives" Version="0.2.3" />
<PackageVersion Include="Robust.Natives.Zstd" Version="0.1.1-zstd1.5.7" />
<PackageVersion Include="Robust.Natives.Cef" Version="131.3.5" />
<PackageVersion Include="Robust.Shared.AuthLib" Version="0.1.2" />
<PackageVersion Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
<PackageVersion Include="SQLitePCLRaw.provider.sqlite3" Version="2.1.10" />
<PackageVersion Include="Serilog" Version="4.2.0" />
<PackageVersion Include="Serilog.Sinks.Loki" Version="4.0.0-beta3" />
<PackageVersion Include="SharpZstd.Interop" Version="1.5.2-beta2" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.11" />
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.1" />
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.2.2" />
<PackageVersion Include="SpaceWizards.Sdl" Version="1.0.0" />
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.1.0" />
<PackageVersion Include="SpaceWizards.Sodium" Version="0.2.1" />
<PackageVersion Include="SpaceWizards.Fontconfig.Interop" Version="1.0.0" />
<PackageVersion Include="libsodium" Version="1.0.20.1" />
<PackageVersion Include="System.Management" Version="9.0.8" />
<PackageVersion Include="TerraFX.Interop.Windows" Version="10.0.26100.1" />
<PackageVersion Include="TerraFX.Interop.Xlib" Version="6.4.0" />
<PackageVersion Include="VorbisPizza" Version="1.3.0" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
<PackageVersion Include="prometheus-net" Version="8.2.1" />
<PackageVersion Include="prometheus-net.DotNetRuntime" Version="4.4.0" />
<PackageVersion Include="PolySharp" Version="1.15.0" />
<!-- Transitive deps that we need to pin versions for to avoid NuGet warnings. -->
<PackageVersion Include="System.Formats.Asn1" Version="9.0.0" />
<PackageVersion Include="System.Reflection.Metadata" Version="9.0.0" />
<PackageVersion Include="System.Text.Json" Version="9.0.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.12.0" />
</ItemGroup>
</Project>

View File

@@ -1,9 +0,0 @@
using System.Runtime.CompilerServices;
// So I wanted to mess with NetIncomingMessage and NetOutgoingMessage from tests.
// Now.. the instructors are internal...
// Unless...
// I mean we have this project here from the weird way we're compiling Lidgren.
// I could just put this in here... it wouldn't touch the main Lidgren repo at all...
[assembly: InternalsVisibleTo("Robust.UnitTesting")]

View File

@@ -10,14 +10,9 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<SkipRobustAnalyzer>true</SkipRobustAnalyzer>
<Nullable>enable</Nullable>
<LangVersion>13.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="CursedHorrorsBeyondOurWildestImagination.cs" />
<Compile Include="Lidgren.Network\Lidgren.Network\**\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>

View File

@@ -13,7 +13,7 @@
</When>
<Otherwise>
<PropertyGroup>
<DefineConstants>$(DefineConstants);LINUX;UNIX;FREEDESKTOP</DefineConstants>
<DefineConstants>$(DefineConstants);LINUX;UNIX</DefineConstants>
</PropertyGroup>
</Otherwise>
</Choose>

View File

@@ -1,4 +1,4 @@
<Project>
<!-- This file automatically reset by Tools/version.py -->
<!-- This file automatically reset by Tools/version.py -->

View File

@@ -1,8 +1,8 @@
<Project>
<!-- Engine-specific properties. Content should not use this file. -->
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>13</LangVersion>
<TargetFramework>net7.0</TargetFramework>
<LangVersion>11</LangVersion>
<Nullable>enable</Nullable>
<WarningsAsErrors>nullable</WarningsAsErrors>
</PropertyGroup>

View File

@@ -31,6 +31,5 @@
<Python>python3</Python>
<Python Condition="'$(ActualOS)' == 'Windows'">py -3</Python>
<UseSystemSqlite Condition="'$(TargetOS)' == 'FreeBSD'">True</UseSystemSqlite>
<IsFreedesktop Condition="'$(TargetOS)' == 'FreeBSD' Or '$(TargetOS)' == 'Linux'">True</IsFreedesktop>
</PropertyGroup>
</Project>

View File

@@ -3,8 +3,7 @@
<!-- Import this at the end of any project files in Robust and Content. -->
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>
<Import Project="Robust.Custom.targets" Condition="Exists('Robust.Custom.targets')"/>
@@ -28,5 +27,5 @@
<Import Project="Robust.Analyzers.targets" Condition="'$(SkipRobustAnalyzer)' != 'true'" />
<!-- serialization generator -->
<Import Project="Robust.Serialization.Generator.targets" Condition="'$(SkipRobustAnalyzer)' != 'true'" />
<Import Project="Robust.Serialization.Generator.targets" />
</Project>

View File

@@ -16,10 +16,7 @@
<ItemGroup>
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Client.NameGenerator\Robust.Client.NameGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\Robust.Client.Injectors.csproj" ReferenceOutputAssembly="false">
<SetConfiguration Condition="'$(Configuration)' == 'DebugOpt'">Configuration=Debug</SetConfiguration>
<SetConfiguration Condition="'$(Configuration)' == 'Tools'">Configuration=Release</SetConfiguration>
</ProjectReference>
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\Robust.Client.Injectors.csproj" ReferenceOutputAssembly="false"/>
</ItemGroup>
<!-- XamlIL does not make use of special Robust configurations like DebugOpt. Convert these down. -->
@@ -27,16 +24,12 @@
<RobustInjectorsConfiguration>$(Configuration)</RobustInjectorsConfiguration>
<RobustInjectorsConfiguration Condition="'$(Configuration)' == 'DebugOpt'">Debug</RobustInjectorsConfiguration>
<RobustInjectorsConfiguration Condition="'$(Configuration)' == 'Tools'">Release</RobustInjectorsConfiguration>
<RobustInjectorsConfiguration Condition="'$(UseArtifactsOutput)' == 'true' And '$(RuntimeIdentifier)' != ''">$(RobustInjectorsConfiguration)_$(RuntimeIdentifier)</RobustInjectorsConfiguration>
<RobustInjectorsConfiguration Condition="'$(UseArtifactsOutput)' == 'true'">$(RobustInjectorsConfiguration.ToLower())</RobustInjectorsConfiguration>
<CompileRobustXamlTaskAssemblyFile Condition="'$(UseArtifactsOutput)' != 'true'">$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\bin\$(RobustInjectorsConfiguration)\netstandard2.0\Robust.Client.Injectors.dll</CompileRobustXamlTaskAssemblyFile>
<CompileRobustXamlTaskAssemblyFile Condition="'$(UseArtifactsOutput)' == 'true'">$(MSBuildThisFileDirectory)\..\..\artifacts\bin\Robust.Client.Injectors\$(RobustInjectorsConfiguration)\Robust.Client.Injectors.dll</CompileRobustXamlTaskAssemblyFile>
</PropertyGroup>
<UsingTask
Condition="'$(_RobustUseExternalMSBuild)' != 'true' And $(DesignTimeBuild) != true"
TaskName="CompileRobustXamlTask"
AssemblyFile="$(CompileRobustXamlTaskAssemblyFile)"/>
AssemblyFile="$(MSBuildThisFileDirectory)\..\Robust.Client.Injectors\bin\$(RobustInjectorsConfiguration)\netstandard2.0\Robust.Client.Injectors.dll"/>
<Target
Name="CompileRobustXaml"
Condition="Exists('@(IntermediateAssembly)')"
@@ -74,6 +67,6 @@
</PropertyGroup>
<Exec
Condition="'$(_RobustUseExternalMSBuild)' == 'true'"
Command="&quot;$(DOTNET_HOST_PATH)&quot; msbuild /nodereuse:false $(MSBuildProjectFile) /t:CompileRobustXaml /p:_RobustForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:RuntimeIdentifier=$(RuntimeIdentifier) /p:TargetFramework=$(TargetFramework) /p:BuildProjectReferences=false /p:IntermediateOutputPath=&quot;$(IntermediateOutputPath.TrimEnd('\'))/&quot;"/>
Command="&quot;$(DOTNET_HOST_PATH)&quot; msbuild /nodereuse:false $(MSBuildProjectFile) /t:CompileRobustXaml /p:_RobustForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:RuntimeIdentifier=$(RuntimeIdentifier) /p:TargetFramework=$(TargetFramework) /p:BuildProjectReferences=false"/>
</Target>
</Project>

View File

@@ -61,5 +61,18 @@ namespace OpenToolkit.GraphicsLibraryFramework
: base(message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GLFWException"/> class with the specified context
/// and the serialization information.
/// </summary>
/// <param name="info">The <see cref="SerializationInfo"/> associated with this exception.</param>
/// <param name="context">
/// A <see cref="StreamingContext"/> that represents the context of this exception.
/// </param>
protected GLFWException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +0,0 @@
- type: entity
id: Audio
name: Audio
description: Audio entity used by engine
save: false
components:
- type: Transform
gridTraversal: false

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
- type: entity
id: debugRotation
abstract: true
categories: [ Debug ]
categories: [ debug ]
components:
- type: Sprite
netsync: false

View File

@@ -4,12 +4,6 @@
kind: canvas
light_mode: unshaded
# Simple mix blend
- type: shader
id: Mix
kind: canvas
blend_mode: Mix
- type: shader
id: shaded
kind: canvas
@@ -18,8 +12,3 @@
id: bgra
kind: source
path: "/Shaders/Internal/bgra.swsl"
- type: shader
id: ColorPicker
kind: source
path: "/Shaders/color_picker.swsl"

View File

@@ -1,26 +1,17 @@
# debug related entities
- type: entityCategory
id: Debug
id: debug
name: entity-category-name-debug
description: entity-category-desc-debug
suffix: entity-category-suffix-debug
# entities that spawn other entities
- type: entityCategory
id: Spawner
id: spawner
name: entity-category-name-spawner
description: entity-category-desc-spawner
# simple category that just exists to hide prototypes in spawn menus
# entities that should be hidden from the spawn menu
- type: entityCategory
id: HideSpawnMenu
id: hideSpawnMenu
name: entity-category-name-hide
description: entity-category-desc-hide
hideSpawnMenu: true
inheritable: false
# Entity prototypes added by the fork. With CVar you can hide all entities without this category
- type: entityCategory
id: ForkFiltered
name: entity-category-name-fork
description: entity-category-desc-fork

View File

@@ -20,26 +20,6 @@ zzzz-object-pronoun = { GENDER($ent) ->
*[neuter] it
}
# Used internally by the DAT-OBJ() function.
# Not used in en-US. Created to support other languages.
# (e.g., "to him," "for her")
zzzz-dat-object = { GENDER($ent) ->
[male] him
[female] her
[epicene] them
*[neuter] it
}
# Used internally by the GENITIVE() function.
# Not used in en-US. Created to support other languages.
# e.g., "у него" (Russian), "seines Vaters" (German).
zzzz-genitive = { GENDER($ent) ->
[male] his
[female] her
[epicene] their
*[neuter] its
}
# Used internally by the POSS-PRONOUN() function.
zzzz-possessive-pronoun = { GENDER($ent) ->
[male] his

View File

@@ -1,3 +0,0 @@
generic-map = map
generic-grid = grid
generic-mapid = map Id

View File

@@ -1,16 +1,16 @@
# Loc strings for various entity state & client-side PVS related commands
cmd-reset-ent-help = Usage: {$command} <Entity UID>
cmd-reset-ent-desc = Reset an entity to the most recently received server state. This will also reset entities that have been detached to null-space.
cmd-reset-ent-help = Usage: resetent <Entity UID>
cmd-reset-ent-desc = Reset an entity to the most recently received server state. This will also reset entities that have been detached to null-space.
cmd-reset-all-ents-help = Usage: {$command}
cmd-reset-all-ents-desc = Resets all entities to the most recently received server state. This only impacts entities that have not been detached to null-space.
cmd-reset-all-ents-help = Usage: resetallents
cmd-reset-all-ents-desc = Resets all entities to the most recently received server state. This only impacts entities that have not been detached to null-space.
cmd-detach-ent-help = Usage: {$command} <Entity UID>
cmd-detach-ent-help = Usage: detachent <Entity UID>
cmd-detach-ent-desc = Detach an entity to null-space, as if it had left PVS range.
cmd-local-delete-help = Usage: {$command} <Entity UID>
cmd-local-delete-help = Usage: localdelete <Entity UID>
cmd-local-delete-desc = Deletes an entity. Unlike the normal delete command, this is CLIENT-SIDE. Unless the entity is a client-side entity, this will likely cause errors.
cmd-full-state-reset-help = Usage: {$command}
cmd-full-state-reset-help = Usage: fullstatereset
cmd-full-state-reset-desc = Discards any entity state information and requests a full-state from the server.

View File

@@ -1,33 +0,0 @@
color-hue-chroma-lightness = {$lightness} {$chroma} {$hue}
color-hue-chroma = {$chroma} {$hue}
color-hue-lightness = {$lightness} {$hue}
color-very-dark = very dark
color-dark = dark
color-light = light
color-very-light = very light
color-mixed-hue = {$a} {$b}
color-pale = pale
color-gray-adjective = gray
color-strong = strong
color-pink = pink
color-red = red
color-orange = orange
color-yellow = yellow
color-green = green
color-cyan = cyan
color-blue = blue
color-purple = purple
color-brown = brown
color-white = white
color-gray = gray
color-black = black
color-unknown = unknown color, you should not see this
color-pink-color-red = pinkish red
color-red-color-orange = reddish orange
color-orange-color-yellow = orangeish yellow
color-yellow-color-green = yellowish green
color-green-color-cyan = greenish cyan
color-cyan-color-blue = cyanish blue
color-blue-color-purple = blueish purple
color-purple-color-pink = purpleish pink

View File

@@ -1,7 +1,5 @@
### Localization for engine console commands
cmd-hint-float = [float]
## generic command errors
cmd-invalid-arg-number-error = Invalid number of arguments.
@@ -11,11 +9,7 @@ cmd-parse-failure-float = {$arg} is not a valid float.
cmd-parse-failure-bool = {$arg} is not a valid bool.
cmd-parse-failure-uid = {$arg} is not a valid entity UID.
cmd-parse-failure-mapid = {$arg} is not a valid MapId.
cmd-parse-failure-enum = {$arg} is not a {$enum} Enum.
cmd-parse-failure-grid = {$arg} is not a valid grid.
cmd-parse-failure-cultureinfo = "{$arg}" is not valid CultureInfo.
cmd-parse-failure-entity-exist = UID {$arg} does not correspond to an existing entity.
cmd-parse-failure-session = There is no session with username: {$username}
cmd-error-file-not-found = Could not find file: {$file}.
cmd-error-dir-not-found = Could not find directory: {$dir}.
@@ -23,19 +17,19 @@ cmd-error-dir-not-found = Could not find directory: {$dir}.
cmd-failure-no-attached-entity = There is no entity attached to this shell.
## 'help' command
cmd-help-desc = Display general help or help text for a specific command.
cmd-help-help = Usage: {$command} [command name]
cmd-oldhelp-desc = Display general help or help text for a specific command
cmd-oldhelp-help = Usage: help [command name]
When no command name is provided, displays general-purpose help text. If a command name is provided, displays help text for that command.
cmd-help-no-args = To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'. To search for commands, use 'list <filter>'.
cmd-help-unknown = Unknown command: { $command }
cmd-help-top = { $command } - { $description }
cmd-help-invalid-args = Invalid amount of arguments.
cmd-help-arg-cmdname = [command name]
cmd-oldhelp-no-args = To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'. To search for commands, use 'list <filter>'.
cmd-oldhelp-unknown = Unknown command: { $command }
cmd-oldhelp-top = { $command } - { $description }
cmd-oldhelp-invalid-args = Invalid amount of arguments.
cmd-oldhelp-arg-cmdname = [command name]
## 'cvar' command
cmd-cvar-desc = Gets or sets a CVar.
cmd-cvar-help = Usage: {$command} <name | ?> [value]
cmd-cvar-help = Usage: cvar <name | ?> [value]
If a value is passed, the value is parsed and stored as the new value of the CVar.
If not, the current value of the CVar is displayed.
Use 'cvar ?' to get a list of all registered CVars.
@@ -47,16 +41,9 @@ cmd-cvar-compl-list = List available CVars
cmd-cvar-arg-name = <name | ?>
cmd-cvar-value-hidden = <value hidden>
## 'cvar_subs' command
cmd-cvar_subs-desc = Lists the OnValueChanged subscriptions for a CVar.
cmd-cvar_subs-help = Usage: {$command} <name>
cmd-cvar_subs-invalid-args = Must provide exactly one argument.
cmd-cvar_subs-arg-name = <name>
## 'list' command
cmd-list-desc = Lists available commands, with optional search filter.
cmd-list-help = Usage: {$command} [filter]
cmd-list-desc = Lists available commands, with optional search filter
cmd-list-help = Usage: list [filter]
Lists all available commands. If an argument is provided, it will be used to filter commands by name.
cmd-list-heading = SIDE NAME DESC{"\u000A"}-------------------------{"\u000A"}
@@ -64,13 +51,13 @@ cmd-list-heading = SIDE NAME DESC{"\u000A"}-------------------------{
cmd-list-arg-filter = [filter]
## '>' command, aka remote exec
cmd-remoteexec-desc = Executes server-side commands.
cmd-remoteexec-desc = Executes server-side commands
cmd-remoteexec-help = Usage: > <command> [arg] [arg] [arg...]
Executes a command on the server. This is necessary if a command with the same name exists on the client, as simply running the command would run the client command first.
## 'gc' command
cmd-gc-desc = Run the GC (Garbage Collector).
cmd-gc-help = Usage: {$command} [generation]
cmd-gc-desc = Run the GC (Garbage Collector)
cmd-gc-help = Usage: gc [generation]
Uses GC.Collect() to execute the Garbage Collector.
If an argument is provided, it is parsed as a GC generation number and GC.Collect(int) is used.
Use the 'gfc' command to do an LOH-compacting full GC.
@@ -79,13 +66,13 @@ cmd-gc-arg-generation = [generation]
## 'gcf' command
cmd-gcf-desc = Run the GC, fully, compacting LOH and everything.
cmd-gcf-help = Usage: {$command}
cmd-gcf-help = Usage: gcf
Does a full GC.Collect(2, GCCollectionMode.Forced, true, true) while also compacting LOH.
This will probably lock up for hundreds of milliseconds, be warned.
## 'gc_mode' command
cmd-gc_mode-desc = Change/Read the GC Latency mode.
cmd-gc_mode-help = Usage: {$command} [type]
cmd-gc_mode-desc = Change/Read the GC Latency mode
cmd-gc_mode-help = Usage: gc_mode [type]
If no argument is provided, returns the current GC latency mode.
If an argument is passed, it is parsed as GCLatencyMode and set as the GC latency mode.
@@ -98,8 +85,8 @@ cmd-gc_mode-result = resulting gc latency mode: { $mode }
cmd-gc_mode-arg-type = [type]
## 'mem' command
cmd-mem-desc = Prints managed memory info.
cmd-mem-help = Usage: {$command}
cmd-mem-desc = Prints managed memory info
cmd-mem-help = Usage: mem
cmd-mem-report = Heap Size: { TOSTRING($heapSize, "N0") }
Total Allocated: { TOSTRING($totalAllocated, "N0") }
@@ -108,26 +95,26 @@ cmd-mem-report = Heap Size: { TOSTRING($heapSize, "N0") }
cmd-physics-overlay = {$overlay} is not a recognised overlay
## 'lsasm' command
cmd-lsasm-desc = Lists loaded assemblies by load context.
cmd-lsasm-desc = Lists loaded assemblies by load context
cmd-lsasm-help = Usage: lsasm
## 'exec' command
cmd-exec-desc = Executes a script file from the game's writeable user data.
cmd-exec-help = Usage: {$command} <fileName>
cmd-exec-desc = Executes a script file from the game's writeable user data
cmd-exec-help = Usage: exec <fileName>
Each line in the file is executed as a single command, unless it starts with a #
cmd-exec-arg-filename = <fileName>
## 'dump_net_comps' command
cmd-dump_net_comps-desc = Prints the table of networked components.
cmd-dump_net_comps-help = Usage: {$command}
cmd-dump_net_comps-help = Usage: dump_net-comps
cmd-dump_net_comps-error-writeable = Registration still writeable, network ids have not been generated.
cmd-dump_net_comps-header = Networked Component Registrations:
## 'dump_event_tables' command
cmd-dump_event_tables-desc = Prints directed event tables for an entity.
cmd-dump_event_tables-help = Usage: {$command} <entityUid>
cmd-dump_event_tables-help = Usage: dump_event_tables <entityUid>
cmd-dump_event_tables-missing-arg-entity = Missing entity argument
cmd-dump_event_tables-error-entity = Invalid entity
@@ -135,7 +122,7 @@ cmd-dump_event_tables-arg-entity = <entityUid>
## 'monitor' command
cmd-monitor-desc = Toggles a debug monitor in the F3 menu.
cmd-monitor-help = Usage: {$command} <name>
cmd-monitor-help = Usage: monitor <name>
Possible monitors are: { $monitors }
You can also use the special values "-all" and "+all" to hide or show all monitors, respectively.
@@ -148,24 +135,23 @@ cmd-monitor-plus-all-hint = Shows all monitors
## 'setambientlight' command
cmd-set-ambient-light-desc = Allows you to set the ambient light for the specified map, in SRGB.
cmd-set-ambient-light-help = Usage: {$command} [mapid] [r g b a]
cmd-set-ambient-light-help = setambientlight [mapid] [r g b a]
cmd-set-ambient-light-parse = Unable to parse args as a byte values for a color.
## Mapping commands
cmd-savemap-desc = Serializes a map to disk. Will not save a post-init map unless forced.
cmd-savemap-help = Usage: {$command} <MapID> <Path> [force]
cmd-savemap-help = savemap <MapID> <Path> [force]
cmd-savemap-not-exist = Target map does not exist.
cmd-savemap-init-warning = Attempted to save a post-init map without forcing the save.
cmd-savemap-attempt = Attempting to save map {$mapId} to {$path}.
cmd-savemap-success = Map successfully saved.
cmd-savemap-error = Could not save map! See server log for details.
cmd-hint-savemap-id = <MapID>
cmd-hint-savemap-path = <Path>
cmd-hint-savemap-force = [bool]
cmd-loadmap-desc = Loads a map from disk into the game.
cmd-loadmap-help = Usage: {$command} <MapID> <Path> [x] [y] [rotation] [consistentUids]
cmd-loadmap-help = loadmap <MapID> <Path> [x] [y] [rotation] [consistentUids]
cmd-loadmap-nullspace = You cannot load into map 0.
cmd-loadmap-exists = Map {$mapId} already exists.
cmd-loadmap-success = Map {$mapId} has been loaded from {$path}.
@@ -180,74 +166,73 @@ cmd-hint-savebp-id = <Grid EntityID>
## 'flushcookies' command
# Note: the flushcookies command is from Robust.Client.WebView, it's not in the main engine code.
cmd-flushcookies-desc = Flush CEF cookie storage to disk.
cmd-flushcookies-help = Usage: {$command}
This ensure cookies are properly saved to disk in the event of unclean shutdowns.
cmd-flushcookies-desc = Flush CEF cookie storage to disk
cmd-flushcookies-help = This ensure cookies are properly saved to disk in the event of unclean shutdowns.
Note that the actual operation is asynchronous.
cmd-ldrsc-desc = Pre-caches a resource.
cmd-ldrsc-help = Usage: {$command} <path> <type>
cmd-ldrsc-help = Usage: ldrsc <path> <type>
cmd-rldrsc-desc = Reloads a resource.
cmd-rldrsc-help = Usage: {$command} <path> <type>
cmd-rldrsc-help = Usage: rldrsc <path> <type>
cmd-gridtc-desc = Gets the tile count of a grid.
cmd-gridtc-help = Usage: {$command} <gridId>
cmd-gridtc-help = Usage: gridtc <gridId>
# Client-side commands
cmd-guidump-desc = Dump GUI tree to /guidump.txt in user data.
cmd-guidump-help = Usage: {$command}
cmd-guidump-help = Usage: guidump
cmd-uitest-desc = Open a dummy UI testing window.
cmd-uitest-help = Usage: {$command}
cmd-uitest-desc = Open a dummy UI testing window
cmd-uitest-help = Usage: uitest
## 'uitest2' command
cmd-uitest2-desc = Opens a UI control testing OS window.
cmd-uitest2-help = Usage: {$command} <tab>
cmd-uitest2-desc = Opens a UI control testing OS window
cmd-uitest2-help = Usage: uitest2 <tab>
cmd-uitest2-arg-tab = <tab>
cmd-uitest2-error-args = Expected at most one argument
cmd-uitest2-error-tab = Invalid tab: '{$value}'
cmd-uitest2-title = UITest2
cmd-setclipboard-desc = Sets the system clipboard.
cmd-setclipboard-help = Usage: {$command} <text>
cmd-setclipboard-desc = Sets the system clipboard
cmd-setclipboard-help = Usage: setclipboard <text>
cmd-getclipboard-desc = Gets the system clipboard.
cmd-getclipboard-help = Usage: {$command}
cmd-getclipboard-desc = Gets the system clipboard
cmd-getclipboard-help = Usage: Getclipboard
cmd-togglelight-desc = Toggles light rendering.
cmd-togglelight-help = Usage: {$command}
cmd-togglelight-help = Usage: togglelight
cmd-togglefov-desc = Toggles fov for client.
cmd-togglefov-help = Usage: {$command}
cmd-togglefov-help = Usage: togglefov
cmd-togglehardfov-desc = Toggles hard fov for client. (for debugging space-station-14#2353)
cmd-togglehardfov-help = Usage: {$command}
cmd-togglehardfov-help = Usage: togglehardfov
cmd-toggleshadows-desc = Toggles shadow rendering.
cmd-toggleshadows-help = Usage: {$command}
cmd-toggleshadows-help = Usage: toggleshadows
cmd-togglelightbuf-desc = Toggles lighting rendering. This includes shadows but not FOV.
cmd-togglelightbuf-help = Usage: {$command}
cmd-togglelightbuf-help = Usage: togglelightbuf
cmd-chunkinfo-desc = Gets info about a chunk under your mouse cursor.
cmd-chunkinfo-help = Usage: {$command}
cmd-chunkinfo-help = Usage: chunkinfo
cmd-rldshader-desc = Reloads all shaders.
cmd-rldshader-help = Usage: {$command}
cmd-rldshader-help = Usage: rldshader
cmd-cldbglyr-desc = Toggle fov and light debug layers.
cmd-cldbglyr-help= Usage: {$command} <layer>: Toggle <layer>
cmd-cldbglyr-help= Usage: cldbglyr <layer>: Toggle <layer>
cldbglyr: Turn all Layers off
cmd-key-info-desc = Keys key info for a key.
cmd-key-info-help = Usage: {$command} <Key>
cmd-key-info-help = Usage: keyinfo <Key>
## 'bind' command
cmd-bind-desc = Binds an input key combination to an input command.
cmd-bind-help = Usage: {$command} { cmd-bind-arg-key } { cmd-bind-arg-mode } { cmd-bind-arg-command }
cmd-bind-help = Usage: bind { cmd-bind-arg-key } { cmd-bind-arg-mode } { cmd-bind-arg-command }
Note that this DOES NOT automatically save bindings.
Use the 'svbind' command to save binding configuration.
@@ -256,333 +241,323 @@ cmd-bind-arg-mode = <BindMode>
cmd-bind-arg-command = <InputCommand>
cmd-net-draw-interp-desc = Toggles the debug drawing of the network interpolation.
cmd-net-draw-interp-help = Usage: {$command}
cmd-net-draw-interp-help = Usage: net_draw_interp
cmd-net-draw-interp-desc = Toggles the debug drawing of the network interpolation.
cmd-net-draw-interp-help = Usage: net_draw_interp
cmd-net-watch-ent-desc = Dumps all network updates for an EntityId to the console.
cmd-net-watch-ent-help = Usage: {$command} <0|EntityUid>
cmd-net-watch-ent-help = Usage: net_watchent <0|EntityUid>
cmd-net-refresh-desc = Requests a full server state.
cmd-net-refresh-help = Usage: {$command}
cmd-net-refresh-help = Usage: net_refresh
cmd-net-entity-report-desc = Toggles the net entity report panel.
cmd-net-entity-report-help = Usage: {$command}
cmd-net-entity-report-help = Usage: net_entityreport
cmd-fill-desc = Fill up the console for debugging.
cmd-fill-help = Usage: {$command}
Fills the console with some nonsense for debugging.
cmd-fill-help = Fills the console with some nonsense for debugging.
cmd-cls-desc = Clears the console.
cmd-cls-help = Usage: {$command}
Clears the debug console of all messages.
cmd-cls-help = Clears the debug console of all messages.
cmd-sendgarbage-desc = Sends garbage to the server.
cmd-sendgarbage-help = Usage: {$command}
The server will reply with 'no u'
cmd-sendgarbage-help = The server will reply with 'no u'
cmd-loadgrid-desc = Loads a grid from a file into an existing map.
cmd-loadgrid-help = Usage: {$command} <MapID> <Path> [x y] [rotation] [storeUids]
cmd-loadgrid-help = loadgrid <MapID> <Path> [x y] [rotation] [storeUids]
cmd-loc-desc = Prints the absolute location of the player's entity to console.
cmd-loc-help = Usage: {$command}
cmd-loc-help = loc
cmd-tpgrid-desc = Teleports a grid to a new location.
cmd-tpgrid-help = Usage: {$command} <gridId> <X> <Y> [<MapId>]
cmd-tpgrid-help = tpgrid <gridId> <X> <Y> [<MapId>]
cmd-rmgrid-desc = Removes a grid from a map. You cannot remove the default grid.
cmd-rmgrid-help = Usage: {$command} <gridId>
cmd-rmgrid-help = rmgrid <gridId>
cmd-mapinit-desc = Runs map init on a map.
cmd-mapinit-help = Usage: {$command} <mapID>
cmd-mapinit-help = mapinit <mapID>
cmd-lsmap-desc = Lists maps.
cmd-lsmap-help = Usage: {$command}
cmd-lsmap-help = lsmap
cmd-lsgrid-desc = Lists grids.
cmd-lsgrid-help = Usage: {$command}
cmd-lsgrid-help = lsgrid
cmd-addmap-desc = Adds a new empty map to the round. If the mapID already exists, this command does nothing.
cmd-addmap-help = Usage: {$command} <mapID> [pre-init]
cmd-addmap-help = addmap <mapID> [initialize]
cmd-rmmap-desc = Removes a map from the world. You cannot remove nullspace.
cmd-rmmap-help = Usage: {$command} <mapId>
cmd-rmmap-help = rmmap <mapId>
cmd-savegrid-desc = Serializes a grid to disk.
cmd-savegrid-help = Usage: {$command} <gridID> <Path>
cmd-savegrid-help = savegrid <gridID> <Path>
cmd-testbed-desc = Loads a physics testbed on the specified map.
cmd-testbed-help = Usage: {$command} <mapid> <test>
cmd-testbed-help = testbed <mapid> <test>
cmd-saveconfig-desc = Saves the client configuration to the config file.
cmd-saveconfig-help = saveconfig
## 'flushcookies' command
# Note: the flushcookies command is from Robust.Client.WebView, it's not in the main engine code.
cmd-flushcookies-desc = Flush CEF cookie storage to disk
cmd-flushcookies-help = This ensure cookies are properly saved to disk in the event of unclean shutdowns.
Note that the actual operation is asynchronous.
## 'addcomp' command
cmd-addcomp-desc = Adds a component to an entity.
cmd-addcomp-help = Usage: {$command} <uid> <componentName>
cmd-addcomp-help = addcomp <uid> <componentName>
cmd-addcompc-desc = Adds a component to an entity on the client.
cmd-addcompc-help = Usage: {$command} <uid> <componentName>
cmd-addcompc-help = addcompc <uid> <componentName>
## 'rmcomp' command
cmd-rmcomp-desc = Removes a component from an entity.
cmd-rmcomp-help = Usage: {$command} <uid> <componentName>
cmd-rmcomp-help = rmcomp <uid> <componentName>
cmd-rmcompc-desc = Removes a component from an entity on the client.
cmd-rmcompc-help = Usage: {$command} <uid> <componentName>
cmd-rmcompc-help = rmcomp <uid> <componentName>
## 'addview' command
cmd-addview-desc = Allows you to subscribe to an entity's view for debugging purposes.
cmd-addview-help = Usage: {$command} <entityUid>
cmd-addview-help = addview <entityUid>
cmd-addviewc-desc = Allows you to subscribe to an entity's view for debugging purposes.
cmd-addviewc-help = Usage: {$command} <entityUid>
cmd-addviewc-help = addview <entityUid>
## 'removeview' command
cmd-removeview-desc = Allows you to unsubscribe to an entity's view for debugging purposes.
cmd-removeview-help = Usage: {$command} <entityUid>
cmd-removeview-help = removeview <entityUid>
## 'loglevel' command
cmd-loglevel-desc = Changes the log level for a provided sawmill.
cmd-loglevel-help = Usage: {$command} <sawmill> <level>
cmd-loglevel-help = Usage: loglevel <sawmill> <level>
sawmill: A label prefixing log messages. This is the one you're setting the level for.
level: The log level. Must match one of the values of the LogLevel enum.
cmd-testlog-desc = Writes a test log to a sawmill.
cmd-testlog-help = Usage: {$command} <sawmill> <level> <message>
cmd-testlog-help = Usage: testlog <sawmill> <level> <message>
sawmill: A label prefixing the logged message.
level: The log level. Must match one of the values of the LogLevel enum.
message: The message to be logged. Wrap this in double quotes if you want to use spaces.
## 'vv' command
cmd-vv-desc = Opens View Variables.
cmd-vv-help = Usage: {$command} <entity ID|IoC interface name|SIoC interface name>
cmd-vv-help = Usage: vv <entity ID|IoC interface name|SIoC interface name>
## 'showvelocities' command
cmd-showvelocities-desc = Displays your angular and linear velocities.
cmd-showvelocities-help = Usage: {$command}
cmd-showvelocities-help = Usage: showvelocities
## 'setinputcontext' command
cmd-setinputcontext-desc = Sets the active input context.
cmd-setinputcontext-help = Usage: {$command} <context>
cmd-setinputcontext-help = Usage: setinputcontext <context>
## 'forall' command
cmd-forall-desc = Runs a command over all entities with a given component.
cmd-forall-help = Usage: {$command} <bql query> do <command...>
cmd-forall-help = Usage: forall <bql query> do <command...>
## 'delete' command
cmd-delete-desc = Deletes the entity with the specified ID.
cmd-delete-help = Usage: {$command} <entity UID>
cmd-delete-help = delete <entity UID>
# System commands
cmd-showtime-desc = Shows the server time.
cmd-showtime-help = Usage: {$command}
cmd-showtime-help = showtime
cmd-restart-desc = Gracefully restarts the server (not just the round).
cmd-restart-help = Usage: {$command}
cmd-restart-help = restart
cmd-shutdown-desc = Gracefully shuts down the server.
cmd-shutdown-help = Usage: {$command}
cmd-shutdown-help = shutdown
cmd-saveconfig-desc = Saves the server configuration to the config file.
cmd-saveconfig-help = Usage: {$command}
cmd-saveconfig-help = saveconfig
cmd-netaudit-desc = Prints into about NetMsg security.
cmd-netaudit-help = Usage: {$command}
cmd-netaudit-help = netaudit
# Player commands
cmd-tp-desc = Teleports a player to any location in the round.
cmd-tp-help = Usage: {$command} <x> <y> [<mapID>]
cmd-tp-help = tp <x> <y> [<mapID>]
cmd-tpto-desc = Teleports the current player or the specified players/entities to the location of the first player/entity.
cmd-tpto-help = Usage: {$command} <username|uid> [username|NetEntity]...
cmd-tpto-destination-hint = destination (NetEntity or username)
cmd-tpto-victim-hint = entity to teleport (NetEntity or username)
cmd-tpto-help = tpto <username|uid> [username|uid]...
cmd-tpto-destination-hint = destination (uid or username)
cmd-tpto-victim-hint = entity to teleport (uid or username)
cmd-tpto-parse-error = Cant resolve entity or player: {$str}
cmd-listplayers-desc = Lists all players currently connected.
cmd-listplayers-help = Usage: {$command}
cmd-listplayers-help = listplayers
cmd-kick-desc = Kicks a connected player out of the server, disconnecting them.
cmd-kick-help = Usage: {$command} <PlayerIndex> [<Reason>]
cmd-kick-help = kick <PlayerIndex> [<Reason>]
# Spin command
cmd-spin-desc = Causes an entity to spin. Default entity is the attached player's parent.
cmd-spin-help = Usage: {$command} velocity [drag] [entityUid]
cmd-spin-help = spin velocity [drag] [entityUid]
# Localization command
cmd-rldloc-desc = Reloads localization (client & server).
cmd-rldloc-help = Usage: {$command}
cmd-rldloc-help = Usage: rldloc
# Debug entity controls
cmd-spawn-desc = Spawns an entity with specific type.
cmd-spawn-help = Usage: {$command} <prototype> | {$command} <prototype> <relative entity ID> | {$command} <prototype> <x> <y>
cmd-spawn-help = spawn <prototype> OR spawn <prototype> <relative entity ID> OR spawn <prototype> <x> <y>
cmd-cspawn-desc = Spawns a client-side entity with specific type at your feet.
cmd-cspawn-help = Usage: {$command} <entity type>
cmd-cspawn-help = cspawn <entity type>
cmd-scale-desc = Increases or decreases an entity's size naively.
cmd-scale-help = scale <entityUid> <float>
cmd-dumpentities-desc = Dump entity list.
cmd-dumpentities-help = Usage: {$command}
Dumps entity list of UIDs and prototype.
cmd-dumpentities-help = Dumps entity list of UIDs and prototype.
cmd-getcomponentregistration-desc = Gets component registration information.
cmd-getcomponentregistration-help = Usage: {$command} <componentName>
cmd-getcomponentregistration-help = Usage: getcomponentregistration <componentName>
cmd-showrays-desc = Toggles debug drawing of physics rays. An integer for <raylifetime> must be provided.
cmd-showrays-help = Usage: {$command} <raylifetime>
cmd-showrays-help = Usage: showrays <raylifetime>
cmd-disconnect-desc = Immediately disconnect from the server and go back to the main menu.
cmd-disconnect-help = Usage: {$command}
cmd-disconnect-help = Usage: disconnect
cmd-entfo-desc = Displays verbose diagnostics for an entity.
cmd-entfo-help = Usage: {$command} <entityuid>
cmd-entfo-help = Usage: entfo <entityuid>
The entity UID can be prefixed with 'c' to convert it to a client entity UID.
cmd-fuck-desc = Throws an exception.
cmd-fuck-help = Usage: {$command}
cmd-fuck-desc = Throws an exception
cmd-fuck-help = Throws an exception
cmd-showpos-desc = Show the position of all entities on the screen.
cmd-showpos-help = Usage: {$command}
cmd-showrot-desc = Show the rotation of all entities on the screen.
cmd-showrot-help = Usage: {$command}
cmd-showvel-desc = Show the local velocity of all entites on the screen.
cmd-showvel-help = Usage: {$command}
cmd-showangvel-desc = Show the angular velocity of all entities on the screen.
cmd-showangvel-help = Usage: {$command}
cmd-showpos-desc = Enables debug drawing over all entity positions in the game.
cmd-showpos-help = Usage: showpos
cmd-sggcell-desc = Lists entities on a snap grid cell.
cmd-sggcell-help = Usage: {$command} <gridID> <vector2i>\nThat vector2i param is in the form x<int>,y<int>.
cmd-sggcell-help = Usage: sggcell <gridID> <vector2i>\nThat vector2i param is in the form x<int>,y<int>.
cmd-overrideplayername-desc = Changes the name used when attempting to connect to the server.
cmd-overrideplayername-help = Usage: {$command} <name>
cmd-overrideplayername-help = Usage: overrideplayername <name>
cmd-showanchored-desc = Shows anchored entities on a particular tile.
cmd-showanchored-help = Usage: {$command}
cmd-showanchored-desc = Shows anchored entities on a particular tile
cmd-showanchored-help = Usage: showanchored
cmd-dmetamem-desc = Dumps a type's members in a format suitable for the sandbox configuration file.
cmd-dmetamem-help = Usage: {$command} <type>
cmd-dmetamem-help = Usage: dmetamem <type>
cmd-dmetamem-desc = Displays chunk bounds for the purposes of rendering.
cmd-dmetamem-help = Usage: showchunkbb <type>
cmd-launchauth-desc = Load authentication tokens from launcher data to aid in testing of live servers.
cmd-launchauth-help = Usage: {$command} <account name>
cmd-launchauth-help = Usage: launchauth <account name>
cmd-lightbb-desc = Toggles whether to show light bounding boxes.
cmd-lightbb-help = Usage: {$command}
cmd-lightbb-help = Usage: lightbb
cmd-monitorinfo-desc = Monitors info.
cmd-monitorinfo-help = Usage: {$command} <id>
cmd-monitorinfo-desc = Monitors info
cmd-monitorinfo-help = Usage: monitorinfo <id>
cmd-setmonitor-desc = Set monitor.
cmd-setmonitor-help = Usage: {$command} <id>
cmd-setmonitor-desc = Set monitor
cmd-setmonitor-help = Usage: setmonitor <id>
cmd-physics-desc = Shows a debug physics overlay. The arg supplied specifies the overlay.
cmd-physics-help = Usage: {$command} <aabbs / com / contactnormals / contactpoints / distance / joints / shapeinfo / shapes>
cmd-physics-help = Usage: physics <aabbs / com / contactnormals / contactpoints / distance / joints / shapeinfo / shapes>
cmd-hardquit-desc = Kills the game client instantly.
cmd-hardquit-help = Usage: {$command}
Kills the game client instantly, leaving no traces. No telling the server goodbye.
cmd-hardquit-help = Kills the game client instantly, leaving no traces. No telling the server goodbye.
cmd-quit-desc = Shuts down the game client gracefully.
cmd-quit-help = Usage: {$command}
Properly shuts down the game client, notifying the connected server and such.
cmd-quit-help = Properly shuts down the game client, notifying the connected server and such.
cmd-csi-desc = Opens a C# interactive console.
cmd-csi-help = Usage: {$command}
cmd-csi-help = Usage: csi
cmd-scsi-desc = Opens a C# interactive console on the server.
cmd-scsi-help = Usage: {$command}
cmd-scsi-help = Usage: scsi
cmd-watch-desc = Opens a variable watch window.
cmd-watch-help = Usage: {$command}
cmd-watch-help = Usage: watch
cmd-showspritebb-desc = Toggle whether sprite bounds are shown.
cmd-showspritebb-help = Usage: {$command}
cmd-showspritebb-desc = Toggle whether sprite bounds are shown
cmd-showspritebb-help = Usage: showspritebb
cmd-togglelookup-desc = Shows / hides entitylookup bounds via an overlay.
cmd-togglelookup-help = Usage: {$command}
cmd-togglelookup-help = Usage: togglelookup
cmd-net_entityreport-desc = Toggles the net entity report panel.
cmd-net_entityreport-help = Usage: {$command}
cmd-net_entityreport-help = Usage: net_entityreport
cmd-net_refresh-desc = Requests a full server state.
cmd-net_refresh-help = Usage: {$command}
cmd-net_refresh-help = Usage: net_refresh
cmd-net_graph-desc = Toggles the net statistics panel.
cmd-net_graph-help = Usage: {$command}
cmd-net_graph-desc = Toggles the net statistics pannel.
cmd-net_graph-help = Usage: net_graph
cmd-net_watchent-desc = Dumps all network updates for an EntityId to the console.
cmd-net_watchent-help = Usage: {$command} <0|EntityUid>
cmd-net_watchent-help = Usage: net_watchent <0|EntityUid>
cmd-net_draw_interp-desc = Toggles the debug drawing of the network interpolation.
cmd-net_draw_interp-help = Usage: {$command} <0|EntityUid>
cmd-net_draw_interp-help = Usage: net_draw_interp <0|EntityUid>
cmd-vram-desc = Displays video memory usage statics by the game.
cmd-vram-help = Usage: {$command}
cmd-vram-help = Usage: vram
cmd-showislands-desc = Shows the current physics bodies involved in each physics island.
cmd-showislands-help = Usage: {$command}
cmd-showislands-help = Usage: showislands
cmd-showgridnodes-desc = Shows the nodes for grid split purposes.
cmd-showgridnodes-help = Usage: {$command}
cmd-showgridnodes-help = Usage: showgridnodes
cmd-profsnap-desc = Make a profiling snapshot.
cmd-profsnap-help = Usage: {$command}
cmd-profsnap-help = Usage: profsnap
cmd-devwindow-desc = Dev Window.
cmd-devwindow-help = Usage: {$command}
cmd-devwindow-desc = Dev Window
cmd-devwindow-help = Usage: devwindow
cmd-devwindow-desc = Open file
cmd-devwindow-help = Usage: testopenfile
cmd-scene-desc = Immediately changes the UI scene/state.
cmd-scene-help = Usage: {$command} <className>
cmd-scene-help = Usage: scene <className>
cmd-szr_stats-desc = Report serializer statistics.
cmd-szr_stats-help = Usage: {$command}
cmd-szr_stats-help = Usage: szr_stats
cmd-hwid-desc = Returns the current HWID (HardWare ID).
cmd-hwid-help = Usage: {$command}
cmd-hwid-help = Usage: hwid
cmd-vvread-desc = Retrieve a path's value using VV (View Variables).
cmd-vvread-help = Usage: {$command} <path>
cmd-vvread-desc = Usage: vvread <path>
cmd-vvwrite-desc = Modify a path's value using VV (View Variables).
cmd-vvwrite-help = Usage: {$command} <path>
cmd-vvwrite-help = Usage: vvwrite <path>
cmd-vv-desc = Opens View Variables (VV).
cmd-vv-help = Usage: vv <path|entity ID|guihover>
cmd-vvinvoke-desc = Invoke/Call a path with arguments using VV.
cmd-vvinvoke-help = Usage: {$command} <path> [arguments...]
cmd-vvinvoke-help = Usage: vvinvoke <path> [arguments...]
cmd-dump_dependency_injectors-desc = Dump IoCManager's dependency injector cache.
cmd-dump_dependency_injectors-help = Usage: {$command}
cmd-dump_dependency_injectors-help = Usage: dump_dependency_injectors
cmd-dump_dependency_injectors-total-count = Total count: { $total }
cmd-dump_netserializer_type_map-desc = Dump NetSerializer's type map and serializer hash.
cmd-dump_netserializer_type_map-help = Usage: {$command}
cmd-dump_netserializer_type_map-help = Usage: dump_netserializer_type_map
cmd-hub_advertise_now-desc = Immediately advertise to the master hub server.
cmd-hub_advertise_now-help = Usage: {$command}
cmd-hub_advertise_now-desc = Immediately advertise to the master hub server
cmd-hub_advertise_now-help = Usage: hub_advertise_now
cmd-echo-desc = Echo arguments back to the console.
cmd-echo-help = Usage: {$command} "<message>"
cmd-echo-desc = Echo arguments back to the console
cmd-echo-help = Usage: echo "<message>"
## 'vfs_ls' command
cmd-vfs_ls-desc = List directory contents in the VFS.
cmd-vfs_ls-help = Usage: {$command} <path>
cmd-vfs_ls-help = Usage: vfs_list <path>
Example:
vfs_list /Assemblies
cmd-vfs_ls-err-args = Need exactly 1 argument.
cmd-vfs_ls-hint-path = <path>
cmd-reloadtiletextures-desc = Reloads the tile texture atlas to allow hot reloading tile sprites.
cmd-reloadtiletextures-help = Usage: {$command}
cmd-audio_length-desc = Shows the length of an audio file
cmd-audio_length-help = Usage: {$command} { cmd-audio_length-arg-file-name }
cmd-audio_length-arg-file-name = <file name>
## PVS
cmd-pvs-override-info-desc = Prints information about any PVS overrides associated with an entity.
cmd-pvs-override-info-empty = Entity {$nuid} has no PVS overrides.
cmd-pvs-override-info-global = Entity {$nuid} has a global override.
cmd-pvs-override-info-clients = Entity {$nuid} has a session override for {$clients}.
cmd-localization_set_culture-desc = Set DefaultCulture for the client LocalizationManager.
cmd-localization_set_culture-help = Usage: {$command} <cultureName>
cmd-localization_set_culture-culture-name = <cultureName>
cmd-localization_set_culture-changed = Localization changed to { $code } ({ $nativeName } / { $englishName })
cmd-addmap-hint-2 = runMapInit [true / false]
cmd-reloadtiletextures-desc = Reloads the tile texture atlas to allow hot reloading tile sprites
cmd-reloadtiletextures-help = Usage: reloadtiletextures

View File

@@ -8,5 +8,3 @@ color-selector-sliders-alpha = A
color-selector-sliders-rgb = RGB
color-selector-sliders-hsv = HSV
option-button-filter = Filter

View File

@@ -1,24 +1,12 @@
## EntitySpawnWindow
entity-spawn-window-title = Entity Spawn Panel
entity-spawn-window-search-bar-placeholder = search
entity-spawn-window-clear-button = Clear
entity-spawn-window-replace-button-text = Replace
entity-spawn-window-erase-button-text = Erase Mode
entity-spawn-window-override-menu-tooltip = Override placement
## TileSpawnWindow
tile-spawn-window-title = Place Tiles
tile-spawn-window-mirror-button-text = Mirror Tiles
## Console
console-line-edit-placeholder = Command Here
## OutputPanel
output-panel-scroll-down-button-text = Scroll Down
## Common Used
window-erase-button-text = Erase Mode
window-search-bar-placeholder = Search
window-clear-button = Clear

View File

@@ -1,25 +0,0 @@
## "Textures" dev window tab
dev-window-tab-textures-title = Textures
dev-window-tab-textures-reload = Reload
dev-window-tab-textures-filter = Filter
dev-window-tab-textures-summary = Total (est): { $bytes }
dev-window-tab-textures-info = Width: { $width } Height: { $height }
PixelType: { $pixelType } sRGB: { $srgb }
Name: { $name }
Est. memory usage: { $bytes }
## "Render Targets" dev window tab
dev-window-tab-render-targets-title = Render Targets
dev-window-tab-render-targets-reload = Reload
dev-window-tab-render-targets-filter = Filter
dev-window-tab-render-targets-column-id = ID
dev-window-tab-render-targets-column-name = Name
dev-window-tab-render-targets-column-size = Size
dev-window-tab-render-targets-column-type = Type
dev-window-tab-render-targets-column-vram = VRAM
dev-window-tab-render-targets-column-thumbnail = Thumbnail
dev-window-tab-render-targets-value-null = null
dev-window-tab-render-targets-value-not-available = Not available
dev-window-tab-render-targets-summary = Total VRAM: { $vram }

View File

@@ -1,12 +1,8 @@
entity-category-name-debug = Debug
entity-category-desc-debug = Entity prototypes intended for debugging & testing.
entity-category-suffix-debug = Debug
entity-category-name-spawner = Spawner
entity-category-desc-spawner = Entity prototypes that spawn other entities.
entity-category-name-hide = Hidden
entity-category-desc-hide = Entity prototypes that should be hidden from entity spawn menus
entity-category-name-fork = Fork Filtered
entity-category-desc-fork = Entity prototypes added by the fork. With CVar you can hide all entities without this category
entity-category-desc-hide = Entity prototypes that should be hidden from the spawn menu

View File

@@ -2,7 +2,6 @@ input-key-Escape = Escape
input-key-Control = Control
input-key-Shift = Shift
input-key-Alt = Alt
input-key-Alt-mac = ⌥
input-key-Menu = Menu
input-key-F1 = F1
input-key-F2 = F2
@@ -71,8 +70,8 @@ input-key-MouseButton9 = Mouse 9
input-key-LSystem-win = Left Win
input-key-RSystem-win = Right Win
input-key-LSystem-mac = Left
input-key-RSystem-mac = Right
input-key-LSystem-mac = Left Cmd
input-key-RSystem-mac = Right Cmd
input-key-LSystem-linux = Left Meta
input-key-RSystem-linux = Right Meta

View File

@@ -1,8 +0,0 @@
cmd-merge_grids-desc = Combines 2 grids into 1 grid
cmd-merge_grids-help = merge_grids <gridUid1> <gridUid2> <offsetX> <offsetY> [angle]
cmd-merge_grids-hintA = Grid A
cmd-merge_grids-hintB = Grid B
cmd-merge_grids-xOffset = X offset
cmd-merge_grids-yOffset = Y offset
cmd-merge_grids-angle = [Angle]

View File

@@ -1,8 +1,4 @@
command-help-usage =
Usage:
command-help-invertible =
The behaviour of this command can be inverted using the "not" prefix.
command-description-tpto =
command-description-tpto =
Teleport the given entities to some target entity.
command-description-player-list =
Returns a list of all player sessions.
@@ -23,7 +19,7 @@ command-description-buildinfo =
command-description-cmd-list =
Returns a list of all commands, for this side.
command-description-explain =
Explains the given expression, providing command descriptions and signatures. This only works for valid expressions, it can't explain commands that it fails to parse.
Explains the given expression, providing command descriptions and signatures.
command-description-search =
Searches through the input for the provided value.
command-description-stopwatch =
@@ -42,7 +38,8 @@ command-description-as =
command-description-count =
Counts the amount of entries in it's input, returning an integer.
command-description-map =
Maps the input over the given block.
Maps the input over the given block, with the provided expected return type.
This command may be modified to not need an explicit return type in the future.
command-description-select =
Selects N objects or N% of objects from the input.
One can additionally invert this command with not to make it select everything except N objects instead.
@@ -56,8 +53,10 @@ command-description-entities =
Returns all entities on the server.
command-description-paused =
Filters the input entities by whether or not they are paused.
This command can be inverted with not.
command-description-with =
Filters the input entities by whether or not they have the given component.
This command can be inverted with not.
command-description-fuck =
Throws an exception.
command-description-ecscomp-listty =
@@ -71,9 +70,9 @@ command-description-ls-in =
command-description-methods-get =
Returns all methods associated with the input type.
command-description-methods-overrides =
Returns all methods overridden on the input type.
Returns all methods overriden on the input type.
command-description-methods-overridesfrom =
Returns all methods overridden from the given type on the input type.
Returns all methods overriden from the given type on the input type.
command-description-cmd-moo =
Asks the important questions.
command-description-cmd-descloc =
@@ -96,8 +95,6 @@ command-description-vars =
Provides a list of all variables set in this session.
command-description-any =
Returns true if there's any values in the input, otherwise false.
command-description-contains =
Returns whether the input enumerable contains the specified value.
command-description-ArrowCommand =
Assigns the input to a variable.
command-description-isempty =
@@ -122,8 +119,6 @@ command-description-splat =
"Splats" a block, value, or variable, creating N copies of it in a list.
command-description-val =
Casts the given value, block, or variable to the given type. This is mostly a workaround for current limitations of variables.
command-description-var =
Returns the contents of the given variable. This will attempt to automatically infer a variables type. Compound commands that modify a variable may need to use the 'val' command instead.
command-description-actor-controlled =
Filters entities by whether or not they're actively controlled.
command-description-actor-session =
@@ -148,7 +143,7 @@ command-description-max =
Returns the maximum of two values.
command-description-BitAndCommand =
Performs bitwise AND.
command-description-bitor =
command-description-BitOrCommand =
Performs bitwise OR.
command-description-BitXorCommand =
Performs bitwise XOR.
@@ -195,8 +190,6 @@ command-description-spawn-at =
Spawns an entity at the given coordinates.
command-description-spawn-on =
Spawns an entity on the given entity, at it's coordinates.
command-description-spawn-in =
Spawns an entity in the given container on the given entity, dropping it at its coordinates if it doesn't fit
command-description-spawn-attached =
Spawns an entity attached to the given entity, at (0 0) relative to it.
command-description-mappos =
@@ -204,11 +197,11 @@ command-description-mappos =
command-description-pos =
Returns an entity's coordinates.
command-description-tp-coords =
Teleports the given entities to the target coordinates.
Teleports the target to the given coordinates.
command-description-tp-to =
Teleports the given entities to the target entity.
Teleports the target to the given other entity.
command-description-tp-into =
Teleports the given entities "into" the target entity, attaching it at (0 0) relative to it.
Teleports the target "into" the given other entity, attaching it at (0 0) relative to it.
command-description-comp-get =
Gets the given component from the given entity.
command-description-comp-add =
@@ -226,9 +219,9 @@ command-description-MulVecCommand =
command-description-DivVecCommand =
Divides every element in the input by a scalar (single value).
command-description-rng-to =
Returns a number between the input (inclusive) and the argument (exclusive).
Returns a number from its input to its argument (i.e. n..m inclusive)
command-description-rng-from =
Returns a number between the argument (inclusive) and the input (exclusive))
Returns a number to its input from its argument (i.e. m..n inclusive)
command-description-rng-prob =
Returns a boolean based on the input probability/chance (from 0 to 1)
command-description-sum =
@@ -278,7 +271,7 @@ command-description-ModVecCommand =
Performs the modulus operation over the input with the given constant right-hand value.
command-description-BitAndNotCommand =
Performs bitwise AND-NOT over the input.
command-description-bitornot =
command-description-BitOrNotCommand =
Performs bitwise OR-NOT over the input.
command-description-BitXnorCommand =
Performs bitwise XNOR over the input.
@@ -425,10 +418,6 @@ command-description-tee =
This essentially lets you have a branch in your code to do multiple operations on one value.
command-description-cmd-info =
Returns a CommandSpec for the given command.
On its own, this means it'll print the command's help message.
On it's own, this means it'll print the comamnd's help message.
command-description-comp-rm =
Removes the given component from the entity.
command-description-overlay-toggle = Toggle an overlay on or off
command-description-overlay-add = Add an overlay (if it does not already exist)
command-description-overlay-remove = Remove an overlay

View File

@@ -1,2 +0,0 @@
popup-copy-button = Copy
popup-title = Alert!

View File

@@ -10,24 +10,3 @@ view-variable-instance-entity-client-components-search-bar-placeholder = Search
view-variable-instance-entity-server-components-search-bar-placeholder = Search
view-variable-instance-entity-add-window-server-components = Add Component [S]
view-variable-instance-entity-add-window-client-components = Add Component [C]
## SoundSpecifier
vv-sound-none = None
vv-sound-path = Path
vv-sound-collection = Collection
vv-sound-volume = volume
vv-sound-pitch = Pitch
vv-sound-max-distance = Max Distance
vv-sound-rolloff-factor = Rolloff Factor
vv-sound-reference-distance = Reference Distance
vv-sound-loop = Loop
vv-sound-play-offset = Play Offset (s)
vv-sound-variation = Pitch variation
## ProtoId
vv-protoid-id-placeholder = Prototype ID
vv-protoid-select-button-label = Select
vv-protoid-addwindow-title = Set Prototype

View File

@@ -136,7 +136,6 @@ cmd-savemap-not-exist = O mapa de destino não existe.
cmd-savemap-init-warning = Tentativa de salvar um mapa pós-inicialização sem forçar o salvamento.
cmd-savemap-attempt = Tentando salvar o mapa {$mapId} em {$path}.
cmd-savemap-success = Mapa salvo com sucesso.
cmd-savemap-error = Não foi possível salvar o mapa! Consulte o log do servidor para obter detalhes.
cmd-hint-savemap-id = <MapID>
cmd-hint-savemap-path = <Path>
cmd-hint-savemap-force = [bool]

View File

@@ -14,8 +14,6 @@ uniform highp vec2 lightCenter;
uniform highp float lightRange;
uniform highp float lightPower;
uniform highp float lightSoftness;
uniform highp float lightFalloff;
uniform highp float lightCurveFactor;
uniform highp float lightIndex;
uniform sampler2D shadowMap;
@@ -49,15 +47,8 @@ void fragment()
discard;
}
// this implementation of light attenuation primarily adapted from
// https://lisyarus.github.io/blog/posts/point-light-attenuation.html
highp float sqr_dist = dot(diff, diff) + LIGHTING_HEIGHT;
highp float s = clamp(sqrt(sqr_dist) / lightRange, 0.0, 1.0);
highp float s2 = s * s;
// controls curve by lerping between two variants (inverse-shape and inversequadratic-shape)
highp float curveFactor = mix(s, s2, clamp(lightCurveFactor, 0.0, 1.0));
highp float val = clamp(((1.0 - s2) * (1.0 - s2)) / (1.0 + lightFalloff * curveFactor), 0.0, 1.0);
highp float dist = dot(diff, diff) + LIGHTING_HEIGHT;
highp float val = clamp((1.0 - clamp(sqrt(dist) / lightRange, 0.0, 1.0)) * (1.0 / (sqrt(dist + 1.0))), 0.0, 1.0);
val *= lightPower;
val *= mask;

View File

@@ -1,46 +0,0 @@
// Simple shader for creating a box with colours varying along the x and y axes.
uniform highp vec2 size;
uniform highp vec2 offset;
uniform highp vec4 xAxis;
uniform highp vec4 yAxis;
uniform highp vec4 baseColor;
uniform bool hsv;
void fragment()
{
// Calculate local uv coordinates.
// I.e., if using this shader to draw a box to the screen, (0,0) is the bottom left of the box.
highp float yCoords = 1.0/SCREEN_PIXEL_SIZE.y - FRAGCOORD.y;
highp vec2 uv = vec2(FRAGCOORD.x - offset.x, yCoords - offset.y);
uv /= size;
uv.y = 1.0 - uv.y;
highp vec4 modulate = baseColor + uv.x * xAxis + uv.y * yAxis;
if (hsv)
{
modulate.xyz = hsv2rgb(modulate.xyz);
}
// The UV used for the texture lookup is the TEXTURE UV coordinate, which is different from the coordinates computed above.
COLOR = zTexture(UV) * modulate;
}
// hsv to RGB conversion taken from www.shadertoy.com/view/MsS3Wc
// The MIT License
// Copyright © 2014 Inigo Quilez
// 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.
// https://www.youtube.com/c/InigoQuilez
// https://iquilezles.org
highp vec3 hsv2rgb( in highp vec3 c )
{
highp vec3 rgb = clamp( abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0 );
return c.z * mix( vec3(1.0), rgb, c.y);
}

View File

@@ -1,8 +1,12 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Testing.Verifiers;
using NUnit.Framework;
using VerifyCS = Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.AccessAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
using Robust.Analyzers;
using VerifyCS = Microsoft.CodeAnalysis.CSharp.Testing.NUnit.AnalyzerVerifier<Robust.Analyzers.AccessAnalyzer>;
using static Microsoft.CodeAnalysis.Testing.DiagnosticResult;
namespace Robust.Analyzers.Tests;
@@ -12,20 +16,15 @@ public sealed class AccessAnalyzer_Test
{
public Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<AccessAnalyzer, DefaultVerifier>()
var test = new CSharpAnalyzerTest<AccessAnalyzer, NUnitVerifier>()
{
TestState =
{
AdditionalReferences = { typeof(AccessAnalyzer).Assembly },
Sources = { code }
},
};
TestHelper.AddEmbeddedSources(
test.TestState,
"Robust.Shared.Analyzers.AccessAttribute.cs",
"Robust.Shared.Analyzers.AccessPermissions.cs"
);
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);

View File

@@ -1,110 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.AfterAutoHandleStateAnalyzer,
Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture, TestOf(typeof(AfterAutoHandleStateAnalyzer))]
public sealed class AfterAutoHandleStateAnalyzerTest
{
private const string SubscribeEventDef = """
using System;
namespace Robust.Shared.GameObjects;
public readonly struct EntityUid;
public abstract class EntitySystem
{
public void SubscribeLocalEvent<T, TEvent>() where TEvent : notnull { }
}
public interface IComponent;
public interface IComponentState;
""";
// A rare case for block-scoped namespace, I thought. Then I realized this
// only needed the one type definition.
private const string OtherTypeDefs = """
using System;
namespace JetBrains.Annotations
{
public sealed class BaseTypeRequiredAttribute(Type baseType) : Attribute;
}
""";
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<AfterAutoHandleStateAnalyzer, DefaultVerifier>
{
TestState = { Sources = { code } }
};
TestHelper.AddEmbeddedSources(test.TestState,
"Robust.Shared.Analyzers.ComponentNetworkGeneratorAuxiliary.cs",
"Robust.Shared.GameObjects.EventBusAttributes.cs");
test.TestState.Sources.Add(("EntitySystem.Subscriptions.cs", SubscribeEventDef));
test.TestState.Sources.Add(("Types.cs", OtherTypeDefs));
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task Test()
{
const string code = """
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
[AutoGenerateComponentState(true)]
public sealed class AutoGenTrue;
[AutoGenerateComponentState(true, true)]
public sealed class AutoGenTrueTrue;
public sealed class NotAutoGen;
[AutoGenerateComponentState]
public sealed class AutoGenNoArgs;
[AutoGenerateComponentState(false)]
public sealed class AutoGenFalse;
public sealed class Foo : EntitySystem
{
public void Good()
{
// Subscribing to other events works
SubscribeLocalEvent<AutoGenNoArgs, object>();
// First arg true allows subscribing
SubscribeLocalEvent<AutoGenTrue, AfterAutoHandleStateEvent>();
SubscribeLocalEvent<AutoGenTrueTrue, AfterAutoHandleStateEvent>();
}
public void Bad()
{
// Can't subscribe if AutoGenerateComponentState isn't even present
SubscribeLocalEvent<NotAutoGen, AfterAutoHandleStateEvent>();
// Can't subscribe if first arg is not specified/false
SubscribeLocalEvent<AutoGenNoArgs, AfterAutoHandleStateEvent>();
SubscribeLocalEvent<AutoGenFalse, AfterAutoHandleStateEvent>();
}
}
""";
await Verifier(code,
// /0/Test0.cs(29,9): error RA0040: Tried to subscribe to AfterAutoHandleStateEvent for 'NotAutoGen' which doesn't have an AutoGenerateComponentState attribute
VerifyCS.Diagnostic(AfterAutoHandleStateAnalyzer.MissingAttribute).WithSpan(29, 9, 29, 69).WithArguments("NotAutoGen"),
// /0/Test0.cs(32,9): error RA0041: Tried to subscribe to AfterAutoHandleStateEvent for 'AutoGenNoArgs' which doesn't have raiseAfterAutoHandleState set
VerifyCS.Diagnostic(AfterAutoHandleStateAnalyzer.MissingAttributeParam).WithSpan(32, 9, 32, 72).WithArguments("AutoGenNoArgs"),
// /0/Test0.cs(33,9): error RA0041: Tried to subscribe to AfterAutoHandleStateEvent for 'AutoGenFalse' which doesn't have raiseAfterAutoHandleState set
VerifyCS.Diagnostic(AfterAutoHandleStateAnalyzer.MissingAttributeParam).WithSpan(33, 9, 33, 71).WithArguments("AutoGenFalse")
);
}
}

View File

@@ -1,8 +0,0 @@
// OH BOY. TURNS OUT IT GETS EVEN MORE CURSED.
//
// So because we're compiling a copy of Robust.Roslyn.Shared into every analyzer project,
// the test project sees multiple copies of it. This would make it impossible to use.
// UNLESS you use this obscure C# feature called "extern alias"
// that I guarantee you you've never heard of before, and are now concerned about.
extern alias SerializationGenerator;

View File

@@ -1,114 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.ByRefEventAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture, TestOf(typeof(ByRefEventAnalyzer))]
public sealed class ByRefEventAnalyzerTest
{
private const string EventBusDef = """
namespace Robust.Shared.GameObjects;
public readonly struct EntityUid;
public sealed class EntitySystem
{
public void RaiseLocalEvent<TEvent>(EntityUid uid, ref TEvent args, bool broadcast = false)
where TEvent : notnull { }
public void RaiseLocalEvent<TEvent>(EntityUid uid, TEvent args, bool broadcast = false)
where TEvent : notnull { }
}
public sealed class EntityEventBus
{
public void RaiseLocalEvent<TEvent>(EntityUid uid, ref TEvent args, bool broadcast = false)
where TEvent : notnull { }
public void RaiseLocalEvent<TEvent>(EntityUid uid, TEvent args, bool broadcast = false)
where TEvent : notnull { }
}
""";
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<ByRefEventAnalyzer, DefaultVerifier>()
{
TestState =
{
Sources = { code }
},
};
TestHelper.AddEmbeddedSources(
test.TestState,
"Robust.Shared.GameObjects.EventBusAttributes.cs"
);
test.TestState.Sources.Add(("EntityEventBus.cs", EventBusDef));
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task TestSuccess()
{
const string code = """
using Robust.Shared.GameObjects;
[ByRefEvent]
public readonly struct RefEvent;
public readonly struct ValueEvent;
public static class Foo
{
public static void Bar(EntityEventBus bus)
{
bus.RaiseLocalEvent(default(EntityUid), new ValueEvent());
var refEv = new RefEvent();
bus.RaiseLocalEvent(default(EntityUid), ref refEv);
}
}
""";
await Verifier(code);
}
[Test]
public async Task TestWrong()
{
const string code = """
using Robust.Shared.GameObjects;
[ByRefEvent]
public readonly struct RefEvent;
public readonly struct ValueEvent;
public static class Foo
{
public static void Bar(EntityEventBus bus)
{
bus.RaiseLocalEvent(default(EntityUid), new RefEvent());
var valueEv = new ValueEvent();
bus.RaiseLocalEvent(default(EntityUid), ref valueEv);
}
}
""";
await Verifier(
code,
// /0/Test0.cs(11,49): error RA0015: Tried to raise a by-ref event 'RefEvent' by value
VerifyCS.Diagnostic(ByRefEventAnalyzer.ByRefEventRaisedByValueRule).WithSpan(11, 49, 11, 63).WithArguments("RefEvent"),
// /0/Test0.cs(13,49): error RA0016: Tried to raise a value event 'ValueEvent' by-ref
VerifyCS.Diagnostic(ByRefEventAnalyzer.ByValueEventRaisedByRefRule).WithSpan(13, 49, 13, 60).WithArguments("ValueEvent")
);
}
}

View File

@@ -1,386 +0,0 @@
extern alias SerializationGenerator;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using NUnit.Framework;
using SerializationGenerator::Robust.Roslyn.Shared;
using SerializationGenerator::Robust.Serialization.Generator;
namespace Robust.Analyzers.Tests;
[TestFixture]
[TestOf(typeof(ComponentPauseGenerator))]
[Parallelizable(ParallelScope.All)]
public sealed class ComponentPauseGeneratorTest
{
private const string TypesCode = """
global using System;
global using Robust.Shared.Analyzers;
global using Robust.Shared.GameObjects;
namespace Robust.Shared.Analyzers
{
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class AutoGenerateComponentPauseAttribute : Attribute
{
public bool Dirty = false;
}
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class AutoPausedFieldAttribute : Attribute;
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class AutoNetworkedFieldAttribute : Attribute
{
}
}
namespace Robust.Shared.GameObjects
{
public interface IComponent;
}
""";
[Test]
public void TestBasic()
{
var result = RunGenerator("""
[AutoGenerateComponentPause]
public sealed partial class FooComponent : IComponent
{
[AutoPausedField]
public TimeSpan Foo;
}
""");
ExpectNoDiagnostics(result);
ExpectSource(
result,
"""
// <auto-generated />
using Robust.Shared.GameObjects;
public partial class FooComponent
{
[RobustAutoGenerated]
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public sealed class FooComponent_AutoPauseSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<FooComponent, EntityUnpausedEvent>(OnEntityUnpaused);
}
private void OnEntityUnpaused(EntityUid uid, FooComponent component, ref EntityUnpausedEvent args)
{
component.Foo += args.PausedTime;
}
}
}
""");
}
[Test]
public void TestNullable()
{
var result = RunGenerator("""
[AutoGenerateComponentPause]
public sealed partial class FooComponent : IComponent
{
[AutoPausedField]
public TimeSpan? Foo;
}
""");
ExpectNoDiagnostics(result);
ExpectSource(
result,
"""
// <auto-generated />
using Robust.Shared.GameObjects;
public partial class FooComponent
{
[RobustAutoGenerated]
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public sealed class FooComponent_AutoPauseSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<FooComponent, EntityUnpausedEvent>(OnEntityUnpaused);
}
private void OnEntityUnpaused(EntityUid uid, FooComponent component, ref EntityUnpausedEvent args)
{
if (component.Foo.HasValue)
component.Foo = component.Foo.Value + args.PausedTime;
}
}
}
""");
}
[Test]
public void TestDictionary()
{
var result = RunGenerator("""
[AutoGenerateComponentPause]
public sealed partial class FooComponent : IComponent
{
[AutoPausedField]
public Dictionary<string, TimeSpan> Foo;
}
""");
ExpectNoDiagnostics(result);
ExpectSource(
result,
"""
// <auto-generated />
using Robust.Shared.GameObjects;
public partial class FooComponent
{
[RobustAutoGenerated]
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public sealed class FooComponent_AutoPauseSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<FooComponent, EntityUnpausedEvent>(OnEntityUnpaused);
}
private void OnEntityUnpaused(EntityUid uid, FooComponent component, ref EntityUnpausedEvent args)
{
foreach (var key in component.Foo.Keys)
component.Foo[key] += args.PausedTime;
}
}
}
""");
}
[Test]
public void TestAutoState()
{
var result = RunGenerator("""
[AutoGenerateComponentPause]
public sealed partial class FooComponent : IComponent
{
[AutoPausedField, AutoNetworkedField]
public TimeSpan Foo;
}
""");
ExpectNoDiagnostics(result);
ExpectSource(
result,
"""
// <auto-generated />
using Robust.Shared.GameObjects;
public partial class FooComponent
{
[RobustAutoGenerated]
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public sealed class FooComponent_AutoPauseSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<FooComponent, EntityUnpausedEvent>(OnEntityUnpaused);
}
private void OnEntityUnpaused(EntityUid uid, FooComponent component, ref EntityUnpausedEvent args)
{
component.Foo += args.PausedTime;
Dirty(uid, component);
}
}
}
""");
}
[Test]
public void TestExplicitDirty()
{
var result = RunGenerator("""
[AutoGenerateComponentPause(Dirty = true)]
public sealed partial class FooComponent : IComponent
{
[AutoPausedField]
public TimeSpan Foo;
}
""");
ExpectNoDiagnostics(result);
ExpectSource(
result,
"""
// <auto-generated />
using Robust.Shared.GameObjects;
public partial class FooComponent
{
[RobustAutoGenerated]
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public sealed class FooComponent_AutoPauseSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<FooComponent, EntityUnpausedEvent>(OnEntityUnpaused);
}
private void OnEntityUnpaused(EntityUid uid, FooComponent component, ref EntityUnpausedEvent args)
{
component.Foo += args.PausedTime;
Dirty(uid, component);
}
}
}
""");
}
[Test]
public void TestDiagnosticNotIComponent()
{
var result = RunGenerator("""
[AutoGenerateComponentPause]
public sealed partial class FooComponent
{
[AutoPausedField]
public TimeSpan Foo;
}
""");
ExpectNoSource(result);
ExpectDiagnostics(result, [
(Diagnostics.IdComponentPauseNotComponent, new LinePositionSpan(new LinePosition(1, 28), new LinePosition(1, 40)))
]);
}
[Test]
public void TestDiagnosticNoFields()
{
var result = RunGenerator("""
[AutoGenerateComponentPause]
public sealed partial class FooComponent : IComponent
{
public TimeSpan Foo;
}
""");
ExpectNoSource(result);
ExpectDiagnostics(result, [
(Diagnostics.IdComponentPauseNoFields, new LinePositionSpan(new LinePosition(1, 28), new LinePosition(1, 40)))
]);
}
[Test]
public void TestDiagnosticNoParentAttribute()
{
var result = RunGenerator("""
public sealed partial class FooComponent : IComponent
{
[AutoPausedField]
public TimeSpan Foo, Fooz;
[AutoPausedField]
public TimeSpan Bar { get; set; }
}
""");
ExpectNoSource(result);
ExpectDiagnostics(result, [
(Diagnostics.IdComponentPauseNoParentAttribute, new LinePositionSpan(new LinePosition(3, 20), new LinePosition(3, 23))),
(Diagnostics.IdComponentPauseNoParentAttribute, new LinePositionSpan(new LinePosition(3, 25), new LinePosition(3, 29))),
(Diagnostics.IdComponentPauseNoParentAttribute, new LinePositionSpan(new LinePosition(6, 20), new LinePosition(6, 23)))
]);
}
[Test]
public void TestDiagnosticWrongType()
{
var result = RunGenerator("""
[AutoGenerateComponentPause]
public sealed partial class FooComponent : IComponent
{
[AutoPausedField]
public int Foo, Fooz;
[AutoPausedField]
public int Bar { get; set; }
}
""");
ExpectNoSource(result);
ExpectDiagnostics(result, [
(Diagnostics.IdComponentPauseWrongTypeAttribute, new LinePositionSpan(new LinePosition(4, 15), new LinePosition(4, 18))),
(Diagnostics.IdComponentPauseWrongTypeAttribute, new LinePositionSpan(new LinePosition(4, 20), new LinePosition(4, 24))),
(Diagnostics.IdComponentPauseWrongTypeAttribute, new LinePositionSpan(new LinePosition(7, 15), new LinePosition(7, 18)))
]);
}
private static void ExpectSource(GeneratorRunResult result, string expected)
{
Assert.That(result.GeneratedSources, Has.Length.EqualTo(1));
var source = result.GeneratedSources[0];
Assert.That(source.SourceText.ToString(), Is.EqualTo(expected));
}
private static void ExpectNoSource(GeneratorRunResult result)
{
Assert.That(result.GeneratedSources, Is.Empty);
}
private static void ExpectNoDiagnostics(GeneratorRunResult result)
{
Assert.That(result.Diagnostics, Is.Empty);
}
private static void ExpectDiagnostics(GeneratorRunResult result, (string code, LinePositionSpan span)[] diagnostics)
{
Assert.Multiple(() =>
{
Assert.That(result.Diagnostics, Has.Length.EqualTo(diagnostics.Length));
foreach (var (code, span) in diagnostics)
{
Assert.That(
result.Diagnostics.Any(x => x.Id == code && x.Location.GetLineSpan().Span == span),
$"Expected diagnostic with code {code} and location {span}");
}
});
}
private static GeneratorRunResult RunGenerator(string source)
{
var compilation = (Compilation)CSharpCompilation.Create("compilation",
new[]
{
CSharpSyntaxTree.ParseText(source, path: "Source.cs"),
CSharpSyntaxTree.ParseText(TypesCode, path: "Types.cs")
},
new[] { MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location) },
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
var generator = new ComponentPauseGenerator();
GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
driver = driver.RunGeneratorsAndUpdateCompilation(compilation, out var newCompilation, out _);
var result = driver.GetRunResult();
return result.Results[0];
}
}

View File

@@ -1,256 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.DataDefinitionAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
public sealed class DataDefinitionAnalyzerTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<DataDefinitionAnalyzer, DefaultVerifier>()
{
TestState =
{
Sources = { code }
},
};
test.TestState.Sources.Add(("TestTypeDefs.cs", TestTypeDefs));
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
private const string TestTypeDefs = """
using System;
namespace Robust.Shared.ViewVariables
{
public sealed class ViewVariablesAttribute : Attribute
{
public readonly VVAccess Access = VVAccess.ReadOnly;
public ViewVariablesAttribute() { }
public ViewVariablesAttribute(VVAccess access)
{
Access = access;
}
}
public enum VVAccess : byte
{
ReadOnly = 0,
ReadWrite = 1,
}
}
namespace Robust.Shared.Serialization.Manager.Attributes
{
public class DataFieldBaseAttribute : Attribute;
public class DataFieldAttribute(string? tag = null) : DataFieldBaseAttribute;
public sealed class DataDefinitionAttribute : Attribute;
public sealed class NotYamlSerializableAttribute : Attribute;
}
""";
[Test]
public async Task NoVVReadOnlyTest()
{
const string code = """
using Robust.Shared.ViewVariables;
using Robust.Shared.Serialization.Manager.Attributes;
[DataDefinition]
public sealed partial class Foo
{
[DataField, ViewVariables(VVAccess.ReadWrite)]
public int Bad;
[DataField]
public int Good;
[DataField, ViewVariables]
public int Good2;
[DataField, ViewVariables(VVAccess.ReadOnly)]
public int Good3;
[ViewVariables]
public int Good4;
}
""";
await Verifier(code,
// /0/Test0.cs(7,17): info RA0028: Data field Bad in data definition Foo has ViewVariables attribute with ReadWrite access, which is redundant
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldNoVVReadWriteRule).WithSpan(7, 17, 7, 50).WithArguments("Bad", "Foo")
);
}
[Test]
public async Task ReadOnlyFieldTest()
{
const string code = """
using Robust.Shared.Serialization.Manager.Attributes;
[DataDefinition]
public sealed partial class Foo
{
[DataField]
public readonly int Bad;
[DataField]
public int Good;
}
""";
await Verifier(code,
// /0/Test0.cs(7,12): error RA0019: Data field Bad in data definition Foo is readonly
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldWritableRule).WithSpan(7, 12, 7, 20).WithArguments("Bad", "Foo")
);
}
[Test]
public async Task PartialDataDefinitionTest()
{
const string code = """
using Robust.Shared.Serialization.Manager.Attributes;
[DataDefinition]
public sealed class Foo { }
""";
await Verifier(code,
// /0/Test0.cs(4,15): error RA0017: Type Foo is a DataDefinition but is not partial
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataDefinitionPartialRule).WithSpan(4, 15, 4, 20).WithArguments("Foo")
);
}
[Test]
public async Task NestedPartialDataDefinitionTest()
{
const string code = """
using Robust.Shared.Serialization.Manager.Attributes;
public sealed class Foo
{
[DataDefinition]
public sealed partial class Nested { }
}
""";
await Verifier(code,
// /0/Test0.cs(3,15): error RA0018: Type Foo contains nested data definition Nested but is not partial
VerifyCS.Diagnostic(DataDefinitionAnalyzer.NestedDataDefinitionPartialRule).WithSpan(3, 15, 3, 20).WithArguments("Foo", "Nested")
);
}
[Test]
public async Task RedundantDataFieldTagTest()
{
const string code = """
using Robust.Shared.Serialization.Manager.Attributes;
[DataDefinition]
public sealed partial class Foo
{
[DataField("someValue")]
public int SomeValue;
}
""";
await Verifier(code,
// /0/Test0.cs(6,6): info RA0027: Data field SomeValue in data definition Foo has an explicitly set tag that matches autogenerated tag
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldRedundantTagRule).WithSpan(6, 6, 6, 28).WithArguments("SomeValue", "Foo")
);
}
[Test]
public async Task ReadOnlyPropertyTest()
{
const string code = """
using Robust.Shared.Serialization.Manager.Attributes;
[DataDefinition]
public sealed partial class Foo
{
[DataField]
public int Bad { get; }
[DataField]
public int Good { get; private set; }
}
""";
await Verifier(code,
// /0/Test0.cs(7,20): error RA0020: Data field property Bad in data definition Foo does not have a setter
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldPropertyWritableRule).WithSpan(7, 20, 7, 28).WithArguments("Bad", "Foo")
);
}
[Test]
public async Task NotYamlSerializableTest()
{
const string code = """
using Robust.Shared.Serialization.Manager.Attributes;
[NotYamlSerializable]
public sealed class NotSerializableClass { }
[NotYamlSerializable]
public readonly struct NotSerializableStruct { }
[DataDefinition]
public sealed partial class Foo
{
[DataField]
public NotSerializableClass BadField;
[DataField]
public NotSerializableClass BadProperty { get; set; }
[DataField]
public NotSerializableClass? BadNullableField;
[DataField]
public NotSerializableStruct BadStructField;
[DataField]
public NotSerializableStruct BadStructProperty { get; set; }
[DataField]
public NotSerializableStruct? BadNullableStructField;
[DataField]
public NotSerializableStruct? BadNullableStructProperty { get; set; }
public NotSerializableClass GoodField; // Not a DataField, not a problem
public NotSerializableClass GoodProperty { get; set; } // Not a DataField, not a problem
}
""";
await Verifier(code,
// /0/Test0.cs(12,12): error RA0033: Data field BadField in data definition Foo is type NotSerializableClass, which is not YAML serializable
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldYamlSerializableRule).WithSpan(12, 12, 12, 32).WithArguments("BadField", "Foo", "NotSerializableClass"),
// /0/Test0.cs(15,12): error RA0033: Data field BadProperty in data definition Foo is type NotSerializableClass, which is not YAML serializable
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldYamlSerializableRule).WithSpan(15, 12, 15, 32).WithArguments("BadProperty", "Foo", "NotSerializableClass"),
// /0/Test0.cs(18,12): error RA0036: Data field BadNullableField in data definition Foo is type NotSerializableClass, which is not YAML serializable
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldYamlSerializableRule).WithSpan(18, 12, 18, 33).WithArguments("BadNullableField", "Foo", "NotSerializableClass"),
// /0/Test0.cs(21,12): error RA0036: Data field BadStructField in data definition Foo is type NotSerializableStruct, which is not YAML serializable
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldYamlSerializableRule).WithSpan(21, 12, 21, 33).WithArguments("BadStructField", "Foo", "NotSerializableStruct"),
// /0/Test0.cs(24,12): error RA0036: Data field BadStructProperty in data definition Foo is type NotSerializableStruct, which is not YAML serializable
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldYamlSerializableRule).WithSpan(24, 12, 24, 33).WithArguments("BadStructProperty", "Foo", "NotSerializableStruct"),
// /0/Test0.cs(27,12): error RA0036: Data field BadNullableStructField in data definition Foo is type NotSerializableStruct, which is not YAML serializable
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldYamlSerializableRule).WithSpan(27, 12, 27, 34).WithArguments("BadNullableStructField", "Foo", "NotSerializableStruct"),
// /0/Test0.cs(30,12): error RA0036: Data field BadNullableStructProperty in data definition Foo is type NotSerializableStruct, which is not YAML serializable
VerifyCS.Diagnostic(DataDefinitionAnalyzer.DataFieldYamlSerializableRule).WithSpan(30, 12, 30, 34).WithArguments("BadNullableStructProperty", "Foo", "NotSerializableStruct")
);
}
}

View File

@@ -1,57 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.DependencyAssignAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
public sealed class DependencyAssignAnalyzerTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<DependencyAssignAnalyzer, DefaultVerifier>()
{
TestState =
{
Sources = { code }
},
};
TestHelper.AddEmbeddedSources(
test.TestState,
"Robust.Shared.IoC.DependencyAttribute.cs"
);
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task Test()
{
const string code = """
using Robust.Shared.IoC;
public sealed class Foo
{
[Dependency]
private object? Field;
public Foo()
{
Field = "A";
}
}
""";
await Verifier(code,
// /0/Test0.cs(10,9): warning RA0025: Tried to assign to [Dependency] field 'Field'. Remove [Dependency] or inject it via field injection instead.
VerifyCS.Diagnostic().WithSpan(10, 9, 10, 20).WithArguments("Field"));
}
}

View File

@@ -1,62 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.DuplicateDependencyAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
[TestOf(typeof(DuplicateDependencyAnalyzer))]
public sealed class DuplicateDependencyAnalyzerTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<DuplicateDependencyAnalyzer, DefaultVerifier>()
{
TestState =
{
Sources = { code }
},
};
TestHelper.AddEmbeddedSources(
test.TestState,
"Robust.Shared.IoC.DependencyAttribute.cs"
);
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task Test()
{
const string code = """
using Robust.Shared.IoC;
public sealed class Foo
{
[Dependency]
private object? Field;
[Dependency]
private object? Field2;
[Dependency]
private string? DifferentField;
private string? NonDependency1;
private string? NonDependency2;
}
""";
await Verifier(code,
// /0/Test0.cs(9,21): warning RA0032: Another [Dependency] field of type 'object?' already exists in this type as field 'Field'
VerifyCS.Diagnostic().WithSpan(9, 21, 9, 27).WithArguments("object?", "Field"));
}
}

View File

@@ -1,189 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.ForbidLiteralAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
public sealed class ForbidLiteralAnalyzerTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<ForbidLiteralAnalyzer, DefaultVerifier>()
{
TestState =
{
Sources = { code },
},
};
TestHelper.AddEmbeddedSources(
test.TestState,
"Robust.Shared.Analyzers.ForbidLiteralAttribute.cs"
);
test.TestState.Sources.Add(("TestTypeDefs.cs", TestTypeDefs));
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
private const string TestTypeDefs = """
using System.Collections.Generic;
using Robust.Shared.Analyzers;
public sealed class TestClass
{
public static void OneParameterForbidden([ForbidLiteral] string value) { }
public static void TwoParametersFirstForbidden([ForbidLiteral] string first, string second) { }
public static void TwoParametersBothForbidden([ForbidLiteral] string first, [ForbidLiteral] string second) { }
public static void ListParameterForbidden([ForbidLiteral] List<string> values) { }
public static void ParamsListParameterForbidden([ForbidLiteral] params List<string> values) { }
}
public record struct StringWrapper(string value)
{
private readonly string _value = value;
public static implicit operator string(StringWrapper wrapper)
{
return wrapper._value;
}
}
""";
[Test]
public async Task TestOneParameter()
{
const string code = """
public sealed class Tester
{
private const string _constValue = "foo";
private static readonly string StaticValue = "bar";
private static readonly StringWrapper WrappedValue = new("biz");
public void Test()
{
TestClass.OneParameterForbidden(_constValue);
TestClass.OneParameterForbidden(StaticValue);
TestClass.OneParameterForbidden(WrappedValue);
TestClass.OneParameterForbidden("baz");
}
}
""";
await Verifier(code,
// /0/Test0.cs(12,41): error RA0033: The "value" parameter of OneParameterForbidden forbids literal values
VerifyCS.Diagnostic().WithSpan(12, 41, 12, 46).WithArguments("value", "OneParameterForbidden")
);
}
[Test]
public async Task TestTwoParametersFirstForbidden()
{
const string code = """
public sealed class Tester
{
private const string _constValue = "foo";
public void Test()
{
TestClass.TwoParametersFirstForbidden(_constValue, "whatever");
TestClass.TwoParametersFirstForbidden(_constValue, _constValue);
TestClass.TwoParametersFirstForbidden("foo", "whatever");
}
}
""";
await Verifier(code,
// /0/Test0.cs(9,47): error RA0033: The "first" parameter of TwoParametersFirstForbidden forbids literal values
VerifyCS.Diagnostic().WithSpan(9, 47, 9, 52).WithArguments("first", "TwoParametersFirstForbidden")
);
}
[Test]
public async Task TestTwoParametersBothForbidden()
{
const string code = """
public sealed class Tester
{
private const string _constValue = "foo";
private static readonly string StaticValue = "bar";
public void Test()
{
TestClass.TwoParametersBothForbidden(_constValue, _constValue);
TestClass.TwoParametersBothForbidden(_constValue, StaticValue);
TestClass.TwoParametersBothForbidden(_constValue, "whatever");
TestClass.TwoParametersBothForbidden("whatever", _constValue);
}
}
""";
await Verifier(code,
// /0/Test0.cs(10,59): error RA0033: The "second" parameter of TwoParametersBothForbidden forbids literal values
VerifyCS.Diagnostic().WithSpan(10, 59, 10, 69).WithArguments("second", "TwoParametersBothForbidden"),
// /0/Test0.cs(11,46): error RA0033: The "first" parameter of TwoParametersBothForbidden forbids literal values
VerifyCS.Diagnostic().WithSpan(11, 46, 11, 56).WithArguments("first", "TwoParametersBothForbidden")
);
}
[Test]
public async Task TestListParameter()
{
const string code = """
public sealed class Tester
{
private const string _constValue = "foo";
private static readonly string StaticValue = "bar";
private static readonly StringWrapper WrappedValue = new("biz");
public void Test()
{
TestClass.ListParameterForbidden([_constValue, StaticValue, WrappedValue]);
TestClass.ListParameterForbidden(["foo", _constValue, "bar"]);
}
}
""";
await Verifier(code,
// /0/Test0.cs(10,43): warning RA0033: The "values" parameter of ListParameterForbidden forbids literal values
VerifyCS.Diagnostic().WithSpan(10, 43, 10, 48).WithArguments("values", "ListParameterForbidden"),
// /0/Test0.cs(10,63): warning RA0033: The "values" parameter of ListParameterForbidden forbids literal values
VerifyCS.Diagnostic().WithSpan(10, 63, 10, 68).WithArguments("values", "ListParameterForbidden")
);
}
[Test]
public async Task TestParamsListParameter()
{
const string code = """
public sealed class Tester
{
private const string _constValue = "foo";
private static readonly string StaticValue = "bar";
private static readonly StringWrapper WrappedValue = new("biz");
public void Test()
{
TestClass.ParamsListParameterForbidden(_constValue, StaticValue, WrappedValue);
TestClass.ParamsListParameterForbidden("foo", _constValue, "bar");
}
}
""";
await Verifier(code,
// /0/Test0.cs(10,48): warning RA0033: The "values" parameter of ParamsListParameterForbidden forbids literal values
VerifyCS.Diagnostic().WithSpan(10, 48, 10, 53).WithArguments("values", "ParamsListParameterForbidden"),
// /0/Test0.cs(10,68): warning RA0033: The "values" parameter of ParamsListParameterForbidden forbids literal values
VerifyCS.Diagnostic().WithSpan(10, 68, 10, 73).WithArguments("values", "ParamsListParameterForbidden")
);
}
}

View File

@@ -1,91 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.MustCallBaseAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
public sealed class MustCallBaseAnalyzerTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<MustCallBaseAnalyzer, DefaultVerifier>()
{
TestState =
{
Sources = { code }
},
};
TestHelper.AddEmbeddedSources(
test.TestState,
"Robust.Shared.IoC.MustCallBaseAttribute.cs"
);
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task Test()
{
const string code = """
using Robust.Shared.Analyzers;
public class Foo
{
[MustCallBase]
public virtual void Function()
{
}
[MustCallBase(true)]
public virtual void Function2()
{
}
}
public class Bar : Foo
{
public override void Function()
{
}
public override void Function2()
{
}
}
public class Baz : Foo
{
public override void Function()
{
base.Function();
}
}
public class Bal : Bar
{
public override void Function2()
{
}
}
""";
await Verifier(code,
// /0/Test0.cs(20,26): warning RA0028: Overriders of this function must always call the base function
VerifyCS.Diagnostic().WithSpan(20, 26, 20, 34),
// /0/Test0.cs(41,26): warning RA0028: Overriders of this function must always call the base function
VerifyCS.Diagnostic().WithSpan(41, 26, 41, 35));
}
}

View File

@@ -1,56 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.NoUncachedRegexAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
public sealed class NoUncachedRegexAnalyzerTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<NoUncachedRegexAnalyzer, DefaultVerifier>()
{
TestState =
{
Sources = { code }
},
};
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task Test()
{
const string code = """
using System.Text.RegularExpressions;
public static class Foo
{
public static void Bad()
{
Regex.Replace("foo", "bar", "baz");
}
public static void Good()
{
var r = new Regex("bar");
r.Replace("foo", "baz");
}
}
""";
await Verifier(code,
// /0/Test0.cs(7,9): warning RA0026: Usage of a static Regex function that takes in a pattern string. This can cause constant re-parsing of the pattern.
VerifyCS.Diagnostic().WithSpan(7, 9, 7, 43)
);
}
}

View File

@@ -1,86 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.ObsoleteInheritanceAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
namespace Robust.Analyzers.Tests;
/// <summary>
/// Analyzer that implements <c>[ObsoleteInheritance]</c> checking, to give obsoletion warnings for inheriting types
/// that should never have been virtual.
/// </summary>
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
public sealed class ObsoleteInheritanceAnalyzerTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<ObsoleteInheritanceAnalyzer, DefaultVerifier>()
{
TestState =
{
Sources = { code },
},
};
TestHelper.AddEmbeddedSources(
test.TestState,
"Robust.Shared.Analyzers.ObsoleteInheritanceAttribute.cs"
);
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task TestBasic()
{
const string code = """
using Robust.Shared.Analyzers;
[ObsoleteInheritance]
public class Base;
public class NotAllowed : Base;
""";
await Verifier(code,
// /0/Test0.cs(6,14): warning RA0034: Type 'NotAllowed' inherits from 'Base', which has obsoleted inheriting from itself
VerifyCS.Diagnostic(ObsoleteInheritanceAnalyzer.Rule).WithSpan(6, 14, 6, 24).WithArguments("NotAllowed", "Base")
);
}
[Test]
public async Task TestMessage()
{
const string code = """
using Robust.Shared.Analyzers;
[ObsoleteInheritance("Sus")]
public class Base;
public class NotAllowed : Base;
""";
await Verifier(code,
// /0/Test0.cs(6,14): warning RA0034: Type 'NotAllowed' inherits from 'Base', which has obsoleted inheriting from itself: "Sus"
VerifyCS.Diagnostic(ObsoleteInheritanceAnalyzer.RuleWithMessage).WithSpan(6, 14, 6, 24).WithArguments("NotAllowed", "Base", "Sus")
);
}
[Test]
public async Task TestNormal()
{
const string code = """
public class Base;
public class AllowedAllowed : Base;
""";
await Verifier(code);
}
}

View File

@@ -1,70 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.PreferNonGenericVariantForAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
public sealed class PreferNonGenericVariantForTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<PreferNonGenericVariantForAnalyzer, DefaultVerifier>()
{
TestState =
{
Sources = { code },
},
};
TestHelper.AddEmbeddedSources(
test.TestState,
"Robust.Shared.Analyzers.PreferNonGenericVariantForAttribute.cs"
);
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task Test()
{
const string code = """
using Robust.Shared.Analyzers;
public class Bar { };
public class Baz { };
public class Okay { };
public static class Foo
{
[PreferNonGenericVariantFor(typeof(Bar), typeof(Baz))]
public static void DoFoo<T>() { }
}
public class Test
{
public void DoBad()
{
Foo.DoFoo<Bar>();
}
public void DoGood()
{
Foo.DoFoo<Okay>();
}
}
""";
await Verifier(code,
// /0/Test0.cs(17,9): warning RA0029: Use the non-generic variant of this method for type Bar
VerifyCS.Diagnostic().WithSpan(17, 9, 17, 25).WithArguments("Bar")
);
}
}

View File

@@ -1,61 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.PreferOtherTypeAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
public sealed class PreferOtherTypeAnalyzerTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new CSharpAnalyzerTest<PreferOtherTypeAnalyzer, DefaultVerifier>()
{
TestState =
{
Sources = { code },
},
};
TestHelper.AddEmbeddedSources(
test.TestState,
"Robust.Shared.Analyzers.PreferOtherTypeAttribute.cs"
);
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task Test()
{
const string code = """
using Robust.Shared.Analyzers;
public class EntityPrototype { };
public class EntProtoId { };
public class ReagentPrototype { };
[PreferOtherType(typeof(EntityPrototype), typeof(EntProtoId))]
public class ProtoId<T> { };
public class Test
{
public ProtoId<EntityPrototype> Bad = new();
public ProtoId<ReagentPrototype> Good = new();
}
""";
await Verifier(code,
// /0/Test0.cs(12,12): warning RA0031: Use the specific type EntProtoId instead of ProtoId when the type argument is EntityPrototype
VerifyCS.Diagnostic().WithSpan(12, 12, 12, 48).WithArguments("EntProtoId", "ProtoId", "EntityPrototype")
);
}
}

View File

@@ -1,80 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.PreferOtherTypeAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
namespace Robust.Analyzers.Tests;
public sealed class PreferOtherTypeFixerTest
{
private static Task Verifier(string code, string fixedCode, params DiagnosticResult[] expected)
{
var test = new CSharpCodeFixTest<PreferOtherTypeAnalyzer, PreferOtherTypeFixer, DefaultVerifier>()
{
TestState =
{
Sources = { code },
},
FixedState =
{
Sources = { fixedCode },
}
};
TestHelper.AddEmbeddedSources(
test.TestState,
"Robust.Shared.Analyzers.PreferOtherTypeAttribute.cs"
);
TestHelper.AddEmbeddedSources(
test.FixedState,
"Robust.Shared.Analyzers.PreferOtherTypeAttribute.cs"
);
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task Test()
{
const string code = """
using Robust.Shared.Analyzers;
public class EntityPrototype { };
public class EntProtoId { };
public class ReagentPrototype { };
[PreferOtherType(typeof(EntityPrototype), typeof(EntProtoId))]
public class ProtoId<T> { };
public class Test
{
public ProtoId<EntityPrototype> Foo = new();
}
""";
const string fixedCode = """
using Robust.Shared.Analyzers;
public class EntityPrototype { };
public class EntProtoId { };
public class ReagentPrototype { };
[PreferOtherType(typeof(EntityPrototype), typeof(EntProtoId))]
public class ProtoId<T> { };
public class Test
{
public EntProtoId Foo = new();
}
""";
await Verifier(code, fixedCode,
// /0/Test0.cs(12,12): error RA0031: Use the specific type EntProtoId instead of ProtoId when the type argument is EntityPrototype
VerifyCS.Diagnostic().WithSpan(12, 12, 12, 48).WithArguments("EntProtoId", "ProtoId", "EntityPrototype"));
}
}

View File

@@ -1,64 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.PrototypeInstantiationAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
[TestOf(typeof(PrototypeInstantiationAnalyzer))]
public sealed class PrototypeInstantiationAnalyzerTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new RTAnalyzerTest<PrototypeInstantiationAnalyzer>()
{
TestState =
{
Sources = { code }
},
};
TestHelper.AddEmbeddedSources(
test.TestState,
"Robust.Shared.Prototypes.Attributes.cs",
"Robust.Shared.Prototypes.IPrototype.cs",
"Robust.Shared.Serialization.Manager.Attributes.DataFieldAttribute.cs"
);
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task Test()
{
const string code = """
using Robust.Shared.Serialization;
using Robust.Shared.Prototypes;
[Prototype]
public sealed class FooPrototype : IPrototype
{
[IdDataField]
public string ID { get; private set; } = default!;
}
public static class Bad
{
public static FooPrototype Real()
{
return new FooPrototype();
}
}
""";
await Verifier(code,
// /0/Test0.cs(15,16): warning RA0039: Do not instantiate prototypes directly. Prototypes should always be instantiated by the prototype manager.
VerifyCS.Diagnostic().WithSpan(15, 16, 15, 34));
}
}

View File

@@ -1,61 +0,0 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.PrototypeNetSerializableAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
namespace Robust.Analyzers.Tests;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
[TestOf(typeof(PrototypeNetSerializableAnalyzer))]
public sealed class PrototypeNetSerializableAnalyzerTest
{
private static Task Verifier(string code, params DiagnosticResult[] expected)
{
var test = new RTAnalyzerTest<PrototypeNetSerializableAnalyzer>()
{
TestState =
{
Sources = { code }
},
};
TestHelper.AddEmbeddedSources(
test.TestState,
"Robust.Shared.Serialization.NetSerializableAttribute.cs",
"Robust.Shared.Prototypes.Attributes.cs",
"Robust.Shared.Prototypes.IPrototype.cs",
"Robust.Shared.Serialization.Manager.Attributes.DataFieldAttribute.cs"
);
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
[Test]
public async Task Test()
{
const string code = """
using System;
using Robust.Shared.Serialization;
using Robust.Shared.Prototypes;
[Prototype]
[Serializable, NetSerializable]
public sealed class FooPrototype : IPrototype
{
[IdDataField]
public string ID { get; private set; } = default!;
}
""";
await Verifier(code,
// /0/Test0.cs(7,21): warning RA0037: Type FooPrototype is a prototype and marked as [NetSerializable]. Prototypes should not be directly sent over the network, send their IDs instead.
VerifyCS.Diagnostic(PrototypeNetSerializableAnalyzer.RuleNetSerializable).WithSpan(7, 21, 7, 33).WithArguments("FooPrototype"),
// /0/Test0.cs(7,21): warning RA0038: Type FooPrototype is a prototype and marked as [Serializable]. Prototypes should not be directly sent over the network, send their IDs instead.
VerifyCS.Diagnostic(PrototypeNetSerializableAnalyzer.RuleSerializable).WithSpan(7, 21, 7, 33).WithArguments("FooPrototype"));
}
}

View File

@@ -1,17 +0,0 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing;
namespace Robust.Analyzers.Tests;
public sealed class RTAnalyzerTest<TAnalyzer> : CSharpAnalyzerTest<TAnalyzer, DefaultVerifier>
where TAnalyzer : DiagnosticAnalyzer, new()
{
protected override ParseOptions CreateParseOptions()
{
var baseOptions = (CSharpParseOptions) base.CreateParseOptions();
return baseOptions.WithPreprocessorSymbols("ROBUST_ANALYZERS_TEST");
}
}

View File

@@ -1,55 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<SkipRobustAnalyzer>true</SkipRobustAnalyzer>
</PropertyGroup>
<Import Project="..\MSBuild\Robust.Properties.targets"/>
<Import Project="..\MSBuild\Robust.Engine.props"/>
<!-- Engine source files needed to make the tests work -->
<ItemGroup>
<EmbeddedResource Include="..\Robust.Shared\Analyzers\AccessAttribute.cs" LogicalName="Robust.Shared.Analyzers.AccessAttribute.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Analyzers\AccessPermissions.cs" LogicalName="Robust.Shared.Analyzers.AccessPermissions.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Analyzers\ComponentNetworkGeneratorAuxiliary.cs" LogicalName="Robust.Shared.Analyzers.ComponentNetworkGeneratorAuxiliary.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Analyzers\MustCallBaseAttribute.cs" LogicalName="Robust.Shared.IoC.MustCallBaseAttribute.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Analyzers\PreferNonGenericVariantForAttribute.cs" LogicalName="Robust.Shared.Analyzers.PreferNonGenericVariantForAttribute.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Analyzers\PreferOtherTypeAttribute.cs" LogicalName="Robust.Shared.Analyzers.PreferOtherTypeAttribute.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Analyzers\ForbidLiteralAttribute.cs" LogicalName="Robust.Shared.Analyzers.ForbidLiteralAttribute.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Analyzers\ObsoleteInheritanceAttribute.cs" LogicalName="Robust.Shared.Analyzers.ObsoleteInheritanceAttribute.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\IoC\DependencyAttribute.cs" LogicalName="Robust.Shared.IoC.DependencyAttribute.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\GameObjects\EventBusAttributes.cs" LogicalName="Robust.Shared.GameObjects.EventBusAttributes.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Serialization\NetSerializableAttribute.cs" LogicalName="Robust.Shared.Serialization.NetSerializableAttribute.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Prototypes\Attributes.cs" LogicalName="Robust.Shared.Prototypes.Attributes.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Prototypes\IPrototype.cs" LogicalName="Robust.Shared.Prototypes.IPrototype.cs" LinkBase="Implementations" />
<EmbeddedResource Include="..\Robust.Shared\Serialization\Manager\Attributes\DataFieldAttribute.cs" LogicalName="Robust.Shared.Serialization.Manager.Attributes.DataFieldAttribute.cs" LinkBase="Implementations" />
</ItemGroup>
<Import Project="..\MSBuild\Robust.Properties.targets" />
<Import Project="..\MSBuild\Robust.Engine.props" />
<PropertyGroup>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageVersion Update="NUnit" Version="3.14.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzer.Testing" Version="1.1.1"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.NUnit" Version="1.1.1"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1"/>
<PackageReference Include="NUnit" Version="3.13.2"/>
<PackageReference Include="NUnit.ConsoleRunner" Version="3.15.0"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1"/>
<PackageReference Include="NUnit.Analyzers" Version="3.3.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzer.Testing"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing"/>
<PackageReference Include="NUnit"/>
<PackageReference Include="NUnit3TestAdapter"/>
<PackageReference Include="NUnit.Analyzers"/>
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
<!-- Needed to fix transitive dependency versions -->
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" />
<PackageReference Include="System.Formats.Asn1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Robust.Analyzers\Robust.Analyzers.csproj"/>
<ProjectReference Include="..\Robust.Serialization.Generator\Robust.Serialization.Generator.csproj" Aliases="SerializationGenerator" />
</ItemGroup>
</Project>

View File

@@ -1,22 +0,0 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Text;
namespace Robust.Analyzers.Tests;
public static class TestHelper
{
public static void AddEmbeddedSources(SolutionState state, params string[] embeddedFiles)
{
AddEmbeddedSources(state, (IEnumerable<string>) embeddedFiles);
}
public static void AddEmbeddedSources(SolutionState state, IEnumerable<string> embeddedFiles)
{
foreach (var fileName in embeddedFiles)
{
using var stream = typeof(AccessAnalyzer_Test).Assembly.GetManifestResourceStream(fileName)!;
state.Sources.Add((fileName, SourceText.From(stream)));
}
}
}

View File

@@ -5,7 +5,6 @@ using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
using Robust.Shared.Analyzers.Implementation;
namespace Robust.Analyzers

View File

@@ -1,85 +0,0 @@
#nullable enable
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class AfterAutoHandleStateAnalyzer : DiagnosticAnalyzer
{
private const string AfterAutoHandleStateEventName = "AfterAutoHandleStateEvent";
private const string AutoGenStateAttribute = "Robust.Shared.Analyzers.AutoGenerateComponentStateAttribute";
private const string SubscribeLocalEventName = "SubscribeLocalEvent";
public static readonly DiagnosticDescriptor MissingAttribute = new(
Diagnostics.IdAutoGenStateAttributeMissing,
"Unreachable AfterAutoHandleState subscription",
"Tried to subscribe to AfterAutoHandleStateEvent for '{0}' which doesn't have an "
+ "AutoGenerateComponentState attribute",
"Usage",
DiagnosticSeverity.Error,
true,
// Does this even show up anywhere in Rider? >:(
"You must mark your component with '[AutoGenerateComponentState(true)]' to subscribe to this event."
);
public static readonly DiagnosticDescriptor MissingAttributeParam = new(
Diagnostics.IdAutoGenStateParamMissing,
"Unreachable AfterAutoHandleState subscription",
"Tried to subscribe to AfterAutoHandleStateEvent for '{0}' which doesn't have "
+ "raiseAfterAutoHandleState set",
"Usage",
DiagnosticSeverity.Error,
true,
"The AutoGenerateComponentState attribute must be passed 'true' in order to subscribe to this event."
);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
[MissingAttribute, MissingAttributeParam];
public override void Initialize(AnalysisContext context)
{
// This is more to stop user error rather than code generation error
// (Plus this shouldn't affect code gen anyway)
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(compilationContext =>
{
var autoGenStateAttribute = compilationContext.Compilation.GetTypeByMetadataName(AutoGenStateAttribute);
// No attribute, no analyzer.
if (autoGenStateAttribute is null)
return;
compilationContext.RegisterOperationAction(
analysisContext => CheckEventSubscription(analysisContext, autoGenStateAttribute),
OperationKind.Invocation);
});
}
private static void CheckEventSubscription(OperationAnalysisContext context, ITypeSymbol autoGenStateAttribute)
{
if (context.Operation is not IInvocationOperation operation)
return;
// Check the method has the right name and has the right type args
if (operation.TargetMethod is not
{ Name: SubscribeLocalEventName, TypeArguments: [var component, { Name: AfterAutoHandleStateEventName }] })
return;
// Search the component's attributes for something matching autoGenStateAttribute
AttributeHelper.HasAttribute(component, autoGenStateAttribute, out var autoGenAttribute);
// First argument is raiseAfterAutoHandleState—note it shouldn't ever
// be null, since it has a default, but eh.
if (autoGenAttribute?.ConstructorArguments[0].Value is true)
return;
context.ReportDiagnostic(Diagnostic.Create(autoGenAttribute is null ? MissingAttribute : MissingAttributeParam,
operation.Syntax.GetLocation(),
component.Name));
}
}

View File

@@ -1,10 +1,9 @@
#nullable enable
#nullable enable
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
using static Microsoft.CodeAnalysis.SymbolEqualityComparer;
namespace Robust.Analyzers;
@@ -17,27 +16,37 @@ public sealed class ByRefEventAnalyzer : DiagnosticAnalyzer
private static readonly DiagnosticDescriptor ByRefEventSubscribedByValueRule = new(
Diagnostics.IdByRefEventSubscribedByValue,
"By-ref event subscribed to by value",
"Tried to subscribe to a by-ref event '{0}' by value",
"Tried to subscribe to a by-ref event '{0}' by value.",
"Usage",
DiagnosticSeverity.Error,
true,
"Make sure that methods subscribing to a ref event have the ref keyword for the event argument."
);
public static readonly DiagnosticDescriptor ByRefEventRaisedByValueRule = new(
private static readonly DiagnosticDescriptor ByValueEventSubscribedByRefRule = new(
Diagnostics.IdValueEventRaisedByRef,
"Value event subscribed to by-ref",
"Tried to subscribe to a value event '{0}' by-ref.",
"Usage",
DiagnosticSeverity.Error,
true,
"Make sure that methods subscribing to value events do not have the ref keyword for the event argument."
);
private static readonly DiagnosticDescriptor ByRefEventRaisedByValueRule = new(
Diagnostics.IdByRefEventRaisedByValue,
"By-ref event raised by value",
"Tried to raise a by-ref event '{0}' by value",
"Tried to raise a by-ref event '{0}' by value.",
"Usage",
DiagnosticSeverity.Error,
true,
"Make sure to use the ref keyword when raising ref events."
);
public static readonly DiagnosticDescriptor ByValueEventRaisedByRefRule = new(
private static readonly DiagnosticDescriptor ByValueEventRaisedByRefRule = new(
Diagnostics.IdValueEventRaisedByRef,
"Value event raised by-ref",
"Tried to raise a value event '{0}' by-ref",
"Tried to raise a value event '{0}' by-ref.",
"Usage",
DiagnosticSeverity.Error,
true,
@@ -46,6 +55,7 @@ public sealed class ByRefEventAnalyzer : DiagnosticAnalyzer
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
ByRefEventSubscribedByValueRule,
ByValueEventSubscribedByRefRule,
ByRefEventRaisedByValueRule,
ByValueEventRaisedByRefRule
);
@@ -54,44 +64,94 @@ public sealed class ByRefEventAnalyzer : DiagnosticAnalyzer
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(compilationContext =>
{
var raiseMethods = compilationContext.Compilation
.GetTypeByMetadataName("Robust.Shared.GameObjects.EntitySystem")?
.GetMembers()
.Where(m => m.Name.Contains("RaiseLocalEvent") && m.Kind == SymbolKind.Method)
.Cast<IMethodSymbol>();
var busRaiseMethods = compilationContext.Compilation
.GetTypeByMetadataName("Robust.Shared.GameObjects.EntityEventBus")?
.GetMembers()
.Where(m => m.Name.Contains("RaiseLocalEvent") && m.Kind == SymbolKind.Method)
.Cast<IMethodSymbol>();
if (raiseMethods == null)
return;
if (busRaiseMethods != null)
raiseMethods = raiseMethods.Concat(busRaiseMethods);
var raiseMethodsArray = raiseMethods.ToArray();
compilationContext.RegisterOperationAction(
ctx => CheckEventRaise(ctx, raiseMethodsArray),
OperationKind.Invocation);
});
context.RegisterOperationAction(CheckEventSubscription, OperationKind.Invocation);
context.RegisterOperationAction(CheckEventRaise, OperationKind.Invocation);
}
private static void CheckEventRaise(
OperationAnalysisContext context,
IReadOnlyCollection<IMethodSymbol> raiseMethods)
private void CheckEventSubscription(OperationAnalysisContext context)
{
if (context.Operation is not IInvocationOperation operation)
return;
if (!operation.TargetMethod.Name.Contains("RaiseLocalEvent"))
var subscribeMethods = context.Compilation
.GetTypeByMetadataName("Robust.Shared.GameObjects.EntitySystem")?
.GetMembers()
.Where(m => m.Name.Contains("SubscribeLocalEvent"))
.Cast<IMethodSymbol>();
if (subscribeMethods == null)
return;
if (!subscribeMethods.Any(m => m.Equals(operation.TargetMethod.OriginalDefinition, Default)))
return;
var typeArguments = operation.TargetMethod.TypeArguments;
if (typeArguments.Length < 1 || typeArguments.Length > 2)
return;
if (operation.Arguments.First().Value is not IDelegateCreationOperation delegateCreation)
return;
if (delegateCreation.Target is not IMethodReferenceOperation methodReference)
return;
var eventParameter = methodReference.Method.Parameters.LastOrDefault();
if (eventParameter == null)
return;
ITypeSymbol eventArgument;
switch (typeArguments.Length)
{
case 1:
eventArgument = typeArguments[0];
break;
case 2:
eventArgument = typeArguments[1];
break;
default:
return;
}
var byRefAttribute = context.Compilation.GetTypeByMetadataName(ByRefAttribute);
if (byRefAttribute == null)
return;
var isByRefEventType = eventArgument
.GetAttributes()
.Any(attribute => attribute.AttributeClass?.Equals(byRefAttribute, Default) ?? false);
var parameterIsRef = eventParameter.RefKind == RefKind.Ref;
if (isByRefEventType != parameterIsRef)
{
var descriptor = isByRefEventType ? ByRefEventSubscribedByValueRule : ByValueEventSubscribedByRefRule;
var diagnostic = Diagnostic.Create(descriptor, operation.Syntax.GetLocation(), eventArgument);
context.ReportDiagnostic(diagnostic);
}
}
private void CheckEventRaise(OperationAnalysisContext context)
{
if (context.Operation is not IInvocationOperation operation)
return;
var raiseMethods = context.Compilation
.GetTypeByMetadataName("Robust.Shared.GameObjects.EntitySystem")?
.GetMembers()
.Where(m => m.Name.Contains("RaiseLocalEvent") && m.Kind == SymbolKind.Method)
.Cast<IMethodSymbol>();
var busRaiseMethods = context.Compilation
.GetTypeByMetadataName("Robust.Shared.GameObjects.EntityEventBus")?
.GetMembers()
.Where(m => m.Name.Contains("RaiseLocalEvent") && m.Kind == SymbolKind.Method)
.Cast<IMethodSymbol>();
if (raiseMethods == null)
return;
if (busRaiseMethods != null)
raiseMethods = raiseMethods.Concat(busRaiseMethods);
if (!raiseMethods.Any(m => m.Equals(operation.TargetMethod.OriginalDefinition, Default)))
{
// If you try to do this normally by concatenating like busRaiseMethods above

View File

@@ -5,9 +5,6 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
using Robust.Shared.Serialization.Manager.Definition;
using Robust.Shared.ViewVariables;
namespace Robust.Analyzers;
@@ -17,84 +14,49 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
private const string DataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataDefinitionAttribute";
private const string ImplicitDataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.ImplicitDataDefinitionForInheritorsAttribute";
private const string DataFieldBaseNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataFieldBaseAttribute";
private const string ViewVariablesNamespace = "Robust.Shared.ViewVariables.ViewVariablesAttribute";
private const string NotYamlSerializableName = "Robust.Shared.Serialization.Manager.Attributes.NotYamlSerializableAttribute";
private const string DataFieldAttributeName = "DataField";
private const string ViewVariablesAttributeName = "ViewVariables";
public static readonly DiagnosticDescriptor DataDefinitionPartialRule = new(
private static readonly DiagnosticDescriptor DataDefinitionPartialRule = new(
Diagnostics.IdDataDefinitionPartial,
"Type must be partial",
"Type {0} is a DataDefinition but is not partial",
"Type {0} is a DataDefinition but is not partial.",
"Usage",
DiagnosticSeverity.Error,
true,
"Make sure to mark any type that is a data definition as partial."
);
public static readonly DiagnosticDescriptor NestedDataDefinitionPartialRule = new(
private static readonly DiagnosticDescriptor NestedDataDefinitionPartialRule = new(
Diagnostics.IdNestedDataDefinitionPartial,
"Type must be partial",
"Type {0} contains nested data definition {1} but is not partial",
"Type {0} contains nested data definition {1} but is not partial.",
"Usage",
DiagnosticSeverity.Error,
true,
"Make sure to mark any type containing a nested data definition as partial."
);
public static readonly DiagnosticDescriptor DataFieldWritableRule = new(
private static readonly DiagnosticDescriptor DataFieldWritableRule = new(
Diagnostics.IdDataFieldWritable,
"Data field must not be readonly",
"Data field {0} in data definition {1} is readonly",
"Data field {0} in data definition {1} is readonly.",
"Usage",
DiagnosticSeverity.Error,
true,
"Make sure to remove the readonly modifier."
);
public static readonly DiagnosticDescriptor DataFieldPropertyWritableRule = new(
private static readonly DiagnosticDescriptor DataFieldPropertyWritableRule = new(
Diagnostics.IdDataFieldPropertyWritable,
"Data field property must have a setter",
"Data field property {0} in data definition {1} does not have a setter",
"Data field property {0} in data definition {1} does not have a setter.",
"Usage",
DiagnosticSeverity.Error,
true,
"Make sure to add a setter."
);
public static readonly DiagnosticDescriptor DataFieldRedundantTagRule = new(
Diagnostics.IdDataFieldRedundantTag,
"Data field has redundant tag specified",
"Data field {0} in data definition {1} has an explicitly set tag that matches autogenerated tag",
"Usage",
DiagnosticSeverity.Info,
true,
"Make sure to remove the tag string from the data field attribute."
);
public static readonly DiagnosticDescriptor DataFieldNoVVReadWriteRule = new(
Diagnostics.IdDataFieldNoVVReadWrite,
"Data field has VV ReadWrite",
"Data field {0} in data definition {1} has ViewVariables attribute with ReadWrite access, which is redundant",
"Usage",
DiagnosticSeverity.Info,
true,
"Make sure to remove the ViewVariables attribute."
);
public static readonly DiagnosticDescriptor DataFieldYamlSerializableRule = new(
Diagnostics.IdDataFieldYamlSerializable,
"Data field type is not YAML serializable",
"Data field {0} in data definition {1} is type {2}, which is not YAML serializable",
"Usage",
DiagnosticSeverity.Error,
true,
"Make sure to use a type that is YAML serializable."
);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
DataDefinitionPartialRule, NestedDataDefinitionPartialRule, DataFieldWritableRule, DataFieldPropertyWritableRule,
DataFieldRedundantTagRule, DataFieldNoVVReadWriteRule, DataFieldYamlSerializableRule
DataDefinitionPartialRule, NestedDataDefinitionPartialRule, DataFieldWritableRule, DataFieldPropertyWritableRule
);
public override void Initialize(AnalysisContext context)
@@ -102,31 +64,23 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSymbolStartAction(symbolContext =>
{
if (symbolContext.Symbol is not INamedTypeSymbol typeSymbol)
return;
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.ClassDeclaration);
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.StructDeclaration);
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.RecordDeclaration);
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.RecordStructDeclaration);
context.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.InterfaceDeclaration);
if (!IsDataDefinition(typeSymbol))
return;
symbolContext.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.ClassDeclaration);
symbolContext.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.StructDeclaration);
symbolContext.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.RecordDeclaration);
symbolContext.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.RecordStructDeclaration);
symbolContext.RegisterSyntaxNodeAction(AnalyzeDataDefinition, SyntaxKind.InterfaceDeclaration);
symbolContext.RegisterSyntaxNodeAction(AnalyzeDataField, SyntaxKind.FieldDeclaration);
symbolContext.RegisterSyntaxNodeAction(AnalyzeDataFieldProperty, SyntaxKind.PropertyDeclaration);
}, SymbolKind.NamedType);
context.RegisterSyntaxNodeAction(AnalyzeDataField, SyntaxKind.FieldDeclaration);
context.RegisterSyntaxNodeAction(AnalyzeDataFieldProperty, SyntaxKind.PropertyDeclaration);
}
private static void AnalyzeDataDefinition(SyntaxNodeAnalysisContext context)
private void AnalyzeDataDefinition(SyntaxNodeAnalysisContext context)
{
if (context.Node is not TypeDeclarationSyntax declaration)
return;
if (context.ContainingSymbol is not INamedTypeSymbol type)
var type = context.SemanticModel.GetDeclaredSymbol(declaration)!;
if (!IsDataDefinition(type))
return;
if (!IsPartial(declaration))
@@ -137,7 +91,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
var containingType = type.ContainingType;
while (containingType != null)
{
var containingTypeDeclaration = (TypeDeclarationSyntax)containingType.DeclaringSyntaxReferences[0].GetSyntax();
var containingTypeDeclaration = (TypeDeclarationSyntax) containingType.DeclaringSyntaxReferences[0].GetSyntax();
if (!IsPartial(containingTypeDeclaration))
{
context.ReportDiagnostic(Diagnostic.Create(NestedDataDefinitionPartialRule, containingTypeDeclaration.Keyword.GetLocation(), containingType.Name, type.Name));
@@ -147,115 +101,60 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
}
}
private static void AnalyzeDataField(SyntaxNodeAnalysisContext context)
private void AnalyzeDataField(SyntaxNodeAnalysisContext context)
{
if (context.Node is not FieldDeclarationSyntax field)
return;
if (context.ContainingSymbol?.ContainingType is not INamedTypeSymbol type)
var typeDeclaration = field.FirstAncestorOrSelf<TypeDeclarationSyntax>();
if (typeDeclaration == null)
return;
var type = context.SemanticModel.GetDeclaredSymbol(typeDeclaration)!;
if (!IsDataDefinition(type))
return;
foreach (var variable in field.Declaration.Variables)
{
var fieldSymbol = context.SemanticModel.GetDeclaredSymbol(variable);
if (fieldSymbol == null)
continue;
if (!IsDataField(fieldSymbol, out _, out var datafieldAttribute))
continue;
if (IsReadOnlyDataField(type, fieldSymbol))
{
TryGetModifierLocation(field, SyntaxKind.ReadOnlyKeyword, out var location);
context.ReportDiagnostic(Diagnostic.Create(DataFieldWritableRule, location, fieldSymbol.Name, type.Name));
}
if (HasRedundantTag(fieldSymbol, datafieldAttribute))
{
TryGetAttributeLocation(field, DataFieldAttributeName, out var location);
context.ReportDiagnostic(Diagnostic.Create(DataFieldRedundantTagRule, location, fieldSymbol.Name, type.Name));
}
if (HasVVReadWrite(fieldSymbol))
{
TryGetAttributeLocation(field, ViewVariablesAttributeName, out var location);
context.ReportDiagnostic(Diagnostic.Create(DataFieldNoVVReadWriteRule, location, fieldSymbol.Name, type.Name));
}
if (context.SemanticModel.GetSymbolInfo(field.Declaration.Type).Symbol is not ITypeSymbol fieldTypeSymbol)
continue;
fieldTypeSymbol = TypeSymbolHelper.GetNullableUnderlyingTypeOrSelf(fieldTypeSymbol);
if (IsNotYamlSerializable(fieldSymbol, fieldTypeSymbol))
{
context.ReportDiagnostic(Diagnostic.Create(DataFieldYamlSerializableRule,
(context.Node as FieldDeclarationSyntax)?.Declaration.Type.GetLocation(),
fieldSymbol.Name,
type.Name,
fieldTypeSymbol.MetadataName
));
context.ReportDiagnostic(Diagnostic.Create(DataFieldWritableRule, context.Node.GetLocation(), fieldSymbol.Name, type.Name));
}
}
}
private static void AnalyzeDataFieldProperty(SyntaxNodeAnalysisContext context)
private void AnalyzeDataFieldProperty(SyntaxNodeAnalysisContext context)
{
if (context.Node is not PropertyDeclarationSyntax property)
return;
if (context.ContainingSymbol is not IPropertySymbol propertySymbol)
var typeDeclaration = property.FirstAncestorOrSelf<TypeDeclarationSyntax>();
if (typeDeclaration == null)
return;
if (propertySymbol.ContainingType is not INamedTypeSymbol type)
return;
if (type.IsRecord || type.IsValueType)
var type = context.SemanticModel.GetDeclaredSymbol(typeDeclaration)!;
if (!IsDataDefinition(type) || type.IsRecord || type.IsValueType)
return;
var propertySymbol = context.SemanticModel.GetDeclaredSymbol(property);
if (propertySymbol == null)
return;
if (!IsDataField(propertySymbol, out _, out var datafieldAttribute))
return;
if (IsReadOnlyDataField(type, propertySymbol))
{
var location = property.AccessorList != null ? property.AccessorList.GetLocation() : property.GetLocation();
context.ReportDiagnostic(Diagnostic.Create(DataFieldPropertyWritableRule, location, propertySymbol.Name, type.Name));
}
if (HasRedundantTag(propertySymbol, datafieldAttribute))
{
TryGetAttributeLocation(property, DataFieldAttributeName, out var location);
context.ReportDiagnostic(Diagnostic.Create(DataFieldRedundantTagRule, location, propertySymbol.Name, type.Name));
}
if (HasVVReadWrite(propertySymbol))
{
TryGetAttributeLocation(property, ViewVariablesAttributeName, out var location);
context.ReportDiagnostic(Diagnostic.Create(DataFieldNoVVReadWriteRule, location, propertySymbol.Name, type.Name));
}
if (context.SemanticModel.GetSymbolInfo(property.Type).Symbol is not ITypeSymbol propertyTypeSymbol)
return;
propertyTypeSymbol = TypeSymbolHelper.GetNullableUnderlyingTypeOrSelf(propertyTypeSymbol);
if (IsNotYamlSerializable(propertySymbol, propertyTypeSymbol))
{
context.ReportDiagnostic(Diagnostic.Create(DataFieldYamlSerializableRule,
(context.Node as PropertyDeclarationSyntax)?.Type.GetLocation(),
propertySymbol.Name,
type.Name,
propertyTypeSymbol.Name
));
context.ReportDiagnostic(Diagnostic.Create(DataFieldPropertyWritableRule, context.Node.GetLocation(), propertySymbol.Name, type.Name));
}
}
private static bool IsReadOnlyDataField(ITypeSymbol type, ISymbol field)
{
if (!IsDataField(field, out _, out _))
return false;
return IsReadOnlyMember(type, field);
}
@@ -317,38 +216,6 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
return false;
}
private static bool TryGetAttributeLocation(MemberDeclarationSyntax syntax, string attributeName, out Location location)
{
foreach (var attributeList in syntax.AttributeLists)
{
foreach (var attribute in attributeList.Attributes)
{
if (attribute.Name.ToString() != attributeName)
continue;
location = attribute.GetLocation();
return true;
}
}
// Default to the declaration syntax's location
location = syntax.GetLocation();
return false;
}
private static bool TryGetModifierLocation(MemberDeclarationSyntax syntax, SyntaxKind modifierKind, out Location location)
{
foreach (var modifier in syntax.Modifiers)
{
if (modifier.IsKind(modifierKind))
{
location = modifier.GetLocation();
return true;
}
}
location = syntax.GetLocation();
return false;
}
private static bool IsReadOnlyMember(ITypeSymbol type, ISymbol member)
{
if (member is IFieldSymbol field)
@@ -380,56 +247,6 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
return false;
}
private static bool HasRedundantTag(ISymbol symbol, AttributeData datafieldAttribute)
{
// No args, no problem
if (datafieldAttribute.ConstructorArguments.Length == 0)
return false;
// If a tag is explicitly specified, it will be the first argument...
var tagArgument = datafieldAttribute.ConstructorArguments[0];
// ...but the first arg could also something else, since tag is optional
// so we make sure that it's a string
if (tagArgument.Value is not string explicitName)
return false;
// Get the name that sourcegen would provide
var automaticName = DataDefinitionUtility.AutoGenerateTag(symbol.Name);
// If the explicit name matches the sourcegen name, we have a redundancy
return explicitName == automaticName;
}
private static bool HasVVReadWrite(ISymbol symbol)
{
// Make sure it has ViewVariablesAttribute
AttributeData? viewVariablesAttribute = null;
foreach (var attr in symbol.GetAttributes())
{
if (attr.AttributeClass?.ToDisplayString() == ViewVariablesNamespace)
{
viewVariablesAttribute = attr;
}
}
if (viewVariablesAttribute == null)
return false;
// Default is ReadOnly, which is fine
if (viewVariablesAttribute.ConstructorArguments.Length == 0)
return false;
var accessArgument = viewVariablesAttribute.ConstructorArguments[0];
if (accessArgument.Value is not byte accessByte)
return false;
return (VVAccess)accessByte == VVAccess.ReadWrite;
}
private static bool IsNotYamlSerializable(ISymbol field, ITypeSymbol type)
{
return HasAttribute(type, NotYamlSerializableName);
}
private static bool IsImplicitDataDefinition(ITypeSymbol type)
{
if (HasAttribute(type, ImplicitDataDefinitionNamespace))

View File

@@ -1,25 +1,23 @@
#nullable enable
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxKind;
using static Robust.Roslyn.Shared.Diagnostics;
using static Robust.Analyzers.Diagnostics;
namespace Robust.Analyzers;
[ExportCodeFixProvider(LanguageNames.CSharp)]
public sealed class DefinitionFixer : CodeFixProvider
{
private const string DataFieldAttributeName = "DataField";
private const string ViewVariablesAttributeName = "ViewVariables";
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(
IdDataDefinitionPartial, IdNestedDataDefinitionPartial, IdDataFieldWritable, IdDataFieldPropertyWritable,
IdDataFieldRedundantTag, IdDataFieldNoVVReadWrite
IdDataDefinitionPartial, IdNestedDataDefinitionPartial, IdDataFieldWritable, IdDataFieldPropertyWritable
);
public override Task RegisterCodeFixesAsync(CodeFixContext context)
@@ -36,10 +34,6 @@ public sealed class DefinitionFixer : CodeFixProvider
return RegisterDataFieldFix(context, diagnostic);
case IdDataFieldPropertyWritable:
return RegisterDataFieldPropertyFix(context, diagnostic);
case IdDataFieldRedundantTag:
return RegisterRedundantTagFix(context, diagnostic);
case IdDataFieldNoVVReadWrite:
return RegisterVVReadWriteFix(context, diagnostic);
}
}
@@ -78,110 +72,6 @@ public sealed class DefinitionFixer : CodeFixProvider
return document.WithSyntaxRoot(root);
}
private static async Task RegisterRedundantTagFix(CodeFixContext context, Diagnostic diagnostic)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
var span = diagnostic.Location.SourceSpan;
var token = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<MemberDeclarationSyntax>().First();
if (token == null)
return;
// Find the DataField attribute
AttributeSyntax? dataFieldAttribute = null;
foreach (var attributeList in token.AttributeLists)
{
foreach (var attribute in attributeList.Attributes)
{
if (attribute.Name.ToString() == DataFieldAttributeName)
{
dataFieldAttribute = attribute;
break;
}
}
if (dataFieldAttribute != null)
break;
}
if (dataFieldAttribute == null)
return;
context.RegisterCodeFix(CodeAction.Create(
"Remove explicitly set tag",
c => RemoveRedundantTag(context.Document, dataFieldAttribute, c),
"Remove explicitly set tag"
), diagnostic);
}
private static async Task<Document> RemoveRedundantTag(Document document, AttributeSyntax syntax, CancellationToken cancellation)
{
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
if (syntax.ArgumentList == null)
return document;
AttributeSyntax? newSyntax;
if (syntax.ArgumentList.Arguments.Count == 1)
{
// If this is the only argument, delete the ArgumentList so we don't leave empty parentheses
newSyntax = syntax.RemoveNode(syntax.ArgumentList, SyntaxRemoveOptions.KeepNoTrivia);
}
else
{
// Remove the first argument, which is the tag
var newArgs = syntax.ArgumentList.Arguments.RemoveAt(0);
var newArgList = syntax.ArgumentList.WithArguments(newArgs);
// Construct a new attribute with the tag removed
newSyntax = syntax.WithArgumentList(newArgList);
}
root = root!.ReplaceNode(syntax, newSyntax!);
return document.WithSyntaxRoot(root);
}
private static async Task RegisterVVReadWriteFix(CodeFixContext context, Diagnostic diagnostic)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
var span = diagnostic.Location.SourceSpan;
var token = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<MemberDeclarationSyntax>().First();
if (token == null)
return;
context.RegisterCodeFix(CodeAction.Create(
"Remove ViewVariables attribute",
c => RemoveVVAttribute(context.Document, token, c),
"Remove ViewVariables attribute"
), diagnostic);
}
private static async Task<Document> RemoveVVAttribute(Document document, MemberDeclarationSyntax syntax, CancellationToken cancellation)
{
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
var newLists = new SyntaxList<AttributeListSyntax>();
foreach (var attributeList in syntax.AttributeLists)
{
var attributes = new SeparatedSyntaxList<AttributeSyntax>();
foreach (var attribute in attributeList.Attributes)
{
if (attribute.Name.ToString() != ViewVariablesAttributeName)
{
attributes = attributes.Add(attribute);
}
}
// Don't add empty lists []
if (attributes.Count > 0)
newLists = newLists.Add(attributeList.WithAttributes(attributes));
}
var newSyntax = syntax.WithAttributeLists(newLists);
root = root!.ReplaceNode(syntax, newSyntax);
return document.WithSyntaxRoot(root);
}
private static async Task RegisterDataFieldFix(CodeFixContext context, Diagnostic diagnostic)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);

View File

@@ -1,61 +0,0 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class DependencyAssignAnalyzer : DiagnosticAnalyzer
{
private const string DependencyAttributeType = "Robust.Shared.IoC.DependencyAttribute";
private static readonly DiagnosticDescriptor Rule = new (
Diagnostics.IdDependencyFieldAssigned,
"Assignment to dependency field",
"Tried to assign to [Dependency] field '{0}'. Remove [Dependency] or inject it via field injection instead.",
"Usage",
DiagnosticSeverity.Warning,
true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.RegisterOperationAction(CheckAssignment, OperationKind.SimpleAssignment);
}
private static void CheckAssignment(OperationAnalysisContext context)
{
if (context.Operation is not ISimpleAssignmentOperation assignment)
return;
if (assignment.Target is not IFieldReferenceOperation fieldRef)
return;
var field = fieldRef.Field;
var attributes = field.GetAttributes();
if (attributes.Length == 0)
return;
var depAttribute = context.Compilation.GetTypeByMetadataName(DependencyAttributeType);
if (!HasAttribute(attributes, depAttribute))
return;
context.ReportDiagnostic(Diagnostic.Create(Rule, assignment.Syntax.GetLocation(), field.Name));
}
private static bool HasAttribute(ImmutableArray<AttributeData> attributes, ISymbol symbol)
{
foreach (var attribute in attributes)
{
if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, symbol))
return true;
}
return false;
}
}

View File

@@ -1,6 +1,6 @@
using Microsoft.CodeAnalysis;
namespace Robust.Roslyn.Shared;
namespace Robust.Analyzers;
public static class Diagnostics
{
@@ -18,33 +18,13 @@ public static class Diagnostics
public const string IdInvalidNotNullableFlagType = "RA0011";
public const string IdNotNullableFlagValueType = "RA0012";
public const string IdByRefEventSubscribedByValue = "RA0013";
public const string IdValueEventSubscribedByRef = "RA0014";
public const string IdByRefEventRaisedByValue = "RA0015";
public const string IdValueEventRaisedByRef = "RA0016";
public const string IdDataDefinitionPartial = "RA0017";
public const string IdNestedDataDefinitionPartial = "RA0018";
public const string IdDataFieldWritable = "RA0019";
public const string IdDataFieldPropertyWritable = "RA0020";
public const string IdComponentPauseNotComponent = "RA0021";
public const string IdComponentPauseNoFields = "RA0022";
public const string IdComponentPauseNoParentAttribute = "RA0023";
public const string IdComponentPauseWrongTypeAttribute = "RA0024";
public const string IdDependencyFieldAssigned = "RA0025";
public const string IdUncachedRegex = "RA0026";
public const string IdDataFieldRedundantTag = "RA0027";
public const string IdMustCallBase = "RA0028";
public const string IdDataFieldNoVVReadWrite = "RA0029";
public const string IdUseNonGenericVariant = "RA0030";
public const string IdPreferOtherType = "RA0031";
public const string IdDuplicateDependency = "RA0032";
public const string IdForbidLiteral = "RA0033";
public const string IdObsoleteInheritance = "RA0034";
public const string IdObsoleteInheritanceWithMessage = "RA0035";
public const string IdDataFieldYamlSerializable = "RA0036";
public const string IdPrototypeNetSerializable = "RA0037";
public const string IdPrototypeSerializable = "RA0038";
public const string IdPrototypeInstantiation = "RA0039";
public const string IdAutoGenStateAttributeMissing = "RA0040";
public const string IdAutoGenStateParamMissing = "RA0041";
public static SuppressionDescriptor MeansImplicitAssignment =>
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");

View File

@@ -1,126 +0,0 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
#nullable enable
/// <summary>
/// Analyzer that detects duplicate <c>[Dependency]</c> fields inside a single type.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class DuplicateDependencyAnalyzer : DiagnosticAnalyzer
{
private const string DependencyAttributeType = "Robust.Shared.IoC.DependencyAttribute";
private static readonly DiagnosticDescriptor Rule = new(
Diagnostics.IdDuplicateDependency,
"Duplicate dependency field",
"Another [Dependency] field of type '{0}' already exists in this type with field '{1}'",
"Usage",
DiagnosticSeverity.Warning,
true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(compilationContext =>
{
var dependencyAttributeType = compilationContext.Compilation.GetTypeByMetadataName(DependencyAttributeType);
if (dependencyAttributeType == null)
return;
compilationContext.RegisterSymbolStartAction(symbolContext =>
{
var typeSymbol = (INamedTypeSymbol)symbolContext.Symbol;
// Only deal with non-static classes, doesn't make sense to have dependencies in anything else.
if (typeSymbol.TypeKind != TypeKind.Class || typeSymbol.IsStatic)
return;
var state = new AnalyzerState(dependencyAttributeType);
symbolContext.RegisterSyntaxNodeAction(state.AnalyzeField, SyntaxKind.FieldDeclaration);
symbolContext.RegisterSymbolEndAction(state.End);
},
SymbolKind.NamedType);
});
}
private sealed class AnalyzerState(INamedTypeSymbol dependencyAttributeType)
{
private readonly Dictionary<ITypeSymbol, List<IFieldSymbol>> _dependencyFields = new(SymbolEqualityComparer.Default);
public void AnalyzeField(SyntaxNodeAnalysisContext context)
{
var field = (FieldDeclarationSyntax)context.Node;
if (field.AttributeLists.Count == 0)
return;
if (context.ContainingSymbol is not IFieldSymbol fieldSymbol)
return;
// Can't have [Dependency]s for non-reference types.
if (!fieldSymbol.Type.IsReferenceType)
return;
if (!IsDependency(context.ContainingSymbol))
return;
lock (_dependencyFields)
{
if (!_dependencyFields.TryGetValue(fieldSymbol.Type, out var dependencyFields))
{
dependencyFields = [];
_dependencyFields.Add(fieldSymbol.Type, dependencyFields);
}
dependencyFields.Add(fieldSymbol);
}
}
private bool IsDependency(ISymbol symbol)
{
foreach (var attributeData in symbol.GetAttributes())
{
if (SymbolEqualityComparer.Default.Equals(attributeData.AttributeClass, dependencyAttributeType))
return true;
}
return false;
}
public void End(SymbolAnalysisContext context)
{
lock (_dependencyFields)
{
foreach (var pair in _dependencyFields)
{
var fieldType = pair.Key;
var fields = pair.Value;
if (fields.Count <= 1)
continue;
// Sort so we can have deterministic order to skip reporting for a single field.
// Whichever sorts first doesn't get reported.
fields.Sort(static (a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
// Start at index 1 to skip first field.
var firstField = fields[0];
for (var i = 1; i < fields.Count; i++)
{
var field = fields[i];
context.ReportDiagnostic(
Diagnostic.Create(Rule, field.Locations[0], fieldType.ToDisplayString(), firstField.Name));
}
}
}
}
}
}

View File

@@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
using Document = Microsoft.CodeAnalysis.Document;
namespace Robust.Analyzers

View File

@@ -5,7 +5,6 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;

View File

@@ -1,101 +0,0 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class ForbidLiteralAnalyzer : DiagnosticAnalyzer
{
private const string ForbidLiteralType = "Robust.Shared.Analyzers.ForbidLiteralAttribute";
public static DiagnosticDescriptor ForbidLiteralRule = new(
Diagnostics.IdForbidLiteral,
"Parameter forbids literal values",
"The {0} parameter of {1} forbids literal values",
"Usage",
DiagnosticSeverity.Warning,
true,
"Pass in a validated wrapper type like ProtoId, or a const or static value."
);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [ForbidLiteralRule];
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();
context.RegisterOperationAction(AnalyzeOperation, OperationKind.Invocation);
}
private void AnalyzeOperation(OperationAnalysisContext context)
{
if (context.Operation is not IInvocationOperation invocationOperation)
return;
// Check each parameter of the method invocation
foreach (var argumentOperation in invocationOperation.Arguments)
{
// Check for our attribute on the parameter
if (!AttributeHelper.HasAttribute(argumentOperation.Parameter, ForbidLiteralType, out _))
continue;
// Handle parameters using the params keyword
if (argumentOperation.Syntax is InvocationExpressionSyntax subExpressionSyntax)
{
// Check each param value
foreach (var subArgument in subExpressionSyntax.ArgumentList.Arguments)
{
CheckArgumentSyntax(context, argumentOperation, subArgument);
}
continue;
}
// Not params, so just check the single parameter
if (argumentOperation.Syntax is not ArgumentSyntax argumentSyntax)
continue;
CheckArgumentSyntax(context, argumentOperation, argumentSyntax);
}
}
private void CheckArgumentSyntax(OperationAnalysisContext context, IArgumentOperation operation, ArgumentSyntax argumentSyntax)
{
// Handle collection types
if (argumentSyntax.Expression is CollectionExpressionSyntax collectionExpressionSyntax)
{
// Check each value of the collection
foreach (var elementSyntax in collectionExpressionSyntax.Elements)
{
if (elementSyntax is not ExpressionElementSyntax expressionSyntax)
continue;
// Check if a literal was passed in
if (expressionSyntax.Expression is not LiteralExpressionSyntax)
continue;
context.ReportDiagnostic(Diagnostic.Create(ForbidLiteralRule,
expressionSyntax.GetLocation(),
operation.Parameter.Name,
(context.Operation as IInvocationOperation).TargetMethod.Name
));
}
return;
}
// Not a collection, just a single value to check
// Check if it's a literal
if (argumentSyntax.Expression is not LiteralExpressionSyntax)
return;
context.ReportDiagnostic(Diagnostic.Create(ForbidLiteralRule,
argumentSyntax.GetLocation(),
operation.Parameter.Name,
(context.Operation as IInvocationOperation).TargetMethod.Name
));
}
}

View File

@@ -2,7 +2,6 @@ using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers
{

View File

@@ -1,111 +0,0 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
#nullable enable
/// <summary>
/// Enforces <c>MustCallBaseAttribute</c>.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class MustCallBaseAnalyzer : DiagnosticAnalyzer
{
private const string Attribute = "Robust.Shared.Analyzers.MustCallBaseAttribute";
private static readonly DiagnosticDescriptor Rule = new(
Diagnostics.IdMustCallBase,
"No base call in overriden function",
"Overriders of this function must always call the base function",
"Usage",
DiagnosticSeverity.Warning,
isEnabledByDefault: true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method);
}
private static void AnalyzeSymbol(SymbolAnalysisContext context)
{
if (context.Symbol is not IMethodSymbol { IsOverride: true } method)
return;
var attrSymbol = context.Compilation.GetTypeByMetadataName(Attribute);
if (attrSymbol == null)
return;
if (DoesMethodOverriderHaveAttribute(method, attrSymbol) is not { } data)
return;
if (data is { onlyOverrides: true, depth: < 2 })
return;
var syntax = (MethodDeclarationSyntax) method.DeclaringSyntaxReferences[0].GetSyntax();
if (HasBaseCall(syntax))
return;
var diag = Diagnostic.Create(Rule, syntax.Identifier.GetLocation());
context.ReportDiagnostic(diag);
}
private static (int depth, bool onlyOverrides)? DoesMethodOverriderHaveAttribute(
IMethodSymbol method,
INamedTypeSymbol attributeSymbol)
{
var depth = 0;
while (method.OverriddenMethod != null)
{
depth += 1;
method = method.OverriddenMethod;
if (GetAttribute(method, attributeSymbol) is not { } attribute)
continue;
var onlyOverrides = attribute.ConstructorArguments is [{Kind: TypedConstantKind.Primitive, Value: true}];
return (depth, onlyOverrides);
}
return null;
}
private static bool HasBaseCall(MethodDeclarationSyntax syntax)
{
return syntax.Accept(new BaseCallLocator());
}
private static AttributeData? GetAttribute(ISymbol namedTypeSymbol, INamedTypeSymbol attrSymbol)
{
return namedTypeSymbol.GetAttributes()
.SingleOrDefault(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol));
}
private sealed class BaseCallLocator : CSharpSyntaxVisitor<bool>
{
public override bool VisitBaseExpression(BaseExpressionSyntax node)
{
return true;
}
public override bool DefaultVisit(SyntaxNode node)
{
foreach (var childNode in node.ChildNodes())
{
if (childNode is not CSharpSyntaxNode cSharpSyntax)
continue;
if (cSharpSyntax.Accept(this))
return true;
}
return false;
}
}
}

View File

@@ -1,66 +0,0 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class NoUncachedRegexAnalyzer : DiagnosticAnalyzer
{
private const string RegexTypeName = "Regex";
private const string RegexType = $"System.Text.RegularExpressions.{RegexTypeName}";
private static readonly DiagnosticDescriptor Rule = new (
Diagnostics.IdUncachedRegex,
"Use of uncached static Regex function",
"Usage of a static Regex function that takes in a pattern string. This can cause constant re-parsing of the pattern.",
"Usage",
DiagnosticSeverity.Warning,
true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public static readonly HashSet<string> BadFunctions =
[
"Count",
"EnumerateMatches",
"IsMatch",
"Match",
"Matches",
"Replace",
"Split"
];
public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.RegisterOperationAction(CheckInvocation, OperationKind.Invocation);
}
private static void CheckInvocation(OperationAnalysisContext context)
{
if (context.Operation is not IInvocationOperation invocation)
return;
// All Regex functions we care about are static.
var targetMethod = invocation.TargetMethod;
if (!targetMethod.IsStatic)
return;
// Bail early.
if (targetMethod.ContainingType.Name != "Regex")
return;
var regexType = context.Compilation.GetTypeByMetadataName(RegexType);
if (!SymbolEqualityComparer.Default.Equals(regexType, targetMethod.ContainingType))
return;
if (!BadFunctions.Contains(targetMethod.Name))
return;
context.ReportDiagnostic(Diagnostic.Create(Rule, invocation.Syntax.GetLocation()));
}
}

View File

@@ -3,7 +3,6 @@ using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
@@ -32,7 +31,7 @@ public sealed class NotNullableFlagAnalyzer : DiagnosticAnalyzer
private static readonly DiagnosticDescriptor InvalidNotNullableImplementationRule = new (
Diagnostics.IdInvalidNotNullableFlagImplementation,
"Invalid NotNullable flag implementation",
"Invalid NotNullable flag implementation.",
"NotNullable flag is either not typed as bool, or does not have a default value equaling false",
"Usage",
DiagnosticSeverity.Error,
@@ -42,7 +41,7 @@ public sealed class NotNullableFlagAnalyzer : DiagnosticAnalyzer
private static readonly DiagnosticDescriptor InvalidNotNullableTypeRule = new (
Diagnostics.IdInvalidNotNullableFlagType,
"Failed to resolve type parameter",
"Failed to resolve type parameter \"{0}\"",
"Failed to resolve type parameter \"{0}\".",
"Usage",
DiagnosticSeverity.Error,
true,
@@ -50,7 +49,7 @@ public sealed class NotNullableFlagAnalyzer : DiagnosticAnalyzer
private static readonly DiagnosticDescriptor NotNullableFlagValueTypeRule = new (
Diagnostics.IdNotNullableFlagValueType,
"NotNullable flag not supported for value types",
"NotNullable flag not supported for value types.",
"Value types as generic arguments are not supported for NotNullable flags",
"Usage",
DiagnosticSeverity.Error,

View File

@@ -1,75 +0,0 @@
#nullable enable
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class ObsoleteInheritanceAnalyzer : DiagnosticAnalyzer
{
private const string Attribute = "Robust.Shared.Analyzers.ObsoleteInheritanceAttribute";
public static readonly DiagnosticDescriptor Rule = new(
Diagnostics.IdObsoleteInheritance,
"Parent type has obsoleted inheritance",
"Type '{0}' inherits from '{1}', which has obsoleted inheriting from itself",
"Usage",
DiagnosticSeverity.Warning,
true);
public static readonly DiagnosticDescriptor RuleWithMessage = new(
Diagnostics.IdObsoleteInheritanceWithMessage,
"Parent type has obsoleted inheritance",
"Type '{0}' inherits from '{1}', which has obsoleted inheriting from itself: \"{2}\"",
"Usage",
DiagnosticSeverity.Warning,
true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Rule, RuleWithMessage];
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSymbolAction(CheckClass, SymbolKind.NamedType);
}
private static void CheckClass(SymbolAnalysisContext context)
{
if (context.Symbol is not INamedTypeSymbol typeSymbol)
return;
if (typeSymbol.IsValueType || typeSymbol.BaseType is not { } baseType)
return;
if (!AttributeHelper.HasAttribute(baseType, Attribute, out var data))
return;
var location = context.Symbol.Locations[0];
if (GetMessageFromAttributeData(data) is { } message)
{
context.ReportDiagnostic(Diagnostic.Create(
RuleWithMessage,
location,
[typeSymbol.Name, baseType.Name, message]));
}
else
{
context.ReportDiagnostic(Diagnostic.Create(
Rule,
location,
[typeSymbol.Name, baseType.Name]));
}
}
private static string? GetMessageFromAttributeData(AttributeData data)
{
if (data.ConstructorArguments is not [var message, ..])
return null;
return message.Value as string;
}
}

View File

@@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;

View File

@@ -1,65 +0,0 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class PreferNonGenericVariantForAnalyzer : DiagnosticAnalyzer
{
private const string AttributeType = "Robust.Shared.Analyzers.PreferNonGenericVariantForAttribute";
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
UseNonGenericVariantDescriptor
);
private static readonly DiagnosticDescriptor UseNonGenericVariantDescriptor = new(
Diagnostics.IdUseNonGenericVariant,
"Consider using the non-generic variant of this method",
"Use the non-generic variant of this method for type {0}",
"Usage",
DiagnosticSeverity.Warning,
true,
"Use the generic variant of this method.");
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics | GeneratedCodeAnalysisFlags.Analyze);
context.EnableConcurrentExecution();
context.RegisterOperationAction(CheckForNonGenericVariant, OperationKind.Invocation);
}
private void CheckForNonGenericVariant(OperationAnalysisContext obj)
{
if (obj.Operation is not IInvocationOperation invocationOperation) return;
var preferNonGenericAttribute = obj.Compilation.GetTypeByMetadataName(AttributeType);
HashSet<ITypeSymbol> forTypes = [];
foreach (var attribute in invocationOperation.TargetMethod.GetAttributes())
{
if (!SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, preferNonGenericAttribute))
continue;
foreach (var type in attribute.ConstructorArguments[0].Values)
forTypes.Add((ITypeSymbol)type.Value);
break;
}
if (forTypes == null)
return;
foreach (var typeArg in invocationOperation.TargetMethod.TypeArguments)
{
if (forTypes.Contains(typeArg))
{
obj.ReportDiagnostic(
Diagnostic.Create(UseNonGenericVariantDescriptor,
invocationOperation.Syntax.GetLocation(), typeArg.Name));
}
}
}
}

View File

@@ -1,75 +0,0 @@
#nullable enable
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class PreferOtherTypeAnalyzer : DiagnosticAnalyzer
{
private const string AttributeType = "Robust.Shared.Analyzers.PreferOtherTypeAttribute";
private static readonly DiagnosticDescriptor PreferOtherTypeDescriptor = new(
Diagnostics.IdPreferOtherType,
"Use the specific type",
"Use the specific type {0} instead of {1} when the type argument is {2}",
"Usage",
DiagnosticSeverity.Error,
true,
"Use the specific type.");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
PreferOtherTypeDescriptor
);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics | GeneratedCodeAnalysisFlags.Analyze);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeField, SyntaxKind.VariableDeclaration);
}
private void AnalyzeField(SyntaxNodeAnalysisContext context)
{
if (context.Node is not VariableDeclarationSyntax node)
return;
// Get the type of the generic being used
if (node.Type is not GenericNameSyntax genericName)
return;
var genericSyntax = genericName.TypeArgumentList.Arguments[0];
if (context.SemanticModel.GetSymbolInfo(genericSyntax).Symbol is not { } genericType)
return;
// Look for the PreferOtherTypeAttribute
var symbolInfo = context.SemanticModel.GetSymbolInfo(node.Type);
if (symbolInfo.Symbol?.GetAttributes() is not { } attributes)
return;
var preferOtherTypeAttribute = context.Compilation.GetTypeByMetadataName(AttributeType);
foreach (var attribute in attributes)
{
if (!SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, preferOtherTypeAttribute))
continue;
// See if the generic type argument matches the type the attribute specifies
if (attribute.ConstructorArguments[0].Value is not ITypeSymbol checkedType)
return;
if (!SymbolEqualityComparer.Default.Equals(checkedType, genericType))
continue;
if (attribute.ConstructorArguments[1].Value is not ITypeSymbol replacementType)
continue;
context.ReportDiagnostic(Diagnostic.Create(PreferOtherTypeDescriptor,
context.Node.GetLocation(),
replacementType.Name,
symbolInfo.Symbol.Name,
genericType.Name));
}
}
}

View File

@@ -1,97 +0,0 @@
#nullable enable
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Robust.Roslyn.Shared.Diagnostics;
namespace Robust.Analyzers;
[ExportCodeFixProvider(LanguageNames.CSharp)]
public sealed class PreferOtherTypeFixer : CodeFixProvider
{
private const string PreferOtherTypeAttributeName = "PreferOtherTypeAttribute";
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(
IdPreferOtherType
);
public override FixAllProvider GetFixAllProvider()
{
return WellKnownFixAllProviders.BatchFixer;
}
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
foreach (var diagnostic in context.Diagnostics)
{
switch (diagnostic.Id)
{
case IdPreferOtherType:
return RegisterReplaceType(context, diagnostic);
}
}
return Task.CompletedTask;
}
private static async Task RegisterReplaceType(CodeFixContext context, Diagnostic diagnostic)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
var span = diagnostic.Location.SourceSpan;
var token = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<VariableDeclarationSyntax>().First();
if (token == null)
return;
context.RegisterCodeFix(CodeAction.Create(
"Replace type",
c => ReplaceType(context.Document, token, c),
"Replace type"
), diagnostic);
}
private static async Task<Document> ReplaceType(Document document, VariableDeclarationSyntax syntax, CancellationToken cancellation)
{
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
var model = await document.GetSemanticModelAsync(cancellation);
if (model == null)
return document;
if (syntax.Type is not GenericNameSyntax genericNameSyntax)
return document;
var genericTypeSyntax = genericNameSyntax.TypeArgumentList.Arguments[0];
if (model.GetSymbolInfo(genericTypeSyntax).Symbol is not {} genericTypeSymbol)
return document;
var symbolInfo = model.GetSymbolInfo(syntax.Type);
if (symbolInfo.Symbol?.GetAttributes() is not { } attributes)
return document;
foreach (var attribute in attributes)
{
if (attribute.AttributeClass?.Name != PreferOtherTypeAttributeName)
continue;
if (attribute.ConstructorArguments[0].Value is not ITypeSymbol checkedTypeSymbol)
continue;
if (!SymbolEqualityComparer.Default.Equals(checkedTypeSymbol, genericTypeSymbol))
continue;
if (attribute.ConstructorArguments[1].Value is not ITypeSymbol replacementTypeSymbol)
continue;
var replacementIdentifier = SyntaxFactory.IdentifierName(replacementTypeSymbol.Name);
var replacementSyntax = syntax.WithType(replacementIdentifier);
root = root!.ReplaceNode(syntax, replacementSyntax);
return document.WithSyntaxRoot(root);
}
return document;
}
}

View File

@@ -1,48 +0,0 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class PrototypeInstantiationAnalyzer : DiagnosticAnalyzer
{
private const string PrototypeInterfaceType = "Robust.Shared.Prototypes.IPrototype";
public static readonly DiagnosticDescriptor Rule = new(
Diagnostics.IdPrototypeInstantiation,
"Do not instantiate prototypes directly",
"Do not instantiate prototypes directly. Prototypes should always be instantiated by the prototype manager.",
"Usage",
DiagnosticSeverity.Warning,
true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Rule];
public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.RegisterCompilationStartAction(static ctx =>
{
var prototypeInterface = ctx.Compilation.GetTypeByMetadataName(PrototypeInterfaceType);
if (prototypeInterface == null)
return;
ctx.RegisterOperationAction(symContext => Check(prototypeInterface, symContext), OperationKind.ObjectCreation);
});
}
private static void Check(INamedTypeSymbol prototypeInterface, OperationAnalysisContext ctx)
{
if (ctx.Operation is not IObjectCreationOperation { Type: { } resultType } creationOp)
return;
if (!TypeSymbolHelper.ImplementsInterface(resultType, prototypeInterface))
return;
ctx.ReportDiagnostic(Diagnostic.Create(Rule, creationOp.Syntax.GetLocation()));
}
}

View File

@@ -1,76 +0,0 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class PrototypeNetSerializableAnalyzer : DiagnosticAnalyzer
{
private const string PrototypeInterfaceType = "Robust.Shared.Prototypes.IPrototype";
private const string NetSerializableAttributeType = "Robust.Shared.Serialization.NetSerializableAttribute";
public static readonly DiagnosticDescriptor RuleNetSerializable = new(
Diagnostics.IdPrototypeNetSerializable,
"Prototypes should not be [NetSerializable]",
"Type {0} is a prototype and marked as [NetSerializable]. Prototypes should not be directly sent over the network, send their IDs instead.",
"Usage",
DiagnosticSeverity.Warning,
true);
public static readonly DiagnosticDescriptor RuleSerializable = new(
Diagnostics.IdPrototypeSerializable,
"Prototypes should not be [Serializable]",
"Type {0} is a prototype and marked as [Serializable]. Prototypes should not be directly sent over the network, send their IDs instead.",
"Usage",
DiagnosticSeverity.Warning,
true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [
RuleNetSerializable,
RuleSerializable
];
public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.RegisterCompilationStartAction(static ctx =>
{
var prototypeInterface = ctx.Compilation.GetTypeByMetadataName(PrototypeInterfaceType);
var netSerializableAttribute = ctx.Compilation.GetTypeByMetadataName(NetSerializableAttributeType);
if (prototypeInterface == null || netSerializableAttribute == null)
return;
ctx.RegisterSymbolAction(symbolContext => CheckClass(prototypeInterface, netSerializableAttribute, symbolContext), SymbolKind.NamedType);
});
}
private static void CheckClass(
INamedTypeSymbol prototypeInterface,
INamedTypeSymbol netSerializableAttribute,
SymbolAnalysisContext symbolContext)
{
if (symbolContext.Symbol is not INamedTypeSymbol symbol)
return;
if (!TypeSymbolHelper.ImplementsInterface(symbol, prototypeInterface))
return;
if (AttributeHelper.HasAttribute(symbol, netSerializableAttribute, out _))
{
symbolContext.ReportDiagnostic(
Diagnostic.Create(RuleNetSerializable, symbol.Locations[0], symbol.ToDisplayString()));
}
if (symbol.IsSerializable)
{
symbolContext.ReportDiagnostic(
Diagnostic.Create(RuleSerializable, symbol.Locations[0], symbol.ToDisplayString()));
}
}
}

View File

@@ -1,47 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10</LangVersion>
</PropertyGroup>
<ItemGroup>
<!-- Needed for NotNullableFlagAnalyzer. -->
<Compile Include="..\Robust.Shared\Analyzers\NotNullableFlagAttribute.cs" LinkBase="Implementations" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.0.1" />
</ItemGroup>
<ItemGroup>
<!-- Needed for NotNullableFlagAnalyzer. -->
<Compile Include="..\Robust.Shared\Analyzers\NotNullableFlagAttribute.cs" />
</ItemGroup>
<ItemGroup>
<!-- Needed for FriendAnalyzer. -->
<Compile Include="..\Robust.Shared\Analyzers\AccessAttribute.cs" LinkBase="Implementations" />
<Compile Include="..\Robust.Shared\Analyzers\AccessPermissions.cs" LinkBase="Implementations" />
<Compile Include="..\Robust.Shared\Analyzers\AccessAttribute.cs" />
<Compile Include="..\Robust.Shared\Analyzers\AccessPermissions.cs" />
</ItemGroup>
<ItemGroup>
<!-- Needed for PreferGenericVariantAnalyzer. -->
<Compile Include="..\Robust.Shared\Analyzers\PreferGenericVariantAttribute.cs" LinkBase="Implementations" />
<Compile Include="..\Robust.Shared\Analyzers\PreferGenericVariantAttribute.cs" />
</ItemGroup>
<ItemGroup>
<!-- Needed for PreferNonGenericVariantAnalyzer. -->
<Compile Include="..\Robust.Shared\Analyzers\PreferNonGenericVariantForAttribute.cs" LinkBase="Implementations" />
</ItemGroup>
<ItemGroup>
<!-- Needed for PreferOtherTypeAnalyzer. -->
<Compile Include="..\Robust.Shared\Analyzers\PreferOtherTypeAttribute.cs" LinkBase="Implementations" />
</ItemGroup>
<ItemGroup>
<!-- Needed for DataDefinitionAnalyzer. -->
<Compile Include="..\Robust.Shared\Serialization\Manager\Definition\DataDefinitionUtility.cs" LinkBase="Implementations" />
<Compile Include="..\Robust.Shared\ViewVariables\ViewVariablesAttribute.cs" LinkBase="Implementations" />
<Compile Include="..\Robust.Shared\Serialization\NetSerializableAttribute.cs" LinkBase="Implementations" />
</ItemGroup>
<Import Project="../Robust.Roslyn.Shared/Robust.Roslyn.Shared.props" />
<PropertyGroup>
<Nullable>disable</Nullable>
<!--
Rider seems to get really confused with hot reload if we directly compile in the above-linked classes.
As such, they have an #if to change their namespace in this project.
-->
<DefineConstants>$(DefineConstants);ROBUST_ANALYZERS_IMPL</DefineConstants>
</PropertyGroup>
</Project>

View File

@@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers
{

View File

@@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Robust.Roslyn.Shared;
namespace Robust.Analyzers;

View File

@@ -1,83 +0,0 @@
using System;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using Robust.Shared.Analyzers;
using Robust.Shared.Collections;
namespace Robust.Benchmarks.Collections;
[Virtual]
public class ValueListEnumerationBenchmarks
{
[Params(4, 16, 64)]
public int N { get; set; }
private sealed class Data(int i)
{
public readonly int I = i;
}
private ValueList<Data> _valueList;
private Data[] _array = default!;
[GlobalSetup]
public void Setup()
{
var list = new List<Data>(N);
for (var i = 0; i < N; i++)
{
list.Add(new(i));
}
_array = list.ToArray();
_valueList = new(list.ToArray());
}
[Benchmark]
public int ValueList()
{
var total = 0;
foreach (var ev in _valueList)
{
total += ev.I;
}
return total;
}
[Benchmark]
public int ValueListSpan()
{
var total = 0;
foreach (var ev in _valueList.Span)
{
total += ev.I;
}
return total;
}
[Benchmark]
public int Array()
{
var total = 0;
foreach (var ev in _array)
{
total += ev.I;
}
return total;
}
[Benchmark]
public int Span()
{
var total = 0;
foreach (var ev in _array.AsSpan())
{
total += ev.I;
}
return total;
}
}

View File

@@ -1,18 +1,15 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Globalization;
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.EventProcessors;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Filters;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Order;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Validators;
using Robust.Benchmarks.Exporters;
@@ -26,8 +23,7 @@ public sealed class DefaultSQLConfig : IConfig
public IEnumerable<IExporter> GetExporters()
{
//yield return SQLExporter.Default;
yield break;
yield return SQLExporter.Default;
}
public IEnumerable<IColumnProvider> GetColumnProviders() => DefaultConfig.Instance.GetColumnProviders();
@@ -48,16 +44,10 @@ public sealed class DefaultSQLConfig : IConfig
public IEnumerable<BenchmarkLogicalGroupRule> GetLogicalGroupRules() => DefaultConfig.Instance.GetLogicalGroupRules();
public IEnumerable<EventProcessor> GetEventProcessors() => DefaultConfig.Instance.GetEventProcessors();
public IEnumerable<IColumnHidingRule> GetColumnHidingRules() => DefaultConfig.Instance.GetColumnHidingRules();
public IOrderer Orderer => DefaultConfig.Instance.Orderer!;
public ICategoryDiscoverer? CategoryDiscoverer => DefaultConfig.Instance.CategoryDiscoverer;
public SummaryStyle SummaryStyle => DefaultConfig.Instance.SummaryStyle;
public ConfigUnionRule UnionRule => DefaultConfig.Instance.UnionRule;
public string ArtifactsPath => DefaultConfig.Instance.ArtifactsPath;
public CultureInfo CultureInfo => DefaultConfig.Instance.CultureInfo!;
public ConfigOptions Options => DefaultConfig.Instance.Options;
public TimeSpan BuildTimeout => DefaultConfig.Instance.BuildTimeout;
public IReadOnlyList<Conclusion> ConfigAnalysisConclusion => DefaultConfig.Instance.ConfigAnalysisConclusion;
}

View File

@@ -26,8 +26,9 @@ public partial class AddRemoveComponentBenchmark
.InitializeInstance();
_entityManager = _simulation.Resolve<IEntityManager>();
var map = _simulation.CreateMap().Uid;
var coords = new EntityCoordinates(map, default);
var coords = new MapCoordinates(0, 0, new MapId(1));
_simulation.AddMap(coords.MapId);
for (var i = 0; i < N; i++)
{

View File

@@ -29,8 +29,8 @@ public partial class ComponentIteratorBenchmark
Comps = new A[N+2];
var map = _simulation.CreateMap().MapId;
var coords = new MapCoordinates(default, map);
var coords = new MapCoordinates(0, 0, new MapId(1));
_simulation.AddMap(coords.MapId);
for (var i = 0; i < N; i++)
{

View File

@@ -31,8 +31,8 @@ public partial class GetComponentBenchmark
Comps = new A[N+2];
var map = _simulation.CreateMap().Uid;
var coords = new EntityCoordinates(map, default);
var coords = new MapCoordinates(0, 0, new MapId(1));
_simulation.AddMap(coords.MapId);
for (var i = 0; i < N; i++)
{

View File

@@ -1,96 +0,0 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Engines;
using JetBrains.Annotations;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.UnitTesting.Server;
namespace Robust.Benchmarks.EntityManager;
[Virtual]
public partial class HasComponentBenchmark
{
private static readonly Consumer Consumer = new();
private ISimulation _simulation = default!;
private IEntityManager _entityManager = default!;
private ComponentRegistration _compReg = default!;
private A _dummyA = new();
[UsedImplicitly]
[Params(1, 10, 100, 1000)]
public int N;
[GlobalSetup]
public void GlobalSetup()
{
_simulation = RobustServerSimulation
.NewSimulation()
.RegisterComponents(f => f.RegisterClass<A>())
.InitializeInstance();
_entityManager = _simulation.Resolve<IEntityManager>();
var map = _simulation.CreateMap().Uid;
var coords = new EntityCoordinates(map, default);
_compReg = _entityManager.ComponentFactory.GetRegistration(typeof(A));
for (var i = 0; i < N; i++)
{
var uid = _entityManager.SpawnEntity(null, coords);
_entityManager.AddComponent<A>(uid);
}
}
[Benchmark]
public void HasComponentGeneric()
{
for (var i = 2; i <= N+1; i++)
{
var uid = new EntityUid(i);
var result = _entityManager.HasComponent<A>(uid);
Consumer.Consume(result);
}
}
[Benchmark]
public void HasComponentCompReg()
{
for (var i = 2; i <= N+1; i++)
{
var uid = new EntityUid(i);
var result = _entityManager.HasComponent(uid, _compReg);
Consumer.Consume(result);
}
}
[Benchmark]
public void HasComponentType()
{
for (var i = 2; i <= N+1; i++)
{
var uid = new EntityUid(i);
var result = _entityManager.HasComponent(uid, typeof(A));
Consumer.Consume(result);
}
}
[Benchmark]
public void HasComponentGetType()
{
for (var i = 2; i <= N+1; i++)
{
var uid = new EntityUid(i);
var type = _dummyA.GetType();
var result = _entityManager.HasComponent(uid, type);
Consumer.Consume(result);
}
}
[ComponentProtoName("A")]
public sealed partial class A : Component
{
}
}

View File

@@ -29,9 +29,10 @@ public partial class SpawnDeleteEntityBenchmark
.InitializeInstance();
_entityManager = _simulation.Resolve<IEntityManager>();
var (map, mapId) = _simulation.CreateMap();
_mapCoords = new MapCoordinates(default, mapId);
_entCoords = new EntityCoordinates(map, 0, 0);
_mapCoords = new MapCoordinates(0, 0, new MapId(1));
var uid = _simulation.AddMap(_mapCoords.MapId);
_entCoords = new EntityCoordinates(uid, 0, 0);
}
[Benchmark(Baseline = true)]

View File

@@ -15,10 +15,11 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Npgsql;
using Npgsql.Internal;
using Npgsql.Internal.TypeHandlers;
using Npgsql.Internal.TypeHandling;
namespace Robust.Benchmarks.Exporters;
/*
public sealed class SQLExporter : IExporter
{
private static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions
@@ -97,9 +98,7 @@ public sealed class SQLExporter : IExporter
public string Name => "sql";
}
*/
/*
// https://github.com/npgsql/efcore.pg/issues/1107#issuecomment-945126627
class JsonOverrideTypeHandlerResolverFactory : TypeHandlerResolverFactory
{
@@ -139,7 +138,6 @@ class JsonOverrideTypeHandlerResolverFactory : TypeHandlerResolverFactory
=> null; // Let the built-in resolver do this
}
}
*/
public sealed class DesignTimeContextFactoryPostgres : IDesignTimeDbContextFactory<BenchmarkContext>
{

View File

@@ -1,96 +0,0 @@
using System;
using System.Numerics;
using BenchmarkDotNet.Attributes;
using Microsoft.Extensions.Configuration;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Systems;
using Robust.UnitTesting.Server;
namespace Robust.Benchmarks.Physics;
[Virtual]
[MediumRunJob]
public class PhysicsBoxStackBenchmark
{
private ISimulation _sim = default!;
[GlobalSetup]
public void Setup()
{
_sim = RobustServerSimulation.NewSimulation().InitializeInstance();
var entManager = _sim.Resolve<IEntityManager>();
entManager.System<SharedMapSystem>().CreateMap(out var mapId);
SetupTumbler(entManager, mapId);
for (var i = 0; i < 30; i++)
{
entManager.TickUpdate(0.016f, false);
}
}
[Benchmark]
public void BoxStack()
{
var entManager = _sim.Resolve<IEntityManager>();
for (var i = 0; i < 10000; i++)
{
entManager.TickUpdate(0.016f, false);
}
}
private void SetupTumbler(IEntityManager entManager, MapId mapId)
{
var physics = entManager.System<SharedPhysicsSystem>();
var fixtures = entManager.System<FixtureSystem>();
var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0, 0, mapId));
var ground = entManager.AddComponent<PhysicsComponent>(groundUid);
var horizontal = new EdgeShape(new Vector2(-40, 0), new Vector2(40, 0));
fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground);
var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10));
fixtures.CreateFixture(groundUid, "fix2", new Fixture(vertical, 2, 2, true), body: ground);
var xs = new[]
{
0.0f, -10.0f, -5.0f, 5.0f, 10.0f
};
var columnCount = 1;
var rowCount = 15;
PolygonShape shape;
for (var j = 0; j < columnCount; j++)
{
for (var i = 0; i < rowCount; i++)
{
var x = 0.0f;
var boxUid = entManager.SpawnEntity(null,
new MapCoordinates(new Vector2(xs[j] + x, 0.55f + 1.1f * i), mapId));
var box = entManager.AddComponent<PhysicsComponent>(boxUid);
physics.SetBodyType(boxUid, BodyType.Dynamic, body: box);
shape = new PolygonShape();
shape.SetAsBox(0.5f, 0.5f);
physics.SetFixedRotation(boxUid, false, body: box);
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true), body: box);
physics.WakeBody(boxUid, body: box);
physics.SetSleepingAllowed(boxUid, box, false);
}
}
physics.WakeBody(groundUid, body: ground);
}
}

View File

@@ -1,92 +0,0 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Systems;
using Robust.UnitTesting.Server;
namespace Robust.Benchmarks.Physics;
[Virtual]
public class PhysicsCircleStackBenchmark
{
private ISimulation _sim = default!;
[GlobalSetup]
public void Setup()
{
_sim = RobustServerSimulation.NewSimulation().InitializeInstance();
var entManager = _sim.Resolve<IEntityManager>();
entManager.System<SharedMapSystem>().CreateMap(out var mapId);
SetupTumbler(entManager, mapId);
for (var i = 0; i < 30; i++)
{
entManager.TickUpdate(0.016f, false);
}
}
[Benchmark]
public void CircleStack()
{
var entManager = _sim.Resolve<IEntityManager>();
for (var i = 0; i < 10000; i++)
{
entManager.TickUpdate(0.016f, false);
}
}
private void SetupTumbler(IEntityManager entManager, MapId mapId)
{
var physics = entManager.System<SharedPhysicsSystem>();
var fixtures = entManager.System<FixtureSystem>();
var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0, 0, mapId));
var ground = entManager.AddComponent<PhysicsComponent>(groundUid);
var horizontal = new EdgeShape(new Vector2(-40, 0), new Vector2(40, 0));
fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground);
var vertical = new EdgeShape(new Vector2(20, 0), new Vector2(20, 20));
fixtures.CreateFixture(groundUid, "fix2", new Fixture(vertical, 2, 2, true), body: ground);
var xs = new[]
{
0.0f, -10.0f, -5.0f, 5.0f, 10.0f
};
var columnCount = 1;
var rowCount = 15;
PhysShapeCircle shape;
for (var j = 0; j < columnCount; j++)
{
for (var i = 0; i < rowCount; i++)
{
var x = 0.0f;
var boxUid = entManager.SpawnEntity(null,
new MapCoordinates(new Vector2(xs[j] + x, 0.55f + 2.1f * i), mapId));
var box = entManager.AddComponent<PhysicsComponent>(boxUid);
physics.SetBodyType(boxUid, BodyType.Dynamic, body: box);
shape = new PhysShapeCircle(0.5f);
physics.SetFixedRotation(boxUid, false, body: box);
// TODO: Need to detect shape and work out if we need to use fixedrotation
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f));
physics.WakeBody(boxUid, body: box);
physics.SetSleepingAllowed(boxUid, box, false);
}
}
physics.WakeBody(groundUid, body: ground);
}
}

View File

@@ -1,91 +0,0 @@
using System;
using System.Numerics;
using BenchmarkDotNet.Attributes;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Systems;
using Robust.UnitTesting.Server;
namespace Robust.Benchmarks.Physics;
[Virtual]
public class PhysicsPyramidBenchmark
{
private ISimulation _sim = default!;
[GlobalSetup]
public void Setup()
{
_sim = RobustServerSimulation.NewSimulation().InitializeInstance();
var entManager = _sim.Resolve<IEntityManager>();
entManager.System<SharedMapSystem>().CreateMap(out var mapId);
SetupTumbler(entManager, mapId);
for (var i = 0; i < 300; i++)
{
entManager.TickUpdate(0.016f, false);
}
}
[Benchmark]
public void Pyramid()
{
var entManager = _sim.Resolve<IEntityManager>();
for (var i = 0; i < 5000; i++)
{
entManager.TickUpdate(0.016f, false);
}
}
private void SetupTumbler(IEntityManager entManager, MapId mapId)
{
const byte count = 20;
// Setup ground
var physics = entManager.System<SharedPhysicsSystem>();
var fixtures = entManager.System<FixtureSystem>();
var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0, 0, mapId));
var ground = entManager.AddComponent<PhysicsComponent>(groundUid);
var horizontal = new EdgeShape(new Vector2(40, 0), new Vector2(-40, 0));
fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground);
physics.WakeBody(groundUid, body: ground);
// Setup boxes
float a = 0.5f;
PolygonShape shape = new();
shape.SetAsBox(a, a);
var x = new Vector2(-7.0f, 0.75f);
Vector2 y;
Vector2 deltaX = new Vector2(0.5625f, 1.25f);
Vector2 deltaY = new Vector2(1.125f, 0.0f);
for (var i = 0; i < count; ++i)
{
y = x;
for (var j = i; j < count; ++j)
{
var boxUid = entManager.SpawnEntity(null, new MapCoordinates(y, mapId));
var box = entManager.AddComponent<PhysicsComponent>(boxUid);
physics.SetBodyType(boxUid, BodyType.Dynamic, body: box);
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f), body: box);
y += deltaY;
physics.WakeBody(boxUid, body: box);
physics.SetSleepingAllowed(boxUid, box, false);
}
x += deltaX;
}
}
}

View File

@@ -1,108 +0,0 @@
using System;
using System.Numerics;
using BenchmarkDotNet.Attributes;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Systems;
using Robust.UnitTesting.Server;
namespace Robust.Benchmarks.Physics;
[Virtual]
public class PhysicsTumblerBenchmark
{
private ISimulation _sim = default!;
[GlobalSetup]
public void Setup()
{
_sim = RobustServerSimulation.NewSimulation().InitializeInstance();
var entManager = _sim.Resolve<IEntityManager>();
var physics = entManager.System<SharedPhysicsSystem>();
var fixtures = entManager.System<FixtureSystem>();
var mapUid = entManager.System<SharedMapSystem>().CreateMap(out var mapId);
SetupTumbler(entManager, mapId);
for (var i = 0; i < 300; i++)
{
entManager.TickUpdate(0.016f, false);
var boxUid = entManager.SpawnEntity(null, new MapCoordinates(0f, 10f, mapId));
var box = entManager.AddComponent<PhysicsComponent>(boxUid);
physics.SetBodyType(boxUid, BodyType.Dynamic, body: box);
physics.SetFixedRotation(boxUid, false, body: box);
var shape = new PolygonShape();
shape.SetAsBox(0.125f, 0.125f);
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 0.0625f), body: box);
physics.WakeBody(boxUid, body: box);
physics.SetSleepingAllowed(boxUid, box, false);
}
if (entManager.TryGetComponent(mapUid, out BroadphaseComponent? mapBroadphase))
entManager.System<SharedBroadphaseSystem>().RebuildBottomUp(mapBroadphase);
}
[Benchmark]
public void Tumbler()
{
var entManager = _sim.Resolve<IEntityManager>();
for (var i = 0; i < 1000; i++)
{
entManager.TickUpdate(0.016f, false);
}
}
private void SetupTumbler(IEntityManager entManager, MapId mapId)
{
var physics = entManager.System<SharedPhysicsSystem>();
var fixtures = entManager.System<FixtureSystem>();
var joints = entManager.System<SharedJointSystem>();
var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0f, 0f, mapId));
var ground = entManager.AddComponent<PhysicsComponent>(groundUid);
// Due to lookup changes fixtureless bodies are invalid, so
var cShape = new PhysShapeCircle(1f);
fixtures.CreateFixture(groundUid, "fix1", new Fixture(cShape, 0, 0, false));
var bodyUid = entManager.SpawnEntity(null, new MapCoordinates(0f, 10f, mapId));
var body = entManager.AddComponent<PhysicsComponent>(bodyUid);
physics.SetBodyType(bodyUid, BodyType.Dynamic, body: body);
physics.SetSleepingAllowed(bodyUid, body, false);
physics.SetFixedRotation(bodyUid, false, body: body);
// TODO: Box2D just deref, bleh shape structs someday
var shape1 = new PolygonShape();
shape1.SetAsBox(0.5f, 10.0f, new Vector2(10.0f, 0.0f), 0.0f);
fixtures.CreateFixture(bodyUid, "fix1", new Fixture(shape1, 2, 0, true, 20f));
var shape2 = new PolygonShape();
shape2.SetAsBox(0.5f, 10.0f, new Vector2(-10.0f, 0.0f), 0f);
fixtures.CreateFixture(bodyUid, "fix2", new Fixture(shape2, 2, 0, true, 20f));
var shape3 = new PolygonShape();
shape3.SetAsBox(10.0f, 0.5f, new Vector2(0.0f, 10.0f), 0f);
fixtures.CreateFixture(bodyUid, "fix3", new Fixture(shape3, 2, 0, true, 20f));
var shape4 = new PolygonShape();
shape4.SetAsBox(10.0f, 0.5f, new Vector2(0.0f, -10.0f), 0f);
fixtures.CreateFixture(bodyUid, "fix4", new Fixture(shape4, 2, 0, true, 20f));
physics.WakeBody(groundUid, body: ground);
physics.WakeBody(bodyUid, body: body);
var revolute = joints.CreateRevoluteJoint(groundUid, bodyUid);
revolute.LocalAnchorA = new Vector2(0f, 10f);
revolute.LocalAnchorB = new Vector2(0f, 0f);
revolute.ReferenceAngle = 0f;
revolute.MotorSpeed = 0.05f * MathF.PI;
revolute.MaxMotorTorque = 100000000f;
revolute.EnableMotor = true;
}
}

Some files were not shown because too many files have changed in this diff Show More