Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding cleaner error handling for promises. #135

Merged
merged 1 commit into from
Mar 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/error-handling.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { logger } from '@runejs/logger/dist/logger';

/*
* Error handling! Feel free to add other types of errors or warnings here. :)
*/

export class WidgetsClosedWarning extends Error {
constructor() {
super();
this.name = 'WidgetsClosedWarning';
this.message = 'The active widget was closed before the action could be completed.';
}
}

export class ActionsCancelledWarning extends Error {
constructor() {
super();
this.name = 'ActionsCancelledWarning';
this.message = 'Pending and active actions were cancelled before they could be completed.';
}
}

const warnings = [
WidgetsClosedWarning,
ActionsCancelledWarning
];

export function initErrorHandling(): void {
process.on('unhandledRejection', (error, promise) => {
for(const t of warnings) {
if(error instanceof t) {
logger.warn(`Promise cancelled with warning: ${error.name}`);
return;
}
}

logger.error(`Unhandled promise rejection from ${promise}, reason: ${error}`);
if(error.hasOwnProperty('stack')) {
logger.error((error as any).stack);
}
});
}
8 changes: 0 additions & 8 deletions src/game-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,6 @@ export function runGameServer(): void {
world.generateFakePlayers();
}

process.on('unhandledRejection', (err, promise) => {
if(err === 'WIDGET_CLOSED' || err === 'ACTION_CANCELLED') {
return;
}

logger.error(`Unhandled promise rejection from ${promise}, reason: ${err}`);
});

net.createServer(socket => {
logger.info('Socket opened');

Expand Down
2 changes: 2 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { runGameServer } from './game-server';
import { runWebServer } from './web-server';
import 'source-map-support/register';
import { initErrorHandling } from '@server/error-handling';
// import { dumpItems } from '@server/data-dump';

initErrorHandling();
runGameServer();
runWebServer();
// dumpItems();
12 changes: 6 additions & 6 deletions src/net/data-parser/client-login-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,25 @@ export class ClientLoginParser extends DataParser {

public parse(buffer?: RsBuffer): void {
if(!buffer) {
throw ('No data supplied for login');
throw new Error('No data supplied for login');
}

const loginType = buffer.readUnsignedByte();

if(loginType !== 16 && loginType !== 18) {
throw ('Invalid login type ' + loginType);
throw new Error('Invalid login type ' + loginType);
}

let loginEncryptedSize = buffer.readUnsignedByte() - (36 + 1 + 1 + 2);

if(loginEncryptedSize <= 0) {
throw ('Invalid login packet length ' + loginEncryptedSize);
throw new Error('Invalid login packet length ' + loginEncryptedSize);
}

const gameVersion = buffer.readIntBE();

if(gameVersion !== 435) {
throw ('Invalid game version ' + gameVersion);
throw new Error('Invalid game version ' + gameVersion);
}

const isLowDetail: boolean = buffer.readByte() === 1;
Expand All @@ -72,15 +72,15 @@ export class ClientLoginParser extends DataParser {
const blockId = decrypted.readByte();

if(blockId !== 10) {
throw ('Invalid block id ' + blockId);
throw new Error('Invalid block id ' + blockId);
}

const clientKey1 = decrypted.readIntBE();
const clientKey2 = decrypted.readIntBE();
const incomingServerKey = decrypted.readLongBE();

if(this.clientConnection.serverKey !== incomingServerKey) {
throw (`Server key mismatch - ${this.clientConnection.serverKey} != ${incomingServerKey}`);
throw new Error(`Server key mismatch - ${this.clientConnection.serverKey} != ${incomingServerKey}`);
}

const clientUuid = decrypted.readIntBE();
Expand Down
4 changes: 2 additions & 2 deletions src/net/data-parser/login-handshake-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class LoginHandshakeParser extends DataParser {

public parse(buffer: RsBuffer, packetId: number): void {
if(!buffer) {
throw ('No data supplied for login handshake');
throw new Error('No data supplied for login handshake');
}

if(packetId === 14) {
Expand All @@ -23,7 +23,7 @@ export class LoginHandshakeParser extends DataParser {

this.clientConnection.serverKey = serverKey;
} else {
throw 'Invalid login handshake packet id.';
throw new Error('Invalid login handshake packet id.');
}
}
}
2 changes: 1 addition & 1 deletion src/net/data-parser/update-server-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export class UpdateServerParser extends DataParser {
}

if(!cacheFile || cacheFile.getBuffer().length === 0) {
throw `Cache file not found; file(${file}) with index(${index})`;
throw new Error(`Cache file not found; file(${file}) with index(${index})`);
}

const cacheFileBuffer = cacheFile.getBuffer();
Expand Down
4 changes: 2 additions & 2 deletions src/net/data-parser/version-handshake-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class VersionHandshakeParser extends DataParser {

public parse(buffer: RsBuffer, packetId: number): void {
if(!buffer) {
throw ('No data supplied for version handshake');
throw new Error('No data supplied for version handshake');
}

if(packetId === 15) {
Expand All @@ -18,7 +18,7 @@ export class VersionHandshakeParser extends DataParser {
outputBuffer.writeByte(gameVersion === 435 ? 0 : 6);
this.clientConnection.socket.write(outputBuffer.getData());
} else {
throw 'Invalid version handshake packet id.';
throw new Error('Invalid version handshake packet id.');
}
}
}
4 changes: 2 additions & 2 deletions src/plugins/commands/give-item-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ const action: commandAction = (details) => {
let amount: number = args.amount as number;

if(amount > 2000000000) {
throw `Unable to give more than 2,000,000,000.`;
throw new Error(`Unable to give more than 2,000,000,000.`);
}

const itemDefinition = gameCache.itemDefinitions.get(itemId);
if(!itemDefinition) {
throw `Item ID ${itemId} not found!`;
throw new Error(`Item ID ${itemId} not found!`);
}

let actualAmount = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/commands/new-dialogue-test-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const action: commandAction = (details) => {
player => [ Emote.GENERIC, `See ya around.` ]
]).then(() => {
// do something with dialogue result.
}).catch(() => {});
});
};

export default new RunePlugin({ type: ActionType.COMMAND, commands: 'd', action });
6 changes: 3 additions & 3 deletions src/plugins/npcs/lumbridge/lumbridge-farm-helpers-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ const millieDialogue: npcAction = (details) => {
player => [ Emote.HAPPY, `Great! Thanks for your help.` ]
],
`I'm fine, thanks.`, [
player => [ Emote.GENERIC, `Then I bring my wheat here?` ]
player => [ Emote.GENERIC, `I'm fine, thanks.` ]
]
]
]).catch(() => {});
]);
};

const gillieDialogue: npcAction = (details) => {
Expand Down Expand Up @@ -82,7 +82,7 @@ const gillieDialogue: npcAction = (details) => {
player => [ Emote.GENERIC, `I'm fine, thanks.` ],
]
]
]).catch(() => {});
]);
};

export default new RunePlugin([{
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/objects/ladders/ladder-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const action: objectAction = (details) => {
action({...details, option: `climb-${direction}`});
return;
}
}).catch(error => console.error(error));
});
return;
}

Expand Down
2 changes: 1 addition & 1 deletion src/plugins/quests/cooks-assistant-quest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ const startQuestAction: npcAction = (details) => {
`Go on your merry way!` ]
]
]
]).catch(error => console.error(error));
]);
};

function youStillNeed(quest: QuestProgress): DialogueTree {
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/skills/skill-guide-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function parseSkillGuides(): SkillGuide[] {
const skillGuides = safeLoad(readFileSync('data/config/skill-guides.yaml', 'utf8'), { schema: JSON_SCHEMA }) as SkillGuide[];

if(!skillGuides || skillGuides.length === 0) {
throw 'Unable to read skill guides.';
throw new Error('Unable to read skill guides.');
}

logger.info(`${skillGuides.length} skill guides found.`);
Expand Down
17 changes: 12 additions & 5 deletions src/world/actor/dialogue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { gameCache } from '@server/game-server';
import { logger } from '@runejs/logger/dist/logger';
import _ from 'lodash';
import { wrapText } from '@server/util/strings';
import { ActionsCancelledWarning, WidgetsClosedWarning } from '@server/error-handling';

export enum Emote {
POMPOUS = 'POMPOUS',
Expand Down Expand Up @@ -328,7 +329,7 @@ async function runParsedDialogue(player: Player, dialogueTree: ParsedDialogueTre

for(let i = 0; i < dialogueTree.length; i++) {
if(stopLoop) {
return Promise.reject('ACTION_CANCELLED');
throw new ActionsCancelledWarning();
}

const sub: Subscription[] = [];
Expand Down Expand Up @@ -404,7 +405,8 @@ async function runParsedDialogue(player: Player, dialogueTree: ParsedDialogueTre
const lines = textDialogueAction.lines;

if(lines.length > 5) {
throw `Too many lines for text dialogue! Dialogue has ${lines.length} lines but the maximum is 5: ${JSON.stringify(lines)}`;
throw new Error(`Too many lines for text dialogue! Dialogue has ${lines.length} lines but ` +
`the maximum is 5: ${JSON.stringify(lines)}`);
}

widgetId = textWidgetIds[lines.length - 1];
Expand Down Expand Up @@ -461,7 +463,8 @@ async function runParsedDialogue(player: Player, dialogueTree: ParsedDialogueTre
const lines = actorDialogueAction.lines;

if(lines.length > 4) {
throw `Too many lines for actor dialogue! Dialogue has ${lines.length} lines but the maximum is 4: ${JSON.stringify(lines)}`;
throw new Error(`Too many lines for actor dialogue! Dialogue has ${lines.length} lines but ` +
`the maximum is 4: ${JSON.stringify(lines)}`);
}

const animation = actorDialogueAction.animation;
Expand Down Expand Up @@ -498,14 +501,18 @@ async function runParsedDialogue(player: Player, dialogueTree: ParsedDialogueTre
widgetId: widgetId,
type: 'CHAT',
closeOnWalk: true,
forceClosed: () => reject('WIDGET_CLOSED')
forceClosed: () => reject(new WidgetsClosedWarning())
};
}
}).then(() => {
sub.forEach(s => s.unsubscribe());
}).catch(error => {
sub.forEach(s => s.unsubscribe());
stopLoop = true;

if(!(error instanceof ActionsCancelledWarning) && !(error instanceof WidgetsClosedWarning)) {
throw error;
}
});
}

Expand All @@ -516,7 +523,7 @@ export async function dialogue(participants: (Player | NpcParticipant)[], dialog
const player = participants.find(p => p instanceof Player) as Player;

if(!player) {
return Promise.reject('Player instance not provided to dialogue action.');
throw new Error('Player instance not provided to dialogue action.');
}

let npcParticipants = participants.filter(p => !(p instanceof Player)) as NpcParticipant[];
Expand Down
7 changes: 4 additions & 3 deletions src/world/actor/player/action/dialogue-action.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Player } from '@server/world/actor/player/player';
import { gameCache } from '@server/game-server';
import { Npc } from '@server/world/actor/npc/npc';
import { WidgetsClosedWarning } from '@server/error-handling';

export const dialogueWidgetIds = {
PLAYER: [ 64, 65, 66, 67 ],
Expand Down Expand Up @@ -85,11 +86,11 @@ export class DialogueAction {

public async dialogue(options: DialogueOptions): Promise<DialogueAction> {
if(options.lines.length < lineConstraints[options.type][0] || options.lines.length > lineConstraints[options.type][1]) {
throw 'Invalid line length.';
throw new Error('Invalid line length.');
}

if(options.type === 'NPC' && options.npc === undefined) {
throw 'NPC not supplied.';
throw new Error('NPC not supplied.');
}

this._action = null;
Expand Down Expand Up @@ -138,7 +139,7 @@ export class DialogueAction {
widgetId: widgetId,
type: 'CHAT',
closeOnWalk: true,
forceClosed: () => reject('WIDGET_CLOSED')
forceClosed: () => reject(new WidgetsClosedWarning())
};

const sub = this.p.dialogueInteractionEvent.subscribe(action => {
Expand Down
2 changes: 1 addition & 1 deletion src/world/actor/player/action/item-selection-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export async function itemSelectionAction(player: Player, type: 'COOKING' | 'MAK
widgetId = 309;
} else {
if(items.length > 5) {
throw `Too many items provided to the item selection action!`;
throw new Error(`Too many items provided to the item selection action!`);
}

widgetId = (301 + items.length);
Expand Down
5 changes: 2 additions & 3 deletions src/world/actor/player/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -536,13 +536,13 @@ export class Player extends Actor {
return Promise.resolve();
} else {
if(messages.length > 5) {
throw `Dialogues have a maximum of 5 lines!`;
throw new Error(`Dialogues have a maximum of 5 lines!`);
}

return dialogueAction(this, { type: 'TEXT', lines: messages }).then(async d => {
d.close();
return Promise.resolve();
}).catch(() => {});
});
}
}

Expand Down Expand Up @@ -733,7 +733,6 @@ export class Player extends Actor {
this.activeWidget = widget;
} else {
this.queuedWidgets.push(widget);
console.log(this.queuedWidgets);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/world/config/item-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export function parseItemData(itemDefinitions: Map<number, ItemDefinition>): Map
const itemDataList = safeLoad(readFileSync('data/config/item-data.yaml', 'utf8'), { schema: JSON_SCHEMA }) as ItemData[];

if(!itemDataList || itemDataList.length === 0) {
throw 'Unable to read item data.';
throw new Error('Unable to read item data.');
}

const itemDetailsMap: Map<number, ItemDetails> = new Map<number, ItemDetails>();
Expand Down
2 changes: 1 addition & 1 deletion src/world/config/npc-spawn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function parseNpcSpawns(): NpcSpawn[] {
const npcSpawns = safeLoad(readFileSync('data/config/npc-spawns.yaml', 'utf8'), { schema: JSON_SCHEMA }) as NpcSpawn[];

if(!npcSpawns || npcSpawns.length === 0) {
throw 'Unable to read npc spawns.';
throw new Error('Unable to read npc spawns.');
}

logger.info(`${npcSpawns.length} npc spawns found.`);
Expand Down
2 changes: 1 addition & 1 deletion src/world/config/server-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function parseServerConfig(useDefault?: boolean): ServerConfig {
logger.warn('Server config not provided, using default...');
return parseServerConfig(true);
} else {
throw 'Syntax Error';
throw new Error('Syntax Error');
}
}

Expand Down
Loading