Compare commits
4 Commits
workbench-
...
magick
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f6e73831a | ||
|
|
a0a2e8fabf | ||
|
|
765d226a9d | ||
|
|
25c174af41 |
@@ -1,5 +1,4 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
|
||||
charset = utf-8
|
||||
@@ -10,10 +9,9 @@ indent_style = space
|
||||
tab_width = 4
|
||||
|
||||
# New line preferences
|
||||
#end_of_line = crlf
|
||||
end_of_line = crlf:suggestion
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
max_line_length = 120
|
||||
|
||||
#### .NET Coding Conventions ####
|
||||
|
||||
@@ -106,6 +104,7 @@ csharp_preferred_modifier_order = public, private, protected, internal, new, abs
|
||||
|
||||
# 'using' directive preferences
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
csharp_style_namespace_declarations = file_scoped:suggestion
|
||||
|
||||
#### C# Formatting Rules ####
|
||||
|
||||
@@ -129,7 +128,7 @@ csharp_indent_braces = false
|
||||
csharp_indent_switch_labels = true
|
||||
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_cast = true
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
@@ -279,7 +278,7 @@ dotnet_naming_style.t_upper_camel_case_style.capitalization = pascal_case
|
||||
dotnet_naming_style.t_upper_camel_case_style.required_prefix = T
|
||||
dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
|
||||
dotnet_naming_symbols.constants_symbols.applicable_kinds = field
|
||||
dotnet_naming_symbols.constants_symbols.required_modifiers = const
|
||||
|
||||
@@ -318,32 +317,27 @@ dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static
|
||||
|
||||
dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private
|
||||
dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static, readonly
|
||||
dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static,readonly
|
||||
|
||||
dotnet_naming_symbols.property_symbols.applicable_accessibilities = *
|
||||
dotnet_naming_symbols.property_symbols.applicable_kinds = property
|
||||
|
||||
dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
|
||||
dotnet_naming_symbols.public_fields_symbols.applicable_kinds = field
|
||||
|
||||
dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
|
||||
dotnet_naming_symbols.static_readonly_symbols.applicable_kinds = field
|
||||
dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static, readonly
|
||||
dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static,readonly
|
||||
|
||||
dotnet_naming_symbols.types_and_namespaces_symbols.applicable_accessibilities = *
|
||||
dotnet_naming_symbols.types_and_namespaces_symbols.applicable_kinds = namespace, class, struct, enum, delegate
|
||||
dotnet_naming_symbols.types_and_namespaces_symbols.applicable_kinds = namespace,class,struct,enum,delegate
|
||||
|
||||
dotnet_naming_symbols.type_parameters_symbols.applicable_accessibilities = *
|
||||
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
|
||||
resharper_csharp_trailing_comma_in_multiline_lists = true
|
||||
|
||||
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props}]
|
||||
indent_size = 2
|
||||
|
||||
44
.github/CODEOWNERS
vendored
44
.github/CODEOWNERS
vendored
@@ -2,30 +2,47 @@
|
||||
|
||||
# Sorting by path instead of by who added it one day :(
|
||||
# this isn't how codeowners rules work pls read the first comment instead of trying to force a sorting order
|
||||
/Resources/ConfigPresets/WizardsDen/ @Chief-Engineer
|
||||
|
||||
/Resources/ConfigPresets/WizardsDen/ @nikthechampiongr @crazybrain23
|
||||
/Content.*/Administration/ @DrSmugleaf @nikthechampiongr @crazybrain23
|
||||
/Resources/ServerInfo/ @nikthechampiongr @crazybrain23
|
||||
/Resources/ServerInfo/Guidebook/ServerRules/ @nikthechampiongr @crazybrain23
|
||||
# Moony's Gargantuan List Of Things She Cares About, or MGLOTSCA for short.
|
||||
# You need to add your name to these entries, not make a new one, if you care about them.
|
||||
/Content.*/Toolshed/ @moonheart08
|
||||
**/Toolshed/** @moonheart08
|
||||
*Command.cs @moonheart08
|
||||
/Content.*/Administration/ @moonheart08 @DrSmugleaf @Chief-Engineer
|
||||
/Content.*/Station/ @moonheart08
|
||||
/Content.*/Maps/ @moonheart08
|
||||
/Content.*/GameTicking/ @moonheart08 @EmoGarbage404
|
||||
/Resources/ServerInfo/ @moonheart08 @Chief-Engineer
|
||||
/Resources/ServerInfo/Guidebook/ @moonheart08 @EmoGarbage404
|
||||
/Resources/engineCommandPerms.yml @moonheart08 @Chief-Engineer
|
||||
/Resources/clientCommandPerms.yml @moonheart08 @Chief-Engineer
|
||||
|
||||
/Resources/Prototypes/Maps/** @Emisse
|
||||
/Resources/Prototypes/Maps/ @Emisse
|
||||
|
||||
/Resources/Prototypes/Body/ @DrSmugleaf # suffering
|
||||
/Resources/Prototypes/Entities/Mobs/Player/ @DrSmugleaf
|
||||
/Resources/Prototypes/Entities/Mobs/Species/ @DrSmugleaf
|
||||
/Resources/Prototypes/Guidebook/rules.yml @nikthechampiongr @crazybrain23
|
||||
/Content.*/Body/ @DrSmugleaf
|
||||
/Content.YAMLLinter @DrSmugleaf
|
||||
/Content.Shared/Damage/ @DrSmugleaf
|
||||
|
||||
/Content.*/Anomaly/ @TheShuEd
|
||||
/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml @TheShuEd
|
||||
/Content.*/Anomaly/ @EmoGarbage404
|
||||
/Content.*/Lathe/ @EmoGarbage404
|
||||
/Content.*/Materials/ @EmoGarbage404
|
||||
/Content.*/Mech/ @EmoGarbage404
|
||||
/Content.*/Research/ @EmoGarbage404
|
||||
/Content.*/Stack/ @EmoGarbage404
|
||||
/Content.*/Xenoarchaeology/ @EmoGarbage404
|
||||
/Content.*/Zombies/ @EmoGarbage404
|
||||
/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml @EmoGarbage404
|
||||
/Resources/Prototypes/Research/ @EmoGarbage404
|
||||
|
||||
/Content.*/Forensics/ @ficcialfaint
|
||||
|
||||
# SKREEEE
|
||||
/Content.*.Database/ @PJB3005 @DrSmugleaf
|
||||
/Content.Shared.Database/Log*.cs @PJB3005 @DrSmugleaf @nikthechampiongr @crazybrain23
|
||||
/Content.Shared.Database/Log*.cs @PJB3005 @DrSmugleaf @Chief-Engineer
|
||||
/Pow3r/ @PJB3005
|
||||
/Content.Server/Power/Pow3r/ @PJB3005
|
||||
|
||||
@@ -33,13 +50,6 @@
|
||||
/Content.*/Atmos/ @Partmedia
|
||||
/Content.*/Botany/ @Partmedia
|
||||
|
||||
# Jezi
|
||||
#Jezi
|
||||
/Content.*/Medical @Jezithyr
|
||||
/Content.*/Body @Jezithyr
|
||||
|
||||
# Sloth
|
||||
/Content.*/Audio @metalgearsloth
|
||||
/Content.*/Movement @metalgearsloth
|
||||
/Content.*/NPC @metalgearsloth
|
||||
/Content.*/Shuttles @metalgearsloth
|
||||
/Content.*/Weapons @metalgearsloth
|
||||
|
||||
14
.github/FUNDING.yml
vendored
14
.github/FUNDING.yml
vendored
@@ -1,14 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
polar: # Replace with a single Polar username
|
||||
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||
custom: ['https://boosty.to/theshued']
|
||||
36
.github/PULL_REQUEST_TEMPLATE.md
vendored
36
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,17 +1,43 @@
|
||||
<!-- Please read these guidelines before opening your PR: https://docs.spacestation14.io/en/getting-started/pr-guideline -->
|
||||
<!-- The text between the arrows are comments - they will not be visible on your PR. -->
|
||||
|
||||
## About the PR
|
||||
<!-- What did you change in this PR? -->
|
||||
<!-- Что вы изменили в своем пулл реквесте? -->
|
||||
|
||||
## Why / Balance
|
||||
<!-- Why was it changed? Link any discussions or issues here. Please discuss how this would affect game balance. -->
|
||||
<!-- Зачем нужно это изменение? Прикрепите любые обсуждения или проблемы здесь. Опишите, как это повлияет на текущий баланс игры. -->
|
||||
|
||||
## Technical details
|
||||
<!-- If this is a code change, summarize at high level how your new code works. This makes it easier to review. -->
|
||||
|
||||
## Media
|
||||
<!--
|
||||
<!--
|
||||
PRs which make ingame changes (adding clothing, items, new features, etc) are required to have media attached that showcase the changes.
|
||||
Small fixes/refactors are exempt.
|
||||
Any media may be used in SS14 progress reports, with clear credit given.
|
||||
|
||||
If you're unsure whether your PR will require media, ask a maintainer.
|
||||
|
||||
Check the box below to confirm that you have in fact seen this (put an X in the brackets, like [X]):
|
||||
-->
|
||||
|
||||
- [ ] I have added screenshots/videos to this PR showcasing its changes ingame, **or** this PR does not require an ingame showcase
|
||||
|
||||
## Breaking changes
|
||||
<!--
|
||||
Пулл реквесты, которые несут за собой игровые изменения (добавления одежды, предметов и так далее) требуют чтобы вы прикрепили скриншоты или видеоролики, демонстрирующие эти изменения.
|
||||
Небольшие исправления не считаются.
|
||||
List any breaking changes, including namespace, public class/method/field changes, prototype renames; and provide instructions for fixing them. This will be pasted in #codebase-changes.
|
||||
-->
|
||||
|
||||
**Changelog**
|
||||
<!--
|
||||
Make players aware of new features and changes that could affect how they play the game by adding a Changelog entry. Please read the Changelog guidelines located at: https://docs.spacestation14.io/en/getting-started/pr-guideline#changelog
|
||||
-->
|
||||
|
||||
<!--
|
||||
Make sure to take this Changelog template out of the comment block in order for it to show up.
|
||||
:cl:
|
||||
- add: Added fun!
|
||||
- remove: Removed fun!
|
||||
- tweak: Changed fun!
|
||||
- fix: Fixed fun!
|
||||
-->
|
||||
|
||||
26
.github/labeler.yml
vendored
26
.github/labeler.yml
vendored
@@ -1,26 +1,12 @@
|
||||
"Changes: Sprites":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: '**/*.rsi/*.png'
|
||||
- '**/*.rsi/*.png'
|
||||
|
||||
"Changes: Map":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'Resources/Maps/**/*.yml'
|
||||
- 'Resources/Prototypes/Maps/**/*.yml'
|
||||
- 'Resources/Maps/*.yml'
|
||||
- 'Resources/Prototypes/Maps/*.yml'
|
||||
|
||||
"Changes: UI":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: '**/*.xaml*'
|
||||
- '**/*.xaml*'
|
||||
|
||||
"Changes: Shaders":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: '**/*.swsl'
|
||||
|
||||
"Changes: Audio":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: '**/*.ogg'
|
||||
|
||||
"Changes: No C#":
|
||||
- changed-files:
|
||||
# Equiv to any-glob-to-all as long as this has one matcher. If ALL changed files are not C# files, then apply label.
|
||||
- all-globs-to-all-files: "!**/*.cs"
|
||||
"No C#":
|
||||
- all: ["!**/*.cs"]
|
||||
|
||||
6
.github/rsi-schema.json
vendored
6
.github/rsi-schema.json
vendored
@@ -75,17 +75,15 @@
|
||||
"license":{
|
||||
"$id":"#/properties/license",
|
||||
"default":"",
|
||||
"description":"The license for the associated icon states. Restricted to CP14-compatible asset licenses.",
|
||||
"description":"The license for the associated icon states. Restricted to SS14-compatible asset licenses.",
|
||||
"enum":[
|
||||
"CC-BY-SA-3.0",
|
||||
"CC-BY-SA-4.0",
|
||||
"CC0-1.0",
|
||||
"CC-BY-NC-3.0",
|
||||
"CC-BY-NC-4.0",
|
||||
"CC-BY-NC-SA-3.0",
|
||||
"CC-BY-NC-SA-4.0",
|
||||
"CC0-1.0",
|
||||
"CLA"
|
||||
"CC0-1.0"
|
||||
],
|
||||
"examples":[
|
||||
"CC-BY-SA-3.0"
|
||||
|
||||
20
.github/workflows/CP14Publish.yml
vendored
20
.github/workflows/CP14Publish.yml
vendored
@@ -1,20 +0,0 @@
|
||||
name: Publish
|
||||
|
||||
concurrency:
|
||||
group: publish
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send POST-request
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: ${{ secrets.BUILD_HOST }}
|
||||
username: ${{ secrets.BUILD_USER }}
|
||||
password: ${{ secrets.BUILD_PASS }}
|
||||
port: 22
|
||||
script: sh update.sh &> /dev/null
|
||||
4
.github/workflows/build-map-renderer.yml
vendored
4
.github/workflows/build-map-renderer.yml
vendored
@@ -2,11 +2,11 @@
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master, staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
4
.github/workflows/build-test-debug.yml
vendored
4
.github/workflows/build-test-debug.yml
vendored
@@ -2,11 +2,11 @@ name: Build & Test Debug
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master, staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
19
.github/workflows/conflict-labeler.yml
vendored
Normal file
19
.github/workflows/conflict-labeler.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Check Merge Conflicts
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request_target:
|
||||
|
||||
jobs:
|
||||
Label:
|
||||
if: github.actor != 'PJBot'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check for Merge Conflicts
|
||||
uses: ike709/actions-label-merge-conflict@9eefdd17e10566023c46d2dc6dc04fcb8ec76142
|
||||
with:
|
||||
dirtyLabel: "Merge Conflict"
|
||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||
commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request."
|
||||
21
.github/workflows/labeler-conflict.yml
vendored
21
.github/workflows/labeler-conflict.yml
vendored
@@ -1,21 +0,0 @@
|
||||
name: Check Merge Conflicts
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
Label:
|
||||
if: ( github.event.pull_request.draft == false ) && ( github.actor != 'PJBot' )
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check for Merge Conflicts
|
||||
uses: eps1lon/actions-label-merge-conflict@v3.0.0
|
||||
with:
|
||||
dirtyLabel: "S: Merge Conflict"
|
||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||
commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request."
|
||||
4
.github/workflows/labeler-needsreview.yml
vendored
4
.github/workflows/labeler-needsreview.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: "S: Needs Review"
|
||||
labels: "Status: Needs Review"
|
||||
- uses: actions-ecosystem/action-remove-labels@v1
|
||||
with:
|
||||
labels: "S: Awaiting Changes"
|
||||
labels: "Status: Awaiting Changes"
|
||||
|
||||
7
.github/workflows/labeler-pr.yml
vendored
7
.github/workflows/labeler-pr.yml
vendored
@@ -6,9 +6,8 @@ on:
|
||||
jobs:
|
||||
labeler:
|
||||
if: github.actor != 'PJBot'
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v5
|
||||
- uses: actions/labeler@v3
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
23
.github/workflows/labeler-review.yml
vendored
23
.github/workflows/labeler-review.yml
vendored
@@ -1,23 +0,0 @@
|
||||
name: "Labels: Approved"
|
||||
on:
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
jobs:
|
||||
add_label:
|
||||
# Change the repository name after you've made sure the team name is correct for your fork!
|
||||
if: ${{ (github.repository == 'space-wizards/space-station-14') && (github.event.review.state == 'APPROVED') }}
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: tspascoal/get-user-teams-membership@v3
|
||||
id: checkUserMember
|
||||
with:
|
||||
username: ${{ github.actor }}
|
||||
team: "content-maintainers,junior-maintainers"
|
||||
GITHUB_TOKEN: ${{ secrets.LABELER_PAT }}
|
||||
- if: ${{ steps.checkUserMember.outputs.isTeamMember == 'true' }}
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: "S: Approved"
|
||||
20
.github/workflows/labeler-size.yml
vendored
20
.github/workflows/labeler-size.yml
vendored
@@ -1,20 +0,0 @@
|
||||
name: "Labels: Size"
|
||||
on: pull_request_target
|
||||
jobs:
|
||||
size-label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: size-label
|
||||
uses: "pascalgn/size-label-action@v0.5.5"
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
with:
|
||||
# Custom size configuration
|
||||
sizes: >
|
||||
{
|
||||
"0": "XS",
|
||||
"10": "S",
|
||||
"100": "M",
|
||||
"1000": "L",
|
||||
"5000": "XL"
|
||||
}
|
||||
16
.github/workflows/labeler-stable.yml
vendored
16
.github/workflows/labeler-stable.yml
vendored
@@ -1,16 +0,0 @@
|
||||
name: "Labels: Branch stable"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
branches:
|
||||
- 'stable'
|
||||
|
||||
jobs:
|
||||
add_label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: "Branch: Stable"
|
||||
16
.github/workflows/labeler-staging.yml
vendored
16
.github/workflows/labeler-staging.yml
vendored
@@ -1,16 +0,0 @@
|
||||
name: "Labels: Branch staging"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
branches:
|
||||
- 'staging'
|
||||
|
||||
jobs:
|
||||
add_label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: "Branch: Staging"
|
||||
5
.github/workflows/labeler-untriaged.yml
vendored
5
.github/workflows/labeler-untriaged.yml
vendored
@@ -3,14 +3,11 @@
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
add_label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions-ecosystem/action-add-labels@v1
|
||||
if: join(github.event.issue.labels) == ''
|
||||
with:
|
||||
labels: "S: Untriaged"
|
||||
labels: "Status: Untriaged"
|
||||
|
||||
34
.github/workflows/publish.yml
vendored
34
.github/workflows/publish.yml
vendored
@@ -5,8 +5,8 @@ concurrency:
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# schedule:
|
||||
# - cron: '0 10 * * *'
|
||||
schedule:
|
||||
- cron: '0 10 * * *'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -41,11 +41,31 @@ jobs:
|
||||
- name: Package client
|
||||
run: dotnet run --project Content.Packaging client --no-wipe-release
|
||||
|
||||
- name: Publish version
|
||||
run: Tools/publish_multi_request.py
|
||||
env:
|
||||
PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
|
||||
GITHUB_REPOSITORY: ${{ vars.GITHUB_REPOSITORY }}
|
||||
- name: Update Build Info
|
||||
run: Tools/gen_build_info.py
|
||||
|
||||
- name: Shuffle files around
|
||||
run: |
|
||||
mkdir "release/${{ github.sha }}"
|
||||
mv release/*.zip "release/${{ github.sha }}"
|
||||
|
||||
- name: Upload files to centcomm
|
||||
uses: appleboy/scp-action@master
|
||||
with:
|
||||
host: centcomm.spacestation14.io
|
||||
username: wizards-build-push
|
||||
key: ${{ secrets.CENTCOMM_WIZARDS_BUILDS_PUSH_KEY }}
|
||||
source: "release/${{ github.sha }}"
|
||||
target: "/home/wizards-build-push/builds_dir/builds/"
|
||||
strip_components: 1
|
||||
|
||||
- name: Update manifest JSON
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: centcomm.spacestation14.io
|
||||
username: wizards-build-push
|
||||
key: ${{ secrets.CENTCOMM_WIZARDS_BUILDS_PUSH_KEY }}
|
||||
script: /home/wizards-build-push/push.ps1 ${{ github.sha }}
|
||||
|
||||
- name: Publish changelog (Discord)
|
||||
run: Tools/actions_changelogs_since_last_run.py
|
||||
|
||||
5
.github/workflows/rsi-diff.yml
vendored
5
.github/workflows/rsi-diff.yml
vendored
@@ -15,12 +15,9 @@ jobs:
|
||||
|
||||
- name: Get changed files
|
||||
id: files
|
||||
uses: Ana06/get-changed-files@v2.3.0
|
||||
uses: Ana06/get-changed-files@v1.2
|
||||
with:
|
||||
format: 'space-delimited'
|
||||
filter: |
|
||||
**.rsi
|
||||
**.png
|
||||
|
||||
- name: Diff changed RSIs
|
||||
id: diff
|
||||
|
||||
12
.github/workflows/test-packaging.yml
vendored
12
.github/workflows/test-packaging.yml
vendored
@@ -2,7 +2,7 @@
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master, staging, trying ]
|
||||
paths:
|
||||
- '**.cs'
|
||||
- '**.csproj'
|
||||
@@ -16,7 +16,7 @@ on:
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- '**.cs'
|
||||
- '**.csproj'
|
||||
@@ -64,3 +64,11 @@ jobs:
|
||||
|
||||
- name: Package client
|
||||
run: dotnet run --project Content.Packaging client --no-wipe-release
|
||||
|
||||
- name: Update Build Info
|
||||
run: Tools/gen_build_info.py
|
||||
|
||||
- name: Shuffle files around
|
||||
run: |
|
||||
mkdir "release/${{ github.sha }}"
|
||||
mv release/*.zip "release/${{ github.sha }}"
|
||||
|
||||
2
.github/workflows/update-credits.yml
vendored
2
.github/workflows/update-credits.yml
vendored
@@ -19,8 +19,6 @@ jobs:
|
||||
|
||||
- name: Get this week's Contributors
|
||||
shell: pwsh
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
run: Tools/dump_github_contributors.ps1 > Resources/Credits/GitHub.txt
|
||||
|
||||
# TODO
|
||||
|
||||
6
.github/workflows/validate-rgas.yml
vendored
6
.github/workflows/validate-rgas.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: RGA schema validator
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master, staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
@@ -21,5 +21,5 @@ jobs:
|
||||
with:
|
||||
schema: RobustToolbox/Schemas/rga.yml
|
||||
path_pattern: .*attributions.ya?ml$
|
||||
validators_path: Schemas/rga_validators.py
|
||||
validators_requirements: Schemas/rga_requirements.txt
|
||||
validators_path: RobustToolbox/Schemas/rga_validators.py
|
||||
validators_requirements: RobustToolbox/Schemas/rga_requirements.txt
|
||||
|
||||
4
.github/workflows/validate-rsis.yml
vendored
4
.github/workflows/validate-rsis.yml
vendored
@@ -2,7 +2,7 @@ name: RSI Validator
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
paths:
|
||||
@@ -23,4 +23,4 @@ jobs:
|
||||
pip3 install --ignore-installed --user pillow jsonschema
|
||||
- name: Validate RSIs
|
||||
run: |
|
||||
python3 Schemas/validate_rsis.py Resources/
|
||||
python3 RobustToolbox/Schemas/validate_rsis.py Resources/
|
||||
|
||||
2
.github/workflows/validate_mapfiles.yml
vendored
2
.github/workflows/validate_mapfiles.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: Map file schema validator
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master, staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
|
||||
2
.github/workflows/yaml-linter.yml
vendored
2
.github/workflows/yaml-linter.yml
vendored
@@ -2,7 +2,7 @@ name: YAML Linter
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master, staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -306,4 +306,3 @@ Resources/MapImages
|
||||
|
||||
# Direnv stuff
|
||||
.direnv/
|
||||
/Tools/_CP14/LocalizationHelper/last_launch
|
||||
|
||||
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"omnisharp.analyzeOpenDocumentsOnly": true,
|
||||
"dotnet.defaultSolution": "SpaceStation14.sln"
|
||||
}
|
||||
6
.vscode/tasks.json
vendored
6
.vscode/tasks.json
vendored
@@ -10,7 +10,7 @@
|
||||
"args": [
|
||||
"build",
|
||||
"/property:GenerateFullPaths=true", // Ask dotnet build to generate full paths for file names.
|
||||
"/consoleloggerparameters:'ForceNoAlign;NoSummary'" // Do not generate summary otherwise it leads to duplicate errors in Problems panel
|
||||
"/consoleloggerparameters:NoSummary" // Do not generate summary otherwise it leads to duplicate errors in Problems panel
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
@@ -29,9 +29,9 @@
|
||||
"build",
|
||||
"${workspaceFolder}/Content.YAMLLinter/Content.YAMLLinter.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:'ForceNoAlign;NoSummary'"
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Configs;
|
||||
using Content.IntegrationTests;
|
||||
using Content.IntegrationTests.Pair;
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Item;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Benchmarks;
|
||||
|
||||
/// <summary>
|
||||
/// Benchmarks for comparing the speed of various component fetching/lookup related methods, including directed event
|
||||
/// subscriptions
|
||||
/// </summary>
|
||||
[Virtual]
|
||||
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
|
||||
[CategoriesColumn]
|
||||
public class ComponentQueryBenchmark
|
||||
{
|
||||
public const string Map = "Maps/atlas.yml";
|
||||
|
||||
private TestPair _pair = default!;
|
||||
private IEntityManager _entMan = default!;
|
||||
private MapId _mapId = new(10);
|
||||
private EntityQuery<ItemComponent> _itemQuery;
|
||||
private EntityQuery<ClothingComponent> _clothingQuery;
|
||||
private EntityQuery<MapComponent> _mapQuery;
|
||||
private EntityUid[] _items = default!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
ProgramShared.PathOffset = "../../../../";
|
||||
PoolManager.Startup(typeof(QueryBenchSystem).Assembly);
|
||||
|
||||
_pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
|
||||
_entMan = _pair.Server.ResolveDependency<IEntityManager>();
|
||||
|
||||
_itemQuery = _entMan.GetEntityQuery<ItemComponent>();
|
||||
_clothingQuery = _entMan.GetEntityQuery<ClothingComponent>();
|
||||
_mapQuery = _entMan.GetEntityQuery<MapComponent>();
|
||||
|
||||
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
||||
_pair.Server.WaitPost(() =>
|
||||
{
|
||||
var success = _entMan.System<MapLoaderSystem>().TryLoad(_mapId, Map, out _);
|
||||
if (!success)
|
||||
throw new Exception("Map load failed");
|
||||
_pair.Server.MapMan.DoMapInitialize(_mapId);
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
_items = new EntityUid[_entMan.Count<ItemComponent>()];
|
||||
var i = 0;
|
||||
var enumerator = _entMan.AllEntityQueryEnumerator<ItemComponent>();
|
||||
while (enumerator.MoveNext(out var uid, out _))
|
||||
{
|
||||
_items[i++] = uid;
|
||||
}
|
||||
}
|
||||
|
||||
[GlobalCleanup]
|
||||
public async Task Cleanup()
|
||||
{
|
||||
await _pair.DisposeAsync();
|
||||
PoolManager.Shutdown();
|
||||
}
|
||||
|
||||
#region TryComp
|
||||
|
||||
/// <summary>
|
||||
/// Baseline TryComp benchmark. When the benchmark was created, around 40% of the items were clothing.
|
||||
/// </summary>
|
||||
[Benchmark(Baseline = true)]
|
||||
[BenchmarkCategory("TryComp")]
|
||||
public int TryComp()
|
||||
{
|
||||
var hashCode = 0;
|
||||
foreach (var uid in _items)
|
||||
{
|
||||
if (_clothingQuery.TryGetComponent(uid, out var clothing))
|
||||
hashCode = HashCode.Combine(hashCode, clothing.GetHashCode());
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="TryComp"/> that is meant to always fail to get a component.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("TryComp")]
|
||||
public int TryCompFail()
|
||||
{
|
||||
var hashCode = 0;
|
||||
foreach (var uid in _items)
|
||||
{
|
||||
if (_mapQuery.TryGetComponent(uid, out var map))
|
||||
hashCode = HashCode.Combine(hashCode, map.GetHashCode());
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="TryComp"/> that is meant to always succeed getting a component.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("TryComp")]
|
||||
public int TryCompSucceed()
|
||||
{
|
||||
var hashCode = 0;
|
||||
foreach (var uid in _items)
|
||||
{
|
||||
if (_itemQuery.TryGetComponent(uid, out var item))
|
||||
hashCode = HashCode.Combine(hashCode, item.GetHashCode());
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="TryComp"/> that uses `Resolve()` to try get the component.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("TryComp")]
|
||||
public int Resolve()
|
||||
{
|
||||
var hashCode = 0;
|
||||
foreach (var uid in _items)
|
||||
{
|
||||
DoResolve(uid, ref hashCode);
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void DoResolve(EntityUid uid, ref int hash, ClothingComponent? clothing = null)
|
||||
{
|
||||
if (_clothingQuery.Resolve(uid, ref clothing, false))
|
||||
hash = HashCode.Combine(hash, clothing.GetHashCode());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Enumeration
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("Item Enumerator")]
|
||||
public int SingleItemEnumerator()
|
||||
{
|
||||
var hashCode = 0;
|
||||
var enumerator = _entMan.AllEntityQueryEnumerator<ItemComponent>();
|
||||
while (enumerator.MoveNext(out var item))
|
||||
{
|
||||
hashCode = HashCode.Combine(hashCode, item.GetHashCode());
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("Item Enumerator")]
|
||||
public int DoubleItemEnumerator()
|
||||
{
|
||||
var hashCode = 0;
|
||||
var enumerator = _entMan.AllEntityQueryEnumerator<ClothingComponent, ItemComponent>();
|
||||
while (enumerator.MoveNext(out _, out var item))
|
||||
{
|
||||
hashCode = HashCode.Combine(hashCode, item.GetHashCode());
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("Item Enumerator")]
|
||||
public int TripleItemEnumerator()
|
||||
{
|
||||
var hashCode = 0;
|
||||
var enumerator = _entMan.AllEntityQueryEnumerator<ClothingComponent, ItemComponent, TransformComponent>();
|
||||
while (enumerator.MoveNext(out _, out _, out var xform))
|
||||
{
|
||||
hashCode = HashCode.Combine(hashCode, xform.GetHashCode());
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("Airlock Enumerator")]
|
||||
public int SingleAirlockEnumerator()
|
||||
{
|
||||
var hashCode = 0;
|
||||
var enumerator = _entMan.AllEntityQueryEnumerator<AirlockComponent>();
|
||||
while (enumerator.MoveNext(out var airlock))
|
||||
{
|
||||
hashCode = HashCode.Combine(hashCode, airlock.GetHashCode());
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("Airlock Enumerator")]
|
||||
public int DoubleAirlockEnumerator()
|
||||
{
|
||||
var hashCode = 0;
|
||||
var enumerator = _entMan.AllEntityQueryEnumerator<AirlockComponent, DoorComponent>();
|
||||
while (enumerator.MoveNext(out _, out var door))
|
||||
{
|
||||
hashCode = HashCode.Combine(hashCode, door.GetHashCode());
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("Airlock Enumerator")]
|
||||
public int TripleAirlockEnumerator()
|
||||
{
|
||||
var hashCode = 0;
|
||||
var enumerator = _entMan.AllEntityQueryEnumerator<AirlockComponent, DoorComponent, TransformComponent>();
|
||||
while (enumerator.MoveNext(out _, out _, out var xform))
|
||||
{
|
||||
hashCode = HashCode.Combine(hashCode, xform.GetHashCode());
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
[BenchmarkCategory("Events")]
|
||||
public int StructEvents()
|
||||
{
|
||||
var ev = new QueryBenchEvent();
|
||||
foreach (var uid in _items)
|
||||
{
|
||||
_entMan.EventBus.RaiseLocalEvent(uid, ref ev);
|
||||
}
|
||||
|
||||
return ev.HashCode;
|
||||
}
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
public struct QueryBenchEvent
|
||||
{
|
||||
public int HashCode;
|
||||
}
|
||||
|
||||
public sealed class QueryBenchSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ClothingComponent, QueryBenchEvent>(OnEvent);
|
||||
}
|
||||
|
||||
private void OnEvent(EntityUid uid, ClothingComponent component, ref QueryBenchEvent args)
|
||||
{
|
||||
args.HashCode = HashCode.Combine(args.HashCode, component.GetHashCode());
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,6 @@ namespace Content.Benchmarks
|
||||
|
||||
var componentFactory = new Mock<IComponentFactory>();
|
||||
componentFactory.Setup(p => p.GetComponent<DummyComponent>()).Returns(new DummyComponent());
|
||||
componentFactory.Setup(m => m.GetIndex(typeof(DummyComponent))).Returns(CompIdx.Index<DummyComponent>());
|
||||
componentFactory.Setup(p => p.GetRegistration(It.IsAny<DummyComponent>())).Returns(dummyReg);
|
||||
componentFactory.Setup(p => p.GetAllRegistrations()).Returns(new[] { dummyReg });
|
||||
componentFactory.Setup(p => p.GetAllRefTypes()).Returns(new[] { CompIdx.Index<DummyComponent>() });
|
||||
|
||||
137
Content.Benchmarks/EntityQueryBenchmark.cs
Normal file
137
Content.Benchmarks/EntityQueryBenchmark.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Content.IntegrationTests;
|
||||
using Content.IntegrationTests.Pair;
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Item;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Benchmarks;
|
||||
|
||||
[Virtual]
|
||||
public class EntityQueryBenchmark
|
||||
{
|
||||
public const string Map = "Maps/atlas.yml";
|
||||
|
||||
private TestPair _pair = default!;
|
||||
private IEntityManager _entMan = default!;
|
||||
private MapId _mapId = new MapId(10);
|
||||
private EntityQuery<ClothingComponent> _clothingQuery;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
ProgramShared.PathOffset = "../../../../";
|
||||
PoolManager.Startup(null);
|
||||
|
||||
_pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
|
||||
_entMan = _pair.Server.ResolveDependency<IEntityManager>();
|
||||
|
||||
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
||||
_pair.Server.WaitPost(() =>
|
||||
{
|
||||
var success = _entMan.System<MapLoaderSystem>().TryLoad(_mapId, Map, out _);
|
||||
if (!success)
|
||||
throw new Exception("Map load failed");
|
||||
_pair.Server.MapMan.DoMapInitialize(_mapId);
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
_clothingQuery = _entMan.GetEntityQuery<ClothingComponent>();
|
||||
|
||||
// Apparently ~40% of entities are items, and 1 in 6 of those are clothing.
|
||||
/*
|
||||
var entCount = _entMan.EntityCount;
|
||||
var itemCount = _entMan.Count<ItemComponent>();
|
||||
var clothingCount = _entMan.Count<ClothingComponent>();
|
||||
var itemRatio = (float) itemCount / entCount;
|
||||
var clothingRatio = (float) clothingCount / entCount;
|
||||
Console.WriteLine($"Entities: {entCount}. Items: {itemRatio:P2}. Clothing: {clothingRatio:P2}.");
|
||||
*/
|
||||
}
|
||||
|
||||
[GlobalCleanup]
|
||||
public async Task Cleanup()
|
||||
{
|
||||
await _pair.DisposeAsync();
|
||||
PoolManager.Shutdown();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int HasComponent()
|
||||
{
|
||||
var hashCode = 0;
|
||||
var enumerator = _entMan.AllEntityQueryEnumerator<ItemComponent>();
|
||||
while (enumerator.MoveNext(out var uid, out var _))
|
||||
{
|
||||
if (_entMan.HasComponent<ClothingComponent>(uid))
|
||||
hashCode = HashCode.Combine(hashCode, uid.Id);
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int HasComponentQuery()
|
||||
{
|
||||
var hashCode = 0;
|
||||
var enumerator = _entMan.AllEntityQueryEnumerator<ItemComponent>();
|
||||
while (enumerator.MoveNext(out var uid, out var _))
|
||||
{
|
||||
if (_clothingQuery.HasComponent(uid))
|
||||
hashCode = HashCode.Combine(hashCode, uid.Id);
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int TryGetComponent()
|
||||
{
|
||||
var hashCode = 0;
|
||||
var enumerator = _entMan.AllEntityQueryEnumerator<ItemComponent>();
|
||||
while (enumerator.MoveNext(out var uid, out var _))
|
||||
{
|
||||
if (_entMan.TryGetComponent(uid, out ClothingComponent? clothing))
|
||||
hashCode = HashCode.Combine(hashCode, clothing.GetHashCode());
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int TryGetComponentQuery()
|
||||
{
|
||||
var hashCode = 0;
|
||||
var enumerator = _entMan.AllEntityQueryEnumerator<ItemComponent>();
|
||||
while (enumerator.MoveNext(out var uid, out var _))
|
||||
{
|
||||
if (_clothingQuery.TryGetComponent(uid, out var clothing))
|
||||
hashCode = HashCode.Combine(hashCode, clothing.GetHashCode());
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerate all entities with both an item and clothing component.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public int Enumerator()
|
||||
{
|
||||
var hashCode = 0;
|
||||
var enumerator = _entMan.AllEntityQueryEnumerator<ItemComponent, ClothingComponent>();
|
||||
while (enumerator.MoveNext(out var _, out var clothing))
|
||||
{
|
||||
hashCode = HashCode.Combine(hashCode, clothing.GetHashCode());
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -26,7 +26,7 @@ public class MapLoadBenchmark
|
||||
public void Setup()
|
||||
{
|
||||
ProgramShared.PathOffset = "../../../../";
|
||||
PoolManager.Startup();
|
||||
PoolManager.Startup(null);
|
||||
|
||||
_pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
|
||||
var server = _pair.Server;
|
||||
@@ -46,7 +46,7 @@ public class MapLoadBenchmark
|
||||
PoolManager.Shutdown();
|
||||
}
|
||||
|
||||
public static readonly string[] MapsSource = { "Empty", "Satlern", "Box", "Bagel", "Dev", "CentComm", "Core", "TestTeg", "Packed", "Omega", "Reach", "Meta", "Marathon", "MeteorArena", "Fland", "Oasis", "Cog" };
|
||||
public static readonly string[] MapsSource = { "Empty", "Box", "Bagel", "Dev", "CentComm", "Atlas", "Core", "TestTeg", "Saltern", "Packed", "Omega", "Cluster", "Reach", "Origin", "Meta", "Marathon", "Europa", "MeteorArena", "Fland", "Barratry" };
|
||||
|
||||
[ParamsSource(nameof(MapsSource))]
|
||||
public string Map;
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Content.IntegrationTests;
|
||||
using Content.IntegrationTests.Pair;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Warps;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
@@ -47,7 +49,7 @@ public class PvsBenchmark
|
||||
#if !DEBUG
|
||||
ProgramShared.PathOffset = "../../../../";
|
||||
#endif
|
||||
PoolManager.Startup();
|
||||
PoolManager.Startup(null);
|
||||
|
||||
_pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
|
||||
_entMan = _pair.Server.ResolveDependency<IEntityManager>();
|
||||
@@ -56,20 +58,15 @@ public class PvsBenchmark
|
||||
_pair.Server.CfgMan.SetCVar(CVars.NetPvsAsync, false);
|
||||
_sys = _entMan.System<SharedTransformSystem>();
|
||||
|
||||
SetupAsync().Wait();
|
||||
}
|
||||
|
||||
private async Task SetupAsync()
|
||||
{
|
||||
// Spawn the map
|
||||
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
||||
await _pair.Server.WaitPost(() =>
|
||||
_pair.Server.WaitPost(() =>
|
||||
{
|
||||
var success = _entMan.System<MapLoaderSystem>().TryLoad(_mapId, Map, out _);
|
||||
if (!success)
|
||||
throw new Exception("Map load failed");
|
||||
_pair.Server.MapMan.DoMapInitialize(_mapId);
|
||||
});
|
||||
}).Wait();
|
||||
|
||||
// Get list of ghost warp positions
|
||||
_spawns = _entMan.AllComponentsList<WarpPointComponent>()
|
||||
@@ -79,19 +76,17 @@ public class PvsBenchmark
|
||||
|
||||
Array.Resize(ref _players, PlayerCount);
|
||||
|
||||
// Spawn "Players"
|
||||
_players = await _pair.Server.AddDummySessions(PlayerCount);
|
||||
await _pair.Server.WaitPost(() =>
|
||||
// Spawn "Players".
|
||||
_pair.Server.WaitPost(() =>
|
||||
{
|
||||
var mind = _pair.Server.System<MindSystem>();
|
||||
for (var i = 0; i < PlayerCount; i++)
|
||||
{
|
||||
var pos = _spawns[i % _spawns.Length];
|
||||
var uid =_entMan.SpawnEntity("MobHuman", pos);
|
||||
_pair.Server.ConsoleHost.ExecuteCommand($"setoutfit {_entMan.GetNetEntity(uid)} CaptainGear");
|
||||
mind.ControlMob(_players[i].UserId, uid);
|
||||
_players[i] = new DummySession{AttachedEntity = uid};
|
||||
}
|
||||
});
|
||||
}).Wait();
|
||||
|
||||
// Repeatedly move players around so that they "explore" the map and see lots of entities.
|
||||
// This will populate their PVS data with out-of-view entities.
|
||||
@@ -173,4 +168,20 @@ public class PvsBenchmark
|
||||
}).Wait();
|
||||
_pair.Server.PvsTick(_players);
|
||||
}
|
||||
|
||||
private sealed class DummySession : ICommonSession
|
||||
{
|
||||
public SessionStatus Status => SessionStatus.InGame;
|
||||
public EntityUid? AttachedEntity {get; set; }
|
||||
public NetUserId UserId => default;
|
||||
public string Name => string.Empty;
|
||||
public short Ping => default;
|
||||
public INetChannel Channel { get; set; } = default!;
|
||||
public LoginType AuthType => default;
|
||||
public HashSet<EntityUid> ViewSubscriptions { get; } = new();
|
||||
public DateTime ConnectedTime { get; set; }
|
||||
public SessionState State => default!;
|
||||
public SessionData Data => default!;
|
||||
public bool ClientSide { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public class SpawnEquipDeleteBenchmark
|
||||
public async Task SetupAsync()
|
||||
{
|
||||
ProgramShared.PathOffset = "../../../../";
|
||||
PoolManager.Startup();
|
||||
PoolManager.Startup(null);
|
||||
_pair = await PoolManager.GetServerClient();
|
||||
var server = _pair.Server;
|
||||
|
||||
|
||||
@@ -2,4 +2,6 @@
|
||||
|
||||
namespace Content.Client.Access;
|
||||
|
||||
public sealed class IdCardSystem : SharedIdCardSystem;
|
||||
public sealed class IdCardSystem : SharedIdCardSystem
|
||||
{
|
||||
}
|
||||
|
||||
@@ -12,18 +12,11 @@ namespace Content.Client.Access.UI;
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AccessLevelControl : GridContainer
|
||||
{
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
public readonly Dictionary<ProtoId<AccessLevelPrototype>, Button> ButtonsList = new();
|
||||
|
||||
public AccessLevelControl()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_sawmill = _logManager.GetSawmill("accesslevelcontrol");
|
||||
}
|
||||
|
||||
public void Populate(List<ProtoId<AccessLevelPrototype>> accessLevels, IPrototypeManager prototypeManager)
|
||||
@@ -32,7 +25,7 @@ public sealed partial class AccessLevelControl : GridContainer
|
||||
{
|
||||
if (!prototypeManager.TryIndex(access, out var accessLevel))
|
||||
{
|
||||
_sawmill.Error($"Unable to find accesslevel for {access}");
|
||||
Logger.Error($"Unable to find accesslevel for {access}");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ using Content.Shared.Access;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.Shared.Access.Components.AccessOverriderComponent;
|
||||
|
||||
@@ -24,28 +23,6 @@ namespace Content.Client.Access.UI
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = this.CreateWindow<AccessOverriderWindow>();
|
||||
RefreshAccess();
|
||||
_window.Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName;
|
||||
_window.OnSubmit += SubmitData;
|
||||
|
||||
_window.PrivilegedIdButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(PrivilegedIdCardSlotId));
|
||||
}
|
||||
|
||||
public override void OnProtoReload(PrototypesReloadedEventArgs args)
|
||||
{
|
||||
base.OnProtoReload(args);
|
||||
if (!args.WasModified<AccessLevelPrototype>())
|
||||
return;
|
||||
|
||||
RefreshAccess();
|
||||
|
||||
if (State != null)
|
||||
_window?.UpdateState(_prototypeManager, (AccessOverriderBoundUserInterfaceState) State);
|
||||
}
|
||||
|
||||
private void RefreshAccess()
|
||||
{
|
||||
List<ProtoId<AccessLevelPrototype>> accessLevels;
|
||||
|
||||
if (EntMan.TryGetComponent<AccessOverriderComponent>(Owner, out var accessOverrider))
|
||||
@@ -53,20 +30,38 @@ namespace Content.Client.Access.UI
|
||||
accessLevels = accessOverrider.AccessLevels;
|
||||
accessLevels.Sort();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
accessLevels = new List<ProtoId<AccessLevelPrototype>>();
|
||||
_accessOverriderSystem.Log.Error($"No AccessOverrider component found for {EntMan.ToPrettyString(Owner)}!");
|
||||
}
|
||||
|
||||
_window?.SetAccessLevels(_prototypeManager, accessLevels);
|
||||
_window = new AccessOverriderWindow(this, _prototypeManager, accessLevels)
|
||||
{
|
||||
Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName
|
||||
};
|
||||
|
||||
_window.PrivilegedIdButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(PrivilegedIdCardSlotId));
|
||||
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
_window?.Dispose();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
var castState = (AccessOverriderBoundUserInterfaceState) state;
|
||||
_window?.UpdateState(_prototypeManager, castState);
|
||||
_window?.UpdateState(castState);
|
||||
}
|
||||
|
||||
public void SubmitData(List<ProtoId<AccessLevelPrototype>> newAccessList)
|
||||
|
||||
@@ -13,24 +13,26 @@ namespace Content.Client.Access.UI
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AccessOverriderWindow : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private readonly AccessOverriderBoundUserInterface _owner;
|
||||
private readonly Dictionary<string, Button> _accessButtons = new();
|
||||
|
||||
public event Action<List<ProtoId<AccessLevelPrototype>>>? OnSubmit;
|
||||
|
||||
public AccessOverriderWindow()
|
||||
public AccessOverriderWindow(AccessOverriderBoundUserInterface owner, IPrototypeManager prototypeManager,
|
||||
List<ProtoId<AccessLevelPrototype>> accessLevels)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
IoCManager.InjectDependencies(this);
|
||||
var logMill = _logManager.GetSawmill(SharedAccessOverriderSystem.Sawmill);
|
||||
|
||||
public void SetAccessLevels(IPrototypeManager protoManager, List<ProtoId<AccessLevelPrototype>> accessLevels)
|
||||
{
|
||||
_accessButtons.Clear();
|
||||
AccessLevelGrid.DisposeAllChildren();
|
||||
_owner = owner;
|
||||
|
||||
foreach (var access in accessLevels)
|
||||
{
|
||||
if (!protoManager.TryIndex(access, out var accessLevel))
|
||||
if (!prototypeManager.TryIndex(access, out var accessLevel))
|
||||
{
|
||||
logMill.Error($"Unable to find accesslevel for {access}");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -42,16 +44,11 @@ namespace Content.Client.Access.UI
|
||||
|
||||
AccessLevelGrid.AddChild(newButton);
|
||||
_accessButtons.Add(accessLevel.ID, newButton);
|
||||
newButton.OnPressed += _ =>
|
||||
{
|
||||
OnSubmit?.Invoke(
|
||||
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair
|
||||
_accessButtons.Where(x => x.Value.Pressed).Select(x => new ProtoId<AccessLevelPrototype>(x.Key)).ToList());
|
||||
};
|
||||
newButton.OnPressed += _ => SubmitData();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateState(IPrototypeManager protoManager, AccessOverriderBoundUserInterfaceState state)
|
||||
public void UpdateState(AccessOverriderBoundUserInterfaceState state)
|
||||
{
|
||||
PrivilegedIdLabel.Text = state.PrivilegedIdName;
|
||||
PrivilegedIdButton.Text = state.IsPrivilegedIdPresent
|
||||
@@ -69,11 +66,11 @@ namespace Content.Client.Access.UI
|
||||
|
||||
if (state.MissingPrivilegesList != null && state.MissingPrivilegesList.Any())
|
||||
{
|
||||
var missingPrivileges = new List<string>();
|
||||
List<string> missingPrivileges = new List<string>();
|
||||
|
||||
foreach (string tag in state.MissingPrivilegesList)
|
||||
{
|
||||
var privilege = Loc.GetString(protoManager.Index<AccessLevelPrototype>(tag)?.Name ?? "generic-unknown");
|
||||
string privilege = Loc.GetString(_prototypeManager.Index<AccessLevelPrototype>(tag)?.Name ?? "generic-unknown");
|
||||
missingPrivileges.Add(privilege);
|
||||
}
|
||||
|
||||
@@ -93,5 +90,13 @@ namespace Content.Client.Access.UI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 => new ProtoId<AccessLevelPrototype>(x.Key)).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Access.UI
|
||||
{
|
||||
@@ -21,11 +18,16 @@ namespace Content.Client.Access.UI
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = this.CreateWindow<AgentIDCardWindow>();
|
||||
_window?.Dispose();
|
||||
_window = new AgentIDCardWindow(this);
|
||||
if (State != null)
|
||||
UpdateState(State);
|
||||
|
||||
_window.OpenCentered();
|
||||
|
||||
_window.OnClose += Close;
|
||||
_window.OnNameChanged += OnNameChanged;
|
||||
_window.OnJobChanged += OnJobChanged;
|
||||
_window.OnJobIconChanged += OnJobIconChanged;
|
||||
}
|
||||
|
||||
private void OnNameChanged(string newName)
|
||||
@@ -38,9 +40,9 @@ namespace Content.Client.Access.UI
|
||||
SendMessage(new AgentIDCardJobChangedMessage(newJob));
|
||||
}
|
||||
|
||||
public void OnJobIconChanged(ProtoId<JobIconPrototype> newJobIconId)
|
||||
public void OnJobIconChanged(string newJobIcon)
|
||||
{
|
||||
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId));
|
||||
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIcon));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -55,7 +57,16 @@ namespace Content.Client.Access.UI
|
||||
|
||||
_window.SetCurrentName(cast.CurrentName);
|
||||
_window.SetCurrentJob(cast.CurrentJob);
|
||||
_window.SetAllowedIcons(cast.CurrentJobIconId);
|
||||
_window.SetAllowedIcons(cast.Icons);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
<LineEdit Name="NameLineEdit" />
|
||||
<Label Name="CurrentJob" Text="{Loc 'agent-id-card-current-job'}" />
|
||||
<LineEdit Name="JobLineEdit" />
|
||||
<Label Text="{Loc 'agent-id-card-job-icon-label'}"/>
|
||||
<GridContainer Name="IconGrid" Columns="10">
|
||||
<!-- Job icon buttons are generated in the code -->
|
||||
</GridContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'agent-id-card-job-icon-label'}"/>
|
||||
<Control HorizontalExpand="True" MinSize="50 0"/>
|
||||
<GridContainer Name="IconGrid" Columns="10">
|
||||
<!-- Job icon buttons are generated in the code -->
|
||||
</GridContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -8,7 +8,6 @@ using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System.Numerics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Client.Access.UI
|
||||
{
|
||||
@@ -18,19 +17,19 @@ namespace Content.Client.Access.UI
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
private readonly AgentIDCardBoundUserInterface _bui;
|
||||
|
||||
private const int JobIconColumnCount = 10;
|
||||
|
||||
public event Action<string>? OnNameChanged;
|
||||
public event Action<string>? OnJobChanged;
|
||||
|
||||
public event Action<ProtoId<JobIconPrototype>>? OnJobIconChanged;
|
||||
|
||||
public AgentIDCardWindow()
|
||||
public AgentIDCardWindow(AgentIDCardBoundUserInterface bui)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_spriteSystem = _entitySystem.GetEntitySystem<SpriteSystem>();
|
||||
_bui = bui;
|
||||
|
||||
NameLineEdit.OnTextEntered += e => OnNameChanged?.Invoke(e.Text);
|
||||
NameLineEdit.OnFocusExit += e => OnNameChanged?.Invoke(e.Text);
|
||||
@@ -39,16 +38,19 @@ namespace Content.Client.Access.UI
|
||||
JobLineEdit.OnFocusExit += e => OnJobChanged?.Invoke(e.Text);
|
||||
}
|
||||
|
||||
public void SetAllowedIcons(string currentJobIconId)
|
||||
public void SetAllowedIcons(HashSet<string> icons)
|
||||
{
|
||||
IconGrid.DisposeAllChildren();
|
||||
|
||||
var jobIconButtonGroup = new ButtonGroup();
|
||||
var jobIconGroup = new ButtonGroup();
|
||||
var i = 0;
|
||||
var icons = _prototypeManager.EnumeratePrototypes<JobIconPrototype>().Where(icon => icon.AllowSelection).ToList();
|
||||
icons.Sort((x, y) => string.Compare(x.LocalizedJobName, y.LocalizedJobName, StringComparison.CurrentCulture));
|
||||
foreach (var jobIcon in icons)
|
||||
foreach (var jobIconId in icons)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<StatusIconPrototype>(jobIconId, out var jobIcon))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
String styleBase = StyleBase.ButtonOpenBoth;
|
||||
var modulo = i % JobIconColumnCount;
|
||||
if (modulo == 0)
|
||||
@@ -62,13 +64,12 @@ namespace Content.Client.Access.UI
|
||||
Access = AccessLevel.Public,
|
||||
StyleClasses = { styleBase },
|
||||
MaxSize = new Vector2(42, 28),
|
||||
Group = jobIconButtonGroup,
|
||||
Pressed = currentJobIconId == jobIcon.ID,
|
||||
ToolTip = jobIcon.LocalizedJobName
|
||||
Group = jobIconGroup,
|
||||
Pressed = i == 0,
|
||||
};
|
||||
|
||||
// Generate buttons textures
|
||||
var jobIconTexture = new TextureRect
|
||||
TextureRect jobIconTexture = new TextureRect
|
||||
{
|
||||
Texture = _spriteSystem.Frame0(jobIcon.Icon),
|
||||
TextureScale = new Vector2(2.5f, 2.5f),
|
||||
@@ -76,9 +77,8 @@ namespace Content.Client.Access.UI
|
||||
};
|
||||
|
||||
jobIconButton.AddChild(jobIconTexture);
|
||||
jobIconButton.OnPressed += _ => OnJobIconChanged?.Invoke(jobIcon.ID);
|
||||
jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIcon.ID);
|
||||
IconGrid.AddChild(jobIconButton);
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Shared.Access;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.CrewManifest;
|
||||
|
||||
@@ -27,9 +27,6 @@ namespace Content.Client.Access.UI
|
||||
private string? _lastJobTitle;
|
||||
private string? _lastJobProto;
|
||||
|
||||
// The job that will be picked if the ID doesn't have a job on the station.
|
||||
private static ProtoId<JobPrototype> _defaultJob = "Passenger";
|
||||
|
||||
public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, IPrototypeManager prototypeManager,
|
||||
List<ProtoId<AccessLevelPrototype>> accessLevels)
|
||||
{
|
||||
@@ -68,6 +65,7 @@ namespace Content.Client.Access.UI
|
||||
}
|
||||
|
||||
JobPresetOptionButton.OnItemSelected += SelectJobPreset;
|
||||
|
||||
_accessButtons.Populate(accessLevels, prototypeManager);
|
||||
AccessLevelControlContainer.AddChild(_accessButtons);
|
||||
|
||||
@@ -174,15 +172,11 @@ namespace Content.Client.Access.UI
|
||||
new List<ProtoId<AccessLevelPrototype>>());
|
||||
|
||||
var jobIndex = _jobPrototypeIds.IndexOf(state.TargetIdJobPrototype);
|
||||
// If the job index is < 0 that means they don't have a job registered in the station records.
|
||||
// For example, a new ID from a box would have no job index.
|
||||
if (jobIndex < 0)
|
||||
if (jobIndex >= 0)
|
||||
{
|
||||
jobIndex = _jobPrototypeIds.IndexOf(_defaultJob);
|
||||
JobPresetOptionButton.SelectId(jobIndex);
|
||||
}
|
||||
|
||||
JobPresetOptionButton.SelectId(jobIndex);
|
||||
|
||||
_lastFullName = state.TargetIdFullName;
|
||||
_lastJobTitle = state.TargetIdJobTitle;
|
||||
_lastJobProto = state.TargetIdJobPrototype;
|
||||
|
||||
@@ -48,30 +48,6 @@ namespace Content.Client.Actions
|
||||
SubscribeLocalEvent<InstantActionComponent, ComponentHandleState>(OnInstantHandleState);
|
||||
SubscribeLocalEvent<EntityTargetActionComponent, ComponentHandleState>(OnEntityTargetHandleState);
|
||||
SubscribeLocalEvent<WorldTargetActionComponent, ComponentHandleState>(OnWorldTargetHandleState);
|
||||
SubscribeLocalEvent<EntityWorldTargetActionComponent, ComponentHandleState>(OnEntityWorldTargetHandleState);
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
|
||||
var worldActionQuery = EntityQueryEnumerator<WorldTargetActionComponent>();
|
||||
while (worldActionQuery.MoveNext(out var uid, out var action))
|
||||
{
|
||||
UpdateAction(uid, action);
|
||||
}
|
||||
|
||||
var instantActionQuery = EntityQueryEnumerator<InstantActionComponent>();
|
||||
while (instantActionQuery.MoveNext(out var uid, out var action))
|
||||
{
|
||||
UpdateAction(uid, action);
|
||||
}
|
||||
|
||||
var entityActionQuery = EntityQueryEnumerator<EntityTargetActionComponent>();
|
||||
while (entityActionQuery.MoveNext(out var uid, out var action))
|
||||
{
|
||||
UpdateAction(uid, action);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInstantHandleState(EntityUid uid, InstantActionComponent component, ref ComponentHandleState args)
|
||||
@@ -100,26 +76,12 @@ namespace Content.Client.Actions
|
||||
BaseHandleState<WorldTargetActionComponent>(uid, component, state);
|
||||
}
|
||||
|
||||
private void OnEntityWorldTargetHandleState(EntityUid uid,
|
||||
EntityWorldTargetActionComponent component,
|
||||
ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not EntityWorldTargetActionComponentState state)
|
||||
return;
|
||||
|
||||
component.Whitelist = state.Whitelist;
|
||||
component.CanTargetSelf = state.CanTargetSelf;
|
||||
BaseHandleState<EntityWorldTargetActionComponent>(uid, component, state);
|
||||
}
|
||||
|
||||
private void BaseHandleState<T>(EntityUid uid, BaseActionComponent component, BaseActionComponentState state) where T : BaseActionComponent
|
||||
{
|
||||
// TODO ACTIONS use auto comp states
|
||||
component.Icon = state.Icon;
|
||||
component.IconOn = state.IconOn;
|
||||
component.IconColor = state.IconColor;
|
||||
component.OriginalIconColor = state.OriginalIconColor;
|
||||
component.DisabledIconColor = state.DisabledIconColor;
|
||||
component.Keywords.Clear();
|
||||
component.Keywords.UnionWith(state.Keywords);
|
||||
component.Enabled = state.Enabled;
|
||||
@@ -145,13 +107,11 @@ namespace Content.Client.Actions
|
||||
UpdateAction(uid, component);
|
||||
}
|
||||
|
||||
public override void UpdateAction(EntityUid? actionId, BaseActionComponent? action = null)
|
||||
protected override void UpdateAction(EntityUid? actionId, BaseActionComponent? action = null)
|
||||
{
|
||||
if (!ResolveActionData(actionId, ref action))
|
||||
return;
|
||||
|
||||
action.IconColor = action.Charges < 1 ? action.DisabledIconColor : action.OriginalIconColor;
|
||||
|
||||
base.UpdateAction(actionId, action);
|
||||
if (_playerManager.LocalEntity != action.AttachedEntity)
|
||||
return;
|
||||
@@ -258,13 +218,13 @@ namespace Content.Client.Actions
|
||||
|
||||
public void LinkAllActions(ActionsComponent? actions = null)
|
||||
{
|
||||
if (_playerManager.LocalEntity is not { } user ||
|
||||
!Resolve(user, ref actions, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_playerManager.LocalEntity is not { } user ||
|
||||
!Resolve(user, ref actions, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LinkActions?.Invoke(actions);
|
||||
LinkActions?.Invoke(actions);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
@@ -286,6 +246,9 @@ namespace Content.Client.Actions
|
||||
|
||||
if (action.ClientExclusive)
|
||||
{
|
||||
if (instantAction.Event != null)
|
||||
instantAction.Event.Performer = user;
|
||||
|
||||
PerformAction(user, actions, actionId, instantAction, instantAction.Event, GameTiming.CurTime);
|
||||
}
|
||||
else
|
||||
@@ -327,7 +290,7 @@ namespace Content.Client.Actions
|
||||
continue;
|
||||
|
||||
var action = _serialization.Read<BaseActionComponent>(actionNode, notNullableOverride: true);
|
||||
var actionId = Spawn();
|
||||
var actionId = Spawn(null);
|
||||
AddComp(actionId, action);
|
||||
AddActionDirect(user, actionId);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.Stylesheets;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -77,12 +77,9 @@ namespace Content.Client.Actions.UI
|
||||
MaxWidth = TooltipTextMaxWidth,
|
||||
StyleClasses = {StyleNano.StyleClassTooltipActionRequirements}
|
||||
};
|
||||
|
||||
if (!FormattedMessage.TryFromMarkup("[color=#635c5c]" + requires + "[/color]", out var markup))
|
||||
return;
|
||||
|
||||
requiresLabel.SetMessage(markup);
|
||||
|
||||
requiresLabel.SetMessage(FormattedMessage.FromMarkup("[color=#635c5c]" +
|
||||
requires +
|
||||
"[/color]"));
|
||||
vbox.AddChild(requiresLabel);
|
||||
}
|
||||
}
|
||||
@@ -100,11 +97,8 @@ namespace Content.Client.Actions.UI
|
||||
if (timeLeft > TimeSpan.Zero)
|
||||
{
|
||||
var duration = Cooldown.Value.End - Cooldown.Value.Start;
|
||||
|
||||
if (!FormattedMessage.TryFromMarkup(Loc.GetString("ui-actionslot-duration", ("duration", (int)duration.TotalSeconds), ("timeLeft", (int)timeLeft.TotalSeconds + 1)), out var markup))
|
||||
return;
|
||||
|
||||
_cooldownLabel.SetMessage(markup);
|
||||
_cooldownLabel.SetMessage(FormattedMessage.FromMarkup(
|
||||
$"[color=#a10505]{(int) duration.TotalSeconds} sec cooldown ({(int) timeLeft.TotalSeconds + 1} sec remaining)[/color]"));
|
||||
_cooldownLabel.Visible = true;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -2,75 +2,72 @@ using System.Numerics;
|
||||
using Content.Client.Administration.Systems;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.Administration;
|
||||
|
||||
internal sealed class AdminNameOverlay : Overlay
|
||||
namespace Content.Client.Administration
|
||||
{
|
||||
private readonly AdminSystem _system;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly IEyeManager _eyeManager;
|
||||
private readonly EntityLookupSystem _entityLookup;
|
||||
private readonly IUserInterfaceManager _userInterfaceManager;
|
||||
private readonly Font _font;
|
||||
|
||||
public AdminNameOverlay(AdminSystem system, IEntityManager entityManager, IEyeManager eyeManager, IResourceCache resourceCache, EntityLookupSystem entityLookup, IUserInterfaceManager userInterfaceManager)
|
||||
internal sealed class AdminNameOverlay : Overlay
|
||||
{
|
||||
_system = system;
|
||||
_entityManager = entityManager;
|
||||
_eyeManager = eyeManager;
|
||||
_entityLookup = entityLookup;
|
||||
_userInterfaceManager = userInterfaceManager;
|
||||
ZIndex = 200;
|
||||
_font = new VectorFont(resourceCache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
||||
}
|
||||
private readonly AdminSystem _system;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly IEyeManager _eyeManager;
|
||||
private readonly EntityLookupSystem _entityLookup;
|
||||
private readonly Font _font;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var viewport = args.WorldAABB;
|
||||
|
||||
foreach (var playerInfo in _system.PlayerList)
|
||||
public AdminNameOverlay(AdminSystem system, IEntityManager entityManager, IEyeManager eyeManager, IResourceCache resourceCache, EntityLookupSystem entityLookup)
|
||||
{
|
||||
var entity = _entityManager.GetEntity(playerInfo.NetEntity);
|
||||
_system = system;
|
||||
_entityManager = entityManager;
|
||||
_eyeManager = eyeManager;
|
||||
_entityLookup = entityLookup;
|
||||
ZIndex = 200;
|
||||
_font = new VectorFont(resourceCache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
||||
}
|
||||
|
||||
// Otherwise the entity can not exist yet
|
||||
if (entity == null || !_entityManager.EntityExists(entity))
|
||||
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var viewport = args.WorldAABB;
|
||||
|
||||
foreach (var playerInfo in _system.PlayerList)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var entity = _entityManager.GetEntity(playerInfo.NetEntity);
|
||||
|
||||
// if not on the same map, continue
|
||||
if (_entityManager.GetComponent<TransformComponent>(entity.Value).MapID != args.MapId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Otherwise the entity can not exist yet
|
||||
if (entity == null || !_entityManager.EntityExists(entity))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var aabb = _entityLookup.GetWorldAABB(entity.Value);
|
||||
// if not on the same map, continue
|
||||
if (_entityManager.GetComponent<TransformComponent>(entity.Value).MapID != _eyeManager.CurrentMap)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// if not on screen, continue
|
||||
if (!aabb.Intersects(in viewport))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var aabb = _entityLookup.GetWorldAABB(entity.Value);
|
||||
|
||||
var uiScale = _userInterfaceManager.RootControl.UIScale;
|
||||
var lineoffset = new Vector2(0f, 11f) * uiScale;
|
||||
var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center +
|
||||
new Angle(-_eyeManager.CurrentEye.Rotation).RotateVec(
|
||||
aabb.TopRight - aabb.Center)) + new Vector2(1f, 7f);
|
||||
if (playerInfo.Antag)
|
||||
{
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 2), "ANTAG", uiScale, Color.OrangeRed);
|
||||
;
|
||||
// if not on screen, continue
|
||||
if (!aabb.Intersects(in viewport))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var lineoffset = new Vector2(0f, 11f);
|
||||
var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center +
|
||||
new Angle(-_eyeManager.CurrentEye.Rotation).RotateVec(
|
||||
aabb.TopRight - aabb.Center)) + new Vector2(1f, 7f);
|
||||
if (playerInfo.Antag)
|
||||
{
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 2), "ANTAG", Color.OrangeRed);
|
||||
}
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates+lineoffset, playerInfo.Username, playerInfo.Connected ? Color.Yellow : Color.White);
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates, playerInfo.CharacterName, playerInfo.Connected ? Color.Aquamarine : Color.White);
|
||||
}
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates+lineoffset, playerInfo.Username, uiScale, playerInfo.Connected ? Color.Yellow : Color.White);
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates, playerInfo.CharacterName, uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Administration.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class HeadstandComponent : SharedHeadstandComponent
|
||||
{
|
||||
|
||||
|
||||
@@ -3,5 +3,6 @@ using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Administration.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class KillSignComponent : SharedKillSignComponent;
|
||||
[NetworkedComponent, RegisterComponent]
|
||||
public sealed partial class KillSignComponent : SharedKillSignComponent
|
||||
{ }
|
||||
|
||||
@@ -126,15 +126,12 @@ namespace Content.Client.Administration.Managers
|
||||
|
||||
public AdminData? GetAdminData(EntityUid uid, bool includeDeAdmin = false)
|
||||
{
|
||||
if (uid == _player.LocalEntity && (_adminData?.Active ?? includeDeAdmin))
|
||||
return _adminData;
|
||||
|
||||
return null;
|
||||
return uid == _player.LocalEntity ? _adminData : null;
|
||||
}
|
||||
|
||||
public AdminData? GetAdminData(ICommonSession session, bool includeDeAdmin = false)
|
||||
{
|
||||
if (_player.LocalUser == session.UserId && (_adminData?.Active ?? includeDeAdmin))
|
||||
if (_player.LocalUser == session.UserId)
|
||||
return _adminData;
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
using Content.Shared.Administration;
|
||||
|
||||
namespace Content.Client.Administration.Systems;
|
||||
|
||||
public sealed class AdminFrozenSystem : SharedAdminFrozenSystem
|
||||
{
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Client.Administration.Systems
|
||||
{
|
||||
@@ -13,7 +11,6 @@ namespace Content.Client.Administration.Systems
|
||||
[Dependency] private readonly IClientAdminManager _adminManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
|
||||
private AdminNameOverlay _adminNameOverlay = default!;
|
||||
|
||||
@@ -22,7 +19,7 @@ namespace Content.Client.Administration.Systems
|
||||
|
||||
private void InitializeOverlay()
|
||||
{
|
||||
_adminNameOverlay = new AdminNameOverlay(this, EntityManager, _eyeManager, _resourceCache, _entityLookup, _userInterfaceManager);
|
||||
_adminNameOverlay = new AdminNameOverlay(this, EntityManager, _eyeManager, _resourceCache, _entityLookup);
|
||||
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
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;
|
||||
@@ -14,12 +11,10 @@ namespace Content.Client.Administration.Systems
|
||||
{
|
||||
[Dependency] private readonly IClientConGroupController _clientConGroupController = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _clientConsoleHost = default!;
|
||||
[Dependency] private readonly ISharedAdminManager _admin = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<GetVerbsEvent<Verb>>(AddAdminVerbs);
|
||||
|
||||
}
|
||||
|
||||
private void AddAdminVerbs(GetVerbsEvent<Verb> args)
|
||||
@@ -38,24 +33,6 @@ namespace Content.Client.Administration.Systems
|
||||
};
|
||||
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<MindContainerComponent>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
xmlns:tabs="clr-namespace:Content.Client.Administration.UI.Tabs"
|
||||
xmlns:playerTab="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
|
||||
xmlns:objectsTab="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab"
|
||||
xmlns:panic="clr-namespace:Content.Client.Administration.UI.Tabs.PanicBunkerTab"
|
||||
xmlns:baby="clr-namespace:Content.Client.Administration.UI.Tabs.BabyJailTab">
|
||||
xmlns:panic="clr-namespace:Content.Client.Administration.UI.Tabs.PanicBunkerTab">
|
||||
<TabContainer Name="MasterTabContainer">
|
||||
<adminTab:AdminTab />
|
||||
<adminbusTab:AdminbusTab />
|
||||
@@ -15,7 +14,6 @@
|
||||
<tabs:RoundTab />
|
||||
<tabs:ServerTab />
|
||||
<panic:PanicBunkerTab Name="PanicBunkerControl" Access="Public" />
|
||||
<baby:BabyJailTab Name="BabyJailControl" Access="Public" />
|
||||
<playerTab:PlayerTab Name="PlayerTabControl" Access="Public" />
|
||||
<objectsTab:ObjectsTab Name="ObjectsTabControl" Access="Public" />
|
||||
</TabContainer>
|
||||
|
||||
@@ -3,57 +3,34 @@ using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Administration.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminMenuWindow : DefaultWindow
|
||||
namespace Content.Client.Administration.UI
|
||||
{
|
||||
public event Action? OnDisposed;
|
||||
|
||||
public AdminMenuWindow()
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminMenuWindow : DefaultWindow
|
||||
{
|
||||
MinSize = new Vector2(650, 250);
|
||||
Title = Loc.GetString("admin-menu-title");
|
||||
RobustXamlLoader.Load(this);
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Admin, Loc.GetString("admin-menu-admin-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Adminbus, Loc.GetString("admin-menu-adminbus-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Atmos, Loc.GetString("admin-menu-atmos-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Round, Loc.GetString("admin-menu-round-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Server, Loc.GetString("admin-menu-server-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.PanicBunker, Loc.GetString("admin-menu-panic-bunker-tab"));
|
||||
/*
|
||||
* TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.BabyJail, Loc.GetString("admin-menu-baby-jail-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Players, Loc.GetString("admin-menu-players-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Objects, Loc.GetString("admin-menu-objects-tab"));
|
||||
MasterTabContainer.OnTabChanged += OnTabChanged;
|
||||
}
|
||||
public event Action? OnDisposed;
|
||||
|
||||
private void OnTabChanged(int tabIndex)
|
||||
{
|
||||
var tabEnum = (TabIndex)tabIndex;
|
||||
if (tabEnum == TabIndex.Objects)
|
||||
ObjectsTabControl.RefreshObjectList();
|
||||
}
|
||||
public AdminMenuWindow()
|
||||
{
|
||||
MinSize = new Vector2(650, 250);
|
||||
Title = Loc.GetString("admin-menu-title");
|
||||
RobustXamlLoader.Load(this);
|
||||
MasterTabContainer.SetTabTitle(0, Loc.GetString("admin-menu-admin-tab"));
|
||||
MasterTabContainer.SetTabTitle(1, Loc.GetString("admin-menu-adminbus-tab"));
|
||||
MasterTabContainer.SetTabTitle(2, Loc.GetString("admin-menu-atmos-tab"));
|
||||
MasterTabContainer.SetTabTitle(3, Loc.GetString("admin-menu-round-tab"));
|
||||
MasterTabContainer.SetTabTitle(4, Loc.GetString("admin-menu-server-tab"));
|
||||
MasterTabContainer.SetTabTitle(5, Loc.GetString("admin-menu-panic-bunker-tab"));
|
||||
MasterTabContainer.SetTabTitle(6, Loc.GetString("admin-menu-players-tab"));
|
||||
MasterTabContainer.SetTabTitle(7, Loc.GetString("admin-menu-objects-tab"));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
OnDisposed?.Invoke();
|
||||
base.Dispose(disposing);
|
||||
OnDisposed = null;
|
||||
}
|
||||
|
||||
private enum TabIndex
|
||||
{
|
||||
Admin = 0,
|
||||
Adminbus,
|
||||
Atmos,
|
||||
Round,
|
||||
Server,
|
||||
PanicBunker,
|
||||
BabyJail,
|
||||
Players,
|
||||
Objects,
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
OnDisposed?.Invoke();
|
||||
base.Dispose(disposing);
|
||||
OnDisposed = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
@@ -13,7 +13,7 @@ public sealed partial class AdminMessagePopupMessage : Control
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
Admin.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString(
|
||||
Admin.SetMessage(FormattedMessage.FromMarkup(Loc.GetString(
|
||||
"admin-notes-message-admin",
|
||||
("admin", message.AdminName),
|
||||
("date", message.AddedOn.ToLocalTime()))));
|
||||
|
||||
@@ -49,7 +49,7 @@ public sealed partial class AdminMessagePopupWindow : Control
|
||||
MessageContainer.AddChild(new AdminMessagePopupMessage(message));
|
||||
}
|
||||
|
||||
Description.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString("admin-notes-message-desc", ("count", state.Messages.Length))));
|
||||
Description.SetMessage(FormattedMessage.FromMarkup(Loc.GetString("admin-notes-message-desc", ("count", state.Messages.Length))));
|
||||
}
|
||||
|
||||
private void OnDismissButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Content.Client.Administration.UI.CustomControls;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Client.AutoGenerated;
|
||||
@@ -12,7 +11,6 @@ using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -22,11 +20,11 @@ namespace Content.Client.Administration.UI.BanPanel;
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class BanPanel : DefaultWindow
|
||||
{
|
||||
public event Action<string?, (IPAddress, int)?, bool, ImmutableTypedHwid?, bool, uint, string, NoteSeverity, string[]?, bool>? BanSubmitted;
|
||||
public event Action<string?, (IPAddress, int)?, bool, byte[]?, bool, uint, string, NoteSeverity, string[]?, bool>? BanSubmitted;
|
||||
public event Action<string>? PlayerChanged;
|
||||
private string? PlayerUsername { get; set; }
|
||||
private (IPAddress, int)? IpAddress { get; set; }
|
||||
private ImmutableTypedHwid? Hwid { get; set; }
|
||||
private byte[]? Hwid { get; set; }
|
||||
private double TimeEntered { get; set; }
|
||||
private uint Multiplier { get; set; }
|
||||
private bool HasBanFlag { get; set; }
|
||||
@@ -34,11 +32,8 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
// This is less efficient than just holding a reference to the root control and enumerating children, but you
|
||||
// have to know how the controls are nested, which makes the code more complicated.
|
||||
private readonly List<CheckBox> _roleCheckboxes = new();
|
||||
private readonly ISawmill _banpanelSawmill;
|
||||
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private enum TabNumbers
|
||||
{
|
||||
@@ -70,7 +65,6 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_banpanelSawmill = _logManager.GetSawmill("admin.banpanel");
|
||||
PlayerList.OnSelectionChanged += OnPlayerSelectionChanged;
|
||||
PlayerNameLine.OnFocusExit += _ => OnPlayerNameChanged();
|
||||
PlayerCheckbox.OnPressed += _ =>
|
||||
@@ -110,11 +104,6 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
};
|
||||
SubmitButton.OnPressed += SubmitButtonOnOnPressed;
|
||||
|
||||
IpCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanIpBanDefault);
|
||||
HwidCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanHwidBanDefault);
|
||||
LastConnCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanUseLastDetails);
|
||||
EraseCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanErasePlayer);
|
||||
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-none"), (int) NoteSeverity.None);
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-low"), (int) NoteSeverity.Minor);
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-medium"), (int) NoteSeverity.Medium);
|
||||
@@ -147,7 +136,7 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
foreach (var proto in prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
|
||||
{
|
||||
CreateRoleGroup(proto.ID, proto.Roles.Select(p => p.Id), proto.Color);
|
||||
CreateRoleGroup(proto.ID, proto.Roles, proto.Color);
|
||||
}
|
||||
|
||||
CreateRoleGroup("Antagonist", prototypeManager.EnumeratePrototypes<AntagPrototype>().Select(p => p.ID), Color.Red);
|
||||
@@ -186,39 +175,6 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
c.Pressed = args.Pressed;
|
||||
}
|
||||
}
|
||||
|
||||
if (args.Pressed)
|
||||
{
|
||||
if (!Enum.TryParse(_cfg.GetCVar(CCVars.DepartmentBanDefaultSeverity), true, out NoteSeverity newSeverity))
|
||||
{
|
||||
_banpanelSawmill
|
||||
.Warning("Departmental role ban severity could not be parsed from config!");
|
||||
return;
|
||||
}
|
||||
SeverityOption.SelectId((int) newSeverity);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var childContainer in RolesContainer.Children)
|
||||
{
|
||||
if (childContainer is Container)
|
||||
{
|
||||
foreach (var child in childContainer.Children)
|
||||
{
|
||||
if (child is CheckBox { Pressed: true })
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Enum.TryParse(_cfg.GetCVar(CCVars.RoleBanDefaultSeverity), true, out NoteSeverity newSeverity))
|
||||
{
|
||||
_banpanelSawmill
|
||||
.Warning("Role ban severity could not be parsed from config!");
|
||||
return;
|
||||
}
|
||||
SeverityOption.SelectId((int) newSeverity);
|
||||
}
|
||||
};
|
||||
outerContainer.AddChild(innerContainer);
|
||||
foreach (var role in roleList)
|
||||
@@ -371,8 +327,9 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
private void OnHwidChanged()
|
||||
{
|
||||
var hwidString = HwidLine.Text;
|
||||
ImmutableTypedHwid? hwid = null;
|
||||
if (HwidCheckbox.Pressed && !(string.IsNullOrEmpty(hwidString) && LastConnCheckbox.Pressed) && !ImmutableTypedHwid.TryParse(hwidString, out hwid))
|
||||
var length = 3 * (hwidString.Length / 4) - hwidString.TakeLast(2).Count(c => c == '=');
|
||||
Hwid = new byte[length];
|
||||
if (HwidCheckbox.Pressed && !(string.IsNullOrEmpty(hwidString) && LastConnCheckbox.Pressed) && !Convert.TryFromBase64String(hwidString, Hwid, out _))
|
||||
{
|
||||
ErrorLevel |= ErrorLevelEnum.Hwid;
|
||||
HwidLine.ModulateSelfOverride = Color.Red;
|
||||
@@ -389,42 +346,13 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
Hwid = null;
|
||||
return;
|
||||
}
|
||||
Hwid = hwid;
|
||||
Hwid = Convert.FromHexString(hwidString);
|
||||
}
|
||||
|
||||
private void OnTypeChanged()
|
||||
{
|
||||
TypeOption.ModulateSelfOverride = null;
|
||||
Tabs.SetTabVisible((int) TabNumbers.Roles, TypeOption.SelectedId == (int) Types.Role);
|
||||
NoteSeverity? newSeverity = null;
|
||||
switch (TypeOption.SelectedId)
|
||||
{
|
||||
case (int)Types.Server:
|
||||
if (Enum.TryParse(_cfg.GetCVar(CCVars.ServerBanDefaultSeverity), true, out NoteSeverity serverSeverity))
|
||||
newSeverity = serverSeverity;
|
||||
else
|
||||
{
|
||||
_banpanelSawmill
|
||||
.Warning("Server ban severity could not be parsed from config!");
|
||||
}
|
||||
|
||||
break;
|
||||
case (int) Types.Role:
|
||||
|
||||
if (Enum.TryParse(_cfg.GetCVar(CCVars.RoleBanDefaultSeverity), true, out NoteSeverity roleSeverity))
|
||||
{
|
||||
newSeverity = roleSeverity;
|
||||
}
|
||||
else
|
||||
{
|
||||
_banpanelSawmill
|
||||
.Warning("Role ban severity could not be parsed from config!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (newSeverity != null)
|
||||
SeverityOption.SelectId((int) newSeverity.Value);
|
||||
}
|
||||
|
||||
private void UpdateSubmitEnabled()
|
||||
|
||||
@@ -11,8 +11,9 @@ using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Client.Administration.UI.Bwoink
|
||||
{
|
||||
@@ -74,7 +75,7 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
if (info.Antag && info.ActiveThisRound)
|
||||
sb.Append(new Rune(0x1F5E1)); // 🗡
|
||||
|
||||
if (info.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold)))
|
||||
if (info.OverallPlaytime <= TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.NewPlayerThreshold)))
|
||||
sb.Append(new Rune(0x23F2)); // ⏲
|
||||
|
||||
sb.AppendFormat("\"{0}\"", text);
|
||||
@@ -87,51 +88,26 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
var ach = AHelpHelper.EnsurePanel(a.SessionId);
|
||||
var bch = AHelpHelper.EnsurePanel(b.SessionId);
|
||||
|
||||
// Pinned players first
|
||||
if (a.IsPinned != b.IsPinned)
|
||||
return a.IsPinned ? -1 : 1;
|
||||
|
||||
// First, sort by unread. Any chat with unread messages appears first.
|
||||
// First, sort by unread. Any chat with unread messages appears first. We just sort based on unread
|
||||
// status, not number of unread messages, so that more recent unread messages take priority.
|
||||
var aUnread = ach.Unread > 0;
|
||||
var bUnread = bch.Unread > 0;
|
||||
if (aUnread != bUnread)
|
||||
return aUnread ? -1 : 1;
|
||||
|
||||
// Sort by recent messages during the current round.
|
||||
var aRecent = a.ActiveThisRound && ach.LastMessage != DateTime.MinValue;
|
||||
var bRecent = b.ActiveThisRound && bch.LastMessage != DateTime.MinValue;
|
||||
if (aRecent != bRecent)
|
||||
return aRecent ? -1 : 1;
|
||||
|
||||
// Next, sort by connection status. Any disconnected players are grouped towards the end.
|
||||
if (a.Connected != b.Connected)
|
||||
return a.Connected ? -1 : 1;
|
||||
|
||||
// Sort connected players by New Player status, then by Antag status
|
||||
if (a.Connected && b.Connected)
|
||||
{
|
||||
var aNewPlayer = a.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold));
|
||||
var bNewPlayer = b.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold));
|
||||
|
||||
if (aNewPlayer != bNewPlayer)
|
||||
return aNewPlayer ? -1 : 1;
|
||||
|
||||
if (a.Antag != b.Antag)
|
||||
return a.Antag ? -1 : 1;
|
||||
}
|
||||
|
||||
// Sort disconnected players by participation in the round
|
||||
if (!a.Connected && !b.Connected)
|
||||
{
|
||||
if (a.ActiveThisRound != b.ActiveThisRound)
|
||||
return a.ActiveThisRound ? -1 : 1;
|
||||
}
|
||||
// Next, group by whether or not the players have participated in this round.
|
||||
// The ahelp window shows all players that have connected since server restart, this groups them all towards the bottom.
|
||||
if (a.ActiveThisRound != b.ActiveThisRound)
|
||||
return a.ActiveThisRound ? -1 : 1;
|
||||
|
||||
// Finally, sort by the most recent message.
|
||||
return bch.LastMessage.CompareTo(ach.LastMessage);
|
||||
};
|
||||
|
||||
|
||||
Bans.OnPressed += _ =>
|
||||
{
|
||||
if (_currentPlayer is not null)
|
||||
@@ -250,7 +226,7 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
if (pl.Antag)
|
||||
sb.Append(new Rune(0x1F5E1)); // 🗡
|
||||
|
||||
if (pl.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold)))
|
||||
if (pl.OverallPlaytime <= TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.NewPlayerThreshold)))
|
||||
sb.Append(new Rune(0x23F2)); // ⏲
|
||||
|
||||
sb.AppendFormat("\"{0}\"", pl.CharacterName);
|
||||
@@ -267,9 +243,9 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
{
|
||||
UpdateButtons();
|
||||
|
||||
AHelpHelper.HideAllPanels();
|
||||
if (ch != null)
|
||||
{
|
||||
AHelpHelper.HideAllPanels();
|
||||
var panel = AHelpHelper.EnsurePanel(ch.Value);
|
||||
panel.Visible = true;
|
||||
}
|
||||
@@ -277,20 +253,7 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
|
||||
public void PopulateList()
|
||||
{
|
||||
// Maintain existing pin statuses
|
||||
var pinnedPlayers = ChannelSelector.PlayerInfo.Where(p => p.IsPinned).ToDictionary(p => p.SessionId);
|
||||
|
||||
ChannelSelector.PopulateList();
|
||||
|
||||
// Restore pin statuses
|
||||
foreach (var player in ChannelSelector.PlayerInfo)
|
||||
{
|
||||
if (pinnedPlayers.TryGetValue(player.SessionId, out var pinnedPlayer))
|
||||
{
|
||||
player.IsPinned = pinnedPlayer.IsPinned;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateButtons();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
Unread++;
|
||||
|
||||
var formatted = new FormattedMessage(1);
|
||||
formatted.AddMarkupOrThrow($"[color=gray]{message.SentAt.ToShortTimeString()}[/color] {message.Text}");
|
||||
formatted.AddMarkup($"[color=gray]{message.SentAt.ToShortTimeString()}[/color] {message.Text}");
|
||||
TextOutput.AddMessage(formatted);
|
||||
LastMessage = message.SentAt;
|
||||
}
|
||||
|
||||
@@ -16,25 +16,18 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
|
||||
Bwoink.ChannelSelector.OnSelectionChanged += sel =>
|
||||
{
|
||||
if (sel is null)
|
||||
if (sel is not null)
|
||||
{
|
||||
Title = Loc.GetString("bwoink-title-none-selected");
|
||||
return;
|
||||
}
|
||||
Title = $"{sel.CharacterName} / {sel.Username}";
|
||||
|
||||
Title = $"{sel.CharacterName} / {sel.Username}";
|
||||
|
||||
if (sel.OverallPlaytime != null)
|
||||
{
|
||||
Title += $" | {Loc.GetString("generic-playtime-title")}: {sel.PlaytimeString}";
|
||||
if (sel.OverallPlaytime != null)
|
||||
{
|
||||
Title += $" | {Loc.GetString("generic-playtime-title")}: {sel.PlaytimeString}";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OnOpen += () =>
|
||||
{
|
||||
Bwoink.ChannelSelector.StopFiltering();
|
||||
Bwoink.PopulateList();
|
||||
};
|
||||
OnOpen += () => Bwoink.PopulateList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<LineEdit Name="FilterLineEdit"
|
||||
MinSize="100 0"
|
||||
HorizontalExpand="True"
|
||||
PlaceHolder="{Loc player-list-filter}"/>
|
||||
PlaceHolder="{Loc Filter}"/>
|
||||
<PanelContainer Name="BackgroundPanel"
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="True">
|
||||
|
||||
@@ -4,166 +4,147 @@ using Content.Client.UserInterface.Controls;
|
||||
using Content.Client.Verbs.UI;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration.UI.CustomControls;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class PlayerListControl : BoxContainer
|
||||
namespace Content.Client.Administration.UI.CustomControls
|
||||
{
|
||||
private readonly AdminSystem _adminSystem;
|
||||
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IUserInterfaceManager _uiManager;
|
||||
|
||||
private PlayerInfo? _selectedPlayer;
|
||||
|
||||
private List<PlayerInfo> _playerList = new();
|
||||
private List<PlayerInfo> _sortedPlayerList = new();
|
||||
|
||||
public Comparison<PlayerInfo>? Comparison;
|
||||
public Func<PlayerInfo, string, string>? OverrideText;
|
||||
|
||||
public PlayerListControl()
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class PlayerListControl : BoxContainer
|
||||
{
|
||||
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||
_uiManager = IoCManager.Resolve<IUserInterfaceManager>();
|
||||
_adminSystem = _entManager.System<AdminSystem>();
|
||||
RobustXamlLoader.Load(this);
|
||||
// Fill the Option data
|
||||
PlayerListContainer.ItemPressed += PlayerListItemPressed;
|
||||
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
|
||||
PlayerListContainer.GenerateItem += GenerateButton;
|
||||
PlayerListContainer.NoItemSelected += PlayerListNoItemSelected;
|
||||
PopulateList(_adminSystem.PlayerList);
|
||||
FilterLineEdit.OnTextChanged += _ => FilterList();
|
||||
_adminSystem.PlayerListChanged += PopulateList;
|
||||
BackgroundPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = new Color(32, 32, 40) };
|
||||
}
|
||||
private readonly AdminSystem _adminSystem;
|
||||
|
||||
public IReadOnlyList<PlayerInfo> PlayerInfo => _playerList;
|
||||
private List<PlayerInfo> _playerList = new();
|
||||
private readonly List<PlayerInfo> _sortedPlayerList = new();
|
||||
|
||||
public event Action<PlayerInfo?>? OnSelectionChanged;
|
||||
public event Action<PlayerInfo>? OnSelectionChanged;
|
||||
public IReadOnlyList<PlayerInfo> PlayerInfo => _playerList;
|
||||
|
||||
private void PlayerListNoItemSelected()
|
||||
{
|
||||
_selectedPlayer = null;
|
||||
OnSelectionChanged?.Invoke(null);
|
||||
}
|
||||
public Func<PlayerInfo, string, string>? OverrideText;
|
||||
public Comparison<PlayerInfo>? Comparison;
|
||||
|
||||
private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
|
||||
{
|
||||
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
|
||||
return;
|
||||
private IEntityManager _entManager;
|
||||
private IUserInterfaceManager _uiManager;
|
||||
|
||||
if (selectedPlayer == _selectedPlayer)
|
||||
return;
|
||||
private PlayerInfo? _selectedPlayer;
|
||||
|
||||
if (args.Event.Function != EngineKeyFunctions.UIClick)
|
||||
return;
|
||||
|
||||
OnSelectionChanged?.Invoke(selectedPlayer);
|
||||
_selectedPlayer = selectedPlayer;
|
||||
|
||||
// update label text. Only required if there is some override (e.g. unread bwoink count).
|
||||
if (OverrideText != null && args.Button.Children.FirstOrDefault()?.Children?.FirstOrDefault() is Label label)
|
||||
label.Text = GetText(selectedPlayer);
|
||||
}
|
||||
|
||||
private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
|
||||
{
|
||||
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
|
||||
return;
|
||||
|
||||
if (args.Function != EngineKeyFunctions.UIRightClick || selectedPlayer.NetEntity == null)
|
||||
return;
|
||||
|
||||
_uiManager.GetUIController<VerbMenuUIController>().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
|
||||
args.Handle();
|
||||
}
|
||||
|
||||
public void StopFiltering()
|
||||
{
|
||||
FilterLineEdit.Text = string.Empty;
|
||||
}
|
||||
|
||||
private void FilterList()
|
||||
{
|
||||
_sortedPlayerList.Clear();
|
||||
foreach (var info in _playerList)
|
||||
public PlayerListControl()
|
||||
{
|
||||
var displayName = $"{info.CharacterName} ({info.Username})";
|
||||
if (info.IdentityName != info.CharacterName)
|
||||
displayName += $" [{info.IdentityName}]";
|
||||
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
|
||||
&& !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
|
||||
continue;
|
||||
_sortedPlayerList.Add(info);
|
||||
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||
_uiManager = IoCManager.Resolve<IUserInterfaceManager>();
|
||||
_adminSystem = _entManager.System<AdminSystem>();
|
||||
RobustXamlLoader.Load(this);
|
||||
// Fill the Option data
|
||||
PlayerListContainer.ItemPressed += PlayerListItemPressed;
|
||||
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
|
||||
PlayerListContainer.GenerateItem += GenerateButton;
|
||||
PopulateList(_adminSystem.PlayerList);
|
||||
FilterLineEdit.OnTextChanged += _ => FilterList();
|
||||
_adminSystem.PlayerListChanged += PopulateList;
|
||||
BackgroundPanel.PanelOverride = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 40)};
|
||||
}
|
||||
|
||||
if (Comparison != null)
|
||||
_sortedPlayerList.Sort((a, b) => Comparison(a, b));
|
||||
|
||||
PlayerListContainer.PopulateList(_sortedPlayerList.Select(info => new PlayerListData(info)).ToList());
|
||||
if (_selectedPlayer != null)
|
||||
PlayerListContainer.Select(new PlayerListData(_selectedPlayer));
|
||||
}
|
||||
|
||||
|
||||
public void PopulateList(IReadOnlyList<PlayerInfo>? players = null)
|
||||
{
|
||||
// Maintain existing pin statuses
|
||||
var pinnedPlayers = _playerList.Where(p => p.IsPinned).ToDictionary(p => p.SessionId);
|
||||
|
||||
players ??= _adminSystem.PlayerList;
|
||||
|
||||
_playerList = players.ToList();
|
||||
|
||||
// Restore pin statuses
|
||||
foreach (var player in _playerList)
|
||||
private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
|
||||
{
|
||||
if (pinnedPlayers.TryGetValue(player.SessionId, out var pinnedPlayer))
|
||||
if (args == null || data is not PlayerListData {Info: var selectedPlayer})
|
||||
return;
|
||||
|
||||
if (selectedPlayer == _selectedPlayer)
|
||||
return;
|
||||
|
||||
if (args.Event.Function != EngineKeyFunctions.UIClick)
|
||||
return;
|
||||
|
||||
OnSelectionChanged?.Invoke(selectedPlayer);
|
||||
_selectedPlayer = selectedPlayer;
|
||||
|
||||
// update label text. Only required if there is some override (e.g. unread bwoink count).
|
||||
if (OverrideText != null && args.Button.Children.FirstOrDefault()?.Children?.FirstOrDefault() is Label label)
|
||||
label.Text = GetText(selectedPlayer);
|
||||
}
|
||||
|
||||
private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
|
||||
{
|
||||
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
|
||||
return;
|
||||
|
||||
if (args.Function != EngineKeyFunctions.UIRightClick || selectedPlayer.NetEntity == null)
|
||||
return;
|
||||
|
||||
_uiManager.GetUIController<VerbMenuUIController>().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
|
||||
args.Handle();
|
||||
}
|
||||
|
||||
public void StopFiltering()
|
||||
{
|
||||
FilterLineEdit.Text = string.Empty;
|
||||
}
|
||||
|
||||
private void FilterList()
|
||||
{
|
||||
_sortedPlayerList.Clear();
|
||||
foreach (var info in _playerList)
|
||||
{
|
||||
player.IsPinned = pinnedPlayer.IsPinned;
|
||||
var displayName = $"{info.CharacterName} ({info.Username})";
|
||||
if (info.IdentityName != info.CharacterName)
|
||||
displayName += $" [{info.IdentityName}]";
|
||||
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
|
||||
&& !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
|
||||
continue;
|
||||
_sortedPlayerList.Add(info);
|
||||
}
|
||||
|
||||
if (Comparison != null)
|
||||
_sortedPlayerList.Sort((a, b) => Comparison(a, b));
|
||||
|
||||
PlayerListContainer.PopulateList(_sortedPlayerList.Select(info => new PlayerListData(info)).ToList());
|
||||
if (_selectedPlayer != null)
|
||||
PlayerListContainer.Select(new PlayerListData(_selectedPlayer));
|
||||
}
|
||||
|
||||
if (_selectedPlayer != null && !_playerList.Contains(_selectedPlayer))
|
||||
_selectedPlayer = null;
|
||||
|
||||
FilterList();
|
||||
}
|
||||
|
||||
|
||||
private string GetText(PlayerInfo info)
|
||||
{
|
||||
var text = $"{info.CharacterName} ({info.Username})";
|
||||
if (OverrideText != null)
|
||||
text = OverrideText.Invoke(info, text);
|
||||
return text;
|
||||
}
|
||||
|
||||
private void GenerateButton(ListData data, ListContainerButton button)
|
||||
{
|
||||
if (data is not PlayerListData { Info: var info })
|
||||
return;
|
||||
|
||||
var entry = new PlayerListEntry();
|
||||
entry.Setup(info, OverrideText);
|
||||
entry.OnPinStatusChanged += _ =>
|
||||
public void PopulateList(IReadOnlyList<PlayerInfo>? players = null)
|
||||
{
|
||||
players ??= _adminSystem.PlayerList;
|
||||
|
||||
_playerList = players.ToList();
|
||||
if (_selectedPlayer != null && !_playerList.Contains(_selectedPlayer))
|
||||
_selectedPlayer = null;
|
||||
|
||||
FilterList();
|
||||
};
|
||||
}
|
||||
|
||||
button.AddChild(entry);
|
||||
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
|
||||
private string GetText(PlayerInfo info)
|
||||
{
|
||||
var text = $"{info.CharacterName} ({info.Username})";
|
||||
if (OverrideText != null)
|
||||
text = OverrideText.Invoke(info, text);
|
||||
return text;
|
||||
}
|
||||
|
||||
private void GenerateButton(ListData data, ListContainerButton button)
|
||||
{
|
||||
if (data is not PlayerListData { Info: var info })
|
||||
return;
|
||||
|
||||
button.AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
ClipText = true,
|
||||
Text = GetText(info)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public record PlayerListData(PlayerInfo Info) : ListData;
|
||||
public record PlayerListData(PlayerInfo Info) : ListData;
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
Orientation="Horizontal" HorizontalExpand="true">
|
||||
<Label Name="PlayerEntryLabel" Text="" ClipText="True" HorizontalExpand="True" />
|
||||
<TextureButton Name="PlayerEntryPinButton"
|
||||
HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
@@ -1,58 +0,0 @@
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration.UI.CustomControls;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class PlayerListEntry : BoxContainer
|
||||
{
|
||||
public PlayerListEntry()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public event Action<PlayerInfo>? OnPinStatusChanged;
|
||||
|
||||
public void Setup(PlayerInfo info, Func<PlayerInfo, string, string>? overrideText)
|
||||
{
|
||||
Update(info, overrideText);
|
||||
PlayerEntryPinButton.OnPressed += HandlePinButtonPressed(info);
|
||||
}
|
||||
|
||||
private Action<BaseButton.ButtonEventArgs> HandlePinButtonPressed(PlayerInfo info)
|
||||
{
|
||||
return args =>
|
||||
{
|
||||
info.IsPinned = !info.IsPinned;
|
||||
UpdatePinButtonTexture(info.IsPinned);
|
||||
OnPinStatusChanged?.Invoke(info);
|
||||
};
|
||||
}
|
||||
|
||||
private void Update(PlayerInfo info, Func<PlayerInfo, string, string>? overrideText)
|
||||
{
|
||||
PlayerEntryLabel.Text = overrideText?.Invoke(info, $"{info.CharacterName} ({info.Username})") ??
|
||||
$"{info.CharacterName} ({info.Username})";
|
||||
|
||||
UpdatePinButtonTexture(info.IsPinned);
|
||||
}
|
||||
|
||||
private void UpdatePinButtonTexture(bool isPinned)
|
||||
{
|
||||
if (isPinned)
|
||||
{
|
||||
PlayerEntryPinButton?.RemoveStyleClass(StyleNano.StyleClassPinButtonUnpinned);
|
||||
PlayerEntryPinButton?.AddStyleClass(StyleNano.StyleClassPinButtonPinned);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayerEntryPinButton?.RemoveStyleClass(StyleNano.StyleClassPinButtonPinned);
|
||||
PlayerEntryPinButton?.AddStyleClass(StyleNano.StyleClassPinButtonUnpinned);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
<Popup xmlns="https://spacestation14.io"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||
<PanelContainer>
|
||||
<PanelContainer StyleClasses="BackgroundDark">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BorderThickness="2" BorderColor="#18181B" BackgroundColor="#25252a"/>
|
||||
<gfx:StyleBoxFlat BorderThickness="1" BorderColor="#18181B"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Orientation="Vertical" Margin="4 4 4 4">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Label Name="PlayerNameLabel"/>
|
||||
<Label Name="IdLabel"/>
|
||||
<Label Name="TypeLabel"/>
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
<Label Name="ExpiryLabel" Text="{Loc admin-note-editor-expiry-label}" Visible="False" />
|
||||
<HistoryLineEdit Name="ExpiryLineEdit" PlaceHolder="{Loc admin-note-editor-expiry-placeholder}"
|
||||
Visible="False" HorizontalExpand="True" />
|
||||
<OptionButton Name="ExpiryLengthDropdown" Visible="False" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<OptionButton Name="TypeOption" HorizontalAlignment="Center" />
|
||||
|
||||
@@ -17,17 +17,6 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||
|
||||
private enum Multipliers
|
||||
{
|
||||
Minutes,
|
||||
Hours,
|
||||
Days,
|
||||
Weeks,
|
||||
Months,
|
||||
Years,
|
||||
Centuries
|
||||
}
|
||||
|
||||
public event Action<int, NoteType, string, NoteSeverity?, bool, DateTime?>? SubmitPressed;
|
||||
|
||||
public NoteEdit(SharedAdminNote? note, string playerName, bool canCreate, bool canEdit)
|
||||
@@ -42,20 +31,6 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
|
||||
ResetSubmitButton();
|
||||
|
||||
// It's weird to use minutes as the IDs, but it works and makes sense kind of :)
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-minutes"), (int) Multipliers.Minutes);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-hours"), (int) Multipliers.Hours);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-days"), (int) Multipliers.Days);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-weeks"), (int) Multipliers.Weeks);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-months"), (int) Multipliers.Months);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-years"), (int) Multipliers.Years);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-centuries"), (int) Multipliers.Centuries);
|
||||
ExpiryLengthDropdown.OnItemSelected += OnLengthChanged;
|
||||
|
||||
ExpiryLengthDropdown.SelectId((int) Multipliers.Weeks);
|
||||
|
||||
ExpiryLineEdit.OnTextChanged += OnTextChanged;
|
||||
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-note"), (int) NoteType.Note);
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-message"), (int) NoteType.Message);
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-watchlist"), (int) NoteType.Watchlist);
|
||||
@@ -159,7 +134,6 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
SecretCheckBox.Pressed = false;
|
||||
SeverityOption.Disabled = false;
|
||||
PermanentCheckBox.Pressed = true;
|
||||
SubmitButton.Disabled = true;
|
||||
UpdatePermanentCheckboxFields();
|
||||
break;
|
||||
case (int) NoteType.Message: // Message: these are shown to the player when they log on
|
||||
@@ -198,9 +172,8 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
{
|
||||
ExpiryLabel.Visible = !PermanentCheckBox.Pressed;
|
||||
ExpiryLineEdit.Visible = !PermanentCheckBox.Pressed;
|
||||
ExpiryLengthDropdown.Visible = !PermanentCheckBox.Pressed;
|
||||
|
||||
ExpiryLineEdit.Text = !PermanentCheckBox.Pressed ? 1.ToString() : string.Empty;
|
||||
ExpiryLineEdit.Text = !PermanentCheckBox.Pressed ? DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") : string.Empty;
|
||||
}
|
||||
|
||||
private void OnSecretPressed(BaseButton.ButtonEventArgs _)
|
||||
@@ -214,16 +187,6 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
SeverityOption.SelectId(args.Id);
|
||||
}
|
||||
|
||||
private void OnLengthChanged(OptionButton.ItemSelectedEventArgs args)
|
||||
{
|
||||
ExpiryLengthDropdown.SelectId(args.Id);
|
||||
}
|
||||
|
||||
private void OnTextChanged(HistoryLineEdit.LineEditEventArgs args)
|
||||
{
|
||||
ParseExpiryTime();
|
||||
}
|
||||
|
||||
private void OnSubmitButtonPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
if (!ParseExpiryTime())
|
||||
@@ -300,24 +263,13 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ExpiryLineEdit.Text) || !uint.TryParse(ExpiryLineEdit.Text, out var inputInt))
|
||||
if (string.IsNullOrWhiteSpace(ExpiryLineEdit.Text) || !DateTime.TryParse(ExpiryLineEdit.Text, out var result) || DateTime.UtcNow > result)
|
||||
{
|
||||
ExpiryLineEdit.ModulateSelfOverride = Color.Red;
|
||||
return false;
|
||||
}
|
||||
|
||||
var mult = ExpiryLengthDropdown.SelectedId switch
|
||||
{
|
||||
(int) Multipliers.Minutes => TimeSpan.FromMinutes(1).TotalMinutes,
|
||||
(int) Multipliers.Hours => TimeSpan.FromHours(1).TotalMinutes,
|
||||
(int) Multipliers.Days => TimeSpan.FromDays(1).TotalMinutes,
|
||||
(int) Multipliers.Weeks => TimeSpan.FromDays(7).TotalMinutes,
|
||||
(int) Multipliers.Months => TimeSpan.FromDays(30).TotalMinutes,
|
||||
(int) Multipliers.Years => TimeSpan.FromDays(365).TotalMinutes,
|
||||
(int) Multipliers.Centuries => TimeSpan.FromDays(36525).TotalMinutes,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(ExpiryLengthDropdown.SelectedId), "Multiplier out of range :(")
|
||||
};
|
||||
ExpiryTime = DateTime.UtcNow.AddMinutes(inputInt * mult);
|
||||
ExpiryTime = result.ToUniversalTime();
|
||||
ExpiryLineEdit.ModulateSelfOverride = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
<ui:FancyWindow
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc ban-panel-title}" MinSize="300 300">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="PlayerName"/>
|
||||
<Button Name="UsernameCopyButton" Text="{Loc player-panel-copy-username}"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="Whitelisted"/>
|
||||
<controls:ConfirmButton Name="WhitelistToggle" Text="{Loc 'player-panel-false'}" Visible="False"></controls:ConfirmButton>
|
||||
</BoxContainer>
|
||||
<Label Name="Playtime"/>
|
||||
<Label Name="Notes"/>
|
||||
<Label Name="Bans"/>
|
||||
<Label Name="RoleBans"/>
|
||||
<Label Name="SharedConnections"/>
|
||||
|
||||
<BoxContainer Align="Center">
|
||||
<GridContainer Rows="5">
|
||||
<Button Name="NotesButton" Text="{Loc player-panel-show-notes}" SetWidth="136" Disabled="True"/>
|
||||
<Button Name="AhelpButton" Text="{Loc player-panel-help}" Disabled="True"/>
|
||||
<Button Name="FreezeButton" Text = "{Loc player-panel-freeze}" Disabled="True"/>
|
||||
<controls:ConfirmButton Name="KickButton" Text="{Loc player-panel-kick}" Disabled="True"/>
|
||||
<controls:ConfirmButton Name="DeleteButton" Text="{Loc player-panel-delete}" Disabled="True"/>
|
||||
<Button Name="ShowBansButton" Text="{Loc player-panel-show-bans}" SetWidth="136" Disabled="True"/>
|
||||
<Button Name="LogsButton" Text="{Loc player-panel-logs}" Disabled="True"/>
|
||||
<Button Name="FreezeAndMuteToggleButton" Text="{Loc player-panel-freeze-and-mute}" Disabled="True"/>
|
||||
<Button Name="BanButton" Text="{Loc player-panel-ban}" Disabled="True"/>
|
||||
<controls:ConfirmButton Name="RejuvenateButton" Text="{Loc player-panel-rejuvenate}" Disabled="True"/>
|
||||
</GridContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</ui:FancyWindow>
|
||||
@@ -1,132 +0,0 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration.UI.PlayerPanel;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class PlayerPanel : FancyWindow
|
||||
{
|
||||
private readonly IClientAdminManager _adminManager;
|
||||
|
||||
public event Action<string>? OnUsernameCopy;
|
||||
public event Action<NetUserId?>? OnOpenNotes;
|
||||
public event Action<NetUserId?>? OnOpenBans;
|
||||
public event Action<NetUserId?>? OnAhelp;
|
||||
public event Action<string?>? OnKick;
|
||||
public event Action<NetUserId?>? OnOpenBanPanel;
|
||||
public event Action<NetUserId?, bool>? OnWhitelistToggle;
|
||||
public event Action? OnFreezeAndMuteToggle;
|
||||
public event Action? OnFreeze;
|
||||
public event Action? OnLogs;
|
||||
public event Action? OnDelete;
|
||||
public event Action? OnRejuvenate;
|
||||
|
||||
public NetUserId? TargetPlayer;
|
||||
public string? TargetUsername;
|
||||
private bool _isWhitelisted;
|
||||
|
||||
public PlayerPanel(IClientAdminManager adminManager)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_adminManager = adminManager;
|
||||
|
||||
UsernameCopyButton.OnPressed += _ => OnUsernameCopy?.Invoke(PlayerName.Text ?? "");
|
||||
BanButton.OnPressed += _ => OnOpenBanPanel?.Invoke(TargetPlayer);
|
||||
KickButton.OnPressed += _ => OnKick?.Invoke(TargetUsername);
|
||||
NotesButton.OnPressed += _ => OnOpenNotes?.Invoke(TargetPlayer);
|
||||
ShowBansButton.OnPressed += _ => OnOpenBans?.Invoke(TargetPlayer);
|
||||
AhelpButton.OnPressed += _ => OnAhelp?.Invoke(TargetPlayer);
|
||||
WhitelistToggle.OnPressed += _ =>
|
||||
{
|
||||
OnWhitelistToggle?.Invoke(TargetPlayer, _isWhitelisted);
|
||||
SetWhitelisted(!_isWhitelisted);
|
||||
};
|
||||
FreezeButton.OnPressed += _ => OnFreeze?.Invoke();
|
||||
FreezeAndMuteToggleButton.OnPressed += _ => OnFreezeAndMuteToggle?.Invoke();
|
||||
LogsButton.OnPressed += _ => OnLogs?.Invoke();
|
||||
DeleteButton.OnPressed += _ => OnDelete?.Invoke();
|
||||
RejuvenateButton.OnPressed += _ => OnRejuvenate?.Invoke();
|
||||
}
|
||||
|
||||
public void SetUsername(string player)
|
||||
{
|
||||
Title = Loc.GetString("player-panel-title", ("player", player));
|
||||
PlayerName.Text = Loc.GetString("player-panel-username", ("player", player));
|
||||
}
|
||||
|
||||
public void SetWhitelisted(bool? whitelisted)
|
||||
{
|
||||
if (whitelisted == null)
|
||||
{
|
||||
Whitelisted.Text = null;
|
||||
WhitelistToggle.Visible = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Whitelisted.Text = Loc.GetString("player-panel-whitelisted");
|
||||
WhitelistToggle.Text = whitelisted.Value ? Loc.GetString("player-panel-true") : Loc.GetString("player-panel-false");
|
||||
WhitelistToggle.Visible = true;
|
||||
_isWhitelisted = whitelisted.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetBans(int? totalBans, int? totalRoleBans)
|
||||
{
|
||||
// If one value exists then so should the other.
|
||||
DebugTools.Assert(totalBans.HasValue && totalRoleBans.HasValue || totalBans == null && totalRoleBans == null);
|
||||
|
||||
Bans.Text = totalBans != null ? Loc.GetString("player-panel-bans", ("totalBans", totalBans)) : null;
|
||||
|
||||
RoleBans.Text = totalRoleBans != null ? Loc.GetString("player-panel-rolebans", ("totalRoleBans", totalRoleBans)) : null;
|
||||
}
|
||||
|
||||
public void SetNotes(int? totalNotes)
|
||||
{
|
||||
Notes.Text = totalNotes != null ? Loc.GetString("player-panel-notes", ("totalNotes", totalNotes)) : null;
|
||||
}
|
||||
|
||||
public void SetSharedConnections(int sharedConnections)
|
||||
{
|
||||
SharedConnections.Text = Loc.GetString("player-panel-shared-connections", ("sharedConnections", sharedConnections));
|
||||
}
|
||||
|
||||
public void SetPlaytime(TimeSpan playtime)
|
||||
{
|
||||
Playtime.Text = Loc.GetString("player-panel-playtime",
|
||||
("days", playtime.Days),
|
||||
("hours", playtime.Hours % 24),
|
||||
("minutes", playtime.Minutes % (24 * 60)));
|
||||
}
|
||||
|
||||
public void SetFrozen(bool canFreeze, bool frozen)
|
||||
{
|
||||
FreezeAndMuteToggleButton.Disabled = !canFreeze;
|
||||
FreezeButton.Disabled = !canFreeze || frozen;
|
||||
|
||||
FreezeAndMuteToggleButton.Text = Loc.GetString(!frozen ? "player-panel-freeze-and-mute" : "player-panel-unfreeze");
|
||||
}
|
||||
|
||||
public void SetAhelp(bool canAhelp)
|
||||
{
|
||||
AhelpButton.Disabled = !canAhelp;
|
||||
}
|
||||
|
||||
public void SetButtons()
|
||||
{
|
||||
BanButton.Disabled = !_adminManager.CanCommand("banpanel");
|
||||
KickButton.Disabled = !_adminManager.CanCommand("kick");
|
||||
NotesButton.Disabled = !_adminManager.CanCommand("adminnotes");
|
||||
ShowBansButton.Disabled = !_adminManager.CanCommand("banlist");
|
||||
WhitelistToggle.Disabled =
|
||||
!(_adminManager.CanCommand("whitelistadd") && _adminManager.CanCommand("whitelistremove"));
|
||||
LogsButton.Disabled = !_adminManager.CanCommand("adminlogs");
|
||||
RejuvenateButton.Disabled = !_adminManager.HasFlag(AdminFlags.Debug);
|
||||
DeleteButton.Disabled = !_adminManager.HasFlag(AdminFlags.Debug);
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Content.Client.Eui;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Eui;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.Administration.UI.PlayerPanel;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class PlayerPanelEui : BaseEui
|
||||
{
|
||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||
[Dependency] private readonly IClientAdminManager _admin = default!;
|
||||
[Dependency] private readonly IClipboardManager _clipboard = default!;
|
||||
|
||||
private PlayerPanel PlayerPanel { get; }
|
||||
|
||||
public PlayerPanelEui()
|
||||
{
|
||||
PlayerPanel = new PlayerPanel(_admin);
|
||||
|
||||
PlayerPanel.OnUsernameCopy += username => _clipboard.SetText(username);
|
||||
PlayerPanel.OnOpenNotes += id => _console.ExecuteCommand($"adminnotes \"{id}\"");
|
||||
// Kick command does not support GUIDs
|
||||
PlayerPanel.OnKick += username => _console.ExecuteCommand($"kick \"{username}\"");
|
||||
PlayerPanel.OnOpenBanPanel += id => _console.ExecuteCommand($"banpanel \"{id}\"");
|
||||
PlayerPanel.OnOpenBans += id => _console.ExecuteCommand($"banlist \"{id}\"");
|
||||
PlayerPanel.OnAhelp += id => _console.ExecuteCommand($"openahelp \"{id}\"");
|
||||
PlayerPanel.OnWhitelistToggle += (id, whitelisted) =>
|
||||
{
|
||||
_console.ExecuteCommand(whitelisted ? $"whitelistremove \"{id}\"" : $"whitelistadd \"{id}\"");
|
||||
};
|
||||
|
||||
PlayerPanel.OnFreezeAndMuteToggle += () => SendMessage(new PlayerPanelFreezeMessage(true));
|
||||
PlayerPanel.OnFreeze += () => SendMessage(new PlayerPanelFreezeMessage());
|
||||
PlayerPanel.OnLogs += () => SendMessage(new PlayerPanelLogsMessage());
|
||||
PlayerPanel.OnRejuvenate += () => SendMessage(new PlayerPanelRejuvenationMessage());
|
||||
PlayerPanel.OnDelete+= () => SendMessage(new PlayerPanelDeleteMessage());
|
||||
|
||||
PlayerPanel.OnClose += () => SendMessage(new CloseEuiMessage());
|
||||
}
|
||||
|
||||
public override void Opened()
|
||||
{
|
||||
PlayerPanel.OpenCentered();
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
{
|
||||
PlayerPanel.Close();
|
||||
}
|
||||
|
||||
public override void HandleState(EuiStateBase state)
|
||||
{
|
||||
if (state is not PlayerPanelEuiState s)
|
||||
return;
|
||||
|
||||
PlayerPanel.TargetPlayer = s.Guid;
|
||||
PlayerPanel.TargetUsername = s.Username;
|
||||
PlayerPanel.SetUsername(s.Username);
|
||||
PlayerPanel.SetPlaytime(s.Playtime);
|
||||
PlayerPanel.SetBans(s.TotalBans, s.TotalRoleBans);
|
||||
PlayerPanel.SetNotes(s.TotalNotes);
|
||||
PlayerPanel.SetWhitelisted(s.Whitelisted);
|
||||
PlayerPanel.SetSharedConnections(s.SharedConnections);
|
||||
PlayerPanel.SetFrozen(s.CanFreeze, s.Frozen);
|
||||
PlayerPanel.SetAhelp(s.CanAhelp);
|
||||
PlayerPanel.SetButtons();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Administration.UI.SetOutfit
|
||||
@@ -64,18 +64,9 @@ namespace Content.Client.Administration.UI.SetOutfit
|
||||
PopulateByFilter(SearchBar.Text);
|
||||
}
|
||||
|
||||
private IEnumerable<StartingGearPrototype> GetPrototypes()
|
||||
{
|
||||
// Filter out any StartingGearPrototypes that belong to loadouts
|
||||
var loadouts = _prototypeManager.EnumeratePrototypes<LoadoutPrototype>();
|
||||
var loadoutGears = loadouts.Select(l => l.StartingGear);
|
||||
return _prototypeManager.EnumeratePrototypes<StartingGearPrototype>()
|
||||
.Where(p => !loadoutGears.Contains(p.ID));
|
||||
}
|
||||
|
||||
private void PopulateList()
|
||||
{
|
||||
foreach (var gear in GetPrototypes())
|
||||
foreach (var gear in _prototypeManager.EnumeratePrototypes<StartingGearPrototype>())
|
||||
{
|
||||
OutfitList.Add(GetItem(gear, OutfitList));
|
||||
}
|
||||
@@ -84,7 +75,7 @@ namespace Content.Client.Administration.UI.SetOutfit
|
||||
private void PopulateByFilter(string filter)
|
||||
{
|
||||
OutfitList.Clear();
|
||||
foreach (var gear in GetPrototypes())
|
||||
foreach (var gear in _prototypeManager.EnumeratePrototypes<StartingGearPrototype>())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(filter) &&
|
||||
gear.ID.ToLowerInvariant().Contains(filter.Trim().ToLowerInvariant()))
|
||||
|
||||
@@ -25,7 +25,7 @@ public sealed class ExplosionDebugOverlay : Overlay
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace | OverlaySpace.ScreenSpace;
|
||||
|
||||
public Matrix3x2 SpaceMatrix;
|
||||
public Matrix3 SpaceMatrix;
|
||||
public MapId Map;
|
||||
|
||||
private readonly Font _font;
|
||||
@@ -78,8 +78,7 @@ public sealed class ExplosionDebugOverlay : Overlay
|
||||
if (SpaceTiles == null)
|
||||
return;
|
||||
|
||||
Matrix3x2.Invert(SpaceMatrix, out var invSpace);
|
||||
gridBounds = invSpace.TransformBox(args.WorldBounds);
|
||||
gridBounds = Matrix3.Invert(SpaceMatrix).TransformBox(args.WorldBounds);
|
||||
|
||||
DrawText(handle, gridBounds, SpaceMatrix, SpaceTiles, SpaceTileSize);
|
||||
}
|
||||
@@ -87,7 +86,7 @@ public sealed class ExplosionDebugOverlay : Overlay
|
||||
private void DrawText(
|
||||
DrawingHandleScreen handle,
|
||||
Box2 gridBounds,
|
||||
Matrix3x2 transform,
|
||||
Matrix3 transform,
|
||||
Dictionary<int, List<Vector2i>> tileSets,
|
||||
ushort tileSize)
|
||||
{
|
||||
@@ -104,7 +103,7 @@ public sealed class ExplosionDebugOverlay : Overlay
|
||||
if (!gridBounds.Contains(centre))
|
||||
continue;
|
||||
|
||||
var worldCenter = Vector2.Transform(centre, transform);
|
||||
var worldCenter = transform.Transform(centre);
|
||||
|
||||
var screenCenter = _eyeManager.WorldToScreen(worldCenter);
|
||||
|
||||
@@ -120,7 +119,7 @@ public sealed class ExplosionDebugOverlay : Overlay
|
||||
if (tileSets.TryGetValue(0, out var set))
|
||||
{
|
||||
var epicenter = set.First();
|
||||
var worldCenter = Vector2.Transform((epicenter + Vector2Helpers.Half) * tileSize, transform);
|
||||
var worldCenter = transform.Transform((epicenter + Vector2Helpers.Half) * tileSize);
|
||||
var screenCenter = _eyeManager.WorldToScreen(worldCenter) + new Vector2(-24, -24);
|
||||
var text = $"{Intensity[0]:F2}\nΣ={TotalIntensity:F1}\nΔ={Slope:F1}";
|
||||
handle.DrawString(_font, screenCenter, text);
|
||||
@@ -149,12 +148,11 @@ public sealed class ExplosionDebugOverlay : Overlay
|
||||
if (SpaceTiles == null)
|
||||
return;
|
||||
|
||||
Matrix3x2.Invert(SpaceMatrix, out var invSpace);
|
||||
gridBounds = invSpace.TransformBox(args.WorldBounds).Enlarged(2);
|
||||
gridBounds = Matrix3.Invert(SpaceMatrix).TransformBox(args.WorldBounds).Enlarged(2);
|
||||
handle.SetTransform(SpaceMatrix);
|
||||
|
||||
DrawTiles(handle, gridBounds, SpaceTiles, SpaceTileSize);
|
||||
handle.SetTransform(Matrix3x2.Identity);
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
}
|
||||
|
||||
private void DrawTiles(
|
||||
|
||||
@@ -3,7 +3,6 @@ using Content.Shared.Explosion;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
@@ -23,7 +22,7 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
|
||||
private readonly SpawnExplosionEui _eui;
|
||||
private List<MapId> _mapData = new();
|
||||
@@ -38,7 +37,6 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_transform = _entMan.System<TransformSystem>();
|
||||
_eui = eui;
|
||||
|
||||
ExplosionOption.OnItemSelected += ExplosionSelected;
|
||||
@@ -106,7 +104,7 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow
|
||||
|
||||
_pausePreview = true;
|
||||
MapOptions.Select(_mapData.IndexOf(transform.MapID));
|
||||
(MapX.Value, MapY.Value) = _transform.GetMapCoordinates(_playerManager.LocalEntity!.Value, xform: transform).Position;
|
||||
(MapX.Value, MapY.Value) = transform.MapPosition.Position;
|
||||
_pausePreview = false;
|
||||
|
||||
UpdatePreview();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Title="{Loc admin-player-actions-window-title}" MinSize="425 272">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-player-actions-reason}" MinWidth="100" />
|
||||
<Label Text="{Loc Reason}" MinWidth="100" />
|
||||
<Control MinWidth="50" />
|
||||
<LineEdit Name="ReasonLine" MinWidth="100" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<DefaultWindow
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Title="{Loc admin-ui-teleport}" MinSize="425 230">
|
||||
Title="{Loc Teleport}" MinSize="425 230">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<cc:PlayerListControl Name="PlayerList" />
|
||||
<Button Name="SubmitButton" Text="{Loc admin-ui-teleport}" />
|
||||
<Button Name="SubmitButton" Text="{Loc Teleport}" />
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
<DefaultWindow
|
||||
xmlns="https://spacestation14.io" Title="{Loc admin-ui-blueprint-load}">
|
||||
xmlns="https://spacestation14.io" Title="{Loc Load Blueprint}">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-blueprint-map}" MinSize="100 0" />
|
||||
<Label Text="{Loc Map}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<OptionButton Name="MapOptions" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-blueprint-path}" MinSize="100 0" />
|
||||
<Label Text="{Loc Path}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<LineEdit Name="MapPath" MinSize="200 0" HorizontalExpand="True" Text="/Maps/" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-blueprint-x}" MinSize="100 0" />
|
||||
<Label Text="{Loc X}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<SpinBox Name="XCoordinate" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-blueprint-y}" MinSize="100 0" />
|
||||
<Label Text="{Loc Y}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<SpinBox Name="YCoordinate" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-blueprint-rotation}" MinSize="100 0" />
|
||||
<Label Text="{Loc Rotation}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<SpinBox Name="RotationSpin" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<Button Name="SubmitButton" Text="{Loc admin-ui-blueprint-load}" />
|
||||
<Button Name="TeleportButton" Text="{Loc admin-ui-blueprint-teleport}" />
|
||||
<Button Name="ResetButton" Text="{Loc admin-ui-blueprint-reset}"></Button>
|
||||
<Button Name="SubmitButton" Text="{Loc Load Blueprint}" />
|
||||
<Button Name="TeleportButton" Text="{Loc Teleport to}" />
|
||||
<Button Name="ResetButton" Text="{Loc Reset to default}"></Button>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<DefaultWindow
|
||||
xmlns="https://spacestation14.io" Title="{Loc admin-ui-atmos-add}">
|
||||
xmlns="https://spacestation14.io" Title="{Loc Add Atmos}">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-atmos-grid}" MinSize="100 0" />
|
||||
<Label Text="{Loc Grid}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<OptionButton Name="GridOptions" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<Button Name="SubmitButton" Text="{Loc admin-ui-atmos-add}" />
|
||||
<Button Name="SubmitButton" Text="{Loc Add Atmos}" />
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
|
||||
while (query.MoveNext(out var uid, out var grid))
|
||||
{
|
||||
_data.Add((uid, grid));
|
||||
GridOptions.AddItem($"{uid} {(playerGrid == uid ? Loc.GetString($"admin-ui-atmos-grid-current") : "")}");
|
||||
GridOptions.AddItem($"{uid} {(playerGrid == uid ? " (Current)" : "")}");
|
||||
}
|
||||
|
||||
GridOptions.OnItemSelected += eventArgs => GridOptions.SelectId(eventArgs.Id);
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
<DefaultWindow
|
||||
xmlns="https://spacestation14.io" Title="{Loc admin-ui-atmos-add-gas}">
|
||||
xmlns="https://spacestation14.io" Title="{Loc Add Gas}">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-atmos-grid}" MinSize="100 0" />
|
||||
<Label Text="{Loc Grid}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<OptionButton Name="GridOptions" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-atmos-tile-x}" MinSize="100 0" />
|
||||
<Label Text="{Loc TileX}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<SpinBox Name="TileXSpin" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-atmos-tile-y}" MinSize="100 0" />
|
||||
<Label Text="{Loc TileY}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<SpinBox Name="TileYSpin" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-atmos-gas}" MinSize="100 0" />
|
||||
<Label Text="{Loc Gas}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<OptionButton Name="GasOptions" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-atmos-gas-amount}" MinSize="100 0" />
|
||||
<Label Text="{Loc Amount}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<SpinBox Name="AmountSpin" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<Button Name="SubmitButton" Text="{Loc admin-ui-atmos-add-gas}" />
|
||||
<Button Name="SubmitButton" Text="{Loc Add Gas}" />
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
|
||||
_gridData.Add(entManager.GetNetEntity(uid));
|
||||
var player = playerManager.LocalEntity;
|
||||
var playerGrid = entManager.GetComponentOrNull<TransformComponent>(player)?.GridUid;
|
||||
GridOptions.AddItem($"{uid} {(playerGrid == uid ? Loc.GetString("admin-ui-atmos-grid-current") : "")}");
|
||||
GridOptions.AddItem($"{uid} {(playerGrid == uid ? " (Current)" : "")}");
|
||||
}
|
||||
|
||||
GridOptions.OnItemSelected += eventArgs => GridOptions.SelectId(eventArgs.Id);
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
Margin="4"
|
||||
MinSize="50 50">
|
||||
<GridContainer Columns="4">
|
||||
<cc:UICommandButton Text="{Loc admin-ui-atmos-add}" Command="addatmos" WindowType="{x:Type at:AddAtmosWindow}" />
|
||||
<cc:UICommandButton Text="{Loc admin-ui-atmos-add-gas}" Command="addgas" WindowType="{x:Type at:AddGasWindow}" />
|
||||
<cc:UICommandButton Text="{Loc admin-ui-atmos-fill-gas}" Command="fillgas" WindowType="{x:Type at:FillGasWindow}" />
|
||||
<cc:UICommandButton Text="{Loc admin-ui-atmos-set-temperature}" Command="settemp"
|
||||
<cc:UICommandButton Text="{Loc Add Atmos}" Command="addatmos" WindowType="{x:Type at:AddAtmosWindow}" />
|
||||
<cc:UICommandButton Text="{Loc Add Gas}" Command="addgas" WindowType="{x:Type at:AddGasWindow}" />
|
||||
<cc:UICommandButton Text="{Loc Fill Gas}" Command="fillgas" WindowType="{x:Type at:FillGasWindow}" />
|
||||
<cc:UICommandButton Text="{Loc Set Temperature}" Command="settemp"
|
||||
WindowType="{x:Type at:SetTemperatureWindow}" />
|
||||
</GridContainer>
|
||||
</Control>
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
<DefaultWindow
|
||||
xmlns="https://spacestation14.io" Title="{Loc admin-ui-atmos-fill-gas}">
|
||||
xmlns="https://spacestation14.io" Title="{Loc Fill Gas}">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-atmos-grid}" MinSize="100 0" />
|
||||
<Label Text="{Loc Grid}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<OptionButton Name="GridOptions" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-atmos-gas}" MinSize="100 0" />
|
||||
<Label Text="{Loc Gas}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<OptionButton Name="GasOptions" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-atmos-gas-amount}" MinSize="100 0" />
|
||||
<Label Text="{Loc Amount}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<SpinBox Name="AmountSpin" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<Button Name="SubmitButton" Text="{Loc admin-ui-atmos-fill-gas}" />
|
||||
<Button Name="SubmitButton" Text="{Loc Fill Gas}" />
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
|
||||
{
|
||||
var player = playerManager.LocalEntity;
|
||||
var playerGrid = entManager.GetComponentOrNull<TransformComponent>(player)?.GridUid;
|
||||
GridOptions.AddItem($"{uid} {(playerGrid == uid ? Loc.GetString($"admin-ui-atmos-grid-current") : "")}");
|
||||
GridOptions.AddItem($"{uid} {(playerGrid == uid ? " (Current)" : "")}");
|
||||
_gridData.Add(entManager.GetNetEntity(uid));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
<DefaultWindow
|
||||
xmlns="https://spacestation14.io" Title="{Loc admin-ui-atmos-set-temperature}">
|
||||
xmlns="https://spacestation14.io" Title="{Loc Set Temperature}">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-atmos-grid}" MinSize="100 0" />
|
||||
<Label Text="{Loc Grid}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<OptionButton Name="GridOptions" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-atmos-tile-x}" MinSize="100 0" />
|
||||
<Label Text="{Loc TileX}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<SpinBox Name="TileXSpin" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-atmos-tile-y}" MinSize="100 0" />
|
||||
<Label Text="{Loc TileY}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<SpinBox Name="TileYSpin" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-ui-atmos-temperature}" MinSize="100 0" />
|
||||
<Label Text="{Loc Temperature}" MinSize="100 0" />
|
||||
<Control MinSize="50 0" />
|
||||
<SpinBox Name="TemperatureSpin" MinSize="100 0" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<Button Name="SubmitButton" Text="{Loc admin-ui-atmos-set-temperature}" />
|
||||
<Button Name="SubmitButton" Text="{Loc Set Temperature}" />
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
|
||||
{
|
||||
var player = playerManager.LocalEntity;
|
||||
var playerGrid = entManager.GetComponentOrNull<TransformComponent>(player)?.GridUid;
|
||||
GridOptions.AddItem($"{uid} {(playerGrid == uid ? Loc.GetString($"admin-ui-atmos-grid-current") : "")}");
|
||||
GridOptions.AddItem($"{uid} {(playerGrid == uid ? " (Current)" : "")}");
|
||||
_data.Add(entManager.GetNetEntity(uid));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<controls:BabyJailStatusWindow
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.Administration.UI.Tabs.BabyJailTab"
|
||||
Title="{Loc admin-ui-baby-jail-window-title}">
|
||||
<RichTextLabel Name="MessageLabel" Access="Public" />
|
||||
</controls:BabyJailStatusWindow>
|
||||
@@ -1,21 +0,0 @@
|
||||
using Content.Client.Message;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.BabyJailTab;
|
||||
|
||||
/*
|
||||
* TODO: Remove me once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class BabyJailStatusWindow : FancyWindow
|
||||
{
|
||||
public BabyJailStatusWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
MessageLabel.SetMarkup(Loc.GetString("admin-ui-baby-jail-is-enabled"));
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<controls:BabyJailTab
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.Administration.UI.Tabs.BabyJailTab"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Margin="4">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<cc:CommandButton Name="EnabledButton" Command="babyjail" ToggleMode="True"
|
||||
Text="{Loc admin-ui-baby-jail-disabled}"
|
||||
ToolTip="{Loc admin-ui-baby-jail-tooltip}" />
|
||||
<cc:CommandButton Name="ShowReasonButton" Command="babyjail_show_reason"
|
||||
ToggleMode="True" Text="{Loc admin-ui-baby-jail-show-reason}"
|
||||
ToolTip="{Loc admin-ui-baby-jail-show-reason-tooltip}" />
|
||||
<BoxContainer Orientation="Vertical" Margin="0 10 0 0">
|
||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||
<Label Text="{Loc admin-ui-baby-jail-max-account-age}" MinWidth="175" />
|
||||
<LineEdit Name="MaxAccountAge" MinWidth="50" Margin="0 0 5 0" />
|
||||
<Label Text="{Loc generic-minutes}" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||
<Label Text="{Loc admin-ui-baby-jail-max-overall-minutes}" MinWidth="175" />
|
||||
<LineEdit Name="MaxOverallMinutes" MinWidth="50" Margin="0 0 5 0" />
|
||||
<Label Text="{Loc generic-minutes}" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:BabyJailTab>
|
||||
@@ -1,75 +0,0 @@
|
||||
using Content.Shared.Administration.Events;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
/*
|
||||
* TODO: Remove me once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.BabyJailTab;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class BabyJailTab : Control
|
||||
{
|
||||
[Dependency] private readonly IConsoleHost _console = default!;
|
||||
|
||||
private string _maxAccountAge;
|
||||
private string _maxOverallMinutes;
|
||||
|
||||
public BabyJailTab()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
MaxAccountAge.OnTextEntered += args => SendMaxAccountAge(args.Text);
|
||||
MaxAccountAge.OnFocusExit += args => SendMaxAccountAge(args.Text);
|
||||
_maxAccountAge = MaxAccountAge.Text;
|
||||
|
||||
MaxOverallMinutes.OnTextEntered += args => SendMaxOverallMinutes(args.Text);
|
||||
MaxOverallMinutes.OnFocusExit += args => SendMaxOverallMinutes(args.Text);
|
||||
_maxOverallMinutes = MaxOverallMinutes.Text;
|
||||
}
|
||||
|
||||
private void SendMaxAccountAge(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text) ||
|
||||
text == _maxAccountAge ||
|
||||
!int.TryParse(text, out var minutes))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_console.ExecuteCommand($"babyjail_max_account_age {minutes}");
|
||||
}
|
||||
|
||||
private void SendMaxOverallMinutes(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text) ||
|
||||
text == _maxOverallMinutes ||
|
||||
!int.TryParse(text, out var minutes))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_console.ExecuteCommand($"babyjail_max_overall_minutes {minutes}");
|
||||
}
|
||||
|
||||
public void UpdateStatus(BabyJailStatus status)
|
||||
{
|
||||
EnabledButton.Pressed = status.Enabled;
|
||||
EnabledButton.Text = Loc.GetString(status.Enabled
|
||||
? "admin-ui-baby-jail-enabled"
|
||||
: "admin-ui-baby-jail-disabled"
|
||||
);
|
||||
EnabledButton.ModulateSelfOverride = status.Enabled ? Color.Red : null;
|
||||
ShowReasonButton.Pressed = status.ShowReason;
|
||||
|
||||
MaxAccountAge.Text = status.MaxAccountAgeMinutes.ToString();
|
||||
_maxAccountAge = MaxAccountAge.Text;
|
||||
|
||||
MaxOverallMinutes.Text = status.MaxOverallMinutes.ToString();
|
||||
_maxOverallMinutes = MaxOverallMinutes.Text;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,15 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
xmlns:ot="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab"
|
||||
xmlns:co="clr-namespace:Content.Client.UserInterface.Controls">
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc object-tab-object-type}" />
|
||||
<OptionButton Name="ObjectTypeOptions" HorizontalAlignment="Left" />
|
||||
<LineEdit Name="SearchLineEdit" PlaceHolder="{Loc object-tab-object-search}" HorizontalExpand="True"
|
||||
SizeFlagsStretchRatio="1" />
|
||||
<Button Name="RefreshListButton" Text="{Loc object-tab-refresh-button}" ToggleMode="False" />
|
||||
</BoxContainer>
|
||||
<cc:HSeparator />
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
|
||||
<ot:ObjectsTabHeader Name="ListHeader" />
|
||||
<cc:HSeparator />
|
||||
<co:SearchListContainer Name="SearchList" Access="Public" VerticalExpand="True" />
|
||||
<Label HorizontalExpand="True" SizeFlagsStretchRatio="0.50"
|
||||
Text="{Loc Object type:}" />
|
||||
<OptionButton Name="ObjectTypeOptions" HorizontalExpand="True" SizeFlagsStretchRatio="0.25"/>
|
||||
</BoxContainer>
|
||||
<cc:HSeparator/>
|
||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Vertical" Name="ObjectList">
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Content.Client.Station;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class ObjectsTab : Control
|
||||
{
|
||||
[Dependency] private readonly IClientAdminManager _admin = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private readonly Color _altColor = Color.FromHex("#292B38");
|
||||
private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
|
||||
private readonly List<ObjectsTabEntry> _objects = new();
|
||||
private List<ObjectsTabSelection> _selections = new();
|
||||
|
||||
private bool _ascending;
|
||||
private ObjectsTabHeader.Header _headerClicked = ObjectsTabHeader.Header.ObjectName;
|
||||
public event Action<ObjectsTabEntry, GUIBoundKeyEventArgs>? OnEntryKeyBindDown;
|
||||
|
||||
private readonly List<ObjectsTabSelection> _selections = [];
|
||||
public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
|
||||
// Listen I could either have like 4 different event subscribers (for map / grid / station changes) and manage their lifetimes in AdminUIController
|
||||
// OR
|
||||
// I can do this.
|
||||
private TimeSpan _updateFrequency = TimeSpan.FromSeconds(2);
|
||||
|
||||
private TimeSpan _nextUpdate = TimeSpan.FromSeconds(2);
|
||||
|
||||
public ObjectsTab()
|
||||
{
|
||||
@@ -37,35 +36,16 @@ public sealed partial class ObjectsTab : Control
|
||||
RefreshObjectList(_selections[ev.Id]);
|
||||
};
|
||||
|
||||
foreach (var type in Enum.GetValues<ObjectsTabSelection>())
|
||||
foreach (var type in Enum.GetValues(typeof(ObjectsTabSelection)))
|
||||
{
|
||||
_selections.Add(type);
|
||||
ObjectTypeOptions.AddItem(GetLocalizedEnumValue(type));
|
||||
_selections.Add((ObjectsTabSelection)type!);
|
||||
ObjectTypeOptions.AddItem(Enum.GetName((ObjectsTabSelection)type)!);
|
||||
}
|
||||
|
||||
ListHeader.OnHeaderClicked += HeaderClicked;
|
||||
SearchList.SearchBar = SearchLineEdit;
|
||||
SearchList.GenerateItem += GenerateButton;
|
||||
SearchList.DataFilterCondition += DataFilterCondition;
|
||||
SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
|
||||
RefreshListButton.OnPressed += _ => RefreshObjectList();
|
||||
|
||||
var defaultSelection = ObjectsTabSelection.Grids;
|
||||
ObjectTypeOptions.SelectId((int)defaultSelection);
|
||||
RefreshObjectList(defaultSelection);
|
||||
RefreshObjectList();
|
||||
}
|
||||
|
||||
private void TeleportTo(NetEntity nent)
|
||||
{
|
||||
_console.ExecuteCommand($"tpto {nent}");
|
||||
}
|
||||
|
||||
private void Delete(NetEntity nent)
|
||||
{
|
||||
_console.ExecuteCommand($"delete {nent}");
|
||||
}
|
||||
|
||||
public void RefreshObjectList()
|
||||
private void RefreshObjectList()
|
||||
{
|
||||
RefreshObjectList(_selections[ObjectTypeOptions.SelectedId]);
|
||||
}
|
||||
@@ -95,96 +75,41 @@ public sealed partial class ObjectsTab : Control
|
||||
{
|
||||
entities.Add((metadata.EntityName, _entityManager.GetNetEntity(uid)));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(selection), selection, null);
|
||||
}
|
||||
|
||||
entities.Sort((a, b) =>
|
||||
foreach (var control in _objects)
|
||||
{
|
||||
var valueA = GetComparableValue(a, _headerClicked);
|
||||
var valueB = GetComparableValue(b, _headerClicked);
|
||||
return _ascending
|
||||
? Comparer<object>.Default.Compare(valueA, valueB)
|
||||
: Comparer<object>.Default.Compare(valueB, valueA);
|
||||
});
|
||||
|
||||
var listData = new List<ObjectsListData>();
|
||||
for (var index = 0; index < entities.Count; index++)
|
||||
{
|
||||
var info = entities[index];
|
||||
listData.Add(new ObjectsListData(info,
|
||||
$"{info.Name} {info.Entity}",
|
||||
index % 2 == 0 ? _altColor : _defaultColor));
|
||||
ObjectList.RemoveChild(control);
|
||||
}
|
||||
|
||||
SearchList.PopulateList(listData);
|
||||
_objects.Clear();
|
||||
|
||||
foreach (var (name, nent) in entities)
|
||||
{
|
||||
var ctrl = new ObjectsTabEntry(name, nent);
|
||||
_objects.Add(ctrl);
|
||||
ObjectList.AddChild(ctrl);
|
||||
ctrl.OnKeyBindDown += args => OnEntryKeyBindDown?.Invoke(ctrl, args);
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateButton(ListData data, ListContainerButton button)
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
if (data is not ObjectsListData { Info: var info, BackgroundColor: var backgroundColor })
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (_timing.CurTime < _nextUpdate)
|
||||
return;
|
||||
|
||||
var entry = new ObjectsTabEntry(_admin, info.Name, info.Entity, new StyleBoxFlat { BackgroundColor = backgroundColor });
|
||||
entry.OnTeleport += TeleportTo;
|
||||
entry.OnDelete += Delete;
|
||||
button.ToolTip = $"{info.Name}, {info.Entity}";
|
||||
// I do not care for precision.
|
||||
_nextUpdate = _timing.CurTime + _updateFrequency;
|
||||
|
||||
button.AddChild(entry);
|
||||
}
|
||||
|
||||
private bool DataFilterCondition(string filter, ListData listData)
|
||||
{
|
||||
if (listData is not ObjectsListData { FilteringString: var filteringString })
|
||||
return false;
|
||||
|
||||
// If the filter is empty, do not filter out any entries
|
||||
if (string.IsNullOrEmpty(filter))
|
||||
return true;
|
||||
|
||||
return filteringString.Contains(filter, StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
|
||||
private object GetComparableValue((string Name, NetEntity Entity) entity, ObjectsTabHeader.Header header)
|
||||
{
|
||||
return header switch
|
||||
{
|
||||
ObjectsTabHeader.Header.ObjectName => entity.Name,
|
||||
ObjectsTabHeader.Header.EntityID => entity.Entity.ToString(),
|
||||
_ => entity.Name,
|
||||
};
|
||||
}
|
||||
|
||||
private void HeaderClicked(ObjectsTabHeader.Header header)
|
||||
{
|
||||
if (_headerClicked == header)
|
||||
{
|
||||
_ascending = !_ascending;
|
||||
}
|
||||
else
|
||||
{
|
||||
_headerClicked = header;
|
||||
_ascending = true;
|
||||
}
|
||||
|
||||
ListHeader.UpdateHeaderSymbols(_headerClicked, _ascending);
|
||||
RefreshObjectList();
|
||||
}
|
||||
|
||||
private string GetLocalizedEnumValue(ObjectsTabSelection selection)
|
||||
{
|
||||
return selection switch
|
||||
{
|
||||
ObjectsTabSelection.Grids => Loc.GetString("object-tab-object-type-grids"),
|
||||
ObjectsTabSelection.Maps => Loc.GetString("object-tab-object-type-maps"),
|
||||
ObjectsTabSelection.Stations => Loc.GetString("object-tab-object-type-stations"),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(selection), selection, null),
|
||||
};
|
||||
}
|
||||
|
||||
private enum ObjectsTabSelection
|
||||
{
|
||||
Grids,
|
||||
@@ -193,5 +118,3 @@ public sealed partial class ObjectsTab : Control
|
||||
}
|
||||
}
|
||||
|
||||
public record ObjectsListData((string Name, NetEntity Entity) Info, string FilteringString, Color BackgroundColor)
|
||||
: ListData;
|
||||
|
||||
@@ -1,29 +1,17 @@
|
||||
<PanelContainer xmlns="https://spacestation14.io"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Name="BackgroundColorPanel">
|
||||
<ContainerButton xmlns="https://spacestation14.io"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||
<PanelContainer Name="BackgroundColorPanel"/>
|
||||
<BoxContainer Orientation="Horizontal"
|
||||
HorizontalExpand="True"
|
||||
SeparationOverride="4">
|
||||
<Label Name="NameLabel"
|
||||
SizeFlagsStretchRatio="5"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
<customControls:VSeparator/>
|
||||
<Label Name="EIDLabel"
|
||||
SizeFlagsStretchRatio="5"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
<customControls:VSeparator/>
|
||||
<Button Name="TeleportButton"
|
||||
Text="{Loc object-tab-entity-teleport}"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
<customControls:VSeparator/>
|
||||
<Button Name="DeleteButton"
|
||||
Text="{Loc object-tab-entity-delete}"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
<Label Name="EIDLabel"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</ContainerButton>
|
||||
|
||||
@@ -1,40 +1,19 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class ObjectsTabEntry : PanelContainer
|
||||
public sealed partial class ObjectsTabEntry : ContainerButton
|
||||
{
|
||||
public NetEntity AssocEntity;
|
||||
|
||||
public Action<NetEntity>? OnTeleport;
|
||||
public Action<NetEntity>? OnDelete;
|
||||
private readonly Dictionary<Button, ConfirmationData> _confirmations = new();
|
||||
|
||||
public ObjectsTabEntry(IClientAdminManager manager, string name, NetEntity nent, StyleBox styleBox)
|
||||
public ObjectsTabEntry(string name, NetEntity nent)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
AssocEntity = nent;
|
||||
EIDLabel.Text = nent.ToString();
|
||||
NameLabel.Text = name;
|
||||
BackgroundColorPanel.PanelOverride = styleBox;
|
||||
|
||||
TeleportButton.Disabled = !manager.CanCommand("tpto");
|
||||
DeleteButton.Disabled = !manager.CanCommand("delete");
|
||||
|
||||
TeleportButton.OnPressed += _ => OnTeleport?.Invoke(nent);
|
||||
DeleteButton.OnPressed += _ =>
|
||||
{
|
||||
if (!AdminUIHelpers.TryConfirm(DeleteButton, _confirmations))
|
||||
{
|
||||
return;
|
||||
}
|
||||
OnDelete?.Invoke(nent);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||
<PanelContainer Name="BackgroundColorPanel" Access="Public"/>
|
||||
<BoxContainer Orientation="Horizontal"
|
||||
HorizontalExpand="True"
|
||||
SeparationOverride="4">
|
||||
<Label Name="ObjectNameLabel"
|
||||
SizeFlagsStretchRatio="5"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"
|
||||
Text="{Loc object-tab-object-name}"
|
||||
MouseFilter="Pass"/>
|
||||
<cc:VSeparator/>
|
||||
<Label Name="EntityIDLabel"
|
||||
SizeFlagsStretchRatio="5"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"
|
||||
Text="{Loc object-tab-entity-id}"
|
||||
MouseFilter="Pass"/>
|
||||
<Label Name="EntityTeleportLabel"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"/>
|
||||
<Label Name="EntityDeleteLabel"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"/>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
@@ -1,86 +0,0 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Input;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.ObjectsTab
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class ObjectsTabHeader : Control
|
||||
{
|
||||
public event Action<Header>? OnHeaderClicked;
|
||||
|
||||
private const string ArrowUp = "↑";
|
||||
private const string ArrowDown = "↓";
|
||||
|
||||
public ObjectsTabHeader()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
ObjectNameLabel.OnKeyBindDown += ObjectNameClicked;
|
||||
EntityIDLabel.OnKeyBindDown += EntityIDClicked;
|
||||
}
|
||||
|
||||
public Label GetHeader(Header header)
|
||||
{
|
||||
return header switch
|
||||
{
|
||||
Header.ObjectName => ObjectNameLabel,
|
||||
Header.EntityID => EntityIDLabel,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(header), header, null)
|
||||
};
|
||||
}
|
||||
|
||||
public void ResetHeaderText()
|
||||
{
|
||||
ObjectNameLabel.Text = Loc.GetString("object-tab-object-name");
|
||||
EntityIDLabel.Text = Loc.GetString("object-tab-entity-id");
|
||||
}
|
||||
|
||||
public void UpdateHeaderSymbols(Header headerClicked, bool ascending)
|
||||
{
|
||||
ResetHeaderText();
|
||||
var arrow = ascending ? ArrowUp : ArrowDown;
|
||||
GetHeader(headerClicked).Text += $" {arrow}";
|
||||
}
|
||||
|
||||
private void HeaderClicked(GUIBoundKeyEventArgs args, Header header)
|
||||
{
|
||||
if (args.Function != EngineKeyFunctions.UIClick)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnHeaderClicked?.Invoke(header);
|
||||
args.Handle();
|
||||
}
|
||||
|
||||
private void ObjectNameClicked(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
HeaderClicked(args, Header.ObjectName);
|
||||
}
|
||||
|
||||
private void EntityIDClicked(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
HeaderClicked(args, Header.EntityID);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
ObjectNameLabel.OnKeyBindDown -= ObjectNameClicked;
|
||||
EntityIDLabel.OnKeyBindDown -= EntityIDClicked;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Header
|
||||
{
|
||||
ObjectName,
|
||||
EntityID
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,12 +31,12 @@
|
||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||
<Label Text="{Loc admin-ui-panic-bunker-min-account-age}" MinWidth="175" />
|
||||
<LineEdit Name="MinAccountAge" MinWidth="50" Margin="0 0 5 0" />
|
||||
<Label Text="{Loc generic-minutes}" />
|
||||
<Label Text="{Loc generic-hours}" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||
<Label Text="{Loc admin-ui-panic-bunker-min-overall-minutes}" MinWidth="175" />
|
||||
<LineEdit Name="MinOverallMinutes" MinWidth="50" Margin="0 0 5 0" />
|
||||
<Label Text="{Loc generic-minutes}" />
|
||||
<Label Text="{Loc admin-ui-panic-bunker-min-overall-hours}" MinWidth="175" />
|
||||
<LineEdit Name="MinOverallHours" MinWidth="50" Margin="0 0 5 0" />
|
||||
<Label Text="{Loc generic-hours}" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
@@ -12,7 +12,7 @@ public sealed partial class PanicBunkerTab : Control
|
||||
[Dependency] private readonly IConsoleHost _console = default!;
|
||||
|
||||
private string _minAccountAge;
|
||||
private string _minOverallMinutes;
|
||||
private string _minOverallHours;
|
||||
|
||||
public PanicBunkerTab()
|
||||
{
|
||||
@@ -25,9 +25,9 @@ public sealed partial class PanicBunkerTab : Control
|
||||
MinAccountAge.OnFocusExit += args => SendMinAccountAge(args.Text);
|
||||
_minAccountAge = MinAccountAge.Text;
|
||||
|
||||
MinOverallMinutes.OnTextEntered += args => SendMinOverallMinutes(args.Text);
|
||||
MinOverallMinutes.OnFocusExit += args => SendMinOverallMinutes(args.Text);
|
||||
_minOverallMinutes = MinOverallMinutes.Text;
|
||||
MinOverallHours.OnTextEntered += args => SendMinOverallHours(args.Text);
|
||||
MinOverallHours.OnFocusExit += args => SendMinOverallHours(args.Text);
|
||||
_minOverallHours = MinOverallHours.Text;
|
||||
}
|
||||
|
||||
private void SendMinAccountAge(string text)
|
||||
@@ -42,16 +42,16 @@ public sealed partial class PanicBunkerTab : Control
|
||||
_console.ExecuteCommand($"panicbunker_min_account_age {minutes}");
|
||||
}
|
||||
|
||||
private void SendMinOverallMinutes(string text)
|
||||
private void SendMinOverallHours(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text) ||
|
||||
text == _minOverallMinutes ||
|
||||
!int.TryParse(text, out var minutes))
|
||||
text == _minOverallHours ||
|
||||
!int.TryParse(text, out var hours))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_console.ExecuteCommand($"panicbunker_min_overall_minutes {minutes}");
|
||||
_console.ExecuteCommand($"panicbunker_min_overall_hours {hours}");
|
||||
}
|
||||
|
||||
public void UpdateStatus(PanicBunkerStatus status)
|
||||
@@ -68,10 +68,10 @@ public sealed partial class PanicBunkerTab : Control
|
||||
CountDeadminnedButton.Pressed = status.CountDeadminnedAdmins;
|
||||
ShowReasonButton.Pressed = status.ShowReason;
|
||||
|
||||
MinAccountAge.Text = status.MinAccountAgeMinutes.ToString();
|
||||
MinAccountAge.Text = status.MinAccountAgeHours.ToString();
|
||||
_minAccountAge = MinAccountAge.Text;
|
||||
|
||||
MinOverallMinutes.Text = status.MinOverallMinutes.ToString();
|
||||
_minOverallMinutes = MinOverallMinutes.Text;
|
||||
MinOverallHours.Text = status.MinOverallHours.ToString();
|
||||
_minOverallHours = MinOverallHours.Text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:pt="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
xmlns:co="clr-namespace:Content.Client.UserInterface.Controls">
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="PlayerCount" HorizontalExpand="True" Text="{Loc player-tab-player-count}" />
|
||||
<LineEdit Name="SearchLineEdit" HorizontalExpand="True"
|
||||
PlaceHolder="{Loc player-tab-filter-line-edit-placeholder}" />
|
||||
<Button Name="ShowDisconnectedButton" HorizontalExpand="True"
|
||||
Text="{Loc player-tab-show-disconnected}" ToggleMode="True" />
|
||||
<Button Name="OverlayButton" HorizontalExpand="True" Text="{Loc player-tab-overlay}" ToggleMode="True" />
|
||||
<Label Name="PlayerCount" HorizontalExpand="True" SizeFlagsStretchRatio="0.50"
|
||||
Text="{Loc Player Count}" />
|
||||
<Button Name="ShowDisconnectedButton" HorizontalExpand="True" SizeFlagsStretchRatio="0.25"
|
||||
Text="{Loc player-tab-show-disconnected}" ToggleMode="True"/>
|
||||
<Button Name="OverlayButton" HorizontalExpand="True" SizeFlagsStretchRatio="0.25"
|
||||
Text="{Loc player-tab-overlay}" ToggleMode="True"/>
|
||||
</BoxContainer>
|
||||
<Control MinSize="0 5"/>
|
||||
<pt:PlayerTabHeader Name="ListHeader"/>
|
||||
<cc:HSeparator/>
|
||||
<co:SearchListContainer Name="SearchList" Access="Public" VerticalExpand="True"/>
|
||||
<Control MinSize="0 5" />
|
||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Vertical" Name="PlayerList">
|
||||
<pt:PlayerTabHeader Name="ListHeader" />
|
||||
<cc:HSeparator />
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user