lenix 1.8.8 → 1.8.10

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/client/_init.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { asserts, type JsonValue } from '@lenix/lenix'
2
2
  import type { Request } from '../shared/types.ts'
3
- import { onNui } from './nui.ts'
3
+ import { state, onNui } from './nui.ts'
4
4
 
5
5
  type Resolve = {
6
6
  success: true,
@@ -22,8 +22,8 @@ onNui<Request<JsonValue, '__nuiServer', {
22
22
  }
23
23
  }>>('__nuiServer', async ({ id, data }) => {
24
24
  const { timeout = DEFAULT_TIMEOUT, requestData } = data
25
-
26
- const response = await new Promise<Resolve>(resolve => {
25
+
26
+ const response = await new Promise<Resolve>(resolve => {
27
27
  requestId = requestId + 1
28
28
  const currentRequestId = requestId
29
29
  pendingResolves[currentRequestId] = resolve
@@ -47,7 +47,7 @@ onNui<Request<JsonValue, '__nuiServer', {
47
47
  }
48
48
  }, timeout)
49
49
  })
50
-
50
+
51
51
  asserts(response.success, `server nui<${id}> timed out after ${timeout}ms, possible slow respose or request does not exist`)
52
52
 
53
53
  return response.returned
@@ -63,3 +63,8 @@ onNui<Request<true, '__nuiFocus', {
63
63
  SetNuiFocus(keyboard, cursor)
64
64
  return true
65
65
  })
66
+
67
+ onNui<Request<null, '__nuiInit'>>('__nuiInit', () => {
68
+ state.nuiReady = true
69
+ return null
70
+ })
package/client/client.ts CHANGED
@@ -1,197 +1,171 @@
1
1
  import type { JsonValue } from '@lenix/lenix'
2
- import { repeat } from '../shared/repeat.ts'
2
+ import { repeat } from '../../../typescript/repeat.ts'
3
3
  import type { Vec3, Vec4 } from '../shared/types.ts'
4
4
 
5
5
 
6
- export const client = {
7
- entity: {
8
- /**
9
- * Gets the local entity handle from a network ID.
10
- *
11
- * Network IDs are shared, but entity handles are local.
12
- * The returned handle is only valid on the current machine.
13
- *
14
- * If no network ID is provided, this resolves your own player ped
15
- * by first getting its network ID.
16
- *
17
- * Examples:
18
- * - entity(vehicleNetId) -> local vehicle handle
19
- * - entity(pedNetId) -> local ped handle
20
- * - entity() -> your own player ped handle
21
- */
22
- handle: (entityNetId: number) => NetworkGetEntityFromNetworkId(entityNetId),
23
-
24
- /**
25
- * Gets the network ID for an entity.
26
- *
27
- * Entity handles are local to the current machine.
28
- * Network IDs are shared references for networked entities.
29
- *
30
- * If no entity is provided, this uses your own player ped.
31
- *
32
- * Examples:
33
- * - netId(vehicle) -> network ID of that vehicle
34
- * - netId(targetPed) -> network ID of that ped
35
- * - netId() -> network ID of your own player ped
36
- */
37
- netId: (entity: number) => NetworkGetNetworkIdFromEntity(entity),
38
-
39
- /**
40
- * Gets entity coordinates and heading.
41
- */
42
- coords: (() => {
43
- function coords(entity: number, excludeH?: true, isAlive?: boolean): Vec3
44
- function coords(entity: number, excludeH?: false, isAlive?: boolean): Vec4
45
- function coords(entity: number, excludeH = false, isAlive = true) {
46
- const entityCoords = GetEntityCoords(entity, isAlive) as Vec3
47
-
48
- if (excludeH) return entityCoords
49
-
50
- return [
51
- ...entityCoords,
52
- GetEntityHeading(entity)
53
- ]
54
- }
55
-
56
- return coords
57
- })(),
58
-
59
- /* */
60
- teleport: (
61
- entity: number,
62
- x: number,
63
- y: number,
64
- z: number,
65
- h?: number,
66
- keepTasks = false,
67
- keepIK = false,
68
- doWarp = false
69
- ) => {
70
- SetEntityCoordsNoOffset(entity, x, y, z, keepTasks, keepIK, doWarp)
71
- h && SetEntityHeading(entity, h)
72
- },
6
+ export const entity = {
7
+ /**
8
+ * Gets the local entity handle from a network ID.
9
+ *
10
+ * Network IDs are shared, but entity handles are local.
11
+ * The returned handle is only valid on the current machine.
12
+ *
13
+ * If no network ID is provided, this resolves your own player ped
14
+ * by first getting its network ID.
15
+ *
16
+ * Examples:
17
+ * - entity(vehicleNetId) -> local vehicle handle
18
+ * - entity(pedNetId) -> local ped handle
19
+ * - entity() -> your own player ped handle
20
+ */
21
+ handle: (entityNetId: number) => NetworkGetEntityFromNetworkId(entityNetId),
22
+
23
+ /**
24
+ * Gets the network ID for an entity.
25
+ *
26
+ * Entity handles are local to the current machine.
27
+ * Network IDs are shared references for networked entities.
28
+ *
29
+ * If no entity is provided, this uses your own player ped.
30
+ *
31
+ * Examples:
32
+ * - netId(vehicle) -> network ID of that vehicle
33
+ * - netId(targetPed) -> network ID of that ped
34
+ * - netId() -> network ID of your own player ped
35
+ */
36
+ netId: (entity: number) => NetworkGetNetworkIdFromEntity(entity),
37
+
38
+ /**
39
+ * Gets entity coordinates and heading.
40
+ */
41
+ coords: (() => {
42
+ function coords(entity?: number, excludeH?: true, isAlive?: boolean): Vec3
43
+ function coords(entity?: number, excludeH?: false, isAlive?: boolean): Vec4
44
+ function coords(handle = player.entity(), excludeH = false, isAlive = true) {
45
+ const entityCoords = GetEntityCoords(handle, isAlive) as Vec3
46
+
47
+ if (excludeH) return entityCoords
48
+
49
+ return [
50
+ ...entityCoords,
51
+ GetEntityHeading(handle)
52
+ ]
53
+ }
73
54
 
74
- /* */
75
- playAnim: async (
76
- ped: number,
77
- dict: string,
78
- name: string,
79
- blendIn = 2.0,
80
- blendOut = 2.0,
81
- duration = -1,
82
- flag = 0,
83
- playbackFrom = 0.0,
84
- x = false,
85
- y = false,
86
- z = false
87
- ) => {
88
- RequestAnimDict(dict)
89
- await repeat(() => HasAnimDictLoaded(dict), true)
90
- TaskPlayAnim(ped, dict, name, blendIn, blendOut, duration, flag, playbackFrom, x, y, z)
91
- },
55
+ return coords
56
+ })(),
57
+
58
+ /* */
59
+ teleport: (
60
+ x: number,
61
+ y: number,
62
+ z: number,
63
+ handle: number = player.entity(),
64
+ h?: number,
65
+ keepTasks = false,
66
+ keepIK = false,
67
+ doWarp = false
68
+ ) => {
69
+ SetEntityCoordsNoOffset(handle, x, y, z, keepTasks, keepIK, doWarp)
70
+ h && SetEntityHeading(handle, h)
71
+ },
92
72
 
93
- /* */
94
- stopAnim: (ped: number, dict: string) => {
95
- RemoveAnimDict(dict)
96
- ClearPedTasks(ped)
97
- }
73
+ /* */
74
+ playAnim: async (
75
+ ped: number,
76
+ dict: string,
77
+ name: string,
78
+ blendIn = 2.0,
79
+ blendOut = 2.0,
80
+ duration = -1,
81
+ flag = 0,
82
+ playbackFrom = 0.0,
83
+ x = false,
84
+ y = false,
85
+ z = false
86
+ ) => {
87
+ RequestAnimDict(dict)
88
+ await repeat(() => HasAnimDictLoaded(dict), true)
89
+ TaskPlayAnim(ped, dict, name, blendIn, blendOut, duration, flag, playbackFrom, x, y, z)
98
90
  },
99
- player: {
100
- /**
101
- * Gets a client player ID.
102
- *
103
- * If a server ID is provided, this converts that server ID into a client
104
- * player ID on the current client.
105
- *
106
- * If no server ID is provided, this returns your own client player ID.
107
- *
108
- * Examples:
109
- * - id(targetServerId) -> target player's client player ID
110
- * - id() -> your own client player ID
111
- */
112
- id: (serverId?: number) => serverId !== undefined ? GetPlayerFromServerId(serverId) : PlayerId(),
113
-
114
- /**
115
- * Gets a player's server ID from their client player ID.
116
- *
117
- * Client player IDs are local to each client.
118
- * Server IDs, also called source IDs, identify connected players on the server.
119
- *
120
- * If no client player ID is provided, this uses your own client player ID.
121
- *
122
- * Examples:
123
- * - serverId(targetPlayerId) -> target player's server ID
124
- * - serverId() -> your own server ID
125
- */
126
- serverId(playerId?: number) {
127
- return GetPlayerServerId(playerId ?? this.id())
128
- },
129
91
 
130
- /* */
131
- entity: (playerId?: number) => playerId !== undefined ? GetPlayerPed(playerId) : PlayerPedId(),
92
+ /* */
93
+ stopAnim: (ped: number, dict: string) => {
94
+ RemoveAnimDict(dict)
95
+ ClearPedTasks(ped)
96
+ }
97
+ }
132
98
 
133
- /* */
134
- netId(playerId?: number) {
135
- return PedToNet(this.entity(playerId))
136
- },
99
+ export const player = {
100
+ /**
101
+ * Gets a client player ID.
102
+ *
103
+ * If a server ID is provided, this converts that server ID into a client
104
+ * player ID on the current
105
+ *
106
+ * If no server ID is provided, this returns your own client player ID.
107
+ *
108
+ * Examples:
109
+ * - id(targetServerId) -> target player's client player ID
110
+ * - id() -> your own client player ID
111
+ */
112
+ id: (serverId?: number) => serverId !== undefined ? GetPlayerFromServerId(serverId) : PlayerId(),
113
+
114
+ /**
115
+ * Gets a player's server ID from their client player ID.
116
+ *
117
+ * Client player IDs are local to each
118
+ * Server IDs, also called source IDs, identify connected players on the server.
119
+ *
120
+ * If no client player ID is provided, this uses your own client player ID.
121
+ *
122
+ * Examples:
123
+ * - serverId(targetPlayerId) -> target player's server ID
124
+ * - serverId() -> your own server ID
125
+ */
126
+ serverId(playerId?: number) {
127
+ return GetPlayerServerId(playerId ?? this.id())
128
+ },
137
129
 
138
- /* */
139
- coords<T extends true | undefined = undefined>(
140
- excludeH?: T,
141
- isAlive?: boolean
142
- ): T extends true ? Vec3 : Vec4 {
143
- return (
144
- excludeH === true
145
- ? client.entity.coords(this.entity(), true, isAlive)
146
- : client.entity.coords(this.entity(), false, isAlive)
147
- ) as T extends true ? Vec3 : Vec4;
148
- },
130
+ /* */
131
+ entity: (playerId?: number) => playerId !== undefined ? GetPlayerPed(playerId) : PlayerPedId(),
149
132
 
150
- /* */
151
- teleport(
152
- x: number,
153
- y: number,
154
- z: number,
155
- h?: number,
156
- keepTasks = false,
157
- keepIK = false,
158
- doWarp = false
159
- ) {
160
- return client.entity.teleport(this.entity(), x, y, z, h, keepTasks, keepIK, doWarp)
161
- },
133
+ /* */
134
+ netId(playerId: number): number {
135
+ return PedToNet(entity.handle(playerId))
136
+ },
162
137
 
163
- /* */
164
- storage: {
165
- set: <Storage>(
166
- ...[key, value]: {
167
- [K in keyof Storage]: Storage[K] extends JsonValue
168
- ? [key: Extract<K, string>, value: Storage[K]]
169
- : never
170
- }[keyof Storage]
171
- ) => SetResourceKvp(key, JSON.stringify(value)),
172
-
173
- get: <Storage, K extends Extract<keyof Storage, string>>(
174
- key: K,
175
- init?: Storage[K] extends JsonValue ? Storage[K] : never
176
- ): Storage[K] extends JsonValue ? Storage[K] : never => {
177
- const value = GetResourceKvpString(key)
178
-
179
- if (value === null) {
180
- if (init === undefined) {
181
- throw new Error(`Storage<${key}> was never assigned`)
182
- }
183
-
184
- SetResourceKvp(key, JSON.stringify(init))
185
- return init as Storage[K] extends JsonValue ? Storage[K] : never
138
+ /* */
139
+ storage: {
140
+ set: <Storage>(
141
+ ...[key, value]: {
142
+ [K in keyof Storage]: Storage[K] extends JsonValue
143
+ ? [key: Extract<K, string>, value: Storage[K]]
144
+ : never
145
+ }[keyof Storage]
146
+ ) => SetResourceKvp(key, JSON.stringify(value)),
147
+
148
+ get: <Storage, K extends Extract<keyof Storage, string>>(
149
+ key: K,
150
+ init?: Storage[K] extends JsonValue ? Storage[K] : never
151
+ ): Storage[K] extends JsonValue ? Storage[K] : never => {
152
+ const value = GetResourceKvpString(key)
153
+
154
+ if (value === null) {
155
+ if (init === undefined) {
156
+ throw `Storage<${key}> was never assigned`
186
157
  }
187
158
 
188
- return JSON.parse(value) as Storage[K] extends JsonValue ? Storage[K] : never
189
- },
159
+ SetResourceKvp(key, JSON.stringify(init))
160
+ return init as Storage[K] extends JsonValue ? Storage[K] : never
161
+ }
190
162
 
191
- delete: <Storage>(
192
- key: Extract<keyof Storage, string>
193
- ) => DeleteResourceKvp(key),
194
- /* TODO: add on event */
195
- }
163
+ return JSON.parse(value) as Storage[K] extends JsonValue ? Storage[K] : never
164
+ },
165
+
166
+ delete: <Storage>(
167
+ key: Extract<keyof Storage, string>
168
+ ) => DeleteResourceKvp(key),
169
+ /* TODO: add on event */
196
170
  }
197
- }
171
+ }
package/client/nearest.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Vec3 } from '../shared/types.ts'
2
- import { client } from './client.ts'
2
+ import { entity } from './client.ts'
3
3
 
4
4
  /**
5
5
  * Finds the nearest coordinates from a list.
@@ -50,7 +50,7 @@ const player = (
50
50
  const playerPed = GetPlayerPed(playerId)
51
51
  const vehicle = GetVehiclePedIsIn(playerPed, false)
52
52
  const playerCoords: Vec3 = vehicle === 0
53
- ? client.entity.coords(playerPed, true)
53
+ ? entity.coords(playerPed, true)
54
54
  : GetWorldPositionOfEntityBone(playerPed, 0) as Vec3
55
55
 
56
56
  const distance = Vdist(
@@ -83,15 +83,15 @@ const player = (
83
83
  /**
84
84
  * Finds the nearest vehicle around an entity.
85
85
  */
86
- const vehicle = (entity: number, radialSpace: number): number | undefined => {
87
- const coords: Vec3 = client.entity.coords(entity, true)
86
+ const vehicle = (entityHandle: number, radialSpace: number): number | undefined => {
87
+ const coords: Vec3 = entity.coords(entityHandle, true)
88
88
  const vehicles = GetGamePool('CVehicle') as number[]
89
89
 
90
90
  let closest: number | undefined
91
91
  let closestDistance = radialSpace
92
92
 
93
93
  for (const vehicle of vehicles) {
94
- const vehCoords: Vec3 = client.entity.coords(vehicle, true)
94
+ const vehCoords: Vec3 = entity.coords(vehicle, true)
95
95
  const x = coords[0] - vehCoords[0]
96
96
  const y = coords[1] - vehCoords[1]
97
97
  const z = coords[2] - vehCoords[2]
package/client/nui.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import type { NuiFetchGeneric } from '../shared/types.ts'
2
+ import { State } from '@lenix/lenix'
3
+
4
+ export const state = new State({ nuiReady: false as boolean })
2
5
 
3
6
  /**
4
7
  * Sends an event from the game client to the NUI browser.
@@ -29,8 +32,10 @@ export const onNui = <T extends NuiFetchGeneric>(
29
32
  ) => {
30
33
  try {
31
34
  reply(await cb(data))
32
- } catch(e) {
35
+ } catch (e) {
33
36
  throw e
34
37
  }
35
38
  })
36
39
  }
40
+
41
+ export const isNuiReady = () => state.nuiReady
package/client/pool.ts CHANGED
@@ -4,10 +4,12 @@ let running = false
4
4
  /**
5
5
  * Adds a task to a shared FiveM tick pool.
6
6
  */
7
- export const pool = (func: () => void): void => {
8
- tasks.add(func)
7
+ export const pool = (cb: () => void): () => boolean => {
8
+ const cleanUp = () => tasks.delete(cb)
9
+ tasks.add(cb)
9
10
 
10
- if (running) return
11
+ if (running) return cleanUp
12
+
11
13
  running = true
12
14
 
13
15
  const tick = setTick(() => {
@@ -25,4 +27,6 @@ export const pool = (func: () => void): void => {
25
27
  }
26
28
  }
27
29
  })
30
+
31
+ return cleanUp
28
32
  }
package/nui/_init.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { Request } from '../shared/types'
2
+ import { fetchNui } from './fetch'
3
+
4
+ fetchNui<Request<null, '__nuiInit'>>('__nuiInit', {})
package/nui/index.ts CHANGED
@@ -9,3 +9,4 @@ export * from './on.ts'
9
9
  export * from './fetch.ts'
10
10
  export * from './focus.ts'
11
11
  export * from './keydown.ts'
12
+ export * from './_init.ts'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lenix",
3
- "version": "1.8.8",
3
+ "version": "1.8.10",
4
4
  "description": "Typed FiveM utilities for client, server, shared, and NUI scripts",
5
5
  "author": "Lenix",
6
6
  "license": "MIT",
@@ -47,6 +47,6 @@
47
47
  "typescript": "^5"
48
48
  },
49
49
  "dependencies": {
50
- "@lenix/lenix": "npm:@jsr/lenix__lenix@1.8.16"
50
+ "@lenix/lenix": "npm:@jsr/lenix__lenix@1.8.19"
51
51
  }
52
52
  }
package/server/server.ts CHANGED
@@ -1,20 +1,18 @@
1
- export const server = {
2
- entity: {
3
- handle: (netId: number) => NetworkGetEntityFromNetworkId(netId),
4
- handleBySource: (source: number) => GetPlayerPed(source.toString()),
5
- teleport: (
6
- entity: number,
7
- x: number,
8
- y: number,
9
- z: number,
10
- h?: number,
11
- clearArea = false,
12
- alive = true,
13
- deadDisable = false,
14
- ragdol = false
15
- ) => {
16
- SetEntityCoords(entity, x, y, z, alive, deadDisable, ragdol, clearArea)
17
- h && SetEntityHeading(entity, h)
18
- },
19
- }
1
+ export const entity = {
2
+ handle: (netId: number) => NetworkGetEntityFromNetworkId(netId),
3
+ handleBySource: (source: number) => GetPlayerPed(source.toString()),
4
+ teleport: (
5
+ entity: number,
6
+ x: number,
7
+ y: number,
8
+ z: number,
9
+ h?: number,
10
+ clearArea = false,
11
+ alive = true,
12
+ deadDisable = false,
13
+ ragdol = false
14
+ ) => {
15
+ SetEntityCoords(entity, x, y, z, alive, deadDisable, ragdol, clearArea)
16
+ h && SetEntityHeading(entity, h)
17
+ },
20
18
  }
package/shared/index.ts CHANGED
@@ -5,5 +5,4 @@
5
5
  */
6
6
 
7
7
  export * from './types.ts'
8
- export * from './repeat.ts'
9
8
  export * from './palette.ts'
package/shared/types.ts CHANGED
@@ -3,12 +3,17 @@ import type { JsonValue } from "@lenix/lenix"
3
3
  /**
4
4
  * Three-dimensional coordinates plus heading.
5
5
  */
6
- export type Vec4 = [number, number, number, number]
6
+ export type Vec4 = readonly [number, number, number, number]
7
7
 
8
8
  /**
9
9
  * Three-dimensional coordinates.
10
10
  */
11
- export type Vec3 = [number, number, number]
11
+ export type Vec3 = readonly [number, number, number]
12
+
13
+ /**
14
+ * Two-dimensional coordinates.
15
+ */
16
+ export type Vec2 = readonly [number, number, number]
12
17
 
13
18
  /**
14
19
  * Typed event tuple containing an event id and positional parameters.
package/shared/repeat.ts DELETED
@@ -1,34 +0,0 @@
1
- /* */
2
- export function repeat<T>(check: () => T): Promise<T>
3
- export function repeat<T, C extends T>(
4
- check: () => T,
5
- untilOrEqual: C | ((checked: T) => checked is C),
6
- each?: number,
7
- ): Promise<C>
8
- export function repeat<T>(
9
- check: () => T,
10
- untilOrEqual?: T | ((checked: T) => boolean),
11
- each?: number,
12
- ): Promise<T> {
13
- const hasCondition = arguments.length > 1
14
- const predicate = typeof untilOrEqual === 'function'
15
- ? untilOrEqual as (checked: T) => boolean
16
- : undefined
17
-
18
- return new Promise(resolve => {
19
- const interval = setInterval(() => {
20
- const checked = check()
21
-
22
- const met = predicate
23
- ? predicate(checked)
24
- : hasCondition
25
- ? checked === untilOrEqual
26
- : checked !== undefined && checked !== null
27
-
28
- if (!met) return
29
-
30
- clearInterval(interval)
31
- resolve(checked)
32
- }, each)
33
- })
34
- }