diff --git a/.editorconfig b/.editorconfig index 872a068c7c..370c3f9dee 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,6 +12,7 @@ tab_width = 4 #end_of_line = crlf insert_final_newline = true trim_trailing_whitespace = true +max_line_length = 120 #### .NET Coding Conventions #### @@ -71,7 +72,7 @@ csharp_style_expression_bodied_constructors = false:suggestion #csharp_style_expression_bodied_indexers = true:silent #csharp_style_expression_bodied_lambdas = true:silent #csharp_style_expression_bodied_local_functions = false:silent -csharp_style_expression_bodied_methods = false:suggestion +csharp_style_expression_bodied_methods = true:suggestion #csharp_style_expression_bodied_operators = false:silent csharp_style_expression_bodied_properties = true:suggestion @@ -336,7 +337,11 @@ dotnet_naming_symbols.type_parameters_symbols.applicable_kinds = type_parameter # ReSharper properties resharper_braces_for_ifelse = required_for_multiline +resharper_csharp_wrap_arguments_style = chop_if_long +resharper_csharp_wrap_parameters_style = chop_if_long resharper_keep_existing_attribute_arrangement = true +resharper_wrap_chained_binary_patterns = chop_if_long +resharper_wrap_chained_method_calls = chop_if_long [*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props}] indent_size = 2 diff --git a/.envrc b/.envrc index 5def8fd66a..7fd05db3e5 100644 --- a/.envrc +++ b/.envrc @@ -1,4 +1,4 @@ -if ! has nix_direnv_version || ! nix_direnv_version 2.3.0; then - source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.3.0/direnvrc" "sha256-Dmd+j63L84wuzgyjITIfSxSD57Tx7v51DMxVZOsiUD8=" +if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4=" fi use flake diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6a40012b4c..a50306f179 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,19 +4,19 @@ /Content.*/ @DEATHB4DEFEAT -/Content.*/SimpleStation14/ @Pspritechologist @DEATHB4DEFEAT +/Content.*/Parkstation/ @Pspritechologist @DEATHB4DEFEAT /Resources/ @DEATHB4DEFEAT -/Resources/Audio/SimpleStation14 @Pspritechologist @DEATHB4DEFEAT +/Resources/Audio/Parkstation @Pspritechologist @DEATHB4DEFEAT /Resources/Maps/ @Pspritechologist @DEATHB4DEFEAT /Resources/Prototypes/Maps @Pspritechologist @DEATHB4DEFEAT -/Resources/Prototypes/SimpleStation14/ @Pspritechologist @DEATHB4DEFEAT +/Resources/Prototypes/Parkstation/ @Pspritechologist @DEATHB4DEFEAT -/Resources/Textures/SimpleStation14/ @Pspritechologist @DEATHB4DEFEAT +/Resources/Textures/Parkstation/ @Pspritechologist @DEATHB4DEFEAT *.ftl @Pspritechologist @DEATHB4DEFEAT diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 09c9e76b19..0000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,7 +0,0 @@ -contact_links: - - name: Report a Security Vulnerability - url: https://github.com/space-wizards/space-station-14/blob/master/SECURITY.md - about: Please report security vulnerabilities privately so we can fix them before they are publicly disclosed. - - name: Request a Feature - url: https://discord.gg/rGvu9hKffJ - about: Submit feature requests on our Discord server (https://discord.gg/rGvu9hKffJ). diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 0000000000..aba549332f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,27 @@ +--- +name: Request a Feature +about: "Template for noting future planned features. Please ask for approval in the Discord if you aren't an organization Member before posting a feature request" +title: '' +labels: '' +assignees: '' + +--- + + + +# Description + + +I want Goldfish crackers + +# Media + + +![Example Screenshot](https://example.com/thisimageisntreal.png) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..51f9de8baf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,27 @@ +--- +name: Request a feature +about: "Please outline your request in Discord first if you aren't a maintainer." +title: '' +labels: ["Type: Feature"] +assignees: '' + +--- + + + +# Description + + +I want Goldfish crackers + +# Media + + +![Example Screenshot](https://example.com/thisimageisntreal.png) diff --git a/.github/ISSUE_TEMPLATE/issue_report.md b/.github/ISSUE_TEMPLATE/issue_report.md index f18221e055..c74f24554a 100644 --- a/.github/ISSUE_TEMPLATE/issue_report.md +++ b/.github/ISSUE_TEMPLATE/issue_report.md @@ -1,32 +1,55 @@ --- -name: Report an Issue -about: "..." +name: Report an issue +about: "Any issues found in gameplay or the codebase" title: '' -labels: '' +labels: 'Type: Bug' assignees: '' --- + + # Description +Description + # Reproduction -# Screenshots +1. Open game +2. Game doesn't open + +# Expected behavior +1. Open game +2. Game opens + +# Media + + +![Example Screenshot](https://example.com/thisimageisntreal.png) + # Additional context + +There's a ghost in my computer diff --git a/.github/ISSUE_TEMPLATE/toolshed-feature-request.md b/.github/ISSUE_TEMPLATE/toolshed-feature-request.md deleted file mode 100644 index dae84c3e25..0000000000 --- a/.github/ISSUE_TEMPLATE/toolshed-feature-request.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: Toolshed feature request -about: Suggest a feature for Toolshed (for game admins/developers) -title: "[TOOLSHED REQUEST]" -labels: Toolshed -assignees: moonheart08 - ---- - -**Is your feature request related to a problem/bug? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the command you'd like** -A clear and concise description of what you want and what it should do. -If you're a technical user (i.e. programmer) including type signatures is helpful. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index bb0ae5066a..5d3ce070bc 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,20 +1,46 @@ + + # Description + + Description. --- # TODO + + - [ ] Task - [x] Completed Task --- + +

Media

- +![Example Media Embed](https://example.com/thisimageisntreal.png)

@@ -23,10 +49,14 @@ Description. # Changelog - + :cl: -- add: Added fun! -- tweak: Tweaked fun! +- add: Added fun :D +- tweak: Tweaked fun - fix: Fixed fun! -- remove: Removed fun! +- remove: Removed fun :( diff --git a/.github/labeler.yml b/.github/labeler.yml index 694c83b602..4cfe775ed4 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,36 +1,56 @@ -"Changes: C#": - - "**/*.cs" +"Changes: Audio": + - changed-files: + - any-glob-to-any-file: "**/*.ogg" + +"Changes: C#": + - changed-files: + - any-glob-to-any-file: "**/*.cs" "Changes: Config": - - "**/*.toml" - - "**/*.config" - - "*.json" - - ".github/*.yml" - - ".github/*.json" - - ".vscode/*.json" - - ".editorconfig" +- changed-files: + - any-glob-to-any-file: + - "**/*.toml" + - "**/*.config" + - "*.json" + - ".github/*.yml" + - ".github/*.json" + - ".vscode/*.json" + - ".editorconfig" "Changes: Documentation": - - "**/*.xml" - - "**/*.md" + - changed-files: + - any-glob-to-any-file: + - "**/*.xml" + - "**/*.md" "Changes: Localization": -- 'Resources/Locale/**/*.ftl' + - changed-files: + - any-glob-to-any-file: 'Resources/Locale/**/*.ftl' "Changes: Map": - - "Resources/Maps/**/*.yml" - - "Resources/Prototypes/Maps/**/*.yml" + - changed-files: + - any-glob-to-any-file: + - "Resources/Maps/**/*.yml" + - "Resources/Prototypes/Maps/**/*.yml" "Changes: Sprite": - - "**/*.rsi/*.png" - - "**/*.rsi/*.json" + - changed-files: + - any-glob-to-any-file: + - "**/*.rsi/*.png" + - "**/*.rsi/*.json" "Changes: UI": - - "**/*.xaml*" + - changed-files: + - any-glob-to-any-file: "**/*.xaml*" "Changes: YML": - - any: ["**/*.yml"] - all: ["!Resources/Maps/**/*.yml", "!Resources/Prototypes/Maps/**/*.yml"] + - changed-files: + - any-glob-to-any-file: + - "**/*.yml" + - all-globs-to-all-files: + - "!Resources/Maps/**/*.yml" + - "!Resources/Prototypes/Maps/**/*.yml" "Changes: Workflow": - - ".github/workflows/*.yml" + - changed-files: + - any-glob-to-any-file: ".github/workflows/*.yml" diff --git a/.github/workflows/build-docfx.yml b/.github/workflows/build-docfx.yml index ca1a6f0af1..d37e37026d 100644 --- a/.github/workflows/build-docfx.yml +++ b/.github/workflows/build-docfx.yml @@ -21,7 +21,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v3.2.0 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.100 - name: Install dependencies run: dotnet restore diff --git a/.github/workflows/build-map-renderer.yml b/.github/workflows/build-map-renderer.yml index 01575f64b9..c1790feadb 100644 --- a/.github/workflows/build-map-renderer.yml +++ b/.github/workflows/build-map-renderer.yml @@ -10,7 +10,7 @@ on: jobs: build: - if: github.actor != 'PJBot' && github.event.pull_request.draft == false && github.actor != 'DeltaV-Bot' + if: github.actor != 'PJBot' && github.event.pull_request.draft == false && github.actor != 'DeltaV-Bot' && github.actor != 'SimpleStation14' strategy: matrix: os: [ubuntu-latest] @@ -36,7 +36,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v3.2.0 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.100 - name: Install dependencies run: dotnet restore diff --git a/.github/workflows/build-test-debug.yml b/.github/workflows/build-test-debug.yml index 519f5af6f4..bb0624e3ba 100644 --- a/.github/workflows/build-test-debug.yml +++ b/.github/workflows/build-test-debug.yml @@ -10,7 +10,7 @@ on: jobs: build: - if: github.actor != 'PJBot' && github.event.pull_request.draft == false && github.actor != 'DeltaV-Bot' + if: github.actor != 'PJBot' && github.event.pull_request.draft == false && github.actor != 'DeltaV-Bot' && github.actor != 'SimpleStation14' strategy: matrix: os: [ubuntu-latest] @@ -36,7 +36,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v3.2.0 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.100 - name: Install dependencies run: dotnet restore diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index a44cac2cd1..79cfdd83cd 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -16,41 +16,40 @@ jobs: permissions: contents: write steps: - - name: Checkout Master - uses: actions/checkout@v3 - with: - token: ${{ secrets.BOT_TOKEN }} - ref: "${{ vars.CHANGELOG_BRANCH }}" + - name: Checkout Master + uses: actions/checkout@v3 + with: + token: ${{ secrets.BOT_TOKEN }} + ref: ${{ vars.CHANGELOG_BRANCH }} - - name: Setup Git - run: | - git config --global user.name "${{ vars.CHANGELOG_USER }}" - git config --global user.email "${{ vars.CHANGELOG_EMAIL }}" - shell: bash + - name: Setup Git + run: | + git config --global user.name "${{ vars.CHANGELOG_USER }}" + git config --global user.email "${{ vars.CHANGELOG_EMAIL }}" + shell: bash - - name: Setup Node - uses: actions/setup-node@v3 - with: - node-version: 18.x + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18.x - - name: Install Dependencies - run: | - cd "Tools/changelog" - npm install - shell: bash - continue-on-error: true + - name: Install Dependencies + run: | + cd "Tools/changelogs" + npm install + shell: bash - - name: Generate Changelog - run: | - cd "Tools/changelog" - node changelog.js - shell: bash - continue-on-error: true + - name: Generate Changelog + run: | + cd "Tools/changelogs" + node changelog.js + shell: bash - - name: Commit Changelog - run: | - git add *.yml - git commit -m "${{ vars.CHANGELOG_MESSAGE }} (#${{ env.PR_NUMBER }})" - git push - shell: bash - continue-on-error: true + - name: Commit Changelog + run: | + git pull origin master + git add *.yml + git commit -m "${{ vars.CHANGELOG_MESSAGE }} (#${{ env.PR_NUMBER }})" + git push + shell: bash + continue-on-error: true diff --git a/.github/workflows/close-master-pr.yml b/.github/workflows/close-master-pr.yml index 66843d35dd..a74e838099 100644 --- a/.github/workflows/close-master-pr.yml +++ b/.github/workflows/close-master-pr.yml @@ -1,4 +1,4 @@ -name: Close PR's on master +name: Close PRs on master on: pull_request_target: @@ -12,7 +12,7 @@ jobs: steps: - uses: superbrothers/close-pull-request@v3 with: - comment: "Thank you for contributing to the Space Station 14 repository. Unfortunately, it looks like you submitted your pull request from the master branch. We suggest you follow [our git usage documentation](https://docs.spacestation14.com/en/general-development/setup/git-for-the-ss14-developer.html) \n\n You can move your current work from the master branch to another branch by doing `git branch Resources/Credits/GitHub.txt # TODO @@ -47,6 +49,6 @@ jobs: with: commit-message: Update Credits title: Update Credits - body: This is an automated Pull Request. This PR updates the github contributors in the credits section. - author: DeltaV-Bot + body: This is an automated Pull Request. This PR updates the GitHub contributors in the credits section. + author: ${{ vars.CHANGELOG_USER }} <${{ vars.CHANGELOG_EMAIL }}> branch: automated/credits-${{env.NOW}} diff --git a/.github/workflows/validate-rgas.yml b/.github/workflows/validate-rgas.yml index 0ed04021eb..72ce39ceff 100644 --- a/.github/workflows/validate-rgas.yml +++ b/.github/workflows/validate-rgas.yml @@ -9,7 +9,7 @@ on: jobs: yaml-schema-validation: name: YAML RGA schema validator - if: github.actor != 'PJBot' && github.event.pull_request.draft == false && github.actor != 'DeltaV-Bot' + if: github.actor != 'PJBot' && github.event.pull_request.draft == false && github.actor != 'DeltaV-Bot' && github.actor != 'SimpleStation14' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3.6.0 diff --git a/.github/workflows/validate_mapfiles.yml b/.github/workflows/validate_mapfiles.yml index 43d77841a8..190ee97d8f 100644 --- a/.github/workflows/validate_mapfiles.yml +++ b/.github/workflows/validate_mapfiles.yml @@ -9,7 +9,7 @@ on: jobs: yaml-schema-validation: name: YAML map schema validator - if: github.actor != 'PJBot' && github.event.pull_request.draft == false && github.actor != 'DeltaV-Bot' + if: github.actor != 'PJBot' && github.event.pull_request.draft == false && github.actor != 'DeltaV-Bot' && github.actor != 'SimpleStation14' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3.6.0 diff --git a/.github/workflows/yaml-linter.yml b/.github/workflows/yaml-linter.yml index 796795b234..8660ccc040 100644 --- a/.github/workflows/yaml-linter.yml +++ b/.github/workflows/yaml-linter.yml @@ -10,7 +10,7 @@ on: jobs: build: name: YAML Linter - if: github.actor != 'PJBot' && github.event.pull_request.draft == false && github.actor != 'DeltaV-Bot' + if: github.actor != 'PJBot' && github.event.pull_request.draft == false && github.actor != 'DeltaV-Bot' && github.actor != 'SimpleStation14' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3.6.0 @@ -26,7 +26,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v3.2.0 with: - dotnet-version: 8.0.x + dotnet-version: 8.0.100 - name: Install dependencies run: dotnet restore - name: Build diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 83bca6f97b..737b90563e 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,12 @@ { "recommendations": [ "ms-dotnettools.csharp", - "editorconfig.editorconfig" + "editorconfig.editorconfig", + "aaron-bond.better-comments", + "tamasfe.even-better-toml", + "slava0135.robust-yaml", + "slevesque.shader", + "macabeus.vscode-fluent", + "redhat.vscode-yaml" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 0e0d3ae890..dc6e26cbea 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,10 @@ { "omnisharp.analyzeOpenDocumentsOnly": true, - "dotnet.defaultSolution": "SpaceStation14.sln" + "dotnet.defaultSolution": "SpaceStation14.sln", + "json.schemas": [ + { + "fileMatch": [ "**/meta.json" ], + "url": "https://raw.githubusercontent.com/Simple-Station/Einstein-Engines/master/.github/rsi-schema.json" + } + ] } diff --git a/BuildChecker/BuildChecker.csproj b/BuildChecker/BuildChecker.csproj index 63d16fa970..d4f9a41254 100644 --- a/BuildChecker/BuildChecker.csproj +++ b/BuildChecker/BuildChecker.csproj @@ -14,8 +14,6 @@ https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild --> - python3 - py -3 {C899FCA4-7037-4E49-ABC2-44DE72487110} .NETFramework, Version=v4.7.2 false @@ -39,7 +37,7 @@ https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild bin\DebugOpt\ - + diff --git a/BuildChecker/git_helper.py b/BuildChecker/git_helper.py deleted file mode 100644 index becd4506e8..0000000000 --- a/BuildChecker/git_helper.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env python3 -# Installs git hooks, updates them, updates submodules, that kind of thing. - -import subprocess -import sys -import os -import shutil -from pathlib import Path -from typing import List - -SOLUTION_PATH = Path("..") / "SpaceStation14.sln" -# If this doesn't match the saved version we overwrite them all. -CURRENT_HOOKS_VERSION = "2" -QUIET = len(sys.argv) == 2 and sys.argv[1] == "--quiet" - - -def run_command(command: List[str], capture: bool = False) -> subprocess.CompletedProcess: - """ - Runs a command with pretty output. - """ - text = ' '.join(command) - if not QUIET: - print("$ {}".format(text)) - - sys.stdout.flush() - - completed = None - - if capture: - completed = subprocess.run(command, cwd="..", stdout=subprocess.PIPE) - else: - completed = subprocess.run(command, cwd="..") - - if completed.returncode != 0: - print("Error: command exited with code {}!".format(completed.returncode)) - - return completed - - -def update_submodules(): - """ - Updates all submodules. - """ - - if ('GITHUB_ACTIONS' in os.environ): - return - - if os.path.isfile("DISABLE_SUBMODULE_AUTOUPDATE"): - return - - if shutil.which("git") is None: - raise FileNotFoundError("git not found in PATH") - - # If the status doesn't match, force VS to reload the solution. - # status = run_command(["git", "submodule", "status"], capture=True) - run_command(["git", "submodule", "update", "--init", "--recursive"]) - # status2 = run_command(["git", "submodule", "status"], capture=True) - - # Something changed. - # if status.stdout != status2.stdout: - # print("Git submodules changed. Reloading solution.") - # reset_solution() - - -def install_hooks(): - """ - Installs the necessary git hooks into .git/hooks. - """ - - # Read version file. - if os.path.isfile("INSTALLED_HOOKS_VERSION"): - with open("INSTALLED_HOOKS_VERSION", "r") as f: - if f.read() == CURRENT_HOOKS_VERSION: - if not QUIET: - print("No hooks change detected.") - return - - with open("INSTALLED_HOOKS_VERSION", "w") as f: - f.write(CURRENT_HOOKS_VERSION) - - print("Hooks need updating.") - - hooks_target_dir = Path("..")/".git"/"hooks" - hooks_source_dir = Path("hooks") - - # Clear entire tree since we need to kill deleted files too. - for filename in os.listdir(str(hooks_target_dir)): - os.remove(str(hooks_target_dir/filename)) - - for filename in os.listdir(str(hooks_source_dir)): - print("Copying hook {}".format(filename)) - shutil.copy2(str(hooks_source_dir/filename), - str(hooks_target_dir/filename)) - - -def reset_solution(): - """ - Force VS to think the solution has been changed to prompt the user to reload it, thus fixing any load errors. - """ - - with SOLUTION_PATH.open("r") as f: - content = f.read() - - with SOLUTION_PATH.open("w") as f: - f.write(content) - - -if __name__ == '__main__': - install_hooks() - update_submodules() diff --git a/BuildChecker/hooks/post-checkout b/BuildChecker/hooks/post-checkout deleted file mode 100755 index c5662445c2..0000000000 --- a/BuildChecker/hooks/post-checkout +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -gitroot=`git rev-parse --show-toplevel` - -cd "$gitroot/BuildChecker" - -if [[ `uname` == MINGW* || `uname` == CYGWIN* ]]; then - # Windows - py -3 git_helper.py --quiet -else - # Not Windows, so probably some other Unix thing. - python3 git_helper.py --quiet -fi diff --git a/BuildChecker/hooks/post-merge b/BuildChecker/hooks/post-merge deleted file mode 100755 index 85fe61d966..0000000000 --- a/BuildChecker/hooks/post-merge +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -# Just call post-checkout since it does the same thing. -gitroot=`git rev-parse --show-toplevel` -bash "$gitroot/.git/hooks/post-checkout" diff --git a/Content.Benchmarks/ColorInterpolateBenchmark.cs b/Content.Benchmarks/ColorInterpolateBenchmark.cs index 2243bb4819..eb182328d4 100644 --- a/Content.Benchmarks/ColorInterpolateBenchmark.cs +++ b/Content.Benchmarks/ColorInterpolateBenchmark.cs @@ -131,8 +131,8 @@ public static Color InterpolateSysVector4(Color a, Color b, public static Color InterpolateSysVector4In(in Color endPoint1, in Color endPoint2, float lambda) { - ref var sva = ref Unsafe.As(ref Unsafe.AsRef(endPoint1)); - ref var svb = ref Unsafe.As(ref Unsafe.AsRef(endPoint2)); + ref var sva = ref Unsafe.As(ref Unsafe.AsRef(in endPoint1)); + ref var svb = ref Unsafe.As(ref Unsafe.AsRef(in endPoint2)); var res = SysVector4.Lerp(svb, sva, lambda); @@ -156,8 +156,8 @@ public static Color InterpolateSimd(Color a, Color b, public static Color InterpolateSimdIn(in Color a, in Color b, float lambda) { - var vecA = Unsafe.As>(ref Unsafe.AsRef(a)); - var vecB = Unsafe.As>(ref Unsafe.AsRef(b)); + var vecA = Unsafe.As>(ref Unsafe.AsRef(in a)); + var vecB = Unsafe.As>(ref Unsafe.AsRef(in b)); vecB = Fma.MultiplyAdd(Sse.Subtract(vecB, vecA), Vector128.Create(lambda), vecA); diff --git a/Content.Benchmarks/DeviceNetworkingBenchmark.cs b/Content.Benchmarks/DeviceNetworkingBenchmark.cs index 16805c2684..bb2a22312e 100644 --- a/Content.Benchmarks/DeviceNetworkingBenchmark.cs +++ b/Content.Benchmarks/DeviceNetworkingBenchmark.cs @@ -4,8 +4,8 @@ using Content.IntegrationTests; using Content.IntegrationTests.Pair; using Content.IntegrationTests.Tests.DeviceNetwork; -using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Systems; +using Content.Shared.DeviceNetwork; using Robust.Shared; using Robust.Shared.Analyzers; using Robust.Shared.GameObjects; diff --git a/Content.Benchmarks/MapLoadBenchmark.cs b/Content.Benchmarks/MapLoadBenchmark.cs index 7caa995836..cc41d62575 100644 --- a/Content.Benchmarks/MapLoadBenchmark.cs +++ b/Content.Benchmarks/MapLoadBenchmark.cs @@ -46,7 +46,7 @@ public async Task Cleanup() PoolManager.Shutdown(); } - public static readonly string[] MapsSource = { "Empty", "Box", "Aspid", "Bagel", "Dev", "CentComm", "Atlas", "Core", "TestTeg", "Saltern", "Packed", "Omega", "Cluster", "Gemini", "Reach", "Origin", "Meta", "Marathon", "Europa", "MeteorArena", "Fland", "Barratry" }; + public static readonly string[] MapsSource = { "Empty", "Dev", "CentComm", "TestTeg", }; [ParamsSource(nameof(MapsSource))] public string Map; diff --git a/Content.Benchmarks/Program.cs b/Content.Benchmarks/Program.cs index 0beb0a613d..42a436597d 100644 --- a/Content.Benchmarks/Program.cs +++ b/Content.Benchmarks/Program.cs @@ -18,11 +18,6 @@ internal static class Program public static void Main(string[] args) { - MainAsync(args).GetAwaiter().GetResult(); - } - - public static async Task MainAsync(string[] args) - { #if DEBUG Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("\nWARNING: YOU ARE RUNNING A DEBUG BUILD, USE A RELEASE BUILD FOR AN ACCURATE BENCHMARK"); diff --git a/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs b/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs index de51b2fb19..8512107b69 100644 --- a/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs +++ b/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs @@ -58,7 +58,7 @@ await _pair.Server.WaitPost(() => for (var i = 0; i < N; i++) { _entity = server.EntMan.SpawnAttachedTo(Mob, _coords); - _spawnSys.EquipStartingGear(_entity, _gear, null); + _spawnSys.EquipStartingGear(_entity, _gear); server.EntMan.DeleteEntity(_entity); } }); diff --git a/Content.Client/Access/AccessOverlay.cs b/Content.Client/Access/AccessOverlay.cs index 2be3d07e90..59c9441036 100644 --- a/Content.Client/Access/AccessOverlay.cs +++ b/Content.Client/Access/AccessOverlay.cs @@ -9,20 +9,20 @@ namespace Content.Client.Access; public sealed class AccessOverlay : Overlay { + private const string TextFontPath = "/Fonts/NotoSans/NotoSans-Regular.ttf"; + private const int TextFontSize = 12; + private readonly IEntityManager _entityManager; - private readonly EntityLookupSystem _lookup; - private readonly SharedTransformSystem _xform; + private readonly SharedTransformSystem _transformSystem; private readonly Font _font; public override OverlaySpace Space => OverlaySpace.ScreenSpace; - public AccessOverlay(IEntityManager entManager, IResourceCache cache, EntityLookupSystem lookup, SharedTransformSystem xform) + public AccessOverlay(IEntityManager entityManager, IResourceCache resourceCache, SharedTransformSystem transformSystem) { - _entityManager = entManager; - _lookup = lookup; - _xform = xform; - - _font = cache.GetFont("/Fonts/NotoSans/NotoSans-Regular.ttf", 12); + _entityManager = entityManager; + _transformSystem = transformSystem; + _font = resourceCache.GetFont(TextFontPath, TextFontSize); } protected override void Draw(in OverlayDrawArgs args) @@ -30,52 +30,65 @@ protected override void Draw(in OverlayDrawArgs args) if (args.ViewportControl == null) return; - var readerQuery = _entityManager.GetEntityQuery(); - var xformQuery = _entityManager.GetEntityQuery(); - - foreach (var ent in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldAABB, - LookupFlags.Static | LookupFlags.Approximate)) + var textBuffer = new StringBuilder(); + var query = _entityManager.EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var accessReader, out var transform)) { - if (!readerQuery.TryGetComponent(ent, out var reader) || - !xformQuery.TryGetComponent(ent, out var xform)) + textBuffer.Clear(); + + var entityName = _entityManager.ToPrettyString(uid); + textBuffer.AppendLine(entityName.Prototype); + textBuffer.Append("UID: "); + textBuffer.Append(entityName.Uid.Id); + textBuffer.Append(", NUID: "); + textBuffer.Append(entityName.Nuid.Id); + textBuffer.AppendLine(); + + if (!accessReader.Enabled) { + textBuffer.AppendLine("-Disabled"); continue; } - var text = new StringBuilder(); - var index = 0; - var a = $"{_entityManager.ToPrettyString(ent)}"; - text.Append(a); - - foreach (var list in reader.AccessLists) + if (accessReader.AccessLists.Count > 0) { - a = $"Tag {index}"; - text.AppendLine(a); - - foreach (var entry in list) + var groupNumber = 0; + foreach (var accessList in accessReader.AccessLists) { - a = $"- {entry}"; - text.AppendLine(a); + groupNumber++; + foreach (var entry in accessList) + { + textBuffer.Append("+Set "); + textBuffer.Append(groupNumber); + textBuffer.Append(": "); + textBuffer.Append(entry.Id); + textBuffer.AppendLine(); + } } - - index++; } - - string textStr; - - if (text.Length >= 2) + else { - textStr = text.ToString(); - textStr = textStr[..^2]; + textBuffer.AppendLine("+Unrestricted"); } - else + + foreach (var key in accessReader.AccessKeys) { - textStr = ""; + textBuffer.Append("+Key "); + textBuffer.Append(key.OriginStation); + textBuffer.Append(": "); + textBuffer.Append(key.Id); + textBuffer.AppendLine(); } - var screenPos = args.ViewportControl.WorldToScreen(_xform.GetWorldPosition(xform)); + foreach (var tag in accessReader.DenyTags) + { + textBuffer.Append("-Tag "); + textBuffer.AppendLine(tag.Id); + } - args.ScreenHandle.DrawString(_font, screenPos, textStr, Color.Gold); + var accessInfoText = textBuffer.ToString(); + var screenPos = args.ViewportControl.WorldToScreen(_transformSystem.GetWorldPosition(transform)); + args.ScreenHandle.DrawString(_font, screenPos, accessInfoText, Color.Gold); } } } diff --git a/Content.Client/Access/Commands/ShowAccessReadersCommand.cs b/Content.Client/Access/Commands/ShowAccessReadersCommand.cs index 7c804dd969..cb6cb6cf6b 100644 --- a/Content.Client/Access/Commands/ShowAccessReadersCommand.cs +++ b/Content.Client/Access/Commands/ShowAccessReadersCommand.cs @@ -7,8 +7,16 @@ namespace Content.Client.Access.Commands; public sealed class ShowAccessReadersCommand : IConsoleCommand { public string Command => "showaccessreaders"; - public string Description => "Shows all access readers in the viewport"; - public string Help => $"{Command}"; + + public string Description => "Toggles showing access reader permissions on the map"; + public string Help => """ + Overlay Info: + -Disabled | The access reader is disabled + +Unrestricted | The access reader has no restrictions + +Set [Index]: [Tag Name]| A tag in an access set (accessor needs all tags in the set to be allowed by the set) + +Key [StationUid]: [StationRecordKeyId] | A StationRecordKey that is allowed + -Tag [Tag Name] | A tag that is not allowed (takes priority over other allows) + """; public void Execute(IConsoleShell shell, string argStr, string[] args) { var collection = IoCManager.Instance; @@ -26,10 +34,9 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) var entManager = collection.Resolve(); var cache = collection.Resolve(); - var lookup = entManager.System(); var xform = entManager.System(); - overlay.AddOverlay(new AccessOverlay(entManager, cache, lookup, xform)); + overlay.AddOverlay(new AccessOverlay(entManager, cache, xform)); shell.WriteLine($"Set access reader debug overlay to true"); } } diff --git a/Content.Client/Access/IdCardSystem.cs b/Content.Client/Access/IdCardSystem.cs index fcf2bf57de..e0c02976f7 100644 --- a/Content.Client/Access/IdCardSystem.cs +++ b/Content.Client/Access/IdCardSystem.cs @@ -2,6 +2,4 @@ namespace Content.Client.Access; -public sealed class IdCardSystem : SharedIdCardSystem -{ -} +public sealed class IdCardSystem : SharedIdCardSystem; diff --git a/Content.Client/Access/UI/AccessLevelControl.xaml b/Content.Client/Access/UI/AccessLevelControl.xaml new file mode 100644 index 0000000000..56968d8983 --- /dev/null +++ b/Content.Client/Access/UI/AccessLevelControl.xaml @@ -0,0 +1,4 @@ + + diff --git a/Content.Client/Access/UI/AccessLevelControl.xaml.cs b/Content.Client/Access/UI/AccessLevelControl.xaml.cs new file mode 100644 index 0000000000..34db80b7af --- /dev/null +++ b/Content.Client/Access/UI/AccessLevelControl.xaml.cs @@ -0,0 +1,52 @@ +using System.Linq; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Prototypes; +using Content.Shared.Access; +using Content.Shared.Access.Systems; + +namespace Content.Client.Access.UI; + +[GenerateTypedNameReferences] +public sealed partial class AccessLevelControl : GridContainer +{ + public readonly Dictionary, Button> ButtonsList = new(); + + public AccessLevelControl() + { + RobustXamlLoader.Load(this); + } + + public void Populate(List> accessLevels, IPrototypeManager prototypeManager) + { + foreach (var access in accessLevels) + { + if (!prototypeManager.TryIndex(access, out var accessLevel)) + { + Logger.Error($"Unable to find accesslevel for {access}"); + continue; + } + + var newButton = new Button + { + Text = accessLevel.GetAccessLevelName(), + ToggleMode = true, + }; + AddChild(newButton); + ButtonsList.Add(accessLevel.ID, newButton); + } + } + + public void UpdateState( + List> pressedList, + List>? enabledList = null) + { + foreach (var (accessName, button) in ButtonsList) + { + button.Pressed = pressedList.Contains(accessName); + button.Disabled = !(enabledList?.Contains(accessName) ?? true); + } + } +} diff --git a/Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs b/Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs index 0c23542f79..c1b63dc4d0 100644 --- a/Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs +++ b/Content.Client/Access/UI/AccessOverriderBoundUserInterface.cs @@ -64,7 +64,7 @@ protected override void UpdateState(BoundUserInterfaceState state) _window?.UpdateState(castState); } - public void SubmitData(List newAccessList) + public void SubmitData(List> newAccessList) { SendMessage(new WriteToTargetAccessReaderIdMessage(newAccessList)); } diff --git a/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs b/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs index 2fd0057121..6025c3b551 100644 --- a/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs +++ b/Content.Client/Access/UI/AccessOverriderWindow.xaml.cs @@ -16,7 +16,6 @@ public sealed partial class AccessOverriderWindow : DefaultWindow [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - private readonly ISawmill _logMill = default!; private readonly AccessOverriderBoundUserInterface _owner; private readonly Dictionary _accessButtons = new(); @@ -25,7 +24,7 @@ public AccessOverriderWindow(AccessOverriderBoundUserInterface owner, IPrototype { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); - _logMill = _logManager.GetSawmill(SharedAccessOverriderSystem.Sawmill); + var logMill = _logManager.GetSawmill(SharedAccessOverriderSystem.Sawmill); _owner = owner; @@ -33,13 +32,13 @@ public AccessOverriderWindow(AccessOverriderBoundUserInterface owner, IPrototype { if (!prototypeManager.TryIndex(access, out var accessLevel)) { - _logMill.Error($"Unable to find accesslevel for {access}"); + logMill.Error($"Unable to find accesslevel for {access}"); continue; } var newButton = new Button { - Text = GetAccessLevelName(accessLevel), + Text = accessLevel.GetAccessLevelName(), ToggleMode = true, }; @@ -49,14 +48,6 @@ public AccessOverriderWindow(AccessOverriderBoundUserInterface owner, IPrototype } } - private static string GetAccessLevelName(AccessLevelPrototype prototype) - { - if (prototype.Name is { } name) - return Loc.GetString(name); - - return prototype.ID; - } - public void UpdateState(AccessOverriderBoundUserInterfaceState state) { PrivilegedIdLabel.Text = state.PrivilegedIdName; @@ -105,7 +96,7 @@ private void SubmitData() _owner.SubmitData( // Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair - _accessButtons.Where(x => x.Value.Pressed).Select(x => x.Key).ToList()); + _accessButtons.Where(x => x.Value.Pressed).Select(x => new ProtoId(x.Key)).ToList()); } } } diff --git a/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs b/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs index 73f18aec8d..c3fac8cb92 100644 --- a/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs +++ b/Content.Client/Access/UI/AgentIDCardBoundUserInterface.cs @@ -40,9 +40,9 @@ private void OnJobChanged(string newJob) SendMessage(new AgentIDCardJobChangedMessage(newJob)); } - public void OnJobIconChanged(string newJobIcon) + public void OnJobIconChanged(string newJobIconId) { - SendMessage(new AgentIDCardJobIconChangedMessage(newJobIcon)); + SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId)); } /// @@ -57,7 +57,7 @@ protected override void UpdateState(BoundUserInterfaceState state) _window.SetCurrentName(cast.CurrentName); _window.SetCurrentJob(cast.CurrentJob); - _window.SetAllowedIcons(cast.Icons); + _window.SetAllowedIcons(cast.Icons, cast.CurrentJobIconId); } protected override void Dispose(bool disposing) diff --git a/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs b/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs index beca0c41ba..9a38c0c485 100644 --- a/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs +++ b/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs @@ -38,7 +38,7 @@ public AgentIDCardWindow(AgentIDCardBoundUserInterface bui) JobLineEdit.OnFocusExit += e => OnJobChanged?.Invoke(e.Text); } - public void SetAllowedIcons(HashSet icons) + public void SetAllowedIcons(HashSet icons, string currentJobIconId) { IconGrid.DisposeAllChildren(); @@ -79,6 +79,10 @@ public void SetAllowedIcons(HashSet icons) jobIconButton.AddChild(jobIconTexture); jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIcon.ID); IconGrid.AddChild(jobIconButton); + + if (jobIconId.Equals(currentJobIconId)) + jobIconButton.Pressed = true; + i++; } } diff --git a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs index 898792aa03..a321b4121e 100644 --- a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs +++ b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs @@ -28,7 +28,6 @@ protected override void Open() if (EntMan.TryGetComponent(Owner, out var idCard)) { accessLevels = idCard.AccessLevels; - accessLevels.Sort(); } else { @@ -65,7 +64,7 @@ protected override void UpdateState(BoundUserInterfaceState state) _window?.UpdateState(castState); } - public void SubmitData(string newFullName, string newJobTitle, List newAccessList, string newJobPrototype) + public void SubmitData(string newFullName, string newJobTitle, List> newAccessList, string newJobPrototype) { if (newFullName.Length > MaxFullNameLength) newFullName = newFullName[..MaxFullNameLength]; diff --git a/Content.Client/Access/UI/IdCardConsoleWindow.xaml b/Content.Client/Access/UI/IdCardConsoleWindow.xaml index c29adc8ebd..a2f5f3382b 100644 --- a/Content.Client/Access/UI/IdCardConsoleWindow.xaml +++ b/Content.Client/Access/UI/IdCardConsoleWindow.xaml @@ -30,10 +30,6 @@ public void LoadActionAssignments(string path, bool userData) { - if (_playerManager.LocalPlayer?.ControlledEntity is not { } user) + if (_playerManager.LocalEntity is not { } user) return; var file = new ResPath(path).ToRootedPath(); @@ -310,6 +317,70 @@ public void LoadActionAssignments(string path, bool userData) AssignSlot?.Invoke(assignments); } + /// + /// Load actions and their toolbar assignments from a file. + /// DeltaV - Load from an existing yaml stream instead + /// + public void LoadActionAssignments(YamlStream stream) + { + if (_playerManager.LocalEntity is not { } user) + return; + + if (stream.Documents[0].RootNode.ToDataNode() is not SequenceDataNode sequence) + return; + + ClearAssignments?.Invoke(); + + var assignments = new List(); + var existingActions = GetClientActions(); + var existingActionsList = existingActions.ToList(); + + foreach (var entry in sequence.Sequence) + { + if (entry is not MappingDataNode map) + continue; + + if (!map.TryGet("action", out var actionNode)) + continue; + + if (!map.TryGet("name", out var nameNode)) + continue; + + var action = _serialization.Read(actionNode, notNullableOverride: true); + + // Prevent spawning actions multiple times + var existing = existingActionsList.FirstOrNull(a => + Name(a.Id) == nameNode.Value); + + EntityUid actionId; + if (existing == null) + { + actionId = Spawn(null); + AddComp(actionId, action); + _metaData.SetEntityName(actionId, nameNode.Value); + DirtyEntity(actionId); + AddActionDirect(user, actionId); + } + else + { + actionId = existing.Value.Id; + } + + if (!map.TryGet("assignments", out var assignmentNode)) + continue; + + var nodeAssignments = _serialization.Read>(assignmentNode, notNullableOverride: true); + + foreach (var index in nodeAssignments) + { + var assignment = new SlotAssignment(index.Hotbar, index.Slot, actionId); + assignments.Add(assignment); + } + } + + AssignSlot?.Invoke(assignments); + } + public record struct SlotAssignment(byte Hotbar, byte Slot, EntityUid ActionId); } } diff --git a/Content.Client/Administration/Components/HeadstandComponent.cs b/Content.Client/Administration/Components/HeadstandComponent.cs index d95e74576b..a4e3bfc5aa 100644 --- a/Content.Client/Administration/Components/HeadstandComponent.cs +++ b/Content.Client/Administration/Components/HeadstandComponent.cs @@ -3,7 +3,7 @@ namespace Content.Client.Administration.Components; -[RegisterComponent, NetworkedComponent] +[RegisterComponent] public sealed partial class HeadstandComponent : SharedHeadstandComponent { diff --git a/Content.Client/Administration/Components/KillSignComponent.cs b/Content.Client/Administration/Components/KillSignComponent.cs index 1cf47b93ff..91c44ef3f2 100644 --- a/Content.Client/Administration/Components/KillSignComponent.cs +++ b/Content.Client/Administration/Components/KillSignComponent.cs @@ -3,6 +3,5 @@ namespace Content.Client.Administration.Components; -[NetworkedComponent, RegisterComponent] -public sealed partial class KillSignComponent : SharedKillSignComponent -{ } +[RegisterComponent] +public sealed partial class KillSignComponent : SharedKillSignComponent; diff --git a/Content.Client/Administration/Managers/ClientAdminManager.cs b/Content.Client/Administration/Managers/ClientAdminManager.cs index 1a1366c6f2..0f740c8104 100644 --- a/Content.Client/Administration/Managers/ClientAdminManager.cs +++ b/Content.Client/Administration/Managers/ClientAdminManager.cs @@ -2,6 +2,7 @@ using Content.Shared.Administration.Managers; using Robust.Client.Console; using Robust.Client.Player; +using Robust.Client.UserInterface; using Robust.Shared.ContentPack; using Robust.Shared.Network; using Robust.Shared.Player; @@ -15,11 +16,14 @@ public sealed class ClientAdminManager : IClientAdminManager, IClientConGroupImp [Dependency] private readonly IClientNetManager _netMgr = default!; [Dependency] private readonly IClientConGroupController _conGroup = default!; [Dependency] private readonly IResourceManager _res = default!; + [Dependency] private readonly ILogManager _logManager = default!; + [Dependency] private readonly IUserInterfaceManager _userInterface = default!; private AdminData? _adminData; private readonly HashSet _availableCommands = new(); private readonly AdminCommandPermissions _localCommandPermissions = new(); + private ISawmill _sawmill = default!; public event Action? AdminStatusUpdated; @@ -92,17 +96,20 @@ private void UpdateMessageRx(MsgUpdateAdminStatus message) } _availableCommands.UnionWith(message.AvailableCommands); - Logger.DebugS("admin", $"Have {message.AvailableCommands.Length} commands available"); + _sawmill.Debug($"Have {message.AvailableCommands.Length} commands available"); _adminData = message.Admin; if (_adminData != null) { var flagsText = string.Join("|", AdminFlagsHelper.FlagsToNames(_adminData.Flags)); - Logger.InfoS("admin", $"Updated admin status: {_adminData.Active}/{_adminData.Title}/{flagsText}"); + _sawmill.Info($"Updated admin status: {_adminData.Active}/{_adminData.Title}/{flagsText}"); + + if (_adminData.Active) + _userInterface.DebugMonitors.SetMonitor(DebugMonitor.Coords, true); } else { - Logger.InfoS("admin", "Updated admin status: Not admin"); + _sawmill.Info("Updated admin status: Not admin"); } AdminStatusUpdated?.Invoke(); @@ -114,18 +121,20 @@ private void UpdateMessageRx(MsgUpdateAdminStatus message) void IPostInjectInit.PostInject() { _conGroup.Implementation = this; + _sawmill = _logManager.GetSawmill("admin"); } public AdminData? GetAdminData(EntityUid uid, bool includeDeAdmin = false) { - return uid == _player.LocalPlayer?.ControlledEntity - ? _adminData - : null; + if (uid == _player.LocalEntity && (_adminData?.Active ?? includeDeAdmin)) + return _adminData; + + return null; } public AdminData? GetAdminData(ICommonSession session, bool includeDeAdmin = false) { - if (_player.LocalPlayer?.UserId == session.UserId) + if (_player.LocalUser == session.UserId && (_adminData?.Active ?? includeDeAdmin)) return _adminData; return null; @@ -133,7 +142,7 @@ void IPostInjectInit.PostInject() public AdminData? GetAdminData(bool includeDeAdmin = false) { - if (_player.LocalPlayer is { Session: { } session }) + if (_player.LocalSession is { } session) return GetAdminData(session, includeDeAdmin); return null; diff --git a/Content.Client/Administration/Systems/AdminSystem.cs b/Content.Client/Administration/Systems/AdminSystem.cs index f7451d2304..db1b721343 100644 --- a/Content.Client/Administration/Systems/AdminSystem.cs +++ b/Content.Client/Administration/Systems/AdminSystem.cs @@ -10,16 +10,9 @@ public sealed partial class AdminSystem : EntitySystem { public event Action>? PlayerListChanged; - private Dictionary? _playerList; - public IReadOnlyList PlayerList - { - get - { - if (_playerList != null) return _playerList.Values.ToList(); - - return new List(); - } - } + public Dictionary PlayerInfos = new(); + public IReadOnlyList PlayerList => + PlayerInfos != null ? PlayerInfos.Values.ToList() : new List(); public override void Initialize() { @@ -40,15 +33,15 @@ private void OnPlayerInfoChanged(PlayerInfoChangedEvent ev) { if(ev.PlayerInfo == null) return; - if (_playerList == null) _playerList = new(); + if (PlayerInfos == null) PlayerInfos = new(); - _playerList[ev.PlayerInfo.SessionId] = ev.PlayerInfo; - PlayerListChanged?.Invoke(_playerList.Values.ToList()); + PlayerInfos[ev.PlayerInfo.SessionId] = ev.PlayerInfo; + PlayerListChanged?.Invoke(PlayerInfos.Values.ToList()); } private void OnPlayerListChanged(FullPlayerListEvent msg) { - _playerList = msg.PlayersInfo.ToDictionary(x => x.SessionId, x => x); + PlayerInfos = msg.PlayersInfo.ToDictionary(x => x.SessionId, x => x); PlayerListChanged?.Invoke(msg.PlayersInfo); } } diff --git a/Content.Client/Administration/Systems/AdminVerbSystem.cs b/Content.Client/Administration/Systems/AdminVerbSystem.cs index e0f84bc4f0..dced59bbf2 100644 --- a/Content.Client/Administration/Systems/AdminVerbSystem.cs +++ b/Content.Client/Administration/Systems/AdminVerbSystem.cs @@ -1,3 +1,6 @@ +using Content.Shared.Administration; +using Content.Shared.Administration.Managers; +using Content.Shared.Mind.Components; using Content.Shared.Verbs; using Robust.Client.Console; using Robust.Shared.Utility; @@ -11,10 +14,12 @@ sealed class AdminVerbSystem : EntitySystem { [Dependency] private readonly IClientConGroupController _clientConGroupController = default!; [Dependency] private readonly IClientConsoleHost _clientConsoleHost = default!; + [Dependency] private readonly ISharedAdminManager _admin = default!; public override void Initialize() { SubscribeLocalEvent>(AddAdminVerbs); + } private void AddAdminVerbs(GetVerbsEvent args) @@ -33,6 +38,24 @@ private void AddAdminVerbs(GetVerbsEvent args) }; args.Verbs.Add(verb); } + + if (!_admin.IsAdmin(args.User)) + return; + + if (_admin.HasAdminFlag(args.User, AdminFlags.Admin)) + args.ExtraCategories.Add(VerbCategory.Admin); + + if (_admin.HasAdminFlag(args.User, AdminFlags.Fun) && HasComp(args.Target)) + args.ExtraCategories.Add(VerbCategory.Antag); + + if (_admin.HasAdminFlag(args.User, AdminFlags.Debug)) + args.ExtraCategories.Add(VerbCategory.Debug); + + if (_admin.HasAdminFlag(args.User, AdminFlags.Fun)) + args.ExtraCategories.Add(VerbCategory.Smite); + + if (_admin.HasAdminFlag(args.User, AdminFlags.Admin)) + args.ExtraCategories.Add(VerbCategory.Tricks); } } } diff --git a/Content.Client/Administration/Systems/BwoinkSystem.cs b/Content.Client/Administration/Systems/BwoinkSystem.cs index eafd40cc9c..a3b295d6b6 100644 --- a/Content.Client/Administration/Systems/BwoinkSystem.cs +++ b/Content.Client/Administration/Systems/BwoinkSystem.cs @@ -1,7 +1,10 @@ #nullable enable +using Content.Client.UserInterface.Systems.Bwoink; using Content.Shared.Administration; using JetBrains.Annotations; +using Robust.Client.Audio; using Robust.Shared.Network; +using Robust.Shared.Player; using Robust.Shared.Timing; namespace Content.Client.Administration.Systems @@ -10,6 +13,8 @@ namespace Content.Client.Administration.Systems public sealed class BwoinkSystem : SharedBwoinkSystem { [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly AdminSystem _adminSystem = default!; public event EventHandler? OnBwoinkTextMessageRecieved; private (TimeSpan Timestamp, bool Typing) _lastTypingUpdateSent; @@ -19,11 +24,15 @@ protected override void OnBwoinkTextMessage(BwoinkTextMessage message, EntitySes OnBwoinkTextMessageRecieved?.Invoke(this, message); } - public void Send(NetUserId channelId, string text) + public void Send(NetUserId channelId, string text, bool playSound) { + var info = _adminSystem.PlayerInfos.GetValueOrDefault(channelId)?.Connected ?? true; + _audio.PlayGlobal(info ? AHelpUIController.AHelpSendSound : AHelpUIController.AHelpErrorSound, + Filter.Local(), false); + // Reuse the channel ID as the 'true sender'. // Server will ignore this and if someone makes it not ignore this (which is bad, allows impersonation!!!), that will help. - RaiseNetworkEvent(new BwoinkTextMessage(channelId, channelId, text)); + RaiseNetworkEvent(new BwoinkTextMessage(channelId, channelId, text, playSound: playSound)); SendInputTextUpdated(channelId, false); } @@ -31,9 +40,7 @@ public void SendInputTextUpdated(NetUserId channel, bool typing) { if (_lastTypingUpdateSent.Typing == typing && _lastTypingUpdateSent.Timestamp + TimeSpan.FromSeconds(1) > _timing.RealTime) - { return; - } _lastTypingUpdateSent = (_timing.RealTime, typing); RaiseNetworkEvent(new BwoinkClientTypingUpdated(channel, typing)); diff --git a/Content.Client/Administration/UI/AdminRemarks/AdminMessageEui.cs b/Content.Client/Administration/UI/AdminRemarks/AdminMessageEui.cs index 06eace118d..502c56a5a6 100644 --- a/Content.Client/Administration/UI/AdminRemarks/AdminMessageEui.cs +++ b/Content.Client/Administration/UI/AdminRemarks/AdminMessageEui.cs @@ -2,6 +2,7 @@ using Content.Shared.Administration.Notes; using Content.Shared.Eui; using JetBrains.Annotations; +using Robust.Client.UserInterface.Controls; using static Content.Shared.Administration.Notes.AdminMessageEuiMsg; namespace Content.Client.Administration.UI.AdminRemarks; @@ -14,9 +15,8 @@ public sealed class AdminMessageEui : BaseEui public AdminMessageEui() { _popup = new AdminMessagePopupWindow(); - _popup.OnAcceptPressed += () => SendMessage(new Accept()); - _popup.OnDismissPressed += () => SendMessage(new Dismiss()); - _popup.OnClose += () => SendMessage(new CloseEuiMessage()); + _popup.OnAcceptPressed += () => SendMessage(new Dismiss(true)); + _popup.OnDismissPressed += () => SendMessage(new Dismiss(false)); } public override void HandleState(EuiStateBase state) @@ -26,13 +26,17 @@ public override void HandleState(EuiStateBase state) return; } - _popup.SetMessage(s.Message); - _popup.SetDetails(s.AdminName, s.AddedOn); - _popup.Timer = s.Time; + _popup.SetState(s); } public override void Opened() { - _popup.OpenCentered(); + _popup.UserInterfaceManager.WindowRoot.AddChild(_popup); + LayoutContainer.SetAnchorPreset(_popup, LayoutContainer.LayoutPreset.Wide); + } + + public override void Closed() + { + _popup.Orphan(); } } diff --git a/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupMessage.xaml b/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupMessage.xaml new file mode 100644 index 0000000000..9a60a6f72a --- /dev/null +++ b/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupMessage.xaml @@ -0,0 +1,6 @@ + + + + + + diff --git a/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupMessage.xaml.cs b/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupMessage.xaml.cs new file mode 100644 index 0000000000..7bb425f618 --- /dev/null +++ b/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupMessage.xaml.cs @@ -0,0 +1,23 @@ +using Content.Shared.Administration.Notes; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Utility; + +namespace Content.Client.Administration.UI.AdminRemarks; + +[GenerateTypedNameReferences] +public sealed partial class AdminMessagePopupMessage : Control +{ + public AdminMessagePopupMessage(AdminMessageEuiState.Message message) + { + RobustXamlLoader.Load(this); + + Admin.SetMessage(FormattedMessage.FromMarkup(Loc.GetString( + "admin-notes-message-admin", + ("admin", message.AdminName), + ("date", message.AddedOn.ToLocalTime())))); + + Message.SetMessage(message.Text); + } +} diff --git a/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupWindow.xaml b/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupWindow.xaml index 311829e8b2..cc5207bb3a 100644 --- a/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupWindow.xaml +++ b/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupWindow.xaml @@ -1,22 +1,36 @@ - - - - -