Skip to content

Commit

Permalink
TGUI 4.3: Stack, Webpack 5 & UI Tweaks, Yarn 3.6.4, TS support, Jest (P…
Browse files Browse the repository at this point in the history
…aradiseSS13#23677)

* Stack & UI's Refactor

Almost final, polish required

Stack & UI's Refactor (Part 1)

Stack & UI's Refactor (Part 2)

Stack & UI's Refactor (Part 3)

* Prettier

Maybe bad

* ClearChat & some tweaks

* Adds a Chat Reliability Layer tgstation/tgstation#79479

* Fix chat BSOD

tgstation/tgstation#79821

* WebPack 5 (Didn't work)

I hate this shit

* Yarn 3.6.4

* make it all work

* revert snowflake fix

* Stories

* adds TS support

* re-enable test and prettier

* update yarn sdk's

* Fix some box regression

* ping/reply

* Fixes regressions and some things

* Zebra and Fix chat button transfer

+rebuild

* make VSC use the proper local typescript lib

* Popper Tooltips tgstation/tgstation#58980

* Popper Tooltips performancy fixes

* Dropdown v2 tgstation/tgstation#75164

Without Icon.tsx

* BB test map

* run build

---------

Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com>
  • Loading branch information
AyIong and S34NW committed Jan 14, 2024
1 parent a10fa70 commit df50dae
Show file tree
Hide file tree
Showing 290 changed files with 15,260 additions and 13,519 deletions.
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"eamodio.gitlens",
"usernamehw.errorlens",
"anturk.dmi-editor",
"esbenp.prettier-vscode"
"esbenp.prettier-vscode",
"arcanis.vscode-zipfs"
]
}
9 changes: 8 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,15 @@
],
// ESLint settings:
"eslint.workingDirectories": [
"tgui/"
"./tgui"
],
"search.exclude": {
"**/.yarn": true,
"**/.pnp.*": true
},
"eslint.nodePath": "tgui/.yarn/sdks",
"typescript.tsdk": "tgui/.yarn/sdks/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"eslint.rules.customizations": [
// We really want to fail the CI builds on styling errors,
// but it's better to show them as yellow squigglies in IDE
Expand Down
5 changes: 5 additions & 0 deletions code/__DEFINES/chat.dm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
* SPDX-License-Identifier: MIT
*/

/// How many chat payloads to keep in history
#define CHAT_RELIABILITY_HISTORY_SIZE 5
/// How many resends to allow before giving up
#define CHAT_RELIABILITY_MAX_RESENDS 3

#define MESSAGE_TYPE_SYSTEM "system"
#define MESSAGE_TYPE_LOCALCHAT "localchat"
#define MESSAGE_TYPE_RADIO "radio"
Expand Down
98 changes: 78 additions & 20 deletions code/controllers/subsystem/SSchat.dm
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,88 @@ SUBSYSTEM_DEF(chat)
priority = FIRE_PRIORITY_CHAT
init_order = INIT_ORDER_CHAT

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) > CHAT_RELIABILITY_HISTORY_SIZE)
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())

/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)
SEND_TEXT(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
LAZYADDASSOC(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 > CHAT_RELIABILITY_MAX_RESENDS)
return // we tried but byond said no

payload.resends += 1
send_payload_to_client(client, client_history[sequence])
SSblackbox.record_feedback(
"nested tally",
"chat_resend_byond_version",
1,
list(
"[client.byond_version]",
"[client.byond_build]",
),
)
12 changes: 12 additions & 0 deletions code/datums/chat_payload.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// 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)]}"
1 change: 1 addition & 0 deletions code/modules/asset_cache/assets/asset_safe.dm
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/datum/asset/simple/safe
keep_local_name = TRUE
assets = list(
"safe_dial.png" = 'icons/safe_dial.png'
)
6 changes: 0 additions & 6 deletions code/modules/asset_cache/assets/asset_tgui.dm
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
/datum/asset/simple/tgui_common
keep_local_name = TRUE
assets = list(
"tgui-common.chunk.js" = 'tgui/public/tgui-common.chunk.js',
)

/datum/asset/simple/tgui
keep_local_name = TRUE
assets = list(
Expand Down
1 change: 0 additions & 1 deletion code/modules/tgui/tgui_datum.dm
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@
window.initialize(
fancy = (user.client.prefs.toggles2 & PREFTOGGLE_2_FANCYUI),
inline_assets = list(
get_asset_datum(/datum/asset/simple/tgui_common),
get_asset_datum(/datum/asset/simple/tgui),
))
else
Expand Down
30 changes: 30 additions & 0 deletions code/modules/tgui/tgui_panel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
## /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 recieved sequence numbers. Should the client recieve 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 recieves 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.
4 changes: 3 additions & 1 deletion code/modules/tgui/tgui_panel/tgui_panel.dm
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@
* Initializes tgui panel.
*/
/datum/tgui_panel/proc/initialize(force = FALSE)
set waitfor = FALSE
// Minimal sleep to defer initialization to after client constructor
sleep(1)
initialized_at = world.time
// Perform a clean initialization
window.initialize(inline_assets = list(
get_asset_datum(/datum/asset/simple/tgui_common),
get_asset_datum(/datum/asset/simple/tgui_panel),
))
window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/fontawesome))
Expand Down
2 changes: 1 addition & 1 deletion code/modules/tgui/tgui_panel/tgui_panel_external.dm
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*/
/client/verb/fix_tgui_panel()
set name = "Fix chat"
set category = "OOC"
set category = "Special Verbs"
var/action
log_tgui(src, "Started fixing.")

Expand Down
20 changes: 3 additions & 17 deletions code/modules/tgui/tgui_panel/to_chat.dm
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,9 @@
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
SEND_TEXT(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
SEND_TEXT(client, message_html)

// send it immediately
SSchat.send_immediate(target, message)

/**
* Sends the message to the recipient (target).
Expand Down
2 changes: 2 additions & 0 deletions code/modules/tgui/tgui_window.dm
Original file line number Diff line number Diff line change
Expand Up @@ -312,3 +312,5 @@
// Resend the assets
for(var/asset in sent_assets)
send_asset(asset)
if("chat/resend")
SSchat.handle_resend(client, payload)
1 change: 1 addition & 0 deletions paradise.dme
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@
#include "code\datums\beam.dm"
#include "code\datums\browser.dm"
#include "code\datums\callback.dm"
#include "code\datums\chat_payload.dm"
#include "code\datums\chatmessage.dm"
#include "code\datums\click_intercept.dm"
#include "code\datums\custom_user_item.dm"
Expand Down
2 changes: 1 addition & 1 deletion tgui/.eslintignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/.yarn/**
/**/node_modules
/**/*.bundle.*
/**/*.chunk.*
/**/*.hot-update.*
/packages/inferno/**
/packages/tgui/public/shim-*.js
49 changes: 39 additions & 10 deletions tgui/.eslintrc-harder.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,43 @@
rules:
## Enforce a maximum cyclomatic complexity allowed in a program
complexity: [error, { max: 25 }]
# complexity: [warn, { max: 25 }]
## Enforce consistent brace style for blocks
brace-style: [error, stroustrup, { allowSingleLine: false }]
# brace-style: [warn, stroustrup, { allowSingleLine: false }]
## Enforce the consistent use of either backticks, double, or single quotes
quotes: [error, single, {
avoidEscape: true,
allowTemplateLiterals: true,
}]
react/jsx-closing-bracket-location: [error, {
selfClosing: after-props,
nonEmpty: after-props,
}]
# quotes: [warn, single, {
# avoidEscape: true,
# allowTemplateLiterals: true,
# }]
# react/jsx-closing-bracket-location: [warn, {
# selfClosing: after-props,
# nonEmpty: after-props,
# }]
# react/display-name: warn

## Radar
## ------------------------------------------------------
# radar/cognitive-complexity: warn
radar/max-switch-cases: warn
radar/no-all-duplicated-branches: warn
radar/no-collapsible-if: warn
radar/no-collection-size-mischeck: warn
radar/no-duplicate-string: warn
radar/no-duplicated-branches: warn
radar/no-element-overwrite: warn
radar/no-extra-arguments: warn
radar/no-identical-conditions: warn
radar/no-identical-expressions: warn
radar/no-identical-functions: warn
radar/no-inverted-boolean-check: warn
radar/no-one-iteration-loop: warn
radar/no-redundant-boolean: warn
radar/no-redundant-jump: warn
radar/no-same-line-conditional: warn
radar/no-small-switch: warn
radar/no-unused-collection: warn
radar/no-use-of-empty-return-value: warn
radar/no-useless-catch: warn
radar/prefer-immediate-return: warn
radar/prefer-object-literal: warn
radar/prefer-single-boolean-return: warn
radar/prefer-while: warn
17 changes: 10 additions & 7 deletions tgui/.eslintrc.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
parser: '@babel/eslint-parser'
root: true
parser: '@typescript-eslint/parser'
parserOptions:
ecmaVersion: 2019
ecmaVersion: 2020
sourceType: module
ecmaFeatures:
jsx: true
env:
es6: true
browser: true
node: true
globals:
Byond: readonly
plugins:
- radar
- react
settings:
import/resolver:
node:
extensions: ['.js','.jsx','.ts','.tsx']
react:
version: '16.10'
rules:
Expand Down Expand Up @@ -279,7 +282,7 @@ rules:
no-shadow-restricted-names: error
## Disallow the use of undeclared variables unless mentioned
## in /*global*/ comments
no-undef: error
# no-undef: error
## Disallow initializing variables to undefined
no-undef-init: error
## Disallow the use of undefined as an identifier
Expand Down Expand Up @@ -646,7 +649,7 @@ rules:
## Enforce ES5 or ES6 class for React Components
react/prefer-es6-class: error
## Enforce that props are read-only
react/prefer-read-only-props: error
# react/prefer-read-only-props: error
## Enforce stateless React Components to be written as a pure function
react/prefer-stateless-function: error
## Prevent missing props validation in a React component definition
Expand Down Expand Up @@ -728,7 +731,7 @@ rules:
## Prevent usage of unsafe target='_blank'
react/jsx-no-target-blank: error
## Disallow undeclared variables in JSX
react/jsx-no-undef: error
# react/jsx-no-undef: error
## Disallow unnecessary fragments (fixable)
react/jsx-no-useless-fragment: error
## Limit to one expression per line in JSX
Expand Down
2 changes: 2 additions & 0 deletions tgui/.gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ bin/tgui text eol=lf
## Treat bundles as binary and ignore them during conflicts
*.bundle.* binary merge=tgui-merge-bundle
*.chunk.* binary merge=tgui-merge-bundle
.yarn/releases/**/* binary
.yarn/plugins/**/* binary

0 comments on commit df50dae

Please sign in to comment.