bun-memory 1.1.24 → 1.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/example/benchmark.ts +107 -0
- package/example/trigger-bot.ts +28 -54
- package/package.json +5 -1
- package/runtime/extensions.ts +87 -865
- package/structs/Memory.ts +301 -92
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// ! Warning:
|
|
2
|
+
// ! This benchmark may take 1-2+ minutes. It repeatedly traverses the entire entity list in
|
|
3
|
+
// ! Counter-Strike 2, storing it into `EntityClassInfoNames`…
|
|
4
|
+
|
|
5
|
+
import Memory from 'bun-memory';
|
|
6
|
+
|
|
7
|
+
// Get the latest client_dll.json and offsets.json from:
|
|
8
|
+
// https://github.com/a2x/cs2-dumper/tree/main/output
|
|
9
|
+
|
|
10
|
+
import ClientDLLJSON from './offsets/client_dll.json';
|
|
11
|
+
import OffsetsJSON from './offsets/offsets.json';
|
|
12
|
+
|
|
13
|
+
// Load the needed offsets as bigints… It's ugly but… IntelliSense! 🫠…
|
|
14
|
+
const Client = {
|
|
15
|
+
...Object.fromEntries(Object.entries(ClientDLLJSON['client.dll'].classes).map(([class_, { fields }]) => [class_, Object.fromEntries(Object.entries(fields).map(([field, value]) => [field, BigInt(value)]))])),
|
|
16
|
+
Other: Object.fromEntries(Object.entries(OffsetsJSON['client.dll']).map(([key, value]) => [key, BigInt(value)])),
|
|
17
|
+
} as {
|
|
18
|
+
[Class in keyof (typeof ClientDLLJSON)['client.dll']['classes']]: { [Field in keyof (typeof ClientDLLJSON)['client.dll']['classes'][Class]['fields']]: bigint };
|
|
19
|
+
} & { Other: { [K in keyof (typeof OffsetsJSON)['client.dll']]: bigint } };
|
|
20
|
+
|
|
21
|
+
// Open a handle to cs2.exe…
|
|
22
|
+
const cs2 = new Memory('cs2.exe');
|
|
23
|
+
|
|
24
|
+
// Get the base for client.dll…
|
|
25
|
+
const ClientPtr = cs2.modules['client.dll']?.base;
|
|
26
|
+
|
|
27
|
+
// Make sure client.dll is loaded…
|
|
28
|
+
if (ClientPtr === undefined) {
|
|
29
|
+
throw new TypeError('ClientPtr must not be undefined.');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Warmup…
|
|
33
|
+
console.log('Warming up…');
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < 1e6; i++) {
|
|
36
|
+
const GlobalVarsPtr = cs2.u64(ClientPtr + Client.Other.dwGlobalVars);
|
|
37
|
+
/* */ const CurTime = cs2.f32(GlobalVarsPtr + 0x30n);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Create caches and scratches to optimize performance…
|
|
41
|
+
const Cache_Names = new Map<bigint, string>();
|
|
42
|
+
|
|
43
|
+
const EntityChunkScratch = new BigUint64Array(0xf000 / 0x08);
|
|
44
|
+
const EntityListScratch = new BigUint64Array(0x200 / 0x08);
|
|
45
|
+
|
|
46
|
+
// Start the test…
|
|
47
|
+
console.log('Starting the test…');
|
|
48
|
+
|
|
49
|
+
const performance1 = performance.now();
|
|
50
|
+
|
|
51
|
+
const EntityListPtr = cs2.u64(ClientPtr + Client.Other.dwEntityList);
|
|
52
|
+
|
|
53
|
+
for (let i = 0; i < 1e6; i++) {
|
|
54
|
+
const EntityClassInfoNames = new Map<string, bigint[]>();
|
|
55
|
+
const EntityClassInfoPtrs = new Map<bigint, bigint[]>();
|
|
56
|
+
|
|
57
|
+
// Traverse the entity list…
|
|
58
|
+
cs2.read(EntityListPtr + 0x10n, EntityListScratch);
|
|
59
|
+
|
|
60
|
+
for (let i = 0; i < 0x40; i++) {
|
|
61
|
+
const EntityChunkPtr = EntityListScratch[i];
|
|
62
|
+
|
|
63
|
+
if (EntityChunkPtr === 0n) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
cs2.read(EntityChunkPtr, EntityChunkScratch);
|
|
68
|
+
|
|
69
|
+
for (let j = 0, l = 0; j < 0x200; j++, l += 0x0f) {
|
|
70
|
+
const BaseEntityPtr = EntityChunkScratch[l];
|
|
71
|
+
|
|
72
|
+
if (BaseEntityPtr === 0n) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const EntityClassInfoPtr = EntityChunkScratch[l + 0x01];
|
|
77
|
+
|
|
78
|
+
let BaseEntityPtrs = EntityClassInfoPtrs.get(EntityClassInfoPtr);
|
|
79
|
+
|
|
80
|
+
if (BaseEntityPtrs === undefined) {
|
|
81
|
+
BaseEntityPtrs = [];
|
|
82
|
+
|
|
83
|
+
EntityClassInfoPtrs.set(EntityClassInfoPtr, BaseEntityPtrs);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
BaseEntityPtrs.push(BaseEntityPtr);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (const [EntityClassInfoPtr, BaseEntityPtrs] of EntityClassInfoPtrs) {
|
|
90
|
+
let Name = Cache_Names.get(EntityClassInfoPtr);
|
|
91
|
+
|
|
92
|
+
if (Name === undefined) {
|
|
93
|
+
const SchemaClassInfoDataPtr = cs2.u64(EntityClassInfoPtr + 0x30n);
|
|
94
|
+
/* */ const NamePtr = cs2.u64(SchemaClassInfoDataPtr + 0x08n);
|
|
95
|
+
/* */ Name = cs2.cString(NamePtr, 0x2a).toString();
|
|
96
|
+
|
|
97
|
+
Cache_Names.set(EntityClassInfoPtr, Name);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
EntityClassInfoNames.set(Name, BaseEntityPtrs);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const performance2 = performance.now();
|
|
106
|
+
|
|
107
|
+
console.log('Test completed in %fms…', (performance2 - performance1).toFixed(2));
|
package/example/trigger-bot.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FFIType, dlopen } from 'bun:ffi';
|
|
2
2
|
import { sleep } from 'bun';
|
|
3
3
|
|
|
4
|
-
import Memory from '
|
|
4
|
+
import Memory from 'bun-memory';
|
|
5
5
|
|
|
6
6
|
// Get the latest client_dll.json and offsets.json from:
|
|
7
7
|
// https://github.com/a2x/cs2-dumper/tree/main/output
|
|
@@ -21,21 +21,12 @@ const {
|
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
// Load the needed offsets as bigints…
|
|
24
|
-
const
|
|
25
|
-
Object.entries(ClientDLLJSON['client.dll'].classes).map(([class_, { fields }]) => [class_, Object.fromEntries(Object.entries(fields).map(([field, value]) => [field, BigInt(value)]))])
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// Load the needed offsets as bigints…
|
|
31
|
-
const {
|
|
32
|
-
'client.dll': { dwEntityList, dwGlobalVars, dwLocalPlayerController, dwLocalPlayerPawn },
|
|
33
|
-
'engine2.dll': {},
|
|
34
|
-
} = Object.fromEntries(
|
|
35
|
-
Object.entries(OffsetsJSON).map(([name, section]) => [name, Object.fromEntries(Object.entries(section).map(([key, value]) => [key, BigInt(value as number)]))]) //
|
|
36
|
-
) as {
|
|
37
|
-
[M in keyof typeof OffsetsJSON]: { [K in keyof (typeof OffsetsJSON)[M]]: bigint };
|
|
38
|
-
};
|
|
24
|
+
const Client = {
|
|
25
|
+
...Object.fromEntries(Object.entries(ClientDLLJSON['client.dll'].classes).map(([class_, { fields }]) => [class_, Object.fromEntries(Object.entries(fields).map(([field, value]) => [field, BigInt(value)]))])),
|
|
26
|
+
Other: Object.fromEntries(Object.entries(OffsetsJSON['client.dll']).map(([key, value]) => [key, BigInt(value)])),
|
|
27
|
+
} as {
|
|
28
|
+
[Class in keyof (typeof ClientDLLJSON)['client.dll']['classes']]: { [Field in keyof (typeof ClientDLLJSON)['client.dll']['classes'][Class]['fields']]: bigint };
|
|
29
|
+
} & { Other: { [K in keyof (typeof OffsetsJSON)['client.dll']]: bigint } };
|
|
39
30
|
|
|
40
31
|
// Open a handle to cs2.exe…
|
|
41
32
|
const cs2 = new Memory('cs2.exe');
|
|
@@ -46,23 +37,6 @@ const ClientPtr = cs2.modules['client.dll']?.base;
|
|
|
46
37
|
if (ClientPtr === undefined) {
|
|
47
38
|
throw new TypeError('ClientPtr must not be undefined.');
|
|
48
39
|
}
|
|
49
|
-
// !
|
|
50
|
-
|
|
51
|
-
console.time('test');
|
|
52
|
-
|
|
53
|
-
// while (true) {
|
|
54
|
-
// for (const pointer of pointers) {
|
|
55
|
-
// const cString = cs2.cString(pointer, 32).toString();
|
|
56
|
-
|
|
57
|
-
// if (cString.length > 32) {
|
|
58
|
-
// console.log(cString);
|
|
59
|
-
// }
|
|
60
|
-
// }
|
|
61
|
-
// }
|
|
62
|
-
|
|
63
|
-
console.timeEnd('test');
|
|
64
|
-
|
|
65
|
-
// !
|
|
66
40
|
|
|
67
41
|
// Create a cache for class name strings… 🫠…
|
|
68
42
|
const Cache_Names = new Map<bigint, string>();
|
|
@@ -74,26 +48,26 @@ async function tick(ClientPtr: bigint) {
|
|
|
74
48
|
ticks++;
|
|
75
49
|
|
|
76
50
|
// Read relevant info from memory…
|
|
77
|
-
const GlobalVarsPtr = cs2.u64(ClientPtr + dwGlobalVars);
|
|
51
|
+
const GlobalVarsPtr = cs2.u64(ClientPtr + Client.Other.dwGlobalVars);
|
|
78
52
|
/* */ const CurTime = cs2.f32(GlobalVarsPtr + 0x30n);
|
|
79
53
|
|
|
80
|
-
const Local_PlayerControllerPtr = cs2.u64(ClientPtr + dwLocalPlayerController);
|
|
81
|
-
/* */ const Local_TickBase = cs2.u32(Local_PlayerControllerPtr + CBasePlayerController.m_nTickBase);
|
|
82
|
-
|
|
83
|
-
const Local_PlayerPawnPtr = cs2.u64(ClientPtr + dwLocalPlayerPawn);
|
|
84
|
-
/* */ const Local_FlashOverlayAlpha = cs2.f32(Local_PlayerPawnPtr + C_CSPlayerPawnBase.m_flFlashOverlayAlpha);
|
|
85
|
-
/* */ const Local_IDEntIndex = cs2.i32(Local_PlayerPawnPtr + C_CSPlayerPawn.m_iIDEntIndex);
|
|
86
|
-
/* */ const Local_IsScoped = cs2.i32(Local_PlayerPawnPtr + C_CSPlayerPawn.m_bIsScoped);
|
|
87
|
-
/* */ const Local_Player_WeaponServicesPtr = cs2.u64(Local_PlayerPawnPtr + C_BasePlayerPawn.m_pWeaponServices);
|
|
88
|
-
/* */ const Local_NextAttack = cs2.f32(Local_Player_WeaponServicesPtr + CCSPlayer_WeaponServices.m_flNextAttack);
|
|
89
|
-
/* */ const Local_TeamNum = cs2.u8(Local_PlayerPawnPtr + C_BaseEntity.m_iTeamNum);
|
|
90
|
-
/* */ const Local_WeaponBasePtr = cs2.u64(Local_PlayerPawnPtr + C_CSPlayerPawn.m_pClippingWeapon);
|
|
91
|
-
/* */ const Local_Clip1 = cs2.i32(Local_WeaponBasePtr + C_BasePlayerWeapon.m_iClip1);
|
|
92
|
-
/* */ const Local_NextPrimaryAttackTick = cs2.u32(Local_WeaponBasePtr + C_BasePlayerWeapon.m_nNextPrimaryAttackTick);
|
|
93
|
-
/* */ const Local_WeaponBaseVDataPtr = cs2.u64(Local_WeaponBasePtr + C_BaseEntity.m_nSubclassID + 0x08n);
|
|
94
|
-
/* */ const Local_IsFullAuto = cs2.bool(Local_WeaponBaseVDataPtr + CCSWeaponBaseVData.m_bIsFullAuto);
|
|
95
|
-
/* */ const Local_WeaponType = cs2.i32(Local_WeaponBaseVDataPtr + CCSWeaponBaseVData.m_WeaponType);
|
|
96
|
-
/* */ const Local_ZoomLevel = cs2.i32(Local_WeaponBasePtr + C_CSWeaponBaseGun.m_zoomLevel);
|
|
54
|
+
const Local_PlayerControllerPtr = cs2.u64(ClientPtr + Client.Other.dwLocalPlayerController);
|
|
55
|
+
/* */ const Local_TickBase = cs2.u32(Local_PlayerControllerPtr + Client.CBasePlayerController.m_nTickBase);
|
|
56
|
+
|
|
57
|
+
const Local_PlayerPawnPtr = cs2.u64(ClientPtr + Client.Other.dwLocalPlayerPawn);
|
|
58
|
+
/* */ const Local_FlashOverlayAlpha = cs2.f32(Local_PlayerPawnPtr + Client.C_CSPlayerPawnBase.m_flFlashOverlayAlpha);
|
|
59
|
+
/* */ const Local_IDEntIndex = cs2.i32(Local_PlayerPawnPtr + Client.C_CSPlayerPawn.m_iIDEntIndex);
|
|
60
|
+
/* */ const Local_IsScoped = cs2.i32(Local_PlayerPawnPtr + Client.C_CSPlayerPawn.m_bIsScoped);
|
|
61
|
+
/* */ const Local_Player_WeaponServicesPtr = cs2.u64(Local_PlayerPawnPtr + Client.C_BasePlayerPawn.m_pWeaponServices);
|
|
62
|
+
/* */ const Local_NextAttack = cs2.f32(Local_Player_WeaponServicesPtr + Client.CCSPlayer_WeaponServices.m_flNextAttack);
|
|
63
|
+
/* */ const Local_TeamNum = cs2.u8(Local_PlayerPawnPtr + Client.C_BaseEntity.m_iTeamNum);
|
|
64
|
+
/* */ const Local_WeaponBasePtr = cs2.u64(Local_PlayerPawnPtr + Client.C_CSPlayerPawn.m_pClippingWeapon);
|
|
65
|
+
/* */ const Local_Clip1 = cs2.i32(Local_WeaponBasePtr + Client.C_BasePlayerWeapon.m_iClip1);
|
|
66
|
+
/* */ const Local_NextPrimaryAttackTick = cs2.u32(Local_WeaponBasePtr + Client.C_BasePlayerWeapon.m_nNextPrimaryAttackTick);
|
|
67
|
+
/* */ const Local_WeaponBaseVDataPtr = cs2.u64(Local_WeaponBasePtr + Client.C_BaseEntity.m_nSubclassID + 0x08n);
|
|
68
|
+
/* */ const Local_IsFullAuto = cs2.bool(Local_WeaponBaseVDataPtr + Client.CCSWeaponBaseVData.m_bIsFullAuto);
|
|
69
|
+
/* */ const Local_WeaponType = cs2.i32(Local_WeaponBaseVDataPtr + Client.CCSWeaponBaseVData.m_WeaponType);
|
|
70
|
+
/* */ const Local_ZoomLevel = cs2.i32(Local_WeaponBasePtr + Client.C_CSWeaponBaseGun.m_zoomLevel);
|
|
97
71
|
|
|
98
72
|
// Conditions where we should not fire…
|
|
99
73
|
if (CurTime < Local_NextAttack) {
|
|
@@ -113,7 +87,7 @@ async function tick(ClientPtr: bigint) {
|
|
|
113
87
|
// Weapon types: https://swiftlys2.net/sdk/cs2/types/csweapontype
|
|
114
88
|
|
|
115
89
|
// Get the entity that we're aiming at from the entity list…
|
|
116
|
-
const EntityListPtr = cs2.u64(ClientPtr + dwEntityList);
|
|
90
|
+
const EntityListPtr = cs2.u64(ClientPtr + Client.Other.dwEntityList);
|
|
117
91
|
/* */ const EntityChunkPtr = cs2.u64(EntityListPtr + (BigInt(Local_IDEntIndex) >> 0x09n) * 0x08n + 0x10n);
|
|
118
92
|
/* */ const BaseEntityPtr = cs2.u64(EntityChunkPtr + (BigInt(Local_IDEntIndex) & 0x1ffn) * 0x78n);
|
|
119
93
|
/* */ const EntityClassInfoPtr = cs2.u64(EntityChunkPtr + (BigInt(Local_IDEntIndex) & 0x1ffn) * 0x78n + 0x08n);
|
|
@@ -134,7 +108,7 @@ async function tick(ClientPtr: bigint) {
|
|
|
134
108
|
}
|
|
135
109
|
|
|
136
110
|
const PlayerPawnPtr = BaseEntityPtr;
|
|
137
|
-
/* */ const TeamNum = cs2.u8(PlayerPawnPtr +
|
|
111
|
+
/* */ const TeamNum = cs2.u8(PlayerPawnPtr + Client.C_BaseEntity.m_iTeamNum);
|
|
138
112
|
|
|
139
113
|
if (TeamNum === Local_TeamNum) {
|
|
140
114
|
return;
|
|
@@ -148,7 +122,7 @@ async function tick(ClientPtr: bigint) {
|
|
|
148
122
|
// If the gun is automatic, hold the trigger until we're no longer aiming at them…
|
|
149
123
|
do {
|
|
150
124
|
await sleep(random() * Delay + Delay);
|
|
151
|
-
} while (Local_IDEntIndex === cs2.u32(Local_PlayerPawnPtr + C_CSPlayerPawn.m_iIDEntIndex) && Local_IsFullAuto);
|
|
125
|
+
} while (Local_IDEntIndex === cs2.u32(Local_PlayerPawnPtr + Client.C_CSPlayerPawn.m_iIDEntIndex) && Local_IsFullAuto);
|
|
152
126
|
|
|
153
127
|
// Release the trigger…
|
|
154
128
|
mouse_event(0x04, 0x00, 0x00, 0x00, 0n);
|
package/package.json
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"url": "git://github.com/obscuritysrl/bun-memory.git"
|
|
23
23
|
},
|
|
24
24
|
"type": "module",
|
|
25
|
-
"version": "1.1.
|
|
25
|
+
"version": "1.1.26",
|
|
26
26
|
"main": "./index.ts",
|
|
27
27
|
"keywords": [
|
|
28
28
|
"bun",
|
|
@@ -46,5 +46,9 @@
|
|
|
46
46
|
],
|
|
47
47
|
"engines": {
|
|
48
48
|
"bun": ">=1.1.0"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"run:benchmark": "bun ./example/benchmark.ts",
|
|
52
|
+
"run:trigger-bot": "bun ./example/trigger-bot.ts"
|
|
49
53
|
}
|
|
50
54
|
}
|