Update to Angular 14

This commit is contained in:
2026-03-23 20:46:24 +01:00
parent 1dea5953f6
commit f23e34dd09
33 changed files with 2483 additions and 2977 deletions
+2352 -2895
View File
File diff suppressed because it is too large Load Diff
+20 -20
View File
@@ -50,24 +50,24 @@
"webpack-cli": "^3.3.12" "webpack-cli": "^3.3.12"
}, },
"dependencies": { "dependencies": {
"@angular-devkit/build-angular": "^12.2.18", "@angular-devkit/build-angular": "^14.2.13",
"@angular/animations": "^12.2.17", "@angular/animations": "^14.3.0",
"@angular/cdk": "^12.2.13", "@angular/cdk": "^14.2.7",
"@angular/common": "^12.2.17", "@angular/common": "^14.3.0",
"@angular/compiler": "^12.2.17", "@angular/compiler": "^14.3.0",
"@angular/compiler-cli": "^12.2.17", "@angular/compiler-cli": "^14.3.0",
"@angular/core": "^12.2.17", "@angular/core": "^14.3.0",
"@angular/forms": "^12.2.17", "@angular/forms": "^14.3.0",
"@angular/platform-browser": "^12.2.17", "@angular/platform-browser": "^14.3.0",
"@angular/platform-browser-dynamic": "^12.2.17", "@angular/platform-browser-dynamic": "^14.3.0",
"@angular/platform-server": "^12.2.17", "@angular/platform-server": "^14.3.0",
"@angular/router": "^12.2.17", "@angular/router": "^14.3.0",
"@encharm/cws": "^4.0.2", "@encharm/cws": "^4.0.2",
"@fortawesome/angular-fontawesome": "^0.5.0", "@fortawesome/angular-fontawesome": "^0.11.1",
"@fortawesome/fontawesome-svg-core": "^1.2.22", "@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-brands-svg-icons": "^5.10.2", "@fortawesome/free-brands-svg-icons": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^5.10.2", "@fortawesome/free-solid-svg-icons": "^6.7.2",
"@ngtools/webpack": "^12.2.18", "@ngtools/webpack": "^14.2.13",
"@passport-next/passport-facebook": "^3.1.2", "@passport-next/passport-facebook": "^3.1.2",
"@passport-next/passport-google-oauth2": "^1.0.0", "@passport-next/passport-google-oauth2": "^1.0.0",
"@types/base64-js": "^1.2.5", "@types/base64-js": "^1.2.5",
@@ -167,7 +167,7 @@
"moment": "^2.24.0", "moment": "^2.24.0",
"mongoose": "^5.6.11", "mongoose": "^5.6.11",
"morgan": "^1.9.1", "morgan": "^1.9.1",
"ngx-bootstrap": "^5.1.1", "ngx-bootstrap": "^10.3.0",
"node-ipc": "^9.1.1", "node-ipc": "^9.1.1",
"parallel-webpack": "^2.4.0", "parallel-webpack": "^2.4.0",
"passport": "^0.4.0", "passport": "^0.4.0",
@@ -189,7 +189,7 @@
"request": "^2.88.0", "request": "^2.88.0",
"request-promise": "^4.2.4", "request-promise": "^4.2.4",
"rollbar": "^2.12.2", "rollbar": "^2.12.2",
"rxjs": "^6.6.7", "rxjs": "^7.8.2",
"sass": "^1.58.3", "sass": "^1.58.3",
"sass-loader": "^13.3.3", "sass-loader": "^13.3.3",
"serve-favicon": "^2.5.0", "serve-favicon": "^2.5.0",
@@ -205,7 +205,7 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"tslint": "^5.19.0", "tslint": "^5.19.0",
"tsutils": "^3.17.1", "tsutils": "^3.17.1",
"typescript": "^4.2.4", "typescript": "^4.7.4",
"ua-parser-js": "^0.7.20", "ua-parser-js": "^0.7.20",
"uglifyjs-webpack-plugin": "^1.3.0", "uglifyjs-webpack-plugin": "^1.3.0",
"webgl-debug": "^2.0.1", "webgl-debug": "^2.0.1",
+1 -1
View File
@@ -307,7 +307,7 @@ export function hasFeatureFlag(flag: keyof ServerFeatureFlags) {
export function hardReload() { export function hardReload() {
unregisterServiceWorker() unregisterServiceWorker()
.then(() => location.reload(true)); .then(() => location.reload());
} }
const LOGGING = false; const LOGGING = false;
+5 -4
View File
@@ -8,7 +8,8 @@ import {
FakeEntity, SelectFlags, WorldMap, EntityFlags, houseTiles, isValidTile, GraphicsQuality FakeEntity, SelectFlags, WorldMap, EntityFlags, houseTiles, isValidTile, GraphicsQuality
} from '../common/interfaces'; } from '../common/interfaces';
import { import {
clamp, lengthOfXY, setFlag, hasFlag, boundsIntersect, point, toInt, lerpColor, distanceXY clamp, lengthOfXY, setFlag, hasFlag, boundsIntersect, point, toInt, lerpColor, distanceXY,
isErrorAlike
} from '../common/utils'; } from '../common/utils';
import { import {
OFFLINE_PONY, MAX_SCALE, SUPPORTER_PONY, DISCORD_PONY, HOUR, MIN_SCALE, CAMERA_WIDTH_MIN, CAMERA_WIDTH_MAX, OFFLINE_PONY, MAX_SCALE, SUPPORTER_PONY, DISCORD_PONY, HOUR, MIN_SCALE, CAMERA_WIDTH_MIN, CAMERA_WIDTH_MAX,
@@ -767,10 +768,10 @@ export class PonyTownGame implements Game {
this.discordPony = createPony(0, 0, DISCORD_PONY, palettes.defaultPalette, this.paletteManager); this.discordPony = createPony(0, 0, DISCORD_PONY, palettes.defaultPalette, this.paletteManager);
initializeToys(this.paletteManager); initializeToys(this.paletteManager);
} catch (e) { } catch (e) {
this.errorReporter.captureEvent({ name: 'failed game.initWebGL', error: e.message, stack: e.stack }); this.errorReporter.captureEvent({ name: 'failed game.initWebGL', error: isErrorAlike(e) ? e.message : '', stack: isErrorAlike(e) ? e.stack : '' });
this.releaseWebGL(); this.releaseWebGL();
DEVELOPMENT && console.error(e); DEVELOPMENT && console.error(e);
throw new Error(`Failed to initialize graphics device (${e.message})`); throw new Error(`Failed to initialize graphics device (${isErrorAlike(e) ? e.message : 'Unknown error'})`);
} }
} }
private releaseWebGL() { private releaseWebGL() {
@@ -1478,7 +1479,7 @@ export class PonyTownGame implements Game {
const height = getMapHeightAt(this.map, this.hover.x, this.hover.y, this.time); const height = getMapHeightAt(this.map, this.hover.x, this.hover.y, this.time);
drawText(paletteBatch, `${height.toFixed(2)}`, fontSmallPal, BLACK, x, y); drawText(paletteBatch, `${height.toFixed(2)}`, fontSmallPal, BLACK, x, y);
} catch (e) { } catch (e) {
console.warn(e.message); console.warn(e);
} }
} }
+4
View File
@@ -1,3 +1,5 @@
import { isError } from "../common/utils";
export interface Game { export interface Game {
fps: number; fps: number;
load(): any; load(): any;
@@ -44,8 +46,10 @@ export function startGameLoop(game: Game, onError = (e: Error) => console.error(
game.draw(); game.draw();
} }
} catch (e) { } catch (e) {
if (isError(e)) {
onError(e); onError(e);
} }
}
last = now; last = now;
} }
+1 -1
View File
@@ -76,7 +76,7 @@ export {
faArrowUp, faArrowUp,
faArrowDown, faArrowDown,
faEyeDropper, faEyeDropper,
faPaintBrush, faPaintbrush,
faEllipsisV, faEllipsisV,
faExclamationCircle, faExclamationCircle,
faUserPlus, faUserPlus,
+1 -1
View File
@@ -69,7 +69,7 @@ export class MouseController implements InputController {
this.manager.setValue(button, 0); this.manager.setValue(button, 0);
} }
} }
private mousewheel: any = (e: MouseWheelEvent) => { private mousewheel: any = (e: WheelEvent) => {
this.manager.addValue(Key.MOUSE_WHEEL_X, clamp(e.deltaX, -1, 1)); this.manager.addValue(Key.MOUSE_WHEEL_X, clamp(e.deltaX, -1, 1));
this.manager.addValue(Key.MOUSE_WHEEL_Y, clamp(e.deltaY, -1, 1)); this.manager.addValue(Key.MOUSE_WHEEL_Y, clamp(e.deltaY, -1, 1));
} }
+2 -2
View File
@@ -716,7 +716,7 @@ function drawLeg(
} }
} }
function getEyeFrame(base: Eye, expression: Eye, anim: Eye, animationProperties: HeadAnimationProperties, blinkFrame: number) { function getEyeFrame(base: Eye, expression: Eye | -1, anim: Eye | -1, animationProperties: HeadAnimationProperties, blinkFrame: number) {
const frame = expression === -1 ? base : expression; const frame = expression === -1 ? base : expression;
if (anim !== -1) { if (anim !== -1) {
@@ -741,7 +741,7 @@ function getEyeFrame(base: Eye, expression: Eye, anim: Eye, animationProperties:
return frame; return frame;
} }
function getMouthFrame(holding: boolean, expression: Expression | undefined, headFrameMuzzle: Muzzle, function getMouthFrame(holding: boolean, expression: Expression | undefined, headFrameMuzzle: Muzzle | -1,
currentMuzzle: Muzzle | undefined, properties: HeadAnimationProperties) { currentMuzzle: Muzzle | undefined, properties: HeadAnimationProperties) {
if (holding) { if (holding) {
return Muzzle.Smile; return Muzzle.Smile;
+2 -1
View File
@@ -1,4 +1,5 @@
import { BinaryWriter, getWriterBuffer, createBinaryWriter, resizeWriter } from 'ag-sockets/dist/browser'; import { BinaryWriter, getWriterBuffer, createBinaryWriter, resizeWriter } from 'ag-sockets/dist/browser';
import { isDataViewError } from './utils';
export function writeBinary(write: (writer: BinaryWriter) => void): Uint8Array { export function writeBinary(write: (writer: BinaryWriter) => void): Uint8Array {
const writer = createBinaryWriter(); const writer = createBinaryWriter();
@@ -8,7 +9,7 @@ export function writeBinary(write: (writer: BinaryWriter) => void): Uint8Array {
write(writer); write(writer);
break; break;
} catch (e) { } catch (e) {
if (e instanceof RangeError || /DataView/.test(e.message)) { if (e instanceof RangeError || isDataViewError(e)) {
resizeWriter(writer); resizeWriter(writer);
} else { } else {
throw e; throw e;
+1 -1
View File
@@ -1479,7 +1479,7 @@ function tryRegex(value: string, flags: string) {
try { try {
return new RegExp(value, flags); return new RegExp(value, flags);
} catch (e) { } catch (e) {
console.error(e.message); console.error(e);
return new RegExp(/(?!.*)/); return new RegExp(/(?!.*)/);
} }
} }
+29 -3
View File
@@ -1,5 +1,5 @@
import { HttpErrorResponse } from '@angular/common/http'; import { HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs'; import { lastValueFrom, Observable } from 'rxjs';
import { Point, Rect, Entity, Dict, PrimitiveValues } from './interfaces'; import { Point, Rect, Entity, Dict, PrimitiveValues } from './interfaces';
import { tileWidth, tileHeight, SECOND, MINUTE, HOUR, DAY } from './constants'; import { tileWidth, tileHeight, SECOND, MINUTE, HOUR, DAY } from './constants';
import { ACCESS_ERROR, NOT_FOUND_ERROR, OFFLINE_ERROR, PROTECTION_ERROR } from './errors'; import { ACCESS_ERROR, NOT_FOUND_ERROR, OFFLINE_ERROR, PROTECTION_ERROR } from './errors';
@@ -449,7 +449,7 @@ export function delay(timeout: number) {
} }
export function observableToPromise<T>(observable: Observable<T>) { export function observableToPromise<T>(observable: Observable<T>) {
return observable.toPromise() return lastValueFrom(observable)
.catch(({ status, error }: HttpErrorResponse) => { .catch(({ status, error }: HttpErrorResponse) => {
const text = error && error.text; const text = error && error.text;
@@ -461,7 +461,7 @@ export function observableToPromise<T>(observable: Observable<T>) {
e.status = status; e.status = status;
e.text = text; e.text = text;
throw e; throw e;
}); }) as Promise<T>;
} }
// other // other
@@ -573,3 +573,29 @@ export function uniqById<T>(array: T[], uniqueKey: (self: T) => PrimitiveValues)
return !isDuplicate; return !isDuplicate;
}); });
} }
export function isErrorAlike(
errorQuestionMark: unknown,
): errorQuestionMark is { message: string; stack?: string; code?: string } {
if (errorQuestionMark && typeof errorQuestionMark === 'object') {
return 'message' in errorQuestionMark || 'stack' in errorQuestionMark || 'code' in errorQuestionMark;
}
return false;
}
export function isError(
errorQuestionMark: unknown,
): errorQuestionMark is Error {
if (errorQuestionMark instanceof Error) {
return 'message' in errorQuestionMark || 'stack' in errorQuestionMark || 'code' in errorQuestionMark;
}
return false;
}
export function isDataViewError(e: unknown) {
return isErrorAlike(e) ? /DataView/.test(e.message) : false;
}
export function isOverflowError(e: unknown) {
return e instanceof RangeError || isDataViewError(e);
}
@@ -3,6 +3,7 @@ import { compact } from 'lodash';
import { AdminModel } from '../../services/adminModel'; import { AdminModel } from '../../services/adminModel';
import { Account, GeneralSettings } from '../../../common/adminInterfaces'; import { Account, GeneralSettings } from '../../../common/adminInterfaces';
import { Subscription } from '../../../common/interfaces'; import { Subscription } from '../../../common/interfaces';
import { isErrorAlike } from '../../../common/utils';
interface Field { interface Field {
key: keyof GeneralSettings; key: keyof GeneralSettings;
@@ -65,7 +66,7 @@ export class AdminOther implements OnInit, OnDestroy {
compact(this.suspiciousPonies!.split(/\n/g).map(x => x.trim())).map(x => JSON.parse(x)); compact(this.suspiciousPonies!.split(/\n/g).map(x => x.trim())).map(x => JSON.parse(x));
this.model.updateSettings({ suspiciousPonies: this.suspiciousPonies }); this.model.updateSettings({ suspiciousPonies: this.suspiciousPonies });
} catch (e) { } catch (e) {
this.suspiciousPoniesError = e.message; this.suspiciousPoniesError = isErrorAlike(e) ? e.message : 'Unknown error';
} }
} }
resetSuspiciousPonies() { resetSuspiciousPonies() {
+2 -2
View File
@@ -9,7 +9,7 @@ import {
OAuthProvider, EntitiesEditorInfo, FriendData, PalettePonyInfo, HiddenPlayer OAuthProvider, EntitiesEditorInfo, FriendData, PalettePonyInfo, HiddenPlayer
} from '../../common/interfaces'; } from '../../common/interfaces';
import { createDefaultPony, syncLockedPonyInfo, mockPaletteManager } from '../../common/ponyInfo'; import { createDefaultPony, syncLockedPonyInfo, mockPaletteManager } from '../../common/ponyInfo';
import { removeById, observableToPromise, delay, computeFriendsCRC } from '../../common/utils'; import { removeById, observableToPromise, delay, computeFriendsCRC, isErrorAlike } from '../../common/utils';
import { isMod, getSupporterInviteLimit, getCharacterLimit } from '../../common/accountUtils'; import { isMod, getSupporterInviteLimit, getCharacterLimit } from '../../common/accountUtils';
import { import {
NAME_ERROR, ACCESS_ERROR, CHARACTER_SAVING_ERROR, NOT_AUTHENTICATED_ERROR, OFFLINE_ERROR, PROTECTION_ERROR NAME_ERROR, ACCESS_ERROR, CHARACTER_SAVING_ERROR, NOT_AUTHENTICATED_ERROR, OFFLINE_ERROR, PROTECTION_ERROR
@@ -277,7 +277,7 @@ export class Model {
return { ponyInfo, ...pony }; return { ponyInfo, ...pony };
} catch (e) { } catch (e) {
this.errorReporter.reportError(e, { ponyInfo: pony.info }); this.errorReporter.reportError(e, { ponyInfo: pony.info });
this.errorReporter.reportError('Pony info reading error', { originalError: e.message, ponyInfo: pony.info }); this.errorReporter.reportError('Pony info reading error', { originalError: isErrorAlike(e) ? e.message: '', ponyInfo: pony.info });
throw new Error('Error while reading pony info'); throw new Error('Error while reading pony info');
} }
} }
@@ -1,4 +1,4 @@
import { Component, Input, ViewChild, ElementRef } from '@angular/core'; import { Component, Input, ViewChild, ElementRef, TemplateRef } from '@angular/core';
import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal'; import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';
import { ButtonAction } from '../../../common/interfaces'; import { ButtonAction } from '../../../common/interfaces';
import { PonyTownGame } from '../../../client/game'; import { PonyTownGame } from '../../../client/game';
@@ -16,7 +16,7 @@ import { faCog } from '../../../client/icons';
}) })
export class ActionBar { export class ActionBar {
@ViewChild('scroller', { static: true }) scroller!: ElementRef; @ViewChild('scroller', { static: true }) scroller!: ElementRef;
@ViewChild('actionsModal', { static: true }) actionsModal!: ElementRef; @ViewChild('actionsModal', { static: true }) actionsModal!: TemplateRef<any>;
@Input() blurred = false; @Input() blurred = false;
readonly cogIcon = faCog; readonly cogIcon = faCog;
activeAction: ButtonAction | undefined = undefined; activeAction: ButtonAction | undefined = undefined;
@@ -68,7 +68,7 @@ export class ActionBar {
const settings = { ...this.settings.account, actions: serializeActions(this.actions) }; const settings = { ...this.settings.account, actions: serializeActions(this.actions) };
this.settings.saveAccountSettings(settings); this.settings.saveAccountSettings(settings);
} }
scroll(e: MouseWheelEvent) { scroll(e: WheelEvent) {
if (e.deltaY) { if (e.deltaY) {
const delta = e.deltaY > 0 ? 1 : -1; const delta = e.deltaY > 0 ? 1 : -1;
this.scroller.nativeElement.scrollLeft += delta * 20; this.scroller.nativeElement.scrollLeft += delta * 20;
@@ -2,7 +2,7 @@ import { Component, Input } from '@angular/core';
import { fill } from 'lodash'; import { fill } from 'lodash';
import { PonyInfo } from '../../../common/interfaces'; import { PonyInfo } from '../../../common/interfaces';
import { CM_SIZE } from '../../../common/constants'; import { CM_SIZE } from '../../../common/constants';
import { faTrash, faEraser, faPaintBrush, faEyeDropper } from '../../../client/icons'; import { faTrash, faEraser, faPaintbrush, faEyeDropper } from '../../../client/icons';
export interface ButtMarkEditorState { export interface ButtMarkEditorState {
brushType: string; brushType: string;
@@ -17,7 +17,7 @@ export class ButtMarkEditor {
readonly trashIcon = faTrash; readonly trashIcon = faTrash;
readonly eraserIcon = faEraser; readonly eraserIcon = faEraser;
readonly eyeDropperIcon = faEyeDropper; readonly eyeDropperIcon = faEyeDropper;
readonly paintBrushIcon = faPaintBrush; readonly paintBrushIcon = faPaintbrush;
readonly cmSize = CM_SIZE; readonly cmSize = CM_SIZE;
@Input() info!: PonyInfo; @Input() info!: PonyInfo;
@Input() state = { @Input() state = {
@@ -18,7 +18,7 @@ export interface AgDragOptions {
export function handleDrag(element: HTMLElement, emit: (event: AgDragEvent) => void, options: AgDragOptions = {}) { export function handleDrag(element: HTMLElement, emit: (event: AgDragEvent) => void, options: AgDragOptions = {}) {
// typeof PointerEvent !== 'undefined' // typeof PointerEvent !== 'undefined'
const eventSets = window.navigator.pointerEnabled ? [ const eventSets = 'pointerEnabled' in window.navigator && (window.navigator as any).pointerEnabled ? [
{ down: 'pointerdown', move: 'pointermove', up: 'pointerup' }, // , up2: 'pointercancel' }, { down: 'pointerdown', move: 'pointermove', up: 'pointerup' }, // , up2: 'pointercancel' },
] : [ ] : [
{ down: 'mousedown', move: 'mousemove', up: 'mouseup' }, { down: 'mousedown', move: 'mousemove', up: 'mouseup' },
@@ -156,7 +156,7 @@ export class PlayBox implements OnInit {
this.gameService.leave('Cancelled joining'); this.gameService.leave('Cancelled joining');
} }
reload() { reload() {
location.reload(true); location.reload();
} }
hardReload() { hardReload() {
hardReload(); hardReload();
+2 -2
View File
@@ -1,6 +1,6 @@
import { Palette, PaletteManager as IPaletteManager } from '../common/interfaces'; import { Palette, PaletteManager as IPaletteManager } from '../common/interfaces';
import { Texture2D, resizeTexture, disposeTexture, createEmptyTexture } from './webgl/texture2d'; import { Texture2D, resizeTexture, disposeTexture, createEmptyTexture } from './webgl/texture2d';
import { times, flatten, computeCRC } from '../common/utils'; import { times, flatten, computeCRC, isErrorAlike } from '../common/utils';
const INITIAL_SIZE = 512; const INITIAL_SIZE = 512;
const MAX_SIZE = 2048; const MAX_SIZE = 2048;
@@ -163,7 +163,7 @@ export class PaletteManager implements IPaletteManager {
resizeTexture(gl, this.paletteTexture, size, size); resizeTexture(gl, this.paletteTexture, size, size);
} }
} catch (e) { } catch (e) {
throw new Error(`Failed to create/resize texture (${size}) ${e.stack}`); throw new Error(`Failed to create/resize texture (${size})${isErrorAlike(e) ? ` ${e.stack}` : ''}`);
} }
this.size = size; this.size = size;
+2 -2
View File
@@ -4,7 +4,7 @@ import { uniq, truncate } from 'lodash';
import { AccountState, AccountFlags, AuthBase } from '../common/adminInterfaces'; import { AccountState, AccountFlags, AuthBase } from '../common/adminInterfaces';
import { Profile, ModInfo, AccountDataFlags } from '../common/interfaces'; import { Profile, ModInfo, AccountDataFlags } from '../common/interfaces';
import { ACCOUNT_NAME_MAX_LENGTH, DAY } from '../common/constants'; import { ACCOUNT_NAME_MAX_LENGTH, DAY } from '../common/constants';
import { fromNow, includes, hasFlag } from '../common/utils'; import { fromNow, includes, hasFlag, isErrorAlike } from '../common/utils';
import { import {
isAdmin, getCharacterLimit as getCharacterLimitInternal, getSupporterInviteLimit as getSupporterInviteLimitInternal isAdmin, getCharacterLimit as getCharacterLimitInternal, getSupporterInviteLimit as getSupporterInviteLimitInternal
} from '../common/accountUtils'; } from '../common/accountUtils';
@@ -113,7 +113,7 @@ async function checkNewAccount(account: IAccount, options: CreateAccountOptions)
} }
} }
} catch (e) { } catch (e) {
options.warn(account._id, `Error when checking new account`, e.message); options.warn(account._id, `Error when checking new account`, isErrorAlike(e) ? e.message : undefined);
} }
}); });
} }
+2 -2
View File
@@ -16,7 +16,7 @@ import * as paths from '../paths';
import { logger } from '../logger'; import { logger } from '../logger';
import { AdminService } from '../services/adminService'; import { AdminService } from '../services/adminService';
import { loadSettings, saveSettings } from '../settings'; import { loadSettings, saveSettings } from '../settings';
import { flatten } from '../../common/utils'; import { flatten, isErrorAlike } from '../../common/utils';
function encodeItems<T>(items: T[], base: BaseValues, encode: (items: T, base: BaseTimes) => any[]): any[][] { function encodeItems<T>(items: T[], base: BaseValues, encode: (items: T, base: BaseTimes) => any[]): any[][] {
const baseValues = getBaseTimes(base); const baseValues = getBaseTimes(base);
@@ -139,7 +139,7 @@ export async function getChat(search: string, date: string, caseInsensitive: boo
const log = await fetchChatlog(lines); const log = await fetchChatlog(lines);
return more + log; return more + log;
} catch (e) { } catch (e) {
if (e.message !== 'stdout maxBuffer exceeded') { if (isErrorAlike(e) && e.message !== 'stdout maxBuffer exceeded') {
throw e; throw e;
} }
} }
+2 -1
View File
@@ -12,6 +12,7 @@ import { decompressPony, compressPony } from '../../common/compressPony';
import { getCharacterLimit } from '../accountUtils'; import { getCharacterLimit } from '../accountUtils';
import { PLAYER_DESC_MAX_LENGTH } from '../../common/constants'; import { PLAYER_DESC_MAX_LENGTH } from '../../common/constants';
import { cleanName, validatePonyName } from '../../common/stringUtils'; import { cleanName, validatePonyName } from '../../common/stringUtils';
import { isError } from 'lodash';
function colorToText(c: number): string { function colorToText(c: number): string {
return c ? colorToHexRGB(c) : ''; return c ? colorToHexRGB(c) : '';
@@ -93,7 +94,7 @@ export const createSavePony =
character.lastUsed = new Date(); character.lastUsed = new Date();
} catch (error) { } catch (error) {
const message = DEVELOPMENT ? `${CHARACTER_SAVING_ERROR} (${error})` : CHARACTER_SAVING_ERROR; const message = DEVELOPMENT ? `${CHARACTER_SAVING_ERROR} (${error})` : CHARACTER_SAVING_ERROR;
throw new UserError(message, { error, data: { pony: data }, desc: `info: "${data.info}"` }); throw new UserError(message, { error: isError(error) ? error : new Error('Unknown error'), data: { pony: data }, desc: `info: "${data.info}"` });
} }
const count = created ? await characterCount(account._id) : 0; const count = created ? await characterCount(account._id) : 0;
@@ -9,7 +9,7 @@ import { Character, Account } from '../db';
import { createClientAndPony } from '../playerUtils'; import { createClientAndPony } from '../playerUtils';
import { CounterService } from '../services/counter'; import { CounterService } from '../services/counter';
import { CharacterState, ServerConfig } from '../../common/adminInterfaces'; import { CharacterState, ServerConfig } from '../../common/adminInterfaces';
import { removeItem, times } from '../../common/utils'; import { isDataViewError, removeItem, times } from '../../common/utils';
import { timingStart, timingEnd } from '../timing'; import { timingStart, timingEnd } from '../timing';
interface Options { interface Options {
@@ -128,7 +128,7 @@ async function joinFakeClient(token: any, server: ServerConfig, world: World): P
break; break;
} catch (e) { } catch (e) {
if (e instanceof RangeError || /DataView/.test(e.message)) { if (e instanceof RangeError || isDataViewError(e)) {
resizeWriter(packetWriter); resizeWriter(packetWriter);
} else { } else {
throw e; throw e;
+2 -6
View File
@@ -3,7 +3,7 @@ import { encodeString } from 'ag-sockets/dist/utf8';
import { import {
Entity, Rect, EntityState, UpdateFlags, Action, EntityOrPonyOptions, UpdateType, TileType, canWalk, setAnimationToEntityState Entity, Rect, EntityState, UpdateFlags, Action, EntityOrPonyOptions, UpdateType, TileType, canWalk, setAnimationToEntityState
} from '../common/interfaces'; } from '../common/interfaces';
import { normalize, containsPoint, boundsIntersect, clamp, pointInXYWH, hasFlag, setFlag } from '../common/utils'; import { normalize, containsPoint, boundsIntersect, clamp, pointInXYWH, hasFlag, setFlag, isOverflowError } from '../common/utils';
import { ServerEntity, ServerEntityWithClient, ServerMap, EntityUpdateBase, IClient } from './serverInterfaces'; import { ServerEntity, ServerEntityWithClient, ServerMap, EntityUpdateBase, IClient } from './serverInterfaces';
import { import {
isCritter, isDecal, entityInRange, SIT_ON_BOUNDS_WIDTH, SIT_ON_BOUNDS_HEIGHT, SIT_ON_BOUNDS_OFFSET isCritter, isDecal, entityInRange, SIT_ON_BOUNDS_WIDTH, SIT_ON_BOUNDS_HEIGHT, SIT_ON_BOUNDS_OFFSET
@@ -163,11 +163,7 @@ export function pushUpdateEntity(update: EntityUpdateBase) {
} }
} }
export function isOverflowError(e: Error) { function resizePreserveWriter(error: unknown, writer: BinaryWriter, offset: number) {
return e instanceof RangeError || /DataView/.test(e.message);
}
function resizePreserveWriter(error: Error, writer: BinaryWriter, offset: number) {
if (isOverflowError(error)) { if (isOverflowError(error)) {
const bytes = writer.bytes; const bytes = writer.bytes;
resizeWriter(writer); resizeWriter(writer);
+10 -2
View File
@@ -116,8 +116,16 @@ async function join(joinServer: InternalGameServerState, account: IAccount, char
return await joinServer.api.join(account._id.toString(), character._id.toString()); return await joinServer.api.join(account._id.toString(), character._id.toString());
} catch (error) { } catch (error) {
if (error.error && error.error.userError) { if (
throw new UserError(error.error.error); typeof error === 'object' &&
error !== null &&
'error' in error &&
typeof (error as any).error === 'object' &&
(error as any).error !== null &&
'userError' in (error as any).error &&
(error as any).error.userError
) {
throw new UserError((error as any).error.error);
} else { } else {
logger.error(error); logger.error(error);
throw new Error('Internal error'); throw new Error('Internal error');
+5 -4
View File
@@ -1,10 +1,10 @@
import { createBinaryWriter, getWriterBuffer, BinaryWriter } from 'ag-sockets'; import { createBinaryWriter, getWriterBuffer, BinaryWriter } from 'ag-sockets';
import { removeItem, pointInRect, clamp, includes } from '../common/utils'; import { removeItem, pointInRect, clamp, includes, isErrorAlike, isOverflowError } from '../common/utils';
import { ServerEntity, IClient, ServerRegion, ServerMap } from './serverInterfaces'; import { ServerEntity, IClient, ServerRegion, ServerMap } from './serverInterfaces';
import { import {
tickTilesRestoration, resetRegionUpdates, pushRemoveEntityToRegion, removeEntityFromRegion, addEntityToRegion tickTilesRestoration, resetRegionUpdates, pushRemoveEntityToRegion, removeEntityFromRegion, addEntityToRegion
} from './serverRegion'; } from './serverRegion';
import { updateEntity, isEntityShadowed, isOverflowError, pushAddEntityToClient } from './entityUtils'; import { updateEntity, isEntityShadowed, pushAddEntityToClient } from './entityUtils';
import { writeRegion, writeUpdate } from '../common/encoders/updateEncoder'; import { writeRegion, writeUpdate } from '../common/encoders/updateEncoder';
import { toWorldX, toWorldY } from '../common/positionUtils'; import { toWorldX, toWorldY } from '../common/positionUtils';
import { isRectVisible } from '../common/camera'; import { isRectVisible } from '../common/camera';
@@ -21,11 +21,12 @@ export function resetEncodeUpdate() {
updatesBufferOffset = 0; updatesBufferOffset = 0;
} }
function resizeUpdatesBuffer(e: Error) { function resizeUpdatesBuffer(e: unknown) {
if (isOverflowError(e)) { if (isOverflowError(e)) {
updatesBuffer = new ArrayBuffer(updatesBuffer.byteLength * 2); updatesBuffer = new ArrayBuffer(updatesBuffer.byteLength * 2);
updatesBufferOffset = 0; updatesBufferOffset = 0;
DEVELOPMENT && logger.debug(`resize buffer to ${updatesBuffer.byteLength} (${e.message})`); const errorMsg = isErrorAlike(e) ? e.message : `${e}`;
DEVELOPMENT && logger.debug(`resize buffer to ${updatesBuffer.byteLength} (${errorMsg})`);
} else { } else {
throw e; throw e;
} }
+3 -2
View File
@@ -3,7 +3,7 @@ import * as passport from 'passport';
import { Strategy as LocalStrategy } from 'passport-local'; import { Strategy as LocalStrategy } from 'passport-local';
import { remove } from 'lodash'; import { remove } from 'lodash';
import { MINUTE } from '../../common/constants'; import { MINUTE } from '../../common/constants';
import { fromNow, hasFlag, includes, uniqById } from '../../common/utils'; import { fromNow, hasFlag, includes, isError, uniqById } from '../../common/utils';
import { BannedMuted, Settings, ServerConfig, AccountFlags, ServerLiveSettings } from '../../common/adminInterfaces'; import { BannedMuted, Settings, ServerConfig, AccountFlags, ServerLiveSettings } from '../../common/adminInterfaces';
import { Account, IAccount, Origin, IOrigin } from '../db'; import { Account, IAccount, Origin, IOrigin } from '../db';
import { limit, auth as authRequest, wrap } from '../requestUtils'; import { limit, auth as authRequest, wrap } from '../requestUtils';
@@ -214,7 +214,8 @@ async function handleAuth(
} }
} catch (e) { } catch (e) {
const message = merge ? 'Account merge error' : 'Authentication error'; const message = merge ? 'Account merge error' : 'Authentication error';
handleErrorAndRedirect(server, merge ? '/account' : '/', message, e, req, res); const error = isError(e) ? e : new Error(`Unknown error: ${e}`);
handleErrorAndRedirect(server, merge ? '/account' : '/', message, error, req, res);
} }
} }
+2 -2
View File
@@ -18,11 +18,11 @@ export async function loadSettings() {
const json = await readFileAsync(settingsPath, 'utf8'); const json = await readFileAsync(settingsPath, 'utf8');
return JSON.parse(json) as Settings; return JSON.parse(json) as Settings;
} catch (e) { } catch (e) {
if (e.code === 'ENOENT') { if ((e as NodeJS.ErrnoException).code === 'ENOENT') {
try { try {
await mkdirAsync(paths.pathTo('settings')); await mkdirAsync(paths.pathTo('settings'));
} catch (e2) { } catch (e2) {
if (e2.code !== 'EEXIST') throw e; if ((e2 as NodeJS.ErrnoException).code !== 'EEXIST') throw e;
} }
} else { } else {
console.error('Error reading settings file: ' + e); console.error('Error reading settings file: ' + e);
+4 -1
View File
@@ -17,6 +17,7 @@ import { createCaveMap } from './maps/caveMap';
import { createCustomMap } from './maps/customMap'; import { createCustomMap } from './maps/customMap';
import { createSign } from './controllerUtils'; import { createSign } from './controllerUtils';
import { signQuestion } from '../common/entities'; import { signQuestion } from '../common/entities';
import { isErrorAlike } from '../common/utils';
export function start(world: World, server: ServerConfig) { export function start(world: World, server: ServerConfig) {
const data = readFileSync(pathTo('src', 'ts', 'generated', 'pony.bin')); const data = readFileSync(pathTo('src', 'ts', 'generated', 'pony.bin'));
@@ -25,7 +26,7 @@ export function start(world: World, server: ServerConfig) {
width: 512, width: 512,
height: 512, height: 512,
data: new Uint8ClampedArray(data.buffer, data.byteOffset, data.byteLength), data: new Uint8ClampedArray(data.buffer, data.byteOffset, data.byteLength),
}; } as ImageData;
initializeTileHeightmaps(); initializeTileHeightmaps();
@@ -94,7 +95,9 @@ export function start(world: World, server: ServerConfig) {
world.sparseUpdate(now); world.sparseUpdate(now);
} }
} catch (e) { } catch (e) {
if (isErrorAlike(e)) {
create(server).danger(e.message); create(server).danger(e.message);
}
logger.error(e); logger.error(e);
} }
+1 -1
View File
@@ -192,7 +192,7 @@ export class StatsTracker {
try { try {
fs.mkdirSync(path.dirname(this.statsPath), { recursive: true }); fs.mkdirSync(path.dirname(this.statsPath), { recursive: true });
} catch (e) { } catch (e) {
if (e.code !== 'EEXIST') throw e; if ((e as NodeJS.ErrnoException).code !== 'EEXIST') throw e;
} }
fs.writeFileSync(this.statsPath, encodeCSV(statsHeaders), { encoding: 'utf8' }); fs.writeFileSync(this.statsPath, encodeCSV(statsHeaders), { encoding: 'utf8' });
} }
+3 -2
View File
@@ -3,6 +3,7 @@ import { logger } from './logger';
import { createFromRequest } from './reporter'; import { createFromRequest } from './reporter';
import { IClient, Reporter } from './serverInterfaces'; import { IClient, Reporter } from './serverInterfaces';
import { ServerConfig } from '../common/adminInterfaces'; import { ServerConfig } from '../common/adminInterfaces';
import { isError } from 'lodash';
export interface UserErrorInfo { export interface UserErrorInfo {
error?: Error; error?: Error;
@@ -21,8 +22,8 @@ export class UserError extends Error {
} }
} }
export function isUserError(e: Error): e is UserError { export function isUserError(e: unknown): e is UserError {
return e.name === 'UserError'; return isError(e) && e.name === 'UserError';
} }
function report(message: string, info: UserErrorInfo, reporter: Reporter | undefined, extra = '') { function report(message: string, info: UserErrorInfo, reporter: Reporter | undefined, extra = '') {
+1 -1
View File
@@ -173,7 +173,7 @@ export function saveCanvas(filePath: string, canvas: HTMLCanvasElement) {
try { try {
fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.mkdirSync(path.dirname(filePath), { recursive: true });
} catch (e) { } catch (e) {
if (e.code !== 'EEXIST') throw e; if ((e as NodeJS.ErrnoException).code !== 'EEXIST') throw e;
} }
fs.writeFileSync(filePath, canvas.toBuffer()); fs.writeFileSync(filePath, canvas.toBuffer());
+5
View File
@@ -4,6 +4,7 @@ import { Layer as PsdLayer, Psd as PsdFile, readPsd, initializeCanvas } from 'ag
import { Layer, Psd } from './types'; import { Layer, Psd } from './types';
import { createExtCanvas } from './canvas-utils'; import { createExtCanvas } from './canvas-utils';
import { matcher, parseWithNumber } from './common'; import { matcher, parseWithNumber } from './common';
import { isErrorAlike } from '../common/utils';
initializeCanvas((width, height) => createExtCanvas(width, height, 'loaded from psd')); initializeCanvas((width, height) => createExtCanvas(width, height, 'loaded from psd'));
@@ -20,7 +21,11 @@ export function openPsd(filePath: string) {
}); });
return toPsd(psd, name, dir); return toPsd(psd, name, dir);
} catch (e) { } catch (e) {
if (isErrorAlike(e)) {
console.error(`Failed to load: ${filePath}: ${e.message}`); console.error(`Failed to load: ${filePath}: ${e.message}`);
} else {
console.error(e);
}
throw e; throw e;
} }
} }
+1 -1
View File
@@ -290,7 +290,7 @@ function getSpriteImageData(s: ExtSprite): ImageData | undefined {
const context = s.image.getContext('2d')!; const context = s.image.getContext('2d')!;
const { width, height, data } = context.getImageData(s.ox, s.oy, s.w, s.h); const { width, height, data } = context.getImageData(s.ox, s.oy, s.w, s.h);
return { width, height, data }; return { width, height, data } as ImageData;
} }
export function createSpriteSheet(name: string, images: ExtSprite[], log: boolean, size: number, bg?: string, pack = false) { export function createSpriteSheet(name: string, images: ExtSprite[], log: boolean, size: number, bg?: string, pack = false) {