mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-30 00:21:11 +01:00
Bun, Inferno->React migration (#22529)
Re-creation of https://github.com/Aurorastation/Aurora.3/pull/21046 to skip merge conflict hell. Brings us modern TGUI. **ALTERNATE TITLE: TGUI HELLSCAPE PR ABANDON ALL HOPE YE WHO ENTER HERE** - [x] Migrate build tools (javascript -> typescript, bun for package management). - [x] Upgrade all TGUI dependencies and associated root files to TG-congruent versions (axios, babel, dompurify, eslint, highlight, marked, prettier, sass, source-map, stacktrace-parser, typescript). - [x] InfernoJS -> React migrations - [x] React cleanup and polish (migrate all remaining .js files to appropriate .ts or .tsx filetype, all remaining hooks, linting, error corrections, etc.) - [ ] Test all remaining TGUI interfaces
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
# This action is a wrapper around `oven-sh/setup-bun` to use the version specified in
|
||||
# `dependencies.sh`.
|
||||
name: Setup Bun
|
||||
description: Install Bun using the version specified in `dependencies.sh`
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Configure Bun version
|
||||
shell: bash
|
||||
run: |
|
||||
source dependencies.sh
|
||||
echo "BUN_VERSION_REQUIRED=$BUN_VERSION" >> $GITHUB_ENV
|
||||
- name: Install Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: ${{ env.BUN_VERSION_REQUIRED }}
|
||||
@@ -0,0 +1,26 @@
|
||||
# This action is a wrapper around `actions/setup-node`, to use the version specified in
|
||||
# `dependencies.sh`.
|
||||
name: Setup Node
|
||||
description: Install Node using the version specified in `dependencies.sh`; additionally, restores the Yarn cache if one exists
|
||||
|
||||
inputs:
|
||||
restore-yarn-cache:
|
||||
description: "If `true`, restores the Yarn cache alongside installing node."
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Configure Node version
|
||||
shell: bash
|
||||
run: |
|
||||
source dependencies.sh
|
||||
echo "NODE_VERSION_REQUIRED=$NODE_VERSION_LTS" >> $GITHUB_ENV
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION_REQUIRED }}
|
||||
cache: ${{ fromJSON(inputs.restore-yarn-cache) && 'yarn' || '' }}
|
||||
cache-dependency-path: ${{ fromJSON(inputs.restore-yarn-cache) && 'tgui/yarn.lock' || '' }}
|
||||
@@ -281,18 +281,14 @@ jobs:
|
||||
- name: Set ENV variables
|
||||
run: bash dependencies.sh
|
||||
|
||||
#Setup NodeJS using YARN as cache
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION_LTS }}
|
||||
cache: "yarn"
|
||||
cache-dependency-path: tgui/yarn.lock
|
||||
#Setup bun
|
||||
- name: Setup Bun
|
||||
uses: ./.github/actions/setup_bun
|
||||
|
||||
#Lint TGUI
|
||||
- name: Check Tgui
|
||||
run: |
|
||||
tools/build/build --ci lint tgui-test
|
||||
tools/build/build.sh --ci lint tgui-test
|
||||
|
||||
###########################################
|
||||
############### MISC LINTING ##############
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
name: "Build TGUI"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update-tgui:
|
||||
concurrency: tgui
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Install Bun
|
||||
uses: ./.github/actions/setup_bun
|
||||
|
||||
- name: Update TGUI
|
||||
run: |
|
||||
tools/bootstrap/javascript tools/build/build.sh tgui
|
||||
|
||||
- name: Commit TGUI
|
||||
run: |
|
||||
git pull origin master
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "AuroraBuildBot"
|
||||
git add --force tgui/public/*
|
||||
git commit -m "Automatic TGUI compile [ci skip]" -a || true
|
||||
|
||||
- name: Push
|
||||
uses: ad-m/github-push-action@master
|
||||
with:
|
||||
github_token: ${{ secrets.BOT_TOKEN_AURORA }}
|
||||
@@ -73,3 +73,6 @@ define_sanity_output.txt
|
||||
|
||||
# Tracy dumps
|
||||
*.utracy
|
||||
|
||||
# JavaScript tools
|
||||
**/node_modules
|
||||
|
||||
Vendored
+5
-2
@@ -7,6 +7,9 @@
|
||||
"vscode.typescript-language-features",
|
||||
"redhat.vscode-yaml",
|
||||
"esbenp.prettier-vscode",
|
||||
"rome.rome"
|
||||
]
|
||||
"rome.rome",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"editorconfig.editorconfig",
|
||||
"esbenp.prettier-vscode",
|
||||
"biomejs.biome"
|
||||
}
|
||||
|
||||
Vendored
+10
-8
@@ -1,25 +1,27 @@
|
||||
{
|
||||
"eslint.nodePath": "./tgui/.yarn/sdks",
|
||||
"eslint.workingDirectories": ["./tgui"],
|
||||
"prettier.prettierPath": "./tgui/.yarn/sdks/prettier/index.cjs",
|
||||
"typescript.tsdk": "./tgui/.yarn/sdks/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"search.exclude": {
|
||||
"**/.yarn": true,
|
||||
"**/.pnp.*": true
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
"source.organizeImports.biome": "explicit",
|
||||
"source.fixAll.biome": "explicit"
|
||||
},
|
||||
"editor.insertSpaces": false,
|
||||
"files.eol": "\n",
|
||||
"files.insertFinalNewline": true,
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"gitlens.advanced.blame.customArguments": ["-w"],
|
||||
"[javascript][typescript][typescriptreact][javascriptreact][html][scss][css][json][jsonc][markdown][yaml]": {
|
||||
"editor.rulers": [80],
|
||||
"[javascript][typescript][javascriptreact][typescriptreact][css][json][jsonc]": {
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.rulers": [80]
|
||||
},
|
||||
"[yaml][markdown][html][scss]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true
|
||||
"editor.formatOnSave": true,
|
||||
"editor.rulers": [80]
|
||||
},
|
||||
"workbench.editorAssociations": {
|
||||
"*.dmi": "dmiEditor.dmiEditor"
|
||||
|
||||
Vendored
+4
-34
@@ -16,8 +16,7 @@
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$dreammaker",
|
||||
"$tsc",
|
||||
"$eslint-stylish"
|
||||
"$tsc"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
@@ -61,8 +60,7 @@
|
||||
"command": ".\\bin\\tgui-build.cmd"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$tsc",
|
||||
"$eslint-stylish"
|
||||
"$tsc"
|
||||
],
|
||||
"group": "build",
|
||||
"label": "tgui: build"
|
||||
@@ -74,38 +72,11 @@
|
||||
"command": ".\\bin\\tgui-dev.cmd"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$tsc",
|
||||
"$eslint-stylish"
|
||||
"$tsc"
|
||||
],
|
||||
"group": "build",
|
||||
"label": "tgui: dev server"
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"command": "bin/tgui-bench",
|
||||
"windows": {
|
||||
"command": ".\\bin\\tgui-bench.cmd"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$tsc",
|
||||
"$eslint-stylish"
|
||||
],
|
||||
"group": "build",
|
||||
"label": "tgui: bench"
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"command": "bin/tgui-sonar",
|
||||
"windows": {
|
||||
"command": ".\\bin\\tgui-sonar.cmd"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$tsc",
|
||||
"$eslint-stylish"
|
||||
],
|
||||
"group": "build",
|
||||
"label": "tgui: sonar"
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"command": "bin/tgfont",
|
||||
@@ -113,8 +84,7 @@
|
||||
"command": ".\\bin\\tgfont.cmd"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$tsc",
|
||||
"$eslint-stylish"
|
||||
"$tsc"
|
||||
],
|
||||
"group": "build",
|
||||
"label": "tgui: rebuild tgfont"
|
||||
|
||||
+9
-9
@@ -155,6 +155,7 @@
|
||||
#include "code\__DEFINES\targeting.dm"
|
||||
#include "code\__DEFINES\technomancer.dm"
|
||||
#include "code\__DEFINES\text.dm"
|
||||
#include "code\__DEFINES\tgs.config.dm"
|
||||
#include "code\__DEFINES\tgs.dm"
|
||||
#include "code\__DEFINES\tgui.dm"
|
||||
#include "code\__DEFINES\time.dm"
|
||||
@@ -184,6 +185,7 @@
|
||||
#include "code\__DEFINES\dcs\signals\signals_record.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_spatial_grid.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_subsystem.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_tgui.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_tools.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_turf.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_weather.dm"
|
||||
@@ -404,6 +406,7 @@
|
||||
#include "code\controllers\subsystems\stickyban.dm"
|
||||
#include "code\controllers\subsystems\sun.dm"
|
||||
#include "code\controllers\subsystems\sunlight.dm"
|
||||
#include "code\controllers\subsystems\tgui.dm"
|
||||
#include "code\controllers\subsystems\throwing.dm"
|
||||
#include "code\controllers\subsystems\ticker.dm"
|
||||
#include "code\controllers\subsystems\time_track.dm"
|
||||
@@ -448,7 +451,6 @@
|
||||
#include "code\controllers\subsystems\processing\electronics.dm"
|
||||
#include "code\controllers\subsystems\processing\fast_process.dm"
|
||||
#include "code\controllers\subsystems\processing\modifiers.dm"
|
||||
#include "code\controllers\subsystems\processing\nanoui.dm"
|
||||
#include "code\controllers\subsystems\processing\ntsl2.dm"
|
||||
#include "code\controllers\subsystems\processing\obj_tab_items.dm"
|
||||
#include "code\controllers\subsystems\processing\odyssey.dm"
|
||||
@@ -457,7 +459,6 @@
|
||||
#include "code\controllers\subsystems\processing\projectiles.dm"
|
||||
#include "code\controllers\subsystems\processing\psi.dm"
|
||||
#include "code\controllers\subsystems\processing\shuttle.dm"
|
||||
#include "code\controllers\subsystems\processing\tgui.dm"
|
||||
#include "code\datums\ai_law_sets.dm"
|
||||
#include "code\datums\ai_laws.dm"
|
||||
#include "code\datums\beam.dm"
|
||||
@@ -465,6 +466,7 @@
|
||||
#include "code\datums\callback.dm"
|
||||
#include "code\datums\cargo.dm"
|
||||
#include "code\datums\category.dm"
|
||||
#include "code\datums\chat_payload.dm"
|
||||
#include "code\datums\computerfiles.dm"
|
||||
#include "code\datums\datum.dm"
|
||||
#include "code\datums\datumvars.dm"
|
||||
@@ -3167,13 +3169,6 @@
|
||||
#include "code\modules\multiz\zmimic\mimic_common.dm"
|
||||
#include "code\modules\multiz\zmimic\mimic_movable.dm"
|
||||
#include "code\modules\multiz\zmimic\mimic_turf.dm"
|
||||
#include "code\modules\nano\nanoexternal.dm"
|
||||
#include "code\modules\nano\nanomapgen.dm"
|
||||
#include "code\modules\nano\nanoui.dm"
|
||||
#include "code\modules\nano\modules\follow_menu.dm"
|
||||
#include "code\modules\nano\modules\human_appearance.dm"
|
||||
#include "code\modules\nano\modules\nano_module.dm"
|
||||
#include "code\modules\nano\modules\narrate_panel.dm"
|
||||
#include "code\modules\ntsl2\guide.dm"
|
||||
#include "code\modules\ntsl2\ntsl2_program.dm"
|
||||
#include "code\modules\ntsl2\ntsl2_types.dm"
|
||||
@@ -3936,8 +3931,11 @@
|
||||
#include "code\modules\tgui\modules\armor_values.dm"
|
||||
#include "code\modules\tgui\modules\faction_select.dm"
|
||||
#include "code\modules\tgui\modules\flavor_text.dm"
|
||||
#include "code\modules\tgui\modules\follow_menu.dm"
|
||||
#include "code\modules\tgui\modules\hivenet_manifest.dm"
|
||||
#include "code\modules\tgui\modules\human_appearance.dm"
|
||||
#include "code\modules\tgui\modules\ipc_diagnostic.dm"
|
||||
#include "code\modules\tgui\modules\narrate_panel.dm"
|
||||
#include "code\modules\tgui\modules\neural_configuration.dm"
|
||||
#include "code\modules\tgui\states\admin.dm"
|
||||
#include "code\modules\tgui\states\always.dm"
|
||||
@@ -3960,6 +3958,8 @@
|
||||
#include "code\modules\tgui\states\self.dm"
|
||||
#include "code\modules\tgui\states\zlevel.dm"
|
||||
#include "code\modules\tgui_input\alert.dm"
|
||||
#include "code\modules\tgui_input\checkboxes.dm"
|
||||
#include "code\modules\tgui_input\keycombo.dm"
|
||||
#include "code\modules\tgui_input\list.dm"
|
||||
#include "code\modules\tgui_input\number.dm"
|
||||
#include "code\modules\tgui_input\text.dm"
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.1.2/schema.json",
|
||||
"assist": {
|
||||
"actions": {
|
||||
"source": {
|
||||
"organizeImports": "on"
|
||||
}
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
"css": {
|
||||
"formatter": {
|
||||
"quoteStyle": "double"
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": false,
|
||||
"includes": [
|
||||
"**",
|
||||
"!**/node_modules",
|
||||
"!tgui/public",
|
||||
"tgui/public/tgui.html",
|
||||
"!tgui/packages/tgui-setup/helpers.js"
|
||||
]
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "tab",
|
||||
"lineEnding": "lf"
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"indentStyle": "space",
|
||||
"quoteStyle": "single"
|
||||
}
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true,
|
||||
"a11y": "off",
|
||||
"correctness": {
|
||||
"noUnusedImports": "warn",
|
||||
"noUnusedVariables": "off",
|
||||
"useExhaustiveDependencies": "off",
|
||||
"noUnusedFunctionParameters": "off",
|
||||
"useHookAtTopLevel": "off"
|
||||
},
|
||||
"security": {
|
||||
"noDangerouslySetInnerHtml": "warn"
|
||||
},
|
||||
"style": {
|
||||
"noNonNullAssertion": "off",
|
||||
"useSelfClosingElements": "error"
|
||||
},
|
||||
"suspicious": {
|
||||
"noArrayIndexKey": "off",
|
||||
"noExplicitAny": "off",
|
||||
"noImplicitAnyLet": "off"
|
||||
}
|
||||
}
|
||||
},
|
||||
"vcs": {
|
||||
"clientKind": "git",
|
||||
"enabled": false,
|
||||
"useIgnoreFile": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.1.2",
|
||||
"prettier": "^3.6.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@biomejs/biome": ["@biomejs/biome@2.4.15", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.15", "@biomejs/cli-darwin-x64": "2.4.15", "@biomejs/cli-linux-arm64": "2.4.15", "@biomejs/cli-linux-arm64-musl": "2.4.15", "@biomejs/cli-linux-x64": "2.4.15", "@biomejs/cli-linux-x64-musl": "2.4.15", "@biomejs/cli-win32-arm64": "2.4.15", "@biomejs/cli-win32-x64": "2.4.15" }, "bin": { "biome": "bin/biome" } }, "sha512-j5VH3a/h/HXTKBM50MDMxRCzkeLv9S2XJcW2WgnZT1+xyisi+0bISrXR82gCX+8S9lvK0skEvHJRN+3Ktr2hlw=="],
|
||||
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-rF3PPqLq1yoST79zaQbDjVJwsuIeci/O+9bgNmC5QpgOqz6aqYuzA4abyAGx+mgyiDXn4A049xAN8gijbuR1Qg=="],
|
||||
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-/5KHXYMfSJs1fNXiX30xFtI8JcCFV6zaVVLxOa0M2sfqBKHkpQhRTv94yxQWxeTY2lzo2OuTlNvPC+hDQt2wcQ=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-owaAMZD/T4LrD0ELNCk0Km3qrRHuM0X6EAyVE1FSqGY0rbLoiDLrO4Us2tllm6cAeB2Ioa9C2C08NZPdr8+0Ug=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZPcxznxm0pogHBLZhYntyR3sR+MrZjqJIKEr7ZqVen0Rl+P/4upVmfYXjftizi9RoqZntg33fv/1fbdhbYXpEQ=="],
|
||||
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.15", "", { "os": "linux", "cpu": "x64" }, "sha512-0jj7THz12GbUOLmMibktK6DZjqz2zV64KFxyBtcFTKPiiOIY0a7vns1elpO1dERvxpsZ5ik0oFfz0oGwFde1+g=="],
|
||||
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.15", "", { "os": "linux", "cpu": "x64" }, "sha512-CNq/9W38SYSH023lfcQ4KKU8K0YX8T//FZUhcgtMMRABDojx5XsMV7jlweAvGSl389wJQB29Qo6Zb/a+jdvt+w=="],
|
||||
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-ouhkYdlhp/1GghEJPdWwD/Vi3gQ1nFxuSpMolWsbq3Lsq3QUR4jl6UdhhscdCugKU5vOEuMiJhvKj66O0OCq+w=="],
|
||||
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.15", "", { "os": "win32", "cpu": "x64" }, "sha512-zBrGq5mx5wwpnow4+2BxUvleDM+GNd4sLbPaMapsSLQLD0NGRCquqPBTgN+7XkUteHvj7M+BstuI8tmnV7+HgQ=="],
|
||||
|
||||
"prettier": ["prettier@3.8.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw=="],
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@
|
||||
#define COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON "atom_init_success_on"
|
||||
///from base of atom/examine(): (/mob, list/examine_text)
|
||||
#define COMSIG_ATOM_EXAMINE "atom_examine"
|
||||
///from base of atom/examine_tags(): (/mob, list/examine_tags)
|
||||
#define COMSIG_ATOM_EXAMINE_TAGS "atom_examine_tags"
|
||||
///from base of atom/Entered(): (atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
||||
#define COMSIG_ATOM_ENTERED "atom_entered"
|
||||
/// Sent from the atom that just Entered src. From base of atom/Entered(): (/atom/destination, atom/old_loc, list/atom/old_locs)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
/// Window is fully visible and we can make fragile calls
|
||||
#define COMSIG_TGUI_WINDOW_VISIBLE "tgui_window_visible"
|
||||
@@ -24,6 +24,7 @@
|
||||
#define SPAN_COLOR(color, str) SPAN_STYLE("color: [color]", "[str]")
|
||||
#define SPAN_CAUTION(str) ("<span class='caution'>" + str + "</span>")
|
||||
#define SPAN_STORYTELLER(str) ("<span class='storyteller'>" + str + "</span>")
|
||||
#define SPAN_BOLDANNOUNCE(str) ("<span class='boldannounce'>" + str + "</span>")
|
||||
|
||||
#define SPAN_MACHINE_WARNING(str) ("<span class='machine-warning'>" + str + "</span>")
|
||||
#define SPAN_MACHINE_DANGER(str) ("<span class='machine-danger'>" + str + "</span>")
|
||||
@@ -42,6 +43,9 @@
|
||||
#define SPAN_BOLDNOTICE(str) ("<span class='boldnotice'>" + str + "</span>")
|
||||
#define SPAN_BOLDWARNING(X) "<span class='boldwarning'>[X]</span>"
|
||||
|
||||
// Spans that use embedded tgui components:
|
||||
#define SPAN_TOOLTIP(tip, main_text) ("<span data-component=\"Tooltip\" data-content=\"" + tip + "\" class=\"tooltip\">" + main_text + "</span>")
|
||||
|
||||
/*
|
||||
#####################
|
||||
Font sizes
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
#define SS_PRIORITY_STATPANELS 25 // Statpanels.
|
||||
#define SS_PRIORITY_LIGHTING 25 // Queued lighting engine updates.
|
||||
#define SS_PRIORITY_MACHINERY 25 // Machinery + powernet ticks.
|
||||
#define SS_PRIORITY_NANOUI 25 // UI updates.
|
||||
#define SS_PRIORITY_ELECTRONICS 20 // Integrated Electronics processing.
|
||||
#define SS_PRIORITY_CALAMITY 20 // Singularity, Tesla, Nar'sie, blob, etc.
|
||||
#define SS_PRIORITY_EVENT 20
|
||||
|
||||
@@ -277,6 +277,7 @@
|
||||
#define FIRE_PRIORITY_SPACEDRIFT 30
|
||||
#define FIRE_PRIORITY_DEFAULT 50
|
||||
#define FIRE_PRIORITY_MOBS 100
|
||||
#define FIRE_PRIORITY_TGUI 110
|
||||
#define FIRE_PRIORITY_STATPANEL 390
|
||||
#define FIRE_PRIORITY_CHAT 400
|
||||
#define FIRE_PRIORITY_RUNECHAT 410
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
#define CHAT_MESSAGE_WIDTH 112
|
||||
|
||||
// Setting this much higher than 1024 could allow spammers to DOS the server easily.
|
||||
#define MAX_MESSAGE_LEN 1024
|
||||
#define MAX_MESSAGE_LEN 4096
|
||||
#define MAX_PAPER_MESSAGE_LEN 3072
|
||||
#define MAX_BOOK_MESSAGE_LEN 9216
|
||||
#define MAX_LNAME_LEN 64
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#define TGS_EXTERNAL_CONFIGURATION
|
||||
#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) GLOBAL_VAR_INIT(##Name, ##Value); GLOBAL_PROTECT(##Name)
|
||||
#define TGS_READ_GLOBAL(Name) GLOB.##Name
|
||||
#define TGS_WRITE_GLOBAL(Name, Value) GLOB.##Name = ##Value
|
||||
#define TGS_WORLD_ANNOUNCE(message) to_chat(world, SPAN_BOLDANNOUNCE("[html_encode(##message)]"))
|
||||
#define TGS_INFO_LOG(message) log_world("TGS Info: [##message]")
|
||||
#define TGS_WARNING_LOG(message) log_world("TGS Warn: [##message]")
|
||||
#define TGS_ERROR_LOG(message) stack_trace("TGS Error: [##message]")
|
||||
#define TGS_NOTIFY_ADMINS(event) message_admins(##event)
|
||||
#define TGS_CLIENT_COUNT GLOB.clients.len
|
||||
#define TGS_PROTECT_DATUM(Path) GENERAL_PROTECT_DATUM(##Path)
|
||||
+132
-44
@@ -1,55 +1,68 @@
|
||||
// tgstation-server DMAPI
|
||||
// The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in IETF RFC 2119.
|
||||
|
||||
#define TGS_DMAPI_VERSION "6.6.2"
|
||||
#define TGS_DMAPI_VERSION "7.3.3"
|
||||
|
||||
// All functions and datums outside this document are subject to change with any version and should not be relied on.
|
||||
|
||||
// CONFIGURATION
|
||||
|
||||
/// Create this define if you want to do TGS configuration outside of this file.
|
||||
/// Consumers SHOULD create this define if you want to do TGS configuration outside of this file.
|
||||
#ifndef TGS_EXTERNAL_CONFIGURATION
|
||||
|
||||
// Comment this out once you've filled in the below.
|
||||
//#error TGS API unconfigured
|
||||
// Consumers MUST comment this out once you've filled in the below and are not using [TGS_EXTERNAL_CONFIGURATION].
|
||||
// #error TGS API unconfigured
|
||||
|
||||
// Uncomment this if you wish to allow the game to interact with TGS 3..
|
||||
// Consumers MUST uncomment this if you wish to allow the game to interact with TGS version 3.
|
||||
// This will raise the minimum required security level of your game to TGS_SECURITY_TRUSTED due to it utilizing call()().
|
||||
//#define TGS_V3_API
|
||||
|
||||
// Required interfaces (fill in with your codebase equivalent):
|
||||
|
||||
/// Create a global variable named `Name` and set it to `Value`.
|
||||
#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) var/global/##Name = ##Value
|
||||
#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value)
|
||||
|
||||
/// Read the value in the global variable `Name`.
|
||||
#define TGS_READ_GLOBAL(Name) global.##Name
|
||||
#define TGS_READ_GLOBAL(Name)
|
||||
|
||||
/// Set the value in the global variable `Name` to `Value`.
|
||||
#define TGS_WRITE_GLOBAL(Name, Value) global.##Name = ##Value
|
||||
#define TGS_WRITE_GLOBAL(Name, Value)
|
||||
|
||||
/// Disallow ANYONE from reflecting a given `path`, security measure to prevent in-game use of DD -> TGS capabilities.
|
||||
#define TGS_PROTECT_DATUM(Path)
|
||||
|
||||
/// Display an announcement `message` from the server to all players.
|
||||
#define TGS_WORLD_ANNOUNCE(message) world << "<b>TGS Notification</b>: [html_encode(##message)]"
|
||||
#define TGS_WORLD_ANNOUNCE(message)
|
||||
|
||||
/// Notify current in-game administrators of a string `event`.
|
||||
#define TGS_NOTIFY_ADMINS(event) message_admins(##event)
|
||||
#define TGS_NOTIFY_ADMINS(event)
|
||||
|
||||
/// Write an info `message` to a server log.
|
||||
#define TGS_INFO_LOG(message) log_tgs("[##message]")
|
||||
#define TGS_INFO_LOG(message)
|
||||
|
||||
/// Write an warning `message` to a server log.
|
||||
#define TGS_WARNING_LOG(message) log_tgs("[##message]", SEVERITY_WARNING)
|
||||
#define TGS_WARNING_LOG(message)
|
||||
|
||||
/// Write an error `message` to a server log.
|
||||
#define TGS_ERROR_LOG(message) log_tgs("[##message]", SEVERITY_ERROR)
|
||||
#define TGS_ERROR_LOG(message)
|
||||
|
||||
/// Get the number of connected /clients.
|
||||
#define TGS_CLIENT_COUNT clients.len
|
||||
#define TGS_CLIENT_COUNT
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef TGS_FILE2TEXT_NATIVE
|
||||
#ifdef file2text
|
||||
#error Your codebase is re-defining the BYOND proc file2text. The DMAPI requires the native version to read the result of world.Export(). You SHOULD fix this by adding "#define TGS_FILE2TEXT_NATIVE file2text" before your override of file2text to allow the DMAPI to use the native version. This will only be used for world.Export(), not regular file accesses
|
||||
#endif
|
||||
#define TGS_FILE2TEXT_NATIVE file2text
|
||||
#endif
|
||||
|
||||
// SpacemanDMM compatibility
|
||||
#ifndef CAN_BE_REDEFINED
|
||||
#define CAN_BE_REDEFINED(X)
|
||||
#endif
|
||||
|
||||
// EVENT CODES
|
||||
|
||||
/// Before a reboot mode change, extras parameters are the current and new reboot mode enums.
|
||||
@@ -73,12 +86,12 @@
|
||||
#define TGS_EVENT_REPO_MERGE_PULL_REQUEST 3
|
||||
/// Before the repository makes a sychronize operation. Parameters: Absolute repostiory path.
|
||||
#define TGS_EVENT_REPO_PRE_SYNCHRONIZE 4
|
||||
/// Before a BYOND install operation begins. Parameters: [/datum/tgs_version] of the installing BYOND.
|
||||
#define TGS_EVENT_BYOND_INSTALL_START 5
|
||||
/// When a BYOND install operation fails. Parameters: Error message
|
||||
#define TGS_EVENT_BYOND_INSTALL_FAIL 6
|
||||
/// When the active BYOND version changes. Parameters: (Nullable) [/datum/tgs_version] of the current BYOND, [/datum/tgs_version] of the new BYOND.
|
||||
#define TGS_EVENT_BYOND_ACTIVE_VERSION_CHANGE 7
|
||||
/// Before a engine install operation begins. Parameters: Version string of the installing engine.
|
||||
#define TGS_EVENT_ENGINE_INSTALL_START 5
|
||||
/// When a engine install operation fails. Parameters: Error message
|
||||
#define TGS_EVENT_ENGINE_INSTALL_FAIL 6
|
||||
/// When the active engine version changes. Parameters: (Nullable) Version string of the current engine, version string of the new engine.
|
||||
#define TGS_EVENT_ENGINE_ACTIVE_VERSION_CHANGE 7
|
||||
/// When the compiler starts running. Parameters: Game directory path, origin commit SHA.
|
||||
#define TGS_EVENT_COMPILE_START 8
|
||||
/// When a compile is cancelled. No parameters.
|
||||
@@ -108,7 +121,7 @@
|
||||
// #define TGS_EVENT_DREAM_DAEMON_LAUNCH 22
|
||||
/// After a single submodule update is performed. Parameters: Updated submodule name.
|
||||
#define TGS_EVENT_REPO_SUBMODULE_UPDATE 23
|
||||
/// After CodeModifications are applied, before DreamMaker is run. Parameters: Game directory path, origin commit sha, byond version.
|
||||
/// After CodeModifications are applied, before DreamMaker is run. Parameters: Game directory path, origin commit sha, version string of the used engine.
|
||||
#define TGS_EVENT_PRE_DREAM_MAKER 24
|
||||
/// Whenever a deployment folder is deleted from disk. Parameters: Game directory path.
|
||||
#define TGS_EVENT_DEPLOYMENT_CLEANUP 25
|
||||
@@ -122,6 +135,7 @@
|
||||
/// The watchdog will restart on reboot.
|
||||
#define TGS_REBOOT_MODE_RESTART 2
|
||||
|
||||
// Note that security levels are currently meaningless in OpenDream
|
||||
/// DreamDaemon Trusted security level.
|
||||
#define TGS_SECURITY_TRUSTED 0
|
||||
/// DreamDaemon Safe security level.
|
||||
@@ -136,20 +150,26 @@
|
||||
/// DreamDaemon invisible visibility level.
|
||||
#define TGS_VISIBILITY_INVISIBLE 2
|
||||
|
||||
/// The Build Your Own Net Dream engine.
|
||||
#define TGS_ENGINE_TYPE_BYOND 0
|
||||
/// The OpenDream engine.
|
||||
#define TGS_ENGINE_TYPE_OPENDREAM 1
|
||||
|
||||
//REQUIRED HOOKS
|
||||
|
||||
/**
|
||||
* Call this somewhere in [/world/proc/New] that is always run. This function may sleep!
|
||||
* Consumers MUST call this somewhere in [/world/proc/New] that is always run. This function may sleep!
|
||||
*
|
||||
* * event_handler - Optional user defined [/datum/tgs_event_handler].
|
||||
* * minimum_required_security_level: The minimum required security level to run the game in which the DMAPI is integrated. Can be one of [TGS_SECURITY_ULTRASAFE], [TGS_SECURITY_SAFE], or [TGS_SECURITY_TRUSTED].
|
||||
* * http_handler - Optional user defined [/datum/tgs_http_handler].
|
||||
*/
|
||||
/world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE)
|
||||
/world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE, datum/tgs_http_handler/http_handler)
|
||||
CAN_BE_REDEFINED(TRUE)
|
||||
return
|
||||
|
||||
/**
|
||||
* Call this when your initializations are complete and your game is ready to play before any player interactions happen.
|
||||
* Consumers MUST call this when world initializations are complete and the game is ready to play before any player interactions happen.
|
||||
*
|
||||
* This may use [/world/var/sleep_offline] to make this happen so ensure no changes are made to it while this call is running.
|
||||
* Afterwards, consider explicitly setting it to what you want to avoid this BYOND bug: http://www.byond.com/forum/post/2575184
|
||||
@@ -159,12 +179,10 @@
|
||||
CAN_BE_REDEFINED(TRUE)
|
||||
return
|
||||
|
||||
/// Put this at the start of [/world/proc/Topic].
|
||||
/// Consumers MUST run this macro at the start of [/world/proc/Topic].
|
||||
#define TGS_TOPIC var/tgs_topic_return = TgsTopic(args[1]); if(tgs_topic_return) return tgs_topic_return
|
||||
|
||||
/**
|
||||
* Call this as late as possible in [world/proc/Reboot] (BEFORE ..()).
|
||||
*/
|
||||
/// Consumers MUST call this as late as possible in [world/proc/Reboot] (BEFORE ..()).
|
||||
/world/proc/TgsReboot()
|
||||
CAN_BE_REDEFINED(TRUE)
|
||||
return
|
||||
@@ -261,7 +279,7 @@
|
||||
/// The [/datum/tgs_chat_channel] the user was from.
|
||||
var/datum/tgs_chat_channel/channel
|
||||
|
||||
/// User definable handler for TGS events.
|
||||
/// User definable handler for TGS events This abstract version SHOULD be overridden to be used.
|
||||
/datum/tgs_event_handler
|
||||
/// If the handler receieves [TGS_EVENT_HEALTH_CHECK] events.
|
||||
var/receive_health_checks = FALSE
|
||||
@@ -275,7 +293,41 @@
|
||||
set waitfor = FALSE
|
||||
return
|
||||
|
||||
/// User definable chat command.
|
||||
/// User definable handler for HTTP calls. This abstract version MUST be overridden to be used.
|
||||
/datum/tgs_http_handler
|
||||
|
||||
/**
|
||||
* User definable callback for executing HTTP GET requests.
|
||||
* MUST perform BYOND sleeps while the request is in flight.
|
||||
* MUST return a [/datum/tgs_http_result].
|
||||
* SHOULD log its own errors
|
||||
*
|
||||
* url - The full URL to execute the GET request for including query parameters.
|
||||
*/
|
||||
/datum/tgs_http_handler/proc/PerformGet(url)
|
||||
CRASH("[type]/PerformGet not implemented!")
|
||||
|
||||
/// Result of a [/datum/tgs_http_handler] call. MUST NOT be overridden.
|
||||
/datum/tgs_http_result
|
||||
/// HTTP response as text
|
||||
var/response_text
|
||||
/// Boolean request success flag. Set for any 2XX response code.
|
||||
var/success
|
||||
|
||||
/**
|
||||
* Create a [/datum/tgs_http_result].
|
||||
*
|
||||
* * response_text - HTTP response as text. Must be provided in New().
|
||||
* * success - Boolean request success flag. Set for any 2XX response code. Must be provided in New().
|
||||
*/
|
||||
/datum/tgs_http_result/New(response_text, success)
|
||||
if(response_text && !istext(response_text))
|
||||
CRASH("response_text was not text!")
|
||||
|
||||
src.response_text = response_text
|
||||
src.success = success
|
||||
|
||||
/// User definable chat command. This abstract version MUST be overridden to be used.
|
||||
/datum/tgs_chat_command
|
||||
/// The string to trigger this command on a chat bot. e.g `@bot name ...` or `!tgs name ...`.
|
||||
var/name = ""
|
||||
@@ -288,29 +340,36 @@
|
||||
|
||||
/**
|
||||
* Process command activation. Should return a [/datum/tgs_message_content] to respond to the issuer with.
|
||||
* MUST be implemented
|
||||
*
|
||||
* sender - The [/datum/tgs_chat_user] who issued the command.
|
||||
* params - The trimmed string following the command `/datum/tgs_chat_command/var/name].
|
||||
* * sender - The [/datum/tgs_chat_user] who issued the command.
|
||||
* * params - The trimmed string following the command `/datum/tgs_chat_command/var/name].
|
||||
*/
|
||||
/datum/tgs_chat_command/proc/Run(datum/tgs_chat_user/sender, params)
|
||||
CRASH("[type] has no implementation for Run()")
|
||||
|
||||
/// User definable chat message.
|
||||
/// User definable chat message. MUST NOT be overridden.
|
||||
/datum/tgs_message_content
|
||||
/// The tring content of the message. Must be provided in New().
|
||||
/// The string content of the message. Must be provided in New().
|
||||
var/text
|
||||
|
||||
/// The [/datum/tgs_chat_embed] to embed in the message. Not supported on all chat providers.
|
||||
var/datum/tgs_chat_embed/structure/embed
|
||||
|
||||
/**
|
||||
* Create a [/datum/tgs_message_content].
|
||||
*
|
||||
* * text - The string content of the message.
|
||||
*/
|
||||
/datum/tgs_message_content/New(text)
|
||||
..()
|
||||
if(!istext(text))
|
||||
TGS_ERROR_LOG("[/datum/tgs_message_content] created with no text!")
|
||||
text = null
|
||||
|
||||
src.text = text
|
||||
|
||||
/// User definable chat embed. Currently mirrors Discord chat embeds. See https://discord.com/developers/docs/resources/channel#embed-object-embed-structure for details.
|
||||
/// User definable chat embed. Currently mirrors Discord chat embeds. See https://discord.com/developers/docs/resources/message#embed-object for details.
|
||||
/datum/tgs_chat_embed/structure
|
||||
var/title
|
||||
var/description
|
||||
@@ -322,13 +381,13 @@
|
||||
/// Colour must be #AARRGGBB or #RRGGBB hex string.
|
||||
var/colour
|
||||
|
||||
/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-image-structure for details.
|
||||
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-image-structure for details.
|
||||
var/datum/tgs_chat_embed/media/image
|
||||
|
||||
/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-thumbnail-structure for details.
|
||||
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-thumbnail-structure for details.
|
||||
var/datum/tgs_chat_embed/media/thumbnail
|
||||
|
||||
/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-image-structure for details.
|
||||
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-video-structure for details.
|
||||
var/datum/tgs_chat_embed/media/video
|
||||
|
||||
var/datum/tgs_chat_embed/footer/footer
|
||||
@@ -337,7 +396,7 @@
|
||||
|
||||
var/list/datum/tgs_chat_embed/field/fields
|
||||
|
||||
/// Common datum for similar discord embed medias.
|
||||
/// Common datum for similar Discord embed medias.
|
||||
/datum/tgs_chat_embed/media
|
||||
/// Must be set in New().
|
||||
var/url
|
||||
@@ -345,48 +404,58 @@
|
||||
var/height
|
||||
var/proxy_url
|
||||
|
||||
/// Create a [/datum/tgs_chat_embed].
|
||||
/datum/tgs_chat_embed/media/New(url)
|
||||
..()
|
||||
if(!istext(url))
|
||||
CRASH("[/datum/tgs_chat_embed/media] created with no url!")
|
||||
|
||||
src.url = url
|
||||
|
||||
/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-footer-structure for details.
|
||||
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-footer-structure for details.
|
||||
/datum/tgs_chat_embed/footer
|
||||
/// Must be set in New().
|
||||
var/text
|
||||
var/icon_url
|
||||
var/proxy_icon_url
|
||||
|
||||
/// Create a [/datum/tgs_chat_embed/footer].
|
||||
/datum/tgs_chat_embed/footer/New(text)
|
||||
..()
|
||||
if(!istext(text))
|
||||
CRASH("[/datum/tgs_chat_embed/footer] created with no text!")
|
||||
|
||||
src.text = text
|
||||
|
||||
/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-provider-structure for details.
|
||||
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-provider-structure for details.
|
||||
/datum/tgs_chat_embed/provider
|
||||
var/name
|
||||
var/url
|
||||
|
||||
/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-author-structure for details. Must have name set in New().
|
||||
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-author-structure for details. Must have name set in New().
|
||||
/datum/tgs_chat_embed/provider/author
|
||||
var/icon_url
|
||||
var/proxy_icon_url
|
||||
|
||||
/// Create a [/datum/tgs_chat_embed/footer].
|
||||
/datum/tgs_chat_embed/provider/author/New(name)
|
||||
..()
|
||||
if(!istext(name))
|
||||
CRASH("[/datum/tgs_chat_embed/provider/author] created with no name!")
|
||||
|
||||
src.name = name
|
||||
|
||||
/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-field-structure for details. Must have name and value set in New().
|
||||
/// See https://discord.com/developers/docs/resources/message#embed-object-embed-field-structure for details.
|
||||
/datum/tgs_chat_embed/field
|
||||
/// Must be set in New().
|
||||
var/name
|
||||
/// Must be set in New().
|
||||
var/value
|
||||
var/is_inline
|
||||
|
||||
/// Create a [/datum/tgs_chat_embed/field].
|
||||
/datum/tgs_chat_embed/field/New(name, value)
|
||||
..()
|
||||
if(!istext(name))
|
||||
CRASH("[/datum/tgs_chat_embed/field] created with no name!")
|
||||
|
||||
@@ -429,6 +498,7 @@
|
||||
|
||||
/**
|
||||
* Send a message to connected chats. This function may sleep!
|
||||
* If TGS is offline when called, the message may be placed in a queue to be sent and this function will return immediately. Your message will be sent when TGS reconnects to the game.
|
||||
*
|
||||
* message - The [/datum/tgs_message_content] to send.
|
||||
* admin_only: If [TRUE], message will be sent to admin connected chats. Vice-versa applies.
|
||||
@@ -439,6 +509,7 @@
|
||||
|
||||
/**
|
||||
* Send a private message to a specific user. This function may sleep!
|
||||
* If TGS is offline when called, the message may be placed in a queue to be sent and this function will return immediately. Your message will be sent when TGS reconnects to the game.
|
||||
*
|
||||
* message - The [/datum/tgs_message_content] to send.
|
||||
* user: The [/datum/tgs_chat_user] to PM.
|
||||
@@ -449,6 +520,7 @@
|
||||
|
||||
/**
|
||||
* Send a message to connected chats that are flagged as game-related in TGS. This function may sleep!
|
||||
* If TGS is offline when called, the message may be placed in a queue to be sent and this function will return immediately. Your message will be sent when TGS reconnects to the game.
|
||||
*
|
||||
* message - The [/datum/tgs_message_content] to send.
|
||||
* channels - Optional list of [/datum/tgs_chat_channel]s to restrict the message to.
|
||||
@@ -462,6 +534,11 @@
|
||||
CAN_BE_REDEFINED(TRUE)
|
||||
return
|
||||
|
||||
/// Returns the running engine type
|
||||
/world/proc/TgsEngine()
|
||||
CAN_BE_REDEFINED(TRUE)
|
||||
return
|
||||
|
||||
/// Returns the current [/datum/tgs_version] of the DMAPI being used if it was activated, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping!
|
||||
/world/proc/TgsApiVersion()
|
||||
CAN_BE_REDEFINED(TRUE)
|
||||
@@ -497,10 +574,21 @@
|
||||
CAN_BE_REDEFINED(TRUE)
|
||||
return
|
||||
|
||||
/**
|
||||
* Trigger an event in TGS. Requires TGS version >= 6.3.0. Returns [TRUE] if the event was triggered successfully, [FALSE] otherwise. This function may sleep!
|
||||
*
|
||||
* event_name - The name of the event to trigger
|
||||
* parameters - Optional list of string parameters to pass as arguments to the event script. The first parameter passed to a script will always be the running game's directory followed by these parameters.
|
||||
* wait_for_completion - If set, this function will not return until the event has run to completion.
|
||||
*/
|
||||
/world/proc/TgsTriggerEvent(event_name, list/parameters, wait_for_completion = FALSE)
|
||||
CAN_BE_REDEFINED(TRUE)
|
||||
return
|
||||
|
||||
/*
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2017-2023 Jordan Brown
|
||||
Copyright (c) 2017-2024 Jordan Brown
|
||||
|
||||
Permission is hereby granted, free of charge,
|
||||
to any person obtaining a copy of this software and
|
||||
|
||||
@@ -24,6 +24,16 @@
|
||||
/// Window is free and ready to receive data
|
||||
#define TGUI_WINDOW_READY 2
|
||||
|
||||
/// Though not the maximum renderable ByondUis within tgui, this is the maximum that the server will manage per-UI
|
||||
#define TGUI_MANAGED_BYONDUI_LIMIT 10
|
||||
|
||||
// These are defines instead of being inline, as they're being sent over
|
||||
// from tgui-core, so can't be easily played with
|
||||
#define TGUI_MANAGED_BYONDUI_TYPE_RENDER "renderByondUi"
|
||||
#define TGUI_MANAGED_BYONDUI_TYPE_UNMOUNT "unmountByondUi"
|
||||
|
||||
#define TGUI_MANAGED_BYONDUI_PAYLOAD_ID "renderByondUi"
|
||||
|
||||
/// Get a window id based on the provided pool index
|
||||
#define TGUI_WINDOW_ID(index) "tgui-window-[index]"
|
||||
/// Get a pool index of the provided window id
|
||||
|
||||
@@ -912,3 +912,31 @@
|
||||
/proc/endswith(input_text, ending)
|
||||
var/input_length = LAZYLEN(ending)
|
||||
return !!findtext(input_text, ending, -input_length)
|
||||
|
||||
///Returns a string based on the weight class define used as argument
|
||||
/proc/weight_class_to_text(w_class)
|
||||
switch(w_class)
|
||||
if(WEIGHT_CLASS_TINY)
|
||||
. = "tiny"
|
||||
if(WEIGHT_CLASS_SMALL)
|
||||
. = "small"
|
||||
if(WEIGHT_CLASS_NORMAL)
|
||||
. = "normal-sized"
|
||||
if(WEIGHT_CLASS_BULKY)
|
||||
. = "bulky"
|
||||
if(WEIGHT_CLASS_HUGE)
|
||||
. = "huge"
|
||||
if(WEIGHT_CLASS_GIGANTIC)
|
||||
. = "gigantic"
|
||||
else
|
||||
. = ""
|
||||
|
||||
/proc/weight_class_to_tooltip(w_class)
|
||||
switch(w_class)
|
||||
if(WEIGHT_CLASS_TINY to WEIGHT_CLASS_SMALL)
|
||||
return "This item can fit into pockets, boxes and backpacks."
|
||||
if(WEIGHT_CLASS_NORMAL)
|
||||
return "This item can fit into backpacks."
|
||||
if(WEIGHT_CLASS_BULKY to WEIGHT_CLASS_GIGANTIC)
|
||||
return "This item is too large to fit into any standard storage."
|
||||
return ""
|
||||
|
||||
@@ -10,31 +10,82 @@ SUBSYSTEM_DEF(chat)
|
||||
flags = SS_TICKER | SS_NO_INIT
|
||||
wait = 1
|
||||
priority = FIRE_PRIORITY_CHAT
|
||||
init_order = INIT_ORDER_CHAT
|
||||
init_order = INITSTAGE_MAX
|
||||
|
||||
var/list/payload_by_client = list()
|
||||
/// Assosciates a ckey with a list of messages to send to them.
|
||||
var/list/list/datum/chat_payload/client_to_payloads = list()
|
||||
|
||||
/// Associates a ckey with an assosciative list of their last CHAT_RELIABILITY_HISTORY_SIZE messages.
|
||||
var/list/list/datum/chat_payload/client_to_reliability_history = list()
|
||||
|
||||
/// Assosciates a ckey with their next sequence number.
|
||||
var/list/client_to_sequence_number = list()
|
||||
|
||||
/datum/controller/subsystem/chat/proc/generate_payload(client/target, message_data)
|
||||
var/sequence = client_to_sequence_number[target.ckey]
|
||||
client_to_sequence_number[target.ckey] += 1
|
||||
|
||||
var/datum/chat_payload/payload = new
|
||||
payload.sequence = sequence
|
||||
payload.content = message_data
|
||||
|
||||
if(!(target.ckey in client_to_reliability_history))
|
||||
client_to_reliability_history[target.ckey] = list()
|
||||
var/list/client_history = client_to_reliability_history[target.ckey]
|
||||
client_history["[sequence]"] = payload
|
||||
|
||||
if(length(client_history) > 5)
|
||||
var/oldest = text2num(client_history[1])
|
||||
for(var/index in 2 to length(client_history))
|
||||
var/test = text2num(client_history[index])
|
||||
if(test < oldest)
|
||||
oldest = test
|
||||
client_history -= "[oldest]"
|
||||
return payload
|
||||
|
||||
/datum/controller/subsystem/chat/proc/send_payload_to_client(client/target, datum/chat_payload/payload)
|
||||
target.tgui_panel.window.send_message("chat/message", payload.into_message())
|
||||
SEND_TEXT(target, payload.get_content_as_html())
|
||||
|
||||
/datum/controller/subsystem/chat/fire()
|
||||
for(var/key in payload_by_client)
|
||||
var/client/client = key
|
||||
var/payload = payload_by_client[key]
|
||||
payload_by_client -= key
|
||||
if(client)
|
||||
// Send to tgchat
|
||||
client.tgui_panel?.window.send_message("chat/message", payload)
|
||||
// Send to old chat
|
||||
for(var/message in payload)
|
||||
to_target(client, message_to_html(message))
|
||||
for(var/ckey in client_to_payloads)
|
||||
var/client/target = GLOB.directory[ckey]
|
||||
if(isnull(target)) // verify client still exists
|
||||
LAZYREMOVE(client_to_payloads, ckey)
|
||||
continue
|
||||
|
||||
for(var/datum/chat_payload/payload as anything in client_to_payloads[ckey])
|
||||
send_payload_to_client(target, payload)
|
||||
LAZYREMOVE(client_to_payloads, ckey)
|
||||
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
/datum/controller/subsystem/chat/proc/queue(target, message)
|
||||
if(islist(target))
|
||||
for(var/_target in target)
|
||||
var/client/client = CLIENT_FROM_VAR(_target)
|
||||
if(client)
|
||||
LAZYADD(payload_by_client[client], list(message))
|
||||
/datum/controller/subsystem/chat/proc/queue(queue_target, list/message_data)
|
||||
var/list/targets = islist(queue_target) ? queue_target : list(queue_target)
|
||||
for(var/target in targets)
|
||||
var/client/client = CLIENT_FROM_VAR(target)
|
||||
if(isnull(client))
|
||||
continue
|
||||
LAZYADDASSOCLIST(client_to_payloads, client.ckey, generate_payload(client, message_data))
|
||||
|
||||
/datum/controller/subsystem/chat/proc/send_immediate(send_target, list/message_data)
|
||||
var/list/targets = islist(send_target) ? send_target : list(send_target)
|
||||
for(var/target in targets)
|
||||
var/client/client = CLIENT_FROM_VAR(target)
|
||||
if(isnull(client))
|
||||
continue
|
||||
send_payload_to_client(client, generate_payload(client, message_data))
|
||||
|
||||
/datum/controller/subsystem/chat/proc/handle_resend(client/client, sequence)
|
||||
var/list/client_history = client_to_reliability_history[client.ckey]
|
||||
sequence = "[sequence]"
|
||||
if(isnull(client_history) || !(sequence in client_history))
|
||||
return
|
||||
var/client/client = CLIENT_FROM_VAR(target)
|
||||
if(client)
|
||||
LAZYADD(payload_by_client[client], list(message))
|
||||
|
||||
var/datum/chat_payload/payload = client_history[sequence]
|
||||
if(payload.resends > 3)
|
||||
return // we tried but byond said no
|
||||
|
||||
payload.resends += 1
|
||||
send_payload_to_client(client, client_history[sequence])
|
||||
|
||||
@@ -50,12 +50,16 @@ SUBSYSTEM_DEF(nightlight)
|
||||
/datum/controller/subsystem/nightlight/proc/get_apc_list(var/whitelisted_only = 1)
|
||||
var/list/obj/structure/machinery/power/apc/lighting_apcs = list()
|
||||
|
||||
for (var/A in get_sorted_areas())
|
||||
var/area/B = A
|
||||
if (B.no_light_control || (!(B.allow_nightmode) && whitelisted_only))
|
||||
for (var/obj/structure/machinery/power/apc/APC in SSmachinery.apc_units)
|
||||
if (QDELETED(APC) || APC.aidisabled)
|
||||
continue
|
||||
if (B.apc && !B.apc.aidisabled)
|
||||
lighting_apcs += B.apc
|
||||
|
||||
var/area/apc_area = APC.area || get_area(APC)
|
||||
if (!apc_area)
|
||||
continue
|
||||
if (apc_area.no_light_control || (!(apc_area.allow_nightmode) && whitelisted_only))
|
||||
continue
|
||||
lighting_apcs += APC
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
|
||||
@@ -1,241 +0,0 @@
|
||||
PROCESSING_SUBSYSTEM_DEF(nanoui)
|
||||
// Subsystem stuff.
|
||||
name = "NanoUI"
|
||||
flags = SS_NO_INIT
|
||||
priority = SS_PRIORITY_NANOUI
|
||||
stat_tag = "A"
|
||||
|
||||
// NanoUI stuff.
|
||||
var/list/open_nanouis = list()
|
||||
|
||||
/**
|
||||
* Get an open /nanoui ui for the current user, src_object and ui_key and try to update it with data
|
||||
*
|
||||
* * user - /mob The mob who opened/owns the ui
|
||||
* * src_object - /obj|/mob The obj or mob which the ui belongs to
|
||||
* * ui_key - A string key used for the ui
|
||||
* * data - List, the data to be passed to the ui, if it exists
|
||||
* * force_open - Boolean, the ui is being forced to (re)open, so close ui if it exists (instead of updating)
|
||||
*
|
||||
* Returns the `/nanoui` found ui, for null if none exists
|
||||
*/
|
||||
/datum/controller/subsystem/processing/nanoui/proc/try_update_ui(mob/user, src_object, ui_key, datum/nanoui/ui, data, force_open = FALSE)
|
||||
if (!ui) // no ui has been passed, so we'll search for one
|
||||
ui = get_open_ui(user, src_object, ui_key)
|
||||
|
||||
if (ui)
|
||||
// The UI is already open
|
||||
if (!force_open)
|
||||
ui.push_data(data)
|
||||
return ui
|
||||
else
|
||||
ui.reinitialise(new_initial_data=data)
|
||||
return ui
|
||||
|
||||
return null
|
||||
|
||||
/**
|
||||
* Get an open /nanoui ui for the current user, src_object and ui_key
|
||||
*
|
||||
* * user - The `/mob` who opened/owns the ui
|
||||
* * src_object - The `/obj` or `/mob` which the ui belongs to
|
||||
* * ui_key - A string key used for the ui
|
||||
*/
|
||||
/datum/controller/subsystem/processing/nanoui/proc/get_open_ui(mob/user, src_object, ui_key)
|
||||
var/src_object_key = REF(src_object)
|
||||
if (!LAZYLEN(open_nanouis[src_object_key]) || !LAZYLEN(open_nanouis[src_object_key][ui_key]))
|
||||
return null
|
||||
|
||||
for (var/datum/nanoui/ui in open_nanouis[src_object_key][ui_key])
|
||||
if (ui.user == user)
|
||||
return ui
|
||||
|
||||
//testing("nanomanager/get_open_ui mob [user.name] [src_object:name] [ui_key] - ui not found")
|
||||
return null
|
||||
|
||||
/**
|
||||
* Update all `/nanoui` uis attached to src_object
|
||||
*
|
||||
* * src_object - The `/obj` or `/mob` which the uis are attached to
|
||||
*
|
||||
* Returns the number of UIs updated
|
||||
*/
|
||||
/datum/controller/subsystem/processing/nanoui/proc/update_uis(src_object)
|
||||
var/src_object_key = REF(src_object)
|
||||
if (!LAZYLEN(open_nanouis[src_object_key]))
|
||||
return 0
|
||||
|
||||
. = 0
|
||||
var/list/obj_uis = open_nanouis[src_object_key]
|
||||
for (var/ui_key in obj_uis)
|
||||
for (var/thing in obj_uis[ui_key])
|
||||
var/datum/nanoui/ui = thing
|
||||
if(ui && ui.src_object && ui.user && ui.src_object.ui_host())
|
||||
ui.process(1)
|
||||
.++
|
||||
|
||||
/**
|
||||
* Close all `/nanoui` uis attached to src_object
|
||||
*
|
||||
* * src_object - The `/obj` or `/mob` which the uis are attached to
|
||||
*
|
||||
* Returns the number of UIs closed
|
||||
*/
|
||||
/datum/controller/subsystem/processing/nanoui/proc/close_uis(src_object)
|
||||
var/src_object_key = REF(src_object)
|
||||
if (!open_nanouis[src_object_key] || !islist(open_nanouis[src_object_key]))
|
||||
return 0
|
||||
|
||||
. = 0
|
||||
var/list/obj_uis = open_nanouis[src_object_key]
|
||||
for (var/ui_key in obj_uis)
|
||||
for (var/thing in obj_uis[ui_key])
|
||||
var/datum/nanoui/ui = thing
|
||||
if(ui && ui.src_object && ui.user && ui.src_object.ui_host())
|
||||
ui.close()
|
||||
.++
|
||||
|
||||
/**
|
||||
* Update /nanoui uis belonging to user
|
||||
*
|
||||
* * user - The `/mob` who owns the uis
|
||||
* * src_object - An `/obj` or `/mob` that, if is provided, only update uis which are attached to it (optional)
|
||||
* * ui_key - A string, if ui_key is provided, only update uis with a matching ui_key (optional)
|
||||
*
|
||||
* Returns the number of UIs updated
|
||||
*/
|
||||
/datum/controller/subsystem/processing/nanoui/proc/update_user_uis(mob/user, src_object, ui_key)
|
||||
if (!LAZYLEN(user.open_nanouis))
|
||||
return 0 // has no open uis
|
||||
|
||||
. = 0
|
||||
for (var/thing in user.open_nanouis)
|
||||
var/datum/nanoui/ui = thing
|
||||
if (NULL_OR_EQUAL(src_object, ui.src_object) && NULL_OR_EQUAL(ui_key, ui.ui_key))
|
||||
ui.process(1)
|
||||
.++
|
||||
|
||||
/datum/controller/subsystem/processing/nanoui/proc/close_user_uis(mob/user, src_object, ui_key)
|
||||
if (!LAZYLEN(user.open_nanouis))
|
||||
return 0
|
||||
|
||||
for (var/thing in user.open_nanouis)
|
||||
var/datum/nanoui/ui = thing
|
||||
if (NULL_OR_EQUAL(src_object, ui.src_object) && NULL_OR_EQUAL(ui_key, ui.ui_key))
|
||||
ui.close()
|
||||
.++
|
||||
|
||||
//testing("nanomanager/close_user_uis mob [user.name] closed [open_nanouis.len] of [.] uis")
|
||||
|
||||
/**
|
||||
* Add a /nanoui ui to the list of open uis
|
||||
* This is called by the /nanoui open() proc
|
||||
*
|
||||
* * ui - The `/nanoui` ui to add
|
||||
*/
|
||||
/datum/controller/subsystem/processing/nanoui/proc/ui_opened(datum/nanoui/ui)
|
||||
var/src_object_key = REF(ui.src_object)
|
||||
LAZYINITLIST(open_nanouis[src_object_key])
|
||||
|
||||
LAZYADD(ui.user.open_nanouis, ui)
|
||||
LAZYADD(open_nanouis[src_object_key][ui.ui_key], ui)
|
||||
START_PROCESSING(SSnanoui, ui)
|
||||
//testing("nanomanager/ui_opened mob [ui.user.name] [ui.src_object:name] [ui.ui_key] - user.open_nanouis [ui.user.open_nanouis.len] | uis [uis.len] | processing_uis [processing_uis.len]")
|
||||
|
||||
/**
|
||||
* Remove a /nanoui ui from the list of open uis
|
||||
* This is called by the /nanoui close() proc
|
||||
*
|
||||
* * ui - A `/nanoui` to remove
|
||||
*
|
||||
* Returns FALSE if no ui was removed, TRUE if removed successfully
|
||||
*/
|
||||
/datum/controller/subsystem/processing/nanoui/proc/ui_closed(datum/nanoui/ui)
|
||||
var/src_object_key = REF(ui.src_object)
|
||||
var/ui_key = ui.ui_key
|
||||
var/list/obj_uis = open_nanouis[src_object_key]
|
||||
|
||||
if (!LAZYLEN(obj_uis) || !obj_uis[ui_key])
|
||||
return 0 // Wasn't open.
|
||||
|
||||
STOP_PROCESSING(SSnanoui, ui)
|
||||
if(ui.user) // Sanity check in case a user has been deleted (say a blown up borg watching the alarm interface)
|
||||
LAZYREMOVE(ui.user.open_nanouis, ui)
|
||||
|
||||
obj_uis[ui_key] -= ui
|
||||
|
||||
if (!LAZYLEN(obj_uis[ui_key]))
|
||||
obj_uis -= ui_key
|
||||
|
||||
if (!LAZYLEN(obj_uis))
|
||||
open_nanouis -= src_object_key
|
||||
|
||||
//testing("nanomanager/ui_closed mob [ui.user.name] [ui.src_object:name] [ui.ui_key] - user.open_nanouis [ui.user.open_nanouis.len] | uis [uis.len] | processing_uis [processing_uis.len]")
|
||||
|
||||
return 1
|
||||
|
||||
/**
|
||||
* This is called on user logout
|
||||
* Closes/clears all uis attached to the user's `/mob`
|
||||
*
|
||||
* * user - The user's `/mob`
|
||||
*/
|
||||
/datum/controller/subsystem/processing/nanoui/proc/user_logout(mob/user)
|
||||
return close_user_uis(user)
|
||||
|
||||
/**
|
||||
* This is called when a player transfers from one mob to another
|
||||
* Transfers all open UIs to the new mob
|
||||
*
|
||||
* * oldMob - The user's old `/mob`
|
||||
* * newMob - The user's new `/mob`
|
||||
*/
|
||||
/datum/controller/subsystem/processing/nanoui/proc/user_transferred(mob/oldMob, mob/newMob)
|
||||
//testing("nanomanager/user_transferred from mob [oldMob.name] to mob [newMob.name]")
|
||||
if (!oldMob || !LAZYLEN(oldMob.open_nanouis) || !LAZYLEN(open_nanouis))
|
||||
//testing("nanomanager/user_transferred mob [oldMob.name] has no open uis")
|
||||
return 0 // has no open uis
|
||||
|
||||
for (var/thing in oldMob.open_nanouis)
|
||||
var/datum/nanoui/ui = thing
|
||||
ui.user = newMob
|
||||
LAZYADD(newMob.open_nanouis, ui)
|
||||
|
||||
oldMob.open_nanouis = null
|
||||
|
||||
return 1 // success
|
||||
|
||||
/datum/asset/nanoui
|
||||
var/list/common = list()
|
||||
|
||||
var/list/common_dirs = list(
|
||||
"nano/css/",
|
||||
"nano/js/",
|
||||
"nano/images/",
|
||||
"nano/images/status_icons/",
|
||||
"nano/templates/"
|
||||
)
|
||||
var/list/uncommon_dirs = list()
|
||||
|
||||
/datum/asset/nanoui/register()
|
||||
// Crawl the directories to find files.
|
||||
for (var/path in common_dirs)
|
||||
var/list/filenames = flist(path)
|
||||
for(var/filename in filenames)
|
||||
if(copytext(filename, length(filename)) != "/") // Ignore directories.
|
||||
if(fexists(path + filename))
|
||||
common[filename] = fcopy_rsc(path + filename)
|
||||
SSassets.transport.register_asset(filename, common[filename])
|
||||
for (var/path in uncommon_dirs)
|
||||
var/list/filenames = flist(path)
|
||||
for(var/filename in filenames)
|
||||
if(copytext(filename, length(filename)) != "/") // Ignore directories.
|
||||
if(fexists(path + filename))
|
||||
SSassets.transport.register_asset(filename, fcopy_rsc(path + filename))
|
||||
|
||||
/datum/asset/nanoui/send(client, uncommon)
|
||||
if(!islist(uncommon))
|
||||
uncommon = list(uncommon)
|
||||
|
||||
SSassets.transport.send_assets(client, uncommon)
|
||||
SSassets.transport.send_assets(client, common)
|
||||
+36
-53
@@ -10,11 +10,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
SUBSYSTEM_DEF(tgui)
|
||||
name = "tgui"
|
||||
wait = 9
|
||||
flags = SS_NO_INIT
|
||||
priority = SS_PRIORITY_NANOUI
|
||||
priority = FIRE_PRIORITY_TGUI
|
||||
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
|
||||
|
||||
/// A list of UIs scheduled to process
|
||||
@@ -24,54 +24,39 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
/// The HTML base used for all UIs.
|
||||
var/basehtml
|
||||
|
||||
/datum/controller/subsystem/processing/tgui/PreInit()
|
||||
basehtml = file2text('tgui/public/tgui.html')
|
||||
// Inject inline polyfills
|
||||
var/polyfill = file2text('tgui/public/tgui-polyfill.min.js')
|
||||
polyfill = "<script>\n[polyfill]\n</script>"
|
||||
basehtml = replacetextEx(basehtml, "<!-- tgui:inline-polyfill -->", polyfill)
|
||||
basehtml = replacetextEx(basehtml, "<!-- tgui:nt-copyright -->", "NanoTrasen © 2457-[text2num(time2text(world.realtime, "YYYY")) + 442]")
|
||||
/datum/controller/subsystem/tgui/PreInit()
|
||||
basehtml = file2text("tgui/public/tgui.html")
|
||||
|
||||
basehtml = replacetext(basehtml, "tgui:stylesheet", MAP_STYLESHEET)
|
||||
// Inject inline helper functions
|
||||
var/helpers = file2text("tgui/public/helpers.min.js")
|
||||
helpers = "<script type='text/javascript'>\n[helpers]\n</script>"
|
||||
basehtml = replacetextEx(basehtml, "<!-- tgui:helpers -->", helpers)
|
||||
|
||||
/datum/controller/subsystem/processing/tgui/OnConfigLoad()
|
||||
var/storage_iframe = GLOB.config.storage_cdn_iframe
|
||||
if(storage_iframe && storage_iframe != /datum/configuration::storage_cdn_iframe)
|
||||
basehtml = replacetextEx(basehtml, "tgui:storagecdn", storage_iframe)
|
||||
return
|
||||
// Inject inline ntos-error styles
|
||||
var/ntos_error = file2text("tgui/public/ntos-error.min.css")
|
||||
ntos_error = "<style type='text/css'>\n[ntos_error]\n</style>"
|
||||
basehtml = replacetextEx(basehtml, "<!-- tgui:ntos-error -->", ntos_error)
|
||||
|
||||
if(GLOB.config.asset_transport == "webroot")
|
||||
var/datum/asset_transport/webroot/webroot = SSassets.transport
|
||||
basehtml = replacetextEx(basehtml, "<!-- tgui:nt-copyright -->", "Nanotrasen (c) 2395-[num2text(GLOB.game_year)]")
|
||||
|
||||
var/datum/asset_cache_item/item = webroot.register_asset("iframe.html", file("tgui/public/iframe.html"))
|
||||
basehtml = replacetext(basehtml, "tgui:storagecdn", webroot.get_asset_url("iframe.html", item))
|
||||
return
|
||||
|
||||
if(!storage_iframe)
|
||||
return
|
||||
|
||||
basehtml = replacetextEx(basehtml, "tgui:storagecdn", storage_iframe)
|
||||
|
||||
/datum/controller/subsystem/processing/tgui/Shutdown()
|
||||
/datum/controller/subsystem/tgui/Shutdown()
|
||||
close_all_uis()
|
||||
|
||||
/datum/controller/subsystem/processing/tgui/stat_entry(msg)
|
||||
/datum/controller/subsystem/tgui/stat_entry(msg)
|
||||
msg = "P:[length(all_uis)]"
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/processing/tgui/fire(resumed = FALSE)
|
||||
CAN_BE_REDEFINED(TRUE)
|
||||
/datum/controller/subsystem/tgui/fire(resumed = FALSE)
|
||||
if(!resumed)
|
||||
src.current_run = all_uis.Copy()
|
||||
// Cache for sanic speed (lists are references anyways)
|
||||
var/list/current_run = src.current_run
|
||||
var/seconds_per_tick = wait * 0.1
|
||||
while(current_run.len)
|
||||
var/datum/tgui/ui = current_run[current_run.len]
|
||||
current_run.len--
|
||||
// TODO: Move user/src_object check to process()
|
||||
if(ui?.user && ui.src_object)
|
||||
ui.process(seconds_per_tick)
|
||||
ui.process(wait * 0.1)
|
||||
else
|
||||
ui.close(0)
|
||||
if(MC_TICK_CHECK)
|
||||
@@ -84,9 +69,9 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
* Returns null if pool was exhausted.
|
||||
*
|
||||
* required user mob
|
||||
* return datum/tgui
|
||||
* return datum/tgui_window
|
||||
*/
|
||||
/datum/controller/subsystem/processing/tgui/proc/request_pooled_window(mob/user)
|
||||
/datum/controller/subsystem/tgui/proc/request_pooled_window(mob/user)
|
||||
if(!user.client)
|
||||
return null
|
||||
var/list/windows = user.client.tgui_windows
|
||||
@@ -122,7 +107,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
*
|
||||
* required user mob
|
||||
*/
|
||||
/datum/controller/subsystem/processing/tgui/proc/force_close_all_windows(mob/user)
|
||||
/datum/controller/subsystem/tgui/proc/force_close_all_windows(mob/user)
|
||||
log_tgui(user, context = "SStgui/force_close_all_windows")
|
||||
if(user.client)
|
||||
user.client.tgui_windows = list()
|
||||
@@ -138,14 +123,12 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
* required user mob
|
||||
* required window_id string
|
||||
*/
|
||||
/datum/controller/subsystem/processing/tgui/proc/force_close_window(mob/user, window_id)
|
||||
/datum/controller/subsystem/tgui/proc/force_close_window(mob/user, window_id)
|
||||
log_tgui(user, context = "SStgui/force_close_window")
|
||||
// Close all tgui datums based on window_id.
|
||||
for(var/datum/tgui/ui in user.tgui_open_uis)
|
||||
if(ui.window && ui.window.id == window_id)
|
||||
ui.close(can_be_suspended = FALSE)
|
||||
// Unset machine just to be sure.
|
||||
user.unset_machine()
|
||||
// Close window directly just to be sure.
|
||||
user << browse(null, "window=[window_id]")
|
||||
|
||||
@@ -161,7 +144,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
*
|
||||
* return datum/tgui The found UI.
|
||||
*/
|
||||
/datum/controller/subsystem/processing/tgui/proc/try_update_ui(
|
||||
/datum/controller/subsystem/tgui/proc/try_update_ui(
|
||||
mob/user,
|
||||
datum/src_object,
|
||||
datum/tgui/ui)
|
||||
@@ -191,7 +174,8 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
*
|
||||
* return datum/tgui The found UI.
|
||||
*/
|
||||
/datum/controller/subsystem/processing/tgui/proc/get_open_ui(mob/user, datum/src_object)
|
||||
/datum/controller/subsystem/tgui/proc/get_open_ui(mob/user, datum/src_object)
|
||||
// No UIs opened for this src_object
|
||||
if(!LAZYLEN(src_object?.open_uis))
|
||||
return null
|
||||
for(var/datum/tgui/ui in src_object.open_uis)
|
||||
@@ -209,15 +193,15 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
*
|
||||
* return int The number of UIs updated.
|
||||
*/
|
||||
/datum/controller/subsystem/processing/tgui/proc/update_uis(datum/src_object)
|
||||
/datum/controller/subsystem/tgui/proc/update_uis(datum/src_object)
|
||||
// No UIs opened for this src_object
|
||||
if(!LAZYLEN(src_object?.open_uis))
|
||||
return 0
|
||||
var/count = 0
|
||||
var/seconds_per_tick = wait * 0.1
|
||||
for(var/datum/tgui/ui in src_object.open_uis)
|
||||
// Check if UI is valid.
|
||||
if(ui?.src_object && ui.user && ui.src_object.ui_host(ui.user))
|
||||
INVOKE_ASYNC(ui, TYPE_PROC_REF(/datum/tgui, process), seconds_per_tick, TRUE)
|
||||
INVOKE_ASYNC(ui, TYPE_PROC_REF(/datum/tgui, process), wait * 0.1, TRUE)
|
||||
count++
|
||||
return count
|
||||
|
||||
@@ -230,7 +214,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
*
|
||||
* return int The number of UIs closed.
|
||||
*/
|
||||
/datum/controller/subsystem/processing/tgui/proc/close_uis(datum/src_object)
|
||||
/datum/controller/subsystem/tgui/proc/close_uis(datum/src_object)
|
||||
// No UIs opened for this src_object
|
||||
if(!LAZYLEN(src_object?.open_uis))
|
||||
return 0
|
||||
@@ -249,7 +233,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
*
|
||||
* return int The number of UIs closed.
|
||||
*/
|
||||
/datum/controller/subsystem/processing/tgui/proc/close_all_uis()
|
||||
/datum/controller/subsystem/tgui/proc/close_all_uis()
|
||||
var/count = 0
|
||||
for(var/datum/tgui/ui in all_uis)
|
||||
// Check if UI is valid.
|
||||
@@ -268,14 +252,13 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
*
|
||||
* return int The number of UIs updated.
|
||||
*/
|
||||
/datum/controller/subsystem/processing/tgui/proc/update_user_uis(mob/user, datum/src_object)
|
||||
/datum/controller/subsystem/tgui/proc/update_user_uis(mob/user, datum/src_object)
|
||||
var/count = 0
|
||||
if(length(user?.tgui_open_uis) == 0)
|
||||
return count
|
||||
var/seconds_per_tick = wait * 0.1
|
||||
for(var/datum/tgui/ui in user.tgui_open_uis)
|
||||
if(isnull(src_object) || ui.src_object == src_object)
|
||||
ui.process(seconds_per_tick, force = 1)
|
||||
ui.process(wait * 0.1, force = 1)
|
||||
count++
|
||||
return count
|
||||
|
||||
@@ -289,7 +272,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
*
|
||||
* return int The number of UIs closed.
|
||||
*/
|
||||
/datum/controller/subsystem/processing/tgui/proc/close_user_uis(mob/user, datum/src_object)
|
||||
/datum/controller/subsystem/tgui/proc/close_user_uis(mob/user, datum/src_object)
|
||||
var/count = 0
|
||||
if(length(user?.tgui_open_uis) == 0)
|
||||
return count
|
||||
@@ -306,7 +289,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
*
|
||||
* required ui datum/tgui The UI to be added.
|
||||
*/
|
||||
/datum/controller/subsystem/processing/tgui/proc/on_open(datum/tgui/ui)
|
||||
/datum/controller/subsystem/tgui/proc/on_open(datum/tgui/ui)
|
||||
ui.user?.tgui_open_uis |= ui
|
||||
LAZYOR(ui.src_object.open_uis, ui)
|
||||
all_uis |= ui
|
||||
@@ -320,7 +303,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
*
|
||||
* return bool If the UI was removed or not.
|
||||
*/
|
||||
/datum/controller/subsystem/processing/tgui/proc/on_close(datum/tgui/ui)
|
||||
/datum/controller/subsystem/tgui/proc/on_close(datum/tgui/ui)
|
||||
// Remove it from the list of processing UIs.
|
||||
all_uis -= ui
|
||||
current_run -= ui
|
||||
@@ -340,7 +323,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
*
|
||||
* return int The number of UIs closed.
|
||||
*/
|
||||
/datum/controller/subsystem/processing/tgui/proc/on_logout(mob/user)
|
||||
/datum/controller/subsystem/tgui/proc/on_logout(mob/user)
|
||||
close_user_uis(user)
|
||||
|
||||
/**
|
||||
@@ -353,7 +336,7 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
*
|
||||
* return bool If the UIs were transferred.
|
||||
*/
|
||||
/datum/controller/subsystem/processing/tgui/proc/on_transfer(mob/source, mob/target)
|
||||
/datum/controller/subsystem/tgui/proc/on_transfer(mob/source, mob/target)
|
||||
// The old mob had no open UIs.
|
||||
if(length(source?.tgui_open_uis) == 0)
|
||||
return FALSE
|
||||
@@ -0,0 +1,16 @@
|
||||
/// Stores information about a chat payload
|
||||
/datum/chat_payload
|
||||
/// Sequence number of this payload
|
||||
var/sequence = 0
|
||||
/// Message we are sending
|
||||
var/list/content
|
||||
/// Resend count
|
||||
var/resends = 0
|
||||
|
||||
/// Converts the chat payload into a JSON string
|
||||
/datum/chat_payload/proc/into_message()
|
||||
return "{\"sequence\":[sequence],\"content\":[json_encode(content)]}"
|
||||
|
||||
/// Returns an HTML-encoded message from our contents.
|
||||
/datum/chat_payload/proc/get_content_as_html()
|
||||
return message_to_html(content)
|
||||
@@ -47,11 +47,6 @@
|
||||
lan = null
|
||||
return lan
|
||||
|
||||
/datum/component/local_network_member/nano_host()
|
||||
if(parent)
|
||||
return parent.nano_host()
|
||||
. = ..()
|
||||
|
||||
/datum/component/local_network_member/proc/get_new_tag(mob/user)
|
||||
var/new_ident = input(user, "Enter a new ident tag.", "[parent]", id_tag) as null|text
|
||||
if(new_ident && parent && user.Adjacent(parent) && CanInteract(user, GLOB.physical_state))
|
||||
|
||||
@@ -94,7 +94,6 @@
|
||||
current.remove_vampire_powers()
|
||||
current.mind = null
|
||||
|
||||
SSnanoui.user_transferred(current, new_character)
|
||||
SStgui.on_transfer(current, new_character)
|
||||
if(current.client && GLOB.ticket_panels[current.client])
|
||||
var/datum/ticket_panel/tp = GLOB.ticket_panels[current.client]
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
wires = list(
|
||||
WIRE_POWER,
|
||||
WIRE_IDSCAN, WIRE_AI,
|
||||
WIRE_PANIC, WIRE_ALARM
|
||||
WIRE_PANIC, WIRE_ALARM,
|
||||
WIRE_RCON
|
||||
)
|
||||
add_duds(3)
|
||||
..()
|
||||
@@ -24,6 +25,7 @@
|
||||
. += A.locked ? "The air alarm is locked." : "The air alarm is unlocked."
|
||||
. += (A.shorted || (A.stat & (NOPOWER|BROKEN))) ? "The Air Alarm is offline." : "The Air Alarm is working properly!"
|
||||
. += A.aidisabled ? "The 'AI control allowed' light is off." : "The 'AI control allowed' light is on."
|
||||
. += A.rcon_setting ? "The remote control link is enabled." : "The remote control link is disabled."
|
||||
|
||||
/datum/wires/alarm/on_cut(wire, mend, source)
|
||||
var/obj/structure/machinery/alarm/A = holder
|
||||
@@ -51,6 +53,12 @@
|
||||
A.post_alert(2)
|
||||
A.update_icon()
|
||||
|
||||
if(WIRE_RCON)
|
||||
if(!mend)
|
||||
A.rcon_setting = FALSE
|
||||
else
|
||||
A.rcon_setting = TRUE
|
||||
|
||||
/datum/wires/alarm/on_pulse(wire)
|
||||
var/obj/structure/machinery/alarm/A = holder
|
||||
switch(wire)
|
||||
@@ -67,7 +75,6 @@
|
||||
A.shorted = 0
|
||||
A.update_icon()
|
||||
|
||||
|
||||
if (WIRE_AI)
|
||||
if (A.aidisabled == 0)
|
||||
A.aidisabled = 1
|
||||
@@ -83,7 +90,7 @@
|
||||
A.mode = 1 // AALARM_MODE_SCRUB
|
||||
A.apply_mode()
|
||||
|
||||
if(WIRE_ALARM)
|
||||
if(WIRE_ALARM, WIRE_RCON)
|
||||
if (A.alarm_area.atmosalert(0, A))
|
||||
A.post_alert(0)
|
||||
A.update_icon()
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
if(WIRE_TRANSMIT)
|
||||
R.set_broadcasting(!R.get_broadcasting() && !is_cut(WIRE_SIGNAL))
|
||||
SSnanoui.update_uis(holder)
|
||||
SStgui.update_uis(holder)
|
||||
|
||||
/datum/wires/radio/on_cut(wire, mend, source)
|
||||
var/obj/item/radio/R = holder
|
||||
@@ -45,4 +45,4 @@
|
||||
|
||||
if(WIRE_TRANSMIT)
|
||||
R.set_broadcasting(mend && !is_cut(WIRE_SIGNAL))
|
||||
SSnanoui.update_uis(holder)
|
||||
SStgui.update_uis(holder)
|
||||
|
||||
@@ -87,6 +87,15 @@
|
||||
if(src.desc)
|
||||
. += src.desc
|
||||
|
||||
var/list/tags_list = examine_tags(user)
|
||||
if(length(tags_list))
|
||||
var/tag_string = list()
|
||||
for (var/atom_tag in tags_list)
|
||||
tag_string += (isnull(tags_list[atom_tag]) ? atom_tag : SPAN_TOOLTIP(tags_list[atom_tag], atom_tag))
|
||||
// some regex to ensure that we don't add another "and" if the final element's main text (not tooltip) has one
|
||||
tag_string = english_list(tag_string, and_text = (findtext(tag_string[length(tag_string)], regex(@">.*?and .*?<"))) ? " " : " and ")
|
||||
. += "It is a [tag_string] [examine_descriptor(user)]."
|
||||
|
||||
// Returns a SPAN_* based on health, if configured.
|
||||
var/list/condition_hints = src.condition_hints()
|
||||
if(length(condition_hints))
|
||||
@@ -145,6 +154,35 @@
|
||||
if(H.glasses)
|
||||
H.glasses.glasses_examine_atom(src, H)
|
||||
|
||||
/// What this atom should be called in examine tags
|
||||
/atom/proc/examine_descriptor(mob/user)
|
||||
return "object"
|
||||
|
||||
/**
|
||||
* A list of "tags" displayed after atom's description in examine.
|
||||
* This should return an assoc list of tags -> tooltips for them. If item is null, then no tooltip is assigned.
|
||||
*
|
||||
* * TGUI tooltips (not the main text) in chat cannot use HTML stuff at all, so
|
||||
* trying something like `<b><big>ffff</big></b>` will not work for tooltips.
|
||||
*
|
||||
* For example:
|
||||
* ```byond
|
||||
* . = list()
|
||||
* .["small"] = "It is a small item."
|
||||
* .["fireproof"] = "It is made of fire-retardant materials."
|
||||
* .["and conductive"] = "It's made of conductive materials and whatnot. Blah blah blah." // having "and " in the end tag's main text/key works too!
|
||||
* ```
|
||||
* will result in
|
||||
*
|
||||
* It is a *small*, *fireproof* *and conductive* item.
|
||||
*
|
||||
* where "item" is pulled from [/atom/proc/examine_descriptor]
|
||||
*/
|
||||
/atom/proc/examine_tags(mob/user)
|
||||
. = list()
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_ATOM_EXAMINE_TAGS, user, .)
|
||||
|
||||
/**
|
||||
* Used to check if "examine_fluff" from the HTML link in examine() is true, i.e. if it was clicked.
|
||||
*/
|
||||
|
||||
+15
-16
@@ -344,23 +344,22 @@
|
||||
|
||||
I.forceMove(T)
|
||||
|
||||
/obj/item/get_examine_text(mob/user, distance, is_adjacent, infix, suffix, get_extended = FALSE)
|
||||
var/size
|
||||
switch(src.w_class)
|
||||
if (WEIGHT_CLASS_HUGE to INFINITY)
|
||||
size = "huge"
|
||||
if (WEIGHT_CLASS_BULKY to WEIGHT_CLASS_HUGE)
|
||||
size = "bulky"
|
||||
if (WEIGHT_CLASS_NORMAL to WEIGHT_CLASS_BULKY)
|
||||
size = "normal-sized"
|
||||
if (WEIGHT_CLASS_SMALL to WEIGHT_CLASS_NORMAL)
|
||||
size = "small"
|
||||
if (0 to WEIGHT_CLASS_SMALL)
|
||||
size = "tiny"
|
||||
//Changed this switch to ranges instead of tiered values, to cope with granularity and also
|
||||
//things outside its range ~Nanako
|
||||
/obj/item/examine_descriptor(mob/user)
|
||||
return "item"
|
||||
|
||||
. = ..(user, distance, is_adjacent, "It is a [size] item.", get_extended = get_extended)
|
||||
/obj/item/examine_tags(mob/user)
|
||||
var/list/parent_tags = ..()
|
||||
parent_tags.Insert(1, weight_class_to_text(w_class)) // To make size display first, otherwise it looks goofy
|
||||
. = parent_tags
|
||||
.[weight_class_to_text(w_class)] = weight_class_to_tooltip(w_class)
|
||||
|
||||
if (siemens_coefficient == 0)
|
||||
.["insulated"] = "It is made from a robust electrical insulator and will block any electricity passing through it!"
|
||||
else if (siemens_coefficient <= 0.5)
|
||||
.["partially insulated"] = "It is made from a poor insulator that will dampen (but not fully block) electric shocks passing through it."
|
||||
|
||||
/obj/item/get_examine_text(mob/user, distance, is_adjacent, infix, suffix, get_extended = FALSE)
|
||||
. = ..(user, distance, is_adjacent, get_extended = get_extended)
|
||||
var/datum/component/armor/armor_component = GetComponent(/datum/component/armor)
|
||||
if(armor_component && !armor_component.hidden)
|
||||
. += FONT_SMALL(SPAN_NOTICE("\[?\] This item has armor values. <a href='byond://?src=[REF(src)];examine_armor=1'>\[Show Armor Values\]</a>"))
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
else if(crystal_type == CRYSTAL_TYPE_BLUECRYSTAL)
|
||||
I.hidden_uplink.bluecrystals += amount
|
||||
I.hidden_uplink.update_tgui_data()
|
||||
SSnanoui.update_uis(I.hidden_uplink)
|
||||
SStgui.update_uis(I.hidden_uplink)
|
||||
use(amount)
|
||||
to_chat(user, SPAN_NOTICE("You slot \the [src] into \the [I] and charge its internal uplink."))
|
||||
|
||||
|
||||
@@ -6,16 +6,20 @@
|
||||
pass_flags_self = PASSSTRUCTURE
|
||||
should_use_health = TRUE
|
||||
|
||||
var/material_alteration = MATERIAL_ALTERATION_ALL // Overrides for material shit. Set them manually if you don't want colors etc. See wood chairs/office chairs.
|
||||
/// Overrides for material shit. Set them manually if you don't want colors etc. See wood chairs/office chairs.
|
||||
var/material_alteration = MATERIAL_ALTERATION_ALL
|
||||
var/climbable
|
||||
var/parts
|
||||
var/list/climbers
|
||||
var/list/footstep_sound //footstep sounds when stepped on
|
||||
/// Footstep sounds when stepped on
|
||||
var/list/footstep_sound
|
||||
|
||||
var/material/material
|
||||
var/build_amt = 2 // used by some structures to determine into how many pieces they should disassemble into or be made with
|
||||
/// Used by some structures to determine into how many pieces they should disassemble into or be made with
|
||||
var/build_amt = 2
|
||||
|
||||
var/slowdown = 0 //amount that pulling mobs have their movement delayed by
|
||||
/// Amount that pulling mobs have their movement delayed by
|
||||
var/slowdown = 0
|
||||
|
||||
/obj/structure/Initialize(mapload)
|
||||
. = ..()
|
||||
@@ -40,6 +44,14 @@
|
||||
material = null
|
||||
return ..()
|
||||
|
||||
/obj/structure/examine_descriptor(mob/user)
|
||||
return "structure"
|
||||
|
||||
/obj/structure/examine_tags(atom/source, mob/user, list/examine_list)
|
||||
. = ..()
|
||||
if(climbable)
|
||||
.["climbable"] = "It can be climbed on, either by dragging your mob onto it or middle-clicking it."
|
||||
|
||||
/obj/structure/attackby(obj/item/attacking_item, mob/user, params)
|
||||
. = ..()
|
||||
if(user?.a_intent == I_HURT && maxhealth)
|
||||
|
||||
@@ -258,6 +258,9 @@ Class Procs:
|
||||
return FALSE
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/machinery/examine_descriptor(mob/user)
|
||||
return "machine"
|
||||
|
||||
// /obj/structure/machinery/proc/process_all()
|
||||
// /* Uncomment this if/when you need component processing
|
||||
// if(processing_flags & MACHINERY_PROCESS_COMPONENTS)
|
||||
@@ -643,5 +646,5 @@ Class Procs:
|
||||
/obj/structure/machinery/ui_status(mob/user, datum/ui_state/state)
|
||||
. = ..()
|
||||
if(. < UI_INTERACTIVE)
|
||||
if(user.machine)
|
||||
if(user?.machine)
|
||||
user.unset_machine()
|
||||
|
||||
@@ -118,9 +118,8 @@ pixel_x = 10;
|
||||
var/breach_detection = 1
|
||||
//var/skipprocess = 0 //Experimenting
|
||||
var/alarm_frequency = 1437
|
||||
var/remote_control = 0
|
||||
var/rcon_setting = 2
|
||||
var/rcon_time = 0
|
||||
/// If true, RCON is enabled. If false, RCON has been disabled.
|
||||
var/rcon_setting = TRUE
|
||||
var/locked = 1
|
||||
var/aidisabled = 0
|
||||
var/shorted = 0
|
||||
@@ -475,18 +474,6 @@ pixel_x = 10;
|
||||
mode=AALARM_MODE_FILL
|
||||
apply_mode()
|
||||
|
||||
//atmos computer remote controll stuff
|
||||
switch(rcon_setting)
|
||||
if(RCON_NO)
|
||||
remote_control = 0
|
||||
if(RCON_AUTO)
|
||||
if(danger_level == 2)
|
||||
remote_control = 1
|
||||
else
|
||||
remote_control = 0
|
||||
if(RCON_YES)
|
||||
remote_control = 1
|
||||
|
||||
return
|
||||
|
||||
/obj/structure/machinery/alarm/proc/handle_heating_cooling(datum/gas_mixture/environment, seconds_per_tick)
|
||||
@@ -698,6 +685,22 @@ pixel_x = 10;
|
||||
|
||||
frequency.post_signal(src, alert_signal)
|
||||
|
||||
/obj/structure/machinery/alarm/proc/is_alarming()
|
||||
return max(danger_level, alarm_area?.atmosalm) > 0
|
||||
|
||||
/obj/structure/machinery/alarm/proc/is_remote_user(mob/user)
|
||||
if(!user)
|
||||
return FALSE
|
||||
for(var/datum/tgui/open_tgui in user.tgui_open_uis)
|
||||
if(open_tgui.interface == "AtmosAlarmControl")
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/structure/machinery/alarm/proc/is_unlocked_for(mob/user)
|
||||
if(is_remote_user(user) && rcon_setting)
|
||||
return TRUE
|
||||
return !locked || issilicon(user)
|
||||
|
||||
/obj/structure/machinery/alarm/attack_ai(mob/user)
|
||||
if(!ai_can_interact(user))
|
||||
return
|
||||
@@ -751,7 +754,8 @@ pixel_x = 10;
|
||||
data["target_temperature"] = round(target_temperature - T0C, 0.1)
|
||||
|
||||
// Access
|
||||
data["locked"] = locked && !issilicon(user)
|
||||
data["locked"] = !is_unlocked_for(user)
|
||||
data["remote_view"] = is_remote_user(user)
|
||||
data["shorted"] = shorted
|
||||
data["rcon"] = rcon_setting
|
||||
|
||||
@@ -848,14 +852,6 @@ pixel_x = 10;
|
||||
|
||||
// Actions available without unlocking
|
||||
switch(action)
|
||||
if("rcon")
|
||||
var/new_rcon = text2num(params["value"])
|
||||
switch(new_rcon)
|
||||
if(RCON_NO) rcon_setting = RCON_NO
|
||||
if(RCON_AUTO) rcon_setting = RCON_AUTO
|
||||
if(RCON_YES) rcon_setting = RCON_YES
|
||||
return TRUE
|
||||
|
||||
if("temperature")
|
||||
var/list/tlv = TLV["temperature"]
|
||||
var/max_temperature = min(tlv[3] - T0C, MAX_TEMPERATURE)
|
||||
@@ -871,7 +867,7 @@ pixel_x = 10;
|
||||
return TRUE
|
||||
|
||||
// Actions that require the alarm to be unlocked
|
||||
if(locked && !issilicon(usr))
|
||||
if(!is_unlocked_for(usr))
|
||||
return
|
||||
|
||||
switch(action)
|
||||
@@ -962,11 +958,10 @@ pixel_x = 10;
|
||||
apply_mode()
|
||||
return TRUE
|
||||
|
||||
/// Snowflake proc to make it so users of the Atmosphere Control app (having the UI open) have full access to an alarm.
|
||||
/// Lets Atmosphere Control users remotely view alarms.
|
||||
/obj/structure/machinery/alarm/ui_status(mob/user)
|
||||
for(var/datum/tgui/open_tgui in user.tgui_open_uis)
|
||||
if(open_tgui.interface == "AtmosAlarmControl")
|
||||
return UI_INTERACTIVE
|
||||
if(is_remote_user(user) && rcon_setting)
|
||||
return UI_INTERACTIVE
|
||||
return ..()
|
||||
|
||||
/**
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
|
||||
var/list/sensor_information = list()
|
||||
var/datum/radio_frequency/radio_connection
|
||||
var/ui_type = "AtmosControl"
|
||||
ui_type = "AtmosControl"
|
||||
circuit = /obj/item/circuitboard/air_management
|
||||
|
||||
/obj/structure/machinery/computer/general_air_control/Destroy()
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
/// The access cable inserted into this computer, if any.
|
||||
var/obj/item/access_cable/inserted_cable
|
||||
|
||||
/// Contains the name of the TGUI interface file opened when using this computer. If null,
|
||||
var/ui_type = ""
|
||||
|
||||
/obj/structure/machinery/computer/Initialize()
|
||||
. = ..()
|
||||
overlay_layer = layer
|
||||
@@ -45,11 +48,6 @@
|
||||
inserted_cable = null
|
||||
return ..()
|
||||
|
||||
/obj/structure/machinery/computer/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = TRUE)
|
||||
if(!operable() || !is_station_level(z) || user.stat)
|
||||
user.unset_machine()
|
||||
return
|
||||
|
||||
/obj/structure/machinery/computer/emp_act(severity)
|
||||
. = ..()
|
||||
|
||||
|
||||
@@ -248,10 +248,7 @@
|
||||
/obj/structure/machinery/computer/scan_consolenew/attackby(obj/item/attacking_item, mob/user)
|
||||
if (istype(attacking_item, /obj/item/disk/data)) //INSERT SOME diskS
|
||||
if (!src.disk)
|
||||
user.drop_from_inventory(attacking_item, src)
|
||||
src.disk = attacking_item
|
||||
to_chat(user, "You insert [attacking_item].")
|
||||
SSnanoui.update_uis(src) // update all UIs attached to src
|
||||
to_chat(user, "You try to insert [attacking_item], but the drive seems jammed.")
|
||||
return TRUE
|
||||
else
|
||||
return ..()
|
||||
|
||||
@@ -274,7 +274,6 @@
|
||||
signal.data["dock_status"] = get_docking_status()
|
||||
post_signal(signal)
|
||||
|
||||
//this is mostly for NanoUI
|
||||
/datum/computer/file/embedded_program/docking/proc/get_docking_status()
|
||||
switch (dock_state)
|
||||
if (STATE_UNDOCKED) return "undocked"
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
var/categories = CAT_NORMAL
|
||||
/// What we're requesting payment for right now
|
||||
var/datum/data/vending_product/currently_vending = null
|
||||
/// Status screen messages like "insufficient funds", displayed in NanoUI
|
||||
/// Status screen messages like "insufficient funds", displayed in UI
|
||||
var/status_message = ""
|
||||
/// Set to 1 if status_message is an error
|
||||
var/status_error = 0
|
||||
@@ -770,7 +770,6 @@
|
||||
sel_key = params["vendItem"]
|
||||
var/datum/data/vending_product/R = product_records[key]
|
||||
|
||||
// This should not happen unless the request from NanoUI was bad
|
||||
if(!(R.category & categories))
|
||||
return
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
/// If the turf should generate details. Default: TRUE
|
||||
var/has_edge_icon = TRUE
|
||||
|
||||
/turf/simulated/floor/examine_descriptor(mob/user)
|
||||
return "floor"
|
||||
|
||||
/turf/simulated/floor/disassembly_hints(mob/user, distance, is_adjacent)
|
||||
. += ..()
|
||||
if(flooring)
|
||||
|
||||
@@ -62,6 +62,9 @@
|
||||
state = SPAN_NOTICE("\The [src] seems completely intact.")
|
||||
. = state
|
||||
|
||||
/turf/simulated/wall/examine_descriptor(mob/user)
|
||||
return "wall"
|
||||
|
||||
/turf/simulated/wall/mechanics_hints(mob/user, distance, is_adjacent)
|
||||
. += ..()
|
||||
if(locate(/obj/effect/overlay/wallrot) in src)
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
icon = 'icons/turf/flooring/tiles.dmi'
|
||||
icon_state = "tiled_preview"
|
||||
|
||||
/turf/unsimulated/floor/examine_descriptor(mob/user)
|
||||
return "floor"
|
||||
|
||||
/turf/unsimulated/floor/monotile
|
||||
icon_state = "monotile_preview"
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
blocks_air = TRUE
|
||||
pass_flags_self = PASSCLOSEDTURF
|
||||
|
||||
/turf/unsimulated/wall/examine_descriptor(mob/user)
|
||||
return "wall"
|
||||
|
||||
/turf/unsimulated/wall/fakeglass
|
||||
name = "window"
|
||||
icon = 'icons/turf/walls.dmi'
|
||||
|
||||
@@ -265,14 +265,6 @@
|
||||
else //Delayed to avoid wingets from Login calls.
|
||||
addtimer(CALLBACK(src, VERB_REF(fit_viewport), 1 SECONDS))
|
||||
|
||||
/client/verb/refresh_tgui()
|
||||
set name = "Refresh TGUI"
|
||||
set category = "OOC.Debug"
|
||||
|
||||
for(var/window_id in tgui_windows)
|
||||
var/datum/tgui_window/window = tgui_windows[window_id]
|
||||
window.reinitialize()
|
||||
|
||||
/client/verb/fix_stat_panel()
|
||||
set name = "Fix-Stat-Panel"
|
||||
set hidden = TRUE
|
||||
|
||||
@@ -180,7 +180,6 @@ GLOBAL_LIST_INIT(admin_verbs_server, list(
|
||||
/datum/admins/proc/toggle_round_spookyness,
|
||||
/datum/admins/proc/toggle_space_ninja,
|
||||
/client/proc/toggle_random_events,
|
||||
/client/proc/nanomapgen_DumpImage,
|
||||
/client/proc/toggle_recursive_explosions,
|
||||
/client/proc/restart_controller,
|
||||
/client/proc/cmd_ss_panic,
|
||||
@@ -355,7 +354,6 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
|
||||
/datum/admins/proc/adjump,
|
||||
/datum/admins/proc/toggle_space_ninja,
|
||||
/client/proc/toggle_random_events,
|
||||
/client/proc/nanomapgen_DumpImage,
|
||||
/client/proc/play_local_sound,
|
||||
/client/proc/play_sound,
|
||||
/client/proc/play_server_sound,
|
||||
@@ -475,7 +473,6 @@ GLOBAL_LIST_INIT(admin_verbs_dev, list( //will need to be altered - Ryan784
|
||||
/client/proc/jumptoship,
|
||||
/client/proc/jumptosector,
|
||||
/client/proc/cmd_dev_say,
|
||||
/client/proc/nanomapgen_DumpImage,
|
||||
/client/proc/admin_ghost,
|
||||
/client/proc/air_report,
|
||||
/client/proc/enable_debug_verbs,
|
||||
|
||||
@@ -463,21 +463,6 @@
|
||||
|
||||
usr << browse(HTML_SKELETON(replacetext(SSatoms.InitLog(), "\n", "<br>")), "window=initlog")
|
||||
|
||||
/client/proc/reload_nanoui_resources()
|
||||
set category = "Debug"
|
||||
set name = "Reload NanoUI Resources"
|
||||
set desc = "Force the client to redownload NanoUI Resources"
|
||||
|
||||
// Close open NanoUIs.
|
||||
SSnanoui.close_user_uis(usr)
|
||||
|
||||
// Re-load the assets.
|
||||
var/datum/asset/assets = get_asset_datum(/datum/asset/nanoui)
|
||||
assets.register()
|
||||
|
||||
// Clear the user's cache so they get resent.
|
||||
usr.client.sent_assets = list()
|
||||
|
||||
/**
|
||||
* Used to generate lag and load the MC to test how things work under live server stress
|
||||
*/
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
S["UI_style"] >> pref.UI_style
|
||||
S["UI_style_color"] >> pref.UI_style_color
|
||||
S["UI_style_alpha"] >> pref.UI_style_alpha
|
||||
S["tgui_fancy"] >> pref.tgui_fancy
|
||||
S["tgui_lock"] >> pref.tgui_lock
|
||||
S["ooccolor"] >> pref.ooccolor
|
||||
S["clientfps"] >> pref.clientfps
|
||||
@@ -21,7 +20,6 @@
|
||||
S["UI_style"] << pref.UI_style
|
||||
S["UI_style_color"] << pref.UI_style_color
|
||||
S["UI_style_alpha"] << pref.UI_style_alpha
|
||||
S["tgui_fancy"] << pref.tgui_fancy
|
||||
S["tgui_lock"] << pref.tgui_lock
|
||||
S["ooccolor"] << pref.ooccolor
|
||||
S["clientfps"] << pref.clientfps
|
||||
@@ -39,7 +37,6 @@
|
||||
"UI_style",
|
||||
"UI_style_color",
|
||||
"UI_style_alpha",
|
||||
"tgui_fancy",
|
||||
"tgui_lock",
|
||||
"ooccolor",
|
||||
"clientfps",
|
||||
@@ -63,7 +60,6 @@
|
||||
"UI_style",
|
||||
"UI_style_color",
|
||||
"UI_style_alpha",
|
||||
"tgui_fancy",
|
||||
"tgui_lock",
|
||||
"ooccolor",
|
||||
"clientfps",
|
||||
@@ -83,7 +79,6 @@
|
||||
"UI_style_alpha" = pref.UI_style_alpha,
|
||||
"UI_style_color" = pref.UI_style_color,
|
||||
"UI_style" = pref.UI_style,
|
||||
"tgui_fancy" = pref.tgui_fancy,
|
||||
"tgui_lock" = pref.tgui_lock,
|
||||
"ooccolor" = pref.ooccolor,
|
||||
"clientfps" = pref.clientfps,
|
||||
@@ -100,7 +95,6 @@
|
||||
pref.UI_style_color = sanitize_hexcolor(pref.UI_style_color, initial(pref.UI_style_color))
|
||||
pref.UI_style_alpha = sanitize_integer(text2num(pref.UI_style_alpha), 0, 255, initial(pref.UI_style_alpha))
|
||||
pref.clientfps = sanitize_integer(text2num(pref.clientfps), 0, 1000, initial(pref.clientfps))
|
||||
pref.tgui_fancy = sanitize_bool(pref.tgui_fancy, TRUE)
|
||||
pref.tgui_lock = sanitize_bool(pref.tgui_lock, FALSE)
|
||||
pref.tgui_inputs = sanitize_bool(pref.tgui_inputs, TRUE)
|
||||
pref.tgui_buttons_large = sanitize_bool(pref.tgui_buttons_large, FALSE)
|
||||
@@ -116,7 +110,6 @@
|
||||
dat += "-Color: <a href='byond://?src=[REF(src)];select_color=1'><b>[pref.UI_style_color]</b></a> [HTML_RECT(pref.UI_style_color)] - <a href='byond://?src=[REF(src)];reset=ui'>reset</a><br>"
|
||||
dat += "-Alpha(transparency): <a href='byond://?src=[REF(src)];select_alpha=1'><b>[pref.UI_style_alpha]</b></a> - <a href='byond://?src=[REF(src)];reset=alpha'>reset</a><br>"
|
||||
dat += "<b>Tooltip Style:</b> <a href='byond://?src=[REF(src)];select_tooltip_style=1'><b>[pref.tooltip_style]</b></a><br>"
|
||||
dat += "<b>TGUI Fancy:</b> <a href='byond://?src=[REF(src)];select_tguif=1'><b>[pref.tgui_fancy ? "ON" : "OFF"]</b></a><br>"
|
||||
dat += "<b>TGUI Lock:</b> <a href='byond://?src=[REF(src)];select_tguil=1'><b>[pref.tgui_lock ? "ON" : "OFF"]</b></a><br>"
|
||||
dat += "<b>TGUI Inputs:</b> <a href='byond://?src=[REF(src)];tgui_inputs=1'><b>[pref.tgui_inputs ? "ON" : "OFF"]</b></a><br>"
|
||||
dat += "<b>TGUI Input Large Buttons:</b> <a href='byond://?src=[REF(src)];tgui_inputs_large=1'><b>[pref.tgui_buttons_large ? "ON" : "OFF"]</b></a><br>"
|
||||
@@ -152,10 +145,6 @@
|
||||
pref.UI_style_alpha = UI_style_alpha_new
|
||||
return TOPIC_REFRESH
|
||||
|
||||
else if(href_list["select_tguif"])
|
||||
pref.tgui_fancy = !pref.tgui_fancy
|
||||
return TOPIC_REFRESH
|
||||
|
||||
else if(href_list["select_tguil"])
|
||||
pref.tgui_lock = !pref.tgui_lock
|
||||
return TOPIC_REFRESH
|
||||
|
||||
@@ -26,7 +26,6 @@ GLOBAL_LIST_EMPTY_TYPED(preferences_datums, /datum/preferences)
|
||||
var/sfx_toggles = ASFX_DEFAULT
|
||||
var/UI_style_color = "#ffffff"
|
||||
var/UI_style_alpha = 255
|
||||
var/tgui_fancy = TRUE
|
||||
var/tgui_lock = FALSE
|
||||
var/tgui_inputs = TRUE
|
||||
var/tgui_buttons_large = FALSE
|
||||
|
||||
@@ -77,6 +77,16 @@
|
||||
QDEL_LIST(accessories)
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/examine_descriptor(mob/user)
|
||||
return "clothing"
|
||||
|
||||
/obj/item/clothing/examine_tags(mob/user)
|
||||
. = ..()
|
||||
if(item_flags & ITEM_FLAG_THICK_MATERIAL)
|
||||
.["thick"] = "Stops or slows minor piercing effects, such as from injectors."
|
||||
if(item_flags & ITEM_FLAG_INJECTION_PORT)
|
||||
.["port-enabled"] = "Has a dedicated injection port for syringes and hypo-sprays to be used on the wearer."
|
||||
|
||||
//Updates the icons of the mob wearing the clothing item, if any.
|
||||
/obj/item/clothing/proc/update_clothing_icon()
|
||||
return
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
icon_state = "hardhat_yellow"
|
||||
item_state = "hardhat_yellow"
|
||||
light_overlay = "hardhat_light"
|
||||
item_flags = ITEM_FLAG_THICK_MATERIAL
|
||||
contained_sprite = TRUE
|
||||
action_button_name = "Toggle Headlamp"
|
||||
light_range = 4 //luminosity when on
|
||||
|
||||
@@ -325,9 +325,6 @@
|
||||
if(istype(exosuit) && exosuit.head && exosuit.head.radio && exosuit.head.radio.is_functional())
|
||||
return ..()
|
||||
|
||||
/obj/item/radio/exosuit/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/ui_state/state = GLOB.mech_state)
|
||||
. = ..()
|
||||
|
||||
/mob/living/heavy_vehicle/proc/become_remote()
|
||||
for(var/mob/user in pilots)
|
||||
eject(user, FALSE)
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
//TGUI
|
||||
remove_all_indicators()
|
||||
|
||||
SSnanoui.user_logout(src) // this is used to clean up (remove) this user's Nano UIs
|
||||
GLOB.player_list -= src
|
||||
disconnect_time = world.realtime
|
||||
log_access("Logout: [key_name(src)]")
|
||||
|
||||
@@ -45,8 +45,7 @@ if one is destroyed the second will take over. If all relays are gone it stops w
|
||||
possible to enable/disable Software Downloading, P2P file transfers and Communication (IC version of IRC, PDA messages for more than two people)
|
||||
|
||||
5. Software
|
||||
Software would almost exclusively use NanoUI modules. Few exceptions are text editor (uses similar screen as TCS IDE used for editing and classic HTML for previewing as Nano looks differently)
|
||||
and similar programs which for some reason require HTML UI. Most software will be highly dependent on NTNet to work as laptops are not physically connected to the station's network.
|
||||
Software would almost exclusively use TGUI modules. Most software will be highly dependent on NTNet to work as laptops are not physically connected to the station's network.
|
||||
What i plan to add:
|
||||
|
||||
Note: XXXXDB programs will use ingame_manuals to display basic help for players, similar to how books, etc. do
|
||||
|
||||
@@ -416,13 +416,10 @@
|
||||
|
||||
/obj/item/modular_computer/proc/update_uis()
|
||||
if(active_program) //Should we update program ui or computer ui?
|
||||
SSnanoui.update_uis(active_program)
|
||||
SStgui.update_uis(src)
|
||||
if(active_program.NM)
|
||||
SSnanoui.update_uis(active_program.NM)
|
||||
if(active_program)
|
||||
SStgui.update_uis(active_program)
|
||||
else
|
||||
SStgui.update_uis(src)
|
||||
SSnanoui.update_uis(src)
|
||||
|
||||
/obj/item/modular_computer/proc/check_update_ui_need()
|
||||
var/ui_update_needed = FALSE
|
||||
@@ -458,18 +455,6 @@
|
||||
if(ui_update_needed)
|
||||
update_uis()
|
||||
|
||||
// Used by camera monitor program
|
||||
/obj/item/modular_computer/check_eye(var/mob/user)
|
||||
if(active_program)
|
||||
return active_program.check_eye(user)
|
||||
return ..()
|
||||
|
||||
// Used by camera monitor program
|
||||
/obj/item/modular_computer/grants_equipment_vision(var/mob/user)
|
||||
if(active_program)
|
||||
return active_program.grants_equipment_vision(user)
|
||||
return ..()
|
||||
|
||||
/obj/item/modular_computer/get_cell()
|
||||
return battery_module ? battery_module.get_cell() : DEVICE_NO_CELL
|
||||
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
|
||||
/obj/item/modular_computer/proc/get_header_data(list/data)
|
||||
LAZYINITLIST(data)
|
||||
data["PC_batteryicon"] = get_battery_icon()
|
||||
data["PC_batteryicon"] = battery_module ? get_battery_icon() : null
|
||||
data["PC_showbatteryicon"] = !!battery_module
|
||||
data["PC_batterypercent"] = battery_module ? "[round(battery_module.battery.percent())] %" : "N/C"
|
||||
data["PC_batterypercent"] = battery_module ? "[round(battery_module.battery.percent())] %" : null
|
||||
data["PC_apclinkicon"] = (tesla_link?.enabled && apc_powered) ? "charging.gif" : ""
|
||||
data["PC_device_theme"] = active_program ? active_program.tgui_theme : "scc"
|
||||
data["PC_ntneticon"] = get_ntnet_status_icon()
|
||||
@@ -206,5 +206,15 @@
|
||||
return UI_INTERACTIVE
|
||||
. = ..()
|
||||
if(. < UI_INTERACTIVE)
|
||||
if(user.machine)
|
||||
if(user?.machine)
|
||||
user.unset_machine()
|
||||
|
||||
/obj/item/modular_computer/check_eye(mob/user)
|
||||
if(active_program)
|
||||
return active_program.check_eye(user)
|
||||
return ..()
|
||||
|
||||
/obj/item/modular_computer/grants_equipment_vision(mob/user)
|
||||
if(active_program)
|
||||
return active_program.grants_equipment_vision(user)
|
||||
return ..()
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
/obj/item/modular_computer/silicon/ui_host()
|
||||
. = computer_host
|
||||
|
||||
/obj/item/modular_computer/silicon/ui_state(mob/user)
|
||||
return GLOB.self_state
|
||||
|
||||
/obj/item/modular_computer/silicon/Initialize(mapload)
|
||||
if(istype(loc, /mob/living/silicon))
|
||||
computer_host = loc
|
||||
|
||||
@@ -84,11 +84,7 @@ ABSTRACT_TYPE(/datum/computer_file/program)
|
||||
var/tgui_theme = "scc"
|
||||
|
||||
/// If this TGUI should autoupdate or not.
|
||||
var/ui_auto_update = TRUE
|
||||
|
||||
// TO BE DEPRECATED:
|
||||
var/datum/nano_module/NM // If the program uses NanoModule, put it here and it will be automagically opened. Otherwise implement ui_interact.
|
||||
var/nanomodule_path // Path to nanomodule, make sure to set this if implementing new program.
|
||||
var/ui_auto_update = TRUE // Path to nanomodule, make sure to set this if implementing new program.
|
||||
|
||||
/datum/computer_file/program/New(obj/item/modular_computer/comp)
|
||||
..()
|
||||
@@ -136,7 +132,6 @@ ABSTRACT_TYPE(/datum/computer_file/program)
|
||||
var/datum/computer_file/program/temp = ..(rename, computer)
|
||||
temp.required_access_run = required_access_run
|
||||
temp.required_access_download = required_access_download
|
||||
temp.nanomodule_path = nanomodule_path
|
||||
temp.filedesc = filedesc
|
||||
temp.program_icon_state = program_icon_state
|
||||
temp.requires_ntnet = requires_ntnet
|
||||
@@ -200,6 +195,14 @@ ABSTRACT_TYPE(/datum/computer_file/program)
|
||||
return computer.get_ntnet_status(specific_action)
|
||||
return FALSE
|
||||
|
||||
/// Allows active programs to preserve non-standard user views, such as camera monitors.
|
||||
/datum/computer_file/program/proc/check_eye(mob/user)
|
||||
return -1
|
||||
|
||||
/// Allows active programs to grant equipment vision while controlling the user's view.
|
||||
/datum/computer_file/program/proc/grants_equipment_vision(mob/user)
|
||||
return FALSE
|
||||
|
||||
// Called by Process() on device that runs us, once every tick.
|
||||
/datum/computer_file/program/proc/process_tick()
|
||||
return TRUE
|
||||
@@ -307,31 +310,12 @@ ABSTRACT_TYPE(/datum/computer_file/program)
|
||||
else // Should never happen - So fail silently
|
||||
return FALSE
|
||||
|
||||
// This attempts to retrieve header data for NanoUIs. If implementing completely new device of different type than existing ones
|
||||
// This attempts to retrieve header data for UIs. If implementing completely new device of different type than existing ones
|
||||
// always include the device here in this proc. This proc basically relays the request to whatever is running the program.
|
||||
/datum/computer_file/program/proc/get_header_data()
|
||||
if(computer)
|
||||
return computer.get_header_data()
|
||||
|
||||
|
||||
/datum/computer_file/program/Topic(href, href_list)
|
||||
if(..())
|
||||
return TRUE
|
||||
if(computer)
|
||||
return computer.Topic(href, href_list)
|
||||
|
||||
// Relays the call to nano module, if we have one
|
||||
/datum/computer_file/program/proc/check_eye(var/mob/user)
|
||||
if(NM)
|
||||
return NM.check_eye(user)
|
||||
else
|
||||
return -1
|
||||
|
||||
/// Relays the call to nano module, if we have one
|
||||
/datum/computer_file/program/proc/grants_equipment_vision(var/mob/user)
|
||||
if(NM)
|
||||
return NM.grants_equipment_vision(user)
|
||||
|
||||
/datum/computer_file/program/proc/message_dead(var/message)
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(M.stat == DEAD && (M.client && M.client.prefs.toggles & CHAT_GHOSTEARS))
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
// When implementing new program based device, use this to run the program.
|
||||
/datum/computer_file/program/proc/run_program(var/mob/user)
|
||||
if(can_run(user, 1) || !requires_access_to_run)
|
||||
if(nanomodule_path)
|
||||
NM = new nanomodule_path(src, new /datum/topic_manager/program(src), src)
|
||||
if(requires_ntnet && network_destination)
|
||||
generate_network_log("Connection opened to [network_destination].")
|
||||
program_state = PROGRAM_STATE_ACTIVE
|
||||
@@ -18,9 +16,6 @@
|
||||
program_state = PROGRAM_STATE_KILLED
|
||||
if(network_destination)
|
||||
generate_network_log("Connection to [network_destination] closed.")
|
||||
if(NM)
|
||||
qdel(NM)
|
||||
NM = null
|
||||
return TRUE
|
||||
|
||||
// Is called when program service is being activated
|
||||
|
||||
@@ -1,39 +1,5 @@
|
||||
/obj/item/modular_computer/initial_data()
|
||||
/obj/item/modular_computer/proc/initial_data()
|
||||
return list("_PC" = get_header_data())
|
||||
|
||||
/datum/computer_file/program/initial_data()
|
||||
/datum/computer_file/program/proc/initial_data()
|
||||
return list("_PC" = get_header_data())
|
||||
|
||||
/obj/item/modular_computer/update_layout()
|
||||
return TRUE
|
||||
|
||||
/datum/computer_file/program/update_layout()
|
||||
return TRUE
|
||||
|
||||
/datum/nano_module/program
|
||||
available_to_ai = FALSE
|
||||
var/datum/computer_file/program/program // Program-Based computer program that runs this nano module. Defaults to null.
|
||||
|
||||
/datum/nano_module/program/New(var/host, var/topic_manager, var/program)
|
||||
..()
|
||||
src.program = program
|
||||
|
||||
/datum/nano_module/program/Destroy()
|
||||
program = null
|
||||
return ..()
|
||||
|
||||
/datum/topic_manager/program
|
||||
var/datum/program
|
||||
|
||||
/datum/topic_manager/program/New(var/datum/program)
|
||||
..()
|
||||
src.program = program
|
||||
|
||||
/datum/topic_manager/program/Destroy()
|
||||
program = null
|
||||
return ..()
|
||||
|
||||
// Calls forwarded to PROGRAM itself should begin with "PRG_"
|
||||
// Calls forwarded to COMPUTER running the program should begin with "PC_"
|
||||
/datum/topic_manager/program/Topic(href, href_list)
|
||||
return program && program.Topic(href, href_list)
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
available_on_ntnet = TRUE
|
||||
usage_flags = PROGRAM_ALL
|
||||
tgui_id = "CargoOrder"
|
||||
ui_auto_update = FALSE
|
||||
|
||||
var/page = "main" //main - Main Menu, order - Order Page, item_details - Item Details Page, tracking - Tracking Page
|
||||
var/selected_category = null // Category that is currently selected
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
size = 8
|
||||
color = LIGHT_COLOR_BLUE
|
||||
tgui_id = "IDCardModification"
|
||||
ui_auto_update = FALSE
|
||||
var/is_centcom = FALSE
|
||||
var/show_assignments = FALSE
|
||||
|
||||
|
||||
+6
-2
@@ -12,7 +12,6 @@
|
||||
network_destination = "station long-range communication array"
|
||||
color = LIGHT_COLOR_BLUE
|
||||
tgui_id = "CommandCommunications"
|
||||
ui_auto_update = FALSE
|
||||
var/datum/comm_message_listener/message_core
|
||||
var/intercept = FALSE
|
||||
var/can_call_shuttle = FALSE //If calling the shuttle should be available from this console
|
||||
@@ -272,7 +271,12 @@ GLOBAL_VAR_INIT(last_message_id, 0)
|
||||
/datum/comm_message_listener/proc/Add(var/list/message)
|
||||
messages[++messages.len] = message
|
||||
|
||||
/datum/comm_message_listener/proc/Remove(var/list/message)
|
||||
/datum/comm_message_listener/proc/Remove(var/message)
|
||||
if(isnum(message))
|
||||
for(var/list/stored_message in messages)
|
||||
if(stored_message["id"] == message)
|
||||
message = stored_message
|
||||
break
|
||||
messages -= list(message)
|
||||
/*
|
||||
Command action procs
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
// Opens the interface for the given air alarm.
|
||||
if("alarm")
|
||||
var/obj/structure/machinery/alarm/alarm = locate(params["alarm"]) in (monitored_alarms.len ? monitored_alarms : SSmachinery.processing)
|
||||
if(alarm)
|
||||
if(alarm?.rcon_setting)
|
||||
alarm.ui_interact(usr)
|
||||
return TRUE
|
||||
// Manually clear and repopulate the alarm list.
|
||||
@@ -65,6 +65,8 @@
|
||||
|
||||
var/alarms = list()
|
||||
for(var/obj/structure/machinery/alarm/alarm in monitored_alarms)
|
||||
if(!alarm.rcon_setting)
|
||||
continue
|
||||
alarms += list(list(
|
||||
"deck" = alarm.alarm_area.horizon_deck,
|
||||
"dept" = alarm.alarm_area.department,
|
||||
@@ -77,4 +79,3 @@
|
||||
data["alarms"] = alarms
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
..()
|
||||
lstate = SSnightlight.is_active() ? "dark" : "full"
|
||||
|
||||
/datum/computer_file/program/lighting_control/proc/update_lighting()
|
||||
/datum/computer_file/program/lighting_control/proc/update_lighting(var/new_context = context, var/new_lstate = lstate)
|
||||
context = new_context
|
||||
lstate = new_lstate
|
||||
|
||||
// whether to only select areas explicitly marked for nightlighting
|
||||
var/wl_only = context == "all" ? 0 : 1
|
||||
@@ -48,7 +50,8 @@
|
||||
lstate = params["mode"]
|
||||
. = TRUE
|
||||
if ("set")
|
||||
update_lighting()
|
||||
update_lighting(params["context"] || context, params["mode"] || lstate)
|
||||
. = TRUE
|
||||
|
||||
/datum/computer_file/program/lighting_control/ui_data(mob/user)
|
||||
var/list/data = initial_data()
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
color = LIGHT_COLOR_YELLOW
|
||||
tgui_id = "RCON"
|
||||
tgui_theme = "hephaestus"
|
||||
ui_auto_update = FALSE
|
||||
|
||||
/datum/computer_file/program/rcon_console/New()
|
||||
..()
|
||||
|
||||
@@ -162,9 +162,10 @@
|
||||
return FALSE
|
||||
|
||||
set_current(C)
|
||||
user.set_machine(ui_host())
|
||||
var/obj/host = ui_host()
|
||||
if(host)
|
||||
user.set_machine(host)
|
||||
user.reset_view(current_camera)
|
||||
check_eye(user)
|
||||
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
@@ -172,6 +173,22 @@
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/computer_file/program/camera_monitor/check_eye(mob/user)
|
||||
if(user.stat || user.blinded)
|
||||
return -1
|
||||
if(!current_camera)
|
||||
return 0
|
||||
|
||||
var/viewflag = current_camera.check_eye(user)
|
||||
if(viewflag < 0)
|
||||
reset_current()
|
||||
return viewflag
|
||||
|
||||
/datum/computer_file/program/camera_monitor/grants_equipment_vision(mob/user)
|
||||
if(user.stat || user.blinded || !current_camera)
|
||||
return FALSE
|
||||
return current_camera.grants_equipment_vision(user)
|
||||
|
||||
/datum/computer_file/program/camera_monitor/proc/set_current(var/obj/structure/machinery/camera/C)
|
||||
if(current_camera == C)
|
||||
return
|
||||
@@ -192,44 +209,14 @@
|
||||
L.tracking_cancelled()
|
||||
current_camera = null
|
||||
|
||||
/datum/computer_file/program/camera_monitor/check_eye(var/mob/user as mob)
|
||||
var/obj/item/modular_computer/MC = user.machine
|
||||
if(istype(MC) && ui_host() == MC)
|
||||
if(!MC.working || user.blinded || user.stat)
|
||||
user.unset_machine()
|
||||
return -1
|
||||
if(!current_camera)
|
||||
return 0
|
||||
var/viewflag = current_camera.check_eye(user)
|
||||
if ( viewflag < 0 ) //camera doesn't work
|
||||
reset_current()
|
||||
return viewflag
|
||||
|
||||
/datum/computer_file/program/camera_monitor/grants_equipment_vision(mob/user)
|
||||
var/obj/item/modular_computer/MC = user.machine
|
||||
if(istype(MC) && ui_host() == MC)
|
||||
if(!MC.working || user.blinded || user.stat)
|
||||
return FALSE
|
||||
if(!current_camera)
|
||||
return FALSE
|
||||
var/viewflag = current_camera.check_eye(user)
|
||||
if (viewflag < 0) //camera doesn't work
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
// ERT Variant of the program
|
||||
/datum/computer_file/program/camera_monitor/ert
|
||||
filename = "ntcammon"
|
||||
filedesc = "Advanced Camera Monitoring"
|
||||
extended_desc = "This program allows remote access to station's camera system. Some camera networks may have additional access requirements. This version has an integrated database with additional encrypted keys."
|
||||
size = 14
|
||||
nanomodule_path = /datum/nano_module/camera_monitor/ert
|
||||
available_on_ntnet = FALSE
|
||||
|
||||
/datum/nano_module/camera_monitor/ert
|
||||
name = "Advanced Camera Monitoring Program"
|
||||
available_to_ai = FALSE
|
||||
|
||||
// The ERT variant has access to ERT and crescent cams, but still checks for accesses. ERT members should be able to use it.
|
||||
/datum/computer_file/program/camera_monitor/ert/modify_networks_list(var/list/networks)
|
||||
..()
|
||||
@@ -242,13 +229,9 @@
|
||||
filedesc = "Journalism Camera Monitoring"
|
||||
extended_desc = "This program allows remote access to station's camera system. Some camera networks may have additional access requirements. This version has has a connection to news networks."
|
||||
size = 2
|
||||
nanomodule_path = /datum/nano_module/camera_monitor/news
|
||||
required_access_download = null
|
||||
usage_flags = PROGRAM_ALL
|
||||
|
||||
/datum/nano_module/camera_monitor/news
|
||||
name = "Journalism Camera Monitoring Program"
|
||||
|
||||
/datum/computer_file/program/camera_monitor/news/modify_networks_list(var/list/networks)
|
||||
networks = list()
|
||||
networks.Add(list(list("tag" = NETWORK_NEWS, "has_access" = 1)))
|
||||
|
||||
@@ -128,7 +128,6 @@
|
||||
set_current(C)
|
||||
user.machine = ui_host()
|
||||
user.reset_view(current_camera)
|
||||
check_eye(user)
|
||||
return TRUE
|
||||
|
||||
/datum/computer_file/program/penal_mechs/proc/set_current(var/obj/structure/machinery/camera/C)
|
||||
@@ -150,11 +149,3 @@
|
||||
if(istype(L))
|
||||
L.tracking_cancelled()
|
||||
current_camera = null
|
||||
|
||||
/datum/computer_file/program/penal_mechs/check_eye(var/mob/user)
|
||||
if(!current_camera)
|
||||
return FALSE
|
||||
var/viewflag = current_camera.check_eye(user)
|
||||
if(viewflag < 0) //camera doesn't work
|
||||
reset_current()
|
||||
return viewflag
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
undeletable = TRUE
|
||||
tgui_id = "FileManager"
|
||||
var/open_file
|
||||
var/open_file_is_usb = FALSE
|
||||
var/error
|
||||
|
||||
/datum/computer_file/program/filemanager/ui_data(mob/user)
|
||||
@@ -27,7 +28,11 @@
|
||||
if(!computer || !computer.hard_drive)
|
||||
data["error"] = "I/O ERROR: Unable to access hard drive."
|
||||
else
|
||||
HDD = computer.hard_drive
|
||||
HDD = open_file_is_usb ? computer.portable_drive : computer.hard_drive
|
||||
data["file_is_usb"] = open_file_is_usb
|
||||
if(!HDD)
|
||||
data["error"] = "I/O ERROR: Unable to open file."
|
||||
return data
|
||||
file = HDD.find_file_by_name(open_file)
|
||||
script = file
|
||||
if(!istype(file))
|
||||
@@ -45,6 +50,7 @@
|
||||
else
|
||||
data["scriptdata"] = null
|
||||
data["filedata"] = null
|
||||
data["file_is_usb"] = FALSE
|
||||
data["filename"] = null
|
||||
HDD = computer.hard_drive
|
||||
RHDD = computer.portable_drive
|
||||
@@ -85,10 +91,23 @@
|
||||
return
|
||||
if(F.can_access_file(usr))
|
||||
open_file = params["PRG_openfile"]
|
||||
open_file_is_usb = FALSE
|
||||
|
||||
if("PRG_usbopenfile")
|
||||
. = TRUE
|
||||
var/obj/item/computer_hardware/hard_drive/RHDD = computer.portable_drive
|
||||
if(!RHDD)
|
||||
return
|
||||
var/datum/computer_file/F = RHDD.find_file_by_name(params["PRG_usbopenfile"])
|
||||
if(!F)
|
||||
return
|
||||
if(F.can_access_file(usr))
|
||||
open_file = params["PRG_usbopenfile"]
|
||||
open_file_is_usb = TRUE
|
||||
|
||||
if("PRG_newtextfile")
|
||||
. = TRUE
|
||||
var/newname = sanitize(input(usr, "Enter file name or leave blank to cancel:", "File rename"))
|
||||
var/newname = sanitize_filename(tgui_input_text(usr, "Enter file name or leave blank to cancel:", "File rename"))
|
||||
if(!newname)
|
||||
return TRUE
|
||||
var/obj/item/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
@@ -97,7 +116,9 @@
|
||||
var/datum/computer_file/data/F = new/datum/computer_file/data()
|
||||
F.filename = newname
|
||||
F.filetype = "TXT"
|
||||
HDD.store_file(F)
|
||||
if(!HDD.store_file(F))
|
||||
qdel(F)
|
||||
error = "I/O ERROR: Unable to create file. The hard drive may be full, read-only, or contain a conflicting file."
|
||||
|
||||
if("PRG_deletefile")
|
||||
. = TRUE
|
||||
@@ -122,6 +143,7 @@
|
||||
if("PRG_closefile")
|
||||
. = TRUE
|
||||
open_file = null
|
||||
open_file_is_usb = FALSE
|
||||
error = null
|
||||
|
||||
if("PRG_clone")
|
||||
@@ -132,8 +154,13 @@
|
||||
var/datum/computer_file/F = HDD.find_file_by_name(params["PRG_clone"])
|
||||
if(!F || !istype(F))
|
||||
return TRUE
|
||||
if(istype(F, /datum/computer_file/program))
|
||||
error = "I/O ERROR: Executable programs cannot be cloned from File Manager."
|
||||
return TRUE
|
||||
var/datum/computer_file/C = F.clone(1)
|
||||
HDD.store_file(C)
|
||||
if(!HDD.store_file(C))
|
||||
qdel(C)
|
||||
error = "I/O ERROR: Unable to clone file. The hard drive may be full, read-only, or contain a conflicting file."
|
||||
|
||||
if("PRG_rename")
|
||||
. = TRUE
|
||||
@@ -143,7 +170,7 @@
|
||||
var/datum/computer_file/file = HDD.find_file_by_name(params["PRG_rename"])
|
||||
if(!file || !istype(file))
|
||||
return TRUE
|
||||
var/newname = sanitize(input(usr, "Enter new file name:", "File rename", file.filename))
|
||||
var/newname = sanitize_filename(tgui_input_text(usr, "Enter new file name or leave blank to cancel:", "File rename", file.filename))
|
||||
if(file && newname)
|
||||
file.filename = newname
|
||||
|
||||
@@ -163,7 +190,7 @@
|
||||
var/oldtext = html_decode(F.stored_data)
|
||||
oldtext = replacetext(oldtext, "\[editorbr\]", "\n")
|
||||
|
||||
var/newtext = sanitize(replacetext(input(usr, "Editing file [open_file]. You may use most tags used in paper formatting:", "Text Editor", oldtext) as message|null, "\n", "\[editorbr\]"), MAX_TEXTFILE_LENGTH)
|
||||
var/newtext = sanitize_filename(replacetext(tgui_input_text(usr, "Editing file [open_file]. You may use most tags used in paper formatting:", "Text Editor", oldtext), "\n", "\[editorbr\]"), MAX_TEXTFILE_LENGTH)
|
||||
if(!newtext)
|
||||
return
|
||||
|
||||
@@ -185,10 +212,10 @@
|
||||
return TRUE
|
||||
var/obj/item/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
if(!HDD)
|
||||
return TRUE
|
||||
return FALSE
|
||||
if(!computer.nano_printer)
|
||||
error = "Missing Hardware: Your computer does not have required hardware to complete this operation."
|
||||
return TRUE
|
||||
return FALSE
|
||||
var/datum/computer_file/data/F = HDD.find_file_by_name(open_file)
|
||||
var/datum/computer_file/script/S = F
|
||||
if(!F)
|
||||
@@ -196,21 +223,21 @@
|
||||
if(istype(F))
|
||||
if(!computer.nano_printer.print_text(F.stored_data))
|
||||
error = "Hardware error: Printer was unable to print the file. It may be out of paper."
|
||||
return TRUE
|
||||
return FALSE
|
||||
else if(istype(S))
|
||||
if(!computer.nano_printer.print_text(S.code))
|
||||
error = "Hardware error: Printer was unable to print the file. It may be out of paper."
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
if("PRG_copytousb")
|
||||
. = TRUE
|
||||
var/obj/item/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
var/obj/item/computer_hardware/hard_drive/portable/RHDD = computer.portable_drive
|
||||
if(!HDD || !RHDD)
|
||||
return TRUE
|
||||
return FALSE
|
||||
var/datum/computer_file/F = HDD.find_file_by_name(params["PRG_copytousb"])
|
||||
if(!F || !istype(F))
|
||||
return TRUE
|
||||
return FALSE
|
||||
var/is_usr_tech_support = FALSE
|
||||
if(ishuman(usr))
|
||||
var/mob/living/carbon/human/H = usr
|
||||
@@ -270,7 +297,7 @@
|
||||
return
|
||||
if(F.password)
|
||||
return
|
||||
F.password = sanitize(input(usr, "Enter an encryption key:", "Encrypt File"))
|
||||
F.password = sanitize_filename(tgui_input_text(usr, "Enter an encryption key:", "Encrypt File"))
|
||||
|
||||
if("PRG_decrypt")
|
||||
. = TRUE
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/datum/nano_module
|
||||
var/name
|
||||
var/datum/host
|
||||
var/available_to_ai = TRUE
|
||||
var/datum/topic_manager/topic_manager
|
||||
|
||||
/datum/nano_module/New(var/datum/host, var/topic_manager)
|
||||
..()
|
||||
src.host = host
|
||||
src.topic_manager = topic_manager
|
||||
|
||||
/datum/nano_module/Destroy()
|
||||
host = null
|
||||
topic_manager = null
|
||||
return ..()
|
||||
|
||||
/datum/nano_module/ui_host()
|
||||
return host ? host.ui_host() : src
|
||||
|
||||
/datum/nano_module/proc/can_still_topic(var/datum/ui_state/state = GLOB.default_state)
|
||||
return CanUseTopic(usr, state) == STATUS_INTERACTIVE
|
||||
|
||||
/datum/nano_module/proc/check_eye(var/mob/user)
|
||||
return -1
|
||||
|
||||
/datum/nano_module/proc/grants_equipment_vision(var/mob/user)
|
||||
return FALSE
|
||||
|
||||
/datum/nano_module/proc/check_access(var/mob/user, var/access)
|
||||
if(!access)
|
||||
return 1
|
||||
|
||||
if(!istype(user))
|
||||
return 0
|
||||
|
||||
var/obj/item/card/id/I = user.GetIdCard()
|
||||
if(!I)
|
||||
return 0
|
||||
|
||||
if(access in I.access)
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
/datum/nano_module/Topic(href, href_list)
|
||||
if(topic_manager && topic_manager.Topic(href, href_list))
|
||||
return TRUE
|
||||
. = ..()
|
||||
|
||||
/datum/nano_module/nano_host()
|
||||
return host ? host : src
|
||||
|
||||
/datum/nano_module/proc/get_host_z()
|
||||
return GET_Z(nano_host())
|
||||
|
||||
/datum/proc/initial_data()
|
||||
return list()
|
||||
|
||||
/datum/proc/update_layout()
|
||||
return FALSE
|
||||
|
||||
/datum/proc/nano_host()
|
||||
return src
|
||||
@@ -1,37 +0,0 @@
|
||||
// This file contains all Nano procs/definitions for external classes/objects
|
||||
|
||||
/**
|
||||
* Called when a Nano UI window is closed
|
||||
* This is how Nano handles closed windows
|
||||
* It must be a verb so that it can be called using winset
|
||||
*/
|
||||
/client/verb/nanoclose(var/uiref as text)
|
||||
set hidden = 1 // hide this verb from the user's panel
|
||||
set name = "nanoclose"
|
||||
|
||||
var/datum/nanoui/ui = locate(uiref)
|
||||
|
||||
if (istype(ui))
|
||||
ui.close()
|
||||
|
||||
if(ui.ref)
|
||||
var/href = "close=1"
|
||||
src.Topic(href, params2list(href), ui.ref) // this will direct to the atom's Topic() proc via client.Topic()
|
||||
else if (ui.on_close_logic)
|
||||
// no atomref specified (or not found)
|
||||
// so just reset the user mob's machine var
|
||||
if(src && src.mob)
|
||||
src.mob.unset_machine()
|
||||
|
||||
/**
|
||||
* The ui_interact proc is used to open and update Nano UIs
|
||||
* If ui_interact is not used then the UI will not update correctly
|
||||
* ui_interact is currently defined for /atom/movable
|
||||
*
|
||||
* @param user /mob The mob who is interacting with this ui
|
||||
* @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main")
|
||||
* @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui
|
||||
* @param force_open boolean Force the UI to (re)open, even if it's already open
|
||||
*/
|
||||
/datum/proc/nanoui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/nanoui/master_ui = null, var/datum/ui_state/state = GLOB.default_state)
|
||||
return
|
||||
@@ -1,90 +0,0 @@
|
||||
// This file is a modified version of https://raw2.github.com/Baystation12/OldCode-BS12/master/code/TakePicture.dm
|
||||
|
||||
#define NANOMAP_ICON_SIZE 4
|
||||
#define NANOMAP_MAX_ICON_DIMENSION 1024
|
||||
|
||||
#define NANOMAP_TILES_PER_IMAGE (NANOMAP_MAX_ICON_DIMENSION / NANOMAP_ICON_SIZE)
|
||||
|
||||
#define NANOMAP_TERMINALERR 5
|
||||
#define NANOMAP_INPROGRESS 2
|
||||
#define NANOMAP_BADOUTPUT 2
|
||||
#define NANOMAP_SUCCESS 1
|
||||
#define NANOMAP_WATCHDOGSUCCESS 4
|
||||
#define NANOMAP_WATCHDOGTERMINATE 3
|
||||
|
||||
|
||||
//Call these procs to dump your world to a series of image files (!!)
|
||||
//NOTE: Does not explicitly support non 32x32 icons or stuff with large pixel_* values, so don't blame me if it doesn't work perfectly
|
||||
|
||||
/client/proc/nanomapgen_DumpImage()
|
||||
set name = "Generate NanoUI Map"
|
||||
set category = "Server"
|
||||
|
||||
if(holder)
|
||||
nanomapgen_DumpTile(1, 1, text2num(input(usr,"Enter the Z level to generate")))
|
||||
|
||||
/client/proc/nanomapgen_DumpTile(var/startX = 1, var/startY = 1, var/currentZ = 1, var/endX = -1, var/endY = -1)
|
||||
|
||||
if (endX < 0 || endX > world.maxx)
|
||||
endX = world.maxx
|
||||
|
||||
if (endY < 0 || endY > world.maxy)
|
||||
endY = world.maxy
|
||||
|
||||
if (currentZ < 0 || currentZ > world.maxz)
|
||||
to_chat(usr, "NanoMapGen: <B>ERROR: currentZ ([currentZ]) must be between 1 and [world.maxz]</B>")
|
||||
|
||||
sleep(3)
|
||||
return NANOMAP_TERMINALERR
|
||||
|
||||
if (startX > endX)
|
||||
to_chat(usr, "NanoMapGen: <B>ERROR: startX ([startX]) cannot be greater than endX ([endX])</B>")
|
||||
|
||||
sleep(3)
|
||||
return NANOMAP_TERMINALERR
|
||||
|
||||
if (startY > endX)
|
||||
to_chat(usr, "NanoMapGen: <B>ERROR: startY ([startY]) cannot be greater than endY ([endY])</B>")
|
||||
sleep(3)
|
||||
return NANOMAP_TERMINALERR
|
||||
|
||||
var/icon/Tile = icon(file("nano/mapbase1024.png"))
|
||||
if (Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION)
|
||||
log_world("ERROR: NanoMapGen: <B>ERROR: BASE IMAGE DIMENSIONS ARE NOT [NANOMAP_MAX_ICON_DIMENSION]x[NANOMAP_MAX_ICON_DIMENSION]</B>")
|
||||
sleep(3)
|
||||
return NANOMAP_TERMINALERR
|
||||
|
||||
LOG_DEBUG("NanoMapGen: <B>GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])</B>")
|
||||
to_chat(usr, "NanoMapGen: <B>GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])</B>")
|
||||
|
||||
var/count = 0;
|
||||
for(var/WorldX = startX, WorldX <= endX, WorldX++)
|
||||
for(var/WorldY = startY, WorldY <= endY, WorldY++)
|
||||
|
||||
var/atom/Turf = locate(WorldX, WorldY, currentZ)
|
||||
|
||||
var/icon/TurfIcon = new(Turf.icon, Turf.icon_state, dir = Turf.dir)
|
||||
TurfIcon.Scale(NANOMAP_ICON_SIZE, NANOMAP_ICON_SIZE)
|
||||
|
||||
Tile.Blend(TurfIcon, ICON_OVERLAY, ((WorldX - 1) * NANOMAP_ICON_SIZE), ((WorldY - 1) * NANOMAP_ICON_SIZE))
|
||||
|
||||
count++
|
||||
|
||||
if (count % 8000 == 0)
|
||||
LOG_DEBUG("NanoMapGen: <B>[count] tiles done</B>")
|
||||
sleep(1)
|
||||
|
||||
var/mapFilename = "new_Aurora[currentZ]"
|
||||
|
||||
LOG_DEBUG("NanoMapGen: <B>sending [mapFilename] to client</B>")
|
||||
|
||||
usr << browse(Tile, "window=picture;file=[mapFilename];display=0")
|
||||
|
||||
LOG_DEBUG("NanoMapGen: <B>Done.</B>")
|
||||
|
||||
to_chat(usr, "NanoMapGen: <B>Done. File [mapFilename] uploaded to your cache.</B>")
|
||||
|
||||
if (Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION)
|
||||
return NANOMAP_BADOUTPUT
|
||||
|
||||
return NANOMAP_SUCCESS
|
||||
@@ -1,537 +0,0 @@
|
||||
/**********************************************************
|
||||
NANO UI FRAMEWORK
|
||||
|
||||
nanoui class (or whatever Byond calls classes)
|
||||
|
||||
nanoui is used to open and update nano browser uis
|
||||
**********************************************************/
|
||||
|
||||
/datum/nanoui
|
||||
// the user who opened this ui
|
||||
var/mob/user
|
||||
// the object this ui "belongs" to
|
||||
var/datum/src_object
|
||||
// the title of this ui
|
||||
var/title
|
||||
// the key of this ui, this is to allow multiple (different) uis for each src_object
|
||||
var/ui_key
|
||||
// window_id is used as the window name/identifier for browse and onclose
|
||||
var/window_id
|
||||
// the browser window width
|
||||
var/width = 0
|
||||
// the browser window height
|
||||
var/height = 0
|
||||
// whether to use extra logic when window closes
|
||||
var/on_close_logic = 1
|
||||
// an extra ref to use when the window is closed, usually null
|
||||
var/atom/ref = null
|
||||
// options for modifying window behaviour
|
||||
var/window_options = "focus=0;can_close=1;can_minimize=1;can_maximize=0;can_resize=1;titlebar=1;" // window option is set using window_id
|
||||
// the list of stylesheets to apply to this ui
|
||||
var/list/stylesheets = list()
|
||||
// the list of javascript scripts to use for this ui
|
||||
var/list/scripts = list()
|
||||
// a list of templates which can be used with this ui
|
||||
var/templates[0]
|
||||
// the layout key for this ui (this is used on the frontend, leave it as "default" unless you know what you're doing)
|
||||
var/layout_key = "default"
|
||||
// this sets whether to re-render the ui layout with each update (default 0, turning on will break the map ui if it's in use)
|
||||
var/auto_update_layout = 0
|
||||
// this sets whether to re-render the ui content with each update (default 1)
|
||||
var/auto_update_content = 1
|
||||
// the default state to use for this ui (this is used on the frontend, leave it as "default" unless you know what you're doing)
|
||||
var/state_key = "default"
|
||||
// show the map ui, this is used by the default layout
|
||||
var/show_map = 0
|
||||
// the map z level to display
|
||||
var/map_z_level = 3
|
||||
// initial data, containing the full data structure, must be sent to the ui (the data structure cannot be extended later on)
|
||||
var/list/initial_data[0]
|
||||
// set to 1 to update the ui automatically every master_controller tick
|
||||
var/is_auto_updating = 0
|
||||
// the current status/visibility of the ui
|
||||
var/status = STATUS_INTERACTIVE
|
||||
|
||||
// Relationship between a master interface and its children. Used in update_status
|
||||
var/datum/nanoui/master_ui
|
||||
var/list/datum/nanoui/children = list()
|
||||
var/datum/ui_state/state = null
|
||||
|
||||
/**
|
||||
* Create a new nanoui instance.
|
||||
*
|
||||
* @param nuser /mob The mob who has opened/owns this ui
|
||||
* @param nsrc_object /obj|/mob The obj or mob which this ui belongs to
|
||||
* @param nui_key string A string key to use for this ui. Allows for multiple unique uis on one src_oject
|
||||
* @param ntemplate string The filename of the template file from /nano/templates (e.g. "my_template.tmpl")
|
||||
* @param ntitle string The title of this ui
|
||||
* @param nwidth int the width of the ui window
|
||||
* @param nheight int the height of the ui window
|
||||
* @param nref /atom A custom ref to use if "on_close_logic" is set to 1
|
||||
*
|
||||
* @return /nanoui new nanoui object
|
||||
*/
|
||||
/datum/nanoui/New(nuser, nsrc_object, nui_key, ntemplate_filename, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null, var/datum/nanoui/master_ui = null, var/datum/ui_state/state = GLOB.default_state)
|
||||
user = nuser
|
||||
src_object = nsrc_object
|
||||
ui_key = nui_key
|
||||
window_id = "[ui_key][REF(src_object)]"
|
||||
|
||||
src.master_ui = master_ui
|
||||
if(master_ui)
|
||||
master_ui.children += src
|
||||
src.state = state
|
||||
|
||||
// add the passed template filename as the "main" template, this is required
|
||||
add_template("main", ntemplate_filename)
|
||||
|
||||
if (ntitle)
|
||||
title = sanitize(ntitle)
|
||||
if (nwidth)
|
||||
width = nwidth
|
||||
if (nheight)
|
||||
height = nheight
|
||||
if (nref)
|
||||
ref = nref
|
||||
|
||||
add_common_assets()
|
||||
|
||||
var/datum/asset/assets = get_asset_datum(/datum/asset/nanoui)
|
||||
assets.send(user, assets)
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Use this proc to add assets which are common to (and required by) all nano uis
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/add_common_assets()
|
||||
add_script("https://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js")
|
||||
add_script("https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.0/jquery-ui.min.js")
|
||||
add_script("libraries.min.js") // A JS file comprising of jQuery, doT.js and jQuery Timer libraries (compressed together)
|
||||
add_script("nano_utility.js") // The NanoUtility JS, this is used to store utility functions.
|
||||
add_script("nano_template.js") // The NanoTemplate JS, this is used to render templates.
|
||||
add_script("nano_state_manager.js") // The NanoStateManager JS, it handles updates from the server and passes data to the current state
|
||||
add_script("nano_state.js") // The NanoState JS, this is the base state which all states must inherit from
|
||||
add_script("nano_state_default.js") // The NanoStateDefault JS, this is the "default" state (used by all UIs by default), which inherits from NanoState
|
||||
add_script("nano_base_callbacks.js") // The NanoBaseCallbacks JS, this is used to set up (before and after update) callbacks which are common to all UIs
|
||||
add_script("nano_base_helpers.js") // The NanoBaseHelpers JS, this is used to set up template helpers which are common to all UIs
|
||||
add_stylesheet("shared.css") // this CSS sheet is common to all UIs
|
||||
add_stylesheet("icons.css") // this CSS sheet is common to all UIs
|
||||
|
||||
/**
|
||||
* Set the current status (also known as visibility) of this ui.
|
||||
*
|
||||
* @param state int The status to set, see the defines at the top of this file
|
||||
* @param push_update int (bool) Push an update to the ui to update it's status (an update is always sent if the status has changed to red (0))
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_status(state, push_update)
|
||||
if (state != status) // Only update if it is different
|
||||
if (status == STATUS_DISABLED)
|
||||
status = state
|
||||
if (push_update)
|
||||
update()
|
||||
else
|
||||
status = state
|
||||
if (push_update || status == 0)
|
||||
push_data(null, 1) // Update the UI, force the update in case the status is 0, data is null so that previous data is used
|
||||
|
||||
/**
|
||||
* Update the status (visibility) of this ui based on the user's status
|
||||
*
|
||||
* @param push_update int (bool) Push an update to the ui to update it's status. This is set to 0/false if an update is going to be pushed anyway (to avoid unnessary updates)
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/update_status(var/push_update = 0)
|
||||
var/obj/host = src_object.ui_host()
|
||||
var/new_status = host.CanUseTopic(user, state)
|
||||
if(master_ui)
|
||||
new_status = min(new_status, master_ui.status)
|
||||
|
||||
set_status(new_status, push_update)
|
||||
if(new_status == STATUS_CLOSE)
|
||||
close()
|
||||
|
||||
/**
|
||||
* Set the ui to auto update (every master_controller tick)
|
||||
*
|
||||
* @param state int (bool) Set auto update to 1 or 0 (true/false)
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_auto_update(nstate = 1)
|
||||
is_auto_updating = nstate
|
||||
|
||||
/**
|
||||
* Set the initial data for the ui. This is vital as the data structure set here cannot be changed when pushing new updates.
|
||||
*
|
||||
* @param data /list The list of data for this ui
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_initial_data(list/data)
|
||||
initial_data = data
|
||||
|
||||
/**
|
||||
* Get config data to sent to the ui.
|
||||
*
|
||||
* @return /list config data
|
||||
*/
|
||||
/datum/nanoui/proc/get_config_data()
|
||||
var/name = "[src_object]"
|
||||
name = sanitize(name)
|
||||
var/list/config_data = list(
|
||||
"title" = title,
|
||||
"srcObject" = list("name" = name),
|
||||
"stateKey" = state_key,
|
||||
"status" = status,
|
||||
"autoUpdateLayout" = auto_update_layout,
|
||||
"autoUpdateContent" = auto_update_content,
|
||||
"showMap" = show_map,
|
||||
"mapName" = "Aurora",
|
||||
"mapZLevel" = map_z_level,
|
||||
"mapZLevels" = SSatlas.current_map.map_levels,
|
||||
"user" = list("name" = user.name)
|
||||
)
|
||||
return config_data
|
||||
|
||||
/**
|
||||
* Get data to sent to the ui.
|
||||
*
|
||||
* @param data /list The list of general data for this ui (can be null to use previous data sent)
|
||||
*
|
||||
* @return /list data to send to the ui
|
||||
*/
|
||||
/datum/nanoui/proc/get_send_data(var/list/data)
|
||||
var/list/config_data = get_config_data()
|
||||
|
||||
var/list/send_data = list("config" = config_data)
|
||||
|
||||
if (!isnull(data))
|
||||
send_data["data"] = data
|
||||
|
||||
return send_data
|
||||
|
||||
/**
|
||||
* Set the browser window options for this ui
|
||||
*
|
||||
* @param nwindow_options string The new window options
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_window_options(nwindow_options)
|
||||
window_options = nwindow_options
|
||||
|
||||
/**
|
||||
* Add a CSS stylesheet to this UI
|
||||
* These must be added before the UI has been opened, adding after that will have no effect
|
||||
*
|
||||
* @param file string The name of the CSS file from /nano/css (e.g. "my_style.css")
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/add_stylesheet(file)
|
||||
stylesheets.Add(file)
|
||||
|
||||
/**
|
||||
* Add a JavsScript script to this UI
|
||||
* These must be added before the UI has been opened, adding after that will have no effect
|
||||
*
|
||||
* @param file string The name of the JavaScript file from /nano/js (e.g. "my_script.js")
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/add_script(file)
|
||||
scripts.Add(file)
|
||||
|
||||
/**
|
||||
* Add a template for this UI
|
||||
* Templates are combined with the data sent to the UI to create the rendered view
|
||||
* These must be added before the UI has been opened, adding after that will have no effect
|
||||
*
|
||||
* @param key string The key which is used to reference this template in the frontend
|
||||
* @param filename string The name of the template file from /nano/templates (e.g. "my_template.tmpl")
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/add_template(key, filename)
|
||||
templates[key] = filename
|
||||
|
||||
/**
|
||||
* Set the layout key for use in the frontend Javascript
|
||||
* The layout key is the basic layout key for the page
|
||||
* Two files are loaded on the client based on the layout key varable:
|
||||
* -> a template in /nano/templates with the filename "layout_<layout_key>.tmpl
|
||||
* -> a CSS stylesheet in /nano/css with the filename "layout_<layout_key>.css
|
||||
*
|
||||
* @param nlayout string The layout key to use
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_layout_key(nlayout_key)
|
||||
layout_key = lowertext(nlayout_key)
|
||||
|
||||
/**
|
||||
* Set the ui to update the layout (re-render it) on each update, turning this on will break the map ui (if it's being used)
|
||||
*
|
||||
* @param state int (bool) Set update to 1 or 0 (true/false) (default 0)
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_auto_update_layout(nstate)
|
||||
auto_update_layout = nstate
|
||||
|
||||
/**
|
||||
* Set the ui to update the main content (re-render it) on each update
|
||||
*
|
||||
* @param state int (bool) Set update to 1 or 0 (true/false) (default 1)
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_auto_update_content(nstate)
|
||||
auto_update_content = nstate
|
||||
|
||||
/**
|
||||
* Set the state key for use in the frontend Javascript
|
||||
*
|
||||
* @param nstate_key string The key of the state to use
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_state_key(nstate_key)
|
||||
state_key = nstate_key
|
||||
|
||||
/**
|
||||
* Toggle showing the map ui
|
||||
*
|
||||
* @param nstate_key boolean 1 to show map, 0 to hide (default is 0)
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_show_map(nstate)
|
||||
show_map = nstate
|
||||
|
||||
/**
|
||||
* Toggle showing the map ui
|
||||
*
|
||||
* @param nstate_key boolean 1 to show map, 0 to hide (default is 0)
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_map_z_level(nz)
|
||||
map_z_level = nz
|
||||
|
||||
/**
|
||||
* Set whether or not to use the "old" on close logic (mainly unset_machine())
|
||||
*
|
||||
* @param state int (bool) Set on_close_logic to 1 or 0 (true/false)
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/use_on_close_logic(state)
|
||||
on_close_logic = state
|
||||
|
||||
/**
|
||||
* Return the HTML for this UI
|
||||
*
|
||||
* @return string HTML for the UI
|
||||
*/
|
||||
/datum/nanoui/proc/get_html()
|
||||
|
||||
// before the UI opens, add the layout files based on the layout key
|
||||
add_stylesheet("layout_[layout_key].css")
|
||||
add_template("layout", "layout_[layout_key].tmpl")
|
||||
|
||||
var/head_content = ""
|
||||
|
||||
for (var/filename in scripts)
|
||||
head_content += "<script type='text/javascript' src='[filename]'></script> "
|
||||
|
||||
for (var/filename in stylesheets)
|
||||
head_content += "<link rel='stylesheet' type='text/css' href='[filename]'> "
|
||||
|
||||
var/template_data_json = "{}" // An empty JSON object
|
||||
if (templates.len > 0)
|
||||
template_data_json = strip_improper(json_encode(templates))
|
||||
|
||||
var/list/send_data = get_send_data(initial_data)
|
||||
var/initial_data_json = replacetext(replacetext(json_encode(send_data), """, "&#34;"), "'", "'")
|
||||
initial_data_json = strip_improper(initial_data_json);
|
||||
|
||||
var/url_parameters_json = json_encode(list("src" = "[REF(src)]"))
|
||||
|
||||
return {"
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<head>
|
||||
<script type='text/javascript'>
|
||||
function receiveUpdateData(jsonString)
|
||||
{
|
||||
// We need both jQuery and NanoStateManager to be able to receive data
|
||||
// At the moment any data received before those libraries are loaded will be lost
|
||||
if (typeof NanoStateManager != 'undefined' && typeof jQuery != 'undefined')
|
||||
{
|
||||
NanoStateManager.receiveUpdateData(jsonString);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
[head_content]
|
||||
</head>
|
||||
<body scroll=auto data-template-data='[template_data_json]' data-url-parameters='[url_parameters_json]' data-initial-data='[initial_data_json]'>
|
||||
<div id='uiLayout'>
|
||||
</div>
|
||||
<noscript>
|
||||
<div id='uiNoScript'>
|
||||
<h2>JAVASCRIPT REQUIRED</h2>
|
||||
<p>Your Internet Explorer's Javascript is disabled (or broken).<br/>
|
||||
Enable Javascript and then open this UI again.</p>
|
||||
</div>
|
||||
</noscript>
|
||||
</body>
|
||||
</html>
|
||||
"}
|
||||
|
||||
/**
|
||||
* Open this UI
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/open()
|
||||
if(!user.client)
|
||||
return
|
||||
|
||||
// An attempted fix to UIs sometimes locking up spamming runtime errors due to src_object being null for whatever reason.
|
||||
// This hard-deletes the UI, preventing the device that uses the UI from being locked up permanently.
|
||||
if(!src_object)
|
||||
del(src)
|
||||
|
||||
var/window_size = ""
|
||||
if (width && height)
|
||||
window_size = "size=[width]x[height];"
|
||||
update_status(0)
|
||||
if(status == STATUS_CLOSE)
|
||||
return
|
||||
|
||||
user << browse(get_html(), "window=[window_id];[window_size][window_options]")
|
||||
winset(user, "mapwindow.map", "focus=true") // return keyboard focus to map
|
||||
on_close_winset()
|
||||
//onclose(user, window_id)
|
||||
SSnanoui.ui_opened(src)
|
||||
|
||||
/**
|
||||
* Reinitialise this UI, potentially with a different template and/or initial data
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/reinitialise(template, new_initial_data)
|
||||
if(template)
|
||||
add_template("main", template)
|
||||
if(new_initial_data)
|
||||
set_initial_data(new_initial_data)
|
||||
open()
|
||||
|
||||
/**
|
||||
* Close this UI
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/close()
|
||||
is_auto_updating = 0
|
||||
SSnanoui.ui_closed(src)
|
||||
user << browse(null, "window=[window_id]")
|
||||
for(var/datum/nanoui/child in children)
|
||||
child.close()
|
||||
children.Cut()
|
||||
state = null
|
||||
master_ui = null
|
||||
|
||||
if(istype(user))
|
||||
user.unset_machine()
|
||||
|
||||
/**
|
||||
* Set the UI window to call the nanoclose verb when the window is closed
|
||||
* This allows Nano to handle closed windows
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/on_close_winset()
|
||||
if(!user.client)
|
||||
return
|
||||
var/params = "[REF(src)]"
|
||||
|
||||
winset(user, window_id, "on-close=\"nanoclose [params]\"")
|
||||
|
||||
/**
|
||||
* Push data to an already open UI window
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/push_data(data, force_push = 0)
|
||||
update_status(0)
|
||||
if (status == STATUS_DISABLED && !force_push)
|
||||
return // Cannot update UI, no visibility
|
||||
|
||||
var/list/send_data = get_send_data(data)
|
||||
|
||||
//to_chat(user, list2json(data) // used for debugging)
|
||||
send_output(user, list2params(list(json_encode(send_data))),"[window_id].browser:receiveUpdateData")
|
||||
|
||||
/**
|
||||
* This Topic() proc is called whenever a user clicks on a link within a Nano UI
|
||||
* If the UI status is currently STATUS_INTERACTIVE then call the src_object Topic()
|
||||
* If the src_object Topic() returns 1 (true) then update all UIs attached to src_object
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/Topic(href, href_list)
|
||||
update_status(0) // update the status
|
||||
if (status != STATUS_INTERACTIVE || user != usr) // If UI is not interactive or usr calling Topic is not the UI user
|
||||
return
|
||||
|
||||
// This is used to toggle the nano map ui
|
||||
var/map_update = 0
|
||||
if(href_list["showMap"])
|
||||
set_show_map(text2num(href_list["showMap"]))
|
||||
map_update = 1
|
||||
|
||||
if(href_list["mapZLevel"])
|
||||
var/map_z = (text2num(href_list["mapZLevel"]))
|
||||
if(map_z in SSatlas.current_map.map_levels)
|
||||
set_map_z_level(map_z)
|
||||
map_update = 1
|
||||
else
|
||||
return
|
||||
|
||||
if ((src_object && src_object.Topic(href, href_list, state)) || map_update)
|
||||
SSnanoui.update_uis(src_object) // update all UIs attached to src_object
|
||||
|
||||
/**
|
||||
* Process this UI, updating the entire UI or just the status (aka visibility)
|
||||
* This process proc is called by the master_controller
|
||||
*
|
||||
* @param update string For this UI to update
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/process(update = 0)
|
||||
if (QDELETED(src_object) || QDELETED(user))
|
||||
close()
|
||||
return
|
||||
|
||||
if (status && (update || is_auto_updating))
|
||||
update() // Update the UI (update_status() is called whenever a UI is updated)
|
||||
else
|
||||
update_status(1) // Not updating UI, so lets check here if status has changed
|
||||
|
||||
/**
|
||||
* Update the UI
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/update(var/force_open = 0)
|
||||
src_object.ui_interact(user, ui_key, src, force_open, master_ui, state)
|
||||
@@ -399,17 +399,34 @@
|
||||
|
||||
ui_interact(user)
|
||||
|
||||
/obj/item/organ/internal/vaurca/preserve/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
/obj/item/organ/internal/vaurca/preserve/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "Tank", ui_x=400, ui_y=180)
|
||||
ui.open()
|
||||
|
||||
/obj/item/organ/internal/vaurca/preserve/ui_host(mob/user)
|
||||
. = ..()
|
||||
if(istype(loc,/obj/item/transfer_valve))
|
||||
return loc
|
||||
|
||||
/obj/item/organ/internal/vaurca/preserve/ui_data(mob/user)
|
||||
var/mob/living/carbon/location = null
|
||||
|
||||
if(istype(loc, /obj/item/rig))
|
||||
if(istype(loc.loc, /mob/living/carbon))
|
||||
location = loc.loc
|
||||
else if(istype(loc, /mob/living/carbon))
|
||||
location = loc
|
||||
|
||||
var/using_internal
|
||||
if(istype(location))
|
||||
if(location.internal==src)
|
||||
using_internal = 1
|
||||
|
||||
// this is the data which will be sent to the ui
|
||||
var/data[0]
|
||||
data["tankPressure"] = round(SAFE_XGM_PRESSURE(air_contents))
|
||||
var/list/data = list()
|
||||
|
||||
data["tankPressure"] = round(XGM_PRESSURE(air_contents))
|
||||
data["releasePressure"] = round(distribute_pressure ? distribute_pressure : 0)
|
||||
data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE)
|
||||
data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE)
|
||||
@@ -437,36 +454,13 @@
|
||||
if(H.head && (H.head.item_flags & ITEM_FLAG_AIRTIGHT))
|
||||
data["maskConnected"] = 1
|
||||
|
||||
// update the ui if it exists, returns null if no ui is passed/found
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
// the ui does not exist, so we'll create a new() one
|
||||
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
|
||||
ui = new(user, src, ui_key, "tanks.tmpl", "Tank", 500, 300)
|
||||
// when the ui is first opened this is the data it will use
|
||||
ui.set_initial_data(data)
|
||||
// open the new ui window
|
||||
ui.open()
|
||||
// auto update every Master Controller tick
|
||||
ui.set_auto_update(1)
|
||||
return data
|
||||
|
||||
/obj/item/organ/internal/vaurca/preserve/Topic(href, href_list)
|
||||
..()
|
||||
if (usr.stat|| usr.restrained())
|
||||
return 0
|
||||
if (src.loc != usr)
|
||||
return 0
|
||||
|
||||
if (href_list["dist_p"])
|
||||
if (href_list["dist_p"] == "reset")
|
||||
src.distribute_pressure = TANK_DEFAULT_RELEASE_PRESSURE
|
||||
else if (href_list["dist_p"] == "max")
|
||||
src.distribute_pressure = TANK_MAX_RELEASE_PRESSURE
|
||||
else
|
||||
var/cp = text2num(href_list["dist_p"])
|
||||
src.distribute_pressure += cp
|
||||
src.distribute_pressure = min(max(round(src.distribute_pressure), 0), TANK_MAX_RELEASE_PRESSURE)
|
||||
if (href_list["stat"])
|
||||
/obj/item/organ/internal/vaurca/preserve/ui_act(action,params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(action=="toggleReleaseValve")
|
||||
if(istype(loc,/mob/living/carbon))
|
||||
var/mob/living/carbon/location = loc
|
||||
if(location.internal == src)
|
||||
@@ -476,7 +470,6 @@
|
||||
if (location.internals)
|
||||
location.internals.icon_state = "internal0"
|
||||
else
|
||||
|
||||
var/can_open_valve
|
||||
if(location.wear_mask && (location.wear_mask.item_flags & ITEM_FLAG_AIRTIGHT))
|
||||
can_open_valve = 1
|
||||
@@ -491,11 +484,12 @@
|
||||
if (location.internals)
|
||||
location.internals.icon_state = "internal1"
|
||||
else
|
||||
to_chat(usr, SPAN_NOTICE("You need something to connect to \the [src]."))
|
||||
|
||||
src.add_fingerprint(usr)
|
||||
return 1
|
||||
|
||||
to_chat(usr, SPAN_WARNING("You need something to connect to \the [src]."))
|
||||
. = TRUE
|
||||
update_icon()
|
||||
if(action=="setReleasePressure")
|
||||
distribute_pressure = min(max(round(text2num(params["release_pressure"])), 0), TANK_MAX_RELEASE_PRESSURE)
|
||||
. = TRUE
|
||||
|
||||
/obj/item/organ/internal/vaurca/preserve/remove_air(amount)
|
||||
return air_contents.remove(amount)
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
/obj/structure/machinery/computer/ship/engines/ui_interact(mob/user, datum/tgui/ui)
|
||||
if(!connected)
|
||||
display_reconnect_dialog(user, "ship control systems")
|
||||
balloon_alert(user, "no connection!")
|
||||
return
|
||||
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
|
||||
@@ -117,6 +117,10 @@
|
||||
return 1
|
||||
|
||||
/obj/structure/machinery/computer/ship/helm/ui_interact(mob/user, datum/tgui/ui)
|
||||
if(!connected)
|
||||
balloon_alert(user, "no connection!")
|
||||
return
|
||||
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "Helm", capitalize_first_letters(name))
|
||||
@@ -126,55 +130,52 @@
|
||||
/obj/structure/machinery/computer/ship/helm/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
if(!connected)
|
||||
display_reconnect_dialog(user, "helm")
|
||||
else
|
||||
var/turf/T = get_turf(connected)
|
||||
var/obj/effect/overmap/visitable/sector/current_sector = locate() in T
|
||||
var/turf/T = get_turf(connected)
|
||||
var/obj/effect/overmap/visitable/sector/current_sector = locate() in T
|
||||
|
||||
data["sector"] = current_sector ? current_sector.name : "Deep Space"
|
||||
data["sector_info"] = current_sector ? current_sector.desc : "Not Available"
|
||||
data["landed"] = connected.get_landed_info()
|
||||
data["ship_coord_x"] = connected.x
|
||||
data["ship_coord_y"] = connected.y
|
||||
data["dest"] = dy && dx
|
||||
data["autopilot_x"] = dx
|
||||
data["autopilot_y"] = dy
|
||||
data["speedlimit"] = speedlimit ? speedlimit*1000 : "Halted"
|
||||
data["accel"] = get_acceleration()
|
||||
data["heading"] = connected.get_heading() ? dir2angle(connected.get_heading()) : 0
|
||||
data["direction"] = dir2angle(connected.dir)
|
||||
data["autopilot"] = autopilot
|
||||
data["manual_control"] = viewing_overmap(user)
|
||||
data["canburn"] = connected.can_burn()
|
||||
data["canturn"] = connected.can_turn()
|
||||
data["cancombatroll"] = connected.can_combat_roll()
|
||||
data["cancombatturn"] = connected.can_combat_turn()
|
||||
data["accellimit"] = accellimit*1000
|
||||
data["sector"] = current_sector ? current_sector.name : "Deep Space"
|
||||
data["sector_info"] = current_sector ? current_sector.desc : "Not Available"
|
||||
data["landed"] = connected.get_landed_info()
|
||||
data["ship_coord_x"] = connected.x
|
||||
data["ship_coord_y"] = connected.y
|
||||
data["dest"] = dy && dx
|
||||
data["autopilot_x"] = dx
|
||||
data["autopilot_y"] = dy
|
||||
data["speedlimit"] = speedlimit ? speedlimit*1000 : "Halted"
|
||||
data["accel"] = get_acceleration()
|
||||
data["heading"] = connected.get_heading() ? dir2angle(connected.get_heading()) : 0
|
||||
data["direction"] = dir2angle(connected.dir)
|
||||
data["autopilot"] = autopilot
|
||||
data["manual_control"] = viewing_overmap(user)
|
||||
data["canburn"] = connected.can_burn()
|
||||
data["canturn"] = connected.can_turn()
|
||||
data["cancombatroll"] = connected.can_combat_roll()
|
||||
data["cancombatturn"] = connected.can_combat_turn()
|
||||
data["accellimit"] = accellimit*1000
|
||||
|
||||
var/speed = round(connected.get_speed()*1000, 0.01)
|
||||
data["speed"] = speed
|
||||
if(connected.get_speed() < SHIP_SPEED_SLOW)
|
||||
data["speed_slow"] = TRUE
|
||||
if(connected.get_speed() > SHIP_SPEED_FAST)
|
||||
data["speed_fast"] = TRUE
|
||||
var/list/speed_xy = connected.get_speed_xy()
|
||||
data["ship_speed_x"] = speed_xy[1]
|
||||
data["ship_speed_y"] = speed_xy[2]
|
||||
var/speed = round(connected.get_speed()*1000, 0.01)
|
||||
data["speed"] = speed
|
||||
if(connected.get_speed() < SHIP_SPEED_SLOW)
|
||||
data["speed_slow"] = TRUE
|
||||
if(connected.get_speed() > SHIP_SPEED_FAST)
|
||||
data["speed_fast"] = TRUE
|
||||
var/list/speed_xy = connected.get_speed_xy()
|
||||
data["ship_speed_x"] = speed_xy[1]
|
||||
data["ship_speed_y"] = speed_xy[2]
|
||||
|
||||
data["ETAnext"] = get_eta()
|
||||
data["ETAnext"] = get_eta()
|
||||
|
||||
var/list/locations[0]
|
||||
for (var/key in known_sectors)
|
||||
var/datum/computer_file/data/waypoint/R = known_sectors[key]
|
||||
var/list/rdata[0]
|
||||
rdata["name"] = R.fields["name"]
|
||||
rdata["x"] = R.fields["x"]
|
||||
rdata["y"] = R.fields["y"]
|
||||
rdata["reference"] = "[REF(R)]"
|
||||
locations.Add(list(rdata))
|
||||
var/list/locations[0]
|
||||
for (var/key in known_sectors)
|
||||
var/datum/computer_file/data/waypoint/R = known_sectors[key]
|
||||
var/list/rdata[0]
|
||||
rdata["name"] = R.fields["name"]
|
||||
rdata["x"] = R.fields["x"]
|
||||
rdata["y"] = R.fields["y"]
|
||||
rdata["reference"] = "[REF(R)]"
|
||||
locations.Add(list(rdata))
|
||||
|
||||
data["locations"] = locations
|
||||
data["locations"] = locations
|
||||
|
||||
return data
|
||||
|
||||
@@ -367,6 +368,10 @@
|
||||
ui_interact(user)
|
||||
|
||||
/obj/structure/machinery/computer/ship/navigation/ui_interact(mob/user, datum/tgui/ui)
|
||||
if(!connected)
|
||||
balloon_alert(user, "no connection!")
|
||||
return
|
||||
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "Nav", capitalize_first_letters(name), ui_x=470, ui_y=320)
|
||||
@@ -376,25 +381,22 @@
|
||||
/obj/structure/machinery/computer/ship/navigation/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
if(!connected)
|
||||
display_reconnect_dialog(user, "navigation")
|
||||
else
|
||||
var/turf/T = get_turf(connected)
|
||||
var/obj/effect/overmap/visitable/sector/current_sector = locate() in T
|
||||
var/turf/T = get_turf(connected)
|
||||
var/obj/effect/overmap/visitable/sector/current_sector = locate() in T
|
||||
|
||||
data["sector"] = current_sector ? current_sector.name : "Deep Space"
|
||||
data["sector_info"] = current_sector ? current_sector.desc : "Not Available"
|
||||
data["ship_coord_x"] = connected.x
|
||||
data["ship_coord_y"] = connected.y
|
||||
data["speed"] = round(connected.get_speed()*1000, 0.01)
|
||||
data["accel"] = round(connected.get_acceleration()*1000, 0.01)
|
||||
var/list/speed_xy = connected.get_speed_xy()
|
||||
data["ship_speed_x"] = speed_xy[1]
|
||||
data["ship_speed_y"] = speed_xy[2]
|
||||
data["direction"] = dir2angle(connected.dir)
|
||||
data["heading"] = connected.get_heading() ? dir2angle(connected.get_heading()) : 0
|
||||
data["ETAnext"] = get_eta()
|
||||
data["viewing"] = viewing_overmap(user)
|
||||
data["sector"] = current_sector ? current_sector.name : "Deep Space"
|
||||
data["sector_info"] = current_sector ? current_sector.desc : "Not Available"
|
||||
data["ship_coord_x"] = connected.x
|
||||
data["ship_coord_y"] = connected.y
|
||||
data["speed"] = round(connected.get_speed()*1000, 0.01)
|
||||
data["accel"] = round(connected.get_acceleration()*1000, 0.01)
|
||||
var/list/speed_xy = connected.get_speed_xy()
|
||||
data["ship_speed_x"] = speed_xy[1]
|
||||
data["ship_speed_y"] = speed_xy[2]
|
||||
data["direction"] = dir2angle(connected.dir)
|
||||
data["heading"] = connected.get_heading() ? dir2angle(connected.get_heading()) : 0
|
||||
data["ETAnext"] = get_eta()
|
||||
data["viewing"] = viewing_overmap(user)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@@ -22,11 +22,6 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov
|
||||
. += "Consoles like these are typically access-locked."
|
||||
. += "You can remove this lock with <b>wirecutters</b>, but it would take awhile! Alternatively, you can also use a cryptographic sequencer (emag) for instant removal."
|
||||
|
||||
/obj/structure/machinery/computer/ship/proc/display_reconnect_dialog(var/mob/user, var/flavor)
|
||||
var/datum/browser/popup = new (user, "[src]", "[src]")
|
||||
popup.set_content("<center><strong><font color = 'red'>Error</strong></font><br>Unable to connect to [flavor].<br><a href='byond://?src=[REF(src)];sync=1'>Reconnect</a></center>")
|
||||
popup.open()
|
||||
|
||||
/obj/structure/machinery/computer/ship/attackby(obj/item/attacking_item, mob/user)
|
||||
if(attacking_item.tool_behaviour == TOOL_CABLECOIL) // Repair from hotwire
|
||||
var/obj/item/stack/cable_coil/C = attacking_item
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
// Powernet sensors are devices which relay information about connected powernet. This information may be relayed
|
||||
// via two procs. Proc return_reading_text will return fully HTML styled string which contains all information. This
|
||||
// may be used in PDAs or similar applications. Second proc, return_reading_data will return list containing needed data.
|
||||
// This is used in NanoUI, for example.
|
||||
|
||||
/obj/structure/machinery/power/sensor
|
||||
name = "Powernet Sensor"
|
||||
|
||||
@@ -70,4 +70,3 @@
|
||||
/obj/structure/machinery/alarm/monitor/isolation
|
||||
req_one_access = list(ACCESS_RESEARCH, ACCESS_ATMOSPHERICS, ACCESS_ENGINE_EQUIP)
|
||||
locked = 0
|
||||
remote_control = 1
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
## /TG/ Chat
|
||||
|
||||
/TG/ Chat, which will be referred to as TgChat from this point onwards, is a system in which we can send messages to clients in a controlled and semi-reliable manner. The standard way of sending messages to BYOND clients simply dumps whatever you output to them directly into their chat window, however BYOND allows us to load our own code on the client to change this behaviour in a way that allows us to do some pretty neat things.
|
||||
|
||||
### Message Format
|
||||
|
||||
TgChat handles sending messages from the server to the client through the use of JSON payloads, of which the format will change depending on the type of message and the intended client endpoint. An example of the payload for chat messages is as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"sequence": 0,
|
||||
"content": {
|
||||
"type": ". . .", // ?optional
|
||||
"text": ". . .", // ?optional !atleast-one
|
||||
"html": ". . .", // ?optional !atleast-one
|
||||
"avoidHighlighting": 0 // ?optional
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Reliability
|
||||
|
||||
In the past there have been issues where BYOND will silently and without reason lose a message we sent to the client, to detect this and recover from it seamlessly TgChat also has a baked in reliability layer. This reliability layer is very primitive, and simply keeps track of received sequence numbers. Should the client receive an unexpected sequence number TgChat asks the server to resend any missing packets.
|
||||
|
||||
### Ping System
|
||||
|
||||
TgChat supports a round trip time ping measurement, which is displayed to the client so they can know how long it takes for their commands and inputs to reach the server. This is done by sending the client a ping request, `ping/soft`, which tells the client to send a ping to the server. When the server receives said ping it sends a reply, `ping/reply`, to the client with a payload containing the current DateTime which the client can reference against the initial ping request.
|
||||
|
||||
### Chat Tabs, Local Storage, and Highlighting
|
||||
|
||||
To make organizing and managing chat easier and more functional for both players and admins, TgChat has the ability to filter out messages based on their primary tag, such as individual departmental radios, to a dedicated chat tab for easier reading and comprehension. These tabs can also be configured to highlist messages based on a simple keyword search. You can set a multitude of different keywords to search for and they will be highlighting for instant alerting of the client. Said tabs, highlighting rules, and your chat history will persist thanks to use of local storage on the client. Using local storage TgChat can ensure that your preferences are saved and maintained between client restarts and switching between other /TG/ servers. Local Storage is also used to keep your chat history aswell, should you need to scroll through your chat logs.
|
||||
@@ -35,23 +35,8 @@
|
||||
if(text) message["text"] = text
|
||||
if(html) message["html"] = html
|
||||
if(avoid_highlighting) message["avoidHighlighting"] = avoid_highlighting
|
||||
var/message_blob = TGUI_CREATE_MESSAGE("chat/message", message)
|
||||
var/message_html = message_to_html(message)
|
||||
if(islist(target))
|
||||
for(var/_target in target)
|
||||
var/client/client = CLIENT_FROM_VAR(_target)
|
||||
if(client)
|
||||
// Send to tgchat
|
||||
client.tgui_panel?.window.send_raw_message(message_blob)
|
||||
// Send to old chat
|
||||
to_target(client, message_html)
|
||||
return
|
||||
var/client/client = CLIENT_FROM_VAR(target)
|
||||
if(client)
|
||||
// Send to tgchat
|
||||
client.tgui_panel?.window.send_raw_message(message_blob)
|
||||
// Send to old chat
|
||||
to_target(client, message_html)
|
||||
// send it immediately
|
||||
SSchat.send_immediate(target, message)
|
||||
|
||||
/**
|
||||
* Sends the message to the recipient (target).
|
||||
@@ -74,7 +59,7 @@
|
||||
trailing_newline = TRUE,
|
||||
confidential = FALSE
|
||||
)
|
||||
if(isnull(Master) || !SSchat?.initialized)
|
||||
if(isnull(Master) || !SSchat?.initialized || !MC_RUNNING(SSchat.init_stage))
|
||||
to_chat_immediate(target, html, type, text, avoid_highlighting)
|
||||
return
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2017-2023 Jordan Brown
|
||||
Copyright (c) 2017-2024 Jordan Brown
|
||||
|
||||
Permission is hereby granted, free of charge,
|
||||
to any person obtaining a copy of this software and
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# DMAPI Internals
|
||||
|
||||
This folder should be placed on it's own inside a codebase that wishes to use the TGS DMAPI. Warranty void if modified.
|
||||
This folder should be placed on its own inside a codebase that wishes to use the TGS DMAPI. Warranty void if modified.
|
||||
|
||||
- [includes.dm](./includes.dm) is the file that should be included by DM code, it handles including the rest.
|
||||
- The [core](./core) folder includes all code not directly part of any API version.
|
||||
- The other versioned folders contain code for the different DMAPI versions.
|
||||
- [v3210](./v3210) contains the final TGS3 API.
|
||||
- [v4](./v4) is the legacy DMAPI 4 (Used in TGS 4.0.X versions).
|
||||
- [v5](./v5) is the current DMAPI version used by TGS >=4.1.
|
||||
- [v3210](./v3210) contains the final TGS3 API.
|
||||
- [v4](./v4) is the legacy DMAPI 4 (Used in TGS 4.0.X versions).
|
||||
- [v5](./v5) is the current DMAPI version used by TGS >=4.1.
|
||||
- [LICENSE](./LICENSE) is the MIT license for the DMAPI.
|
||||
|
||||
APIs communicate with TGS in two ways. All versions implement TGS -> DM communication using /world/Topic. DM -> TGS communication, called the bridge method, is different for each version.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
This folder contains all DMAPI code not directly involved in an API.
|
||||
|
||||
- [_definitions.dm](./definitions.dm) contains defines needed across DMAPI internals.
|
||||
- [\_definitions.dm](./definitions.dm) contains defines needed across DMAPI internals.
|
||||
- [byond_world_export.dm](./byond_world_export.dm) contains the default `/datum/tgs_http_handler` implementation which uses `world.Export()`.
|
||||
- [core.dm](./core.dm) contains the implementations of the `/world/proc/TgsXXX()` procs. Many map directly to the `/datum/tgs_api` functions. It also contains the /datum selection and setup code.
|
||||
- [datum.dm](./datum.dm) contains the `/datum/tgs_api` declarations that all APIs must implement.
|
||||
- [tgs_version.dm](./tgs_version.dm) contains the `/datum/tgs_version` definition
|
||||
-
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/datum/tgs_http_handler/byond_world_export
|
||||
|
||||
/datum/tgs_http_handler/byond_world_export/PerformGet(url)
|
||||
// This is an infinite sleep until we get a response
|
||||
var/export_response = world.Export(url)
|
||||
TGS_DEBUG_LOG("byond_world_export: Export complete")
|
||||
|
||||
if(!export_response)
|
||||
TGS_ERROR_LOG("byond_world_export: Failed request: [url]")
|
||||
return new /datum/tgs_http_result(null, FALSE)
|
||||
|
||||
var/content = export_response["CONTENT"]
|
||||
if(!content)
|
||||
TGS_ERROR_LOG("byond_world_export: Failed request, missing content!")
|
||||
return new /datum/tgs_http_result(null, FALSE)
|
||||
|
||||
var/response_json = TGS_FILE2TEXT_NATIVE(content)
|
||||
if(!response_json)
|
||||
TGS_ERROR_LOG("byond_world_export: Failed request, failed to load content!")
|
||||
return new /datum/tgs_http_result(null, FALSE)
|
||||
|
||||
return new /datum/tgs_http_result(response_json, TRUE)
|
||||
@@ -1,7 +1,7 @@
|
||||
/world/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE)
|
||||
/world/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE, datum/tgs_http_handler/http_handler = null)
|
||||
var/current_api = TGS_READ_GLOBAL(tgs)
|
||||
if(current_api)
|
||||
TGS_ERROR_LOG("API datum already set ([REF(current_api)] ([current_api]))! Was TgsNew() called more than once?")
|
||||
TGS_ERROR_LOG("API datum already set ([current_api])! Was TgsNew() called more than once?")
|
||||
return
|
||||
|
||||
if(!(minimum_required_security_level in list(TGS_SECURITY_ULTRASAFE, TGS_SECURITY_SAFE, TGS_SECURITY_TRUSTED)))
|
||||
@@ -42,11 +42,11 @@
|
||||
|
||||
var/datum/tgs_version/max_api_version = TgsMaximumApiVersion();
|
||||
if(version.suite != null && version.minor != null && version.patch != null && version.deprecated_patch != null && version.deprefixed_parameter > max_api_version.deprefixed_parameter)
|
||||
TGS_ERROR_LOG("Detected unknown API version! Defaulting to latest. Update the DMAPI to fix this problem.")
|
||||
TGS_ERROR_LOG("Detected unknown Interop API version! Defaulting to latest. Update the DMAPI to fix this problem.")
|
||||
api_datum = /datum/tgs_api/latest
|
||||
|
||||
if(!api_datum)
|
||||
TGS_ERROR_LOG("Found unsupported API version: [raw_parameter]. If this is a valid version please report this, backporting is done on demand.")
|
||||
TGS_ERROR_LOG("Found unsupported Interop API version: [raw_parameter]. If this is a valid version please report this, backporting is done on demand.")
|
||||
return
|
||||
|
||||
TGS_INFO_LOG("Activating API for version [version.deprefixed_parameter]")
|
||||
@@ -55,7 +55,10 @@
|
||||
TGS_ERROR_LOG("Invalid parameter for event_handler: [event_handler]")
|
||||
event_handler = null
|
||||
|
||||
var/datum/tgs_api/new_api = new api_datum(event_handler, version)
|
||||
if(!http_handler)
|
||||
http_handler = new /datum/tgs_http_handler/byond_world_export
|
||||
|
||||
var/datum/tgs_api/new_api = new api_datum(event_handler, version, http_handler)
|
||||
|
||||
TGS_WRITE_GLOBAL(tgs, new_api)
|
||||
|
||||
@@ -107,6 +110,13 @@
|
||||
if(api)
|
||||
return api.ApiVersion()
|
||||
|
||||
/world/TgsEngine()
|
||||
#ifdef OPENDREAM
|
||||
return TGS_ENGINE_TYPE_OPENDREAM
|
||||
#else
|
||||
return TGS_ENGINE_TYPE_BYOND
|
||||
#endif
|
||||
|
||||
/world/TgsInstanceName()
|
||||
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
|
||||
if(api)
|
||||
@@ -159,3 +169,11 @@
|
||||
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
|
||||
if(api)
|
||||
return api.Visibility()
|
||||
|
||||
/world/TgsTriggerEvent(event_name, list/parameters, wait_for_completion = FALSE)
|
||||
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
|
||||
if(api)
|
||||
if(!istype(parameters, /list))
|
||||
parameters = list()
|
||||
|
||||
return api.TriggerEvent(event_name, parameters, wait_for_completion)
|
||||
|
||||
@@ -6,8 +6,8 @@ TGS_DEFINE_AND_SET_GLOBAL(tgs, null)
|
||||
|
||||
var/list/warned_deprecated_command_runs
|
||||
|
||||
/datum/tgs_api/New(datum/tgs_event_handler/event_handler, datum/tgs_version/version)
|
||||
. = ..()
|
||||
/datum/tgs_api/New(datum/tgs_event_handler/event_handler, datum/tgs_version/version, datum/tgs_http_handler/http_handler)
|
||||
..()
|
||||
src.event_handler = event_handler
|
||||
src.version = version
|
||||
|
||||
@@ -17,7 +17,7 @@ TGS_DEFINE_AND_SET_GLOBAL(tgs, null)
|
||||
world.sleep_offline = FALSE // https://www.byond.com/forum/post/2894866
|
||||
del(world)
|
||||
world.sleep_offline = FALSE // just in case, this is BYOND after all...
|
||||
sleep(1)
|
||||
sleep(world.tick_lag)
|
||||
TGS_DEBUG_LOG("BYOND DIDN'T TERMINATE THE WORLD!!! TICK IS: [world.time], sleep_offline: [world.sleep_offline]")
|
||||
|
||||
/datum/tgs_api/latest
|
||||
@@ -69,3 +69,6 @@ TGS_PROTECT_DATUM(/datum/tgs_api)
|
||||
|
||||
/datum/tgs_api/proc/Visibility()
|
||||
return TGS_UNIMPLEMENTED
|
||||
|
||||
/datum/tgs_api/proc/TriggerEvent(event_name, list/parameters, wait_for_completion)
|
||||
return FALSE
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/datum/tgs_version/New(raw_parameter)
|
||||
..()
|
||||
src.raw_parameter = raw_parameter
|
||||
deprefixed_parameter = replacetext(raw_parameter, "/tg/station 13 Server v", "")
|
||||
var/list/version_bits = splittext(deprefixed_parameter, ".")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "core\_definitions.dm"
|
||||
#include "core\byond_world_export.dm"
|
||||
#include "core\core.dm"
|
||||
#include "core\datum.dm"
|
||||
#include "core\tgs_version.dm"
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
var/json = json_encode(data)
|
||||
|
||||
while(requesting_new_port && !override_requesting_new_port)
|
||||
sleep(1)
|
||||
sleep(world.tick_lag)
|
||||
|
||||
//we need some port open at this point to facilitate return communication
|
||||
if(!world.port)
|
||||
@@ -209,7 +209,7 @@
|
||||
requesting_new_port = FALSE
|
||||
|
||||
while(export_lock)
|
||||
sleep(1)
|
||||
sleep(world.tick_lag)
|
||||
export_lock = TRUE
|
||||
|
||||
last_interop_response = null
|
||||
@@ -217,7 +217,7 @@
|
||||
text2file(json, server_commands_json_path)
|
||||
|
||||
for(var/I = 0; I < EXPORT_TIMEOUT_DS && !last_interop_response; ++I)
|
||||
sleep(1)
|
||||
sleep(world.tick_lag)
|
||||
|
||||
if(!last_interop_response)
|
||||
TGS_ERROR_LOG("Failed to get export result for: [json]")
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
This DMAPI implements bridge requests using HTTP GET requests to TGS. It has no security restrictions.
|
||||
|
||||
- [__interop_version.dm](./__interop_version.dm) contains the version of the API used between the DMAPI and TGS.
|
||||
- [_defines.dm](./_defines.dm) contains constant definitions.
|
||||
- [\_\_interop_version.dm](./__interop_version.dm) contains the version of the API used between the DMAPI and TGS.
|
||||
- [\_defines.dm](./_defines.dm) contains constant definitions.
|
||||
- [api.dm](./api.dm) contains the bulk of the API code.
|
||||
- [bridge.dm](./bridge.dm) contains functions related to making bridge requests.
|
||||
- [chunking.dm](./chunking.dm) contains common function for splitting large raw data sets into chunks BYOND can natively process.
|
||||
|
||||
@@ -1 +1 @@
|
||||
"5.6.2"
|
||||
"5.10.1"
|
||||
|
||||
@@ -8,16 +8,17 @@
|
||||
#define DMAPI5_TOPIC_REQUEST_LIMIT 65528
|
||||
#define DMAPI5_TOPIC_RESPONSE_LIMIT 65529
|
||||
|
||||
#define DMAPI5_BRIDGE_COMMAND_PORT_UPDATE 0
|
||||
#define DMAPI5_BRIDGE_COMMAND_STARTUP 1
|
||||
#define DMAPI5_BRIDGE_COMMAND_PRIME 2
|
||||
#define DMAPI5_BRIDGE_COMMAND_REBOOT 3
|
||||
#define DMAPI5_BRIDGE_COMMAND_KILL 4
|
||||
#define DMAPI5_BRIDGE_COMMAND_CHAT_SEND 5
|
||||
#define DMAPI5_BRIDGE_COMMAND_CHUNK 6
|
||||
#define DMAPI5_BRIDGE_COMMAND_EVENT 7
|
||||
|
||||
#define DMAPI5_PARAMETER_ACCESS_IDENTIFIER "accessIdentifier"
|
||||
#define DMAPI5_PARAMETER_CUSTOM_COMMANDS "customCommands"
|
||||
#define DMAPI5_PARAMETER_TOPIC_PORT "topicPort"
|
||||
|
||||
#define DMAPI5_CHUNK "chunk"
|
||||
#define DMAPI5_CHUNK_PAYLOAD "payload"
|
||||
@@ -34,6 +35,7 @@
|
||||
#define DMAPI5_BRIDGE_PARAMETER_VERSION "version"
|
||||
#define DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE "chatMessage"
|
||||
#define DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL "minimumSecurityLevel"
|
||||
#define DMAPI5_BRIDGE_PARAMETER_EVENT_INVOCATION "eventInvocation"
|
||||
|
||||
#define DMAPI5_BRIDGE_RESPONSE_NEW_PORT "newPort"
|
||||
#define DMAPI5_BRIDGE_RESPONSE_RUNTIME_INFORMATION "runtimeInformation"
|
||||
@@ -80,6 +82,8 @@
|
||||
#define DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH 8
|
||||
#define DMAPI5_TOPIC_COMMAND_SEND_CHUNK 9
|
||||
#define DMAPI5_TOPIC_COMMAND_RECEIVE_CHUNK 10
|
||||
#define DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST 11
|
||||
#define DMAPI5_TOPIC_COMMAND_COMPLETE_EVENT 12
|
||||
|
||||
#define DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE "commandType"
|
||||
#define DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND "chatCommand"
|
||||
@@ -89,7 +93,9 @@
|
||||
#define DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME "newInstanceName"
|
||||
#define DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE "chatUpdate"
|
||||
#define DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION "newServerVersion"
|
||||
#define DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE "broadcastMessage"
|
||||
|
||||
#define DMAPI5_TOPIC_RESPONSE_CLIENT_COUNT "clientCount"
|
||||
#define DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE "commandResponse"
|
||||
#define DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE "commandResponseMessage"
|
||||
#define DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES "chatResponses"
|
||||
@@ -114,3 +120,9 @@
|
||||
#define DMAPI5_CUSTOM_CHAT_COMMAND_NAME "name"
|
||||
#define DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT "helpText"
|
||||
#define DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY "adminOnly"
|
||||
|
||||
#define DMAPI5_EVENT_ID "eventId"
|
||||
|
||||
#define DMAPI5_EVENT_INVOCATION_NAME "eventName"
|
||||
#define DMAPI5_EVENT_INVOCATION_PARAMETERS "parameters"
|
||||
#define DMAPI5_EVENT_INVOCATION_NOTIFY_COMPLETION "notifyCompletion"
|
||||
|
||||
+96
-30
@@ -8,8 +8,12 @@
|
||||
|
||||
var/reboot_mode = TGS_REBOOT_MODE_NORMAL
|
||||
|
||||
/// List of chat messages list()s that attempted to be sent during a topic call. To be bundled in the result of the call
|
||||
var/list/intercepted_message_queue
|
||||
|
||||
/// List of chat messages list()s that attempted to be sent during a topic call. To be bundled in the result of the call
|
||||
var/list/offline_message_queue
|
||||
|
||||
var/list/custom_commands
|
||||
|
||||
var/list/test_merges
|
||||
@@ -17,15 +21,23 @@
|
||||
var/list/chat_channels
|
||||
|
||||
var/initialized = FALSE
|
||||
var/initial_bridge_request_received = FALSE
|
||||
var/datum/tgs_version/interop_version
|
||||
|
||||
var/chunked_requests = 0
|
||||
var/list/chunked_topics = list()
|
||||
|
||||
var/list/pending_events = list()
|
||||
|
||||
var/detached = FALSE
|
||||
|
||||
/datum/tgs_api/v5/New()
|
||||
var/datum/tgs_http_handler/http_handler
|
||||
|
||||
/datum/tgs_api/v5/New(datum/tgs_event_handler/event_handler, datum/tgs_version/version, datum/tgs_http_handler/http_handler)
|
||||
. = ..()
|
||||
TGS_DEBUG_LOG("V5 API created")
|
||||
interop_version = version
|
||||
src.http_handler = http_handler
|
||||
TGS_DEBUG_LOG("V5 API created: [json_encode(args)]")
|
||||
|
||||
/datum/tgs_api/v5/ApiVersion()
|
||||
return new /datum/tgs_version(
|
||||
@@ -38,8 +50,14 @@
|
||||
access_identifier = world.params[DMAPI5_PARAM_ACCESS_IDENTIFIER]
|
||||
|
||||
var/datum/tgs_version/api_version = ApiVersion()
|
||||
version = null
|
||||
var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands()))
|
||||
version = null // we want this to be the TGS version, not the interop version
|
||||
|
||||
// sleep once to prevent an issue where world.Export on the first tick can hang indefinitely
|
||||
TGS_DEBUG_LOG("Starting Export bug prevention sleep tick. time:[world.time] sleep_offline:[world.sleep_offline]")
|
||||
sleep(world.tick_lag)
|
||||
TGS_DEBUG_LOG("Export bug prevention sleep complete")
|
||||
|
||||
var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands(), DMAPI5_PARAMETER_TOPIC_PORT = GetTopicPort()))
|
||||
if(!istype(bridge_response))
|
||||
TGS_ERROR_LOG("Failed initial bridge request!")
|
||||
return FALSE
|
||||
@@ -53,7 +71,8 @@
|
||||
TGS_INFO_LOG("DMAPI validation, exiting...")
|
||||
TerminateWorld()
|
||||
|
||||
version = new /datum/tgs_version(runtime_information[DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION])
|
||||
initial_bridge_request_received = TRUE
|
||||
version = new /datum/tgs_version(runtime_information[DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION]) // reassigning this because it can change if TGS updates
|
||||
security_level = runtime_information[DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL]
|
||||
visibility = runtime_information[DMAPI5_RUNTIME_INFORMATION_VISIBILITY]
|
||||
instance_name = runtime_information[DMAPI5_RUNTIME_INFORMATION_INSTANCE_NAME]
|
||||
@@ -78,7 +97,7 @@
|
||||
if(revInfo)
|
||||
tm.commit = revisionData[DMAPI5_REVISION_INFORMATION_COMMIT_SHA]
|
||||
tm.origin_commit = revisionData[DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA]
|
||||
tm.timestamp = entry[DMAPI5_REVISION_INFORMATION_TIMESTAMP]
|
||||
tm.timestamp = revisionData[DMAPI5_REVISION_INFORMATION_TIMESTAMP]
|
||||
else
|
||||
TGS_WARNING_LOG("Failed to decode [DMAPI5_TEST_MERGE_REVISION] from test merge #[tm.number]!")
|
||||
|
||||
@@ -102,15 +121,22 @@
|
||||
initialized = TRUE
|
||||
return TRUE
|
||||
|
||||
/datum/tgs_api/v5/proc/GetTopicPort()
|
||||
#if defined(OPENDREAM) && defined(OPENDREAM_TOPIC_PORT_EXISTS)
|
||||
return "[world.opendream_topic_port]"
|
||||
#else
|
||||
return null
|
||||
#endif
|
||||
|
||||
/datum/tgs_api/v5/proc/RequireInitialBridgeResponse()
|
||||
TGS_DEBUG_LOG("RequireInitialBridgeResponse()")
|
||||
var/logged = FALSE
|
||||
while(!version)
|
||||
while(!initial_bridge_request_received)
|
||||
if(!logged)
|
||||
TGS_DEBUG_LOG("RequireInitialBridgeResponse: Starting sleep")
|
||||
logged = TRUE
|
||||
|
||||
sleep(1)
|
||||
sleep(world.tick_lag)
|
||||
|
||||
TGS_DEBUG_LOG("RequireInitialBridgeResponse: Passed")
|
||||
|
||||
@@ -183,17 +209,7 @@
|
||||
var/datum/tgs_chat_channel/channel = I
|
||||
ids += channel.id
|
||||
|
||||
message2 = UpgradeDeprecatedChatMessage(message2)
|
||||
|
||||
if (!length(channels))
|
||||
return
|
||||
|
||||
var/list/data = message2._interop_serialize()
|
||||
data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = ids
|
||||
if(intercepted_message_queue)
|
||||
intercepted_message_queue += list(data)
|
||||
else
|
||||
Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data))
|
||||
SendChatMessageRaw(message2, ids)
|
||||
|
||||
/datum/tgs_api/v5/ChatTargetedBroadcast(datum/tgs_message_content/message2, admin_only)
|
||||
var/list/channels = list()
|
||||
@@ -202,32 +218,82 @@
|
||||
if (!channel.is_private_channel && ((channel.is_admin_channel && admin_only) || (!channel.is_admin_channel && !admin_only)))
|
||||
channels += channel.id
|
||||
|
||||
SendChatMessageRaw(message2, channels)
|
||||
|
||||
/datum/tgs_api/v5/ChatPrivateMessage(datum/tgs_message_content/message2, datum/tgs_chat_user/user)
|
||||
SendChatMessageRaw(message2, list(user.channel.id))
|
||||
|
||||
/datum/tgs_api/v5/proc/SendChatMessageRaw(datum/tgs_message_content/message2, list/channel_ids)
|
||||
message2 = UpgradeDeprecatedChatMessage(message2)
|
||||
|
||||
if (!length(channels))
|
||||
if (!length(channel_ids))
|
||||
return
|
||||
|
||||
var/list/data = message2._interop_serialize()
|
||||
data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = channels
|
||||
data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = channel_ids
|
||||
if(intercepted_message_queue)
|
||||
intercepted_message_queue += list(data)
|
||||
else
|
||||
Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data))
|
||||
return
|
||||
|
||||
/datum/tgs_api/v5/ChatPrivateMessage(datum/tgs_message_content/message2, datum/tgs_chat_user/user)
|
||||
message2 = UpgradeDeprecatedChatMessage(message2)
|
||||
var/list/data = message2._interop_serialize()
|
||||
data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = list(user.channel.id)
|
||||
if(intercepted_message_queue)
|
||||
intercepted_message_queue += list(data)
|
||||
if(offline_message_queue)
|
||||
offline_message_queue += list(data)
|
||||
return
|
||||
|
||||
if(detached)
|
||||
offline_message_queue = list(data)
|
||||
|
||||
WaitForReattach(FALSE)
|
||||
|
||||
data = offline_message_queue
|
||||
offline_message_queue = null
|
||||
|
||||
for(var/queued_message in data)
|
||||
SendChatDataRaw(queued_message)
|
||||
else
|
||||
Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data))
|
||||
SendChatDataRaw(data)
|
||||
|
||||
/datum/tgs_api/v5/proc/SendChatDataRaw(list/data)
|
||||
Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data))
|
||||
|
||||
/datum/tgs_api/v5/ChatChannelInfo()
|
||||
RequireInitialBridgeResponse()
|
||||
WaitForReattach(TRUE)
|
||||
return chat_channels.Copy()
|
||||
|
||||
/datum/tgs_api/v5/TriggerEvent(event_name, list/parameters, wait_for_completion)
|
||||
RequireInitialBridgeResponse()
|
||||
WaitForReattach(TRUE)
|
||||
|
||||
if(interop_version.minor < 9)
|
||||
TGS_WARNING_LOG("Interop version too low for custom events!")
|
||||
return FALSE
|
||||
|
||||
var/str_parameters = list()
|
||||
for(var/i in parameters)
|
||||
str_parameters += "[i]"
|
||||
|
||||
var/list/response = Bridge(DMAPI5_BRIDGE_COMMAND_EVENT, list(DMAPI5_BRIDGE_PARAMETER_EVENT_INVOCATION = list(DMAPI5_EVENT_INVOCATION_NAME = event_name, DMAPI5_EVENT_INVOCATION_PARAMETERS = str_parameters, DMAPI5_EVENT_INVOCATION_NOTIFY_COMPLETION = wait_for_completion)))
|
||||
if(!response)
|
||||
return FALSE
|
||||
|
||||
var/event_id = response[DMAPI5_EVENT_ID]
|
||||
if(!event_id)
|
||||
return FALSE
|
||||
|
||||
TGS_DEBUG_LOG("Created event ID: [event_id]")
|
||||
if(!wait_for_completion)
|
||||
return TRUE
|
||||
|
||||
TGS_DEBUG_LOG("Waiting for completion of event ID: [event_id]")
|
||||
|
||||
while(!pending_events[event_id])
|
||||
sleep(world.tick_lag)
|
||||
|
||||
TGS_DEBUG_LOG("Completed wait on event ID: [event_id]")
|
||||
pending_events -= event_id
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/tgs_api/v5/proc/DecodeChannels(chat_update_json)
|
||||
TGS_DEBUG_LOG("DecodeChannels()")
|
||||
var/list/chat_channels_json = chat_update_json[DMAPI5_CHAT_UPDATE_CHANNELS]
|
||||
|
||||
@@ -48,7 +48,9 @@
|
||||
var/json = CreateBridgeData(command, data, TRUE)
|
||||
var/encoded_json = url_encode(json)
|
||||
|
||||
var/url = "http://127.0.0.1:[server_port]/Bridge?[DMAPI5_BRIDGE_DATA]=[encoded_json]"
|
||||
var/api_prefix = interop_version.minor >= 8 ? "api/" : ""
|
||||
|
||||
var/url = "http://127.0.0.1:[server_port]/[api_prefix]Bridge?[DMAPI5_BRIDGE_DATA]=[encoded_json]"
|
||||
return url
|
||||
|
||||
/datum/tgs_api/v5/proc/CreateBridgeData(command, list/data, needs_auth)
|
||||
@@ -63,7 +65,7 @@
|
||||
if(detached)
|
||||
// Wait up to one minute
|
||||
for(var/i in 1 to 600)
|
||||
sleep(1)
|
||||
sleep(world.tick_lag)
|
||||
if(!detached && (!require_channels || length(chat_channels)))
|
||||
break
|
||||
|
||||
@@ -75,20 +77,25 @@
|
||||
/datum/tgs_api/v5/proc/PerformBridgeRequest(bridge_request)
|
||||
WaitForReattach(FALSE)
|
||||
|
||||
// This is an infinite sleep until we get a response
|
||||
var/export_response = world.Export(bridge_request)
|
||||
if(!export_response)
|
||||
TGS_ERROR_LOG("Failed bridge request: [bridge_request]")
|
||||
TGS_DEBUG_LOG("Bridge request start")
|
||||
var/datum/tgs_http_result/result = http_handler.PerformGet(bridge_request)
|
||||
TGS_DEBUG_LOG("Bridge request complete")
|
||||
|
||||
if(isnull(result))
|
||||
TGS_ERROR_LOG("Failed bridge request, handler returned null!")
|
||||
return
|
||||
|
||||
var/response_json = file2text(export_response["CONTENT"])
|
||||
if(!response_json)
|
||||
TGS_ERROR_LOG("Failed bridge request, missing content!")
|
||||
if(!istype(result) || result.type != /datum/tgs_http_result)
|
||||
TGS_ERROR_LOG("Failed bridge request, handler returned non-[/datum/tgs_http_result]!")
|
||||
return
|
||||
|
||||
var/list/bridge_response = json_decode(response_json)
|
||||
if(!result.success)
|
||||
TGS_DEBUG_LOG("Failed bridge request, HTTP request failed!")
|
||||
return
|
||||
|
||||
var/list/bridge_response = json_decode(result.response_text)
|
||||
if(!bridge_response)
|
||||
TGS_ERROR_LOG("Failed bridge request, bad json: [response_json]")
|
||||
TGS_ERROR_LOG("Failed bridge request, bad json: [result.response_text]")
|
||||
return
|
||||
|
||||
var/error = bridge_response[DMAPI5_RESPONSE_ERROR_MESSAGE]
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
if(DMAPI5_TOPIC_COMMAND_CHANGE_PORT)
|
||||
var/new_port = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_PORT]
|
||||
if (!isnum(new_port) || !(new_port > 0))
|
||||
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]]")
|
||||
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]")
|
||||
|
||||
if(event_handler != null)
|
||||
event_handler.HandleEvent(TGS_EVENT_PORT_SWAP, new_port)
|
||||
@@ -141,7 +141,7 @@
|
||||
if(DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE)
|
||||
var/new_port = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_PORT]
|
||||
if (!isnum(new_port) || !(new_port > 0))
|
||||
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]]")
|
||||
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]")
|
||||
|
||||
server_port = new_port
|
||||
return TopicResponse()
|
||||
@@ -149,7 +149,9 @@
|
||||
if(DMAPI5_TOPIC_COMMAND_HEALTHCHECK)
|
||||
if(event_handler && event_handler.receive_health_checks)
|
||||
event_handler.HandleEvent(TGS_EVENT_HEALTH_CHECK)
|
||||
return TopicResponse()
|
||||
var/list/health_check_response = TopicResponse()
|
||||
health_check_response[DMAPI5_TOPIC_RESPONSE_CLIENT_COUNT] = TGS_CLIENT_COUNT
|
||||
return health_check_response;
|
||||
|
||||
if(DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH)
|
||||
detached = FALSE
|
||||
@@ -157,7 +159,7 @@
|
||||
var/error_message = null
|
||||
if (new_port != null)
|
||||
if (!isnum(new_port) || !(new_port > 0))
|
||||
error_message = "Invalid [DMAPI5_TOPIC_PARAMETER_NEW_PORT]]"
|
||||
error_message = "Invalid [DMAPI5_TOPIC_PARAMETER_NEW_PORT]"
|
||||
else
|
||||
server_port = new_port
|
||||
|
||||
@@ -165,7 +167,7 @@
|
||||
if (!istext(new_version_string))
|
||||
if(error_message != null)
|
||||
error_message += ", "
|
||||
error_message += "Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION]]"
|
||||
error_message += "Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION]"
|
||||
else
|
||||
var/datum/tgs_version/new_version = new(new_version_string)
|
||||
if (event_handler)
|
||||
@@ -175,6 +177,11 @@
|
||||
|
||||
var/list/reattach_response = TopicResponse(error_message)
|
||||
reattach_response[DMAPI5_PARAMETER_CUSTOM_COMMANDS] = ListCustomCommands()
|
||||
reattach_response[DMAPI5_PARAMETER_TOPIC_PORT] = GetTopicPort()
|
||||
|
||||
for(var/eventId in pending_events)
|
||||
pending_events[eventId] = TRUE
|
||||
|
||||
return reattach_response
|
||||
|
||||
if(DMAPI5_TOPIC_COMMAND_SEND_CHUNK)
|
||||
@@ -267,4 +274,25 @@
|
||||
|
||||
return chunk_to_send
|
||||
|
||||
if(DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST)
|
||||
var/message = topic_parameters[DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE]
|
||||
if (!istext(message))
|
||||
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE]")
|
||||
|
||||
TGS_WORLD_ANNOUNCE(message)
|
||||
return TopicResponse()
|
||||
|
||||
if(DMAPI5_TOPIC_COMMAND_COMPLETE_EVENT)
|
||||
var/event_id = topic_parameters[DMAPI5_EVENT_ID]
|
||||
if (!istext(event_id))
|
||||
return TopicResponse("Invalid or missing [DMAPI5_EVENT_ID]")
|
||||
|
||||
TGS_DEBUG_LOG("Completing event ID [event_id]...")
|
||||
pending_events[event_id] = TRUE
|
||||
return TopicResponse()
|
||||
|
||||
return TopicResponse("Unknown command: [command]")
|
||||
|
||||
/datum/tgs_api/v5/proc/WorldBroadcast(message)
|
||||
set waitfor = FALSE
|
||||
TGS_WORLD_ANNOUNCE(message)
|
||||
|
||||
@@ -8,16 +8,17 @@
|
||||
#undef DMAPI5_TOPIC_REQUEST_LIMIT
|
||||
#undef DMAPI5_TOPIC_RESPONSE_LIMIT
|
||||
|
||||
#undef DMAPI5_BRIDGE_COMMAND_PORT_UPDATE
|
||||
#undef DMAPI5_BRIDGE_COMMAND_STARTUP
|
||||
#undef DMAPI5_BRIDGE_COMMAND_PRIME
|
||||
#undef DMAPI5_BRIDGE_COMMAND_REBOOT
|
||||
#undef DMAPI5_BRIDGE_COMMAND_KILL
|
||||
#undef DMAPI5_BRIDGE_COMMAND_CHAT_SEND
|
||||
#undef DMAPI5_BRIDGE_COMMAND_CHUNK
|
||||
#undef DMAPI5_BRIDGE_COMMAND_EVENT
|
||||
|
||||
#undef DMAPI5_PARAMETER_ACCESS_IDENTIFIER
|
||||
#undef DMAPI5_PARAMETER_CUSTOM_COMMANDS
|
||||
#undef DMAPI5_PARAMETER_TOPIC_PORT
|
||||
|
||||
#undef DMAPI5_CHUNK
|
||||
#undef DMAPI5_CHUNK_PAYLOAD
|
||||
@@ -34,6 +35,7 @@
|
||||
#undef DMAPI5_BRIDGE_PARAMETER_VERSION
|
||||
#undef DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE
|
||||
#undef DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL
|
||||
#undef DMAPI5_BRIDGE_PARAMETER_EVENT_INVOCATION
|
||||
|
||||
#undef DMAPI5_BRIDGE_RESPONSE_NEW_PORT
|
||||
#undef DMAPI5_BRIDGE_RESPONSE_RUNTIME_INFORMATION
|
||||
@@ -78,6 +80,10 @@
|
||||
#undef DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE
|
||||
#undef DMAPI5_TOPIC_COMMAND_HEALTHCHECK
|
||||
#undef DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH
|
||||
#undef DMAPI5_TOPIC_COMMAND_SEND_CHUNK
|
||||
#undef DMAPI5_TOPIC_COMMAND_RECEIVE_CHUNK
|
||||
#undef DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST
|
||||
#undef DMAPI5_TOPIC_COMMAND_COMPLETE_EVENT
|
||||
|
||||
#undef DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE
|
||||
#undef DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND
|
||||
@@ -87,7 +93,9 @@
|
||||
#undef DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME
|
||||
#undef DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE
|
||||
#undef DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION
|
||||
#undef DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE
|
||||
|
||||
#undef DMAPI5_TOPIC_RESPONSE_CLIENT_COUNT
|
||||
#undef DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE
|
||||
#undef DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE
|
||||
#undef DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES
|
||||
@@ -112,3 +120,9 @@
|
||||
#undef DMAPI5_CUSTOM_CHAT_COMMAND_NAME
|
||||
#undef DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT
|
||||
#undef DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY
|
||||
|
||||
#undef DMAPI5_EVENT_ID
|
||||
|
||||
#undef DMAPI5_EVENT_INVOCATION_NAME
|
||||
#undef DMAPI5_EVENT_INVOCATION_PARAMETERS
|
||||
#undef DMAPI5_EVENT_INVOCATION_NOTIFY_COMPLETION
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
*
|
||||
* required user mob The mob interacting with the UI.
|
||||
*
|
||||
* return list Statuic Data to be sent to the UI.
|
||||
* return list Static Data to be sent to the UI.
|
||||
*/
|
||||
/datum/proc/ui_static_data(mob/user)
|
||||
return list()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user