diff --git a/.github/workflows/dev-test.yml b/.github/workflows/dev-test.yml index 46148e58e..78635bcc2 100644 --- a/.github/workflows/dev-test.yml +++ b/.github/workflows/dev-test.yml @@ -7,10 +7,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Use Node.js 20.12.2 + - name: Use Node.js 22.9.0 uses: actions/setup-node@v3 with: - node-version: 20.12.2 + node-version: 22.9.0 - run: npm i - run: npm run lint-quiet prettier: @@ -18,10 +18,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Use Node.js 20.12.2 + - name: Use Node.js 22.9.0 uses: actions/setup-node@v3 with: - node-version: 20.12.2 + node-version: 22.9.0 - run: npm i - run: npm run check_prettier interfaces-up-to-date: @@ -29,10 +29,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Use Node.js 20.12.2 + - name: Use Node.js 22.9.0 uses: actions/setup-node@v3 with: - node-version: 20.12.2 + node-version: 22.9.0 - run: npm i - run: npm run gen-packets-types - run: npm run gen-packets-interfaces @@ -42,7 +42,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [20.x, 21.x] + node-version: [ 22.x] steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} @@ -56,7 +56,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [20.12.2, 20.x, 21.x] + node-version: [22.9.0, 22.x] steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} @@ -72,7 +72,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [20.12.2, 20.x, 21.x] + node-version: [22.9.0, 22.x] steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} @@ -86,7 +86,7 @@ jobs: runs-on: windows-latest strategy: matrix: - node-version: [20.12.2, 20.x, 21.x] + node-version: [22.9.0, 22.x] steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} diff --git a/.github/workflows/doc-gen.yml b/.github/workflows/doc-gen.yml index 9209a2937..5e48bdc5b 100644 --- a/.github/workflows/doc-gen.yml +++ b/.github/workflows/doc-gen.yml @@ -29,10 +29,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Use Node.js 20.12.2 + - name: Use Node.js 22.9.0 uses: actions/setup-node@v3 with: - node-version: 20.12.2 + node-version: 22.9.0 - run: npm i - run: npm run gen-doc - name: Setup Pages diff --git a/.github/workflows/release-test.yml b/.github/workflows/release-test.yml index b1c9593ed..80c420299 100644 --- a/.github/workflows/release-test.yml +++ b/.github/workflows/release-test.yml @@ -11,10 +11,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Use Node.js 20.12.2 + - name: Use Node.js 22.9.0 uses: actions/setup-node@v3 with: - node-version: 20.12.2 + node-version: 22.9.0 - uses: docker-practice/actions-setup-docker@master - run: npm i - run: npm run build-docker-images diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fc8b4afcc..a1a74207f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 registry-url: https://registry.npmjs.org/ - run: npm i - run: npm publish @@ -42,10 +42,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Use Node.js 20.12.2 + - name: Use Node.js 22.9.0 uses: actions/setup-node@v3 with: - node-version: 20.12.2 + node-version: 22.9.0 - uses: olegtarasov/get-tag@v2.1 id: tagName - uses: docker-practice/actions-setup-docker@master diff --git a/data/2016/navData/z1.bin b/data/2016/navData/z1.bin new file mode 100644 index 000000000..e213f0d2e Binary files /dev/null and b/data/2016/navData/z1.bin differ diff --git a/data/2016/navData/z1_fixed.bin b/data/2016/navData/z1_fixed.bin new file mode 100644 index 000000000..70f021ae8 Binary files /dev/null and b/data/2016/navData/z1_fixed.bin differ diff --git a/data/2016/navData/z1_fixed_wow.bin b/data/2016/navData/z1_fixed_wow.bin new file mode 100644 index 000000000..298f681e6 Binary files /dev/null and b/data/2016/navData/z1_fixed_wow.bin differ diff --git a/data/2016/navData/z1_new.bin b/data/2016/navData/z1_new.bin new file mode 100644 index 000000000..09e1d0c64 Binary files /dev/null and b/data/2016/navData/z1_new.bin differ diff --git a/package-lock.json b/package-lock.json index 7ad8a8da4..be2adbdcf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,10 +14,12 @@ "@types/node": "22.1.0", "@types/ws": "8.5.12", "debug": "4.3.6", + "h1emu-ai": "^0.0.1", "h1emu-core": "1.2.8", "h1z1-dataschema": "1.7.2", "js-yaml": "4.1.0", "mongodb": "6.8.0", + "recast-navigation": "^0.34.0", "threads": "1.7.0", "typescript": "5.5.4", "ws": "8.18.0" @@ -298,6 +300,45 @@ "node": ">= 8" } }, + "node_modules/@recast-navigation/core": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@recast-navigation/core/-/core-0.34.0.tgz", + "integrity": "sha512-aJRDmwCSgBgGnnL4pWi2ya/U0NwDJWJrPQnX0a5WG3fVRnL+fkgUfVCVaNAqb0ouLFQVSARqV0QalYtv2kyClw==", + "license": "MIT", + "dependencies": { + "@recast-navigation/wasm": "0.34.0" + } + }, + "node_modules/@recast-navigation/generators": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@recast-navigation/generators/-/generators-0.34.0.tgz", + "integrity": "sha512-DSXZEIw5mlTjHXOKKymo3UMgoEtHz4DgAircw0dF0m0b8RRoA7KnIZ/OXBtT2JwuJqllclCiD9iI6gRmHHjgew==", + "license": "MIT", + "dependencies": { + "@recast-navigation/core": "0.34.0", + "@recast-navigation/wasm": "0.34.0" + } + }, + "node_modules/@recast-navigation/three": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@recast-navigation/three/-/three-0.34.0.tgz", + "integrity": "sha512-JQRiMSUk2Ex9gsOVBfPz49nlaTzlv2HU7QQm1N3/YpPkN/t8SRzj4bDDozhL5rCqtWnbgFZtmM2rLr/c3E3AKA==", + "license": "MIT", + "dependencies": { + "@recast-navigation/core": "0.34.0", + "@recast-navigation/generators": "0.34.0" + }, + "peerDependencies": { + "@types/three": "0.x.x", + "three": "0.x.x" + } + }, + "node_modules/@recast-navigation/wasm": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@recast-navigation/wasm/-/wasm-0.34.0.tgz", + "integrity": "sha512-O14c01YYV/zp4nDQ6L71W3ljfasH8L5xgxpSdqSEs3XkhKyePZz5bng9K7DALHSOZVVXnqdWYazRT1GQ1fQrfw==", + "license": "MIT" + }, "node_modules/@shikijs/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.10.0.tgz", @@ -329,6 +370,13 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, + "node_modules/@tweenjs/tween.js": { + "version": "23.1.3", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", + "license": "MIT", + "peer": true + }, "node_modules/@types/js-yaml": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", @@ -343,11 +391,40 @@ "undici-types": "~6.13.0" } }, + "node_modules/@types/stats.js": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.3.tgz", + "integrity": "sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/three": { + "version": "0.168.0", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.168.0.tgz", + "integrity": "sha512-qAGLGzbaYgkkonOBfwOr+TZpOskPfFjrDAj801WQSVkUz0/D9zwir4vhruJ/CC/GteywzR9pqeVVfs5th/2oKw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@tweenjs/tween.js": "~23.1.3", + "@types/stats.js": "*", + "@types/webxr": "*", + "@webgpu/types": "*", + "fflate": "~0.8.2", + "meshoptimizer": "~0.18.1" + } + }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" }, + "node_modules/@types/webxr": { + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.20.tgz", + "integrity": "sha512-JGpU6qiIJQKUuVSKx1GtQnHJGxRjtfGIhzO2ilq43VZZS//f1h1Sgexbdk+Lq+7569a6EYhOWrUpIruR/1Enmg==", + "license": "MIT", + "peer": true + }, "node_modules/@types/whatwg-url": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.3.tgz", @@ -555,6 +632,13 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@webgpu/types": { + "version": "0.1.45", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.45.tgz", + "integrity": "sha512-0TBBF/mhakJoK0qUWCZugBnh23x+VwmYA5RLmtNQwvZt1pQ4P2fzIvQUiSe6jxzkBi4GF8R4BejJjro0ZSoSXQ==", + "license": "BSD-3-Clause", + "peer": true + }, "node_modules/acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", @@ -1100,6 +1184,13 @@ "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT", + "peer": true + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -1218,6 +1309,11 @@ "dev": true, "license": "MIT" }, + "node_modules/h1emu-ai": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/h1emu-ai/-/h1emu-ai-0.0.1.tgz", + "integrity": "sha512-bHkxqH5QhPyvsX2Cm9lBAPfYG5e2S/9pszDpmEJqL/J6fPY6FwkSREmAqNHppfKOzmwCo4aGge6Y1v1t7wLEBQ==" + }, "node_modules/h1emu-core": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/h1emu-core/-/h1emu-core-1.2.8.tgz", @@ -1508,6 +1604,13 @@ "node": ">= 8" } }, + "node_modules/meshoptimizer": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", + "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", + "license": "MIT", + "peer": true + }, "node_modules/micromatch": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", @@ -1781,6 +1884,17 @@ ], "license": "MIT" }, + "node_modules/recast-navigation": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/recast-navigation/-/recast-navigation-0.34.0.tgz", + "integrity": "sha512-b0bBOk6SaT+PmtkPNV9UxMHwt81DzhdL1X5dwWv7KSt93xajCPsaWikVuiX7DlIGi7GZ0jXaTFR2G6y+xbR8TQ==", + "license": "MIT", + "dependencies": { + "@recast-navigation/core": "0.34.0", + "@recast-navigation/generators": "0.34.0", + "@recast-navigation/three": "0.34.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -1988,6 +2102,13 @@ "tiny-worker": ">= 2" } }, + "node_modules/three": { + "version": "0.168.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.168.0.tgz", + "integrity": "sha512-6m6jXtDwMJEK/GGMbAOTSAmxNdzKvvBzgd7q8bE/7Tr6m7PaBh5kKLrN7faWtlglXbzj7sVba48Idwx+NRsZXw==", + "license": "MIT", + "peer": true + }, "node_modules/tiny-worker": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tiny-worker/-/tiny-worker-2.3.0.tgz", diff --git a/package.json b/package.json index ac826fa42..295803c33 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,12 @@ "@types/node": "22.1.0", "@types/ws": "8.5.12", "debug": "4.3.6", + "h1emu-ai": "^0.0.1", "h1emu-core": "1.2.8", "h1z1-dataschema": "1.7.2", "js-yaml": "4.1.0", "mongodb": "6.8.0", + "recast-navigation": "^0.34.0", "threads": "1.7.0", "typescript": "5.5.4", "ws": "8.18.0" @@ -46,7 +48,8 @@ "fix-signatures": "npx ts-node ./scripts/fixSignatures.ts && npm run prettier_sources", "gen-doc": "npm run updt-doc-config && npx typedoc", "updt-doc-config": "npx ts-node ./scripts/updateTypeDoc.ts", - "start": "node ./scripts/h1z1-server-demo-2016.js", + "start": "node --no-warnings --experimental-require-module ./scripts/h1z1-server-demo-2016.js", + "start-dev": "npm run build && npm start", "start-echo": "npm run build && npm run build-benchs && node --inspect ./benchmarks/out/echo/echo-server-start.js", "lint": "npx eslint src", "lint-quiet": "npx eslint --quiet src", @@ -59,15 +62,14 @@ "build-tests": "npx tsc -p tsconfigs/tsconfig-tests.json", "build-benchs": "npx tsc -p ./benchmarks/tsconfig.json", "build-docker-images": "npx ts-node ./docker/buildDocker.ts", - "test-mongo": "npm run build && npm run build-tests && MONGO_TESTS='true' node --test", - "test": "npm run build && npm run build-tests && node --test", + "test-mongo": "npm run build && npm run build-tests && MONGO_TESTS='true' node --no-warnings --experimental-require-module --test", + "test": "npm run build && npm run build-tests && node --no-warnings --experimental-require-module --test", "postinstall": "npm run build", "publish_release": "git push --tags && git push origin dev && git push origin master && git checkout dev && npm run publish_release-next", "publish_release-next": "npm version --no-git-tag-version prerelease && git add * && git commit -m 'publish-a-next-version' && npm publish --tag next && git push origin dev", "check_prettier": "prettier --check ./src && prettier --check ./tests && prettier --check ./benchmarks", "prettier_sources": "prettier --write ./src && prettier --write ./tests && prettier --write ./benchmarks", - "tests": "npm run gen-packets-types && npm run gen-packets-interfaces && npm run prettier_sources", - "start-dev": "npm run build && npm start" + "tests": "npm run gen-packets-types && npm run gen-packets-interfaces && npm run prettier_sources" }, "repository": { "type": "git", diff --git a/src/servers/ZoneServer2016/entities/baselightweightcharacter.ts b/src/servers/ZoneServer2016/entities/baselightweightcharacter.ts index b1416365d..e2c8ea975 100644 --- a/src/servers/ZoneServer2016/entities/baselightweightcharacter.ts +++ b/src/servers/ZoneServer2016/entities/baselightweightcharacter.ts @@ -15,6 +15,7 @@ import { AddLightweightNpc, AddSimpleNpc } from "types/zone2016packets"; import { ZoneServer2016 } from "../zoneserver"; import { BaseEntity } from "./baseentity"; import { ModelIds, PositionUpdateType } from "../models/enums"; +import { CrowdAgent } from "recast-navigation"; function getHeadActor(modelId: number): string { switch (modelId) { @@ -99,6 +100,8 @@ export abstract class BaseLightweightCharacter extends BaseEntity { /** Determines if the lightweight should be used as a SimpleNpc (non-moving) */ useSimpleStruct: boolean = false; + navAgent?: CrowdAgent; + constructor( characterId: string, transientId: number, diff --git a/src/servers/ZoneServer2016/entities/npc.ts b/src/servers/ZoneServer2016/entities/npc.ts index 3c3fd0c3f..37cdd651c 100644 --- a/src/servers/ZoneServer2016/entities/npc.ts +++ b/src/servers/ZoneServer2016/entities/npc.ts @@ -15,9 +15,19 @@ import { DamageInfo } from "types/zoneserver"; import { ZoneServer2016 } from "../zoneserver"; import { BaseFullCharacter } from "./basefullcharacter"; import { ZoneClient2016 } from "../classes/zoneclient"; -import { logClientActionToMongo } from "../../../utils/utils"; +import { + getCurrentServerTimeWrapper, + getDistance, + logClientActionToMongo +} from "../../../utils/utils"; import { DB_COLLECTIONS } from "../../../utils/enums"; -import { Items, ModelIds, NpcIds, StringIds } from "../models/enums"; +import { + Items, + ModelIds, + NpcIds, + PositionUpdateType, + StringIds +} from "../models/enums"; import { CommandInteractionString } from "types/zone2016packets"; export class Npc extends BaseFullCharacter { @@ -56,6 +66,7 @@ export class Npc extends BaseFullCharacter { public get isAlive(): boolean { return this.deathTime == 0; } + server: ZoneServer2016; constructor( characterId: string, transientId: number, @@ -66,9 +77,11 @@ export class Npc extends BaseFullCharacter { spawnerId: number = 0 ) { super(characterId, transientId, actorModelId, position, rotation, server); + this.positionUpdateType = PositionUpdateType.MOVABLE; this.spawnerId = spawnerId; this.health = 10000; this.initNpcData(); + this.server = server; } async damage(server: ZoneServer2016, damageInfo: DamageInfo) { @@ -369,4 +382,28 @@ export class Npc extends BaseFullCharacter { } ); } + goTo(position: Float32Array) { + const angleInRadians2 = Math.random() * (2 * Math.PI) - Math.PI; + const angleInRadians = Math.atan2( + position[1] - this.state.position[1], + getDistance(this.state.position, position) + ); + this.state.position = position; + this.server.sendDataToAll("PlayerUpdatePosition", { + transientId: this.transientId, + positionUpdate: { + sequenceTime: getCurrentServerTimeWrapper().getTruncatedU32(), + position: this.state.position, + unknown3_int8: 0, + stance: 66565, + engineRPM: 0, + orientation: angleInRadians2, + frontTilt: 0, + sideTilt: 0, + angleChange: 0, + verticalSpeed: angleInRadians, + horizontalSpeed: 0.5 + } + }); + } } diff --git a/src/servers/ZoneServer2016/handlers/commands/dev.ts b/src/servers/ZoneServer2016/handlers/commands/dev.ts index be789451d..b32c06e66 100644 --- a/src/servers/ZoneServer2016/handlers/commands/dev.ts +++ b/src/servers/ZoneServer2016/handlers/commands/dev.ts @@ -36,6 +36,9 @@ import { Zombie } from "../../entities/zombie"; import { WorldObjectManager } from "../../managers/worldobjectmanager"; import { Vehicle2016 } from "../../entities/vehicle"; import { Plane } from "../../entities/plane"; +import { EntityFromJs, EntityType } from "h1emu-ai"; +import { NavManager } from "../../../../utils/recast"; +import { scheduler } from "timers/promises"; const abilities = require("../../../../../data/2016/dataSources/Abilities.json"), vehicleAbilities = require("../../../../../data/2016/dataSources/VehicleAbilities.json"); @@ -131,7 +134,7 @@ const dev: any = { unknownData2: {} }); }, - zombie: function ( + zombie: async function ( server: ZoneServer2016, client: Client, args: Array @@ -147,7 +150,43 @@ const dev: any = { client.character.state.rotation, server ); + server._npcs[characterId] = zombie; + const e = new EntityFromJs(EntityType.Zombie, zombie); + server.aiManager.add_entity(e); + const a = server.navManager.createAgent(zombie.state.position); + zombie.navAgent = a; + + const targetVelocity = { x: 2, y: 2, z: 2 }; + zombie.navAgent.requestMoveVelocity(targetVelocity); + console.log("waiiiting"); + await scheduler.wait(5000); + // if (zombie.navAgent) { + // console.log("ori"); + // console.log(zombie.state.position); + // console.log("closest"); + // console.log(server.navManager.getClosestNavPoint(zombie.state.position)); + // zombie.navAgent.requestMoveTarget( + // server.navManager.getClosestNavPoint(zombie.state.position) + // ); + // server.navManager.updt(); + // console.log(zombie.navAgent.interpolatedPosition); + // zombie.goTo( + // NavManager.Vec3ToFloat32(zombie.navAgent.interpolatedPosition) + // ); + // } + setInterval(() => { + server.navManager.updt(); + if (zombie.navAgent) { + zombie.navAgent.requestMoveTarget( + server.navManager.getClosestNavPoint(client.character.state.position) + ); + console.log(zombie.navAgent.interpolatedPosition); + zombie.goTo( + NavManager.Vec3ToFloat32(zombie.navAgent.interpolatedPosition) + ); + } + }, 200); }, abilities: function ( server: ZoneServer2016, diff --git a/src/servers/ZoneServer2016/zoneserver.ts b/src/servers/ZoneServer2016/zoneserver.ts index 24a7ef1c5..a52e57e68 100644 --- a/src/servers/ZoneServer2016/zoneserver.ts +++ b/src/servers/ZoneServer2016/zoneserver.ts @@ -248,6 +248,10 @@ import { AccountInventoryManager } from "./managers/accountinventorymanager"; import { PlayTimeManager } from "./managers/playtimemanager"; import { RewardManager } from "./managers/rewardmanager"; import { DynamicAppearance } from "types/zonedata"; +import { AiManager, EntityFromJs, EntityType } from "h1emu-ai"; +import { setInterval } from "node:timers"; +import { readFileSync } from "node:fs"; +import { NavManager } from "../../utils/recast"; const spawnLocations2 = require("../../../data/2016/zoneData/Z1_gridSpawns.json"), deprecatedDoors = require("../../../data/2016/sampleData/deprecatedDoors.json"), @@ -395,6 +399,7 @@ export class ZoneServer2016 extends EventEmitter { pluginManager: PluginManager; configManager: ConfigManager; playTimeManager: PlayTimeManager; + aiManager: AiManager; _ready: boolean = false; @@ -460,6 +465,7 @@ export class ZoneServer2016 extends EventEmitter { crowbarHitRewardChance!: number; crowbarHitDamage!: number; /* */ + navManager: NavManager; constructor( serverPort: number, @@ -491,6 +497,8 @@ export class ZoneServer2016 extends EventEmitter { this.pluginManager = new PluginManager(); this.commandHandler = new CommandHandler(); this.playTimeManager = new PlayTimeManager(); + this.aiManager = new AiManager(); + this.navManager = new NavManager(); /* CONFIG MANAGER MUST BE INSTANTIATED LAST ! */ this.configManager = new ConfigManager(this, process.env.CONFIG_PATH); this.enableWorldSaves = @@ -1595,6 +1603,11 @@ export class ZoneServer2016 extends EventEmitter { } private async setupServer() { + const z1_nav = new Uint8Array( + readFileSync(__dirname + "/../../../data/2016/navData/z1_fixed_wow.bin") + ); + await this.navManager.loadNav(z1_nav); + this.weatherManager.init(); this.playTimeManager.init(this); this.initModelsDataSource(); @@ -1818,7 +1831,6 @@ export class ZoneServer2016 extends EventEmitter { if (this.isPvE) { console.log("Server in PvE mode"); } - this.fairPlayManager.decryptFairPlayValues(); this._spawnGrid = this.divideMapIntoSpawnGrid(7448, 7448, 744); this.speedtreeManager.initiateList(); @@ -1847,6 +1859,9 @@ export class ZoneServer2016 extends EventEmitter { this.rconManager.on("message", this.handleRconMessage.bind(this)); this.rewardManager.start(); this.hookManager.checkHook("OnServerReady"); + setInterval(() => { + this.aiManager.run(); + }, 300); } async sendInitData(client: Client) { @@ -4168,6 +4183,8 @@ export class ZoneServer2016 extends EventEmitter { generatedTransient, this ); + const e = new EntityFromJs(EntityType.Player, client.character); + this.aiManager.add_entity(e); return client; } diff --git a/src/utils/recast.ts b/src/utils/recast.ts new file mode 100644 index 000000000..806b49329 --- /dev/null +++ b/src/utils/recast.ts @@ -0,0 +1,92 @@ +// ====================================================================== +// +// GNU GENERAL PUBLIC LICENSE +// Version 3, 29 June 2007 +// copyright (C) 2020 - 2021 Quentin Gruber +// copyright (C) 2021 - 2024 H1emu community +// +// https://github.com/QuentinGruber/h1z1-server +// https://www.npmjs.com/package/h1z1-server +// +// Based on https://github.com/psemu/soe-network +// ====================================================================== + +import { + CrowdAgent, + init as initRecast, + NavMesh, + Vector3 +} from "recast-navigation"; +import { NavMeshQuery } from "recast-navigation"; +import { importNavMesh } from "recast-navigation"; +import { Crowd } from "recast-navigation"; + +export class NavManager { + navmesh!: NavMesh; + crowd!: Crowd; + navMeshQuery!: NavMeshQuery; + lastTimeCall: number = Date.now(); + constructor() {} + async loadNav(navData: Uint8Array) { + await initRecast(); + const { navMesh } = importNavMesh(navData); + this.navmesh = navMesh; + const maxAgents = 100; + const maxAgentRadius = 0.6; + + this.navMeshQuery = new NavMeshQuery(this.navmesh); + this.crowd = new Crowd(navMesh, { maxAgents, maxAgentRadius }); + } + static Float32ToVec3(f: Float32Array): Vector3 { + return { x: f[0], y: f[1], z: f[2] }; + } + static Vec3ToFloat32(v: Vector3): Float32Array { + return new Float32Array([v.x, v.y, v.z]); + } + updt() { + // console.time("crowd updt"); + this.crowd.update(this.lastTimeCall); + this.lastTimeCall = Date.now(); + // console.timeEnd("crowd updt"); + } + getClosestNavPoint(pos: Float32Array): Vector3 { + const n = this.navMeshQuery.findNearestPoly(NavManager.Float32ToVec3(pos)); + return n.nearestPoint; + } + createAgent(pos: Float32Array): CrowdAgent { + const position = this.getClosestNavPoint(pos); + const radius = 2; + + const { randomPoint: initialAgentPosition } = + this.navMeshQuery.findRandomPointAroundCircle(position, radius); + + const agent = this.crowd.addAgent(initialAgentPosition, { + radius: 5, + height: 5, + maxAcceleration: 4.0, + maxSpeed: 1.0, + collisionQueryRange: 0.5, + pathOptimizationRange: 0.0, + separationWeight: 1.0 + }); + return agent; + } + testNavMesh(a: Float32Array, b: Float32Array): Float32Array[] { + console.time("calculating path"); + + const start = NavManager.Float32ToVec3(a); + const end = NavManager.Float32ToVec3(b); + const { success, error, path } = this.navMeshQuery.computePath(start, end); + console.log(success); + console.log(error); + console.log(path); + console.timeEnd("calculating path"); + const pathNodes: Float32Array[] = []; + if (path) { + path.forEach((v) => { + pathNodes.push(new Float32Array([v.x, v.y, v.z])); + }); + } + return pathNodes; + } +} diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 0863ba986..a036020db 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -13,7 +13,7 @@ import { generate_random_guid } from "h1emu-core"; import { compress, compressBound } from "./lz4/lz4"; import fs, { readdirSync } from "node:fs"; -import { normalize, resolve } from "node:path"; +import path, { normalize, resolve } from "node:path"; import { Collection, MongoClient } from "mongodb"; import { DB_NAME, MAX_TRANSIENT_ID, MAX_UINT16, MAX_UINT32 } from "./constants"; import { ZoneServer2016 } from "servers/ZoneServer2016/zoneserver"; @@ -1528,3 +1528,25 @@ export function getDateString(timestamp: number) { export function loadJson(path: string) { return JSON.parse(fs.readFileSync(path, "utf8")); } + +export function loadNavData() { + const folderPath = `${__dirname}/../../data/2016/navData/`; + const files = fs.readdirSync(folderPath); + + const dataInOrder: Uint8Array[] = []; + + files.forEach((file) => { + const filePath = path.join(folderPath, file); + + const stats = fs.statSync(filePath); + + if (stats.isFile()) { + const data = fs.readFileSync(filePath); + const fileIndex: number = + Number(filePath.split(".")[0].split("_part")[1]) - 1; + + dataInOrder[fileIndex] = new Uint8Array(data); + } + }); + return new Uint8Array(Buffer.concat(dataInOrder)); +}