diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 1adc0b803df..4e1a86d79eb 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,65 +1,7 @@
# Last match in file takes precedence.
-# Sorting by path instead of by who added it one day :(
-# this isn't how codeowners rules work pls read the first comment instead of trying to force a sorting order
-/Resources/ConfigPresets/WizardsDen/ @Chief-Engineer
+# Ping for all PRs
+* @Morb0
-# Moony's Gargantuan List Of Things She Cares About, or MGLOTSCA for short.
-# You need to add your name to these entries, not make a new one, if you care about them.
-/Content.*/Toolshed/ @moonheart08
-**/Toolshed/** @moonheart08
-*Command.cs @moonheart08
-/Content.*/Administration/ @moonheart08 @DrSmugleaf @Chief-Engineer
-/Content.*/Station/ @moonheart08
-/Content.*/Maps/ @moonheart08
-/Content.*/GameTicking/ @moonheart08 @EmoGarbage404
-/Resources/ServerInfo/ @moonheart08 @Chief-Engineer
-/Resources/ServerInfo/Guidebook/ @moonheart08 @EmoGarbage404
-/Resources/engineCommandPerms.yml @moonheart08 @Chief-Engineer
-/Resources/clientCommandPerms.yml @moonheart08 @Chief-Engineer
-
-/Resources/Prototypes/Maps/ @Emisse
-
-/Resources/Prototypes/Body/ @DrSmugleaf # suffering
-/Resources/Prototypes/Entities/Mobs/Player/ @DrSmugleaf
-/Resources/Prototypes/Entities/Mobs/Species/ @DrSmugleaf
-/Content.*/Body/ @DrSmugleaf
-/Content.YAMLLinter @DrSmugleaf
-
-/Content.*/Verbs/ @ElectroJr
-/Content.Client/ContextMenu/ @ElectroJr
-/Content.Shared/Damage/ @ElectroJr @DrSmugleaf
-/Content.Shared/Containers/ItemSlot/ @ElectroJr
-/Content.*/PowerCell/ @ElectroJr
-/Content.*/Doors/ @ElectroJr
-/Content.*/Interaction/ @ElectroJr
-/Content.*/Hands/ @ElectroJr
-/Content.*/Actions/ @ElectroJr
-/Content.*/Explosion/ @ElectroJr
-
-/Content.*/Anomaly/ @EmoGarbage404
-/Content.*/Lathe/ @EmoGarbage404
-/Content.*/Materials/ @EmoGarbage404
-/Content.*/Mech/ @EmoGarbage404
-/Content.*/Research/ @EmoGarbage404
-/Content.*/Stack/ @EmoGarbage404
-/Content.*/Xenoarchaeology/ @EmoGarbage404
-/Content.*/Zombies/ @EmoGarbage404
-/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml @EmoGarbage404
-/Resources/Prototypes/Research/ @EmoGarbage404
-
-/Content.*/Forensics/ @ficcialfaint
-
-# SKREEEE
-/Content.*.Database/ @PJB3005 @DrSmugleaf
-/Content.Shared.Database/Log*.cs @PJB3005 @DrSmugleaf @Chief-Engineer
-/Pow3r/ @PJB3005
-/Content.Server/Power/Pow3r/ @PJB3005
-
-# notafet
-/Content.*/Atmos/ @Partmedia
-/Content.*/Botany/ @Partmedia
-
-#Jezi
-/Content.*/Medical @Jezithyr
-/Content.*/Body @Jezithyr
+# Ping for all PRs that include translations/editing fluent strings
+*.ftl @ficcialfaint
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 00000000000..f3666ae94fc
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,29 @@
+name: "Сообщить о проблеме"
+description: "Если что-то работает не так, как ожидалось."
+labels: ["triage"]
+body:
+ - type: textarea
+ id: description
+ validations:
+ required: true
+ attributes:
+ label: "Описание"
+ description: "Опишите проблему как можно подробнее"
+
+ - type: textarea
+ id: reproduction
+ attributes:
+ label: "Шаги воспроизведения"
+ description: "Если приемлемо, опишите шаги для воспроизведения проблемы"
+ placeholder: |
+ 1. Открыть интерфейс консоли
+ 2. Нажать кнопку "Старт"
+ 3. Получить ошибку...
+
+ - type: textarea
+ id: screenshots
+ attributes:
+ label: "Скриншоты"
+ description: |
+ Если приемлемо, добавьте скриншоты, чтобы помочь объяснить вашу проблему
+ **Подсказка**: Вы можете прикрепить изображения щелкнув по области ниже, чтобы выделить её, а затем перетащив в нее файлы
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 09c9e76b193..f00e08f8d2e 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,7 +1,7 @@
contact_links:
- - name: Report a Security Vulnerability
- url: https://github.com/space-wizards/space-station-14/blob/master/SECURITY.md
- about: Please report security vulnerabilities privately so we can fix them before they are publicly disclosed.
- - name: Request a Feature
- url: https://discord.gg/rGvu9hKffJ
- about: Submit feature requests on our Discord server (https://discord.gg/rGvu9hKffJ).
+ - name: Предложение
+ url: https://discord.station14.ru
+ about: Свои предложения можете оставлять в соответствующем канале Discord.
+ - name: Сообщить об уязвимости
+ url: https://discord.station14.ru
+ about: Пожалуйста, сообщайте о серьезных эксплойтах и уязвимостях безопасности Morty#7384 (369476049836310528) в Discord.
diff --git a/.github/ISSUE_TEMPLATE/issue_report.md b/.github/ISSUE_TEMPLATE/issue_report.md
deleted file mode 100644
index 9bc805b74e1..00000000000
--- a/.github/ISSUE_TEMPLATE/issue_report.md
+++ /dev/null
@@ -1,20 +0,0 @@
----
-name: Report an Issue
-about: "..."
-title: ''
-labels: ''
-assignees: ''
-
----
-
-## Description
-
-
-**Reproduction**
-
-
-**Screenshots**
-
-
-**Additional context**
-
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index a397604185a..e56d2bfceda 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,43 +1,36 @@
-
-
+
-## About the PR
-
+## Описание PR
+
-## Why / Balance
-
+**Медиа**
+
-## Technical details
-
+**Проверки**
+
+- [ ] PR полностью завершён и мне не нужна помощь чтобы его закончить.
+- [ ] Я внимательно просмотрел все свои изменения и багов в них не нашёл.
+- [ ] Я запускал локальный сервер со своими изменениями и всё протестировал.
+- [ ] Я добавил скриншот/видео демонстрации PR в игре, **или** этот PR этого не требует.
-## Media
-
+В журнал изменений следует помещать только то, что действительно важно игрокам.
-- [ ] I have added screenshots/videos to this PR showcasing its changes ingame, **or** this PR does not require an ingame showcase
+В списке изменений тип значка не является часть предложения, поэтому явно указывайте - Добавлен, Удалён, Изменён.
+плохо: - add: Новый инструмент для инженеров
+хорошо: - add: Добавлен новый инструмент для инженеров
-## Breaking changes
-
+Вы можете указать своё имя после символа :cl: именно оно будет отображаться в журнале изменений (иначе будет использоваться ваше имя на GitHub)
+Например: :cl: Ian
-**Changelog**
-
-
+- add: Добавлено веселье!
+- remove: Убрано веселье!
+- tweak: Изменено веселье!
+- fix: Исправлено веселье!
diff --git a/.github/labeler.yml b/.github/labeler.yml
index b088f229434..e54e8922146 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -8,5 +8,8 @@
"Changes: UI":
- '**/*.xaml*'
+"Changes: Localization":
+- 'Resources/Locale/**/*.ftl'
+
"No C#":
- all: ["!**/*.cs"]
diff --git a/.github/workflows/build-test-debug.yml b/.github/workflows/build-test-debug.yml
index 9abd4fbe17e..54b11ee98ad 100644
--- a/.github/workflows/build-test-debug.yml
+++ b/.github/workflows/build-test-debug.yml
@@ -10,7 +10,7 @@ on:
jobs:
build:
- if: github.actor != 'PJBot' && github.event.pull_request.draft == false
+ if: github.actor != 'IanComradeBot' && github.event.pull_request.draft == false
strategy:
matrix:
os: [ubuntu-latest]
diff --git a/.github/workflows/conflict-labeler.yml b/.github/workflows/conflict-labeler.yml
index a78716bde68..951042d087b 100644
--- a/.github/workflows/conflict-labeler.yml
+++ b/.github/workflows/conflict-labeler.yml
@@ -8,7 +8,7 @@ on:
jobs:
Label:
- if: github.actor != 'PJBot'
+ if: github.actor != 'IanComradeBot'
runs-on: ubuntu-latest
steps:
- name: Check for Merge Conflicts
diff --git a/.github/workflows/labeler-approve.yml b/.github/workflows/labeler-approve.yml
new file mode 100644
index 00000000000..15b092aa3b3
--- /dev/null
+++ b/.github/workflows/labeler-approve.yml
@@ -0,0 +1,18 @@
+name: "Labels: Approve"
+
+on:
+ pull_request_review:
+ types: [submitted]
+
+jobs:
+ remove_label:
+ permissions:
+ pull-requests: write
+ if: github.event.review.state == 'approved'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions-ecosystem/action-remove-labels@v1
+ with:
+ labels: |
+ Status: Needs Review
+ Status: Awaiting Changes
diff --git a/.github/workflows/labeler-changes.yml b/.github/workflows/labeler-changes.yml
new file mode 100644
index 00000000000..e7d8fc45e16
--- /dev/null
+++ b/.github/workflows/labeler-changes.yml
@@ -0,0 +1,19 @@
+name: "Labels: Changes"
+
+on:
+ pull_request_review:
+ types: [submitted]
+
+jobs:
+ update_label:
+ permissions:
+ pull-requests: write
+ if: github.event.review.state == 'changes_requested'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions-ecosystem/action-add-labels@v1
+ with:
+ labels: "Status: Awaiting Changes"
+ - uses: actions-ecosystem/action-remove-labels@v1
+ with:
+ labels: "Status: Needs Review"
diff --git a/.github/workflows/labeler-pr.yml b/.github/workflows/labeler-pr.yml
index 711eb0ccac0..31b860c3c36 100644
--- a/.github/workflows/labeler-pr.yml
+++ b/.github/workflows/labeler-pr.yml
@@ -5,7 +5,7 @@ on:
jobs:
labeler:
- if: github.actor != 'PJBot'
+ if: github.actor != 'IanComradeBot'
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v3
diff --git a/.github/workflows/publish-publish.yml b/.github/workflows/publish-publish.yml
new file mode 100644
index 00000000000..9f4efd87dfe
--- /dev/null
+++ b/.github/workflows/publish-publish.yml
@@ -0,0 +1,74 @@
+name: Publish Public
+
+#concurrency:
+# group: publish
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: '0 1 * * *'
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Check configuration
+ env:
+ PUBLIC_PATH: ${{ secrets.BUILDS_PUBLIC_PATH }}
+ if: ${{ env.PUBLIC_PATH == '' }}
+ uses: andymckay/cancel-action@0.3
+
+ - uses: actions/checkout@v3.6.0
+ with:
+ submodules: 'recursive'
+
+ - name: Setup .NET Core
+ uses: actions/setup-dotnet@v3.2.0
+ with:
+ dotnet-version: 7.0.x
+
+ - name: Get Engine Tag
+ run: |
+ cd RobustToolbox
+ git fetch --depth=1
+
+ - name: Install dependencies
+ run: dotnet restore
+
+ - name: Build Packaging
+ run: dotnet build Content.Packaging --configuration Release --no-restore /m
+
+ - name: Package server
+ run: dotnet run --project Content.Packaging server --platform win-x64 --platform linux-x64 --platform osx-x64 --platform linux-arm64
+
+ - name: Package client
+ run: dotnet run --project Content.Packaging client --no-wipe-release
+
+ - name: Update Build Info
+ env:
+ FORK_ID: ${{ vars.FORK_ID }}
+ run: Tools/gen_build_info.py
+
+ - name: Shuffle files around
+ run: |
+ mkdir "release/${{ github.sha }}"
+ mv release/SS14.Server*.zip "release/${{ github.sha }}"
+
+ - name: Upload files to mothership
+ uses: burnett01/rsync-deployments@5.2
+ with:
+ switches: -avzr --ignore-existing
+ path: "release/${{ github.sha }}"
+ remote_path: ${{ secrets.BUILDS_PUBLIC_PATH }}
+ remote_host: ${{ secrets.BUILDS_HOST }}
+ remote_user: ${{ secrets.BUILDS_USERNAME }}
+ remote_key: ${{ secrets.BUILDS_SSH_KEY }}
+
+ - name: Generate public HTML
+ uses: appleboy/ssh-action@master
+ with:
+ host: ${{ secrets.BUILDS_HOST }}
+ username: ${{ secrets.BUILDS_USERNAME }}
+ key: ${{ secrets.BUILDS_SSH_KEY }}
+ script: node ~/scripts/generate_html_page.js -fork ${{ vars.FORK_ID_PUBLIC }} -id ${{ github.sha }}
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 82837638390..78b05f9c87e 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -6,19 +6,38 @@ concurrency:
on:
workflow_dispatch:
schedule:
- - cron: '0 10 * * *'
+ - cron: '0 6 * * *'
jobs:
build:
runs-on: ubuntu-latest
steps:
- - name: Install dependencies
- run: sudo apt-get install -y python3-paramiko
+# - name: Install dependencies
+# run: sudo apt-get install -y python3-paramiko
- uses: actions/checkout@v3.6.0
with:
submodules: 'recursive'
+
+ # Corvax-Secrets-Start
+ - name: Setup secrets
+ env:
+ SSH_KEY: ${{ secrets.SECRETS_PRIVATE_KEY }}
+ if: ${{ env.SSH_KEY != '' }}
+ run: |
+ mkdir ~/.ssh
+ echo "${{ secrets.SECRETS_PRIVATE_KEY }}" > ~/.ssh/id_rsa
+ chmod 600 ~/.ssh/id_rsa
+ echo "HOST *" > ~/.ssh/config
+ echo "StrictHostKeyChecking no" >> ~/.ssh/config
+ git -c submodule.Secrets.update=checkout submodule update --init
+ cp -R Secrets/Resources/Prototypes Resources/Prototypes/CorvaxSecrets
+ cp -R Secrets/Resources/ServerPrototypes Resources/Prototypes/CorvaxSecretsServer
+ cp -R Secrets/Resources/Locale Resources/Locale/ru-RU/corvax-secrets
+ cp -R Secrets/Resources/Textures Resources/Textures/CorvaxSecrets
+ # Corvax-Secrets-End
+
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0
with:
@@ -42,6 +61,8 @@ jobs:
run: dotnet run --project Content.Packaging client --no-wipe-release
- name: Update Build Info
+ env:
+ FORK_ID: ${{ vars.FORK_ID }}
run: Tools/gen_build_info.py
- name: Shuffle files around
@@ -49,31 +70,32 @@ jobs:
mkdir "release/${{ github.sha }}"
mv release/*.zip "release/${{ github.sha }}"
- - name: Upload files to centcomm
- uses: appleboy/scp-action@master
+ - name: Upload files to mothership
+ uses: burnett01/rsync-deployments@5.2
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
+ switches: -avzr --ignore-existing
+ path: "release/${{ github.sha }}"
+ remote_path: ${{ secrets.BUILDS_PATH }}
+ remote_host: ${{ secrets.BUILDS_HOST }}
+ remote_user: ${{ secrets.BUILDS_USERNAME }}
+ remote_key: ${{ secrets.BUILDS_SSH_KEY }}
- name: Update manifest JSON
uses: appleboy/ssh-action@master
with:
- host: centcomm.spacestation14.io
- username: wizards-build-push
- key: ${{ secrets.CENTCOMM_WIZARDS_BUILDS_PUSH_KEY }}
- script: /home/wizards-build-push/push.ps1 ${{ github.sha }}
-
- - name: Publish changelog (Discord)
- run: Tools/actions_changelogs_since_last_run.py
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- DISCORD_WEBHOOK_URL: ${{ secrets.CHANGELOG_DISCORD_WEBHOOK }}
-
- - name: Publish changelog (RSS)
- run: Tools/actions_changelog_rss.py
- env:
- CHANGELOG_RSS_KEY: ${{ secrets.CHANGELOG_RSS_KEY }}
+ host: ${{ secrets.BUILDS_HOST }}
+ username: ${{ secrets.BUILDS_USERNAME }}
+ key: ${{ secrets.BUILDS_SSH_KEY }}
+ script: node ~/scripts/push_to_manifest.js -fork ${{ vars.FORK_ID }} -id ${{ github.sha }}
+
+# - name: Publish changelog (Discord)
+# run: Tools/actions_changelogs_since_last_run.py
+# env:
+# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+# DISCORD_WEBHOOK_URL: ${{ secrets.CHANGELOG_DISCORD_WEBHOOK }}
+# TRANSLATION_API_URL: ${{ secrets.CHANGELOG_TRANSLATION_API_URL }}
+#
+# - name: Publish changelog (RSS)
+# run: Tools/actions_changelog_rss.py
+# env:
+# CHANGELOG_RSS_KEY: ${{ secrets.CHANGELOG_RSS_KEY }}
diff --git a/.github/workflows/test-packaging.yml b/.github/workflows/test-packaging.yml
index b22f307de57..859c04ce8cc 100644
--- a/.github/workflows/test-packaging.yml
+++ b/.github/workflows/test-packaging.yml
@@ -29,7 +29,7 @@ on:
jobs:
build:
name: Test Packaging
- if: github.actor != 'PJBot' && github.event.pull_request.draft == false
+ if: github.actor != 'IanComradeBot' && github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
@@ -48,6 +48,20 @@ jobs:
cd RobustToolbox/
git submodule update --init --recursive
+ # Corvax-Secrets-Start
+ - name: Setup secrets
+ env:
+ SSH_KEY: ${{ secrets.SECRETS_PRIVATE_KEY }}
+ if: ${{ env.SSH_KEY != '' }}
+ run: |
+ mkdir ~/.ssh
+ echo "${{ secrets.SECRETS_PRIVATE_KEY }}" > ~/.ssh/id_rsa
+ chmod 600 ~/.ssh/id_rsa
+ echo "HOST *" > ~/.ssh/config
+ echo "StrictHostKeyChecking no" >> ~/.ssh/config
+ git -c submodule.Secrets.update=checkout submodule update --init
+ # Corvax-Secrets-End
+
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0
with:
@@ -66,6 +80,8 @@ jobs:
run: dotnet run --project Content.Packaging client --no-wipe-release
- name: Update Build Info
+ env:
+ FORK_ID: ${{ vars.FORK_ID }}
run: Tools/gen_build_info.py
- name: Shuffle files around
diff --git a/.github/workflows/update-wiki.yml b/.github/workflows/update-wiki.yml
new file mode 100644
index 00000000000..b353d5bfe64
--- /dev/null
+++ b/.github/workflows/update-wiki.yml
@@ -0,0 +1,91 @@
+name: Update Wiki
+
+on:
+ workflow_dispatch:
+ push:
+ branches: [ master, jsondump ]
+ paths:
+ - '.github/workflows/update-wiki.yml'
+ - 'Content.Shared/Chemistry/**.cs'
+ - 'Content.Server/Chemistry/**.cs'
+ - 'Content.Server/GuideGenerator/**.cs'
+ - 'Resources/Prototypes/Reagents/**.yml'
+ - 'Resources/Prototypes/Chemistry/**.yml'
+ - 'Resources/Prototypes/Recipes/Reactions/**.yml'
+ - 'RobustToolbox/'
+
+jobs:
+ update-wiki:
+ name: Build and Publish JSON blobs to wiki
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout Master
+ uses: actions/checkout@v3.6.0
+
+ - name: Setup Submodule
+ run: |
+ git submodule update --init --recursive
+
+ - name: Pull Engine Updates
+ uses: space-wizards/submodule-dependency@v0.1.5
+
+ - name: Update Engine Submodules
+ run: |
+ cd RobustToolbox/
+ git submodule update --init --recursive
+
+ - name: Setup .NET Core
+ uses: actions/setup-dotnet@v3.2.0
+ with:
+ dotnet-version: 7.0.x
+
+ - name: Install Dependencies
+ run: dotnet restore
+
+ - name: Build Project
+ run: dotnet build --configuration Release --no-restore /p:WarningsAsErrors=nullable /m
+
+ - name: Generate JSON blobs for prototypes
+ run: dotnet ./bin/Content.Server/Content.Server.dll --cvar autogen.destination_file=prototypes.json
+ continue-on-error: true
+
+ - name: Upload chem_prototypes.json to wiki
+ uses: jtmullen/mediawiki-edit-action@v0.1.1
+ with:
+ wiki_text_file: ./bin/Content.Server/data/chem_prototypes.json
+ edit_summary: Update chem_prototypes.json via GitHub Actions
+ page_name: "${{ secrets.WIKI_PAGE_ROOT }}/chem_prototypes.json"
+ api_url: ${{ secrets.WIKI_ROOT_URL }}/api.php
+ username: ${{ secrets.WIKI_BOT_USER }}
+ password: ${{ secrets.WIKI_BOT_PASS }}
+
+ - name: Upload react_prototypes.json to wiki
+ uses: jtmullen/mediawiki-edit-action@v0.1.1
+ with:
+ wiki_text_file: ./bin/Content.Server/data/react_prototypes.json
+ edit_summary: Update react_prototypes.json via GitHub Actions
+ page_name: "${{ secrets.WIKI_PAGE_ROOT }}/react_prototypes.json"
+ api_url: ${{ secrets.WIKI_ROOT_URL }}/api.php
+ username: ${{ secrets.WIKI_BOT_USER }}
+ password: ${{ secrets.WIKI_BOT_PASS }}
+
+ - name: Upload entity_prototypes.json to wiki
+ uses: jtmullen/mediawiki-edit-action@v0.1.1
+ with:
+ wiki_text_file: ./bin/Content.Server/data/entity_prototypes.json
+ edit_summary: Update entity_prototypes.json via GitHub Actions
+ page_name: "${{ secrets.WIKI_PAGE_ROOT }}/entity_prototypes.json"
+ api_url: ${{ secrets.WIKI_ROOT_URL }}/api.php
+ username: ${{ secrets.WIKI_BOT_USER }}
+ password: ${{ secrets.WIKI_BOT_PASS }}
+
+ - name: Upload mealrecipes_prototypes.json to wiki
+ uses: jtmullen/mediawiki-edit-action@v0.1.1
+ with:
+ wiki_text_file: ./bin/Content.Server/data/mealrecipes_prototypes.json
+ edit_summary: Update mealrecipes_prototypes.json via GitHub Actions
+ page_name: "${{ secrets.WIKI_PAGE_ROOT }}/mealrecipes_prototypes.json"
+ api_url: ${{ secrets.WIKI_ROOT_URL }}/api.php
+ username: ${{ secrets.WIKI_BOT_USER }}
+ password: ${{ secrets.WIKI_BOT_PASS }}
diff --git a/.github/workflows/validate-rgas.yml b/.github/workflows/validate-rgas.yml
index 2c4bb40fdf3..1132c01960d 100644
--- a/.github/workflows/validate-rgas.yml
+++ b/.github/workflows/validate-rgas.yml
@@ -9,12 +9,25 @@ on:
jobs:
yaml-schema-validation:
name: YAML RGA schema validator
- if: github.actor != 'PJBot' && github.event.pull_request.draft == false
+ if: github.actor != 'IanComradeBot' && github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.6.0
- name: Setup Submodule
run: git submodule update --init
+ # Corvax-Secrets-Start
+ - name: Setup secrets
+ env:
+ SSH_KEY: ${{ secrets.SECRETS_PRIVATE_KEY }}
+ if: ${{ env.SSH_KEY != '' }}
+ run: |
+ mkdir ~/.ssh
+ echo "${{ secrets.SECRETS_PRIVATE_KEY }}" > ~/.ssh/id_rsa
+ chmod 600 ~/.ssh/id_rsa
+ echo "HOST *" > ~/.ssh/config
+ echo "StrictHostKeyChecking no" >> ~/.ssh/config
+ git -c submodule.Secrets.update=checkout submodule update --init
+ # Corvax-Secrets-End
- name: Pull engine updates
uses: space-wizards/submodule-dependency@v0.1.5
- uses: PaulRitter/yaml-schema-validator@v1
diff --git a/.github/workflows/validate-rsis.yml b/.github/workflows/validate-rsis.yml
index b76df28e6af..be37678b5a0 100644
--- a/.github/workflows/validate-rsis.yml
+++ b/.github/workflows/validate-rsis.yml
@@ -16,6 +16,19 @@ jobs:
- uses: actions/checkout@v3.6.0
- name: Setup Submodule
run: git submodule update --init
+ # Corvax-Secrets-Start
+ - name: Setup secrets
+ env:
+ SSH_KEY: ${{ secrets.SECRETS_PRIVATE_KEY }}
+ if: ${{ env.SSH_KEY != '' }}
+ run: |
+ mkdir ~/.ssh
+ echo "${{ secrets.SECRETS_PRIVATE_KEY }}" > ~/.ssh/id_rsa
+ chmod 600 ~/.ssh/id_rsa
+ echo "HOST *" > ~/.ssh/config
+ echo "StrictHostKeyChecking no" >> ~/.ssh/config
+ git -c submodule.Secrets.update=checkout submodule update --init
+ # Corvax-Secrets-End
- name: Pull engine updates
uses: space-wizards/submodule-dependency@v0.1.5
- name: Install Python dependencies
diff --git a/.github/workflows/validate_mapfiles.yml b/.github/workflows/validate_mapfiles.yml
index fb11e1a4697..bb6eeaf411f 100644
--- a/.github/workflows/validate_mapfiles.yml
+++ b/.github/workflows/validate_mapfiles.yml
@@ -9,12 +9,25 @@ on:
jobs:
yaml-schema-validation:
name: YAML map schema validator
- if: github.actor != 'PJBot' && github.event.pull_request.draft == false
+ if: github.actor != 'IanComradeBot' && github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.6.0
- name: Setup Submodule
run: git submodule update --init
+ # Corvax-Secrets-Start
+ - name: Setup secrets
+ env:
+ SSH_KEY: ${{ secrets.SECRETS_PRIVATE_KEY }}
+ if: ${{ env.SSH_KEY != '' }}
+ run: |
+ mkdir ~/.ssh
+ echo "${{ secrets.SECRETS_PRIVATE_KEY }}" > ~/.ssh/id_rsa
+ chmod 600 ~/.ssh/id_rsa
+ echo "HOST *" > ~/.ssh/config
+ echo "StrictHostKeyChecking no" >> ~/.ssh/config
+ git -c submodule.Secrets.update=checkout submodule update --init
+ # Corvax-Secrets-End
- name: Pull engine updates
uses: space-wizards/submodule-dependency@v0.1.5
- uses: PaulRitter/yaml-schema-validator@v1
diff --git a/.github/workflows/yaml-linter.yml b/.github/workflows/yaml-linter.yml
index 254384acff0..8798c95e19a 100644
--- a/.github/workflows/yaml-linter.yml
+++ b/.github/workflows/yaml-linter.yml
@@ -10,7 +10,7 @@ on:
jobs:
build:
name: YAML Linter
- if: github.actor != 'PJBot' && github.event.pull_request.draft == false
+ if: github.actor != 'IanComradeBot' && github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.6.0
diff --git a/.gitignore b/.gitignore
index 2dd5912047c..533060b4996 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,12 @@
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
+# Secret
+Resources/Prototypes/CorvaxSecrets
+Resources/Prototypes/CorvaxSecretsServer
+Resources/Textures/CorvaxSecrets
+Resources/Locale/ru-RU/corvax-secrets
+
# Build results
[Dd]ebug/
[Dd]ebugPublic/
diff --git a/.gitmodules b/.gitmodules
index 08e5dd6d0af..5ba1735ee8c 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,4 +1,9 @@
[submodule "RobustToolbox"]
path = RobustToolbox
url = https://github.com/space-wizards/RobustToolbox.git
- branch = master
\ No newline at end of file
+ branch = master
+[submodule "Secrets"]
+ path = Secrets
+ url = git@github.com:corvax-project/secrets.git
+ branch = master
+ update = none
diff --git a/CHANGES.md b/CHANGES.md
new file mode 100644
index 00000000000..3b4340cd707
--- /dev/null
+++ b/CHANGES.md
@@ -0,0 +1,25 @@
+Main changes of this downstream:
+- Russian translation
+ * Text in ftl files
+ * Accents
+ * Some sprites (direction signs)
+- Merging downstream changelog file with upstream changelog
+- Gender division files of last names for name generator
+- More emotes for chat sanitization or cyrillic versions
+- System of Discord round status notification
+- Decreased interaction range to 1.4
+ * Gravity generator fixture shape on top decreased
+- Custom sprites
+ * Floor tiles
+ * Clothing
+ * Some objects
+- Custom clothing
+ * Disco Elysium suits
+ * [BIG SHOT] glasses
+ * Special Operations Officer [Author]
+- Custom tools for locale update and merging (/Tools/ss14_ru/)
+- Station goals
+- Sponsor rewards
+- Increased role timers
+- Custom roles
+ * Internal affairs agent
diff --git a/Content.Client/Administration/UI/Tabs/AdminTab/AdminShuttleWindow.xaml b/Content.Client/Administration/UI/Tabs/AdminTab/AdminShuttleWindow.xaml
index b26f0cfe445..974e9418461 100644
--- a/Content.Client/Administration/UI/Tabs/AdminTab/AdminShuttleWindow.xaml
+++ b/Content.Client/Administration/UI/Tabs/AdminTab/AdminShuttleWindow.xaml
@@ -7,8 +7,9 @@
+
+
-
diff --git a/Content.Client/Chat/TypingIndicator/TypingIndicatorSystem.cs b/Content.Client/Chat/TypingIndicator/TypingIndicatorSystem.cs
index 0eea36aa573..db1f3ae8d7f 100644
--- a/Content.Client/Chat/TypingIndicator/TypingIndicatorSystem.cs
+++ b/Content.Client/Chat/TypingIndicator/TypingIndicatorSystem.cs
@@ -16,6 +16,7 @@ public sealed class TypingIndicatorSystem : SharedTypingIndicatorSystem
private readonly TimeSpan _typingTimeout = TimeSpan.FromSeconds(2);
private TimeSpan _lastTextChange;
private bool _isClientTyping;
+ private bool _isClientChatFocused; // Corvax-TypingIndicator
public override void Initialize()
{
@@ -30,7 +31,10 @@ public void ClientChangedChatText()
return;
// client typed something - show typing indicator
- ClientUpdateTyping(true);
+ // Corvax-TypingIndicator-Start
+ _isClientTyping = true;
+ ClientUpdateTyping();
+ // Corvax-TypingIndicator-End
_lastTextChange = _time.CurTime;
}
@@ -41,9 +45,26 @@ public void ClientSubmittedChatText()
return;
// client submitted text - hide typing indicator
- ClientUpdateTyping(false);
+ // Corvax-TypingIndicator-Start
+ _isClientTyping = false;
+ _isClientChatFocused = false;
+ ClientUpdateTyping();
+ // Corvax-TypingIndicator-End
}
+ // Corvax-TypingIndicator-Start
+ public void ClientChangedChatFocus(bool isFocused)
+ {
+ // don't update it if player don't want to show typing
+ if (!_cfg.GetCVar(CCVars.ChatShowTypingIndicator))
+ return;
+
+ // client submitted text - hide typing indicator
+ _isClientChatFocused = isFocused;
+ ClientUpdateTyping();
+ }
+ // Corvax-TypingIndicator-End
+
public override void Update(float frameTime)
{
base.Update(frameTime);
@@ -55,23 +76,34 @@ public override void Update(float frameTime)
if (dif > _typingTimeout)
{
// client didn't typed anything for a long time - hide indicator
- ClientUpdateTyping(false);
+ // Corvax-TypingIndicator-Start
+ _isClientTyping = false;
+ ClientUpdateTyping();
+ // Corvax-TypingIndicator-End
}
}
}
- private void ClientUpdateTyping(bool isClientTyping)
+ private void ClientUpdateTyping() // Corvax-TypingIndicator
{
- if (_isClientTyping == isClientTyping)
- return;
- _isClientTyping = isClientTyping;
+ // Corvax-TypingIndicator-Start
+ // if (_isClientTyping == isClientTyping)
+ // return;
+ // _isClientTyping = isClientTyping;
+ // Corvax-TypingIndicator-End
// check if player controls any pawn
if (_playerManager.LocalPlayer?.ControlledEntity == null)
return;
+ // Corvax-TypingIndicator-Start
+ var state = TypingIndicatorState.None;
+ if (_isClientChatFocused)
+ state = _isClientTyping ? TypingIndicatorState.Typing : TypingIndicatorState.Idle;
+ // Corvax-TypingIndicator-End
+
// send a networked event to server
- RaiseNetworkEvent(new TypingChangedEvent(isClientTyping));
+ RaiseNetworkEvent(new TypingChangedEvent(state)); // Corvax-TypingIndicator
}
private void OnShowTypingChanged(bool showTyping)
@@ -79,7 +111,10 @@ private void OnShowTypingChanged(bool showTyping)
// hide typing indicator immediately if player don't want to show it anymore
if (!showTyping)
{
- ClientUpdateTyping(false);
+ // Corvax-TypingIndicator-Start
+ _isClientTyping = false;
+ ClientUpdateTyping();
+ // Corvax-TypingIndicator-End
}
}
}
diff --git a/Content.Client/Chat/TypingIndicator/TypingIndicatorVisualizerSystem.cs b/Content.Client/Chat/TypingIndicator/TypingIndicatorVisualizerSystem.cs
index d04c9d661dd..182ddd74b64 100644
--- a/Content.Client/Chat/TypingIndicator/TypingIndicatorVisualizerSystem.cs
+++ b/Content.Client/Chat/TypingIndicator/TypingIndicatorVisualizerSystem.cs
@@ -20,7 +20,7 @@ protected override void OnAppearanceChange(EntityUid uid, TypingIndicatorCompone
return;
}
- AppearanceSystem.TryGetData(uid, TypingIndicatorVisuals.IsTyping, out var isTyping, args.Component);
+ //AppearanceSystem.TryGetData(uid, TypingIndicatorVisuals.IsTyping, out var isTyping, args.Component); // Corvax-TypingIndicator
var layerExists = args.Sprite.LayerMapTryGet(TypingIndicatorLayers.Base, out var layer);
if (!layerExists)
layer = args.Sprite.LayerMapReserveBlank(TypingIndicatorLayers.Base);
@@ -29,6 +29,19 @@ protected override void OnAppearanceChange(EntityUid uid, TypingIndicatorCompone
args.Sprite.LayerSetState(layer, proto.TypingState);
args.Sprite.LayerSetShader(layer, proto.Shader);
args.Sprite.LayerSetOffset(layer, proto.Offset);
- args.Sprite.LayerSetVisible(layer, isTyping);
+ // args.Sprite.LayerSetVisible(layer, isTyping); // Corvax-TypingIndicator
+ // Corvax-TypingIndicator-Start
+ AppearanceSystem.TryGetData(uid, TypingIndicatorVisuals.State, out var state);
+ args.Sprite.LayerSetVisible(layer, state != TypingIndicatorState.None);
+ switch (state)
+ {
+ case TypingIndicatorState.Idle:
+ args.Sprite.LayerSetState(layer, proto.IdleState);
+ break;
+ case TypingIndicatorState.Typing:
+ args.Sprite.LayerSetState(layer, proto.TypingState);
+ break;
+ }
+ // Corvax-TypingIndicator-End
}
}
diff --git a/Content.Client/Content.Client.csproj b/Content.Client/Content.Client.csproj
index 33ee0e0a34d..2e390efd75e 100644
--- a/Content.Client/Content.Client.csproj
+++ b/Content.Client/Content.Client.csproj
@@ -22,6 +22,8 @@
+
+
diff --git a/Content.Client/Corvax/TTS/HumanoidProfileEditor.TTS.cs b/Content.Client/Corvax/TTS/HumanoidProfileEditor.TTS.cs
new file mode 100644
index 00000000000..908860156c3
--- /dev/null
+++ b/Content.Client/Corvax/TTS/HumanoidProfileEditor.TTS.cs
@@ -0,0 +1,89 @@
+using System.Linq;
+using Content.Client.Corvax.TTS;
+using Content.Shared.Corvax.TTS;
+using Content.Shared.Preferences;
+using Robust.Shared.Random;
+using Content.Corvax.Interfaces.Client;
+
+namespace Content.Client.Preferences.UI;
+
+public sealed partial class HumanoidProfileEditor
+{
+ private IRobustRandom _random = default!;
+ private TTSSystem _ttsSys = default!;
+ private IClientSponsorsManager? _sponsorsMgr;
+ private List _voiceList = default!;
+ private readonly List _sampleText = new()
+ {
+ "Съешь же ещё этих мягких французских булок, да выпей чаю.",
+ "Клоун, прекрати разбрасывать банановые кожурки офицерам под ноги!",
+ "Капитан, вы уверены что хотите назначить клоуна на должность главы персонала?",
+ "Эс Бэ! Тут человек в сером костюме, с тулбоксом и в маске! Помогите!!"
+ };
+
+ private void InitializeVoice()
+ {
+ if (!IoCManager.Instance!.TryResolveType(out _sponsorsMgr))
+ return;
+
+ _random = IoCManager.Resolve();
+ _ttsSys = _entMan.System();
+ _voiceList = _prototypeManager
+ .EnumeratePrototypes()
+ .Where(o => o.RoundStart)
+ .OrderBy(o => Loc.GetString(o.Name))
+ .ToList();
+
+ _voiceButton.OnItemSelected += args =>
+ {
+ _voiceButton.SelectId(args.Id);
+ SetVoice(_voiceList[args.Id].ID);
+ };
+
+ _voicePlayButton.OnPressed += _ => { PlayTTS(); };
+ }
+
+ private void UpdateTTSVoicesControls()
+ {
+ if (Profile is null ||
+ _sponsorsMgr is null)
+ return;
+
+ _voiceButton.Clear();
+
+ var firstVoiceChoiceId = 1;
+ for (var i = 0; i < _voiceList.Count; i++)
+ {
+ var voice = _voiceList[i];
+ if (!HumanoidCharacterProfile.CanHaveVoice(voice, Profile.Sex))
+ continue;
+
+ var name = Loc.GetString(voice.Name);
+ _voiceButton.AddItem(name, i);
+
+ if (firstVoiceChoiceId == 1)
+ firstVoiceChoiceId = i;
+
+ if (voice.SponsorOnly && _sponsorsMgr != null &&
+ !_sponsorsMgr.Prototypes.Contains(voice.ID))
+ {
+ _voiceButton.SetItemDisabled(_voiceButton.GetIdx(i), true);
+ }
+ }
+
+ var voiceChoiceId = _voiceList.FindIndex(x => x.ID == Profile.Voice);
+ if (!_voiceButton.TrySelectId(voiceChoiceId) &&
+ _voiceButton.TrySelectId(firstVoiceChoiceId))
+ {
+ SetVoice(_voiceList[firstVoiceChoiceId].ID);
+ }
+ }
+
+ private void PlayTTS()
+ {
+ if (_previewDummy is null || Profile is null)
+ return;
+
+ _ttsSys.RequestGlobalTTS(_random.Pick(_sampleText), Profile.Voice);
+ }
+}
diff --git a/Content.Client/Corvax/TTS/TTSSystem.cs b/Content.Client/Corvax/TTS/TTSSystem.cs
new file mode 100644
index 00000000000..dc5bce8524c
--- /dev/null
+++ b/Content.Client/Corvax/TTS/TTSSystem.cs
@@ -0,0 +1,88 @@
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using Content.Shared.Corvax.CCCVars;
+using Content.Shared.Corvax.TTS;
+using Content.Shared.Physics;
+using Robust.Client.GameObjects;
+using Robust.Client.Graphics;
+using Robust.Client.ResourceManagement;
+using Robust.Shared.Audio;
+using Robust.Shared.Configuration;
+using Robust.Shared.ContentPack;
+using Robust.Shared.Map;
+using Robust.Shared.Physics;
+using Robust.Shared.Physics.Systems;
+using Robust.Shared.Player;
+using Robust.Shared.Utility;
+
+namespace Content.Client.Corvax.TTS;
+
+///
+/// Plays TTS audio in world
+///
+// ReSharper disable once InconsistentNaming
+public sealed class TTSSystem : EntitySystem
+{
+ [Dependency] private readonly IConfigurationManager _cfg = default!;
+ [Dependency] private readonly IResourceCache _resourceCache = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+
+ private ISawmill _sawmill = default!;
+ private readonly MemoryContentRoot _contentRoot = new();
+ private static readonly ResPath Prefix = ResPath.Root / "TTS";
+
+ private float _volume = 0.0f;
+ private int _fileIdx = 0;
+
+ public override void Initialize()
+ {
+ _sawmill = Logger.GetSawmill("tts");
+ _resourceCache.AddRoot(Prefix, _contentRoot);
+ _cfg.OnValueChanged(CCCVars.TTSVolume, OnTtsVolumeChanged, true);
+ SubscribeNetworkEvent(OnPlayTTS);
+ }
+
+ public override void Shutdown()
+ {
+ base.Shutdown();
+ _cfg.UnsubValueChanged(CCCVars.TTSVolume, OnTtsVolumeChanged);
+ _contentRoot.Dispose();
+ }
+
+ public void RequestGlobalTTS(string text, string voiceId)
+ {
+ RaiseNetworkEvent(new RequestGlobalTTSEvent(text, voiceId));
+ }
+
+ private void OnTtsVolumeChanged(float volume)
+ {
+ _volume = volume;
+ }
+
+ private void OnPlayTTS(PlayTTSEvent ev)
+ {
+ _sawmill.Debug($"Play TTS audio {ev.Data.Length} bytes from {ev.SourceUid} entity");
+
+ var volume = _volume;
+ if (ev.IsWhisper)
+ volume -= 4;
+
+ var filePath = new ResPath($"{_fileIdx++}.ogg");
+ _contentRoot.AddOrUpdateFile(filePath, ev.Data);
+
+ var audioParams = AudioParams.Default.WithVolume(volume);
+ var soundPath = new SoundPathSpecifier(Prefix / filePath, audioParams);
+ if (ev.SourceUid != null)
+ {
+ var sourceUid = GetEntity(ev.SourceUid.Value);
+ _audio.PlayEntity(soundPath, new EntityUid(), sourceUid); // recipient arg ignored on client
+ }
+ else
+ {
+ _audio.PlayGlobal(soundPath, Filter.Local(), false);
+ }
+
+ _contentRoot.RemoveFile(filePath);
+ }
+}
diff --git a/Content.Client/Credits/CreditsWindow.xaml.cs b/Content.Client/Credits/CreditsWindow.xaml.cs
index 60ac5798454..1cdc1ee4c9b 100644
--- a/Content.Client/Credits/CreditsWindow.xaml.cs
+++ b/Content.Client/Credits/CreditsWindow.xaml.cs
@@ -157,6 +157,7 @@ void AddSection(string title, string path, bool markup = false)
AddSection(Loc.GetString("credits-window-contributors-section-title"), "GitHub.txt");
AddSection(Loc.GetString("credits-window-codebases-section-title"), "SpaceStation13.txt");
+ AddSection(Loc.GetString("credits-window-tts-title"), "TTS.txt"); // Corvax-TTS
AddSection(Loc.GetString("credits-window-original-remake-team-section-title"), "OriginalRemake.txt");
AddSection(Loc.GetString("credits-window-special-thanks-section-title"), "SpecialThanks.txt", true);
diff --git a/Content.Client/CrewManifest/CrewManifestUi.xaml b/Content.Client/CrewManifest/CrewManifestUi.xaml
index db3a7ac390d..4dbe2ed99d2 100644
--- a/Content.Client/CrewManifest/CrewManifestUi.xaml
+++ b/Content.Client/CrewManifest/CrewManifestUi.xaml
@@ -2,7 +2,7 @@
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:ui="clr-namespace:Content.Client.CrewManifest.UI"
Title="{Loc 'crew-manifest-window-title'}"
- SetSize="450 750">
+ SetSize="500 800">
diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs
index 04f0acac971..b67d624dc7b 100644
--- a/Content.Client/Entry/EntryPoint.cs
+++ b/Content.Client/Entry/EntryPoint.cs
@@ -1,6 +1,8 @@
using Content.Client.Administration.Managers;
using Content.Client.Changelog;
using Content.Client.Chat.Managers;
+using Content.Client.Corvax.TTS;
+using Content.Client.Options;
using Content.Client.Eui;
using Content.Client.Flash;
using Content.Client.Fullscreen;
@@ -121,6 +123,8 @@ public override void Init()
_prototypeManager.RegisterIgnore("wireLayout");
_prototypeManager.RegisterIgnore("alertLevels");
_prototypeManager.RegisterIgnore("nukeopsRole");
+ _prototypeManager.RegisterIgnore("stationGoal"); // Corvax-StationGoal
+ _prototypeManager.RegisterIgnore("loadout"); // Corvax-Loadout
_componentFactory.GenerateNetIds();
_adminManager.Initialize();
diff --git a/Content.Client/Humanoid/MarkingPicker.xaml.cs b/Content.Client/Humanoid/MarkingPicker.xaml.cs
index 43333439f08..64fcde98657 100644
--- a/Content.Client/Humanoid/MarkingPicker.xaml.cs
+++ b/Content.Client/Humanoid/MarkingPicker.xaml.cs
@@ -1,4 +1,5 @@
using System.Linq;
+using Content.Corvax.Interfaces.Client;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
using Content.Shared.Humanoid.Prototypes;
@@ -18,6 +19,7 @@ public sealed partial class MarkingPicker : Control
{
[Dependency] private readonly MarkingManager _markingManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ private IClientSponsorsManager? _sponsorsManager; // Corvax-Sponsors
public Action? OnMarkingAdded;
public Action? OnMarkingRemoved;
@@ -123,6 +125,7 @@ public MarkingPicker()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
+ IoCManager.Instance!.TryResolveType(out _sponsorsManager); // Corvax-Sponsors
SetupCategoryButtons();
CMarkingCategoryButton.OnItemSelected += OnCategoryChange;
@@ -202,6 +205,10 @@ public void Populate(string filter)
var item = CMarkingsUnused.AddItem($"{GetMarkingName(marking)}", marking.Sprites[0].Frame0());
item.Metadata = marking;
+ // Corvax-Sponsors-Start
+ if (marking.SponsorOnly && _sponsorsManager != null)
+ item.Disabled = !_sponsorsManager.Prototypes.Contains(marking.ID);
+ // Corvax-Sponsors-End
}
CMarkingPoints.Visible = _currentMarkings.PointsLeft(_selectedMarkingCategory) != -1;
diff --git a/Content.Client/Humanoid/SingleMarkingPicker.xaml.cs b/Content.Client/Humanoid/SingleMarkingPicker.xaml.cs
index be3130a58bd..9e072ea4e02 100644
--- a/Content.Client/Humanoid/SingleMarkingPicker.xaml.cs
+++ b/Content.Client/Humanoid/SingleMarkingPicker.xaml.cs
@@ -1,4 +1,5 @@
using System.Linq;
+using Content.Corvax.Interfaces.Client;
using Content.Shared.Humanoid.Markings;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
@@ -11,6 +12,7 @@ namespace Content.Client.Humanoid;
public sealed partial class SingleMarkingPicker : BoxContainer
{
[Dependency] private readonly MarkingManager _markingManager = default!;
+ private IClientSponsorsManager? _sponsorsManager; // Corvax-Sponsors
///
/// What happens if a marking is selected.
@@ -122,6 +124,7 @@ public SingleMarkingPicker()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
+ IoCManager.Instance!.TryResolveType(out _sponsorsManager); // Corvax-Sponsors
MarkingList.OnItemSelected += SelectMarking;
AddButton.OnPressed += _ =>
@@ -190,6 +193,10 @@ public void PopulateList(string filter)
{
var item = MarkingList.AddItem(Loc.GetString($"marking-{id}"), marking.Sprites[0].Frame0());
item.Metadata = marking.ID;
+ // Corvax-Sponsors-Start
+ if (marking.SponsorOnly && _sponsorsManager != null)
+ item.Disabled = !_sponsorsManager.Prototypes.Contains(marking.ID);
+ // Corvax-Sponsors-End
if (_markings[Slot].MarkingId == id)
{
diff --git a/Content.Client/IoC/ClientContentIoC.cs b/Content.Client/IoC/ClientContentIoC.cs
index 70fe1916584..18b9852eee3 100644
--- a/Content.Client/IoC/ClientContentIoC.cs
+++ b/Content.Client/IoC/ClientContentIoC.cs
@@ -1,7 +1,8 @@
-using Content.Client.Administration.Managers;
+using Content.Client.Administration.Managers;
using Content.Client.Changelog;
using Content.Client.Chat.Managers;
using Content.Client.Clickable;
+using Content.Client.Corvax.TTS;
using Content.Client.Options;
using Content.Client.Eui;
using Content.Client.GhostKick;
diff --git a/Content.Client/LateJoin/LateJoinGui.cs b/Content.Client/LateJoin/LateJoinGui.cs
index 9acfb031261..3b5d0e8ef50 100644
--- a/Content.Client/LateJoin/LateJoinGui.cs
+++ b/Content.Client/LateJoin/LateJoinGui.cs
@@ -41,7 +41,7 @@ public sealed class LateJoinGui : DefaultWindow
public LateJoinGui()
{
- MinSize = SetSize = new Vector2(360, 560);
+ MinSize = SetSize = new Vector2(450, 560);
IoCManager.InjectDependencies(this);
_sprites = _entitySystem.GetEntitySystem();
_crewManifest = _entitySystem.GetEntitySystem();
diff --git a/Content.Client/MassMedia/Ui/NewsWriteBoundUserInterface.cs b/Content.Client/MassMedia/Ui/NewsWriteBoundUserInterface.cs
index 975dfa3f818..a3e2d210b72 100644
--- a/Content.Client/MassMedia/Ui/NewsWriteBoundUserInterface.cs
+++ b/Content.Client/MassMedia/Ui/NewsWriteBoundUserInterface.cs
@@ -4,6 +4,8 @@
using Content.Shared.MassMedia.Systems;
using Content.Shared.MassMedia.Components;
using Content.Client.GameTicking.Managers;
+using Content.Shared.CCVar;
+using Robust.Shared.Configuration;
using Robust.Shared.Utility;
namespace Content.Client.MassMedia.Ui
@@ -15,7 +17,7 @@ public sealed class NewsWriteBoundUserInterface : BoundUserInterface
private NewsWriteMenu? _menu;
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
- [Dependency] private readonly IGameTiming _gameTiming = default!;
+ [Dependency] private readonly IConfigurationManager _cfg = default!;
private ClientGameTicker? _gameTicker;
[ViewVariables]
@@ -71,10 +73,16 @@ private void OnShareButtonPressed()
return;
var stringName = _menu.NameInput.Text;
- var name = (stringName.Length <= 25 ? stringName.Trim() : $"{stringName.Trim().Substring(0, 25)}...");
+
+ var maxNameLength = _cfg.GetCVar(CCVars.NewsNameLimit);
+ var maxContentLength = _cfg.GetCVar(CCVars.NewsContentLimit);
+
+ var name = (stringName.Length <= maxNameLength ? stringName.Trim() : $"{stringName.Trim().Substring(0, maxNameLength)}...");
+ var content = (stringContent.Length <= maxContentLength ? stringContent.Trim() : $"{stringContent.Trim().Substring(0, maxContentLength)}...");
+
_menu.ContentInput.TextRope = new Rope.Leaf(string.Empty);
_menu.NameInput.Text = string.Empty;
- SendMessage(new NewsWriteShareMessage(name, stringContent));
+ SendMessage(new NewsWriteShareMessage(name, content));
}
private void OnDeleteButtonPressed(int articleNum)
diff --git a/Content.Client/Nuke/NukeMenu.xaml b/Content.Client/Nuke/NukeMenu.xaml
index 227465341d3..bb7067565fe 100644
--- a/Content.Client/Nuke/NukeMenu.xaml
+++ b/Content.Client/Nuke/NukeMenu.xaml
@@ -1,8 +1,8 @@
+ MinSize="280 256"
+ SetSize="280 256">
diff --git a/Content.Client/Options/UI/Tabs/AudioTab.xaml b/Content.Client/Options/UI/Tabs/AudioTab.xaml
index 528b0ac136c..7d271d02aa4 100644
--- a/Content.Client/Options/UI/Tabs/AudioTab.xaml
+++ b/Content.Client/Options/UI/Tabs/AudioTab.xaml
@@ -74,6 +74,19 @@
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Options/UI/Tabs/AudioTab.xaml.cs b/Content.Client/Options/UI/Tabs/AudioTab.xaml.cs
index 6a9928b8bdd..1e5722c9009 100644
--- a/Content.Client/Options/UI/Tabs/AudioTab.xaml.cs
+++ b/Content.Client/Options/UI/Tabs/AudioTab.xaml.cs
@@ -1,4 +1,5 @@
using Content.Shared.CCVar;
+using Content.Shared.Corvax.CCCVars;
using Robust.Client.Audio;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
@@ -37,6 +38,7 @@ public AudioTab()
AmbienceVolumeSlider.OnValueChanged += OnAmbienceVolumeSliderChanged;
AmbienceSoundsSlider.OnValueChanged += OnAmbienceSoundsSliderChanged;
LobbyVolumeSlider.OnValueChanged += OnLobbyVolumeSliderChanged;
+ TtsVolumeSlider.OnValueChanged += OnTtsVolumeSliderChanged; // Corvax-TTS
LobbyMusicCheckBox.OnToggled += OnLobbyMusicCheckToggled;
RestartSoundsCheckBox.OnToggled += OnRestartSoundsCheckToggled;
EventMusicCheckBox.OnToggled += OnEventMusicCheckToggled;
@@ -57,6 +59,7 @@ protected override void Dispose(bool disposing)
AmbientMusicVolumeSlider.OnValueChanged -= OnAmbientMusicVolumeSliderChanged;
AmbienceVolumeSlider.OnValueChanged -= OnAmbienceVolumeSliderChanged;
LobbyVolumeSlider.OnValueChanged -= OnLobbyVolumeSliderChanged;
+ TtsVolumeSlider.OnValueChanged -= OnTtsVolumeSliderChanged; // Corvax-TTS
base.Dispose(disposing);
}
@@ -91,6 +94,13 @@ private void OnMidiVolumeSliderChanged(Range range)
UpdateChanges();
}
+ // Corvax-TTS-Start
+ private void OnTtsVolumeSliderChanged(Range obj)
+ {
+ UpdateChanges();
+ }
+ // Corvax-TTS-End
+
private void OnLobbyMusicCheckToggled(BaseButton.ButtonEventArgs args)
{
UpdateChanges();
@@ -119,6 +129,7 @@ private void OnApplyButtonPressed(BaseButton.ButtonEventArgs args)
_cfg.SetCVar(CCVars.AmbientMusicVolume, LV100ToDB(AmbientMusicVolumeSlider.Value, CCVars.AmbientMusicMultiplier));
_cfg.SetCVar(CCVars.LobbyMusicVolume, LV100ToDB(LobbyVolumeSlider.Value));
+ _cfg.SetCVar(CCCVars.TTSVolume, LV100ToDB(TtsVolumeSlider.Value)); // Corvax-TTS
_cfg.SetCVar(CCVars.MaxAmbientSources, (int)AmbienceSoundsSlider.Value);
_cfg.SetCVar(CCVars.LobbyMusicEnabled, LobbyMusicCheckBox.Pressed);
_cfg.SetCVar(CCVars.RestartSoundsEnabled, RestartSoundsCheckBox.Pressed);
@@ -141,6 +152,7 @@ private void Reset()
AmbientMusicVolumeSlider.Value =
DBToLV100(_cfg.GetCVar(CCVars.AmbientMusicVolume), CCVars.AmbientMusicMultiplier);
LobbyVolumeSlider.Value = DBToLV100(_cfg.GetCVar(CCVars.LobbyMusicVolume));
+ TtsVolumeSlider.Value = DBToLV100(_cfg.GetCVar(CCCVars.TTSVolume)); // Corvax-TTS
AmbienceSoundsSlider.Value = _cfg.GetCVar(CCVars.MaxAmbientSources);
LobbyMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.LobbyMusicEnabled);
RestartSoundsCheckBox.Pressed = _cfg.GetCVar(CCVars.RestartSoundsEnabled);
@@ -176,6 +188,8 @@ private void UpdateChanges()
Math.Abs(AmbientMusicVolumeSlider.Value - DBToLV100(_cfg.GetCVar(CCVars.AmbientMusicVolume), CCVars.AmbientMusicMultiplier)) < 0.01f;
var isLobbyVolumeSame =
Math.Abs(LobbyVolumeSlider.Value - DBToLV100(_cfg.GetCVar(CCVars.LobbyMusicVolume))) < 0.01f;
+ var isTtsVolumeSame =
+ Math.Abs(TtsVolumeSlider.Value - DBToLV100(_cfg.GetCVar(CCCVars.TTSVolume))) < 0.01f; // Corvax-TTS
var isAmbientSoundsSame = (int)AmbienceSoundsSlider.Value == _cfg.GetCVar(CCVars.MaxAmbientSources);
var isLobbySame = LobbyMusicCheckBox.Pressed == _cfg.GetCVar(CCVars.LobbyMusicEnabled);
var isRestartSoundsSame = RestartSoundsCheckBox.Pressed == _cfg.GetCVar(CCVars.RestartSoundsEnabled);
@@ -183,6 +197,7 @@ private void UpdateChanges()
var isAdminSoundsSame = AdminSoundsCheckBox.Pressed == _cfg.GetCVar(CCVars.AdminSoundsEnabled);
var isEverythingSame = isMasterVolumeSame && isMidiVolumeSame && isAmbientVolumeSame && isAmbientMusicVolumeSame && isAmbientSoundsSame && isLobbySame && isRestartSoundsSame && isEventSame
&& isAdminSoundsSame && isLobbyVolumeSame;
+ isEverythingSame = isEverythingSame && isTtsVolumeSame; // Corvax-TTS
ApplyButton.Disabled = isEverythingSame;
ResetButton.Disabled = isEverythingSame;
MasterVolumeLabel.Text =
@@ -195,6 +210,8 @@ private void UpdateChanges()
Loc.GetString("ui-options-volume-percent", ("volume", AmbienceVolumeSlider.Value / 100));
LobbyVolumeLabel.Text =
Loc.GetString("ui-options-volume-percent", ("volume", LobbyVolumeSlider.Value / 100));
+ TtsVolumeLabel.Text =
+ Loc.GetString("ui-options-volume-percent", ("volume", TtsVolumeSlider.Value / 100)); // Corvax-TTS
AmbienceSoundsLabel.Text = ((int)AmbienceSoundsSlider.Value).ToString();
}
}
diff --git a/Content.Client/Power/PowerMonitoringWindow.xaml.cs b/Content.Client/Power/PowerMonitoringWindow.xaml.cs
index 5cc48395a33..e951b04f2f1 100644
--- a/Content.Client/Power/PowerMonitoringWindow.xaml.cs
+++ b/Content.Client/Power/PowerMonitoringWindow.xaml.cs
@@ -24,7 +24,8 @@ public sealed partial class PowerMonitoringWindow : DefaultWindow, IComputerWind
public PowerMonitoringWindow()
{
RobustXamlLoader.Load(this);
- SetSize = MinSize = new Vector2(300, 450);
+ SetSize = new Vector2(500, 450); // Corvax-Resize
+ MinSize = new Vector2(300, 450); // Corvax-Resize
IoCManager.InjectDependencies(this);
_spriteSystem = IoCManager.Resolve().System();
MasterTabContainer.SetTabTitle(0, Loc.GetString("power-monitoring-window-tab-sources"));
diff --git a/Content.Client/Preferences/ClientPreferencesManager.cs b/Content.Client/Preferences/ClientPreferencesManager.cs
index 34b2c33140d..557c9aef709 100644
--- a/Content.Client/Preferences/ClientPreferencesManager.cs
+++ b/Content.Client/Preferences/ClientPreferencesManager.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Content.Corvax.Interfaces.Client;
using Content.Shared.Preferences;
using Robust.Client;
using Robust.Shared.IoC;
@@ -18,6 +19,7 @@ public sealed class ClientPreferencesManager : IClientPreferencesManager
{
[Dependency] private readonly IClientNetManager _netManager = default!;
[Dependency] private readonly IBaseClient _baseClient = default!;
+ private IClientSponsorsManager? _sponsorsManager; // Corvax-Sponsors
public event Action? OnServerDataLoaded;
@@ -26,6 +28,7 @@ public sealed class ClientPreferencesManager : IClientPreferencesManager
public void Initialize()
{
+ IoCManager.Instance!.TryResolveType(out _sponsorsManager); // Corvax-Sponsors
_netManager.RegisterNetMessage(HandlePreferencesAndSettings);
_netManager.RegisterNetMessage();
_netManager.RegisterNetMessage();
@@ -60,7 +63,10 @@ public void SelectCharacter(int slot)
public void UpdateCharacter(ICharacterProfile profile, int slot)
{
- profile.EnsureValid();
+ // Corvax-Sponsors-Start
+ var sponsorPrototypes = _sponsorsManager?.Prototypes.ToArray() ?? new string[]{};
+ profile.EnsureValid(sponsorPrototypes);
+ // Corvax-Sponsors-End
var characters = new Dictionary(Preferences.Characters) {[slot] = profile};
Preferences = new PlayerPreferences(characters, Preferences.SelectedCharacterIndex, Preferences.AdminOOCColor);
var msg = new MsgUpdateCharacter
diff --git a/Content.Client/Preferences/UI/CharacterSetupGui.xaml b/Content.Client/Preferences/UI/CharacterSetupGui.xaml
index 9a76029ce0b..40aa24cd15b 100644
--- a/Content.Client/Preferences/UI/CharacterSetupGui.xaml
+++ b/Content.Client/Preferences/UI/CharacterSetupGui.xaml
@@ -14,6 +14,12 @@
Text="{Loc 'character-setup-gui-character-setup-stats-button'}"
StyleClasses="ButtonBig"
HorizontalAlignment="Right" />
+
+
+
diff --git a/Content.Client/Preferences/UI/CharacterSetupGui.xaml.cs b/Content.Client/Preferences/UI/CharacterSetupGui.xaml.cs
index f1052086de6..8bb123d963f 100644
--- a/Content.Client/Preferences/UI/CharacterSetupGui.xaml.cs
+++ b/Content.Client/Preferences/UI/CharacterSetupGui.xaml.cs
@@ -6,6 +6,7 @@
using Content.Client.Lobby.UI;
using Content.Client.Resources;
using Content.Client.Stylesheets;
+using Content.Corvax.Interfaces.Client;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Preferences;
@@ -84,6 +85,13 @@ public CharacterSetupGui(
StatsButton.OnPressed += _ => new PlaytimeStatsWindow().OpenCentered();
preferencesManager.OnServerDataLoaded += UpdateUI;
+ // Corvax-Sponsors-Start
+ if (IoCManager.Instance!.TryResolveType(out var creator))
+ {
+ SponsorButton.Visible = true;
+ SponsorButton.OnPressed += _ => creator.OpenWindow();
+ }
+ // Corvax-Sponsors-End
}
protected override void Dispose(bool disposing)
@@ -118,6 +126,7 @@ private void UpdateUI()
Loc.GetString("character-setup-gui-create-new-character-button-tooltip",
("maxCharacters", _preferencesManager.Settings!.MaxCharacterSlots));
+ var isDisplayedMaxSlots = false; // Corvax-Sponsors: Additional slots possible
foreach (var (slot, character) in _preferencesManager.Preferences!.Characters)
{
if (character is null)
@@ -125,6 +134,10 @@ private void UpdateUI()
continue;
}
+ // Corvax-Sponsors-Start
+ isDisplayedMaxSlots = numberOfFullSlots >= _preferencesManager.Settings.MaxCharacterSlots;
+ if (isDisplayedMaxSlots) break;
+ // Corvax-Sponsors-End
numberOfFullSlots++;
var characterPickerButton = new CharacterPickerButton(_entityManager,
_preferencesManager,
@@ -145,8 +158,7 @@ private void UpdateUI()
};
}
- _createNewCharacterButton.Disabled =
- numberOfFullSlots >= _preferencesManager.Settings.MaxCharacterSlots;
+ _createNewCharacterButton.Disabled = isDisplayedMaxSlots; // Corvax-Sponsors
Characters.AddChild(_createNewCharacterButton);
}
diff --git a/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml b/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml
index 5d1220d3650..8a10f17e590 100644
--- a/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml
+++ b/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml
@@ -96,6 +96,14 @@
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs
index c9a64eb0973..1d88977bb72 100644
--- a/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs
+++ b/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs
@@ -69,6 +69,8 @@ public sealed partial class HumanoidProfileEditor : Control
private Button _saveButton => CSaveButton;
private OptionButton _sexButton => CSexButton;
private OptionButton _genderButton => CPronounsButton;
+ private OptionButton _voiceButton => CVoiceButton; // Corvax-TTS
+ private Button _voicePlayButton => CVoicePlayButton; // Corvax-TTS
private Slider _skinColor => CSkin;
private OptionButton _clothingButton => CClothingButton;
private OptionButton _backpackButton => CBackpackButton;
@@ -172,9 +174,21 @@ public HumanoidProfileEditor(IClientPreferencesManager preferencesManager, IProt
#endregion Gender
+ // Corvax-TTS-Start
+ #region Voice
+
+ InitializeVoice();
+
+ #endregion
+ // Corvax-TTS-End
+
#region Species
_speciesList = prototypeManager.EnumeratePrototypes().Where(o => o.RoundStart).ToList();
+ // Corvax-Sponsors-Start
+ if (_sponsorsMgr != null)
+ _speciesList = _speciesList.Where(p => !p.SponsorOnly || _sponsorsMgr.Prototypes.Contains(p.ID)).ToList();
+ // Corvax-Sponsors-End
for (var i = 0; i < _speciesList.Count; i++)
{
var name = Loc.GetString(_speciesList[i].Name);
@@ -747,6 +761,7 @@ private void SetSex(Sex newSex)
break;
}
UpdateGenderControls();
+ UpdateTTSVoicesControls(); // Corvax-TTS
CMarkings.SetSex(newSex);
IsDirty = true;
}
@@ -757,6 +772,14 @@ private void SetGender(Gender newGender)
IsDirty = true;
}
+ // Corvax-TTS-Start
+ private void SetVoice(string newVoice)
+ {
+ Profile = Profile?.WithVoice(newVoice);
+ IsDirty = true;
+ }
+ // Corvax-TTS-End
+
private void SetSpecies(string newSpecies)
{
Profile = Profile?.WithSpecies(newSpecies);
@@ -1108,6 +1131,7 @@ public void UpdateControls()
UpdateTraitPreferences();
UpdateMarkings();
RebuildSpriteView();
+ UpdateTTSVoicesControls(); // Corvax-TTS
UpdateHairPickers();
UpdateCMarkingsHair();
UpdateCMarkingsFacialHair();
diff --git a/Content.Client/Research/UI/ResearchConsoleMenu.xaml b/Content.Client/Research/UI/ResearchConsoleMenu.xaml
index 8de9827c0c1..4caa6826c1c 100644
--- a/Content.Client/Research/UI/ResearchConsoleMenu.xaml
+++ b/Content.Client/Research/UI/ResearchConsoleMenu.xaml
@@ -4,7 +4,7 @@
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
Title="{Loc 'research-console-menu-title'}"
MinSize="625 400"
- SetSize="700 550">
+ SetSize="760 550">
diff --git a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs
index 2d90b371c3c..c82cc672a77 100644
--- a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs
+++ b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs
@@ -888,6 +888,13 @@ public void NotifyChatTextChange()
_typingIndicator?.ClientChangedChatText();
}
+ // Corvax-TypingIndicator-Start
+ public void NotifyChatFocus(bool isFocused)
+ {
+ _typingIndicator?.ClientChangedChatFocus(isFocused);
+ }
+ // Corvax-TypingIndicator-End
+
public void Repopulate()
{
foreach (var chat in _chats)
diff --git a/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml.cs b/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml.cs
index 79d23a94bee..7a6c1b9170c 100644
--- a/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml.cs
+++ b/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml.cs
@@ -35,6 +35,8 @@ public ChatBox()
ChatInput.Input.OnTextEntered += OnTextEntered;
ChatInput.Input.OnKeyBindDown += OnKeyBindDown;
ChatInput.Input.OnTextChanged += OnTextChanged;
+ ChatInput.Input.OnFocusEnter += OnFocusEnter; // Corvax-TypingIndicator
+ ChatInput.Input.OnFocusExit += OnFocusExit; // Corvax-TypingIndicator
ChatInput.ChannelSelector.OnChannelSelect += OnChannelSelect;
ChatInput.FilterButton.ChatFilterPopup.OnChannelFilter += OnChannelFilter;
@@ -175,6 +177,20 @@ private void OnTextChanged(LineEditEventArgs args)
_controller.NotifyChatTextChange();
}
+ // Corvax-TypingIndicator-Start
+ private void OnFocusEnter(LineEditEventArgs args)
+ {
+ // Warn typing indicator about focus
+ _controller.NotifyChatFocus(true);
+ }
+
+ private void OnFocusExit(LineEditEventArgs args)
+ {
+ // Warn typing indicator about focus
+ _controller.NotifyChatFocus(false);
+ }
+ // Corvax-TypingIndicator-End
+
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
diff --git a/Content.Client/VendingMachines/UI/VendingMachineMenu.xaml.cs b/Content.Client/VendingMachines/UI/VendingMachineMenu.xaml.cs
index a2741cc17ab..5ef66d5f74b 100644
--- a/Content.Client/VendingMachines/UI/VendingMachineMenu.xaml.cs
+++ b/Content.Client/VendingMachines/UI/VendingMachineMenu.xaml.cs
@@ -20,7 +20,8 @@ public sealed partial class VendingMachineMenu : DefaultWindow
public VendingMachineMenu()
{
- MinSize = SetSize = new Vector2(250, 150);
+ MinSize = new Vector2(250, 150); // Corvax-Resize
+ SetSize = new Vector2(450, 150); // Corvax-Resize
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
diff --git a/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs b/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs
index 0650482b108..74593b6670e 100644
--- a/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs
+++ b/Content.Client/VoiceMask/VoiceMaskBoundUserInterface.cs
@@ -20,6 +20,7 @@ protected override void Open()
_window.OpenCentered();
_window.OnNameChange += OnNameSelected;
+ _window.OnVoiceChange += (value) => SendMessage(new VoiceMaskChangeVoiceMessage(value)); // Corvax-TTS
_window.OnClose += Close;
}
@@ -35,7 +36,7 @@ protected override void UpdateState(BoundUserInterfaceState state)
return;
}
- _window.UpdateState(cast.Name);
+ _window.UpdateState(cast.Name, cast.Voice); // Corvax-TTS
}
protected override void Dispose(bool disposing)
diff --git a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml
index 2316ec9c7d8..0a4af81fd68 100644
--- a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml
+++ b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml
@@ -7,5 +7,11 @@
+
+
+
+
+
+
diff --git a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs
index e373acbd0a0..bbe2143a983 100644
--- a/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs
+++ b/Content.Client/VoiceMask/VoiceMaskNameChangeWindow.xaml.cs
@@ -1,13 +1,19 @@
+using System.Linq;
+using Content.Shared.Corvax.TTS;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
namespace Content.Client.VoiceMask;
[GenerateTypedNameReferences]
public sealed partial class VoiceMaskNameChangeWindow : DefaultWindow
{
+ private readonly List _voices; // Corvax-TTS
+
public Action? OnNameChange;
+ public Action? OnVoiceChange; // Corvax-TTS
public VoiceMaskNameChangeWindow()
{
@@ -17,10 +23,36 @@ public VoiceMaskNameChangeWindow()
{
OnNameChange!(NameSelector.Text);
};
+ // Corvax-TTS-Start
+ VoiceSelector.OnItemSelected += args =>
+ {
+ VoiceSelector.SelectId(args.Id);
+ if (VoiceSelector.SelectedMetadata != null)
+ OnVoiceChange!((string)VoiceSelector.SelectedMetadata);
+ };
+ _voices = IoCManager
+ .Resolve()
+ .EnumeratePrototypes()
+ .Where(o => o.RoundStart)
+ .OrderBy(o => Loc.GetString(o.Name))
+ .ToList();
+ for (var i = 0; i < _voices.Count; i++)
+ {
+ var name = Loc.GetString(_voices[i].Name);
+ VoiceSelector.AddItem(name);
+ VoiceSelector.SetItemMetadata(i, _voices[i].ID);
+ }
+ // Corvax-TTS-End
}
- public void UpdateState(string name)
+ public void UpdateState(string name, string voice) // Corvax-TTS
{
NameSelector.Text = name;
+
+ // Corvax-TTS-Start
+ var voiceIdx = _voices.FindIndex(v => v.ID == voice);
+ if (voiceIdx != -1)
+ VoiceSelector.Select(voiceIdx);
+ // Corvax-TTS-End
}
}
diff --git a/Content.IntegrationTests/Tests/PostMapInitTest.cs b/Content.IntegrationTests/Tests/PostMapInitTest.cs
index d5cc58a5f2e..355bedcd9bf 100644
--- a/Content.IntegrationTests/Tests/PostMapInitTest.cs
+++ b/Content.IntegrationTests/Tests/PostMapInitTest.cs
@@ -43,6 +43,13 @@ public sealed class PostMapInitTest
private static readonly string[] GameMaps =
{
+ // Corvax-Start
+ "CorvaxAvrite",
+ "CorvaxDelta",
+ "CorvaxSpectrum",
+ "CorvaxGate",
+ "CorvaxSilly",
+ // Corvax-End
"Dev",
"TestTeg",
"Fland",
diff --git a/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs b/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs
index 35aedf43f26..153f2d3348e 100644
--- a/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs
+++ b/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs
@@ -30,9 +30,14 @@ public sealed class ServerDbSqliteTests
- Aaliyah
- type: dataset
- id: sqlite_test_names_last
+ id: sqlite_test_names_last_male
values:
- - Ackerley";
+ - Ackerley
+
+- type: dataset
+ id: sqlite_test_names_last_female
+ values:
+ - Ackerla"; // Corvax-LastnameGender
private static HumanoidCharacterProfile CharlieCharlieson()
{
@@ -40,6 +45,7 @@ private static HumanoidCharacterProfile CharlieCharlieson()
"Charlie Charlieson",
"The biggest boy around.",
"Human",
+ "Eugene", // Corvax-TTS
21,
Sex.Male,
Gender.Epicene,
diff --git a/Content.Packaging/ClientPackaging.cs b/Content.Packaging/ClientPackaging.cs
index a989ebd968e..395a30a66b5 100644
--- a/Content.Packaging/ClientPackaging.cs
+++ b/Content.Packaging/ClientPackaging.cs
@@ -10,6 +10,7 @@ namespace Content.Packaging;
public static class ClientPackaging
{
+ private static readonly bool UseSecrets = File.Exists(Path.Combine("Secrets", "CorvaxSecrets.sln")); // Corvax-Secrets
///
/// Be advised this can be called from server packaging during a HybridACZ build.
///
@@ -34,6 +35,24 @@ await ProcessHelpers.RunCheck(new ProcessStartInfo
"/m"
}
});
+ if (UseSecrets)
+ {
+ await ProcessHelpers.RunCheck(new ProcessStartInfo
+ {
+ FileName = "dotnet",
+ ArgumentList =
+ {
+ "build",
+ Path.Combine("Secrets","Content.Corvax.Client", "Content.Corvax.Client.csproj"),
+ "-c", "Release",
+ "--nologo",
+ "/v:m",
+ "/t:Rebuild",
+ "/p:FullRelease=true",
+ "/m"
+ }
+ });
+ }
}
logger.Info("Packaging client...");
@@ -65,11 +84,17 @@ public static async Task WriteResources(
var inputPass = graph.Input;
+ // Corvax-Secrets-Start: Add Corvax interfaces to Magic ACZ
+ var assemblies = new List { "Content.Client", "Content.Shared", "Content.Shared.Database", "Content.Corvax.Interfaces.Client", "Content.Corvax.Interfaces.Shared" };
+ if (UseSecrets)
+ assemblies.AddRange(new[] { "Content.Corvax.Shared", "Content.Corvax.Client" });
+ // Corvax-Secrets-End
+
await RobustSharedPackaging.WriteContentAssemblies(
inputPass,
contentDir,
"Content.Client",
- new[] { "Content.Client", "Content.Shared", "Content.Shared.Database" },
+ assemblies, // Corvax-Secrets
cancel: cancel);
await RobustClientPackaging.WriteClientResources(contentDir, pass, cancel);
diff --git a/Content.Packaging/ServerPackaging.cs b/Content.Packaging/ServerPackaging.cs
index ba489629f79..87c81d48e66 100644
--- a/Content.Packaging/ServerPackaging.cs
+++ b/Content.Packaging/ServerPackaging.cs
@@ -33,6 +33,10 @@ public static class ServerPackaging
private static readonly List ServerContentAssemblies = new()
{
+ // Corvax-Secrets-Start
+ "Content.Corvax.Interfaces.Shared",
+ "Content.Corvax.Interfaces.Server",
+ // Corvax-Secrets-End
"Content.Server.Database",
"Content.Server",
"Content.Shared",
@@ -69,6 +73,8 @@ public static class ServerPackaging
"zh-Hant"
};
+ private static readonly bool UseSecrets = File.Exists(Path.Combine("Secrets", "CorvaxSecrets.sln")); // Corvax-Secrets
+
public static async Task PackageServer(bool skipBuild, bool hybridAcz, IPackageLogger logger, List? platforms = null)
{
if (platforms == null)
@@ -117,6 +123,28 @@ await ProcessHelpers.RunCheck(new ProcessStartInfo
"/m"
}
});
+ // Corvax-Secrets-Start
+ if (UseSecrets)
+ {
+ logger.Info($"Secrets found. Building secret project for {platform}...");
+ await ProcessHelpers.RunCheck(new ProcessStartInfo
+ {
+ FileName = "dotnet",
+ ArgumentList =
+ {
+ "build",
+ Path.Combine("Secrets","Content.Corvax.Server", "Content.Corvax.Server.csproj"),
+ "-c", "Release",
+ "--nologo",
+ "/v:m",
+ $"/p:TargetOs={platform.TargetOs}",
+ "/t:Rebuild",
+ "/p:FullRelease=true",
+ "/m"
+ }
+ });
+ }
+ // Corvax-Secrets-End
await PublishClientServer(platform.Rid, platform.TargetOs);
}
@@ -175,6 +203,10 @@ private static async Task WriteServerResources(
var inputPassCore = graph.InputCore;
var inputPassResources = graph.InputResources;
var contentAssemblies = new List(ServerContentAssemblies);
+ // Corvax-Secrets-Start
+ if (UseSecrets)
+ contentAssemblies.AddRange(new[] { "Content.Corvax.Shared", "Content.Corvax.Server" });
+ // Corvax-Secrets-End
// Additional assemblies that need to be copied such as EFCore.
var sourcePath = Path.Combine(contentDir, "bin", "Content.Server");
diff --git a/Content.Server.Database/Migrations/Postgres/20221214230019_TTSVoice.Designer.cs b/Content.Server.Database/Migrations/Postgres/20221214230019_TTSVoice.Designer.cs
new file mode 100644
index 00000000000..efb845c9ca2
--- /dev/null
+++ b/Content.Server.Database/Migrations/Postgres/20221214230019_TTSVoice.Designer.cs
@@ -0,0 +1,1338 @@
+//
+using System;
+using System.Net;
+using System.Text.Json;
+using Content.Server.Database;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace Content.Server.Database.Migrations.Postgres
+{
+ [DbContext(typeof(PostgresServerDbContext))]
+ [Migration("20221214230019_TTSVoice")]
+ partial class TTSVoice
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "6.0.5")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("Content.Server.Database.Admin", b =>
+ {
+ b.Property("UserId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.Property("AdminRankId")
+ .HasColumnType("integer")
+ .HasColumnName("admin_rank_id");
+
+ b.Property("Title")
+ .HasColumnType("text")
+ .HasColumnName("title");
+
+ b.HasKey("UserId")
+ .HasName("PK_admin");
+
+ b.HasIndex("AdminRankId")
+ .HasDatabaseName("IX_admin_admin_rank_id");
+
+ b.ToTable("admin", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminFlag", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("admin_flag_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("AdminId")
+ .HasColumnType("uuid")
+ .HasColumnName("admin_id");
+
+ b.Property("Flag")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("flag");
+
+ b.Property("Negative")
+ .HasColumnType("boolean")
+ .HasColumnName("negative");
+
+ b.HasKey("Id")
+ .HasName("PK_admin_flag");
+
+ b.HasIndex("AdminId")
+ .HasDatabaseName("IX_admin_flag_admin_id");
+
+ b.HasIndex("Flag", "AdminId")
+ .IsUnique();
+
+ b.ToTable("admin_flag", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("admin_log_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("RoundId")
+ .HasColumnType("integer")
+ .HasColumnName("round_id");
+
+ b.Property("Date")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("date");
+
+ b.Property("Impact")
+ .HasColumnType("smallint")
+ .HasColumnName("impact");
+
+ b.Property("Json")
+ .IsRequired()
+ .HasColumnType("jsonb")
+ .HasColumnName("json");
+
+ b.Property("Message")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("message");
+
+ b.Property("Type")
+ .HasColumnType("integer")
+ .HasColumnName("type");
+
+ b.HasKey("Id", "RoundId")
+ .HasName("PK_admin_log");
+
+ b.HasIndex("Date");
+
+ b.HasIndex("Message")
+ .HasAnnotation("Npgsql:TsVectorConfig", "english");
+
+ NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Message"), "GIN");
+
+ b.HasIndex("RoundId")
+ .HasDatabaseName("IX_admin_log_round_id");
+
+ b.HasIndex("Type")
+ .HasDatabaseName("IX_admin_log_type");
+
+ b.ToTable("admin_log", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminLogEntity", b =>
+ {
+ b.Property("Uid")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("uid");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Uid"));
+
+ b.Property("AdminLogId")
+ .HasColumnType("integer")
+ .HasColumnName("admin_log_id");
+
+ b.Property("AdminLogRoundId")
+ .HasColumnType("integer")
+ .HasColumnName("admin_log_round_id");
+
+ b.Property("Name")
+ .HasColumnType("text")
+ .HasColumnName("name");
+
+ b.HasKey("Uid")
+ .HasName("PK_admin_log_entity");
+
+ b.HasIndex("AdminLogId", "AdminLogRoundId")
+ .HasDatabaseName("IX_admin_log_entity_admin_log_id_admin_log_round_id");
+
+ b.ToTable("admin_log_entity", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b =>
+ {
+ b.Property("PlayerUserId")
+ .HasColumnType("uuid")
+ .HasColumnName("player_user_id");
+
+ b.Property("LogId")
+ .HasColumnType("integer")
+ .HasColumnName("log_id");
+
+ b.Property("RoundId")
+ .HasColumnType("integer")
+ .HasColumnName("round_id");
+
+ b.HasKey("PlayerUserId", "LogId", "RoundId")
+ .HasName("PK_admin_log_player");
+
+ b.HasIndex("LogId", "RoundId");
+
+ b.ToTable("admin_log_player", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminNote", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("admin_notes_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("CreatedById")
+ .HasColumnType("uuid")
+ .HasColumnName("created_by_id");
+
+ b.Property("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property("DeletedById")
+ .HasColumnType("uuid")
+ .HasColumnName("deleted_by_id");
+
+ b.Property("LastEditedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("last_edited_at");
+
+ b.Property("LastEditedById")
+ .HasColumnType("uuid")
+ .HasColumnName("last_edited_by_id");
+
+ b.Property("Message")
+ .IsRequired()
+ .HasMaxLength(4096)
+ .HasColumnType("character varying(4096)")
+ .HasColumnName("message");
+
+ b.Property("PlayerUserId")
+ .HasColumnType("uuid")
+ .HasColumnName("player_user_id");
+
+ b.Property("RoundId")
+ .HasColumnType("integer")
+ .HasColumnName("round_id");
+
+ b.Property("ShownToPlayer")
+ .HasColumnType("boolean")
+ .HasColumnName("shown_to_player");
+
+ b.HasKey("Id")
+ .HasName("PK_admin_notes");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("DeletedById");
+
+ b.HasIndex("LastEditedById");
+
+ b.HasIndex("PlayerUserId")
+ .HasDatabaseName("IX_admin_notes_player_user_id");
+
+ b.HasIndex("RoundId")
+ .HasDatabaseName("IX_admin_notes_round_id");
+
+ b.ToTable("admin_notes", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminRank", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("admin_rank_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("name");
+
+ b.HasKey("Id")
+ .HasName("PK_admin_rank");
+
+ b.ToTable("admin_rank", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("admin_rank_flag_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("AdminRankId")
+ .HasColumnType("integer")
+ .HasColumnName("admin_rank_id");
+
+ b.Property("Flag")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("flag");
+
+ b.HasKey("Id")
+ .HasName("PK_admin_rank_flag");
+
+ b.HasIndex("AdminRankId")
+ .HasDatabaseName("IX_admin_rank_flag_admin_rank_id");
+
+ b.HasIndex("Flag", "AdminRankId")
+ .IsUnique();
+
+ b.ToTable("admin_rank_flag", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Antag", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("antag_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("AntagName")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("antag_name");
+
+ b.Property("ProfileId")
+ .HasColumnType("integer")
+ .HasColumnName("profile_id");
+
+ b.HasKey("Id")
+ .HasName("PK_antag");
+
+ b.HasIndex("ProfileId", "AntagName")
+ .IsUnique();
+
+ b.ToTable("antag", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AssignedUserId", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("assigned_user_id_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.Property("UserName")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("user_name");
+
+ b.HasKey("Id")
+ .HasName("PK_assigned_user_id");
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.HasIndex("UserName")
+ .IsUnique();
+
+ b.ToTable("assigned_user_id", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.ConnectionLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("connection_log_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Address")
+ .IsRequired()
+ .HasColumnType("inet")
+ .HasColumnName("address");
+
+ b.Property("Denied")
+ .HasColumnType("smallint")
+ .HasColumnName("denied");
+
+ b.Property("HWId")
+ .HasColumnType("bytea")
+ .HasColumnName("hwid");
+
+ b.Property("Time")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("time");
+
+ b.Property("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.Property("UserName")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("user_name");
+
+ b.HasKey("Id")
+ .HasName("PK_connection_log");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("connection_log", (string)null);
+
+ b.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Job", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("job_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("JobName")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("job_name");
+
+ b.Property("Priority")
+ .HasColumnType("integer")
+ .HasColumnName("priority");
+
+ b.Property("ProfileId")
+ .HasColumnType("integer")
+ .HasColumnName("profile_id");
+
+ b.HasKey("Id")
+ .HasName("PK_job");
+
+ b.HasIndex("ProfileId")
+ .HasDatabaseName("IX_job_profile_id");
+
+ b.HasIndex("ProfileId", "JobName")
+ .IsUnique();
+
+ b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority")
+ .IsUnique()
+ .HasFilter("priority = 3");
+
+ b.ToTable("job", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Player", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("player_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("FirstSeenTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("first_seen_time");
+
+ b.Property("LastReadRules")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("last_read_rules");
+
+ b.Property("LastSeenAddress")
+ .IsRequired()
+ .HasColumnType("inet")
+ .HasColumnName("last_seen_address");
+
+ b.Property("LastSeenHWId")
+ .HasColumnType("bytea")
+ .HasColumnName("last_seen_hwid");
+
+ b.Property("LastSeenTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("last_seen_time");
+
+ b.Property("LastSeenUserName")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("last_seen_user_name");
+
+ b.Property("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("PK_player");
+
+ b.HasAlternateKey("UserId")
+ .HasName("ak_player_user_id");
+
+ b.HasIndex("LastSeenUserName");
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.ToTable("player", (string)null);
+
+ b.HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.PlayTime", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("play_time_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("PlayerId")
+ .HasColumnType("uuid")
+ .HasColumnName("player_id");
+
+ b.Property("TimeSpent")
+ .HasColumnType("interval")
+ .HasColumnName("time_spent");
+
+ b.Property("Tracker")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("tracker");
+
+ b.HasKey("Id")
+ .HasName("PK_play_time");
+
+ b.HasIndex("PlayerId", "Tracker")
+ .IsUnique();
+
+ b.ToTable("play_time", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Preference", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("preference_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("AdminOOCColor")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("admin_ooc_color");
+
+ b.Property("SelectedCharacterSlot")
+ .HasColumnType("integer")
+ .HasColumnName("selected_character_slot");
+
+ b.Property("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("PK_preference");
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.ToTable("preference", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Profile", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("profile_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Age")
+ .HasColumnType("integer")
+ .HasColumnName("age");
+
+ b.Property("Backpack")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("backpack");
+
+ b.Property("CharacterName")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("char_name");
+
+ b.Property("Clothing")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("clothing");
+
+ b.Property("EyeColor")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("eye_color");
+
+ b.Property("FacialHairColor")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("facial_hair_color");
+
+ b.Property("FacialHairName")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("facial_hair_name");
+
+ b.Property("FlavorText")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("flavor_text");
+
+ b.Property("Gender")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("gender");
+
+ b.Property("HairColor")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("hair_color");
+
+ b.Property("HairName")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("hair_name");
+
+ b.Property("Markings")
+ .HasColumnType("jsonb")
+ .HasColumnName("markings");
+
+ b.Property("PreferenceId")
+ .HasColumnType("integer")
+ .HasColumnName("preference_id");
+
+ b.Property("PreferenceUnavailable")
+ .HasColumnType("integer")
+ .HasColumnName("pref_unavailable");
+
+ b.Property("Sex")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("sex");
+
+ b.Property("SkinColor")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("skin_color");
+
+ b.Property("Slot")
+ .HasColumnType("integer")
+ .HasColumnName("slot");
+
+ b.Property("Species")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("species");
+
+ b.Property("Voice")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("voice");
+
+ b.HasKey("Id")
+ .HasName("PK_profile");
+
+ b.HasIndex("PreferenceId")
+ .HasDatabaseName("IX_profile_preference_id");
+
+ b.HasIndex("Slot", "PreferenceId")
+ .IsUnique();
+
+ b.ToTable("profile", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Round", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("round_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ServerId")
+ .HasColumnType("integer")
+ .HasColumnName("server_id");
+
+ b.HasKey("Id")
+ .HasName("PK_round");
+
+ b.HasIndex("ServerId")
+ .HasDatabaseName("IX_round_server_id");
+
+ b.ToTable("round", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Server", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("server_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("name");
+
+ b.HasKey("Id")
+ .HasName("PK_server");
+
+ b.ToTable("server", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.ServerBan", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("server_ban_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property?>("Address")
+ .HasColumnType("inet")
+ .HasColumnName("address");
+
+ b.Property("BanTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("ban_time");
+
+ b.Property("BanningAdmin")
+ .HasColumnType("uuid")
+ .HasColumnName("banning_admin");
+
+ b.Property("ExpirationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("expiration_time");
+
+ b.Property("HWId")
+ .HasColumnType("bytea")
+ .HasColumnName("hwid");
+
+ b.Property("Reason")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("reason");
+
+ b.Property("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("PK_server_ban");
+
+ b.HasIndex("Address");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("server_ban", (string)null);
+
+ b.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
+
+ b.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.ServerBanHit", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("server_ban_hit_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("BanId")
+ .HasColumnType("integer")
+ .HasColumnName("ban_id");
+
+ b.Property("ConnectionId")
+ .HasColumnType("integer")
+ .HasColumnName("connection_id");
+
+ b.HasKey("Id")
+ .HasName("PK_server_ban_hit");
+
+ b.HasIndex("BanId")
+ .HasDatabaseName("IX_server_ban_hit_ban_id");
+
+ b.HasIndex("ConnectionId")
+ .HasDatabaseName("IX_server_ban_hit_connection_id");
+
+ b.ToTable("server_ban_hit", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("server_role_ban_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property?>("Address")
+ .HasColumnType("inet")
+ .HasColumnName("address");
+
+ b.Property("BanTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("ban_time");
+
+ b.Property("BanningAdmin")
+ .HasColumnType("uuid")
+ .HasColumnName("banning_admin");
+
+ b.Property("ExpirationTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("expiration_time");
+
+ b.Property("HWId")
+ .HasColumnType("bytea")
+ .HasColumnName("hwid");
+
+ b.Property("Reason")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("reason");
+
+ b.Property("RoleId")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("role_id");
+
+ b.Property("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("PK_server_role_ban");
+
+ b.HasIndex("Address");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("server_role_ban", (string)null);
+
+ b.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
+
+ b.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("role_unban_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("BanId")
+ .HasColumnType("integer")
+ .HasColumnName("ban_id");
+
+ b.Property("UnbanTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("unban_time");
+
+ b.Property("UnbanningAdmin")
+ .HasColumnType("uuid")
+ .HasColumnName("unbanning_admin");
+
+ b.HasKey("Id")
+ .HasName("PK_server_role_unban");
+
+ b.HasIndex("BanId")
+ .IsUnique();
+
+ b.ToTable("server_role_unban", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.ServerUnban", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("unban_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("BanId")
+ .HasColumnType("integer")
+ .HasColumnName("ban_id");
+
+ b.Property("UnbanTime")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("unban_time");
+
+ b.Property("UnbanningAdmin")
+ .HasColumnType("uuid")
+ .HasColumnName("unbanning_admin");
+
+ b.HasKey("Id")
+ .HasName("PK_server_unban");
+
+ b.HasIndex("BanId")
+ .IsUnique();
+
+ b.ToTable("server_unban", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Trait", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("trait_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ProfileId")
+ .HasColumnType("integer")
+ .HasColumnName("profile_id");
+
+ b.Property("TraitName")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("trait_name");
+
+ b.HasKey("Id")
+ .HasName("PK_trait");
+
+ b.HasIndex("ProfileId", "TraitName")
+ .IsUnique();
+
+ b.ToTable("trait", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("uploaded_resource_log_id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Data")
+ .IsRequired()
+ .HasColumnType("bytea")
+ .HasColumnName("data");
+
+ b.Property("Date")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("date");
+
+ b.Property("Path")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("path");
+
+ b.Property("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("PK_uploaded_resource_log");
+
+ b.ToTable("uploaded_resource_log", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Whitelist", b =>
+ {
+ b.Property("UserId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("UserId")
+ .HasName("PK_whitelist");
+
+ b.ToTable("whitelist", (string)null);
+ });
+
+ modelBuilder.Entity("PlayerRound", b =>
+ {
+ b.Property("PlayersId")
+ .HasColumnType("integer")
+ .HasColumnName("players_id");
+
+ b.Property("RoundsId")
+ .HasColumnType("integer")
+ .HasColumnName("rounds_id");
+
+ b.HasKey("PlayersId", "RoundsId")
+ .HasName("PK_player_round");
+
+ b.HasIndex("RoundsId")
+ .HasDatabaseName("IX_player_round_rounds_id");
+
+ b.ToTable("player_round", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Admin", b =>
+ {
+ b.HasOne("Content.Server.Database.AdminRank", "AdminRank")
+ .WithMany("Admins")
+ .HasForeignKey("AdminRankId")
+ .OnDelete(DeleteBehavior.SetNull)
+ .HasConstraintName("FK_admin_admin_rank_admin_rank_id");
+
+ b.Navigation("AdminRank");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminFlag", b =>
+ {
+ b.HasOne("Content.Server.Database.Admin", "Admin")
+ .WithMany("Flags")
+ .HasForeignKey("AdminId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_admin_flag_admin_admin_id");
+
+ b.Navigation("Admin");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminLog", b =>
+ {
+ b.HasOne("Content.Server.Database.Round", "Round")
+ .WithMany("AdminLogs")
+ .HasForeignKey("RoundId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_admin_log_round_round_id");
+
+ b.Navigation("Round");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminLogEntity", b =>
+ {
+ b.HasOne("Content.Server.Database.AdminLog", null)
+ .WithMany("Entities")
+ .HasForeignKey("AdminLogId", "AdminLogRoundId")
+ .HasConstraintName("FK_admin_log_entity_admin_log_admin_log_id_admin_log_round_id");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b =>
+ {
+ b.HasOne("Content.Server.Database.Player", "Player")
+ .WithMany("AdminLogs")
+ .HasForeignKey("PlayerUserId")
+ .HasPrincipalKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_admin_log_player_player_player_user_id");
+
+ b.HasOne("Content.Server.Database.AdminLog", "Log")
+ .WithMany("Players")
+ .HasForeignKey("LogId", "RoundId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_admin_log_player_admin_log_log_id_round_id");
+
+ b.Navigation("Log");
+
+ b.Navigation("Player");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminNote", b =>
+ {
+ b.HasOne("Content.Server.Database.Player", "CreatedBy")
+ .WithMany("AdminNotesCreated")
+ .HasForeignKey("CreatedById")
+ .HasPrincipalKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_admin_notes_player_created_by_id");
+
+ b.HasOne("Content.Server.Database.Player", "DeletedBy")
+ .WithMany("AdminNotesDeleted")
+ .HasForeignKey("DeletedById")
+ .HasPrincipalKey("UserId")
+ .HasConstraintName("FK_admin_notes_player_deleted_by_id");
+
+ b.HasOne("Content.Server.Database.Player", "LastEditedBy")
+ .WithMany("AdminNotesLastEdited")
+ .HasForeignKey("LastEditedById")
+ .HasPrincipalKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_admin_notes_player_last_edited_by_id");
+
+ b.HasOne("Content.Server.Database.Player", "Player")
+ .WithMany("AdminNotesReceived")
+ .HasForeignKey("PlayerUserId")
+ .HasPrincipalKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_admin_notes_player_player_user_id");
+
+ b.HasOne("Content.Server.Database.Round", "Round")
+ .WithMany()
+ .HasForeignKey("RoundId")
+ .HasConstraintName("FK_admin_notes_round_round_id");
+
+ b.Navigation("CreatedBy");
+
+ b.Navigation("DeletedBy");
+
+ b.Navigation("LastEditedBy");
+
+ b.Navigation("Player");
+
+ b.Navigation("Round");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b =>
+ {
+ b.HasOne("Content.Server.Database.AdminRank", "Rank")
+ .WithMany("Flags")
+ .HasForeignKey("AdminRankId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_admin_rank_flag_admin_rank_admin_rank_id");
+
+ b.Navigation("Rank");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Antag", b =>
+ {
+ b.HasOne("Content.Server.Database.Profile", "Profile")
+ .WithMany("Antags")
+ .HasForeignKey("ProfileId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_antag_profile_profile_id");
+
+ b.Navigation("Profile");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Job", b =>
+ {
+ b.HasOne("Content.Server.Database.Profile", "Profile")
+ .WithMany("Jobs")
+ .HasForeignKey("ProfileId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_job_profile_profile_id");
+
+ b.Navigation("Profile");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Profile", b =>
+ {
+ b.HasOne("Content.Server.Database.Preference", "Preference")
+ .WithMany("Profiles")
+ .HasForeignKey("PreferenceId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_profile_preference_preference_id");
+
+ b.Navigation("Preference");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Round", b =>
+ {
+ b.HasOne("Content.Server.Database.Server", "Server")
+ .WithMany("Rounds")
+ .HasForeignKey("ServerId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_round_server_server_id");
+
+ b.Navigation("Server");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.ServerBanHit", b =>
+ {
+ b.HasOne("Content.Server.Database.ServerBan", "Ban")
+ .WithMany("BanHits")
+ .HasForeignKey("BanId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_server_ban_hit_server_ban_ban_id");
+
+ b.HasOne("Content.Server.Database.ConnectionLog", "Connection")
+ .WithMany("BanHits")
+ .HasForeignKey("ConnectionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_server_ban_hit_connection_log_connection_id");
+
+ b.Navigation("Ban");
+
+ b.Navigation("Connection");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b =>
+ {
+ b.HasOne("Content.Server.Database.ServerRoleBan", "Ban")
+ .WithOne("Unban")
+ .HasForeignKey("Content.Server.Database.ServerRoleUnban", "BanId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_server_role_unban_server_role_ban_ban_id");
+
+ b.Navigation("Ban");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.ServerUnban", b =>
+ {
+ b.HasOne("Content.Server.Database.ServerBan", "Ban")
+ .WithOne("Unban")
+ .HasForeignKey("Content.Server.Database.ServerUnban", "BanId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_server_unban_server_ban_ban_id");
+
+ b.Navigation("Ban");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Trait", b =>
+ {
+ b.HasOne("Content.Server.Database.Profile", "Profile")
+ .WithMany("Traits")
+ .HasForeignKey("ProfileId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_trait_profile_profile_id");
+
+ b.Navigation("Profile");
+ });
+
+ modelBuilder.Entity("PlayerRound", b =>
+ {
+ b.HasOne("Content.Server.Database.Player", null)
+ .WithMany()
+ .HasForeignKey("PlayersId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_player_round_player_players_id");
+
+ b.HasOne("Content.Server.Database.Round", null)
+ .WithMany()
+ .HasForeignKey("RoundsId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("FK_player_round_round_rounds_id");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Admin", b =>
+ {
+ b.Navigation("Flags");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminLog", b =>
+ {
+ b.Navigation("Entities");
+
+ b.Navigation("Players");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminRank", b =>
+ {
+ b.Navigation("Admins");
+
+ b.Navigation("Flags");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.ConnectionLog", b =>
+ {
+ b.Navigation("BanHits");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Player", b =>
+ {
+ b.Navigation("AdminLogs");
+
+ b.Navigation("AdminNotesCreated");
+
+ b.Navigation("AdminNotesDeleted");
+
+ b.Navigation("AdminNotesLastEdited");
+
+ b.Navigation("AdminNotesReceived");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Preference", b =>
+ {
+ b.Navigation("Profiles");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Profile", b =>
+ {
+ b.Navigation("Antags");
+
+ b.Navigation("Jobs");
+
+ b.Navigation("Traits");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Round", b =>
+ {
+ b.Navigation("AdminLogs");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Server", b =>
+ {
+ b.Navigation("Rounds");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.ServerBan", b =>
+ {
+ b.Navigation("BanHits");
+
+ b.Navigation("Unban");
+ });
+
+ modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b =>
+ {
+ b.Navigation("Unban");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Content.Server.Database/Migrations/Postgres/20221214230019_TTSVoice.cs b/Content.Server.Database/Migrations/Postgres/20221214230019_TTSVoice.cs
new file mode 100644
index 00000000000..9b6b9738b60
--- /dev/null
+++ b/Content.Server.Database/Migrations/Postgres/20221214230019_TTSVoice.cs
@@ -0,0 +1,26 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Content.Server.Database.Migrations.Postgres
+{
+ public partial class TTSVoice : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "voice",
+ table: "profile",
+ type: "text",
+ nullable: false,
+ defaultValue: "");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "voice",
+ table: "profile");
+ }
+ }
+}
diff --git a/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs b/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs
index e26527be9d9..d00c664bd2e 100644
--- a/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs
+++ b/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs
@@ -801,6 +801,13 @@ protected override void BuildModel(ModelBuilder modelBuilder)
.HasColumnType("text")
.HasColumnName("species");
+ // Corvax-TTS-Start
+ b.Property("Voice")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("voice");
+ // Corvax-TTS-End
+
b.HasKey("Id")
.HasName("PK_profile");
diff --git a/Content.Server.Database/Migrations/Sqlite/20221214230014_TTSVoice.Designer.cs b/Content.Server.Database/Migrations/Sqlite/20221214230014_TTSVoice.Designer.cs
new file mode 100644
index 00000000000..6cae3b22e11
--- /dev/null
+++ b/Content.Server.Database/Migrations/Sqlite/20221214230014_TTSVoice.Designer.cs
@@ -0,0 +1,1272 @@
+//
+using System;
+using Content.Server.Database;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Content.Server.Database.Migrations.Sqlite
+{
+ [DbContext(typeof(SqliteServerDbContext))]
+ [Migration("20221214230014_TTSVoice")]
+ partial class TTSVoice
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "6.0.5");
+
+ modelBuilder.Entity("Content.Server.Database.Admin", b =>
+ {
+ b.Property("UserId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT")
+ .HasColumnName("user_id");
+
+ b.Property("AdminRankId")
+ .HasColumnType("INTEGER")
+ .HasColumnName("admin_rank_id");
+
+ b.Property("Title")
+ .HasColumnType("TEXT")
+ .HasColumnName("title");
+
+ b.HasKey("UserId")
+ .HasName("PK_admin");
+
+ b.HasIndex("AdminRankId")
+ .HasDatabaseName("IX_admin_admin_rank_id");
+
+ b.ToTable("admin", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminFlag", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasColumnName("admin_flag_id");
+
+ b.Property("AdminId")
+ .HasColumnType("TEXT")
+ .HasColumnName("admin_id");
+
+ b.Property("Flag")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("flag");
+
+ b.Property("Negative")
+ .HasColumnType("INTEGER")
+ .HasColumnName("negative");
+
+ b.HasKey("Id")
+ .HasName("PK_admin_flag");
+
+ b.HasIndex("AdminId")
+ .HasDatabaseName("IX_admin_flag_admin_id");
+
+ b.HasIndex("Flag", "AdminId")
+ .IsUnique();
+
+ b.ToTable("admin_flag", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasColumnName("admin_log_id");
+
+ b.Property("RoundId")
+ .HasColumnType("INTEGER")
+ .HasColumnName("round_id");
+
+ b.Property("Date")
+ .HasColumnType("TEXT")
+ .HasColumnName("date");
+
+ b.Property("Impact")
+ .HasColumnType("INTEGER")
+ .HasColumnName("impact");
+
+ b.Property("Json")
+ .IsRequired()
+ .HasColumnType("jsonb")
+ .HasColumnName("json");
+
+ b.Property("Message")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("message");
+
+ b.Property("Type")
+ .HasColumnType("INTEGER")
+ .HasColumnName("type");
+
+ b.HasKey("Id", "RoundId")
+ .HasName("PK_admin_log");
+
+ b.HasIndex("Date");
+
+ b.HasIndex("RoundId")
+ .HasDatabaseName("IX_admin_log_round_id");
+
+ b.HasIndex("Type")
+ .HasDatabaseName("IX_admin_log_type");
+
+ b.ToTable("admin_log", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminLogEntity", b =>
+ {
+ b.Property("Uid")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasColumnName("uid");
+
+ b.Property("AdminLogId")
+ .HasColumnType("INTEGER")
+ .HasColumnName("admin_log_id");
+
+ b.Property("AdminLogRoundId")
+ .HasColumnType("INTEGER")
+ .HasColumnName("admin_log_round_id");
+
+ b.Property("Name")
+ .HasColumnType("TEXT")
+ .HasColumnName("name");
+
+ b.HasKey("Uid")
+ .HasName("PK_admin_log_entity");
+
+ b.HasIndex("AdminLogId", "AdminLogRoundId")
+ .HasDatabaseName("IX_admin_log_entity_admin_log_id_admin_log_round_id");
+
+ b.ToTable("admin_log_entity", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b =>
+ {
+ b.Property("PlayerUserId")
+ .HasColumnType("TEXT")
+ .HasColumnName("player_user_id");
+
+ b.Property("LogId")
+ .HasColumnType("INTEGER")
+ .HasColumnName("log_id");
+
+ b.Property("RoundId")
+ .HasColumnType("INTEGER")
+ .HasColumnName("round_id");
+
+ b.HasKey("PlayerUserId", "LogId", "RoundId")
+ .HasName("PK_admin_log_player");
+
+ b.HasIndex("LogId", "RoundId");
+
+ b.ToTable("admin_log_player", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminNote", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasColumnName("admin_notes_id");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT")
+ .HasColumnName("created_at");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT")
+ .HasColumnName("created_by_id");
+
+ b.Property("Deleted")
+ .HasColumnType("INTEGER")
+ .HasColumnName("deleted");
+
+ b.Property("DeletedAt")
+ .HasColumnType("TEXT")
+ .HasColumnName("deleted_at");
+
+ b.Property("DeletedById")
+ .HasColumnType("TEXT")
+ .HasColumnName("deleted_by_id");
+
+ b.Property("LastEditedAt")
+ .HasColumnType("TEXT")
+ .HasColumnName("last_edited_at");
+
+ b.Property("LastEditedById")
+ .HasColumnType("TEXT")
+ .HasColumnName("last_edited_by_id");
+
+ b.Property("Message")
+ .IsRequired()
+ .HasMaxLength(4096)
+ .HasColumnType("TEXT")
+ .HasColumnName("message");
+
+ b.Property("PlayerUserId")
+ .HasColumnType("TEXT")
+ .HasColumnName("player_user_id");
+
+ b.Property("RoundId")
+ .HasColumnType("INTEGER")
+ .HasColumnName("round_id");
+
+ b.Property("ShownToPlayer")
+ .HasColumnType("INTEGER")
+ .HasColumnName("shown_to_player");
+
+ b.HasKey("Id")
+ .HasName("PK_admin_notes");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("DeletedById");
+
+ b.HasIndex("LastEditedById");
+
+ b.HasIndex("PlayerUserId")
+ .HasDatabaseName("IX_admin_notes_player_user_id");
+
+ b.HasIndex("RoundId")
+ .HasDatabaseName("IX_admin_notes_round_id");
+
+ b.ToTable("admin_notes", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminRank", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasColumnName("admin_rank_id");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("name");
+
+ b.HasKey("Id")
+ .HasName("PK_admin_rank");
+
+ b.ToTable("admin_rank", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasColumnName("admin_rank_flag_id");
+
+ b.Property("AdminRankId")
+ .HasColumnType("INTEGER")
+ .HasColumnName("admin_rank_id");
+
+ b.Property("Flag")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("flag");
+
+ b.HasKey("Id")
+ .HasName("PK_admin_rank_flag");
+
+ b.HasIndex("AdminRankId")
+ .HasDatabaseName("IX_admin_rank_flag_admin_rank_id");
+
+ b.HasIndex("Flag", "AdminRankId")
+ .IsUnique();
+
+ b.ToTable("admin_rank_flag", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Antag", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasColumnName("antag_id");
+
+ b.Property("AntagName")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("antag_name");
+
+ b.Property("ProfileId")
+ .HasColumnType("INTEGER")
+ .HasColumnName("profile_id");
+
+ b.HasKey("Id")
+ .HasName("PK_antag");
+
+ b.HasIndex("ProfileId", "AntagName")
+ .IsUnique();
+
+ b.ToTable("antag", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.AssignedUserId", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasColumnName("assigned_user_id_id");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT")
+ .HasColumnName("user_id");
+
+ b.Property("UserName")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("user_name");
+
+ b.HasKey("Id")
+ .HasName("PK_assigned_user_id");
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.HasIndex("UserName")
+ .IsUnique();
+
+ b.ToTable("assigned_user_id", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.ConnectionLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasColumnName("connection_log_id");
+
+ b.Property("Address")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("address");
+
+ b.Property("Denied")
+ .HasColumnType("INTEGER")
+ .HasColumnName("denied");
+
+ b.Property("HWId")
+ .HasColumnType("BLOB")
+ .HasColumnName("hwid");
+
+ b.Property("Time")
+ .HasColumnType("TEXT")
+ .HasColumnName("time");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT")
+ .HasColumnName("user_id");
+
+ b.Property("UserName")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("user_name");
+
+ b.HasKey("Id")
+ .HasName("PK_connection_log");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("connection_log", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Job", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasColumnName("job_id");
+
+ b.Property("JobName")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("job_name");
+
+ b.Property("Priority")
+ .HasColumnType("INTEGER")
+ .HasColumnName("priority");
+
+ b.Property("ProfileId")
+ .HasColumnType("INTEGER")
+ .HasColumnName("profile_id");
+
+ b.HasKey("Id")
+ .HasName("PK_job");
+
+ b.HasIndex("ProfileId")
+ .HasDatabaseName("IX_job_profile_id");
+
+ b.HasIndex("ProfileId", "JobName")
+ .IsUnique();
+
+ b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority")
+ .IsUnique()
+ .HasFilter("priority = 3");
+
+ b.ToTable("job", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Player", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasColumnName("player_id");
+
+ b.Property("FirstSeenTime")
+ .HasColumnType("TEXT")
+ .HasColumnName("first_seen_time");
+
+ b.Property("LastReadRules")
+ .HasColumnType("TEXT")
+ .HasColumnName("last_read_rules");
+
+ b.Property("LastSeenAddress")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("last_seen_address");
+
+ b.Property("LastSeenHWId")
+ .HasColumnType("BLOB")
+ .HasColumnName("last_seen_hwid");
+
+ b.Property("LastSeenTime")
+ .HasColumnType("TEXT")
+ .HasColumnName("last_seen_time");
+
+ b.Property("LastSeenUserName")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("last_seen_user_name");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("PK_player");
+
+ b.HasAlternateKey("UserId")
+ .HasName("ak_player_user_id");
+
+ b.HasIndex("LastSeenUserName");
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.ToTable("player", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.PlayTime", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasColumnName("play_time_id");
+
+ b.Property("PlayerId")
+ .HasColumnType("TEXT")
+ .HasColumnName("player_id");
+
+ b.Property("TimeSpent")
+ .HasColumnType("TEXT")
+ .HasColumnName("time_spent");
+
+ b.Property("Tracker")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("tracker");
+
+ b.HasKey("Id")
+ .HasName("PK_play_time");
+
+ b.HasIndex("PlayerId", "Tracker")
+ .IsUnique();
+
+ b.ToTable("play_time", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Preference", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasColumnName("preference_id");
+
+ b.Property("AdminOOCColor")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("admin_ooc_color");
+
+ b.Property("SelectedCharacterSlot")
+ .HasColumnType("INTEGER")
+ .HasColumnName("selected_character_slot");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("PK_preference");
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.ToTable("preference", (string)null);
+ });
+
+ modelBuilder.Entity("Content.Server.Database.Profile", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasColumnName("profile_id");
+
+ b.Property("Age")
+ .HasColumnType("INTEGER")
+ .HasColumnName("age");
+
+ b.Property("Backpack")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("backpack");
+
+ b.Property("CharacterName")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("char_name");
+
+ b.Property("Clothing")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("clothing");
+
+ b.Property