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

Use strict mode and fix typings #49

Merged
merged 7 commits into from
Jan 16, 2021
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
27 changes: 15 additions & 12 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
module.exports = {
parser: '@typescript-eslint/parser', // Specifies the ESLint parser
parser: "@typescript-eslint/parser", // Specifies the ESLint parser
extends: [
'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin
'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
"plugin:@typescript-eslint/recommended", // Uses the recommended rules from the @typescript-eslint/eslint-plugin
"prettier/@typescript-eslint", // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
"plugin:prettier/recommended", // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
],
parserOptions: {
project: "tsconfig.json",
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
sourceType: 'module', // Allows for the use of imports
sourceType: "module", // Allows for the use of imports
},
plugins: ["strict-booleans"],
rules: {
// Sorry Jas but switch-case indentation messes with prettier :c
// and I think having prettier is worth it so disabling this for now.
// '@typescript-eslint/indent': ['error', 2, { SwitchCase: 0 }],
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/explicit-member-accessibility': 'warn',
'@typescript-eslint/no-unused-vars': [
'error',
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/explicit-member-accessibility": "warn",
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
},
],
'object-shorthand': ['error', 'never'],
"object-shorthand": ["error", "never"],
"strict-booleans/no-nullable-numbers": "error",
},
};
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,12 @@
"@types/node": "^12.7.3",
"@types/reconnect-core": "^1.3.1",
"@types/semver": "^6.0.2",
"@typescript-eslint/eslint-plugin": "^2.3.0",
"@typescript-eslint/parser": "^2.3.0",
"eslint": "^6.3.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.4",
"@typescript-eslint/eslint-plugin": "^4.9.0",
"@typescript-eslint/parser": "^4.9.0",
"eslint": "^7.15.0",
"eslint-config-prettier": "^7.0.0",
"eslint-plugin-prettier": "^3.2.0",
"eslint-plugin-strict-booleans": "^1.0.0",
"husky": "^4.2.5",
"jest": "^24.9.0",
"open-cli": "^5.0.0",
Expand All @@ -76,7 +77,7 @@
"rollup-plugin-terser": "^5.3.0",
"rollup-plugin-typescript2": "^0.27.1",
"ts-jest": "^24.0.2",
"typescript": "^3.6.2"
"typescript": "^3.8.3"
},
"husky": {
"hooks": {
Expand Down
15 changes: 6 additions & 9 deletions src/SlippiGame.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable no-param-reassign */
import _ from "lodash";
import { openSlpFile, closeSlpFile, iterateEvents, getMetadata, SlpInputSource, SlpReadInput } from "./utils/slpReader";

Expand All @@ -23,8 +22,8 @@ import {
*/
export class SlippiGame {
private input: SlpReadInput;
private metadata: MetadataType | null;
private finalStats: StatsType | null;
private metadata: MetadataType | null = null;
private finalStats: StatsType | null = null;
private parser: SlpParser;
private readPosition: number | null = null;
private actionsComputer: ActionsComputer = new ActionsComputer();
Expand Down Expand Up @@ -95,7 +94,7 @@ export class SlippiGame {
* Gets the game settings, these are the settings that describe the starting state of
* the game such as characters, stage, etc.
*/
public getSettings(): GameStartType {
public getSettings(): GameStartType | null {
// Settings is only complete after post-frame update
this._process(true);
return this.parser.getSettings();
Expand Down Expand Up @@ -128,7 +127,7 @@ export class SlippiGame {
const inputs = this.inputComputer.fetch();
const stocks = this.stockComputer.fetch();
const conversions = this.conversionComputer.fetch();
const indices = getSinglesPlayerPermutationsFromSettings(this.parser.getSettings());
const indices = getSinglesPlayerPermutationsFromSettings(this.parser.getSettings()!);
const playableFrames = this.parser.getPlayableFrameCount();
const overall = generateOverallStats(indices, inputs, stocks, conversions, playableFrames);

Expand All @@ -154,7 +153,7 @@ export class SlippiGame {
return stats;
}

public getMetadata(): MetadataType {
public getMetadata(): MetadataType | null {
if (this.metadata) {
return this.metadata;
}
Expand All @@ -169,8 +168,6 @@ export class SlippiGame {
return null;
}

return this.input.filePath || null;
return this.input.filePath ?? null;
}
}

/* eslint-enable no-param-reassign */
11 changes: 8 additions & 3 deletions src/console/consoleConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ export class ConsoleConnection extends EventEmitter implements Connection {

const handshakeMsgOut = consoleComms.genHandshakeOut(
this.connDetails.gameDataCursor as Uint8Array,
this.connDetails.clientToken,
this.connDetails.clientToken ?? 0,
);

client.write(handshakeMsgOut);
Expand Down Expand Up @@ -296,10 +296,15 @@ export class ConsoleConnection extends EventEmitter implements Connection {
this._handleReplayData(data);
break;
case CommunicationType.HANDSHAKE:
this.connDetails.consoleNick = message.payload.nick;
const { nick, nintendontVersion } = message.payload;
if (nick) {
this.connDetails.consoleNick = nick;
}
const tokenBuf = Buffer.from(message.payload.clientToken);
this.connDetails.clientToken = tokenBuf.readUInt32BE(0);
this.connDetails.version = message.payload.nintendontVersion;
if (nintendontVersion) {
this.connDetails.version = nintendontVersion;
}
this.connDetails.gameDataCursor = Uint8Array.from(message.payload.pos);
this.emit(ConnectionEvent.HANDSHAKE, this.connDetails);
break;
Expand Down
9 changes: 8 additions & 1 deletion src/melee/characters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ export interface CharacterInfo {
colors: CharacterColor[];
}

export const UnknownCharacter: CharacterInfo = {
id: -1,
name: "Unknown Character",
shortName: "Unknown",
colors: ["Default"],
};

const externalCharacters: CharacterInfo[] = [
{
id: Character.CAPTAIN_FALCON,
Expand Down Expand Up @@ -174,7 +181,7 @@ export function getAllCharacters(): CharacterInfo[] {

export function getCharacterInfo(externalCharacterId: number): CharacterInfo {
if (externalCharacterId < 0 || externalCharacterId >= externalCharacters.length) {
throw new Error(`Invalid character id: ${externalCharacterId}`);
return UnknownCharacter;
}
return externalCharacters[externalCharacterId];
}
Expand Down
7 changes: 6 additions & 1 deletion src/melee/stages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ export interface StageInfo {
name: string;
}

export const UnknownStage: StageInfo = {
id: -1,
name: "Unknown Stage",
};

const stages: { [id: number]: StageInfo } = {
[Stage.FOUNTAIN_OF_DREAMS]: {
id: Stage.FOUNTAIN_OF_DREAMS,
Expand Down Expand Up @@ -131,7 +136,7 @@ const stages: { [id: number]: StageInfo } = {
export function getStageInfo(stageId: number): StageInfo {
const s = stages[stageId];
if (!s) {
throw new Error(`Invalid stage with id ${stageId}`);
return UnknownStage;
}
return s;
}
Expand Down
13 changes: 7 additions & 6 deletions src/stats/actions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @flow
import _ from "lodash";
import { State, PlayerIndexedType, ActionCountsType } from "./common";
import { FrameEntryType } from "../types";
Expand Down Expand Up @@ -41,12 +40,14 @@ export class ActionsComputer implements StatComputer<ActionCountsType[]> {
public processFrame(frame: FrameEntryType): void {
this.playerPermutations.forEach((indices) => {
const state = this.state.get(indices);
handleActionCompute(state, indices, frame);
if (state) {
handleActionCompute(state, indices, frame);
}
});
}

public fetch(): ActionCountsType[] {
return Array.from(this.state.keys()).map((key) => this.state.get(key).playerCounts);
return Array.from(this.state.values()).map((val) => val.playerCounts);
}
}

Expand Down Expand Up @@ -95,7 +96,7 @@ function didStartLedgegrab(currentAnimation: State, previousAnimation: State): b
}

function handleActionCompute(state: PlayerActionState, indices: PlayerIndexedType, frame: FrameEntryType): void {
const playerFrame = frame.players[indices.playerIndex].post;
const playerFrame = frame.players[indices.playerIndex]!.post;
const incrementCount = (field: string, condition: boolean): void => {
if (!condition) {
return;
Expand All @@ -106,11 +107,11 @@ function handleActionCompute(state: PlayerActionState, indices: PlayerIndexedTyp
};

// Manage animation state
state.animations.push(playerFrame.actionStateId);
state.animations.push(playerFrame.actionStateId!);

// Grab last 3 frames
const last3Frames = state.animations.slice(-3);
const currentAnimation = playerFrame.actionStateId;
const currentAnimation = playerFrame.actionStateId!;
const prevAnimation = last3Frames[last3Frames.length - 2];

// Increment counts based on conditions
Expand Down
54 changes: 28 additions & 26 deletions src/stats/combos.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import _ from "lodash";
import { FrameEntryType, FramesType, PostFrameUpdateType } from "../types";
import { FrameEntryType, FramesType } from "../types";
import { MoveLandedType, ComboType, PlayerIndexedType } from "./common";
import { isDamaged, isGrabbed, calcDamageTaken, isTeching, didLoseStock, Timers, isDown, isDead } from "./common";
import { StatComputer } from "./stats";
Expand Down Expand Up @@ -32,7 +32,9 @@ export class ComboComputer implements StatComputer<ComboType[]> {
public processFrame(frame: FrameEntryType, allFrames: FramesType): void {
this.playerPermutations.forEach((indices) => {
const state = this.state.get(indices);
handleComboCompute(allFrames, state, indices, frame, this.combos);
if (state) {
handleComboCompute(allFrames, state, indices, frame, this.combos);
}
});
}

Expand All @@ -48,17 +50,17 @@ function handleComboCompute(
frame: FrameEntryType,
combos: ComboType[],
): void {
const playerFrame: PostFrameUpdateType = frame.players[indices.playerIndex].post;
// FIXME: use type PostFrameUpdateType instead of any
// This is because the default value {} should not be casted as a type of PostFrameUpdateType
const prevPlayerFrame: any = _.get(frames, [playerFrame.frame - 1, "players", indices.playerIndex, "post"], {});
const opponentFrame: PostFrameUpdateType = frame.players[indices.opponentIndex].post;
// FIXME: use type PostFrameUpdateType instead of any
// This is because the default value {} should not be casted as a type of PostFrameUpdateType
const prevOpponentFrame: any = _.get(frames, [playerFrame.frame - 1, "players", indices.opponentIndex, "post"], {});

const opntIsDamaged = isDamaged(opponentFrame.actionStateId);
const opntIsGrabbed = isGrabbed(opponentFrame.actionStateId);
const playerFrame = frame.players[indices.playerIndex]!.post;
const currentFrameNumber = playerFrame.frame!;
const prevFrameNumber = currentFrameNumber - 1;

const prevPlayerFrame = frames[prevFrameNumber].players[indices.playerIndex]!.post;
const opponentFrame = frame.players[indices.opponentIndex]!.post;
const prevOpponentFrame = frames[prevFrameNumber].players[indices.opponentIndex]!.post;

const oppActionStateId = opponentFrame.actionStateId!;
const opntIsDamaged = isDamaged(oppActionStateId);
const opntIsGrabbed = isGrabbed(oppActionStateId);
const opntDamageTaken = calcDamageTaken(opponentFrame, prevOpponentFrame);

// Keep track of whether actionState changes after a hit. Used to compute move count
Expand All @@ -68,8 +70,8 @@ function handleComboCompute(
// an animation started. Should be more robust, for old files it should always be
// null and null < null = false
const actionChangedSinceHit = playerFrame.actionStateId !== state.lastHitAnimation;
const actionCounter = playerFrame.actionStateCounter;
const prevActionCounter = prevPlayerFrame.actionStateCounter;
const actionCounter = playerFrame.actionStateCounter!;
const prevActionCounter = prevPlayerFrame.actionStateCounter!;
const actionFrameCounterReset = actionCounter < prevActionCounter;
if (actionChangedSinceHit || actionFrameCounterReset) {
state.lastHitAnimation = null;
Expand All @@ -82,10 +84,10 @@ function handleComboCompute(
state.combo = {
playerIndex: indices.playerIndex,
opponentIndex: indices.opponentIndex,
startFrame: playerFrame.frame,
startFrame: currentFrameNumber,
endFrame: null,
startPercent: prevOpponentFrame.percent || 0,
currentPercent: opponentFrame.percent || 0,
startPercent: prevOpponentFrame.percent ?? 0,
currentPercent: opponentFrame.percent ?? 0,
endPercent: null,
moves: [],
didKill: false,
Expand All @@ -97,10 +99,10 @@ function handleComboCompute(
if (opntDamageTaken) {
// If animation of last hit has been cleared that means this is a new move. This
// prevents counting multiple hits from the same move such as fox's drill
if (!state.lastHitAnimation) {
if (state.lastHitAnimation === null) {
state.move = {
frame: playerFrame.frame,
moveId: playerFrame.lastAttackLanded,
frame: currentFrameNumber,
moveId: playerFrame.lastAttackLanded!,
hitCount: 0,
damage: 0,
};
Expand All @@ -125,14 +127,14 @@ function handleComboCompute(
return;
}

const opntIsTeching = isTeching(opponentFrame.actionStateId);
const opntIsDowned = isDown(opponentFrame.actionStateId);
const opntIsTeching = isTeching(oppActionStateId);
const opntIsDowned = isDown(oppActionStateId);
const opntDidLoseStock = didLoseStock(opponentFrame, prevOpponentFrame);
const opntIsDying = isDead(opponentFrame.actionStateId);
const opntIsDying = isDead(oppActionStateId);

// Update percent if opponent didn't lose stock
if (!opntDidLoseStock) {
state.combo.currentPercent = opponentFrame.percent || 0;
state.combo.currentPercent = opponentFrame.percent ?? 0;
}

if (opntIsDamaged || opntIsGrabbed || opntIsTeching || opntIsDowned || opntIsDying) {
Expand All @@ -158,7 +160,7 @@ function handleComboCompute(
// If combo should terminate, mark the end states and add it to list
if (shouldTerminate) {
state.combo.endFrame = playerFrame.frame;
state.combo.endPercent = prevOpponentFrame.percent || 0;
state.combo.endPercent = prevOpponentFrame.percent ?? 0;

state.combo = null;
state.move = null;
Expand Down
6 changes: 3 additions & 3 deletions src/stats/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export function didLoseStock(frame: PostFrameUpdateType, prevFrame: PostFrameUpd
return false;
}

return prevFrame.stocksRemaining - frame.stocksRemaining > 0;
return prevFrame.stocksRemaining! - frame.stocksRemaining! > 0;
}

export function isInControl(state: number): boolean {
Expand Down Expand Up @@ -197,8 +197,8 @@ export function isDead(state: number): boolean {
}

export function calcDamageTaken(frame: PostFrameUpdateType, prevFrame: PostFrameUpdateType): number {
const percent = _.get(frame, "percent", 0);
const prevPercent = _.get(prevFrame, "percent", 0);
const percent = frame.percent ?? 0;
const prevPercent = prevFrame.percent ?? 0;

return percent - prevPercent;
}
Loading