diff --git a/tgui/packages/common/perf.js b/tgui/packages/common/perf.js new file mode 100644 index 0000000000..319b77cea3 --- /dev/null +++ b/tgui/packages/common/perf.js @@ -0,0 +1,44 @@ +/** + * Ghetto performance measurement tools. + * + * Uses NODE_ENV to redact itself from production bundles. + * + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +let markersByLabel = {}; + +/** + * Marks a certain spot in the code for later measurements. + */ +const mark = (label, timestamp) => { + if (process.env.NODE_ENV !== 'production') { + markersByLabel[label] = timestamp || Date.now(); + } +}; + +/** + * Calculates and returns the difference between two markers as a string. + * + * Use logger.log() to print the measurement. + */ +const measure = (markerA, markerB) => { + if (process.env.NODE_ENV !== 'production') { + return timeDiff( + markersByLabel[markerA], + markersByLabel[markerB]); + } +}; + +const timeDiff = (startedAt, finishedAt) => { + const diff = Math.abs(finishedAt - startedAt); + const diffFrames = (diff / 16.6667).toFixed(2); + return `${diff}ms (${diffFrames} frames)`; +}; + +export const perf = { + mark, + measure, +}; diff --git a/tgui/packages/common/redux.js b/tgui/packages/common/redux.js index 7fb4432408..dc486ff7b8 100644 --- a/tgui/packages/common/redux.js +++ b/tgui/packages/common/redux.js @@ -69,3 +69,31 @@ export const applyMiddleware = (...middlewares) => { }; }; }; + +/** + * Combines reducers by running them in their own object namespaces as + * defined in reducersObj paramter. + * + * Main difference from redux/combineReducers is that it preserves keys + * in the state that are not present in the reducers object. This function + * is also more flexible than the redux counterpart. + */ +export const combineReducers = reducersObj => { + const keys = Object.keys(reducersObj); + let hasChanged = false; + return (prevState, action) => { + const nextState = { ...prevState }; + for (let key of keys) { + const reducer = reducersObj[key]; + const prevDomainState = prevState[key]; + const nextDomainState = reducer(prevDomainState, action); + if (prevDomainState !== nextDomainState) { + hasChanged = true; + nextState[key] = nextDomainState; + } + } + return hasChanged + ? nextState + : prevState; + }; +}; diff --git a/tgui/packages/common/storage.js b/tgui/packages/common/storage.js new file mode 100644 index 0000000000..8e1d2183e4 --- /dev/null +++ b/tgui/packages/common/storage.js @@ -0,0 +1,76 @@ +/** + * Browser-agnostic abstraction of key-value web storage. + * + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +export const STORAGE_NONE = 0; +export const STORAGE_LOCAL_STORAGE = 1; +export const STORAGE_INDEXED_DB = 2; + +const createMock = () => { + let storage = {}; + const get = key => storage[key]; + const set = (key, value) => { + storage[key] = value; + }; + const remove = key => { + storage[key] = undefined; + }; + const clear = () => { + // NOTE: On IE8, this will probably leak memory if used often. + storage = {}; + }; + return { + get, + set, + remove, + clear, + engine: STORAGE_NONE, + }; +}; + +const createLocalStorage = () => { + const get = key => { + const value = localStorage.getItem(key); + if (typeof value !== 'string') { + return; + } + return JSON.parse(value); + }; + const set = (key, value) => { + localStorage.setItem(key, JSON.stringify(value)); + }; + const remove = key => { + localStorage.removeItem(key); + }; + const clear = () => { + localStorage.clear(); + }; + return { + get, + set, + remove, + clear, + engine: STORAGE_LOCAL_STORAGE, + }; +}; + +const testLocalStorage = () => { + // Localstorage can sometimes throw an error, even if DOM storage is not + // disabled in IE11 settings. + // See: https://superuser.com/questions/1080011 + try { + return Boolean(window.localStorage && window.localStorage.getItem); + } + catch { + return false; + } +}; + +export const storage = ( + testLocalStorage() && createLocalStorage() + || createMock() +); diff --git a/tgui/packages/common/vector.js b/tgui/packages/common/vector.js index 1562df5991..c3ac350a4e 100644 --- a/tgui/packages/common/vector.js +++ b/tgui/packages/common/vector.js @@ -1,4 +1,8 @@ /** + * N-dimensional vector manipulation functions. + * + * Vectors are plain number arrays, i.e. [x, y, z]. + * * @file * @copyright 2020 Aleksej Komarov * @license MIT @@ -6,16 +10,6 @@ import { map, reduce, zipWith } from './collections'; -/** - * Creates a vector, with as many dimensions are there are arguments. - */ -export const vecCreate = (...components) => { - if (Array.isArray(components[0])) { - return [...components[0]]; - } - return components; -}; - const ADD = (a, b) => a + b; const SUB = (a, b) => a - b; const MUL = (a, b) => a * b; diff --git a/tgui/packages/tgui-dev-server/link/client.js b/tgui/packages/tgui-dev-server/link/client.js index af7a920c64..4671b340c5 100644 --- a/tgui/packages/tgui-dev-server/link/client.js +++ b/tgui/packages/tgui-dev-server/link/client.js @@ -98,8 +98,8 @@ const sendRawMessage = msg => { socket.send(json); } else { - // Keep only 10 latest messages in the queue - if (queue.length > 10) { + // Keep only 100 latest messages in the queue + if (queue.length > 100) { queue.shift(); } queue.push(json);