Skip to content

Commit

Permalink
feat: parse npcs from packets using mpq data
Browse files Browse the repository at this point in the history
  • Loading branch information
blacha committed Sep 1, 2020
1 parent af8ec8d commit 2fa03e2
Show file tree
Hide file tree
Showing 28 changed files with 464 additions and 380 deletions.
3 changes: 2 additions & 1 deletion packages/bintools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
"test": "ospec --globs 'build/**/*.test.js'"
},
"dependencies": {
"@diablo2/data": "^0.0.1",
"ulid": "^2.3.0",
"binparse": "^0.2.0"
"binparse": "^0.3.0"
},
"publishConfig": {
"access": "public"
Expand Down
2 changes: 1 addition & 1 deletion packages/bintools/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { LangReader, LangNode } from './lang/lang.reader';
export { MonsterReader, MonsterNode } from './monster/monster.stat.reader';

export { Diablo2Mpq } from './mpq.loader';
export { Diablo2MpqLoader } from './mpq.loader';
export { Logger } from './log.type';
16 changes: 16 additions & 0 deletions packages/bintools/src/monster/monster.stat.reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,19 @@ export const MonsterReader = bp.object('Monsters', {
count: bp.variable('count', bp.lu32),
monsters: bp.array('Monster', MonsterParser, 'count'),
});

const Monster2Parser = bp.object('Monster2', {
id: bp.lu32,
unk1: bp.lu32,
sizeX: bp.u8,
sizeY: bp.u8,
unk2: bp.skip(11),
/** This state is used to read the NpcAssign packet */
state: bp.array('MonsterState', bp.u8, 16),
unk100: bp.bytes(0x134 - 10 - 11 - 16),
});
/** MonStats2.bin */
export const MonsterReader2 = bp.object('Monsters2', {
count: bp.variable('count', bp.lu32),
monsters: bp.array('Monster', Monster2Parser, 'count'),
});
81 changes: 0 additions & 81 deletions packages/bintools/src/mpq.data.ts

This file was deleted.

82 changes: 57 additions & 25 deletions packages/bintools/src/mpq.loader.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,71 @@
import * as path from 'path';
import { existsSync } from 'fs';
import { Diablo2MpqLang, Diablo2MpqMonsters } from './mpq.data';
import { Logger } from './log.type';
import { Diablo2Mpq, Diablo2MpqData } from '@diablo2/data';
import { promises as fs } from 'fs';
import { MonsterReader, MonsterReader2 } from './monster/monster.stat.reader';
import { LangReader } from './lang/lang.reader';

export class Diablo2Mpq {
basePath: string;
lang: Diablo2MpqLang = new Diablo2MpqLang(this);
monsters: Diablo2MpqMonsters = new Diablo2MpqMonsters(this);
export class Diablo2MpqLoader {
static async init(basePath: string, log?: Logger, mpq = Diablo2Mpq): Promise<Diablo2MpqData> {
if (!existsSync(path.join(basePath, 'data'))) {
log?.error({ basePath: basePath }, 'No /data found, mpq needs to be extracted');
throw new Error('Failed to init no MPQ data found');
}

constructor(basePath: string) {
this.basePath = basePath;
mpq.basePath = basePath;
await this.initLang(mpq, log);
await this.initMonsters(mpq, log);
return mpq;
}

static async create(basePath: string, log?: Logger): Promise<Diablo2Mpq> {
return new Diablo2Mpq(basePath).init(log);
}
/** Load in the lang files */
static async initLang(mpq: Diablo2MpqData, logger?: Logger): Promise<void> {
// TODO these lang files need to be extracted, would be good to load from the MPQ directly
const LangPath = `${mpq.basePath}/data/local/LNG/ENG`;
const langFolderFiles = await fs.readdir(LangPath);

/**
* Translate a string using the lang files
* @param id Id to translate
*/
t(id: string | number | undefined, defaultValue: string | undefined = undefined): string | undefined {
if (id == null) return defaultValue;
if (typeof id == 'number') return this.lang.index[id]?.value;
return this.lang.map.get(id)?.value;
const langFiles = langFolderFiles.filter((f) => LangReader.LangFiles.find((lf) => f.toLowerCase().startsWith(lf)));
for (const langFile of langFiles) {
const startTime = Date.now();
const bytes = await fs.readFile(path.join(LangPath, langFile));
const langItems = LangReader.parse(bytes);
const duration = Date.now() - startTime;

for (const itm of langItems) mpq.lang.add(itm.key, itm.index, itm.value);
logger?.debug({ file: langFile, records: langItems.length, duration }, 'MPQ:Load:Language');
}
}

async init(log?: Logger): Promise<Diablo2Mpq> {
if (!existsSync(path.join(this.basePath, 'data'))) {
log?.error({ basePath: this.basePath }, 'No /data found, mpq needs to be extracted');
throw new Error('Failed to init no MPQ data found');
/** Load in the lang files */
static async initMonsters(mpq: Diablo2MpqData, logger?: Logger): Promise<void> {
// TODO these lang files need to be extracted, would be good to load from the MPQ directly
const BinPath = `${mpq.basePath}/data/global/excel`;
const binFolderFiles = await fs.readdir(BinPath);

const monStatFile = binFolderFiles.find((f) => f.toLowerCase() == 'monstats.bin');
if (monStatFile) {
const startTime = Date.now();
const bytes = await fs.readFile(path.join(BinPath, monStatFile));
const monItems = MonsterReader.raw(bytes as any);
const duration = Date.now() - startTime;

for (const monster of monItems.monsters) mpq.monsters.add(monster.id, monster.nameLangId);
logger?.debug({ file: monStatFile, records: monItems.monsters.length, duration }, 'MPQ:Load:Monster');
}

await this.lang.init(log);
await this.monsters.init(log);
return this;
const monStat2File = binFolderFiles.find((f) => f.toLowerCase() == 'monstats2.bin');
if (monStat2File) {
const startTime = Date.now();
const bytes = await fs.readFile(path.join(BinPath, monStat2File));
const monItems = MonsterReader2.raw(bytes as any);
const duration = Date.now() - startTime;

for (const mon of monItems.monsters) {
mpq.monsters.addState(mon.id, mon.state);
}

logger?.debug({ file: monStat2File, records: monItems.monsters.length, duration }, 'MPQ:Load:Monster2');
}
}
}
77 changes: 0 additions & 77 deletions packages/bintools/src/scripts/read.files.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/bintools/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"outDir": "./build"
},
"include": ["src/**/*"],
"references": [ ]
"references": [{"path": "../data"} ]
}
16 changes: 7 additions & 9 deletions packages/core/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { Diablo2Mpq, Logger } from '@diablo2/bintools';
import { Diablo2PacketFactory } from '@diablo2/packets';
import { PacketsPod } from '@diablo2/packets/build/packets-pod';
import { Logger, Diablo2MpqLoader } from '@diablo2/bintools';
import { Diablo2Mpq, Diablo2MpqData } from '@diablo2/data';
import { Diablo2PacketFactory, PacketsPod } from '@diablo2/packets';
import { Diablo2GameSession } from './game.state';

export class Diablo2Client {
mpq: Diablo2Mpq;

mpq: Diablo2MpqData;
clientToServer = new Diablo2PacketFactory();
serverToClient = new Diablo2PacketFactory();

Expand All @@ -14,11 +13,10 @@ export class Diablo2Client {
for (const packet of Object.values(PacketsPod.server)) this.serverToClient.register(packet);
}

async init(path: string, logger: Logger): Promise<void> {
async init(path: string, logger: Logger, mpq = Diablo2Mpq): Promise<void> {
logger.info({ path }, 'Reading game data');

this.mpq = new Diablo2Mpq(path);
await this.mpq.init(logger);
this.mpq = mpq;
await Diablo2MpqLoader.init(path, logger, mpq);
}

startSession(log: Logger): Diablo2GameSession {
Expand Down
2 changes: 0 additions & 2 deletions packages/data/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
"test": "ospec --globs 'build/**/*.test.js'"
},
"dependencies": {
"@diablo2/huffman": "0.0.1",
"@diablo2/packets": "0.0.1"
},
"publishConfig": {
"access": "public"
Expand Down
Loading

0 comments on commit 2fa03e2

Please sign in to comment.