Compare commits
4 Commits
LocalHelpe
...
magick
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f6e73831a | ||
|
|
a0a2e8fabf | ||
|
|
765d226a9d | ||
|
|
25c174af41 |
@@ -1,5 +1,4 @@
|
|||||||
root = true
|
root = true
|
||||||
|
|
||||||
[*]
|
[*]
|
||||||
|
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
@@ -10,10 +9,9 @@ indent_style = space
|
|||||||
tab_width = 4
|
tab_width = 4
|
||||||
|
|
||||||
# New line preferences
|
# New line preferences
|
||||||
#end_of_line = crlf
|
end_of_line = crlf:suggestion
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
max_line_length = 120
|
|
||||||
|
|
||||||
#### .NET Coding Conventions ####
|
#### .NET Coding Conventions ####
|
||||||
|
|
||||||
@@ -106,6 +104,7 @@ csharp_preferred_modifier_order = public, private, protected, internal, new, abs
|
|||||||
|
|
||||||
# 'using' directive preferences
|
# 'using' directive preferences
|
||||||
csharp_using_directive_placement = outside_namespace:silent
|
csharp_using_directive_placement = outside_namespace:silent
|
||||||
|
csharp_style_namespace_declarations = file_scoped:suggestion
|
||||||
|
|
||||||
#### C# Formatting Rules ####
|
#### C# Formatting Rules ####
|
||||||
|
|
||||||
@@ -129,7 +128,7 @@ csharp_indent_braces = false
|
|||||||
csharp_indent_switch_labels = true
|
csharp_indent_switch_labels = true
|
||||||
|
|
||||||
# Space preferences
|
# Space preferences
|
||||||
csharp_space_after_cast = false
|
csharp_space_after_cast = true
|
||||||
csharp_space_after_colon_in_inheritance_clause = true
|
csharp_space_after_colon_in_inheritance_clause = true
|
||||||
csharp_space_after_comma = true
|
csharp_space_after_comma = true
|
||||||
csharp_space_after_dot = false
|
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.t_upper_camel_case_style.required_prefix = T
|
||||||
dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case
|
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.applicable_kinds = field
|
||||||
dotnet_naming_symbols.constants_symbols.required_modifiers = const
|
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_accessibilities = private
|
||||||
dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field
|
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_accessibilities = *
|
||||||
dotnet_naming_symbols.property_symbols.applicable_kinds = property
|
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.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.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_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_accessibilities = *
|
||||||
dotnet_naming_symbols.type_parameters_symbols.applicable_kinds = type_parameter
|
dotnet_naming_symbols.type_parameters_symbols.applicable_kinds = type_parameter
|
||||||
|
|
||||||
# ReSharper properties
|
# ReSharper properties
|
||||||
resharper_braces_for_ifelse = required_for_multiline
|
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_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}]
|
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props}]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|||||||
15
.github/CODEOWNERS
vendored
15
.github/CODEOWNERS
vendored
@@ -15,21 +15,19 @@
|
|||||||
/Content.*/GameTicking/ @moonheart08 @EmoGarbage404
|
/Content.*/GameTicking/ @moonheart08 @EmoGarbage404
|
||||||
/Resources/ServerInfo/ @moonheart08 @Chief-Engineer
|
/Resources/ServerInfo/ @moonheart08 @Chief-Engineer
|
||||||
/Resources/ServerInfo/Guidebook/ @moonheart08 @EmoGarbage404
|
/Resources/ServerInfo/Guidebook/ @moonheart08 @EmoGarbage404
|
||||||
/Resources/ServerInfo/Guidebook/ServerRules/ @Chief-Engineer
|
|
||||||
/Resources/engineCommandPerms.yml @moonheart08 @Chief-Engineer
|
/Resources/engineCommandPerms.yml @moonheart08 @Chief-Engineer
|
||||||
/Resources/clientCommandPerms.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/Body/ @DrSmugleaf # suffering
|
||||||
/Resources/Prototypes/Entities/Mobs/Player/ @DrSmugleaf
|
/Resources/Prototypes/Entities/Mobs/Player/ @DrSmugleaf
|
||||||
/Resources/Prototypes/Entities/Mobs/Species/ @DrSmugleaf
|
/Resources/Prototypes/Entities/Mobs/Species/ @DrSmugleaf
|
||||||
/Resources/Prototypes/Guidebook/rules.yml @Chief-Engineer
|
|
||||||
/Content.*/Body/ @DrSmugleaf
|
/Content.*/Body/ @DrSmugleaf
|
||||||
/Content.YAMLLinter @DrSmugleaf
|
/Content.YAMLLinter @DrSmugleaf
|
||||||
/Content.Shared/Damage/ @DrSmugleaf
|
/Content.Shared/Damage/ @DrSmugleaf
|
||||||
|
|
||||||
/Content.*/Anomaly/ @EmoGarbage404 @TheShuEd
|
/Content.*/Anomaly/ @EmoGarbage404
|
||||||
/Content.*/Lathe/ @EmoGarbage404
|
/Content.*/Lathe/ @EmoGarbage404
|
||||||
/Content.*/Materials/ @EmoGarbage404
|
/Content.*/Materials/ @EmoGarbage404
|
||||||
/Content.*/Mech/ @EmoGarbage404
|
/Content.*/Mech/ @EmoGarbage404
|
||||||
@@ -37,7 +35,7 @@
|
|||||||
/Content.*/Stack/ @EmoGarbage404
|
/Content.*/Stack/ @EmoGarbage404
|
||||||
/Content.*/Xenoarchaeology/ @EmoGarbage404
|
/Content.*/Xenoarchaeology/ @EmoGarbage404
|
||||||
/Content.*/Zombies/ @EmoGarbage404
|
/Content.*/Zombies/ @EmoGarbage404
|
||||||
/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml @EmoGarbage404 @TheShuEd
|
/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml @EmoGarbage404
|
||||||
/Resources/Prototypes/Research/ @EmoGarbage404
|
/Resources/Prototypes/Research/ @EmoGarbage404
|
||||||
|
|
||||||
/Content.*/Forensics/ @ficcialfaint
|
/Content.*/Forensics/ @ficcialfaint
|
||||||
@@ -55,10 +53,3 @@
|
|||||||
#Jezi
|
#Jezi
|
||||||
/Content.*/Medical @Jezithyr
|
/Content.*/Medical @Jezithyr
|
||||||
/Content.*/Body @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
|
## About the PR
|
||||||
<!-- What did you change in this PR? -->
|
<!-- What did you change in this PR? -->
|
||||||
<!-- Что вы изменили в своем пулл реквесте? -->
|
|
||||||
|
|
||||||
## Why / Balance
|
## Why / Balance
|
||||||
<!-- Why was it changed? Link any discussions or issues here. Please discuss how this would affect game 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
|
## Media
|
||||||
<!--
|
<!--
|
||||||
PRs which make ingame changes (adding clothing, items, new features, etc) are required to have media attached that showcase the changes.
|
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.
|
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!
|
||||||
-->
|
-->
|
||||||
|
|||||||
20
.github/labeler.yml
vendored
20
.github/labeler.yml
vendored
@@ -1,22 +1,12 @@
|
|||||||
"Changes: Sprites":
|
"Changes: Sprites":
|
||||||
- changed-files:
|
- '**/*.rsi/*.png'
|
||||||
- any-glob-to-any-file: '**/*.rsi/*.png'
|
|
||||||
|
|
||||||
"Changes: Map":
|
"Changes: Map":
|
||||||
- changed-files:
|
- 'Resources/Maps/*.yml'
|
||||||
- any-glob-to-any-file:
|
- 'Resources/Prototypes/Maps/*.yml'
|
||||||
- 'Resources/Maps/**/*.yml'
|
|
||||||
- 'Resources/Prototypes/Maps/**/*.yml'
|
|
||||||
|
|
||||||
"Changes: UI":
|
"Changes: UI":
|
||||||
- changed-files:
|
- '**/*.xaml*'
|
||||||
- any-glob-to-any-file: '**/*.xaml*'
|
|
||||||
|
|
||||||
"Changes: Shaders":
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file: '**/*.swsl'
|
|
||||||
|
|
||||||
"No C#":
|
"No C#":
|
||||||
- changed-files:
|
- all: ["!**/*.cs"]
|
||||||
# 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"
|
|
||||||
|
|||||||
6
.github/rsi-schema.json
vendored
6
.github/rsi-schema.json
vendored
@@ -75,17 +75,15 @@
|
|||||||
"license":{
|
"license":{
|
||||||
"$id":"#/properties/license",
|
"$id":"#/properties/license",
|
||||||
"default":"",
|
"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":[
|
"enum":[
|
||||||
"CC-BY-SA-3.0",
|
"CC-BY-SA-3.0",
|
||||||
"CC-BY-SA-4.0",
|
"CC-BY-SA-4.0",
|
||||||
"CC0-1.0",
|
|
||||||
"CC-BY-NC-3.0",
|
"CC-BY-NC-3.0",
|
||||||
"CC-BY-NC-4.0",
|
"CC-BY-NC-4.0",
|
||||||
"CC-BY-NC-SA-3.0",
|
"CC-BY-NC-SA-3.0",
|
||||||
"CC-BY-NC-SA-4.0",
|
"CC-BY-NC-SA-4.0",
|
||||||
"CC0-1.0",
|
"CC0-1.0"
|
||||||
"CLA"
|
|
||||||
],
|
],
|
||||||
"examples":[
|
"examples":[
|
||||||
"CC-BY-SA-3.0"
|
"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
|
|
||||||
12
.github/workflows/conflict-labeler.yml
vendored
12
.github/workflows/conflict-labeler.yml
vendored
@@ -1,20 +1,18 @@
|
|||||||
name: Check Merge Conflicts
|
name: Check Merge Conflicts
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types:
|
|
||||||
- opened
|
|
||||||
- synchronize
|
|
||||||
- reopened
|
|
||||||
- ready_for_review
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Label:
|
Label:
|
||||||
if: ( github.event.pull_request.draft == false ) && ( github.actor != 'PJBot' )
|
if: github.actor != 'PJBot'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check for Merge Conflicts
|
- name: Check for Merge Conflicts
|
||||||
uses: eps1lon/actions-label-merge-conflict@v3.0.0
|
uses: ike709/actions-label-merge-conflict@9eefdd17e10566023c46d2dc6dc04fcb8ec76142
|
||||||
with:
|
with:
|
||||||
dirtyLabel: "Merge Conflict"
|
dirtyLabel: "Merge Conflict"
|
||||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
|||||||
7
.github/workflows/labeler-pr.yml
vendored
7
.github/workflows/labeler-pr.yml
vendored
@@ -6,9 +6,8 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
labeler:
|
labeler:
|
||||||
if: github.actor != 'PJBot'
|
if: github.actor != 'PJBot'
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/labeler@v5
|
- uses: actions/labeler@v3
|
||||||
|
with:
|
||||||
|
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
|||||||
1
.github/workflows/labeler-untriaged.yml
vendored
1
.github/workflows/labeler-untriaged.yml
vendored
@@ -9,6 +9,5 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions-ecosystem/action-add-labels@v1
|
- uses: actions-ecosystem/action-add-labels@v1
|
||||||
if: join(github.event.issue.labels) == ''
|
|
||||||
with:
|
with:
|
||||||
labels: "Status: Untriaged"
|
labels: "Status: Untriaged"
|
||||||
|
|||||||
34
.github/workflows/publish.yml
vendored
34
.github/workflows/publish.yml
vendored
@@ -5,8 +5,8 @@ concurrency:
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
# schedule:
|
schedule:
|
||||||
# - cron: '0 10 * * *'
|
- cron: '0 10 * * *'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -41,11 +41,31 @@ jobs:
|
|||||||
- name: Package client
|
- name: Package client
|
||||||
run: dotnet run --project Content.Packaging client --no-wipe-release
|
run: dotnet run --project Content.Packaging client --no-wipe-release
|
||||||
|
|
||||||
- name: Publish version
|
- name: Update Build Info
|
||||||
run: Tools/publish_multi_request.py
|
run: Tools/gen_build_info.py
|
||||||
env:
|
|
||||||
PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
|
- name: Shuffle files around
|
||||||
GITHUB_REPOSITORY: ${{ vars.GITHUB_REPOSITORY }}
|
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)
|
- name: Publish changelog (Discord)
|
||||||
run: Tools/actions_changelogs_since_last_run.py
|
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
|
- name: Get changed files
|
||||||
id: files
|
id: files
|
||||||
uses: Ana06/get-changed-files@v2.3.0
|
uses: Ana06/get-changed-files@v1.2
|
||||||
with:
|
with:
|
||||||
format: 'space-delimited'
|
format: 'space-delimited'
|
||||||
filter: |
|
|
||||||
**.rsi
|
|
||||||
**.png
|
|
||||||
|
|
||||||
- name: Diff changed RSIs
|
- name: Diff changed RSIs
|
||||||
id: diff
|
id: diff
|
||||||
|
|||||||
8
.github/workflows/test-packaging.yml
vendored
8
.github/workflows/test-packaging.yml
vendored
@@ -64,3 +64,11 @@ jobs:
|
|||||||
|
|
||||||
- name: Package client
|
- name: Package client
|
||||||
run: dotnet run --project Content.Packaging client --no-wipe-release
|
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
|
- name: Get this week's Contributors
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
|
||||||
run: Tools/dump_github_contributors.ps1 > Resources/Credits/GitHub.txt
|
run: Tools/dump_github_contributors.ps1 > Resources/Credits/GitHub.txt
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
|
|||||||
4
.github/workflows/validate-rgas.yml
vendored
4
.github/workflows/validate-rgas.yml
vendored
@@ -21,5 +21,5 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
schema: RobustToolbox/Schemas/rga.yml
|
schema: RobustToolbox/Schemas/rga.yml
|
||||||
path_pattern: .*attributions.ya?ml$
|
path_pattern: .*attributions.ya?ml$
|
||||||
validators_path: Schemas/rga_validators.py
|
validators_path: RobustToolbox/Schemas/rga_validators.py
|
||||||
validators_requirements: Schemas/rga_requirements.txt
|
validators_requirements: RobustToolbox/Schemas/rga_requirements.txt
|
||||||
|
|||||||
2
.github/workflows/validate-rsis.yml
vendored
2
.github/workflows/validate-rsis.yml
vendored
@@ -23,4 +23,4 @@ jobs:
|
|||||||
pip3 install --ignore-installed --user pillow jsonschema
|
pip3 install --ignore-installed --user pillow jsonschema
|
||||||
- name: Validate RSIs
|
- name: Validate RSIs
|
||||||
run: |
|
run: |
|
||||||
python3 Schemas/validate_rsis.py Resources/
|
python3 RobustToolbox/Schemas/validate_rsis.py Resources/
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -306,4 +306,3 @@ Resources/MapImages
|
|||||||
|
|
||||||
# Direnv stuff
|
# Direnv stuff
|
||||||
.direnv/
|
.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": [
|
"args": [
|
||||||
"build",
|
"build",
|
||||||
"/property:GenerateFullPaths=true", // Ask dotnet build to generate full paths for file names.
|
"/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": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build",
|
||||||
@@ -29,9 +29,9 @@
|
|||||||
"build",
|
"build",
|
||||||
"${workspaceFolder}/Content.YAMLLinter/Content.YAMLLinter.csproj",
|
"${workspaceFolder}/Content.YAMLLinter/Content.YAMLLinter.csproj",
|
||||||
"/property:GenerateFullPaths=true",
|
"/property:GenerateFullPaths=true",
|
||||||
"/consoleloggerparameters:'ForceNoAlign;NoSummary'"
|
"/consoleloggerparameters:NoSummary"
|
||||||
],
|
],
|
||||||
"problemMatcher": "$msCompile"
|
"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>();
|
var componentFactory = new Mock<IComponentFactory>();
|
||||||
componentFactory.Setup(p => p.GetComponent<DummyComponent>()).Returns(new DummyComponent());
|
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.GetRegistration(It.IsAny<DummyComponent>())).Returns(dummyReg);
|
||||||
componentFactory.Setup(p => p.GetAllRegistrations()).Returns(new[] { dummyReg });
|
componentFactory.Setup(p => p.GetAllRegistrations()).Returns(new[] { dummyReg });
|
||||||
componentFactory.Setup(p => p.GetAllRefTypes()).Returns(new[] { CompIdx.Index<DummyComponent>() });
|
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.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -26,7 +26,7 @@ public class MapLoadBenchmark
|
|||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
ProgramShared.PathOffset = "../../../../";
|
ProgramShared.PathOffset = "../../../../";
|
||||||
PoolManager.Startup();
|
PoolManager.Startup(null);
|
||||||
|
|
||||||
_pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
|
_pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
|
||||||
var server = _pair.Server;
|
var server = _pair.Server;
|
||||||
@@ -46,7 +46,7 @@ public class MapLoadBenchmark
|
|||||||
PoolManager.Shutdown();
|
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))]
|
[ParamsSource(nameof(MapsSource))]
|
||||||
public string Map;
|
public string Map;
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using Content.IntegrationTests;
|
using Content.IntegrationTests;
|
||||||
using Content.IntegrationTests.Pair;
|
using Content.IntegrationTests.Pair;
|
||||||
using Content.Server.Mind;
|
|
||||||
using Content.Server.Warps;
|
using Content.Server.Warps;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared;
|
using Robust.Shared;
|
||||||
using Robust.Shared.Analyzers;
|
using Robust.Shared.Analyzers;
|
||||||
|
using Robust.Shared.Enums;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Network;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
@@ -47,7 +49,7 @@ public class PvsBenchmark
|
|||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
ProgramShared.PathOffset = "../../../../";
|
ProgramShared.PathOffset = "../../../../";
|
||||||
#endif
|
#endif
|
||||||
PoolManager.Startup();
|
PoolManager.Startup(null);
|
||||||
|
|
||||||
_pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
|
_pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
|
||||||
_entMan = _pair.Server.ResolveDependency<IEntityManager>();
|
_entMan = _pair.Server.ResolveDependency<IEntityManager>();
|
||||||
@@ -56,20 +58,15 @@ public class PvsBenchmark
|
|||||||
_pair.Server.CfgMan.SetCVar(CVars.NetPvsAsync, false);
|
_pair.Server.CfgMan.SetCVar(CVars.NetPvsAsync, false);
|
||||||
_sys = _entMan.System<SharedTransformSystem>();
|
_sys = _entMan.System<SharedTransformSystem>();
|
||||||
|
|
||||||
SetupAsync().Wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SetupAsync()
|
|
||||||
{
|
|
||||||
// Spawn the map
|
// Spawn the map
|
||||||
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
||||||
await _pair.Server.WaitPost(() =>
|
_pair.Server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
var success = _entMan.System<MapLoaderSystem>().TryLoad(_mapId, Map, out _);
|
var success = _entMan.System<MapLoaderSystem>().TryLoad(_mapId, Map, out _);
|
||||||
if (!success)
|
if (!success)
|
||||||
throw new Exception("Map load failed");
|
throw new Exception("Map load failed");
|
||||||
_pair.Server.MapMan.DoMapInitialize(_mapId);
|
_pair.Server.MapMan.DoMapInitialize(_mapId);
|
||||||
});
|
}).Wait();
|
||||||
|
|
||||||
// Get list of ghost warp positions
|
// Get list of ghost warp positions
|
||||||
_spawns = _entMan.AllComponentsList<WarpPointComponent>()
|
_spawns = _entMan.AllComponentsList<WarpPointComponent>()
|
||||||
@@ -79,19 +76,17 @@ public class PvsBenchmark
|
|||||||
|
|
||||||
Array.Resize(ref _players, PlayerCount);
|
Array.Resize(ref _players, PlayerCount);
|
||||||
|
|
||||||
// Spawn "Players"
|
// Spawn "Players".
|
||||||
_players = await _pair.Server.AddDummySessions(PlayerCount);
|
_pair.Server.WaitPost(() =>
|
||||||
await _pair.Server.WaitPost(() =>
|
|
||||||
{
|
{
|
||||||
var mind = _pair.Server.System<MindSystem>();
|
|
||||||
for (var i = 0; i < PlayerCount; i++)
|
for (var i = 0; i < PlayerCount; i++)
|
||||||
{
|
{
|
||||||
var pos = _spawns[i % _spawns.Length];
|
var pos = _spawns[i % _spawns.Length];
|
||||||
var uid =_entMan.SpawnEntity("MobHuman", pos);
|
var uid =_entMan.SpawnEntity("MobHuman", pos);
|
||||||
_pair.Server.ConsoleHost.ExecuteCommand($"setoutfit {_entMan.GetNetEntity(uid)} CaptainGear");
|
_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.
|
// 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.
|
// This will populate their PVS data with out-of-view entities.
|
||||||
@@ -173,4 +168,20 @@ public class PvsBenchmark
|
|||||||
}).Wait();
|
}).Wait();
|
||||||
_pair.Server.PvsTick(_players);
|
_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()
|
public async Task SetupAsync()
|
||||||
{
|
{
|
||||||
ProgramShared.PathOffset = "../../../../";
|
ProgramShared.PathOffset = "../../../../";
|
||||||
PoolManager.Startup();
|
PoolManager.Startup(null);
|
||||||
_pair = await PoolManager.GetServerClient();
|
_pair = await PoolManager.GetServerClient();
|
||||||
var server = _pair.Server;
|
var server = _pair.Server;
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,6 @@
|
|||||||
|
|
||||||
namespace Content.Client.Access;
|
namespace Content.Client.Access;
|
||||||
|
|
||||||
public sealed class IdCardSystem : SharedIdCardSystem;
|
public sealed class IdCardSystem : SharedIdCardSystem
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,18 +12,11 @@ namespace Content.Client.Access.UI;
|
|||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class AccessLevelControl : GridContainer
|
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 readonly Dictionary<ProtoId<AccessLevelPrototype>, Button> ButtonsList = new();
|
||||||
|
|
||||||
public AccessLevelControl()
|
public AccessLevelControl()
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
IoCManager.InjectDependencies(this);
|
|
||||||
|
|
||||||
_sawmill = _logManager.GetSawmill("accesslevelcontrol");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Populate(List<ProtoId<AccessLevelPrototype>> accessLevels, IPrototypeManager prototypeManager)
|
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))
|
if (!prototypeManager.TryIndex(access, out var accessLevel))
|
||||||
{
|
{
|
||||||
_sawmill.Error($"Unable to find accesslevel for {access}");
|
Logger.Error($"Unable to find accesslevel for {access}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Content.Shared.Access;
|
|||||||
using Content.Shared.Access.Components;
|
using Content.Shared.Access.Components;
|
||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
using Content.Shared.Containers.ItemSlots;
|
using Content.Shared.Containers.ItemSlots;
|
||||||
using Robust.Client.UserInterface;
|
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using static Content.Shared.Access.Components.AccessOverriderComponent;
|
using static Content.Shared.Access.Components.AccessOverriderComponent;
|
||||||
|
|
||||||
@@ -24,28 +23,6 @@ namespace Content.Client.Access.UI
|
|||||||
{
|
{
|
||||||
base.Open();
|
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;
|
List<ProtoId<AccessLevelPrototype>> accessLevels;
|
||||||
|
|
||||||
if (EntMan.TryGetComponent<AccessOverriderComponent>(Owner, out var accessOverrider))
|
if (EntMan.TryGetComponent<AccessOverriderComponent>(Owner, out var accessOverrider))
|
||||||
@@ -53,20 +30,38 @@ namespace Content.Client.Access.UI
|
|||||||
accessLevels = accessOverrider.AccessLevels;
|
accessLevels = accessOverrider.AccessLevels;
|
||||||
accessLevels.Sort();
|
accessLevels.Sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
accessLevels = new List<ProtoId<AccessLevelPrototype>>();
|
accessLevels = new List<ProtoId<AccessLevelPrototype>>();
|
||||||
_accessOverriderSystem.Log.Error($"No AccessOverrider component found for {EntMan.ToPrettyString(Owner)}!");
|
_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)
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
{
|
{
|
||||||
base.UpdateState(state);
|
base.UpdateState(state);
|
||||||
var castState = (AccessOverriderBoundUserInterfaceState) state;
|
var castState = (AccessOverriderBoundUserInterfaceState) state;
|
||||||
_window?.UpdateState(_prototypeManager, castState);
|
_window?.UpdateState(castState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SubmitData(List<ProtoId<AccessLevelPrototype>> newAccessList)
|
public void SubmitData(List<ProtoId<AccessLevelPrototype>> newAccessList)
|
||||||
|
|||||||
@@ -13,24 +13,26 @@ namespace Content.Client.Access.UI
|
|||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class AccessOverriderWindow : DefaultWindow
|
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();
|
private readonly Dictionary<string, Button> _accessButtons = new();
|
||||||
|
|
||||||
public event Action<List<ProtoId<AccessLevelPrototype>>>? OnSubmit;
|
public AccessOverriderWindow(AccessOverriderBoundUserInterface owner, IPrototypeManager prototypeManager,
|
||||||
|
List<ProtoId<AccessLevelPrototype>> accessLevels)
|
||||||
public AccessOverriderWindow()
|
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
}
|
IoCManager.InjectDependencies(this);
|
||||||
|
var logMill = _logManager.GetSawmill(SharedAccessOverriderSystem.Sawmill);
|
||||||
|
|
||||||
public void SetAccessLevels(IPrototypeManager protoManager, List<ProtoId<AccessLevelPrototype>> accessLevels)
|
_owner = owner;
|
||||||
{
|
|
||||||
_accessButtons.Clear();
|
|
||||||
AccessLevelGrid.DisposeAllChildren();
|
|
||||||
|
|
||||||
foreach (var access in accessLevels)
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,16 +44,11 @@ namespace Content.Client.Access.UI
|
|||||||
|
|
||||||
AccessLevelGrid.AddChild(newButton);
|
AccessLevelGrid.AddChild(newButton);
|
||||||
_accessButtons.Add(accessLevel.ID, newButton);
|
_accessButtons.Add(accessLevel.ID, newButton);
|
||||||
newButton.OnPressed += _ =>
|
newButton.OnPressed += _ => SubmitData();
|
||||||
{
|
|
||||||
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());
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateState(IPrototypeManager protoManager, AccessOverriderBoundUserInterfaceState state)
|
public void UpdateState(AccessOverriderBoundUserInterfaceState state)
|
||||||
{
|
{
|
||||||
PrivilegedIdLabel.Text = state.PrivilegedIdName;
|
PrivilegedIdLabel.Text = state.PrivilegedIdName;
|
||||||
PrivilegedIdButton.Text = state.IsPrivilegedIdPresent
|
PrivilegedIdButton.Text = state.IsPrivilegedIdPresent
|
||||||
@@ -69,11 +66,11 @@ namespace Content.Client.Access.UI
|
|||||||
|
|
||||||
if (state.MissingPrivilegesList != null && state.MissingPrivilegesList.Any())
|
if (state.MissingPrivilegesList != null && state.MissingPrivilegesList.Any())
|
||||||
{
|
{
|
||||||
var missingPrivileges = new List<string>();
|
List<string> missingPrivileges = new List<string>();
|
||||||
|
|
||||||
foreach (string tag in state.MissingPrivilegesList)
|
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);
|
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.Access.Systems;
|
||||||
using Content.Shared.StatusIcon;
|
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.UserInterface;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Client.Access.UI
|
namespace Content.Client.Access.UI
|
||||||
{
|
{
|
||||||
@@ -21,11 +18,16 @@ namespace Content.Client.Access.UI
|
|||||||
{
|
{
|
||||||
base.Open();
|
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.OnNameChanged += OnNameChanged;
|
||||||
_window.OnJobChanged += OnJobChanged;
|
_window.OnJobChanged += OnJobChanged;
|
||||||
_window.OnJobIconChanged += OnJobIconChanged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnNameChanged(string newName)
|
private void OnNameChanged(string newName)
|
||||||
@@ -38,9 +40,9 @@ namespace Content.Client.Access.UI
|
|||||||
SendMessage(new AgentIDCardJobChangedMessage(newJob));
|
SendMessage(new AgentIDCardJobChangedMessage(newJob));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnJobIconChanged(ProtoId<JobIconPrototype> newJobIconId)
|
public void OnJobIconChanged(string newJobIcon)
|
||||||
{
|
{
|
||||||
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId));
|
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIcon));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -55,7 +57,16 @@ namespace Content.Client.Access.UI
|
|||||||
|
|
||||||
_window.SetCurrentName(cast.CurrentName);
|
_window.SetCurrentName(cast.CurrentName);
|
||||||
_window.SetCurrentJob(cast.CurrentJob);
|
_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" />
|
<LineEdit Name="NameLineEdit" />
|
||||||
<Label Name="CurrentJob" Text="{Loc 'agent-id-card-current-job'}" />
|
<Label Name="CurrentJob" Text="{Loc 'agent-id-card-current-job'}" />
|
||||||
<LineEdit Name="JobLineEdit" />
|
<LineEdit Name="JobLineEdit" />
|
||||||
<Label Text="{Loc 'agent-id-card-job-icon-label'}"/>
|
<BoxContainer Orientation="Horizontal">
|
||||||
<GridContainer Name="IconGrid" Columns="10">
|
<Label Text="{Loc 'agent-id-card-job-icon-label'}"/>
|
||||||
<!-- Job icon buttons are generated in the code -->
|
<Control HorizontalExpand="True" MinSize="50 0"/>
|
||||||
</GridContainer>
|
<GridContainer Name="IconGrid" Columns="10">
|
||||||
|
<!-- Job icon buttons are generated in the code -->
|
||||||
|
</GridContainer>
|
||||||
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</DefaultWindow>
|
</DefaultWindow>
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ using Robust.Client.UserInterface.CustomControls;
|
|||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Content.Client.Access.UI
|
namespace Content.Client.Access.UI
|
||||||
{
|
{
|
||||||
@@ -18,19 +17,19 @@ namespace Content.Client.Access.UI
|
|||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
||||||
private readonly SpriteSystem _spriteSystem;
|
private readonly SpriteSystem _spriteSystem;
|
||||||
|
private readonly AgentIDCardBoundUserInterface _bui;
|
||||||
|
|
||||||
private const int JobIconColumnCount = 10;
|
private const int JobIconColumnCount = 10;
|
||||||
|
|
||||||
public event Action<string>? OnNameChanged;
|
public event Action<string>? OnNameChanged;
|
||||||
public event Action<string>? OnJobChanged;
|
public event Action<string>? OnJobChanged;
|
||||||
|
|
||||||
public event Action<ProtoId<JobIconPrototype>>? OnJobIconChanged;
|
public AgentIDCardWindow(AgentIDCardBoundUserInterface bui)
|
||||||
|
|
||||||
public AgentIDCardWindow()
|
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
IoCManager.InjectDependencies(this);
|
IoCManager.InjectDependencies(this);
|
||||||
_spriteSystem = _entitySystem.GetEntitySystem<SpriteSystem>();
|
_spriteSystem = _entitySystem.GetEntitySystem<SpriteSystem>();
|
||||||
|
_bui = bui;
|
||||||
|
|
||||||
NameLineEdit.OnTextEntered += e => OnNameChanged?.Invoke(e.Text);
|
NameLineEdit.OnTextEntered += e => OnNameChanged?.Invoke(e.Text);
|
||||||
NameLineEdit.OnFocusExit += 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);
|
JobLineEdit.OnFocusExit += e => OnJobChanged?.Invoke(e.Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetAllowedIcons(string currentJobIconId)
|
public void SetAllowedIcons(HashSet<string> icons)
|
||||||
{
|
{
|
||||||
IconGrid.DisposeAllChildren();
|
IconGrid.DisposeAllChildren();
|
||||||
|
|
||||||
var jobIconButtonGroup = new ButtonGroup();
|
var jobIconGroup = new ButtonGroup();
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var icons = _prototypeManager.EnumeratePrototypes<JobIconPrototype>().Where(icon => icon.AllowSelection).ToList();
|
foreach (var jobIconId in icons)
|
||||||
icons.Sort((x, y) => string.Compare(x.LocalizedJobName, y.LocalizedJobName, StringComparison.CurrentCulture));
|
|
||||||
foreach (var jobIcon in icons)
|
|
||||||
{
|
{
|
||||||
|
if (!_prototypeManager.TryIndex<StatusIconPrototype>(jobIconId, out var jobIcon))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
String styleBase = StyleBase.ButtonOpenBoth;
|
String styleBase = StyleBase.ButtonOpenBoth;
|
||||||
var modulo = i % JobIconColumnCount;
|
var modulo = i % JobIconColumnCount;
|
||||||
if (modulo == 0)
|
if (modulo == 0)
|
||||||
@@ -62,13 +64,12 @@ namespace Content.Client.Access.UI
|
|||||||
Access = AccessLevel.Public,
|
Access = AccessLevel.Public,
|
||||||
StyleClasses = { styleBase },
|
StyleClasses = { styleBase },
|
||||||
MaxSize = new Vector2(42, 28),
|
MaxSize = new Vector2(42, 28),
|
||||||
Group = jobIconButtonGroup,
|
Group = jobIconGroup,
|
||||||
Pressed = currentJobIconId == jobIcon.ID,
|
Pressed = i == 0,
|
||||||
ToolTip = jobIcon.LocalizedJobName
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate buttons textures
|
// Generate buttons textures
|
||||||
var jobIconTexture = new TextureRect
|
TextureRect jobIconTexture = new TextureRect
|
||||||
{
|
{
|
||||||
Texture = _spriteSystem.Frame0(jobIcon.Icon),
|
Texture = _spriteSystem.Frame0(jobIcon.Icon),
|
||||||
TextureScale = new Vector2(2.5f, 2.5f),
|
TextureScale = new Vector2(2.5f, 2.5f),
|
||||||
@@ -76,9 +77,8 @@ namespace Content.Client.Access.UI
|
|||||||
};
|
};
|
||||||
|
|
||||||
jobIconButton.AddChild(jobIconTexture);
|
jobIconButton.AddChild(jobIconTexture);
|
||||||
jobIconButton.OnPressed += _ => OnJobIconChanged?.Invoke(jobIcon.ID);
|
jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIcon.ID);
|
||||||
IconGrid.AddChild(jobIconButton);
|
IconGrid.AddChild(jobIconButton);
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Content.Shared.Access;
|
using Content.Shared.Access;
|
||||||
using Content.Shared.Access.Components;
|
using Content.Shared.Access.Components;
|
||||||
|
using Content.Shared.Access;
|
||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
using Content.Shared.Containers.ItemSlots;
|
using Content.Shared.Containers.ItemSlots;
|
||||||
using Content.Shared.CrewManifest;
|
using Content.Shared.CrewManifest;
|
||||||
|
|||||||
@@ -27,9 +27,6 @@ namespace Content.Client.Access.UI
|
|||||||
private string? _lastJobTitle;
|
private string? _lastJobTitle;
|
||||||
private string? _lastJobProto;
|
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,
|
public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, IPrototypeManager prototypeManager,
|
||||||
List<ProtoId<AccessLevelPrototype>> accessLevels)
|
List<ProtoId<AccessLevelPrototype>> accessLevels)
|
||||||
{
|
{
|
||||||
@@ -68,6 +65,7 @@ namespace Content.Client.Access.UI
|
|||||||
}
|
}
|
||||||
|
|
||||||
JobPresetOptionButton.OnItemSelected += SelectJobPreset;
|
JobPresetOptionButton.OnItemSelected += SelectJobPreset;
|
||||||
|
|
||||||
_accessButtons.Populate(accessLevels, prototypeManager);
|
_accessButtons.Populate(accessLevels, prototypeManager);
|
||||||
AccessLevelControlContainer.AddChild(_accessButtons);
|
AccessLevelControlContainer.AddChild(_accessButtons);
|
||||||
|
|
||||||
@@ -174,15 +172,11 @@ namespace Content.Client.Access.UI
|
|||||||
new List<ProtoId<AccessLevelPrototype>>());
|
new List<ProtoId<AccessLevelPrototype>>());
|
||||||
|
|
||||||
var jobIndex = _jobPrototypeIds.IndexOf(state.TargetIdJobPrototype);
|
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.
|
if (jobIndex >= 0)
|
||||||
// For example, a new ID from a box would have no job index.
|
|
||||||
if (jobIndex < 0)
|
|
||||||
{
|
{
|
||||||
jobIndex = _jobPrototypeIds.IndexOf(_defaultJob);
|
JobPresetOptionButton.SelectId(jobIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
JobPresetOptionButton.SelectId(jobIndex);
|
|
||||||
|
|
||||||
_lastFullName = state.TargetIdFullName;
|
_lastFullName = state.TargetIdFullName;
|
||||||
_lastJobTitle = state.TargetIdJobTitle;
|
_lastJobTitle = state.TargetIdJobTitle;
|
||||||
_lastJobProto = state.TargetIdJobPrototype;
|
_lastJobProto = state.TargetIdJobPrototype;
|
||||||
|
|||||||
@@ -48,30 +48,6 @@ namespace Content.Client.Actions
|
|||||||
SubscribeLocalEvent<InstantActionComponent, ComponentHandleState>(OnInstantHandleState);
|
SubscribeLocalEvent<InstantActionComponent, ComponentHandleState>(OnInstantHandleState);
|
||||||
SubscribeLocalEvent<EntityTargetActionComponent, ComponentHandleState>(OnEntityTargetHandleState);
|
SubscribeLocalEvent<EntityTargetActionComponent, ComponentHandleState>(OnEntityTargetHandleState);
|
||||||
SubscribeLocalEvent<WorldTargetActionComponent, ComponentHandleState>(OnWorldTargetHandleState);
|
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)
|
private void OnInstantHandleState(EntityUid uid, InstantActionComponent component, ref ComponentHandleState args)
|
||||||
@@ -100,26 +76,12 @@ namespace Content.Client.Actions
|
|||||||
BaseHandleState<WorldTargetActionComponent>(uid, component, state);
|
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
|
private void BaseHandleState<T>(EntityUid uid, BaseActionComponent component, BaseActionComponentState state) where T : BaseActionComponent
|
||||||
{
|
{
|
||||||
// TODO ACTIONS use auto comp states
|
// TODO ACTIONS use auto comp states
|
||||||
component.Icon = state.Icon;
|
component.Icon = state.Icon;
|
||||||
component.IconOn = state.IconOn;
|
component.IconOn = state.IconOn;
|
||||||
component.IconColor = state.IconColor;
|
component.IconColor = state.IconColor;
|
||||||
component.OriginalIconColor = state.OriginalIconColor;
|
|
||||||
component.DisabledIconColor = state.DisabledIconColor;
|
|
||||||
component.Keywords.Clear();
|
component.Keywords.Clear();
|
||||||
component.Keywords.UnionWith(state.Keywords);
|
component.Keywords.UnionWith(state.Keywords);
|
||||||
component.Enabled = state.Enabled;
|
component.Enabled = state.Enabled;
|
||||||
@@ -145,13 +107,11 @@ namespace Content.Client.Actions
|
|||||||
UpdateAction(uid, component);
|
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))
|
if (!ResolveActionData(actionId, ref action))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
action.IconColor = action.Charges < 1 ? action.DisabledIconColor : action.OriginalIconColor;
|
|
||||||
|
|
||||||
base.UpdateAction(actionId, action);
|
base.UpdateAction(actionId, action);
|
||||||
if (_playerManager.LocalEntity != action.AttachedEntity)
|
if (_playerManager.LocalEntity != action.AttachedEntity)
|
||||||
return;
|
return;
|
||||||
@@ -286,6 +246,9 @@ namespace Content.Client.Actions
|
|||||||
|
|
||||||
if (action.ClientExclusive)
|
if (action.ClientExclusive)
|
||||||
{
|
{
|
||||||
|
if (instantAction.Event != null)
|
||||||
|
instantAction.Event.Performer = user;
|
||||||
|
|
||||||
PerformAction(user, actions, actionId, instantAction, instantAction.Event, GameTiming.CurTime);
|
PerformAction(user, actions, actionId, instantAction, instantAction.Event, GameTiming.CurTime);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -327,7 +290,7 @@ namespace Content.Client.Actions
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
var action = _serialization.Read<BaseActionComponent>(actionNode, notNullableOverride: true);
|
var action = _serialization.Read<BaseActionComponent>(actionNode, notNullableOverride: true);
|
||||||
var actionId = Spawn();
|
var actionId = Spawn(null);
|
||||||
AddComp(actionId, action);
|
AddComp(actionId, action);
|
||||||
AddActionDirect(user, actionId);
|
AddActionDirect(user, actionId);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Client.Stylesheets;
|
using Content.Client.Stylesheets;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
@@ -77,12 +77,9 @@ namespace Content.Client.Actions.UI
|
|||||||
MaxWidth = TooltipTextMaxWidth,
|
MaxWidth = TooltipTextMaxWidth,
|
||||||
StyleClasses = {StyleNano.StyleClassTooltipActionRequirements}
|
StyleClasses = {StyleNano.StyleClassTooltipActionRequirements}
|
||||||
};
|
};
|
||||||
|
requiresLabel.SetMessage(FormattedMessage.FromMarkup("[color=#635c5c]" +
|
||||||
if (!FormattedMessage.TryFromMarkup("[color=#635c5c]" + requires + "[/color]", out var markup))
|
requires +
|
||||||
return;
|
"[/color]"));
|
||||||
|
|
||||||
requiresLabel.SetMessage(markup);
|
|
||||||
|
|
||||||
vbox.AddChild(requiresLabel);
|
vbox.AddChild(requiresLabel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,11 +97,8 @@ namespace Content.Client.Actions.UI
|
|||||||
if (timeLeft > TimeSpan.Zero)
|
if (timeLeft > TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
var duration = Cooldown.Value.End - Cooldown.Value.Start;
|
var duration = Cooldown.Value.End - Cooldown.Value.Start;
|
||||||
|
_cooldownLabel.SetMessage(FormattedMessage.FromMarkup(
|
||||||
if (!FormattedMessage.TryFromMarkup(Loc.GetString("ui-actionslot-duration", ("duration", (int)duration.TotalSeconds), ("timeLeft", (int)timeLeft.TotalSeconds + 1)), out var markup))
|
$"[color=#a10505]{(int) duration.TotalSeconds} sec cooldown ({(int) timeLeft.TotalSeconds + 1} sec remaining)[/color]"));
|
||||||
return;
|
|
||||||
|
|
||||||
_cooldownLabel.SetMessage(markup);
|
|
||||||
_cooldownLabel.Visible = true;
|
_cooldownLabel.Visible = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -2,75 +2,72 @@ using System.Numerics;
|
|||||||
using Content.Client.Administration.Systems;
|
using Content.Client.Administration.Systems;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.ResourceManagement;
|
using Robust.Client.ResourceManagement;
|
||||||
using Robust.Client.UserInterface;
|
|
||||||
using Robust.Shared;
|
|
||||||
using Robust.Shared.Enums;
|
using Robust.Shared.Enums;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
|
||||||
namespace Content.Client.Administration;
|
namespace Content.Client.Administration
|
||||||
|
|
||||||
internal sealed class AdminNameOverlay : Overlay
|
|
||||||
{
|
{
|
||||||
private readonly AdminSystem _system;
|
internal sealed class AdminNameOverlay : Overlay
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
_system = system;
|
private readonly AdminSystem _system;
|
||||||
_entityManager = entityManager;
|
private readonly IEntityManager _entityManager;
|
||||||
_eyeManager = eyeManager;
|
private readonly IEyeManager _eyeManager;
|
||||||
_entityLookup = entityLookup;
|
private readonly EntityLookupSystem _entityLookup;
|
||||||
_userInterfaceManager = userInterfaceManager;
|
private readonly Font _font;
|
||||||
ZIndex = 200;
|
|
||||||
_font = new VectorFont(resourceCache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
public AdminNameOverlay(AdminSystem system, IEntityManager entityManager, IEyeManager eyeManager, IResourceCache resourceCache, EntityLookupSystem entityLookup)
|
||||||
|
|
||||||
protected override void Draw(in OverlayDrawArgs args)
|
|
||||||
{
|
|
||||||
var viewport = args.WorldAABB;
|
|
||||||
|
|
||||||
foreach (var playerInfo in _system.PlayerList)
|
|
||||||
{
|
{
|
||||||
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
|
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||||
if (entity == null || !_entityManager.EntityExists(entity))
|
|
||||||
|
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
|
// Otherwise the entity can not exist yet
|
||||||
if (_entityManager.GetComponent<TransformComponent>(entity.Value).MapID != args.MapId)
|
if (entity == null || !_entityManager.EntityExists(entity))
|
||||||
{
|
{
|
||||||
continue;
|
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
|
var aabb = _entityLookup.GetWorldAABB(entity.Value);
|
||||||
if (!aabb.Intersects(in viewport))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var uiScale = _userInterfaceManager.RootControl.UIScale;
|
// if not on screen, continue
|
||||||
var lineoffset = new Vector2(0f, 11f) * uiScale;
|
if (!aabb.Intersects(in viewport))
|
||||||
var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center +
|
{
|
||||||
new Angle(-_eyeManager.CurrentEye.Rotation).RotateVec(
|
continue;
|
||||||
aabb.TopRight - aabb.Center)) + new Vector2(1f, 7f);
|
}
|
||||||
if (playerInfo.Antag)
|
|
||||||
{
|
var lineoffset = new Vector2(0f, 11f);
|
||||||
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 2), "ANTAG", uiScale, Color.OrangeRed);
|
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;
|
namespace Content.Client.Administration.Components;
|
||||||
|
|
||||||
[RegisterComponent]
|
[RegisterComponent, NetworkedComponent]
|
||||||
public sealed partial class HeadstandComponent : SharedHeadstandComponent
|
public sealed partial class HeadstandComponent : SharedHeadstandComponent
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -3,5 +3,6 @@ using Robust.Shared.GameStates;
|
|||||||
|
|
||||||
namespace Content.Client.Administration.Components;
|
namespace Content.Client.Administration.Components;
|
||||||
|
|
||||||
[RegisterComponent]
|
[NetworkedComponent, RegisterComponent]
|
||||||
public sealed partial class KillSignComponent : SharedKillSignComponent;
|
public sealed partial class KillSignComponent : SharedKillSignComponent
|
||||||
|
{ }
|
||||||
|
|||||||
@@ -126,15 +126,12 @@ namespace Content.Client.Administration.Managers
|
|||||||
|
|
||||||
public AdminData? GetAdminData(EntityUid uid, bool includeDeAdmin = false)
|
public AdminData? GetAdminData(EntityUid uid, bool includeDeAdmin = false)
|
||||||
{
|
{
|
||||||
if (uid == _player.LocalEntity && (_adminData?.Active ?? includeDeAdmin))
|
return uid == _player.LocalEntity ? _adminData : null;
|
||||||
return _adminData;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AdminData? GetAdminData(ICommonSession session, bool includeDeAdmin = false)
|
public AdminData? GetAdminData(ICommonSession session, bool includeDeAdmin = false)
|
||||||
{
|
{
|
||||||
if (_player.LocalUser == session.UserId && (_adminData?.Active ?? includeDeAdmin))
|
if (_player.LocalUser == session.UserId)
|
||||||
return _adminData;
|
return _adminData;
|
||||||
|
|
||||||
return null;
|
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 Content.Client.Administration.Managers;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.ResourceManagement;
|
using Robust.Client.ResourceManagement;
|
||||||
using Robust.Client.UserInterface;
|
|
||||||
using Robust.Shared.Configuration;
|
|
||||||
|
|
||||||
namespace Content.Client.Administration.Systems
|
namespace Content.Client.Administration.Systems
|
||||||
{
|
{
|
||||||
@@ -13,7 +11,6 @@ namespace Content.Client.Administration.Systems
|
|||||||
[Dependency] private readonly IClientAdminManager _adminManager = default!;
|
[Dependency] private readonly IClientAdminManager _adminManager = default!;
|
||||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||||
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
|
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
|
||||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
|
||||||
|
|
||||||
private AdminNameOverlay _adminNameOverlay = default!;
|
private AdminNameOverlay _adminNameOverlay = default!;
|
||||||
|
|
||||||
@@ -22,7 +19,7 @@ namespace Content.Client.Administration.Systems
|
|||||||
|
|
||||||
private void InitializeOverlay()
|
private void InitializeOverlay()
|
||||||
{
|
{
|
||||||
_adminNameOverlay = new AdminNameOverlay(this, EntityManager, _eyeManager, _resourceCache, _entityLookup, _userInterfaceManager);
|
_adminNameOverlay = new AdminNameOverlay(this, EntityManager, _eyeManager, _resourceCache, _entityLookup);
|
||||||
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
|
_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 Content.Shared.Verbs;
|
||||||
using Robust.Client.Console;
|
using Robust.Client.Console;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
@@ -14,12 +11,10 @@ namespace Content.Client.Administration.Systems
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IClientConGroupController _clientConGroupController = default!;
|
[Dependency] private readonly IClientConGroupController _clientConGroupController = default!;
|
||||||
[Dependency] private readonly IClientConsoleHost _clientConsoleHost = default!;
|
[Dependency] private readonly IClientConsoleHost _clientConsoleHost = default!;
|
||||||
[Dependency] private readonly ISharedAdminManager _admin = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
SubscribeLocalEvent<GetVerbsEvent<Verb>>(AddAdminVerbs);
|
SubscribeLocalEvent<GetVerbsEvent<Verb>>(AddAdminVerbs);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddAdminVerbs(GetVerbsEvent<Verb> args)
|
private void AddAdminVerbs(GetVerbsEvent<Verb> args)
|
||||||
@@ -38,24 +33,6 @@ namespace Content.Client.Administration.Systems
|
|||||||
};
|
};
|
||||||
args.Verbs.Add(verb);
|
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:tabs="clr-namespace:Content.Client.Administration.UI.Tabs"
|
||||||
xmlns:playerTab="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
|
xmlns:playerTab="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
|
||||||
xmlns:objectsTab="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab"
|
xmlns:objectsTab="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab"
|
||||||
xmlns:panic="clr-namespace:Content.Client.Administration.UI.Tabs.PanicBunkerTab"
|
xmlns:panic="clr-namespace:Content.Client.Administration.UI.Tabs.PanicBunkerTab">
|
||||||
xmlns:baby="clr-namespace:Content.Client.Administration.UI.Tabs.BabyJailTab">
|
|
||||||
<TabContainer Name="MasterTabContainer">
|
<TabContainer Name="MasterTabContainer">
|
||||||
<adminTab:AdminTab />
|
<adminTab:AdminTab />
|
||||||
<adminbusTab:AdminbusTab />
|
<adminbusTab:AdminbusTab />
|
||||||
@@ -15,7 +14,6 @@
|
|||||||
<tabs:RoundTab />
|
<tabs:RoundTab />
|
||||||
<tabs:ServerTab />
|
<tabs:ServerTab />
|
||||||
<panic:PanicBunkerTab Name="PanicBunkerControl" Access="Public" />
|
<panic:PanicBunkerTab Name="PanicBunkerControl" Access="Public" />
|
||||||
<baby:BabyJailTab Name="BabyJailControl" Access="Public" />
|
|
||||||
<playerTab:PlayerTab Name="PlayerTabControl" Access="Public" />
|
<playerTab:PlayerTab Name="PlayerTabControl" Access="Public" />
|
||||||
<objectsTab:ObjectsTab Name="ObjectsTabControl" Access="Public" />
|
<objectsTab:ObjectsTab Name="ObjectsTabControl" Access="Public" />
|
||||||
</TabContainer>
|
</TabContainer>
|
||||||
|
|||||||
@@ -3,57 +3,34 @@ using Robust.Client.AutoGenerated;
|
|||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
namespace Content.Client.Administration.UI;
|
namespace Content.Client.Administration.UI
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
|
||||||
public sealed partial class AdminMenuWindow : DefaultWindow
|
|
||||||
{
|
{
|
||||||
public event Action? OnDisposed;
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class AdminMenuWindow : DefaultWindow
|
||||||
public AdminMenuWindow()
|
|
||||||
{
|
{
|
||||||
MinSize = new Vector2(650, 250);
|
public event Action? OnDisposed;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTabChanged(int tabIndex)
|
public AdminMenuWindow()
|
||||||
{
|
{
|
||||||
var tabEnum = (TabIndex)tabIndex;
|
MinSize = new Vector2(650, 250);
|
||||||
if (tabEnum == TabIndex.Objects)
|
Title = Loc.GetString("admin-menu-title");
|
||||||
ObjectsTabControl.RefreshObjectList();
|
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)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
OnDisposed?.Invoke();
|
OnDisposed?.Invoke();
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
OnDisposed = null;
|
OnDisposed = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum TabIndex
|
|
||||||
{
|
|
||||||
Admin = 0,
|
|
||||||
Adminbus,
|
|
||||||
Atmos,
|
|
||||||
Round,
|
|
||||||
Server,
|
|
||||||
PanicBunker,
|
|
||||||
BabyJail,
|
|
||||||
Players,
|
|
||||||
Objects,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Shared.Administration.Notes;
|
using Content.Shared.Administration.Notes;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
@@ -13,7 +13,7 @@ public sealed partial class AdminMessagePopupMessage : Control
|
|||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
|
|
||||||
Admin.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString(
|
Admin.SetMessage(FormattedMessage.FromMarkup(Loc.GetString(
|
||||||
"admin-notes-message-admin",
|
"admin-notes-message-admin",
|
||||||
("admin", message.AdminName),
|
("admin", message.AdminName),
|
||||||
("date", message.AddedOn.ToLocalTime()))));
|
("date", message.AddedOn.ToLocalTime()))));
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public sealed partial class AdminMessagePopupWindow : Control
|
|||||||
MessageContainer.AddChild(new AdminMessagePopupMessage(message));
|
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)
|
private void OnDismissButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.UserInterface.Controls;
|
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using System.Net;
|
|||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using Content.Client.Administration.UI.CustomControls;
|
using Content.Client.Administration.UI.CustomControls;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.CCVar;
|
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Roles;
|
using Content.Shared.Roles;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
@@ -12,7 +11,6 @@ using Robust.Client.UserInterface;
|
|||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Configuration;
|
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
@@ -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
|
// 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.
|
// have to know how the controls are nested, which makes the code more complicated.
|
||||||
private readonly List<CheckBox> _roleCheckboxes = new();
|
private readonly List<CheckBox> _roleCheckboxes = new();
|
||||||
private readonly ISawmill _banpanelSawmill;
|
|
||||||
|
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
|
||||||
[Dependency] private readonly ILogManager _logManager = default!;
|
|
||||||
|
|
||||||
private enum TabNumbers
|
private enum TabNumbers
|
||||||
{
|
{
|
||||||
@@ -70,7 +65,6 @@ public sealed partial class BanPanel : DefaultWindow
|
|||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
IoCManager.InjectDependencies(this);
|
IoCManager.InjectDependencies(this);
|
||||||
_banpanelSawmill = _logManager.GetSawmill("admin.banpanel");
|
|
||||||
PlayerList.OnSelectionChanged += OnPlayerSelectionChanged;
|
PlayerList.OnSelectionChanged += OnPlayerSelectionChanged;
|
||||||
PlayerNameLine.OnFocusExit += _ => OnPlayerNameChanged();
|
PlayerNameLine.OnFocusExit += _ => OnPlayerNameChanged();
|
||||||
PlayerCheckbox.OnPressed += _ =>
|
PlayerCheckbox.OnPressed += _ =>
|
||||||
@@ -110,11 +104,6 @@ public sealed partial class BanPanel : DefaultWindow
|
|||||||
};
|
};
|
||||||
SubmitButton.OnPressed += SubmitButtonOnOnPressed;
|
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-none"), (int) NoteSeverity.None);
|
||||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-low"), (int) NoteSeverity.Minor);
|
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-low"), (int) NoteSeverity.Minor);
|
||||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-medium"), (int) NoteSeverity.Medium);
|
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>();
|
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
foreach (var proto in prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
|
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);
|
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;
|
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);
|
outerContainer.AddChild(innerContainer);
|
||||||
foreach (var role in roleList)
|
foreach (var role in roleList)
|
||||||
@@ -397,35 +353,6 @@ public sealed partial class BanPanel : DefaultWindow
|
|||||||
{
|
{
|
||||||
TypeOption.ModulateSelfOverride = null;
|
TypeOption.ModulateSelfOverride = null;
|
||||||
Tabs.SetTabVisible((int) TabNumbers.Roles, TypeOption.SelectedId == (int) Types.Role);
|
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()
|
private void UpdateSubmitEnabled()
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ using Robust.Client.UserInterface;
|
|||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
using Robust.Shared.Configuration;
|
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
|
||||||
namespace Content.Client.Administration.UI.Bwoink
|
namespace Content.Client.Administration.UI.Bwoink
|
||||||
{
|
{
|
||||||
@@ -74,7 +75,7 @@ namespace Content.Client.Administration.UI.Bwoink
|
|||||||
if (info.Antag && info.ActiveThisRound)
|
if (info.Antag && info.ActiveThisRound)
|
||||||
sb.Append(new Rune(0x1F5E1)); // 🗡
|
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.Append(new Rune(0x23F2)); // ⏲
|
||||||
|
|
||||||
sb.AppendFormat("\"{0}\"", text);
|
sb.AppendFormat("\"{0}\"", text);
|
||||||
@@ -87,51 +88,26 @@ namespace Content.Client.Administration.UI.Bwoink
|
|||||||
var ach = AHelpHelper.EnsurePanel(a.SessionId);
|
var ach = AHelpHelper.EnsurePanel(a.SessionId);
|
||||||
var bch = AHelpHelper.EnsurePanel(b.SessionId);
|
var bch = AHelpHelper.EnsurePanel(b.SessionId);
|
||||||
|
|
||||||
// Pinned players first
|
// First, sort by unread. Any chat with unread messages appears first. We just sort based on unread
|
||||||
if (a.IsPinned != b.IsPinned)
|
// status, not number of unread messages, so that more recent unread messages take priority.
|
||||||
return a.IsPinned ? -1 : 1;
|
|
||||||
|
|
||||||
// First, sort by unread. Any chat with unread messages appears first.
|
|
||||||
var aUnread = ach.Unread > 0;
|
var aUnread = ach.Unread > 0;
|
||||||
var bUnread = bch.Unread > 0;
|
var bUnread = bch.Unread > 0;
|
||||||
if (aUnread != bUnread)
|
if (aUnread != bUnread)
|
||||||
return aUnread ? -1 : 1;
|
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.
|
// Next, sort by connection status. Any disconnected players are grouped towards the end.
|
||||||
if (a.Connected != b.Connected)
|
if (a.Connected != b.Connected)
|
||||||
return a.Connected ? -1 : 1;
|
return a.Connected ? -1 : 1;
|
||||||
|
|
||||||
// Sort connected players by New Player status, then by Antag status
|
// Next, group by whether or not the players have participated in this round.
|
||||||
if (a.Connected && b.Connected)
|
// The ahelp window shows all players that have connected since server restart, this groups them all towards the bottom.
|
||||||
{
|
if (a.ActiveThisRound != b.ActiveThisRound)
|
||||||
var aNewPlayer = a.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold));
|
return a.ActiveThisRound ? -1 : 1;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, sort by the most recent message.
|
// Finally, sort by the most recent message.
|
||||||
return bch.LastMessage.CompareTo(ach.LastMessage);
|
return bch.LastMessage.CompareTo(ach.LastMessage);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Bans.OnPressed += _ =>
|
Bans.OnPressed += _ =>
|
||||||
{
|
{
|
||||||
if (_currentPlayer is not null)
|
if (_currentPlayer is not null)
|
||||||
@@ -250,7 +226,7 @@ namespace Content.Client.Administration.UI.Bwoink
|
|||||||
if (pl.Antag)
|
if (pl.Antag)
|
||||||
sb.Append(new Rune(0x1F5E1)); // 🗡
|
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.Append(new Rune(0x23F2)); // ⏲
|
||||||
|
|
||||||
sb.AppendFormat("\"{0}\"", pl.CharacterName);
|
sb.AppendFormat("\"{0}\"", pl.CharacterName);
|
||||||
@@ -267,9 +243,9 @@ namespace Content.Client.Administration.UI.Bwoink
|
|||||||
{
|
{
|
||||||
UpdateButtons();
|
UpdateButtons();
|
||||||
|
|
||||||
AHelpHelper.HideAllPanels();
|
|
||||||
if (ch != null)
|
if (ch != null)
|
||||||
{
|
{
|
||||||
|
AHelpHelper.HideAllPanels();
|
||||||
var panel = AHelpHelper.EnsurePanel(ch.Value);
|
var panel = AHelpHelper.EnsurePanel(ch.Value);
|
||||||
panel.Visible = true;
|
panel.Visible = true;
|
||||||
}
|
}
|
||||||
@@ -277,20 +253,7 @@ namespace Content.Client.Administration.UI.Bwoink
|
|||||||
|
|
||||||
public void PopulateList()
|
public void PopulateList()
|
||||||
{
|
{
|
||||||
// Maintain existing pin statuses
|
|
||||||
var pinnedPlayers = ChannelSelector.PlayerInfo.Where(p => p.IsPinned).ToDictionary(p => p.SessionId);
|
|
||||||
|
|
||||||
ChannelSelector.PopulateList();
|
ChannelSelector.PopulateList();
|
||||||
|
|
||||||
// Restore pin statuses
|
|
||||||
foreach (var player in ChannelSelector.PlayerInfo)
|
|
||||||
{
|
|
||||||
if (pinnedPlayers.TryGetValue(player.SessionId, out var pinnedPlayer))
|
|
||||||
{
|
|
||||||
player.IsPinned = pinnedPlayer.IsPinned;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateButtons();
|
UpdateButtons();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace Content.Client.Administration.UI.Bwoink
|
|||||||
Unread++;
|
Unread++;
|
||||||
|
|
||||||
var formatted = new FormattedMessage(1);
|
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);
|
TextOutput.AddMessage(formatted);
|
||||||
LastMessage = message.SentAt;
|
LastMessage = message.SentAt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,25 +16,18 @@ namespace Content.Client.Administration.UI.Bwoink
|
|||||||
|
|
||||||
Bwoink.ChannelSelector.OnSelectionChanged += sel =>
|
Bwoink.ChannelSelector.OnSelectionChanged += sel =>
|
||||||
{
|
{
|
||||||
if (sel is null)
|
if (sel is not null)
|
||||||
{
|
{
|
||||||
Title = Loc.GetString("bwoink-title-none-selected");
|
Title = $"{sel.CharacterName} / {sel.Username}";
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Title = $"{sel.CharacterName} / {sel.Username}";
|
if (sel.OverallPlaytime != null)
|
||||||
|
{
|
||||||
if (sel.OverallPlaytime != null)
|
Title += $" | {Loc.GetString("generic-playtime-title")}: {sel.PlaytimeString}";
|
||||||
{
|
}
|
||||||
Title += $" | {Loc.GetString("generic-playtime-title")}: {sel.PlaytimeString}";
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
OnOpen += () =>
|
OnOpen += () => Bwoink.PopulateList();
|
||||||
{
|
|
||||||
Bwoink.ChannelSelector.StopFiltering();
|
|
||||||
Bwoink.PopulateList();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<LineEdit Name="FilterLineEdit"
|
<LineEdit Name="FilterLineEdit"
|
||||||
MinSize="100 0"
|
MinSize="100 0"
|
||||||
HorizontalExpand="True"
|
HorizontalExpand="True"
|
||||||
PlaceHolder="{Loc player-list-filter}"/>
|
PlaceHolder="{Loc Filter}"/>
|
||||||
<PanelContainer Name="BackgroundPanel"
|
<PanelContainer Name="BackgroundPanel"
|
||||||
VerticalExpand="True"
|
VerticalExpand="True"
|
||||||
HorizontalExpand="True">
|
HorizontalExpand="True">
|
||||||
|
|||||||
@@ -4,166 +4,147 @@ using Content.Client.UserInterface.Controls;
|
|||||||
using Content.Client.Verbs.UI;
|
using Content.Client.Verbs.UI;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.GameObjects;
|
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Input;
|
using Robust.Shared.Input;
|
||||||
using Robust.Shared.Utility;
|
|
||||||
|
|
||||||
namespace Content.Client.Administration.UI.CustomControls;
|
namespace Content.Client.Administration.UI.CustomControls
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
|
||||||
public sealed partial class PlayerListControl : BoxContainer
|
|
||||||
{
|
{
|
||||||
private readonly AdminSystem _adminSystem;
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class PlayerListControl : BoxContainer
|
||||||
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()
|
|
||||||
{
|
{
|
||||||
_entManager = IoCManager.Resolve<IEntityManager>();
|
private readonly AdminSystem _adminSystem;
|
||||||
_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) };
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
public Func<PlayerInfo, string, string>? OverrideText;
|
||||||
{
|
public Comparison<PlayerInfo>? Comparison;
|
||||||
_selectedPlayer = null;
|
|
||||||
OnSelectionChanged?.Invoke(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
|
private IEntityManager _entManager;
|
||||||
{
|
private IUserInterfaceManager _uiManager;
|
||||||
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (selectedPlayer == _selectedPlayer)
|
private PlayerInfo? _selectedPlayer;
|
||||||
return;
|
|
||||||
|
|
||||||
if (args.Event.Function != EngineKeyFunctions.UIClick)
|
public PlayerListControl()
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
var displayName = $"{info.CharacterName} ({info.Username})";
|
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||||
if (info.IdentityName != info.CharacterName)
|
_uiManager = IoCManager.Resolve<IUserInterfaceManager>();
|
||||||
displayName += $" [{info.IdentityName}]";
|
_adminSystem = _entManager.System<AdminSystem>();
|
||||||
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
|
RobustXamlLoader.Load(this);
|
||||||
&& !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
|
// Fill the Option data
|
||||||
continue;
|
PlayerListContainer.ItemPressed += PlayerListItemPressed;
|
||||||
_sortedPlayerList.Add(info);
|
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)
|
private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
|
||||||
_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)
|
|
||||||
{
|
{
|
||||||
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))
|
public void PopulateList(IReadOnlyList<PlayerInfo>? players = null)
|
||||||
_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 += _ =>
|
|
||||||
{
|
{
|
||||||
|
players ??= _adminSystem.PlayerList;
|
||||||
|
|
||||||
|
_playerList = players.ToList();
|
||||||
|
if (_selectedPlayer != null && !_playerList.Contains(_selectedPlayer))
|
||||||
|
_selectedPlayer = null;
|
||||||
|
|
||||||
FilterList();
|
FilterList();
|
||||||
};
|
}
|
||||||
|
|
||||||
button.AddChild(entry);
|
private string GetText(PlayerInfo info)
|
||||||
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
|
{
|
||||||
|
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"
|
<Popup xmlns="https://spacestation14.io"
|
||||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||||
<PanelContainer>
|
<PanelContainer StyleClasses="BackgroundDark">
|
||||||
<PanelContainer.PanelOverride>
|
<PanelContainer.PanelOverride>
|
||||||
<gfx:StyleBoxFlat BorderThickness="2" BorderColor="#18181B" BackgroundColor="#25252a"/>
|
<gfx:StyleBoxFlat BorderThickness="1" BorderColor="#18181B"/>
|
||||||
</PanelContainer.PanelOverride>
|
</PanelContainer.PanelOverride>
|
||||||
<BoxContainer Orientation="Vertical" Margin="4 4 4 4">
|
<BoxContainer Orientation="Vertical">
|
||||||
<Label Name="PlayerNameLabel"/>
|
<Label Name="PlayerNameLabel"/>
|
||||||
<Label Name="IdLabel"/>
|
<Label Name="IdLabel"/>
|
||||||
<Label Name="TypeLabel"/>
|
<Label Name="TypeLabel"/>
|
||||||
|
|||||||
@@ -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 System.Numerics;
|
||||||
using Content.Client.UserInterface.Controls;
|
|
||||||
using Content.Shared.Preferences.Loadouts;
|
|
||||||
using Content.Shared.Roles;
|
using Content.Shared.Roles;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.Console;
|
using Robust.Client.Console;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Client.Administration.UI.SetOutfit
|
namespace Content.Client.Administration.UI.SetOutfit
|
||||||
@@ -64,18 +64,9 @@ namespace Content.Client.Administration.UI.SetOutfit
|
|||||||
PopulateByFilter(SearchBar.Text);
|
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()
|
private void PopulateList()
|
||||||
{
|
{
|
||||||
foreach (var gear in GetPrototypes())
|
foreach (var gear in _prototypeManager.EnumeratePrototypes<StartingGearPrototype>())
|
||||||
{
|
{
|
||||||
OutfitList.Add(GetItem(gear, OutfitList));
|
OutfitList.Add(GetItem(gear, OutfitList));
|
||||||
}
|
}
|
||||||
@@ -84,7 +75,7 @@ namespace Content.Client.Administration.UI.SetOutfit
|
|||||||
private void PopulateByFilter(string filter)
|
private void PopulateByFilter(string filter)
|
||||||
{
|
{
|
||||||
OutfitList.Clear();
|
OutfitList.Clear();
|
||||||
foreach (var gear in GetPrototypes())
|
foreach (var gear in _prototypeManager.EnumeratePrototypes<StartingGearPrototype>())
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(filter) &&
|
if (!string.IsNullOrEmpty(filter) &&
|
||||||
gear.ID.ToLowerInvariant().Contains(filter.Trim().ToLowerInvariant()))
|
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 override OverlaySpace Space => OverlaySpace.WorldSpace | OverlaySpace.ScreenSpace;
|
||||||
|
|
||||||
public Matrix3x2 SpaceMatrix;
|
public Matrix3 SpaceMatrix;
|
||||||
public MapId Map;
|
public MapId Map;
|
||||||
|
|
||||||
private readonly Font _font;
|
private readonly Font _font;
|
||||||
@@ -78,8 +78,7 @@ public sealed class ExplosionDebugOverlay : Overlay
|
|||||||
if (SpaceTiles == null)
|
if (SpaceTiles == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Matrix3x2.Invert(SpaceMatrix, out var invSpace);
|
gridBounds = Matrix3.Invert(SpaceMatrix).TransformBox(args.WorldBounds);
|
||||||
gridBounds = invSpace.TransformBox(args.WorldBounds);
|
|
||||||
|
|
||||||
DrawText(handle, gridBounds, SpaceMatrix, SpaceTiles, SpaceTileSize);
|
DrawText(handle, gridBounds, SpaceMatrix, SpaceTiles, SpaceTileSize);
|
||||||
}
|
}
|
||||||
@@ -87,7 +86,7 @@ public sealed class ExplosionDebugOverlay : Overlay
|
|||||||
private void DrawText(
|
private void DrawText(
|
||||||
DrawingHandleScreen handle,
|
DrawingHandleScreen handle,
|
||||||
Box2 gridBounds,
|
Box2 gridBounds,
|
||||||
Matrix3x2 transform,
|
Matrix3 transform,
|
||||||
Dictionary<int, List<Vector2i>> tileSets,
|
Dictionary<int, List<Vector2i>> tileSets,
|
||||||
ushort tileSize)
|
ushort tileSize)
|
||||||
{
|
{
|
||||||
@@ -104,7 +103,7 @@ public sealed class ExplosionDebugOverlay : Overlay
|
|||||||
if (!gridBounds.Contains(centre))
|
if (!gridBounds.Contains(centre))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var worldCenter = Vector2.Transform(centre, transform);
|
var worldCenter = transform.Transform(centre);
|
||||||
|
|
||||||
var screenCenter = _eyeManager.WorldToScreen(worldCenter);
|
var screenCenter = _eyeManager.WorldToScreen(worldCenter);
|
||||||
|
|
||||||
@@ -120,7 +119,7 @@ public sealed class ExplosionDebugOverlay : Overlay
|
|||||||
if (tileSets.TryGetValue(0, out var set))
|
if (tileSets.TryGetValue(0, out var set))
|
||||||
{
|
{
|
||||||
var epicenter = set.First();
|
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 screenCenter = _eyeManager.WorldToScreen(worldCenter) + new Vector2(-24, -24);
|
||||||
var text = $"{Intensity[0]:F2}\nΣ={TotalIntensity:F1}\nΔ={Slope:F1}";
|
var text = $"{Intensity[0]:F2}\nΣ={TotalIntensity:F1}\nΔ={Slope:F1}";
|
||||||
handle.DrawString(_font, screenCenter, text);
|
handle.DrawString(_font, screenCenter, text);
|
||||||
@@ -149,12 +148,11 @@ public sealed class ExplosionDebugOverlay : Overlay
|
|||||||
if (SpaceTiles == null)
|
if (SpaceTiles == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Matrix3x2.Invert(SpaceMatrix, out var invSpace);
|
gridBounds = Matrix3.Invert(SpaceMatrix).TransformBox(args.WorldBounds).Enlarged(2);
|
||||||
gridBounds = invSpace.TransformBox(args.WorldBounds).Enlarged(2);
|
|
||||||
handle.SetTransform(SpaceMatrix);
|
handle.SetTransform(SpaceMatrix);
|
||||||
|
|
||||||
DrawTiles(handle, gridBounds, SpaceTiles, SpaceTileSize);
|
DrawTiles(handle, gridBounds, SpaceTiles, SpaceTileSize);
|
||||||
handle.SetTransform(Matrix3x2.Identity);
|
handle.SetTransform(Matrix3.Identity);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawTiles(
|
private void DrawTiles(
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using Content.Shared.Explosion;
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.Console;
|
using Robust.Client.Console;
|
||||||
using Robust.Client.GameObjects;
|
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
@@ -23,7 +22,7 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow
|
|||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||||
private readonly SharedTransformSystem _transform = default!;
|
|
||||||
|
|
||||||
private readonly SpawnExplosionEui _eui;
|
private readonly SpawnExplosionEui _eui;
|
||||||
private List<MapId> _mapData = new();
|
private List<MapId> _mapData = new();
|
||||||
@@ -38,7 +37,6 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow
|
|||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
IoCManager.InjectDependencies(this);
|
IoCManager.InjectDependencies(this);
|
||||||
_transform = _entMan.System<TransformSystem>();
|
|
||||||
_eui = eui;
|
_eui = eui;
|
||||||
|
|
||||||
ExplosionOption.OnItemSelected += ExplosionSelected;
|
ExplosionOption.OnItemSelected += ExplosionSelected;
|
||||||
@@ -106,7 +104,7 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow
|
|||||||
|
|
||||||
_pausePreview = true;
|
_pausePreview = true;
|
||||||
MapOptions.Select(_mapData.IndexOf(transform.MapID));
|
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;
|
_pausePreview = false;
|
||||||
|
|
||||||
UpdatePreview();
|
UpdatePreview();
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
Title="{Loc admin-player-actions-window-title}" MinSize="425 272">
|
Title="{Loc admin-player-actions-window-title}" MinSize="425 272">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Text="{Loc admin-player-actions-reason}" MinWidth="100" />
|
<Label Text="{Loc Reason}" MinWidth="100" />
|
||||||
<Control MinWidth="50" />
|
<Control MinWidth="50" />
|
||||||
<LineEdit Name="ReasonLine" MinWidth="100" HorizontalExpand="True" />
|
<LineEdit Name="ReasonLine" MinWidth="100" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<DefaultWindow
|
<DefaultWindow
|
||||||
xmlns="https://spacestation14.io"
|
xmlns="https://spacestation14.io"
|
||||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
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">
|
<BoxContainer Orientation="Vertical">
|
||||||
<cc:PlayerListControl Name="PlayerList" />
|
<cc:PlayerListControl Name="PlayerList" />
|
||||||
<Button Name="SubmitButton" Text="{Loc admin-ui-teleport}" />
|
<Button Name="SubmitButton" Text="{Loc Teleport}" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</DefaultWindow>
|
</DefaultWindow>
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
<DefaultWindow
|
<DefaultWindow
|
||||||
xmlns="https://spacestation14.io" Title="{Loc admin-ui-blueprint-load}">
|
xmlns="https://spacestation14.io" Title="{Loc Load Blueprint}">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Text="{Loc admin-ui-blueprint-map}" MinSize="100 0" />
|
<Label Text="{Loc Map}" MinSize="100 0" />
|
||||||
<Control MinSize="50 0" />
|
<Control MinSize="50 0" />
|
||||||
<OptionButton Name="MapOptions" MinSize="100 0" HorizontalExpand="True" />
|
<OptionButton Name="MapOptions" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Text="{Loc admin-ui-blueprint-path}" MinSize="100 0" />
|
<Label Text="{Loc Path}" MinSize="100 0" />
|
||||||
<Control MinSize="50 0" />
|
<Control MinSize="50 0" />
|
||||||
<LineEdit Name="MapPath" MinSize="200 0" HorizontalExpand="True" Text="/Maps/" />
|
<LineEdit Name="MapPath" MinSize="200 0" HorizontalExpand="True" Text="/Maps/" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Text="{Loc admin-ui-blueprint-x}" MinSize="100 0" />
|
<Label Text="{Loc X}" MinSize="100 0" />
|
||||||
<Control MinSize="50 0" />
|
<Control MinSize="50 0" />
|
||||||
<SpinBox Name="XCoordinate" MinSize="100 0" HorizontalExpand="True" />
|
<SpinBox Name="XCoordinate" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Text="{Loc admin-ui-blueprint-y}" MinSize="100 0" />
|
<Label Text="{Loc Y}" MinSize="100 0" />
|
||||||
<Control MinSize="50 0" />
|
<Control MinSize="50 0" />
|
||||||
<SpinBox Name="YCoordinate" MinSize="100 0" HorizontalExpand="True" />
|
<SpinBox Name="YCoordinate" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Text="{Loc admin-ui-blueprint-rotation}" MinSize="100 0" />
|
<Label Text="{Loc Rotation}" MinSize="100 0" />
|
||||||
<Control MinSize="50 0" />
|
<Control MinSize="50 0" />
|
||||||
<SpinBox Name="RotationSpin" MinSize="100 0" HorizontalExpand="True" />
|
<SpinBox Name="RotationSpin" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Button Name="SubmitButton" Text="{Loc admin-ui-blueprint-load}" />
|
<Button Name="SubmitButton" Text="{Loc Load Blueprint}" />
|
||||||
<Button Name="TeleportButton" Text="{Loc admin-ui-blueprint-teleport}" />
|
<Button Name="TeleportButton" Text="{Loc Teleport to}" />
|
||||||
<Button Name="ResetButton" Text="{Loc admin-ui-blueprint-reset}"></Button>
|
<Button Name="ResetButton" Text="{Loc Reset to default}"></Button>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</DefaultWindow>
|
</DefaultWindow>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<DefaultWindow
|
<DefaultWindow
|
||||||
xmlns="https://spacestation14.io" Title="{Loc admin-ui-atmos-add}">
|
xmlns="https://spacestation14.io" Title="{Loc Add Atmos}">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Text="{Loc admin-ui-atmos-grid}" MinSize="100 0" />
|
<Label Text="{Loc Grid}" MinSize="100 0" />
|
||||||
<Control MinSize="50 0" />
|
<Control MinSize="50 0" />
|
||||||
<OptionButton Name="GridOptions" MinSize="100 0" HorizontalExpand="True" />
|
<OptionButton Name="GridOptions" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Button Name="SubmitButton" Text="{Loc admin-ui-atmos-add}" />
|
<Button Name="SubmitButton" Text="{Loc Add Atmos}" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</DefaultWindow>
|
</DefaultWindow>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
|
|||||||
while (query.MoveNext(out var uid, out var grid))
|
while (query.MoveNext(out var uid, out var grid))
|
||||||
{
|
{
|
||||||
_data.Add((uid, 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);
|
GridOptions.OnItemSelected += eventArgs => GridOptions.SelectId(eventArgs.Id);
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
<DefaultWindow
|
<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="Vertical">
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Text="{Loc admin-ui-atmos-grid}" MinSize="100 0" />
|
<Label Text="{Loc Grid}" MinSize="100 0" />
|
||||||
<Control MinSize="50 0" />
|
<Control MinSize="50 0" />
|
||||||
<OptionButton Name="GridOptions" MinSize="100 0" HorizontalExpand="True" />
|
<OptionButton Name="GridOptions" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<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" />
|
<Control MinSize="50 0" />
|
||||||
<SpinBox Name="TileXSpin" MinSize="100 0" HorizontalExpand="True" />
|
<SpinBox Name="TileXSpin" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<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" />
|
<Control MinSize="50 0" />
|
||||||
<SpinBox Name="TileYSpin" MinSize="100 0" HorizontalExpand="True" />
|
<SpinBox Name="TileYSpin" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Text="{Loc admin-ui-atmos-gas}" MinSize="100 0" />
|
<Label Text="{Loc Gas}" MinSize="100 0" />
|
||||||
<Control MinSize="50 0" />
|
<Control MinSize="50 0" />
|
||||||
<OptionButton Name="GasOptions" MinSize="100 0" HorizontalExpand="True" />
|
<OptionButton Name="GasOptions" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<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" />
|
<Control MinSize="50 0" />
|
||||||
<SpinBox Name="AmountSpin" MinSize="100 0" HorizontalExpand="True" />
|
<SpinBox Name="AmountSpin" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Button Name="SubmitButton" Text="{Loc admin-ui-atmos-add-gas}" />
|
<Button Name="SubmitButton" Text="{Loc Add Gas}" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</DefaultWindow>
|
</DefaultWindow>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
|
|||||||
_gridData.Add(entManager.GetNetEntity(uid));
|
_gridData.Add(entManager.GetNetEntity(uid));
|
||||||
var player = playerManager.LocalEntity;
|
var player = playerManager.LocalEntity;
|
||||||
var playerGrid = entManager.GetComponentOrNull<TransformComponent>(player)?.GridUid;
|
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);
|
GridOptions.OnItemSelected += eventArgs => GridOptions.SelectId(eventArgs.Id);
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
Margin="4"
|
Margin="4"
|
||||||
MinSize="50 50">
|
MinSize="50 50">
|
||||||
<GridContainer Columns="4">
|
<GridContainer Columns="4">
|
||||||
<cc:UICommandButton Text="{Loc admin-ui-atmos-add}" Command="addatmos" WindowType="{x:Type at:AddAtmosWindow}" />
|
<cc:UICommandButton Text="{Loc Add Atmos}" 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 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 Fill Gas}" Command="fillgas" WindowType="{x:Type at:FillGasWindow}" />
|
||||||
<cc:UICommandButton Text="{Loc admin-ui-atmos-set-temperature}" Command="settemp"
|
<cc:UICommandButton Text="{Loc Set Temperature}" Command="settemp"
|
||||||
WindowType="{x:Type at:SetTemperatureWindow}" />
|
WindowType="{x:Type at:SetTemperatureWindow}" />
|
||||||
</GridContainer>
|
</GridContainer>
|
||||||
</Control>
|
</Control>
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
<DefaultWindow
|
<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="Vertical">
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Text="{Loc admin-ui-atmos-grid}" MinSize="100 0" />
|
<Label Text="{Loc Grid}" MinSize="100 0" />
|
||||||
<Control MinSize="50 0" />
|
<Control MinSize="50 0" />
|
||||||
<OptionButton Name="GridOptions" MinSize="100 0" HorizontalExpand="True" />
|
<OptionButton Name="GridOptions" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Text="{Loc admin-ui-atmos-gas}" MinSize="100 0" />
|
<Label Text="{Loc Gas}" MinSize="100 0" />
|
||||||
<Control MinSize="50 0" />
|
<Control MinSize="50 0" />
|
||||||
<OptionButton Name="GasOptions" MinSize="100 0" HorizontalExpand="True" />
|
<OptionButton Name="GasOptions" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<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" />
|
<Control MinSize="50 0" />
|
||||||
<SpinBox Name="AmountSpin" MinSize="100 0" HorizontalExpand="True" />
|
<SpinBox Name="AmountSpin" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Button Name="SubmitButton" Text="{Loc admin-ui-atmos-fill-gas}" />
|
<Button Name="SubmitButton" Text="{Loc Fill Gas}" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</DefaultWindow>
|
</DefaultWindow>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
|
|||||||
{
|
{
|
||||||
var player = playerManager.LocalEntity;
|
var player = playerManager.LocalEntity;
|
||||||
var playerGrid = entManager.GetComponentOrNull<TransformComponent>(player)?.GridUid;
|
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));
|
_gridData.Add(entManager.GetNetEntity(uid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
<DefaultWindow
|
<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="Vertical">
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Text="{Loc admin-ui-atmos-grid}" MinSize="100 0" />
|
<Label Text="{Loc Grid}" MinSize="100 0" />
|
||||||
<Control MinSize="50 0" />
|
<Control MinSize="50 0" />
|
||||||
<OptionButton Name="GridOptions" MinSize="100 0" HorizontalExpand="True" />
|
<OptionButton Name="GridOptions" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<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" />
|
<Control MinSize="50 0" />
|
||||||
<SpinBox Name="TileXSpin" MinSize="100 0" HorizontalExpand="True" />
|
<SpinBox Name="TileXSpin" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<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" />
|
<Control MinSize="50 0" />
|
||||||
<SpinBox Name="TileYSpin" MinSize="100 0" HorizontalExpand="True" />
|
<SpinBox Name="TileYSpin" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Text="{Loc admin-ui-atmos-temperature}" MinSize="100 0" />
|
<Label Text="{Loc Temperature}" MinSize="100 0" />
|
||||||
<Control MinSize="50 0" />
|
<Control MinSize="50 0" />
|
||||||
<SpinBox Name="TemperatureSpin" MinSize="100 0" HorizontalExpand="True" />
|
<SpinBox Name="TemperatureSpin" MinSize="100 0" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Button Name="SubmitButton" Text="{Loc admin-ui-atmos-set-temperature}" />
|
<Button Name="SubmitButton" Text="{Loc Set Temperature}" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</DefaultWindow>
|
</DefaultWindow>
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace Content.Client.Administration.UI.Tabs.AtmosTab
|
|||||||
{
|
{
|
||||||
var player = playerManager.LocalEntity;
|
var player = playerManager.LocalEntity;
|
||||||
var playerGrid = entManager.GetComponentOrNull<TransformComponent>(player)?.GridUid;
|
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));
|
_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"
|
<Control xmlns="https://spacestation14.io"
|
||||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
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">
|
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Text="{Loc object-tab-object-type}" />
|
<Label HorizontalExpand="True" SizeFlagsStretchRatio="0.50"
|
||||||
<OptionButton Name="ObjectTypeOptions" HorizontalAlignment="Left" />
|
Text="{Loc Object type:}" />
|
||||||
<LineEdit Name="SearchLineEdit" PlaceHolder="{Loc object-tab-object-search}" HorizontalExpand="True"
|
<OptionButton Name="ObjectTypeOptions" HorizontalExpand="True" SizeFlagsStretchRatio="0.25"/>
|
||||||
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" />
|
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
|
<cc:HSeparator/>
|
||||||
|
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
|
||||||
|
<BoxContainer Orientation="Vertical" Name="ObjectList">
|
||||||
|
</BoxContainer>
|
||||||
|
</ScrollContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</Control>
|
</Control>
|
||||||
|
|||||||
@@ -1,30 +1,29 @@
|
|||||||
using Content.Client.Administration.Managers;
|
|
||||||
using Content.Client.Station;
|
using Content.Client.Station;
|
||||||
using Content.Client.UserInterface.Controls;
|
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.Console;
|
|
||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
|
namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class ObjectsTab : Control
|
public sealed partial class ObjectsTab : Control
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IClientAdminManager _admin = default!;
|
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
|
||||||
|
|
||||||
private readonly Color _altColor = Color.FromHex("#292B38");
|
private readonly List<ObjectsTabEntry> _objects = new();
|
||||||
private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
|
private List<ObjectsTabSelection> _selections = new();
|
||||||
|
|
||||||
private bool _ascending;
|
public event Action<ObjectsTabEntry, GUIBoundKeyEventArgs>? OnEntryKeyBindDown;
|
||||||
private ObjectsTabHeader.Header _headerClicked = ObjectsTabHeader.Header.ObjectName;
|
|
||||||
|
|
||||||
private readonly List<ObjectsTabSelection> _selections = [];
|
// Listen I could either have like 4 different event subscribers (for map / grid / station changes) and manage their lifetimes in AdminUIController
|
||||||
public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
|
// OR
|
||||||
|
// I can do this.
|
||||||
|
private TimeSpan _updateFrequency = TimeSpan.FromSeconds(2);
|
||||||
|
|
||||||
|
private TimeSpan _nextUpdate = TimeSpan.FromSeconds(2);
|
||||||
|
|
||||||
public ObjectsTab()
|
public ObjectsTab()
|
||||||
{
|
{
|
||||||
@@ -37,35 +36,16 @@ public sealed partial class ObjectsTab : Control
|
|||||||
RefreshObjectList(_selections[ev.Id]);
|
RefreshObjectList(_selections[ev.Id]);
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var type in Enum.GetValues<ObjectsTabSelection>())
|
foreach (var type in Enum.GetValues(typeof(ObjectsTabSelection)))
|
||||||
{
|
{
|
||||||
_selections.Add(type);
|
_selections.Add((ObjectsTabSelection)type!);
|
||||||
ObjectTypeOptions.AddItem(GetLocalizedEnumValue(type));
|
ObjectTypeOptions.AddItem(Enum.GetName((ObjectsTabSelection)type)!);
|
||||||
}
|
}
|
||||||
|
|
||||||
ListHeader.OnHeaderClicked += HeaderClicked;
|
RefreshObjectList();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TeleportTo(NetEntity nent)
|
private void RefreshObjectList()
|
||||||
{
|
|
||||||
_console.ExecuteCommand($"tpto {nent}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Delete(NetEntity nent)
|
|
||||||
{
|
|
||||||
_console.ExecuteCommand($"delete {nent}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RefreshObjectList()
|
|
||||||
{
|
{
|
||||||
RefreshObjectList(_selections[ObjectTypeOptions.SelectedId]);
|
RefreshObjectList(_selections[ObjectTypeOptions.SelectedId]);
|
||||||
}
|
}
|
||||||
@@ -95,96 +75,41 @@ public sealed partial class ObjectsTab : Control
|
|||||||
{
|
{
|
||||||
entities.Add((metadata.EntityName, _entityManager.GetNetEntity(uid)));
|
entities.Add((metadata.EntityName, _entityManager.GetNetEntity(uid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException(nameof(selection), selection, null);
|
throw new ArgumentOutOfRangeException(nameof(selection), selection, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
entities.Sort((a, b) =>
|
foreach (var control in _objects)
|
||||||
{
|
{
|
||||||
var valueA = GetComparableValue(a, _headerClicked);
|
ObjectList.RemoveChild(control);
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
return;
|
||||||
|
|
||||||
var entry = new ObjectsTabEntry(_admin, info.Name, info.Entity, new StyleBoxFlat { BackgroundColor = backgroundColor });
|
// I do not care for precision.
|
||||||
entry.OnTeleport += TeleportTo;
|
_nextUpdate = _timing.CurTime + _updateFrequency;
|
||||||
entry.OnDelete += Delete;
|
|
||||||
button.ToolTip = $"{info.Name}, {info.Entity}";
|
|
||||||
|
|
||||||
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();
|
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
|
private enum ObjectsTabSelection
|
||||||
{
|
{
|
||||||
Grids,
|
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"
|
<ContainerButton xmlns="https://spacestation14.io"
|
||||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||||
Name="BackgroundColorPanel">
|
<PanelContainer Name="BackgroundColorPanel"/>
|
||||||
<BoxContainer Orientation="Horizontal"
|
<BoxContainer Orientation="Horizontal"
|
||||||
HorizontalExpand="True"
|
HorizontalExpand="True"
|
||||||
SeparationOverride="4">
|
SeparationOverride="4">
|
||||||
<Label Name="NameLabel"
|
<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"
|
SizeFlagsStretchRatio="3"
|
||||||
HorizontalExpand="True"
|
HorizontalExpand="True"
|
||||||
ClipText="True"/>
|
ClipText="True"/>
|
||||||
<customControls:VSeparator/>
|
<customControls:VSeparator/>
|
||||||
<Button Name="DeleteButton"
|
<Label Name="EIDLabel"
|
||||||
Text="{Loc object-tab-entity-delete}"
|
SizeFlagsStretchRatio="3"
|
||||||
SizeFlagsStretchRatio="3"
|
HorizontalExpand="True"
|
||||||
HorizontalExpand="True"
|
ClipText="True"/>
|
||||||
ClipText="True"/>
|
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</PanelContainer>
|
</ContainerButton>
|
||||||
|
|||||||
@@ -1,40 +1,19 @@
|
|||||||
using Content.Client.Administration.Managers;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.AutoGenerated;
|
|
||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
|
namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class ObjectsTabEntry : PanelContainer
|
public sealed partial class ObjectsTabEntry : ContainerButton
|
||||||
{
|
{
|
||||||
public NetEntity AssocEntity;
|
public NetEntity AssocEntity;
|
||||||
|
|
||||||
public Action<NetEntity>? OnTeleport;
|
public ObjectsTabEntry(string name, NetEntity nent)
|
||||||
public Action<NetEntity>? OnDelete;
|
|
||||||
private readonly Dictionary<Button, ConfirmationData> _confirmations = new();
|
|
||||||
|
|
||||||
public ObjectsTabEntry(IClientAdminManager manager, string name, NetEntity nent, StyleBox styleBox)
|
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
|
|
||||||
AssocEntity = nent;
|
AssocEntity = nent;
|
||||||
EIDLabel.Text = nent.ToString();
|
EIDLabel.Text = nent.ToString();
|
||||||
NameLabel.Text = name;
|
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">
|
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||||
<Label Text="{Loc admin-ui-panic-bunker-min-account-age}" MinWidth="175" />
|
<Label Text="{Loc admin-ui-panic-bunker-min-account-age}" MinWidth="175" />
|
||||||
<LineEdit Name="MinAccountAge" MinWidth="50" Margin="0 0 5 0" />
|
<LineEdit Name="MinAccountAge" MinWidth="50" Margin="0 0 5 0" />
|
||||||
<Label Text="{Loc generic-minutes}" />
|
<Label Text="{Loc generic-hours}" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||||
<Label Text="{Loc admin-ui-panic-bunker-min-overall-minutes}" MinWidth="175" />
|
<Label Text="{Loc admin-ui-panic-bunker-min-overall-hours}" MinWidth="175" />
|
||||||
<LineEdit Name="MinOverallMinutes" MinWidth="50" Margin="0 0 5 0" />
|
<LineEdit Name="MinOverallHours" MinWidth="50" Margin="0 0 5 0" />
|
||||||
<Label Text="{Loc generic-minutes}" />
|
<Label Text="{Loc generic-hours}" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public sealed partial class PanicBunkerTab : Control
|
|||||||
[Dependency] private readonly IConsoleHost _console = default!;
|
[Dependency] private readonly IConsoleHost _console = default!;
|
||||||
|
|
||||||
private string _minAccountAge;
|
private string _minAccountAge;
|
||||||
private string _minOverallMinutes;
|
private string _minOverallHours;
|
||||||
|
|
||||||
public PanicBunkerTab()
|
public PanicBunkerTab()
|
||||||
{
|
{
|
||||||
@@ -25,9 +25,9 @@ public sealed partial class PanicBunkerTab : Control
|
|||||||
MinAccountAge.OnFocusExit += args => SendMinAccountAge(args.Text);
|
MinAccountAge.OnFocusExit += args => SendMinAccountAge(args.Text);
|
||||||
_minAccountAge = MinAccountAge.Text;
|
_minAccountAge = MinAccountAge.Text;
|
||||||
|
|
||||||
MinOverallMinutes.OnTextEntered += args => SendMinOverallMinutes(args.Text);
|
MinOverallHours.OnTextEntered += args => SendMinOverallHours(args.Text);
|
||||||
MinOverallMinutes.OnFocusExit += args => SendMinOverallMinutes(args.Text);
|
MinOverallHours.OnFocusExit += args => SendMinOverallHours(args.Text);
|
||||||
_minOverallMinutes = MinOverallMinutes.Text;
|
_minOverallHours = MinOverallHours.Text;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SendMinAccountAge(string text)
|
private void SendMinAccountAge(string text)
|
||||||
@@ -42,16 +42,16 @@ public sealed partial class PanicBunkerTab : Control
|
|||||||
_console.ExecuteCommand($"panicbunker_min_account_age {minutes}");
|
_console.ExecuteCommand($"panicbunker_min_account_age {minutes}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SendMinOverallMinutes(string text)
|
private void SendMinOverallHours(string text)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(text) ||
|
if (string.IsNullOrWhiteSpace(text) ||
|
||||||
text == _minOverallMinutes ||
|
text == _minOverallHours ||
|
||||||
!int.TryParse(text, out var minutes))
|
!int.TryParse(text, out var hours))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_console.ExecuteCommand($"panicbunker_min_overall_minutes {minutes}");
|
_console.ExecuteCommand($"panicbunker_min_overall_hours {hours}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateStatus(PanicBunkerStatus status)
|
public void UpdateStatus(PanicBunkerStatus status)
|
||||||
@@ -68,10 +68,10 @@ public sealed partial class PanicBunkerTab : Control
|
|||||||
CountDeadminnedButton.Pressed = status.CountDeadminnedAdmins;
|
CountDeadminnedButton.Pressed = status.CountDeadminnedAdmins;
|
||||||
ShowReasonButton.Pressed = status.ShowReason;
|
ShowReasonButton.Pressed = status.ShowReason;
|
||||||
|
|
||||||
MinAccountAge.Text = status.MinAccountAgeMinutes.ToString();
|
MinAccountAge.Text = status.MinAccountAgeHours.ToString();
|
||||||
_minAccountAge = MinAccountAge.Text;
|
_minAccountAge = MinAccountAge.Text;
|
||||||
|
|
||||||
MinOverallMinutes.Text = status.MinOverallMinutes.ToString();
|
MinOverallHours.Text = status.MinOverallHours.ToString();
|
||||||
_minOverallMinutes = MinOverallMinutes.Text;
|
_minOverallHours = MinOverallHours.Text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
<Control xmlns="https://spacestation14.io"
|
<Control xmlns="https://spacestation14.io"
|
||||||
xmlns:pt="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
|
xmlns:pt="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
|
||||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||||
xmlns:co="clr-namespace:Content.Client.UserInterface.Controls">
|
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Name="PlayerCount" HorizontalExpand="True" Text="{Loc player-tab-player-count}" />
|
<Label Name="PlayerCount" HorizontalExpand="True" SizeFlagsStretchRatio="0.50"
|
||||||
<LineEdit Name="SearchLineEdit" HorizontalExpand="True"
|
Text="{Loc Player Count}" />
|
||||||
PlaceHolder="{Loc player-tab-filter-line-edit-placeholder}" />
|
<Button Name="ShowDisconnectedButton" HorizontalExpand="True" SizeFlagsStretchRatio="0.25"
|
||||||
<Button Name="ShowDisconnectedButton" HorizontalExpand="True"
|
Text="{Loc player-tab-show-disconnected}" ToggleMode="True"/>
|
||||||
Text="{Loc player-tab-show-disconnected}" ToggleMode="True" />
|
<Button Name="OverlayButton" HorizontalExpand="True" SizeFlagsStretchRatio="0.25"
|
||||||
<Button Name="OverlayButton" HorizontalExpand="True" Text="{Loc player-tab-overlay}" ToggleMode="True" />
|
Text="{Loc player-tab-overlay}" ToggleMode="True"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Control MinSize="0 5"/>
|
<Control MinSize="0 5" />
|
||||||
<pt:PlayerTabHeader Name="ListHeader"/>
|
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
|
||||||
<cc:HSeparator/>
|
<BoxContainer Orientation="Vertical" Name="PlayerList">
|
||||||
<co:SearchListContainer Name="SearchList" Access="Public" VerticalExpand="True"/>
|
<pt:PlayerTabHeader Name="ListHeader" />
|
||||||
|
<cc:HSeparator />
|
||||||
|
</BoxContainer>
|
||||||
|
</ScrollContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</Control>
|
</Control>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Client.Administration.Systems;
|
using Content.Client.Administration.Systems;
|
||||||
using Content.Client.UserInterface.Controls;
|
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
@@ -10,219 +9,170 @@ using Robust.Client.UserInterface.XAML;
|
|||||||
using static Content.Client.Administration.UI.Tabs.PlayerTab.PlayerTabHeader;
|
using static Content.Client.Administration.UI.Tabs.PlayerTab.PlayerTabHeader;
|
||||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||||
|
|
||||||
namespace Content.Client.Administration.UI.Tabs.PlayerTab;
|
namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
|
||||||
public sealed partial class PlayerTab : Control
|
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
[GenerateTypedNameReferences]
|
||||||
[Dependency] private readonly IPlayerManager _playerMan = default!;
|
public sealed partial class PlayerTab : Control
|
||||||
|
|
||||||
private const string ArrowUp = "↑";
|
|
||||||
private const string ArrowDown = "↓";
|
|
||||||
private readonly Color _altColor = Color.FromHex("#292B38");
|
|
||||||
private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
|
|
||||||
private readonly AdminSystem _adminSystem;
|
|
||||||
private IReadOnlyList<PlayerInfo> _players = new List<PlayerInfo>();
|
|
||||||
|
|
||||||
private Header _headerClicked = Header.Username;
|
|
||||||
private bool _ascending = true;
|
|
||||||
private bool _showDisconnected;
|
|
||||||
|
|
||||||
public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
|
|
||||||
|
|
||||||
public PlayerTab()
|
|
||||||
{
|
{
|
||||||
IoCManager.InjectDependencies(this);
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
RobustXamlLoader.Load(this);
|
[Dependency] private readonly IPlayerManager _playerMan = default!;
|
||||||
|
|
||||||
_adminSystem = _entManager.System<AdminSystem>();
|
private const string ArrowUp = "↑";
|
||||||
_adminSystem.PlayerListChanged += RefreshPlayerList;
|
private const string ArrowDown = "↓";
|
||||||
_adminSystem.OverlayEnabled += OverlayEnabled;
|
private readonly Color _altColor = Color.FromHex("#292B38");
|
||||||
_adminSystem.OverlayDisabled += OverlayDisabled;
|
private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
|
||||||
|
private readonly AdminSystem _adminSystem;
|
||||||
|
private IReadOnlyList<PlayerInfo> _players = new List<PlayerInfo>();
|
||||||
|
|
||||||
OverlayButton.OnPressed += OverlayButtonPressed;
|
private Header _headerClicked = Header.Username;
|
||||||
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed;
|
private bool _ascending = true;
|
||||||
|
private bool _showDisconnected;
|
||||||
|
|
||||||
ListHeader.BackgroundColorPanel.PanelOverride = new StyleBoxFlat(_altColor);
|
public event Action<PlayerTabEntry, GUIBoundKeyEventArgs>? OnEntryKeyBindDown;
|
||||||
ListHeader.OnHeaderClicked += HeaderClicked;
|
|
||||||
|
|
||||||
SearchList.SearchBar = SearchLineEdit;
|
public PlayerTab()
|
||||||
SearchList.GenerateItem += GenerateButton;
|
|
||||||
SearchList.DataFilterCondition += DataFilterCondition;
|
|
||||||
SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
|
|
||||||
|
|
||||||
RefreshPlayerList(_adminSystem.PlayerList);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Antag Overlay
|
|
||||||
|
|
||||||
private void OverlayEnabled()
|
|
||||||
{
|
|
||||||
OverlayButton.Pressed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OverlayDisabled()
|
|
||||||
{
|
|
||||||
OverlayButton.Pressed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OverlayButtonPressed(ButtonEventArgs args)
|
|
||||||
{
|
|
||||||
if (args.Button.Pressed)
|
|
||||||
{
|
{
|
||||||
_adminSystem.AdminOverlayOn();
|
IoCManager.InjectDependencies(this);
|
||||||
}
|
_adminSystem = _entManager.System<AdminSystem>();
|
||||||
else
|
RobustXamlLoader.Load(this);
|
||||||
{
|
RefreshPlayerList(_adminSystem.PlayerList);
|
||||||
_adminSystem.AdminOverlayOff();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
_adminSystem.PlayerListChanged += RefreshPlayerList;
|
||||||
|
_adminSystem.OverlayEnabled += OverlayEnabled;
|
||||||
|
_adminSystem.OverlayDisabled += OverlayDisabled;
|
||||||
|
|
||||||
private void ShowDisconnectedPressed(ButtonEventArgs args)
|
OverlayButton.OnPressed += OverlayButtonPressed;
|
||||||
{
|
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed;
|
||||||
_showDisconnected = args.Button.Pressed;
|
|
||||||
RefreshPlayerList(_players);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
ListHeader.BackgroundColorPanel.PanelOverride = new StyleBoxFlat(_altColor);
|
||||||
{
|
ListHeader.OnHeaderClicked += HeaderClicked;
|
||||||
base.Dispose(disposing);
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
_adminSystem.PlayerListChanged -= RefreshPlayerList;
|
|
||||||
_adminSystem.OverlayEnabled -= OverlayEnabled;
|
|
||||||
_adminSystem.OverlayDisabled -= OverlayDisabled;
|
|
||||||
|
|
||||||
OverlayButton.OnPressed -= OverlayButtonPressed;
|
|
||||||
|
|
||||||
ListHeader.OnHeaderClicked -= HeaderClicked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region ListContainer
|
|
||||||
|
|
||||||
private void RefreshPlayerList(IReadOnlyList<PlayerInfo> players)
|
|
||||||
{
|
|
||||||
_players = players;
|
|
||||||
PlayerCount.Text = Loc.GetString("player-tab-player-count", ("count", _playerMan.PlayerCount));
|
|
||||||
|
|
||||||
var filteredPlayers = players.Where(info => _showDisconnected || info.Connected).ToList();
|
|
||||||
|
|
||||||
var sortedPlayers = new List<PlayerInfo>(filteredPlayers);
|
|
||||||
sortedPlayers.Sort(Compare);
|
|
||||||
|
|
||||||
UpdateHeaderSymbols();
|
|
||||||
|
|
||||||
SearchList.PopulateList(sortedPlayers.Select(info => new PlayerListData(info,
|
|
||||||
$"{info.Username} {info.CharacterName} {info.IdentityName} {info.StartingJob}"))
|
|
||||||
.ToList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GenerateButton(ListData data, ListContainerButton button)
|
|
||||||
{
|
|
||||||
if (data is not PlayerListData { Info: var player})
|
|
||||||
return;
|
|
||||||
|
|
||||||
var entry = new PlayerTabEntry(player, new StyleBoxFlat(button.Index % 2 == 0 ? _altColor : _defaultColor));
|
|
||||||
button.AddChild(entry);
|
|
||||||
button.ToolTip = $"{player.Username}, {player.CharacterName}, {player.IdentityName}, {player.StartingJob}";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.
|
|
||||||
/// If all characters are lowercase, the comparison ignores case.
|
|
||||||
/// If there is an uppercase character, the comparison is case sensitive.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filter"></param>
|
|
||||||
/// <param name="listData"></param>
|
|
||||||
/// <returns>Whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.</returns>
|
|
||||||
private bool DataFilterCondition(string filter, ListData listData)
|
|
||||||
{
|
|
||||||
if (listData is not PlayerListData {Info: var info, FilteringString: var playerString})
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!_showDisconnected && !info.Connected)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (IsAllLower(filter))
|
|
||||||
{
|
|
||||||
if (!playerString.Contains(filter, StringComparison.CurrentCultureIgnoreCase))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!playerString.Contains(filter))
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
private void OverlayEnabled()
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsAllLower(string input)
|
|
||||||
{
|
|
||||||
foreach (var c in input)
|
|
||||||
{
|
{
|
||||||
if (char.IsLetter(c) && !char.IsLower(c))
|
OverlayButton.Pressed = true;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
private void OverlayDisabled()
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Header
|
|
||||||
|
|
||||||
private void UpdateHeaderSymbols()
|
|
||||||
{
|
|
||||||
ListHeader.ResetHeaderText();
|
|
||||||
ListHeader.GetHeader(_headerClicked).Text += $" {(_ascending ? ArrowUp : ArrowDown)}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private int Compare(PlayerInfo x, PlayerInfo y)
|
|
||||||
{
|
|
||||||
if (!_ascending)
|
|
||||||
{
|
{
|
||||||
(x, y) = (y, x);
|
OverlayButton.Pressed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _headerClicked switch
|
private void OverlayButtonPressed(ButtonEventArgs args)
|
||||||
{
|
{
|
||||||
Header.Username => Compare(x.Username, y.Username),
|
if (args.Button.Pressed)
|
||||||
Header.Character => Compare(x.CharacterName, y.CharacterName),
|
{
|
||||||
Header.Job => Compare(x.StartingJob, y.StartingJob),
|
_adminSystem.AdminOverlayOn();
|
||||||
Header.Antagonist => x.Antag.CompareTo(y.Antag),
|
}
|
||||||
Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default),
|
else
|
||||||
_ => 1
|
{
|
||||||
};
|
_adminSystem.AdminOverlayOff();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int Compare(string x, string y)
|
|
||||||
{
|
|
||||||
return string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HeaderClicked(Header header)
|
|
||||||
{
|
|
||||||
if (_headerClicked == header)
|
|
||||||
{
|
|
||||||
_ascending = !_ascending;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_headerClicked = header;
|
|
||||||
_ascending = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RefreshPlayerList(_adminSystem.PlayerList);
|
private void ShowDisconnectedPressed(ButtonEventArgs args)
|
||||||
}
|
{
|
||||||
|
_showDisconnected = args.Button.Pressed;
|
||||||
|
RefreshPlayerList(_players);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_adminSystem.PlayerListChanged -= RefreshPlayerList;
|
||||||
|
_adminSystem.OverlayEnabled -= OverlayEnabled;
|
||||||
|
_adminSystem.OverlayDisabled -= OverlayDisabled;
|
||||||
|
|
||||||
|
OverlayButton.OnPressed -= OverlayButtonPressed;
|
||||||
|
|
||||||
|
ListHeader.OnHeaderClicked -= HeaderClicked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshPlayerList(IReadOnlyList<PlayerInfo> players)
|
||||||
|
{
|
||||||
|
foreach (var child in PlayerList.Children.ToArray())
|
||||||
|
{
|
||||||
|
if (child is PlayerTabEntry)
|
||||||
|
child.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_players = players;
|
||||||
|
PlayerCount.Text = $"Players: {_playerMan.PlayerCount}";
|
||||||
|
|
||||||
|
var sortedPlayers = new List<PlayerInfo>(players);
|
||||||
|
sortedPlayers.Sort(Compare);
|
||||||
|
|
||||||
|
UpdateHeaderSymbols();
|
||||||
|
|
||||||
|
var useAltColor = false;
|
||||||
|
foreach (var player in sortedPlayers)
|
||||||
|
{
|
||||||
|
if (!_showDisconnected && !player.Connected)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var entry = new PlayerTabEntry(player.Username,
|
||||||
|
player.CharacterName,
|
||||||
|
player.IdentityName,
|
||||||
|
player.StartingJob,
|
||||||
|
player.Antag ? "YES" : "NO",
|
||||||
|
new StyleBoxFlat(useAltColor ? _altColor : _defaultColor),
|
||||||
|
player.Connected,
|
||||||
|
player.PlaytimeString);
|
||||||
|
entry.PlayerEntity = player.NetEntity;
|
||||||
|
entry.OnKeyBindDown += args => OnEntryKeyBindDown?.Invoke(entry, args);
|
||||||
|
entry.ToolTip = Loc.GetString("player-tab-entry-tooltip");
|
||||||
|
PlayerList.AddChild(entry);
|
||||||
|
|
||||||
|
useAltColor ^= true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateHeaderSymbols()
|
||||||
|
{
|
||||||
|
ListHeader.ResetHeaderText();
|
||||||
|
ListHeader.GetHeader(_headerClicked).Text += $" {(_ascending ? ArrowUp : ArrowDown)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private int Compare(PlayerInfo x, PlayerInfo y)
|
||||||
|
{
|
||||||
|
if (!_ascending)
|
||||||
|
{
|
||||||
|
(x, y) = (y, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _headerClicked switch
|
||||||
|
{
|
||||||
|
Header.Username => Compare(x.Username, y.Username),
|
||||||
|
Header.Character => Compare(x.CharacterName, y.CharacterName),
|
||||||
|
Header.Job => Compare(x.StartingJob, y.StartingJob),
|
||||||
|
Header.Antagonist => x.Antag.CompareTo(y.Antag),
|
||||||
|
Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default),
|
||||||
|
_ => 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private int Compare(string x, string y)
|
||||||
|
{
|
||||||
|
return string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HeaderClicked(Header header)
|
||||||
|
{
|
||||||
|
if (_headerClicked == header)
|
||||||
|
{
|
||||||
|
_ascending = !_ascending;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_headerClicked = header;
|
||||||
|
_ascending = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshPlayerList(_adminSystem.PlayerList);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record PlayerListData(PlayerInfo Info, string FilteringString) : ListData;
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<PanelContainer xmlns="https://spacestation14.io"
|
<ContainerButton xmlns="https://spacestation14.io"
|
||||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||||
Name="BackgroundColorPanel">
|
<PanelContainer Name="BackgroundColorPanel"/>
|
||||||
<BoxContainer Orientation="Horizontal"
|
<BoxContainer Orientation="Horizontal"
|
||||||
HorizontalExpand="True"
|
HorizontalExpand="True"
|
||||||
SeparationOverride="4">
|
SeparationOverride="4">
|
||||||
@@ -15,18 +15,17 @@
|
|||||||
ClipText="True"/>
|
ClipText="True"/>
|
||||||
<customControls:VSeparator/>
|
<customControls:VSeparator/>
|
||||||
<Label Name="JobLabel"
|
<Label Name="JobLabel"
|
||||||
SizeFlagsStretchRatio="2"
|
SizeFlagsStretchRatio="3"
|
||||||
HorizontalExpand="True"
|
HorizontalExpand="True"
|
||||||
ClipText="True"/>
|
ClipText="True"/>
|
||||||
<customControls:VSeparator/>
|
<customControls:VSeparator/>
|
||||||
<Label Name="AntagonistLabel"
|
<Label Name="AntagonistLabel"
|
||||||
SizeFlagsStretchRatio="1"
|
SizeFlagsStretchRatio="2"
|
||||||
HorizontalExpand="True"
|
HorizontalExpand="True"
|
||||||
ClipText="True"/>
|
ClipText="True"/>
|
||||||
<customControls:VSeparator/>
|
|
||||||
<Label Name="OverallPlaytimeLabel"
|
<Label Name="OverallPlaytimeLabel"
|
||||||
SizeFlagsStretchRatio="1"
|
SizeFlagsStretchRatio="2"
|
||||||
HorizontalExpand="True"
|
HorizontalExpand="True"
|
||||||
ClipText="True"/>
|
ClipText="True"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</PanelContainer>
|
</ContainerButton>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Content.Shared.Administration;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.AutoGenerated;
|
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
@@ -7,24 +6,23 @@ using Robust.Client.UserInterface.XAML;
|
|||||||
namespace Content.Client.Administration.UI.Tabs.PlayerTab;
|
namespace Content.Client.Administration.UI.Tabs.PlayerTab;
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class PlayerTabEntry : PanelContainer
|
public sealed partial class PlayerTabEntry : ContainerButton
|
||||||
{
|
{
|
||||||
public NetEntity? PlayerEntity;
|
public NetEntity? PlayerEntity;
|
||||||
|
|
||||||
public PlayerTabEntry(PlayerInfo player, StyleBoxFlat styleBoxFlat)
|
public PlayerTabEntry(string username, string character, string identity, string job, string antagonist, StyleBox styleBox, bool connected, string overallPlaytime)
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
|
|
||||||
UsernameLabel.Text = player.Username;
|
UsernameLabel.Text = username;
|
||||||
if (!player.Connected)
|
if (!connected)
|
||||||
UsernameLabel.StyleClasses.Add("Disabled");
|
UsernameLabel.StyleClasses.Add("Disabled");
|
||||||
JobLabel.Text = player.StartingJob;
|
JobLabel.Text = job;
|
||||||
CharacterLabel.Text = player.CharacterName;
|
CharacterLabel.Text = character;
|
||||||
if (player.IdentityName != player.CharacterName)
|
if (identity != character)
|
||||||
CharacterLabel.Text += $" [{player.IdentityName}]";
|
CharacterLabel.Text += $" [{identity}]";
|
||||||
AntagonistLabel.Text = Loc.GetString(player.Antag ? "player-tab-is-antag-yes" : "player-tab-is-antag-no");
|
AntagonistLabel.Text = antagonist;
|
||||||
BackgroundColorPanel.PanelOverride = styleBoxFlat;
|
BackgroundColorPanel.PanelOverride = styleBox;
|
||||||
OverallPlaytimeLabel.Text = player.PlaytimeString;
|
OverallPlaytimeLabel.Text = overallPlaytime;
|
||||||
PlayerEntity = player.NetEntity;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,25 +19,23 @@
|
|||||||
MouseFilter="Pass"/>
|
MouseFilter="Pass"/>
|
||||||
<cc:VSeparator/>
|
<cc:VSeparator/>
|
||||||
<Label Name="JobLabel"
|
<Label Name="JobLabel"
|
||||||
SizeFlagsStretchRatio="2"
|
SizeFlagsStretchRatio="3"
|
||||||
HorizontalExpand="True"
|
HorizontalExpand="True"
|
||||||
ClipText="True"
|
ClipText="True"
|
||||||
Text="{Loc player-tab-job}"
|
Text="{Loc player-tab-job}"
|
||||||
MouseFilter="Pass"/>
|
MouseFilter="Pass"/>
|
||||||
<cc:VSeparator/>
|
<cc:VSeparator/>
|
||||||
<Label Name="AntagonistLabel"
|
<Label Name="AntagonistLabel"
|
||||||
SizeFlagsStretchRatio="1"
|
SizeFlagsStretchRatio="2"
|
||||||
HorizontalExpand="True"
|
HorizontalExpand="True"
|
||||||
ClipText="True"
|
ClipText="True"
|
||||||
Text="{Loc player-tab-antagonist}"
|
Text="{Loc player-tab-antagonist}"
|
||||||
MouseFilter="Pass"/>
|
MouseFilter="Pass"/>
|
||||||
<cc:VSeparator/>
|
|
||||||
<Label Name="PlaytimeLabel"
|
<Label Name="PlaytimeLabel"
|
||||||
SizeFlagsStretchRatio="1"
|
SizeFlagsStretchRatio="2"
|
||||||
HorizontalExpand="True"
|
HorizontalExpand="True"
|
||||||
ClipText="True"
|
ClipText="True"
|
||||||
Text="{Loc player-tab-playtime}"
|
Text="{Loc player-tab-playtime}"
|
||||||
MouseFilter="Pass"
|
MouseFilter="Pass"/>
|
||||||
ToolTip="{Loc player-tab-entry-tooltip}"/>
|
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</Control>
|
</Control>
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
MinSize="50 50">
|
MinSize="50 50">
|
||||||
<GridContainer
|
<GridContainer
|
||||||
Columns="3">
|
Columns="3">
|
||||||
<cc:CommandButton Command="startround" Text="{Loc administration-ui-round-tab-start-round}" />
|
<cc:CommandButton Command="startround" Text="{Loc Start Round}" />
|
||||||
<cc:CommandButton Command="endround" Text="{Loc administration-ui-round-tab-end-round}" />
|
<cc:CommandButton Command="endround" Text="{Loc End Round}" />
|
||||||
<cc:CommandButton Command="restartround" Text="{Loc administration-ui-round-tab-restart-round}" />
|
<cc:CommandButton Command="restartround" Text="{Loc Restart Round}" />
|
||||||
<cc:CommandButton Command="restartroundnow" Text="{Loc administration-ui-round-tab-restart-round-now}" />
|
<cc:CommandButton Command="restartroundnow" Text="{Loc administration-ui-round-tab-restart-round-now}" />
|
||||||
</GridContainer>
|
</GridContainer>
|
||||||
</Control>
|
</Control>
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ public sealed class ClientAlertsSystem : AlertsSystem
|
|||||||
ClearAlerts?.Invoke(this, EventArgs.Empty);
|
ClearAlerts?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AlertClicked(ProtoId<AlertPrototype> alertType)
|
public void AlertClicked(AlertType alertType)
|
||||||
{
|
{
|
||||||
RaisePredictiveEvent(new ClickAlertEvent(alertType));
|
RaiseNetworkEvent(new ClickAlertEvent(alertType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Content.Shared.Ame.Components;
|
using Content.Shared.Ame.Components;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.UserInterface;
|
|
||||||
|
|
||||||
namespace Content.Client.Ame.UI
|
namespace Content.Client.Ame.UI
|
||||||
{
|
{
|
||||||
@@ -17,8 +16,9 @@ namespace Content.Client.Ame.UI
|
|||||||
{
|
{
|
||||||
base.Open();
|
base.Open();
|
||||||
|
|
||||||
_window = this.CreateWindow<AmeWindow>();
|
_window = new AmeWindow(this);
|
||||||
_window.OnAmeButton += ButtonPressed;
|
_window.OnClose += Close;
|
||||||
|
_window.OpenCentered();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -40,5 +40,15 @@ namespace Content.Client.Ame.UI
|
|||||||
{
|
{
|
||||||
SendMessage(new UiButtonPressedMessage(button));
|
SendMessage(new UiButtonPressedMessage(button));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_window?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Linq;
|
|
||||||
using Content.Client.UserInterface;
|
using Content.Client.UserInterface;
|
||||||
using Content.Shared.Ame.Components;
|
using Content.Shared.Ame.Components;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
@@ -10,17 +9,15 @@ namespace Content.Client.Ame.UI
|
|||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class AmeWindow : DefaultWindow
|
public sealed partial class AmeWindow : DefaultWindow
|
||||||
{
|
{
|
||||||
public event Action<UiButton>? OnAmeButton;
|
public AmeWindow(AmeControllerBoundUserInterface ui)
|
||||||
|
|
||||||
public AmeWindow()
|
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
IoCManager.InjectDependencies(this);
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
EjectButton.OnPressed += _ => OnAmeButton?.Invoke(UiButton.Eject);
|
EjectButton.OnPressed += _ => ui.ButtonPressed(UiButton.Eject);
|
||||||
ToggleInjection.OnPressed += _ => OnAmeButton?.Invoke(UiButton.ToggleInjection);
|
ToggleInjection.OnPressed += _ => ui.ButtonPressed(UiButton.ToggleInjection);
|
||||||
IncreaseFuelButton.OnPressed += _ => OnAmeButton?.Invoke(UiButton.IncreaseFuel);
|
IncreaseFuelButton.OnPressed += _ => ui.ButtonPressed(UiButton.IncreaseFuel);
|
||||||
DecreaseFuelButton.OnPressed += _ => OnAmeButton?.Invoke(UiButton.DecreaseFuel);
|
DecreaseFuelButton.OnPressed += _ => ui.ButtonPressed(UiButton.DecreaseFuel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -32,7 +29,7 @@ namespace Content.Client.Ame.UI
|
|||||||
var castState = (AmeControllerBoundUserInterfaceState) state;
|
var castState = (AmeControllerBoundUserInterfaceState) state;
|
||||||
|
|
||||||
// Disable all buttons if not powered
|
// Disable all buttons if not powered
|
||||||
if (Contents.Children.Any())
|
if (Contents.Children != null)
|
||||||
{
|
{
|
||||||
ButtonHelpers.SetButtonDisabledRecursive(Contents, !castState.HasPower);
|
ButtonHelpers.SetButtonDisabledRecursive(Contents, !castState.HasPower);
|
||||||
EjectButton.Disabled = false;
|
EjectButton.Disabled = false;
|
||||||
@@ -68,8 +65,8 @@ namespace Content.Client.Ame.UI
|
|||||||
CoreCount.Text = $"{castState.CoreCount}";
|
CoreCount.Text = $"{castState.CoreCount}";
|
||||||
InjectionAmount.Text = $"{castState.InjectionAmount}";
|
InjectionAmount.Text = $"{castState.InjectionAmount}";
|
||||||
// format power statistics to pretty numbers
|
// format power statistics to pretty numbers
|
||||||
CurrentPowerSupply.Text = $"{castState.CurrentPowerSupply:N1}";
|
CurrentPowerSupply.Text = $"{castState.CurrentPowerSupply.ToString("N1")}";
|
||||||
TargetedPowerSupply.Text = $"{castState.TargetedPowerSupply:N1}";
|
TargetedPowerSupply.Text = $"{castState.TargetedPowerSupply.ToString("N1")}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace Content.Client.Animations;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Entities with this component tracks the user's world position every frame.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed partial class TrackUserComponent : Component
|
|
||||||
{
|
|
||||||
public EntityUid? User;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Offset in the direction of the entity's rotation.
|
|
||||||
/// </summary>
|
|
||||||
public Vector2 Offset = Vector2.Zero;
|
|
||||||
}
|
|
||||||
@@ -20,9 +20,8 @@ public sealed class AnomalySystem : SharedAnomalySystem
|
|||||||
SubscribeLocalEvent<AnomalyComponent, AppearanceChangeEvent>(OnAppearanceChanged);
|
SubscribeLocalEvent<AnomalyComponent, AppearanceChangeEvent>(OnAppearanceChanged);
|
||||||
SubscribeLocalEvent<AnomalyComponent, ComponentStartup>(OnStartup);
|
SubscribeLocalEvent<AnomalyComponent, ComponentStartup>(OnStartup);
|
||||||
SubscribeLocalEvent<AnomalyComponent, AnimationCompletedEvent>(OnAnimationComplete);
|
SubscribeLocalEvent<AnomalyComponent, AnimationCompletedEvent>(OnAnimationComplete);
|
||||||
|
|
||||||
SubscribeLocalEvent<AnomalySupercriticalComponent, ComponentShutdown>(OnShutdown);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStartup(EntityUid uid, AnomalyComponent component, ComponentStartup args)
|
private void OnStartup(EntityUid uid, AnomalyComponent component, ComponentStartup args)
|
||||||
{
|
{
|
||||||
_floating.FloatAnimation(uid, component.FloatingOffset, component.AnimationKey, component.AnimationTime);
|
_floating.FloatAnimation(uid, component.FloatingOffset, component.AnimationKey, component.AnimationTime);
|
||||||
@@ -76,13 +75,4 @@ public sealed class AnomalySystem : SharedAnomalySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnShutdown(Entity<AnomalySupercriticalComponent> ent, ref ComponentShutdown args)
|
|
||||||
{
|
|
||||||
if (!TryComp<SpriteComponent>(ent, out var sprite))
|
|
||||||
return;
|
|
||||||
|
|
||||||
sprite.Scale = Vector2.One;
|
|
||||||
sprite.Color = sprite.Color.WithAlpha(1f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
using Content.Shared.Anomaly.Components;
|
|
||||||
using Content.Shared.Anomaly.Effects;
|
|
||||||
using Content.Shared.Body.Components;
|
|
||||||
using Robust.Client.GameObjects;
|
|
||||||
|
|
||||||
namespace Content.Client.Anomaly.Effects;
|
|
||||||
|
|
||||||
public sealed class ClientInnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
|
|
||||||
{
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
SubscribeLocalEvent<InnerBodyAnomalyComponent, AfterAutoHandleStateEvent>(OnAfterHandleState);
|
|
||||||
SubscribeLocalEvent<InnerBodyAnomalyComponent, ComponentShutdown>(OnCompShutdown);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnAfterHandleState(Entity<InnerBodyAnomalyComponent> ent, ref AfterAutoHandleStateEvent args)
|
|
||||||
{
|
|
||||||
if (!TryComp<SpriteComponent>(ent, out var sprite))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (ent.Comp.FallbackSprite is null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!sprite.LayerMapTryGet(ent.Comp.LayerMap, out var index))
|
|
||||||
index = sprite.LayerMapReserveBlank(ent.Comp.LayerMap);
|
|
||||||
|
|
||||||
if (TryComp<BodyComponent>(ent, out var body) &&
|
|
||||||
body.Prototype is not null &&
|
|
||||||
ent.Comp.SpeciesSprites.TryGetValue(body.Prototype.Value, out var speciesSprite))
|
|
||||||
{
|
|
||||||
sprite.LayerSetSprite(index, speciesSprite);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprite.LayerSetSprite(index, ent.Comp.FallbackSprite);
|
|
||||||
}
|
|
||||||
|
|
||||||
sprite.LayerSetVisible(index, true);
|
|
||||||
sprite.LayerSetShader(index, "unshaded");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCompShutdown(Entity<InnerBodyAnomalyComponent> ent, ref ComponentShutdown args)
|
|
||||||
{
|
|
||||||
if (!TryComp<SpriteComponent>(ent, out var sprite))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var index = sprite.LayerMapGet(ent.Comp.LayerMap);
|
|
||||||
sprite.LayerSetVisible(index, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using Content.Shared.Anomaly;
|
using Content.Shared.Anomaly;
|
||||||
|
using Content.Shared.Gravity;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.GameObjects;
|
||||||
|
|
||||||
namespace Content.Client.Anomaly.Ui;
|
namespace Content.Client.Anomaly.Ui;
|
||||||
|
|
||||||
@@ -17,8 +18,10 @@ public sealed class AnomalyGeneratorBoundUserInterface : BoundUserInterface
|
|||||||
{
|
{
|
||||||
base.Open();
|
base.Open();
|
||||||
|
|
||||||
_window = this.CreateWindow<AnomalyGeneratorWindow>();
|
_window = new(Owner);
|
||||||
_window.SetEntity(Owner);
|
|
||||||
|
_window.OpenCentered();
|
||||||
|
_window.OnClose += Close;
|
||||||
|
|
||||||
_window.OnGenerateButtonPressed += () =>
|
_window.OnGenerateButtonPressed += () =>
|
||||||
{
|
{
|
||||||
@@ -34,5 +37,18 @@ public sealed class AnomalyGeneratorBoundUserInterface : BoundUserInterface
|
|||||||
return;
|
return;
|
||||||
_window?.UpdateState(msg);
|
_window?.UpdateState(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
if (!disposing) return;
|
||||||
|
|
||||||
|
_window?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPowerSwitch(bool on)
|
||||||
|
{
|
||||||
|
SendMessage(new SharedGravityGeneratorComponent.SwitchGeneratorMessage(on));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user