diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100644
index 5e6d704..0000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-version: 2
-
-jobs:
- build:
- parallelism: 1
- docker:
- - image: circleci/node:10
- steps:
- - checkout
- - restore_cache:
- key: dependency-cache-{{ checksum "package.json" }}
- - run: sudo apt-get install libpcap-dev
- - run: npm i
- - save_cache:
- key: dependency-cache-{{ checksum "package.json" }}
- paths:
- - ./node_modules
- - run: npm test
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..bbeb50a
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,25 @@
+name: CI
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ node-version: [14.x]
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v1
+ with:
+ node-version: ${{ matrix.node-version }}
+ - run: npm install
+ run: npm test
\ No newline at end of file
diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml
new file mode 100644
index 0000000..9d178bf
--- /dev/null
+++ b/.github/workflows/npm-publish.yml
@@ -0,0 +1,25 @@
+name: npm-publish
+on:
+ push:
+ branches:
+ - master # Change this to your default branch
+jobs:
+ npm-publish:
+ name: npm-publish
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@master
+ - name: Set up Node.js
+ uses: actions/setup-node@master
+ with:
+ node-version: 10.0.0
+ - name: Publish if version has been updated
+ uses: pascalgn/npm-publish-action@4f4bf159e299f65d21cd1cbd96fc5d53228036df
+ with: # All of theses inputs are optional
+ tag_name: "%s"
+ tag_message: "%s"
+ commit_pattern: "^Release (\\S+)"
+ env: # More info about the environment variables in the README
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Leave this as is, it's automatically generated
+ NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} # You need to set this in your repo settings
\ No newline at end of file
diff --git a/README.md b/README.md
index 213601c..1c91de3 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,78 @@
-# diablo2-protocol
-[](http://npmjs.com/package/diablo2-protocol)
-[](https://circleci.com/gh/MephisTools/diablo2-protocol)
-[](https://discord.gg/9RqtApv)
-[](https://gitpod.io/#https://github.com/MephisTools/diablo2-protocol)
-
-
-Network protocol for diablo 2 : create client and servers for diablo 1.13 and 1.14.
-[Bot example](https://www.youtube.com/watch?v=KYPTijLiwMI&feature=youtu.be)
-
-[Sniffer example](https://www.youtube.com/watch?v=R5yfRTR3-mY)
+# diablo2-protocol
+
+
+[![Contributors][contributors-shield]][contributors-url]
+[![Forks][forks-shield]][forks-url]
+[![Stargazers][stars-shield]][stars-url]
+[![Issues][issues-shield]][issues-url]
+[![MIT License][license-shield]][license-url]
+[](npm-url)
+[](build-url)
+[](discord-url)
+[](gitpod-url)
+
+
+
+
+
+
+
+
+
diablo2-protocol
+
+
+ Network protocol for diablo 2 : create client and servers for diablo 1.13 and 1.14.
+
+ Explore the docs »
+
+
+ View Demo
+ ·
+ Report Bug
+ ·
+ Request Feature
+
+
+
+
+## Table of Contents
+
+* [About the Project](#about-the-project)
+ * [Built With](#built-with)
+* [Getting Started](#getting-started)
+ * [Prerequisites](#prerequisites)
+ * [Installation](#installation)
+* [Usage](#usage)
+* [Projects using diablo2-protocol](#projects-using-diablo2-protocol)
+* [Documentation](#documentation)
+* [Roadmap](#roadmap)
+* [Contributing](#contributing)
+* [License](#license)
+* [Acknowledgements](#acknowledgements)
+
+## Built With
+
+*
+*
+
+
+## Getting Started
+
+### Prerequisites
+
+```sh
+npm install npm@latest -g
+```
-## Installation
+### Installation
-```
+```js
npm install diablo2-protocol
```
+
## Usage
Follow bot in a few lines
@@ -50,52 +105,64 @@ start()
```
-See docs/API.md
+## Projects using diablo2-protocol
-Follow bot example
+* [mephistools-sniffer](https://github.com/MephisTools/mephistools-sniffer) diablo 2 packet sniffing.
+* [diablo2-live-viewer](https://github.com/MephisTools/diablo2-live-viewer) web map hack, packets table and inventory.
+* [AutoTathamet](https://github.com/MephisTools/AutoTathamet) high level API to build bots for example
-```
-node examples/simpleBot.js [-h] [-v] -au USERNAME -ap PASSWORD -c CHARACTER -gn \
- GAMENAME -gp GAMEPASSWORD -gs GAMESERVER -s SIDSERVER \
- [-dv DIABLOVERSION] -k1 KEYCLASSIC -k2 KEYEXTENSION
-```
-
-Sniffer (Linux / MacOS only)
-
-```
-cd example/sniffer
-npm install
-sudo node sniffer.js
-```
-
-## Projects using diablo-protocol
-
-* [diablo2-live-viewer](https://github.com/MephisTools/diablo2-live-viewer) displaying a live diablo map and live packets table
-* [AutoTathamet](https://github.com/MephisTools/AutoTathamet) Create Diablo2 bots with a powerful, stable, and high level JavaScript API.
+## Documentation
+*
+
## Roadmap
-- [ ] Test all packets
-- [x] Sniffer
-- [x] more documentation
-- [ ] Proxy ?
-- [ ] More examples
-- [ ] Web / mobile interface
-
-## Docs
-
-### Diablo
-
-* doc of diablo2 implementations https://github.com/MephisTools/diablo2-protocol/wiki/Diablo-2-implementations-and-docs
-* packets general description http://www.blizzhackers.cc/viewtopic.php?f=182&t=444264
-* dump of a normal connection sequence http://www.blizzhackers.cc/viewtopic.php?t=406445
-* index of packets https://bnetdocs.org/packet/index
-* example of packet doc https://bnetdocs.org/packet/146
-* basic example of packet parsing in python of a diablo2 packet https://gist.github.com/rom1504/8d2824d9d89dbd8b991b102696a1321e
-
-### Libs
-
-* node basic client implementation https://nodejs.org/api/net.html#net_net_createconnection_options_connectlistener
-* protodef js implementation doc https://github.com/ProtoDef-io/node-protodef https://github.com/ProtoDef-io/node-protodef/blob/master/doc/api.md and https://github.com/ProtoDef-io/node-protodef/blob/master/example.js
-* protodef types https://github.com/ProtoDef-io/ProtoDef/blob/master/doc/datatypes.md
-* nodepcap for sniffing https://github.com/node-pcap/node_pcap
\ No newline at end of file
+
+See the [open issues](https://github.com/Mephistools/diablo2-protocol/issues) for a list of proposed features (and known issues).
+
+
+## Contributing
+
+Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
+
+1. Fork the Project
+2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
+3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
+4. Push to the Branch (`git push origin feature/AmazingFeature`)
+5. Open a Pull Request
+
+
+## License
+
+Distributed under the MIT License. See `LICENSE` for more information.
+
+
+
+## Acknowledgements
+
+* [GitHub Emoji Cheat Sheet](https://www.webpagefx.com/tools/emoji-cheat-sheet)
+* [Img Shields](https://shields.io)
+* [Choose an Open Source License](https://choosealicense.com)
+* [GitHub Pages](https://pages.github.com)
+
+
+
+[contributors-shield]: https://img.shields.io/github/contributors/Mephistools/diablo2-protocol.svg?style=flat-square
+[contributors-url]: https://github.com/Mephistools/diablo2-protocol/graphs/contributors
+[forks-shield]: https://img.shields.io/github/forks/Mephistools/diablo2-protocol.svg?style=flat-square
+[forks-url]: https://github.com/Mephistools/diablo2-protocol/network/members
+[stars-shield]: https://img.shields.io/github/stars/Mephistools/diablo2-protocol.svg?style=flat-square
+[stars-url]: https://github.com/Mephistools/diablo2-protocol/stargazers
+[issues-shield]: https://img.shields.io/github/issues/Mephistools/diablo2-protocol.svg?style=flat-square
+[issues-url]: https://github.com/Mephistools/diablo2-protocol/issues
+[license-shield]: https://img.shields.io/github/license/Mephistools/diablo2-protocol.svg?style=flat-square
+[license-url]: https://github.com/Mephistools/diablo2-protocol/blob/master/LICENSE.txt
+[product-screenshot]: images/screenshot.png
+[npm-shield]: https://img.shields.io/npm/v/diablo2-protocol.svg
+[npm-url]: http://npmjs.com/package/diablo2-protocol
+[build-shield]: https://github.com/Mephistools/diablo2-protocol/workflows/CI/badge.svg
+[build-url]: https://github.com/Mephistools/diablo2-protocol/actions?query=workflow%3A%22CI%22
+[discord-shield]: https://img.shields.io/badge/chat-on%20discord-brightgreen.svg
+[discord-url]: https://discord.gg/9RqtApv
+[gitpod-shield]: https://img.shields.io/badge/try-on%20gitpod-brightgreen.svg
+[gitpod-url]: https://gitpod.io/#https://github.com/MephisTools/diablo2-protocol
diff --git a/data/1.13/d2gs.json b/data/1.13/d2gs.json
index ee3db24..8829c61 100644
--- a/data/1.13/d2gs.json
+++ b/data/1.13/d2gs.json
@@ -878,6 +878,7 @@
}
]],
"D2GS_GAMEEXIT": ["container", []],
+ "D2GS_UNKNOWN3": ["container", []],
"packet": [
"container",
[
@@ -930,6 +931,7 @@
"0x28": "D2GS_INSERTSOCKETITEM",
"0x29": "D2GS_SCROLLTOTOME",
"0x2A": "D2GS_ITEMTOCUBE",
+ "0x2B": "D2GS_UNKNOWN3",
"0x2D": "D2GS_UNSELECTOBJ",
"0x2E": "D2GS_CHATUNK1",
"0x2F": "D2GS_NPCINIT",
@@ -1078,7 +1080,8 @@
"D2GS_SWITCHEQUIP": "D2GS_SWITCHEQUIP",
"D2GS_RESURRECTMERC": "D2GS_RESURRECTMERC",
"D2GS_INVENTORYTOBELT": "D2GS_INVENTORYTOBELT",
- "D2GS_GAMEEXIT": "D2GS_GAMEEXIT"
+ "D2GS_GAMEEXIT": "D2GS_GAMEEXIT",
+ "D2GS_UNKNOWN3": "D2GS_UNKNOWN3"
}
}
]
@@ -1298,6 +1301,10 @@
{
"name": "ladder",
"type": "lu8"
+ },
+ {
+ "name": "unknown",
+ "type": "lu8"
}
]],
"D2GS_LOADSUCCESSFUL": ["container", []],
@@ -2783,6 +2790,13 @@
}
]],
"D2GS_UNKNOWN38": ["container", []],
+ "D2GS_UNKNOWN39": ["container", [
+ {"name":"unknown","type" : ["array", {"count": 356, "type": "lu8" } ] }
+ ]],
+ "D2GS_UNKNOWN40": ["container", [
+ {"name":"unknown","type" : ["array", {"count": 12, "type": "lu8" } ] }
+ ]],
+ "D2GS_UNKOWNFF": ["container", []],
"packet": [
"container",
[
@@ -2974,7 +2988,10 @@
"0xB2": "D2GS_UNKNOWN37",
"0xB3": "D2GS_IPBAN",
"0xB4": "D2GS_UNKNOWN38",
- "0xB5": "D2GS_OVERHEAD"
+ "0xB5": "D2GS_OVERHEAD",
+ "0xD5": "D2GS_UNKNOWN39",
+ "0xF5": "D2GS_UNKNOWN40",
+ "0xFF": "D2GS_UNKOWNFF"
}
}
]
@@ -3167,7 +3184,10 @@
"D2GS_UNKNOWN36": "D2GS_UNKNOWN36",
"D2GS_UNKNOWN37": "D2GS_UNKNOWN37",
"D2GS_IPBAN": "D2GS_IPBAN",
- "D2GS_UNKNOWN38": "D2GS_UNKNOWN38"
+ "D2GS_UNKNOWN38": "D2GS_UNKNOWN38",
+ "D2GS_UNKNOWN39": "D2GS_UNKNOWN39",
+ "D2GS_UNKNOWN40": "D2GS_UNKNOWN40",
+ "D2GS_UNKOWNFF": "D2GS_UNKOWNFF"
}
}
]
diff --git a/data/1.14/d2gs.json b/data/1.14/d2gs.json
index 63bd19f..d536ef0 100644
--- a/data/1.14/d2gs.json
+++ b/data/1.14/d2gs.json
@@ -1298,6 +1298,10 @@
{
"name": "ladder",
"type": "lu8"
+ },
+ {
+ "name": "unknown",
+ "type": "lu8"
}
]],
"D2GS_LOADSUCCESSFUL": ["container", []],
@@ -2783,6 +2787,7 @@
}
]],
"D2GS_UNKNOWN38": ["container", []],
+ "D2GS_UNKNOWN39": ["container", []],
"D2GS_UNKOWNFF": ["container", []],
"packet": [
"container",
@@ -2976,6 +2981,7 @@
"0xB3": "D2GS_IPBAN",
"0xB4": "D2GS_UNKNOWN38",
"0xB5": "D2GS_OVERHEAD",
+ "0xD5": "D2GS_UNKNOWN39",
"0xFF": "D2GS_UNKOWNFF"
}
}
@@ -3170,6 +3176,7 @@
"D2GS_UNKNOWN37": "D2GS_UNKNOWN37",
"D2GS_IPBAN": "D2GS_IPBAN",
"D2GS_UNKNOWN38": "D2GS_UNKNOWN38",
+ "D2GS_UNKNOWN39": "D2GS_UNKNOWN39",
"D2GS_UNKOWNFF": "D2GS_UNKOWNFF"
}
}
diff --git a/docs/images/logo.png b/docs/images/logo.png
new file mode 100644
index 0000000..8fd9100
Binary files /dev/null and b/docs/images/logo.png differ
diff --git a/examples/simpleBot.js b/examples/simpleBot.js
index ce1ca42..0d4c24e 100644
--- a/examples/simpleBot.js
+++ b/examples/simpleBot.js
@@ -1,8 +1,8 @@
const { createClientDiablo } = require('..')
const { defaultVersion } = require('..')
-var ArgumentParser = require('argparse').ArgumentParser
-var parser = new ArgumentParser({
+const ArgumentParser = require('argparse').ArgumentParser
+const parser = new ArgumentParser({
version: '1.4.1',
addHelp: true,
description: 'Simple bot'
@@ -31,13 +31,11 @@ async function start () {
keyExtension,
delayPackets
})
- clientDiablo.on('D2GS_PLAYERMOVE', ({ targetX, targetY }) => {
- clientDiablo.write('D2GS_RUNTOLOCATION', {
- x: targetX,
- y: targetY
- })
+ clientDiablo.on('packet', packet => console.log(`packet: ${JSON.stringify(packet)}`))
+ clientDiablo.on('log', log => console.log(`log: ${log}`))
+ clientDiablo.on('error', err => {
+ console.log(`Error: message: ${JSON.stringify(err.message)}, raw: ${JSON.stringify(err.raw.toString('hex'))}`)
})
-
await clientDiablo.connect()
await clientDiablo.selectCharacter(character)
await clientDiablo.createGame(gameName, gamePassword, gameServer, 0)
diff --git a/examples/sniffer/package.json b/examples/sniffer/package.json
deleted file mode 100644
index d8f751f..0000000
--- a/examples/sniffer/package.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "name": "sniffer",
- "version": "0.0.0",
- "private": true,
- "dependencies": {
- "express": "^4.16.4",
- "pcap": "^2.1.0",
- "pug": "^2.0.3",
- "ws": "^6.1.2"
- },
- "description": "A sniffer example"
-}
diff --git a/examples/sniffer/sniffer.js b/examples/sniffer/sniffer.js
deleted file mode 100644
index 3fcb624..0000000
--- a/examples/sniffer/sniffer.js
+++ /dev/null
@@ -1,386 +0,0 @@
-const { supportedVersions, defaultVersion } = require('../..')
-
-if (process.argv.length !== 4) {
- console.log('Usage : node sniffer.js ')
- process.exit(1)
-}
-
-// If the version correspond to a supported version else use default
-const version = supportedVersions.find(v => v === process.argv[3]) ? process.argv[3] : defaultVersion
-
-/*
-in a new chrome tab press f12 then do this :
-const ws = new WebSocket('ws://localhost:8080')
-
-ws.onmessage = (message) => console.log(message.data)
- */
-
-const Parser = require('protodef').Parser
-const networkInterface = process.argv[2]
-
-const pcap = require('pcap')
-
-const tcpTracker = new pcap.TCPTracker()
-
-const WebSocket = require('ws')
-
-const wss = new WebSocket.Server({ port: 8080 })
-
-// Broadcast to all.
-wss.broadcast = function broadcast (data) {
- wss.clients.forEach(function each (client) {
- if (client.readyState === WebSocket.OPEN) {
- client.send(data)
- }
- })
-}
-
-const pcapSession = pcap.createSession(networkInterface, 'ip proto \\tcp')
-
-pcapSession.on('packet', function (rawPacket) {
- const packet = pcap.decode.packet(rawPacket)
- tcpTracker.track_packet(packet)
-})
-
-const FullPacketParser = require('protodef').Parser
-const ProtoDef = require('protodef').ProtoDef
-
-const express = require('express')
-const app = express()
-// app.use(express.static(`${__dirname}/public`))
-
-const {
- protocol,
- createSplitter,
- decompress,
- d2gsReader,
- itemParser,
- bitfieldLE
-} = require('../..')
-
-const mcpToServer = new ProtoDef(false)
-mcpToServer.addProtocol(protocol[version].mcp, ['toServer'])
-
-const mcpToClient = new ProtoDef(false)
-mcpToClient.addProtocol(protocol[version].mcp, ['toClient'])
-
-const sidToServer = new ProtoDef(false)
-sidToServer.addProtocol(protocol[version].sid, ['toServer'])
-
-const sidToClient = new ProtoDef(false)
-sidToClient.addProtocol(protocol[version].sid, ['toClient'])
-
-const bnftpToServer = new ProtoDef(false)
-bnftpToServer.addProtocol(protocol[version].bnftp, ['toServer'])
-
-const bnftpToClient = new ProtoDef(false)
-bnftpToClient.addProtocol(protocol['1.13'].bnftp, ['toClient'])
-
-const d2gsToClient = new ProtoDef(false)
-d2gsToClient.addTypes(d2gsReader)
-d2gsToClient.addTypes(bitfieldLE)
-d2gsToClient.addProtocol(protocol['1.13'].d2gs, ['toClient'])
-
-const d2gsToServer = new ProtoDef(false)
-d2gsToServer.addProtocol(protocol[version].d2gs, ['toServer'])
-
-const toClientParser = new FullPacketParser(d2gsToClient, 'packet')
-const splitter = createSplitter()
-splitter.sloppyMode = true
-let messagesToClient = []
-let messagesToServer = []
-
-splitter.on('data', data => {
- const uncompressedData = decompress(data)
-
- toClientParser.write(uncompressedData)
-})
-
-toClientParser.on('data', ({ data, buffer }) => {
- try {
- let { name, params } = data
-
- if (name === 'D2GS_ITEMACTIONWORLD' || name === 'D2GS_ITEMACTIONOWNED') {
- params = itemParser(buffer)
- }
- wss.broadcast(JSON.stringify({ protocol: 'd2gsToClient', name, params }))
- console.info('d2gsToClient : ', name, JSON.stringify(params))
- // console.log('raw', 'd2gsToClient', name, buffer)
- messagesToClient.push(`d2gsToClient : ${name} ${JSON.stringify(params)}`)
- } catch (err) {
- console.log(err)
- console.log('raw', 'd2gsToClient', buffer)
- }
-})
-
-let clientPortSid = null
-let clientPortBnFtp = null
-let compression = false
-
-// server ports
-const sidPort = '6112'
-const d2gsPort = '4000'
-let mcpPort = '6113'
-let mcpIp = null
-
-const trackedPorts = new Set([sidPort, d2gsPort, mcpPort])
-
-function displayD2gsToClient (data) {
- try {
- if (!compression) {
- if (data[0] !== 0xaf) { data = data.slice(1) }
-
- const parsed = d2gsToClient.parsePacketBuffer('packet', data).data
-
- const { name, params } = parsed
- wss.broadcast(JSON.stringify({ protocol: 'd2gsToClient', name, params }))
- console.info('d2gsToClient (uncompressed): ', name, JSON.stringify(params))
- if (name === 'D2GS_NEGOTIATECOMPRESSION' && params.compressionMode !== 0) {
- console.log('enable compression')
- compression = true
- }
- } else {
- splitter.write(data)
- }
- } catch (error) {
- console.log('d2gsToClient : ', error.message)
- console.log('raw', 'd2gsToClient', data)
- }
-}
-
-function displayParsed (proto, protoName, data, raw = false) {
- try {
- const { name, params } = proto.parsePacketBuffer('packet', data).data
- console.log(protoName, ':', name, JSON.stringify(params))
- wss.broadcast(JSON.stringify({ protocol: protoName, name, params }))
- if (raw) console.log('raw', protoName, name, data.toString('hex'))
- messagesToServer.push(`${protoName}:${name} ${JSON.stringify(params)}`)
- return { name, params }
- } catch (error) {
- if (raw) console.log('raw', protoName, data.toString('hex'))
- console.log(protoName, ':', error.message)
- }
-}
-
-function displayD2gsToServer (data) {
- displayParsed(d2gsToServer, 'd2gsToServer', data)
-}
-
-function displayMcpToServer (data) {
- displayParsed(mcpToServer, 'mcpToServer', data)
-}
-
-function displayMcpToClient (data) {
- displayParsed(mcpToClient, 'mcpToClient', data)
-}
-
-function displaySidToServer (data) {
- displayParsed(sidToServer, 'sidToServer', data)
-}
-
-function displaySidToClient (data) {
- const parsed = displayParsed(sidToClient, 'sidToClient', data)
- if (parsed.name === 'SID_LOGONREALMEX') {
- const IP = parsed.params.IP
- mcpIp = IP[0] + '.' + IP[1] + '.' + IP[2] + '.' + IP[3]
- mcpPort = parsed.params.port + ''
- console.log(`received SID_LOGONREALMEX ${JSON.stringify({ mcpIp, mcpPort })}`)
- }
-}
-
-const challengeParserClient = new Parser(bnftpToClient, 'CHALLENGE')
-challengeParserClient.on('error', err => console.log('bnftpToClient challenge error : ', err.message))
-challengeParserClient.on('data', (parsed) => {
- console.info('bnftpToClient challenge : ', JSON.stringify(parsed))
-})
-
-const protocolParserClient = new Parser(bnftpToClient, 'FILE_TRANSFER_PROTOCOL')
-protocolParserClient.on('error', err => console.log('bnftpToClient protocol error : ', err.message))
-protocolParserClient.on('data', (parsed) => {
- console.info('bnftpToClient protocol : ', JSON.stringify(parsed))
-})
-
-function displayBnftpToClient (data) {
- try {
- protocolParserClient.write('FILE_TRANSFER_PROTOCOL')
- // console.log('bnftpToClient protocol: ', JSON.stringify(bnftpToClient.parsePacketBuffer('FILE_TRANSFER_PROTOCOL', data).data))
- } catch (error) {
- console.log('bnftpToClient error: ', error)
- console.log('bnftpToClient protocol: ', data)
- // challengeParserClient.write(data)
- }
-}
-
-const challengeParserServer = new Parser(bnftpToServer, 'CHALLENGE')
-challengeParserServer.on('error', err => console.log('bnftpToServer bnftp error : ', err.message))
-challengeParserServer.on('data', (parsed) => {
- console.info('bnftpToServer challenge : ', JSON.stringify(parsed))
-})
-const protocolParserServer = new Parser(bnftpToServer, 'FILE_TRANSFER_PROTOCOL')
-protocolParserServer.on('error', err => console.log('bnftpToServer bnftp error : ', err.message))
-protocolParserServer.on('data', (parsed) => {
- console.info('bnftpToServer protocol : ', JSON.stringify(parsed))
-})
-function displayBnftpToServer (data) {
- try {
- protocolParserServer.write('FILE_TRANSFER_PROTOCOL')
- // console.log('bnftpToServer protocol: ', JSON.stringify(bnftpToServer.parsePacketBuffer('FILE_TRANSFER_PROTOCOL', data).data))
- // console.log('bnftpToServer protocol: ', data)
- } catch (error) {
- console.log('bnftpToServer error: ', error)
- console.log('bnftpToServer write challenge', data)
- // challengeParserServer.write(data)
- }
-}
-
-// tracker emits sessions, and sessions emit data
-tcpTracker.on('session', function (session) {
- const srcPort = session.src_name.split(':')[1]
- const dstPort = session.dst_name.split(':')[1]
- const srcIp = session.src_name.split(':')[0]
- const dstIp = session.dst_name.split(':')[0]
- if (!trackedPorts.has(srcPort) && !trackedPorts.has(dstPort)) {
- return
- }
-
- /*
- if (dstPort === sidPort) {
- if (clientPortSid === null) {
- console.log('zalloooo')
- clientPortSid = srcPort
- } else {
- clientPortBnFtp = srcPort
- }
- }
- if (srcPort === sidPort) {
- if (clientPortSid === null) {
- clientPortSid = dstPort
- } else {
- clientPortBnFtp = dstPort
- }
- } */
-
- session.on('start', function () {
- if (srcPort === d2gsPort || dstPort === d2gsPort) {
- console.log('Start of d2gs session')
- }
- if ((srcPort === mcpPort || dstPort === mcpPort) && (dstIp === mcpIp || srcIp === mcpIp || mcpIp === null)) {
- console.log('Start of mcp session')
- }
- if ((srcPort === sidPort || dstPort === sidPort) && ((dstIp !== mcpIp && srcIp !== mcpIp) || mcpIp === null)) {
- console.log('Start of sid session')
- }
- })
- session.on('data send', function (session, data) {
- if (srcPort === d2gsPort) {
- displayD2gsToClient(data)
- }
-
- if (dstPort === d2gsPort) {
- displayD2gsToServer(data)
- }
-
- if (srcPort === mcpPort && (srcIp === mcpIp || mcpIp === null)) {
- displayMcpToClient(data)
- }
-
- if (dstPort === mcpPort && (dstIp === mcpIp || mcpIp === null)) {
- displayMcpToServer(data)
- }
-
- if (dstPort === sidPort && data.length === 1 && data[0] === 1) {
- console.log(`sid on port ${srcPort} : ${data}`)
- clientPortSid = srcPort
- return
- }
-
- if (dstPort === sidPort && data.length === 1 && data[0] === 2) {
- console.log(`bnftp on port ${srcPort} : ${data}`)
- clientPortBnFtp = srcPort
- return
- }
-
- if (srcPort === sidPort && dstPort === clientPortSid && (srcIp !== mcpIp || mcpIp === null)) {
- displaySidToClient(data)
- }
-
- if (dstPort === sidPort && srcPort === clientPortSid && (dstIp !== mcpIp || mcpIp === null)) {
- displaySidToServer(data)
- }
-
- if (srcPort === sidPort && dstPort === clientPortBnFtp) {
- displayBnftpToClient(data)
- }
-
- if (dstPort === sidPort && srcPort === clientPortBnFtp) {
- displayBnftpToServer(data)
- }
- })
- session.on('data recv', function (session_, data) {
- if (srcPort === d2gsPort) {
- displayD2gsToServer(data)
- }
-
- if (dstPort === d2gsPort) {
- displayD2gsToClient(data)
- }
-
- if (srcPort === mcpPort && (srcIp === mcpIp || mcpIp === null)) {
- displayMcpToServer(data)
- }
-
- if (dstPort === mcpPort && (dstIp === mcpIp || mcpIp === null)) {
- displayMcpToClient(data)
- }
-
- if (srcPort === sidPort && data.length === 1 && data[0] === 1) {
- console.log(`sid on port ${dstPort} : ${data}`)
- clientPortSid = dstPort
- return
- }
-
- if (srcPort === sidPort && data.length === 1 && data[0] === 2) {
- console.log(`bnftp on port ${dstPort} : ${data}`)
- clientPortBnFtp = dstPort
- return
- }
-
- if (srcPort === sidPort && dstPort === clientPortSid && (srcIp !== mcpIp || mcpIp === null)) {
- displaySidToServer(data)
- }
-
- if (dstPort === sidPort && srcPort === clientPortSid && (dstIp !== mcpIp || mcpIp === null)) {
- displaySidToClient(data)
- }
-
- if (srcPort === sidPort && dstPort === clientPortBnFtp) {
- displayBnftpToServer(data)
- }
-
- if (dstPort === sidPort && srcPort === clientPortBnFtp) {
- displayBnftpToClient(data)
- }
- })
-
- session.on('end', function () {
- if (srcPort === d2gsPort || dstPort === d2gsPort) {
- console.log('End of d2gs session')
- }
- if ((srcPort === mcpPort || dstPort === mcpPort) && (dstIp === mcpIp || srcIp === mcpIp || mcpIp === null)) {
- console.log('End of mcp session')
- }
- if ((srcPort === sidPort || dstPort === sidPort) && (dstIp === mcpIp || srcIp === mcpIp || mcpIp === null)) {
- console.log('End of sid session')
- }
- })
-})
-
-console.log('loaded')
-
-// Set view engine to use, in this case 'pug'
-app.set('view engine', 'pug')
-
-app.get('/', (req, res) => {
- res.render('index', { title: 'Sniffer', messagesToClient: messagesToClient, messagesToServer: messagesToServer })
-})
-app.listen(process.env.PORT || 3001)
diff --git a/index.js b/index.js
index fb1d5e2..97ccc47 100644
--- a/index.js
+++ b/index.js
@@ -4,10 +4,15 @@ const { decompress, compress, getPacketSize } = require('./lib/utils/compression
const d2gsReader = require('./lib/utils/d2gsSpecialReader')
const getHash = require('./lib/utils/getHash')
const createServerDiablo = require('./lib/server/createServerDiablo')
+const ClientSid = require('./lib/client/clientSid')
+const ClientMcp = require('./lib/client/clientMcp')
+const ClientD2gs = require('./lib/client/clientD2gs')
+const ClientDiablo = require('./lib/client/clientDiablo')
const ServerDiablo = require('./lib/server/serverDiablo')
+const ServerD2gs = require('./lib/server/serverD2gs')
+
const createServerSid = require('./lib/server/createServerSid')
const createServerMcp = require('./lib/server/createServerMcp')
-const ServerD2gs = require('./lib/server/serverD2gs')
const itemParser = require('./lib/utils/itemParser')
const bitfieldLE = require('./lib/utils/bitfieldLE')
const { defaultVersion, supportedVersions } = require('./version')
@@ -37,6 +42,10 @@ module.exports = {
createServerDiablo,
createServerSid,
createServerMcp,
+ ClientSid,
+ ClientMcp,
+ ClientD2gs,
+ ClientDiablo,
ServerD2gs,
ServerDiablo,
itemParser,
diff --git a/lib/client/baseClient.js b/lib/client/baseClient.js
new file mode 100644
index 0000000..3889e5e
--- /dev/null
+++ b/lib/client/baseClient.js
@@ -0,0 +1,68 @@
+const EventEmitter = require('events').EventEmitter
+const net = require('net')
+
+
+/**
+ * Abstract Class Client.
+ *
+ * @class BaseClient
+ */
+class BaseClient extends EventEmitter {
+
+ constructor(version, isServer = false) {
+ super()
+ if (this.constructor === BaseClient) {
+ throw new Error("Abstract classes can't be instantiated.");
+ }
+ this.isServer = isServer
+ this.protoToServer = null
+ this.protoToClient = null
+ }
+
+ /**
+ * Parse a packet, should emit the parsed packet if no error
+ * @param data raw data
+ * @param toServer to server or to client
+ */
+ parse(data, toServer) {
+ if (data.length === 1 && data[0] === 0x01) {
+ this.emit('init_connection') // TODO: maybe could emit some useful data (ip ..)
+ return
+ }
+ const proto = this.isServer ? this.protoToServer : this.protoToClient
+ try {
+ const parsed = proto.parsePacketBuffer('packet', data).data
+ const {name, params} = parsed
+ this.emit(name, params)
+ this.emit('packet', {name, params, toServer})
+ } catch (err) {
+ err.raw = data
+ err.toServer = toServer
+ this.emit('error', err)
+ }
+ }
+
+ connect (host, port) {
+ this.host = host
+ this.port = port
+ this.setSocket(net.createConnection({ port: this.port, host: this.host }, () => {
+ this.emit('connect')
+ }))
+ }
+
+ setSocket (socket) {
+ this.socket = socket
+ this.socket.on('data', (data) => {
+ try {
+ this.parse(data, !this.isServer)
+ } catch (err) {
+ this.emit('error', err)
+ }
+ })
+ this.socket.on('end', () => {
+ this.emit('end')
+ })
+ }
+}
+
+module.exports = BaseClient
diff --git a/lib/client/clientD2gs.js b/lib/client/clientD2gs.js
index 104e7af..abe638a 100644
--- a/lib/client/clientD2gs.js
+++ b/lib/client/clientD2gs.js
@@ -1,20 +1,21 @@
-const net = require('net')
-
const ProtoDef = require('protodef').ProtoDef
const FullPacketParser = require('protodef').Parser
-const EventEmitter = require('events').EventEmitter
-const { decompress } = require('../utils/compression')
+const {decompress} = require('../utils/compression')
const d2gsReader = require('../utils/d2gsSpecialReader')
const bitfieldLE = require('../utils/bitfieldLE')
-const itemParser = require('../utils/itemParser')
+const BaseClient = require('./baseClient')
+
-const { createSplitter, createFramer } = require('../utils/splitter')
+const {createSplitter, createFramer} = require('../utils/splitter')
+// const IGNORED_PACKETS = [0x01, 0xF1, 0x02] // Non debugged packets that just pollute
+const LOGIN_PACKETS = [0xAF]
-class Client extends EventEmitter {
- constructor (version, isServer = false) {
- super()
+class Client extends BaseClient {
+ constructor(version, isServer = false, includeRaw = false) {
+ super(version, isServer)
this.compression = false
- this.isServer = isServer
+ this.version = version
+ this.includeRaw = includeRaw
const protocol = require(`../../data/${version}/d2gs`)
this.protoToServer = new ProtoDef(false)
this.protoToServer.addTypes(d2gsReader)
@@ -26,101 +27,89 @@ class Client extends EventEmitter {
this.protoToClient.addTypes(bitfieldLE)
this.protoToClient.addProtocol(protocol, ['toClient'])
this.toClientParser = new FullPacketParser(this.protoToClient, 'packet')
- }
-
- connect (host, port) {
- this.host = host
- this.port = port
- this.setSocket(net.createConnection({ port: this.port, host: this.host }, () => {
- this.emit('connect')
- }))
- }
-
- setSocket (socket) {
- this.socket = socket
-
this.splitter = createSplitter()
+ // this.splitter.sloppyMode = true
this.framer = createFramer()
- this.framer.on('data', data => {
- // console.log('sending buffer d2gs ' + data.toString('hex'))
- this.socket.write(data)
- })
this.splitter.on('data', data => {
- // console.log('compressed d2gs received hex : ' + data.toString('hex'))
- const uncompressedData = decompress(data)
-
- // console.log('after decompression d2gs received hex : ' + uncompressedData.toString('hex'))
- this.toClientParser.write(uncompressedData)
- })
-
- this.toClientParser.on('data', ({ data, buffer }) => {
- let { name, params } = data
-
- if (this.isServer === false && (name === 'D2GS_ITEMACTIONWORLD' || name === 'D2GS_ITEMACTIONOWNED')) {
- params = itemParser(buffer)
+ try {
+ const uncompressedData = decompress(data)
+ this.toClientParser.write(uncompressedData)
+ } catch (err) {
+ err.raw = data
+ err.toServer = false
+ this.emit('error', err)
}
-
- // console.info('received compressed packet', name, JSON.stringify(params))
- console.info('d2gsToClient : ', name, JSON.stringify(params))
-
+ })
+ this.toClientParser.on('data', ({data, buffer}) => {
+ let {name, params} = data
this.emit(name, params)
- this.emit('packet', { name, params })
+ const toServer = false
+ if (this.includeRaw) {
+ const raw = buffer
+ this.emit('packet', {name, params, toServer, raw})
+ } else this.emit('packet', {name, params, toServer})
})
- this.toClientParser.on('error', err => console.log('d2gsToClient error : ', err.message))
-
- const proto = this.isServer ? this.protoToServer : this.protoToClient
- this.socket.on('data', (data) => {
- // console.log('received that d2gs hex', data.toString('hex'))
+ this.toClientParser.on('error', err => this.emit('error', err))
+ }
- if (!this.compression) {
- try {
- if (data[0] === 0x2b && this.isServer === true) {
- console.log('d2gsToClient anti-cheat')
- return
+ /**
+ * Parse a packet, should emit the parsed packet if no error
+ * @param data raw data
+ * @param toServer to server or to client
+ */
+ parse(data, toServer) {
+ try {
+ let parsed
+ // Message to client
+ if (!toServer) {
+ // Uncompressed
+ if (!this.compression) {
+ // Not sure optimal conditions, anyway required in some servers
+ if (this.version === 1.13 &&
+ !LOGIN_PACKETS.includes(data[0]) &&
+ this.isServer === false) {
+ data = data.slice(1)
}
- if (data[0] !== 0xaf && this.isServer === false) { data = data.slice(1) }
-
- const parsed = proto.parsePacketBuffer('packet', data).data
-
- const { name, params } = parsed
- // console.info('received uncompressed packet', name, JSON.stringify(params))
- console.info('d2gsToClient (uncompressed): ', name, JSON.stringify(params))
-
- this.emit(name, params)
- this.emit('packet', { name, params })
- } catch (err) {
- console.log(err.message)
+ parsed = this.protoToClient.parsePacketBuffer('packet', data).data
+ } else { // Compressed, need to be split-ed and uncompressed
+ this.splitter.write(data)
+ return
}
- } else {
- this.splitter.write(data)
+ } else { // Message to server
+ parsed = this.protoToServer.parsePacketBuffer('packet', data).data
}
- })
+ const {name, params} = parsed
+ this.emit(name, params)
+ if (this.includeRaw) {
+ const raw = data
+ this.emit('packet', {name, params, toServer, raw})
+ } else this.emit('packet', {name, params, toServer})
+ } catch (err) {
+ err.raw = data
+ err.toServer = toServer
+ this.emit('error', err)
+ }
+ }
- this.socket.on('end', () => {
- console.log('disconnected from server d2gs')
+ setSocket(socket) {
+ super.setSocket(socket)
+ this.framer.on('data', data => {
+ this.socket.write(data)
})
}
- enableCompression () {
- this.compression = true
+ enableCompression(value) {
+ this.compression = value
}
- write (packetName, params) {
+ write(packetName, params) {
const proto = this.isServer ? this.protoToClient : this.protoToServer
- try {
- const buffer = proto.createPacketBuffer('packet', {
- name: packetName,
- params
- })
-
- // console.info('sending uncompressed packet', packetName, params)
- console.info('d2gsToServer : ', packetName, params)
- const buffer2 = this.isServer && packetName !== 'D2GS_NEGOTIATECOMPRESSION' ? Buffer.concat([Buffer.from([buffer.length + 1]), buffer]) : buffer
- this.socket.write(buffer2)
- // console.log('sending that hex', buffer2)
- } catch (err) {
- console.log(err)
- }
+ const buffer = proto.createPacketBuffer('packet', {
+ name: packetName,
+ params
+ })
+ const buffer2 = this.isServer && packetName !== 'D2GS_NEGOTIATECOMPRESSION' ? Buffer.concat([Buffer.from([buffer.length + 1]), buffer]) : buffer
+ this.socket.write(buffer2)
}
}
diff --git a/lib/client/clientDiablo.js b/lib/client/clientDiablo.js
index e1db12f..1e2f9ef 100644
--- a/lib/client/clientDiablo.js
+++ b/lib/client/clientDiablo.js
@@ -8,49 +8,55 @@ class ClientDiablo extends EventEmitter {
setClientSid (clientSid) {
this.clientSid = clientSid
- this.clientSid.on('packet', ({ name, params }) => {
+ this.clientSid.on('packet', ({ name, params, toServer }) => {
this.emit(name, params)
- this.emit('packet', { name, params, protocol: this.clientSid.isServer ? 'sidToServer' : 'sidToClient' })
+ this.emit('packet', { name, params, toServer })
})
+ this.clientSid.on('error', err => this.emit('error', err))
}
setClientMcp (clientMcp) {
this.clientMcp = clientMcp
- this.clientMcp.on('packet', ({ name, params }) => {
+ this.clientMcp.on('packet', ({ name, params, toServer }) => {
this.emit(name, params)
- this.emit('packet', { name, params, protocol: this.clientMcp.isServer ? 'mcpToServer' : 'mcpToClient' })
+ this.emit('packet', { name, params, toServer })
})
+ this.clientMcp.on('error', err => this.emit('error', err))
+
}
setClientBnftp (clientBnftp) {
this.clientBnftp = clientBnftp
- this.clientBnftp.on('packet', ({ name, params }) => {
+ this.clientBnftp.on('packet', ({ name, params, toServer }) => {
this.emit(name, params)
- this.emit('packet', { name, params, protocol: this.clientBnftp.isServer ? 'bnftpToServer' : 'bnftpToClient' })
+ this.emit('packet', { name, params, toServer })
})
+ this.clientBnftp.on('error', err => this.emit('error', err))
+
}
setClientD2gs (clientD2gs) {
this.clientD2gs = clientD2gs
- this.clientD2gs.on('packet', ({ name, params }) => {
+ this.clientD2gs.on('packet', ({ name, params, toServer, raw }) => {
this.emit(name, params)
- this.emit('packet', { name, params, protocol: this.clientD2gs.isServer ? 'd2gsToServer' : 'd2gsToClient' })
+ this.emit('packet', { name, params, toServer, raw })
})
+ this.clientD2gs.on('error', err => this.emit('error', err))
}
write (name, params) {
setTimeout(() => {
if (name.startsWith('SID')) {
- this.emit('sentPacket', { name, params, protocol: this.clientSid.isServer ? 'sidToClient' : 'sidToServer' })
+ this.emit('sentPacket', { name, params, toServer: !this.clientSid.isServer })
this.clientSid.write(name, params)
} else if (name.startsWith('MCP')) {
- this.emit('sentPacket', { name, params, protocol: this.clientMcp.isServer ? 'mcpToClient' : 'mcpToServer' })
+ this.emit('sentPacket', { name, params, toServer: !this.clientMcp.isServer })
this.clientMcp.write(name, params)
} else if (name.startsWith('FILE_TRANSFER_PROTOCOL')) {
- this.emit('sentPacket', { name, params, protocol: this.clientBnftp.isServer ? 'bnftpToClient' : 'bnftpToServer' })
+ this.emit('sentPacket', { name, params, toServer: !this.clientBnftp.isServer })
this.clientBnftp.write(name, params)
} else if (name.startsWith('D2GS')) {
- this.emit('sentPacket', { name, params, protocol: this.clientD2gs.isServer ? 'd2gsToClient' : 'd2gsToServer' })
+ this.emit('sentPacket', { name, params, toServer: !this.clientD2gs.isServer })
this.clientD2gs.write(name, params)
}
}, this.delayPackets) // Delay between packets, TODO: issue with async ? ...
diff --git a/lib/client/clientMcp.js b/lib/client/clientMcp.js
index 11b8ead..643b75b 100644
--- a/lib/client/clientMcp.js
+++ b/lib/client/clientMcp.js
@@ -1,70 +1,25 @@
-const net = require('net')
-
const ProtoDef = require('protodef').ProtoDef
-const EventEmitter = require('events').EventEmitter
+const BaseClient = require('./baseClient')
-class Client extends EventEmitter {
+class Client extends BaseClient {
constructor (version, isServer = false) {
- super()
- this.isServer = isServer
+ super(version, isServer)
const mcp = require(`../../data/${version}/mcp`)
- this.mcpToServer = new ProtoDef(false)
- this.mcpToServer.addProtocol(mcp, ['toServer'])
-
- this.mcpToClient = new ProtoDef(false)
- this.mcpToClient.addProtocol(mcp, ['toClient'])
- }
+ this.protoToServer = new ProtoDef(false)
+ this.protoToServer.addProtocol(mcp, ['toServer'])
- connect (host, port) {
- this.host = host
- this.port = port
- const socket = net.createConnection({ port: this.port, host: this.host }, () => {
- this.emit('connect')
- })
- this.setSocket(socket)
- }
-
- setSocket (socket) {
- this.socket = socket
-
- this.socket.on('data', (data) => {
- try {
- // console.log('received that hex MCP', data.toString('hex'))
- if (data.length === 1 && data[0] === 0x01) {
- this.emit('init_connection')
- return
- }
- const proto = this.isServer ? this.mcpToServer : this.mcpToClient
- const parsed = proto.parsePacketBuffer('packet', data).data
-
- const { name, params } = parsed
- console.info('mcpToClient : ', name, JSON.stringify(parsed))
-
- this.emit(name, params)
- this.emit('packet', { name, params })
- } catch (err) {
- console.log(err.message)
- }
- })
-
- this.socket.on('end', () => {
- console.log('disconnected from mcp server')
- })
+ this.protoToClient = new ProtoDef(false)
+ this.protoToClient.addProtocol(mcp, ['toClient'])
}
write (name, params) {
- const proto = this.isServer ? this.mcpToClient : this.mcpToServer
+ const proto = this.isServer ? this.protoToClient : this.protoToServer
const buffer = proto.createPacketBuffer('packet', {
size: 0,
name: name,
params
})
-
- console.info('mcpToServer : ', name, params)
buffer.writeInt16LE(buffer.length, 0)
-
- // console.log('sending that hex MCP ', buffer)
-
this.socket.write(buffer)
}
}
diff --git a/lib/client/clientSid.js b/lib/client/clientSid.js
index 241bb3b..094c85b 100644
--- a/lib/client/clientSid.js
+++ b/lib/client/clientSid.js
@@ -1,13 +1,9 @@
-const net = require('net')
-
const ProtoDef = require('protodef').ProtoDef
+const BaseClient = require('./baseClient')
-const EventEmitter = require('events').EventEmitter
-
-class Client extends EventEmitter {
+class Client extends BaseClient {
constructor (version, isServer = false) {
- super()
- this.isServer = isServer
+ super(version, isServer)
const sid = require(`../../data/${version}/sid`)
this.protoToServer = new ProtoDef(false)
this.protoToServer.addProtocol(sid, ['toServer'])
@@ -16,54 +12,15 @@ class Client extends EventEmitter {
this.protoToClient.addProtocol(sid, ['toClient'])
}
- connect (host, port) {
- this.host = host
- this.port = port
- this.setSocket(net.createConnection({ port: this.port, host: this.host }, () => {
- this.emit('connect')
- }))
- }
-
- setSocket (socket) {
- this.socket = socket
- this.socket.on('data', (data) => {
- try {
- // console.log('received that hex sid', data.toString('hex'))
- if (data.length === 1 && data[0] === 0x01) {
- this.emit('init_connection')
- return
- }
- const proto = this.isServer ? this.protoToServer : this.protoToClient
- const parsed = proto.parsePacketBuffer('packet', data).data
-
- const { name, params } = parsed
- console.info('sidToClient : ', name, JSON.stringify(parsed))
-
- this.emit(name, params)
- this.emit('packet', { name, params })
- } catch (err) {
- console.log(err.message)
- }
- })
- this.socket.on('end', () => {
- console.log('disconnected from server sid')
- })
- }
-
- write (packetName, params) {
+ write (name, params) {
const proto = this.isServer ? this.protoToClient : this.protoToServer
const buffer = proto.createPacketBuffer('packet', {
- name: packetName,
+ name: name,
size: 0,
ff: 255,
params
})
-
- console.info('sidToServer : ', packetName, JSON.stringify(params))
buffer.writeInt16LE(buffer.length, 2)
- // console.info(buffer.toString('hex'))
-
- // console.log('sending that hex SID ', buffer)
this.socket.write(buffer)
}
}
diff --git a/lib/client/createClientD2gs.js b/lib/client/createClientD2gs.js
index 6f77a82..93c83da 100644
--- a/lib/client/createClientD2gs.js
+++ b/lib/client/createClientD2gs.js
@@ -12,7 +12,7 @@ async function createClientD2gs (clientDiablo, version) {
clientDiablo.emit('clientD2gsReady', clientD2gs)
clientD2gs.on('connect', () => {
- console.log('connected to clientD2gs')
+ clientDiablo.emit('log', 'connected to clientD2gs')
})
clientDiablo.on('D2GS_NEGOTIATECOMPRESSION', (data) => {
@@ -34,26 +34,31 @@ async function createClientD2gs (clientDiablo, version) {
})
setInterval(() => {
+ clientDiablo.emit('log', `Latency ${clientDiablo.lastPing - clientDiablo.lastPong}`)
+ if (clientDiablo.lastPing - clientDiablo.lastPong > 10000) {
+ clientDiablo.emit('error', new Error('Server stopped answering, probably crashed'))
+ process.exit(1)
+ }
clientDiablo.write('D2GS_PING', {
tickCount: Date.now() - clientDiablo.initialTime,
delay: clientDiablo.latency,
wardenResponse: 0
})
- clientDiablo.timeAtLastPing = Date.now()
+ clientDiablo.lastPing = Date.now()
}, 5000)
clientDiablo.on('D2GS_PONG', () => {
- clientDiablo.latency = Date.now() - clientDiablo.timeAtLastPing
- // console.log('latency is ' + clientDiablo.latency + 'ms')
+ clientDiablo.latency = Date.now() - clientDiablo.lastPing
+ clientDiablo.lastPong = Date.now()
})
if (version === '1.13') await once(clientDiablo, 'D2GS_LOGONRESPONSE')
- else {
- await once(clientDiablo, 'D2GS_GAMELOADING')
- }
+ else await once(clientDiablo, 'D2GS_GAMELOADING')
clientDiablo.write('D2GS_ENTERGAMEENVIRONMENT', {})
- if (version === '1.13') clientD2gs.socket.write(Buffer.from('2b0155332211', 'hex'))// 2b0155332211 // PoD anticheat packet XD
- clientD2gs.enableCompression()
+ if (version === '1.13') {
+ clientD2gs.socket.write(Buffer.from('2b0155332211', 'hex'))// 2b0155332211 // PoD anticheat
+ clientD2gs.enableCompression(true)
+ }
clientDiablo.emit('gameJoined')
}
diff --git a/lib/client/createClientDiablo.js b/lib/client/createClientDiablo.js
index ca1ca95..68d4ff6 100644
--- a/lib/client/createClientDiablo.js
+++ b/lib/client/createClientDiablo.js
@@ -1,14 +1,10 @@
const ClientDiablo = require('./clientDiablo')
-
const createClientSid = require('./createClientSid')
-
const createClientMcp = require('./createClientMcp')
-
const createClientD2gs = require('./createClientD2gs')
-
const { once } = require('once-promise')
-
const { toNumVersion } = require('../../version')
+const diabloData = require('diablo2-data')('pod_1.13d')
function toCamelCase (s) {
if (s === '') { return '' }
@@ -26,7 +22,8 @@ function createClientDiablo ({ username, password, host, version, keyClassic, ke
clientDiablo.connect = async () => {
const { hostMcp, portMcp, MCPCookie, MCPStatus, MCPChunk1, MCPChunk2, battleNetUniqueName } = await createClientSid(clientDiablo, host, version)
- await createClientMcp(clientDiablo, hostMcp, portMcp, MCPCookie, MCPStatus, MCPChunk1, MCPChunk2, battleNetUniqueName, version)
+ const result = await createClientMcp(clientDiablo, hostMcp, portMcp, MCPCookie, MCPStatus, MCPChunk1, MCPChunk2, battleNetUniqueName, version)
+ console.log(`Got characters ${result.characters}`)
}
clientDiablo.selectCharacter = async (character) => {
@@ -56,7 +53,6 @@ function createClientDiablo ({ username, password, host, version, keyClassic, ke
// Create game = join game
clientDiablo.createGame = async (gameName, gamePassword, gameServer, difficulty = 0, joinIfAlreadyExists = true) => {
- console.log('joinIfAlreadyExists', joinIfAlreadyExists)
gameName = toCamelCase(gameName)
gamePassword = toCamelCase(gamePassword)
clientDiablo.write('MCP_CREATEGAME', {
@@ -101,21 +97,7 @@ function createClientDiablo ({ username, password, host, version, keyClassic, ke
const { gameToken, IPOfD2GSServer: IP2, gameHash, result } = await once(clientDiablo, 'MCP_JOINGAME')
if (result !== 0) {
- const errors = {
- 0x00: 'Game joining succeeded. In this case, Diablo 2 terminates the connection with the MCP and initiates the connection with the D2GS.',
- 0x29: 'Password incorrect.',
- 0x2A: 'Game does not exist.',
- 0x2B: 'Game is full.',
- 0x2C: 'You do not meet the level requirements for this game.',
- 0x6E: 'A dead hardcore character cannot join a game.',
- 0x71: 'A non-hardcore character cannot join a game created by a Hardcore character.',
- 0x73: 'Unable to join a Nightmare game.',
- 0x74: 'Unable to join a Hell game.',
- 0x78: 'A non-expansion character cannot join a game created by an Expansion character.',
- 0x79: 'A Expansion character cannot join a game created by a non-expansion character.',
- 0x7D: 'A non-ladder character cannot join a game created by a Ladder character.'
- }
- throw new Error(`Cannot connect error ${result} : ${errors[result]}`)
+ clientDiablo.emit('error', Error(`Cannot connect error ${result} : ${diabloData.responses.joinGame[result]}`))
}
clientDiablo.IP2 = IP2
@@ -136,7 +118,7 @@ function createClientDiablo ({ username, password, host, version, keyClassic, ke
const { status } = await once(clientDiablo, 'SID_STARTADVEX3')
if (status === 1) {
- throw new Error('join failed')
+ clientDiablo.emit('error', new Error('join failed'))
}
}
diff --git a/lib/client/createClientMcp.js b/lib/client/createClientMcp.js
index 7430830..4400c0a 100644
--- a/lib/client/createClientMcp.js
+++ b/lib/client/createClientMcp.js
@@ -1,17 +1,14 @@
const ClientMcp = require('./clientMcp')
-
const { once } = require('once-promise')
+const diabloData = require('diablo2-data')('pod_1.13d')
// Connect to account
async function createClientMcp (clientDiablo, hostMcp, portMcp, MCPCookie, MCPStatus, MCPChunk1, MCPChunk2, battleNetUniqueName, version) {
const clientMcp = new ClientMcp(version)
clientDiablo.setClientMcp(clientMcp)
clientMcp.on('connect', () => {
- // 'connect' listener
- console.log('connected to MCP server!')
-
+ clientDiablo.emit('log', 'connected to MCP server')
clientMcp.socket.write(Buffer.from('01', 'hex')) // This Initialise conversation
-
clientDiablo.write('MCP_STARTUP', {
MCPCookie: MCPCookie,
MCPStatus: MCPStatus,
@@ -22,22 +19,17 @@ async function createClientMcp (clientDiablo, hostMcp, portMcp, MCPCookie, MCPSt
})
clientDiablo.on('MCP_STARTUP', ({ result }) => {
- if (result === 0x02 || (result >= 0x0A && result <= 0x0D)) {
- console.log('Realm Unavailable: No Battle.net connection detected.')
- } else if (result === 0x7E) {
- console.log('CDKey banned from realm play.')
- } else if (result === 0x7F) {
- console.log('Temporary IP ban "Your connection has been temporarily restricted from this realm. Please try to log in at another time."')
- } else {
- console.log('Success!')
+ if (result === 0) {
clientDiablo.write('MCP_CHARLIST2', {
numberOfCharacterToList: 8
})
+ clientDiablo.emit('log', diabloData.responses.startup[result])
+ } else {
+ clientDiablo.emit('error', new Error(diabloData.responses.startup[result]))
}
})
const p = once(clientDiablo, 'MCP_CHARLIST2')
-
clientMcp.connect(hostMcp, portMcp)
const { characters } = await p
diff --git a/lib/client/createClientSid.js b/lib/client/createClientSid.js
index a3184e9..e9ed2e9 100644
--- a/lib/client/createClientSid.js
+++ b/lib/client/createClientSid.js
@@ -1,23 +1,18 @@
const ClientSid = require('./clientSid')
-
const getHash = require('../utils/getHash')
-
const { once } = require('once-promise')
-
-// const cdkey16 = require('../utils/cdkey')
-const cdkey = require('../utils/cdkey26')
+const cdKey = require('../utils/cdkey26')
const checkRevision = require('../utils/checkRevision')
+const diabloData = require('diablo2-data')('pod_1.13d')
+
-// Connect to battlenet
async function createClientSid (clientDiablo, host, version) {
- // const getMpq = require(`../utils/getMpq1.13`)
const portSid = 6112
const clientSid = new ClientSid(version)
clientDiablo.setClientSid(clientSid)
clientDiablo.clientToken = 2045114178
clientSid.on('connect', () => {
- // 'connect' listener
- console.log('connected to server!')
+ clientDiablo.emit('log', 'connected to SID server!')
clientSid.socket.write(Buffer.from('01', 'hex')) // Initialises a normal logon conversation
@@ -64,8 +59,8 @@ async function createClientSid (clientDiablo, host, version) {
}
}, () => {})) */
- const key1 = cdkey(clientDiablo.keyClassic, clientDiablo.clientToken, clientDiablo.serverToken)
- const key2 = cdkey(clientDiablo.keyExtension, clientDiablo.clientToken, clientDiablo.serverToken)
+ const key1 = cdKey(clientDiablo.keyClassic, clientDiablo.clientToken, clientDiablo.serverToken)
+ const key2 = cdKey(clientDiablo.keyExtension, clientDiablo.clientToken, clientDiablo.serverToken)
const { checksum, info } = checkRevision(valuestring)
clientDiablo.write('SID_AUTH_CHECK', {
@@ -94,14 +89,14 @@ async function createClientSid (clientDiablo, host, version) {
clientDiablo.on('SID_AUTH_CHECK', ({ result, additionalInformation }) => {
if (result === 0) {
- console.log('Correct keys')
+ clientDiablo.emit('log', 'Correct keys')
clientDiablo.write('SID_GETFILETIME', {
requestId: 2147483652,
unknown: 0,
filename: 'bnserver-D2DV.ini'
})
} else {
- console.log(`result is ${result}, wrong key`)
+ clientDiablo.emit('error', new Error(`result is ${result}, wrong key`))
}
})
@@ -115,9 +110,11 @@ async function createClientSid (clientDiablo, host, version) {
})
clientDiablo.on('SID_LOGONRESPONSE2', ({ status }) => {
- console.log(status === 0 ? 'Success' : status === 1 ? "Account doesn't exist" : status === 2 ? 'Invalid password' : 'Account closed')
+ clientDiablo.emit('log', diabloData.responses.logon[status])
if (status === 0) {
clientDiablo.write('SID_QUERYREALMS2', {})
+ } else {
+ this.emit('error', new Error(diabloData.responses.logon[status]))
}
})
diff --git a/package.json b/package.json
index f96ed88..ac98fec 100644
--- a/package.json
+++ b/package.json
@@ -28,9 +28,9 @@
"bit-buffer": "^0.2.4",
"bridge.net": "^2.0.0",
"csvtojson": "^2.0.8",
- "diablo2-data": "^1.3.0",
"once-promise": "^2.0.0",
- "protodef": "^1.6.9"
+ "protodef": "^1.6.9",
+ "diablo2-data": "file:../node-diablo2-data"
},
"standard": {
"ignore": [