lenix 1.6.4 → 1.7.0
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/LICENSE +21 -674
- package/client/_init.ts +11 -0
- package/client/blip.ts +51 -0
- package/client/cam.ts +141 -0
- package/client/entity.ts +22 -0
- package/client/index.ts +15 -0
- package/client/nearest.ts +115 -0
- package/client/nui.ts +28 -0
- package/client/pool.ts +30 -0
- package/client/timer.ts +28 -0
- package/nui/focus.ts +10 -0
- package/nui/index.ts +10 -0
- package/nui/on.ts +29 -0
- package/nui/trigger.ts +24 -0
- package/package.json +19 -37
- package/server/index.ts +5 -0
- package/shared/assert.ts +11 -0
- package/shared/index.ts +8 -0
- package/shared/types.ts +36 -0
- package/Readme.md +0 -59
- package/format/index.ts +0 -69
- package/format/preset.json +0 -10
- package/index.ts +0 -17
- package/modules/src/edge/index.ts +0 -22
- package/modules/src/entries/index.ts +0 -14
- package/modules/src/fetch/index.ts +0 -21
- package/modules/src/fxmanifest/fxmanifest.json +0 -8
- package/modules/src/fxmanifest/index.ts +0 -23
- package/modules/src/fxmanifest/tsconfig.json +0 -7
- package/modules/src/guard/index.ts +0 -11
- package/modules/src/index.ts +0 -6
- package/modules/src/lint/preset.json +0 -349
- package/modules/src/raise/index.ts +0 -11
- package/modules/src/wait/index.ts +0 -9
- package/oop/LICENSE +0 -21
- package/oop/README.md +0 -30
- package/oop/mac-lua-modules.sh +0 -65
package/client/_init.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// deno-lint-ignore-file no-undef
|
|
2
|
+
import type { _InternalRequests } from '../shared/types.ts'
|
|
3
|
+
import { onNui } from './nui.ts'
|
|
4
|
+
|
|
5
|
+
onNui<_InternalRequests['focus']>('__nuiFocus', ({
|
|
6
|
+
keyboard,
|
|
7
|
+
cursor
|
|
8
|
+
}) => {
|
|
9
|
+
SetNuiFocus(keyboard, cursor)
|
|
10
|
+
return true
|
|
11
|
+
})
|
package/client/blip.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// deno-lint-ignore-file no-undef
|
|
2
|
+
|
|
3
|
+
import type { Vec4 } from '../shared/types.ts'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export interface Blip {
|
|
7
|
+
/**
|
|
8
|
+
* Blip world coordinates and heading.
|
|
9
|
+
*/
|
|
10
|
+
coords: Vec4
|
|
11
|
+
/**
|
|
12
|
+
* FiveM blip sprite id.
|
|
13
|
+
*/
|
|
14
|
+
icon: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const blips = new Set<number>()
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates a tracked map blip.
|
|
21
|
+
*/
|
|
22
|
+
const create = ({
|
|
23
|
+
coords,
|
|
24
|
+
icon
|
|
25
|
+
}: Blip): number => {
|
|
26
|
+
const blip = AddBlipForCoord(coords[0], coords[1], coords[2])
|
|
27
|
+
|
|
28
|
+
SetBlipSprite(blip, icon)
|
|
29
|
+
blips.add(blip)
|
|
30
|
+
|
|
31
|
+
return blip
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Removes a tracked map blip.
|
|
36
|
+
*/
|
|
37
|
+
const destroy = (blip: number): void => {
|
|
38
|
+
RemoveBlip(blip)
|
|
39
|
+
blips.delete(blip)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const blip = {
|
|
43
|
+
create,
|
|
44
|
+
destroy
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
on('onResourceStop', (resourceName: string) => {
|
|
48
|
+
if (resourceName !== GetCurrentResourceName()) return
|
|
49
|
+
|
|
50
|
+
blips.forEach(destroy)
|
|
51
|
+
})
|
package/client/cam.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// deno-lint-ignore-file no-undef
|
|
2
|
+
|
|
3
|
+
import type { Vec4 } from '../shared/types.ts'
|
|
4
|
+
|
|
5
|
+
export interface BaseCamDetails {
|
|
6
|
+
/**
|
|
7
|
+
* Fade-out duration in milliseconds.
|
|
8
|
+
*/
|
|
9
|
+
fadeOut?: number
|
|
10
|
+
/**
|
|
11
|
+
* Fade-in duration in milliseconds.
|
|
12
|
+
*/
|
|
13
|
+
fadeIn?: number
|
|
14
|
+
/**
|
|
15
|
+
* Delay before the camera state changes.
|
|
16
|
+
*/
|
|
17
|
+
delay?: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface CreateCamSettings {
|
|
21
|
+
/**
|
|
22
|
+
* Camera coordinates and heading.
|
|
23
|
+
*/
|
|
24
|
+
coords: Vec4
|
|
25
|
+
/**
|
|
26
|
+
* Camera vertical and horizontal rotation.
|
|
27
|
+
*/
|
|
28
|
+
rotation: {
|
|
29
|
+
vertical: number,
|
|
30
|
+
horizontal: number
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Optional camera field-of-view, rotation order, and fade settings.
|
|
34
|
+
*/
|
|
35
|
+
details?: {
|
|
36
|
+
fov?: number,
|
|
37
|
+
rotationOrder?: number
|
|
38
|
+
} & BaseCamDetails
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface DestroyCamSettings {
|
|
42
|
+
/**
|
|
43
|
+
* Camera handle to deactivate.
|
|
44
|
+
*/
|
|
45
|
+
cam: number
|
|
46
|
+
/**
|
|
47
|
+
* Optional fade settings.
|
|
48
|
+
*/
|
|
49
|
+
details?: BaseCamDetails
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const toggleCam = ({
|
|
53
|
+
cam,
|
|
54
|
+
state,
|
|
55
|
+
delay,
|
|
56
|
+
fadeIn,
|
|
57
|
+
fadeOut
|
|
58
|
+
}: {
|
|
59
|
+
cam: number,
|
|
60
|
+
state: boolean,
|
|
61
|
+
delay: number,
|
|
62
|
+
fadeIn: number,
|
|
63
|
+
fadeOut: number
|
|
64
|
+
}) => {
|
|
65
|
+
setTimeout(() => {
|
|
66
|
+
SetCamActive(cam, state)
|
|
67
|
+
// deno-lint-ignore no-boolean-literal-for-arguments
|
|
68
|
+
RenderScriptCams(state, state, delay, true, state)
|
|
69
|
+
DoScreenFadeIn(fadeIn)
|
|
70
|
+
}, delay)
|
|
71
|
+
DoScreenFadeOut(fadeOut)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Creates and activates a scripted camera.
|
|
76
|
+
*/
|
|
77
|
+
const create = ({
|
|
78
|
+
coords,
|
|
79
|
+
rotation: {
|
|
80
|
+
vertical,
|
|
81
|
+
horizontal
|
|
82
|
+
},
|
|
83
|
+
details
|
|
84
|
+
}: CreateCamSettings): number => {
|
|
85
|
+
const fov = details?.fov ?? 40.0,
|
|
86
|
+
fadeOut = details?.fadeOut ?? 0,
|
|
87
|
+
fadeIn = details?.fadeIn ?? 0,
|
|
88
|
+
delay = details?.delay ?? 0,
|
|
89
|
+
rotationOrder = details?.rotationOrder ?? 0
|
|
90
|
+
|
|
91
|
+
DoScreenFadeOut(fadeOut)
|
|
92
|
+
// deno-lint-ignore no-boolean-literal-for-arguments
|
|
93
|
+
const cam = CreateCamWithParams(
|
|
94
|
+
'DEFAULT_SCRIPTED_CAMERA',
|
|
95
|
+
coords[0],
|
|
96
|
+
coords[1],
|
|
97
|
+
coords[2],
|
|
98
|
+
vertical,
|
|
99
|
+
horizontal,
|
|
100
|
+
coords[3],
|
|
101
|
+
fov,
|
|
102
|
+
false,
|
|
103
|
+
rotationOrder
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
toggleCam({
|
|
107
|
+
cam,
|
|
108
|
+
state: true,
|
|
109
|
+
delay,
|
|
110
|
+
fadeIn,
|
|
111
|
+
fadeOut
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
return cam
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Deactivates a scripted camera.
|
|
119
|
+
*/
|
|
120
|
+
const destroy = ({
|
|
121
|
+
cam,
|
|
122
|
+
details
|
|
123
|
+
}: DestroyCamSettings): void => {
|
|
124
|
+
const fadeOut = details?.fadeOut ?? 0,
|
|
125
|
+
fadeIn = details?.fadeIn ?? 0,
|
|
126
|
+
delay = details?.delay ?? 0
|
|
127
|
+
|
|
128
|
+
toggleCam({
|
|
129
|
+
cam,
|
|
130
|
+
state: false,
|
|
131
|
+
delay,
|
|
132
|
+
fadeIn,
|
|
133
|
+
fadeOut
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// TODO: delete cams on resource stop
|
|
138
|
+
export const cam = {
|
|
139
|
+
create,
|
|
140
|
+
destroy
|
|
141
|
+
}
|
package/client/entity.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// deno-lint-ignore-file no-undef
|
|
2
|
+
import type { Vec3, Vec4 } from '../shared/types.ts'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Gets entity coordinates and heading.
|
|
6
|
+
*/
|
|
7
|
+
function coords(excludeH: true, entity?: number, isAlive?: boolean): Vec3
|
|
8
|
+
function coords(excludeH?: false, entity?: number, isAlive?: boolean): Vec4
|
|
9
|
+
function coords(excludeH = false, entity = PlayerPedId(), isAlive = true): Vec3 | Vec4 {
|
|
10
|
+
const entityCoords = GetEntityCoords(entity, isAlive) as Vec3
|
|
11
|
+
|
|
12
|
+
if (excludeH) return entityCoords
|
|
13
|
+
|
|
14
|
+
return [
|
|
15
|
+
...entityCoords,
|
|
16
|
+
GetEntityHeading(entity)
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const getEntity = {
|
|
21
|
+
coords
|
|
22
|
+
}
|
package/client/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
*
|
|
4
|
+
* FiveM client-side helpers for NUI messages, blips, cameras, entities, pools,
|
|
5
|
+
* and nearest-entity lookups.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import './_init.ts'
|
|
9
|
+
export * from './nui.ts'
|
|
10
|
+
export * from './blip.ts'
|
|
11
|
+
export * from './cam.ts'
|
|
12
|
+
export * from './nearest.ts'
|
|
13
|
+
export * from './pool.ts'
|
|
14
|
+
export * from './entity.ts'
|
|
15
|
+
export * from './timer.ts'
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// deno-lint-ignore-file no-undef
|
|
2
|
+
import type { Vec3 } from '../shared/types.ts'
|
|
3
|
+
import { getEntity } from './entity.ts'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Finds the nearest coordinates from a list.
|
|
7
|
+
*/
|
|
8
|
+
const coords = (coords: Vec3, zones: Vec3[]): Vec3 | undefined => {
|
|
9
|
+
let closest: Vec3 | undefined
|
|
10
|
+
let closestDistance = Infinity
|
|
11
|
+
|
|
12
|
+
for (const zone of zones) {
|
|
13
|
+
const dx = zone[0] - coords[0]
|
|
14
|
+
const dy = zone[1] - coords[1]
|
|
15
|
+
const dz = zone[2] - coords[2]
|
|
16
|
+
const distance = dx * dx + dy * dy + dz * dz
|
|
17
|
+
|
|
18
|
+
if (distance < closestDistance) {
|
|
19
|
+
closestDistance = distance
|
|
20
|
+
closest = zone
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return closest
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Finds the nearest player around the provided coordinates.
|
|
29
|
+
*/
|
|
30
|
+
const player = (
|
|
31
|
+
coords: Vec3,
|
|
32
|
+
maxDistance = 2.0,
|
|
33
|
+
includePlayer = false
|
|
34
|
+
): {
|
|
35
|
+
playerId?: number,
|
|
36
|
+
playerPed?: number,
|
|
37
|
+
playerCoords?: Vec3,
|
|
38
|
+
playerVehicle?: number
|
|
39
|
+
} => {
|
|
40
|
+
const players = GetActivePlayers()
|
|
41
|
+
|
|
42
|
+
let closestId: number | undefined
|
|
43
|
+
let closestPed: number | undefined
|
|
44
|
+
let closestCoords: Vec3 | undefined
|
|
45
|
+
let closestVehicle: number | undefined
|
|
46
|
+
|
|
47
|
+
for (let i = 0; i < players.length; i++) {
|
|
48
|
+
const playerId = players[i]
|
|
49
|
+
|
|
50
|
+
if (playerId !== PlayerPedId() || includePlayer) {
|
|
51
|
+
const playerPed = GetPlayerPed(playerId)
|
|
52
|
+
// deno-lint-ignore no-boolean-literal-for-arguments
|
|
53
|
+
const vehicle = GetVehiclePedIsIn(playerPed, false)
|
|
54
|
+
const playerCoords: Vec3 = (
|
|
55
|
+
vehicle === 0 ? getEntity.coords(true, playerPed) : GetWorldPositionOfEntityBone(playerPed, 0) as Vec3
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
const distance = Vdist(
|
|
59
|
+
coords[0],
|
|
60
|
+
coords[1],
|
|
61
|
+
coords[2],
|
|
62
|
+
playerCoords[0],
|
|
63
|
+
playerCoords[1],
|
|
64
|
+
playerCoords[2]
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if (distance < maxDistance) {
|
|
68
|
+
maxDistance = distance
|
|
69
|
+
closestId = playerId
|
|
70
|
+
closestPed = playerPed
|
|
71
|
+
closestCoords = playerCoords
|
|
72
|
+
closestVehicle = vehicle
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
playerId: closestId,
|
|
79
|
+
playerPed: closestPed,
|
|
80
|
+
playerCoords: closestCoords,
|
|
81
|
+
playerVehicle: closestVehicle
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Finds the nearest vehicle around an entity.
|
|
87
|
+
*/
|
|
88
|
+
const vehicle = (entity: number, radialSpace: number): number | undefined => {
|
|
89
|
+
const coords: Vec3 = getEntity.coords(true, entity)
|
|
90
|
+
const vehicles = GetGamePool('CVehicle') as number[]
|
|
91
|
+
|
|
92
|
+
let closest: number | undefined
|
|
93
|
+
let closestDistance = radialSpace
|
|
94
|
+
|
|
95
|
+
for (const vehicle of vehicles) {
|
|
96
|
+
const vehCoords: Vec3 = getEntity.coords(true, vehicle)
|
|
97
|
+
const x = coords[0] - vehCoords[0]
|
|
98
|
+
const y = coords[1] - vehCoords[1]
|
|
99
|
+
const z = coords[2] - vehCoords[2]
|
|
100
|
+
const distance = Math.sqrt(x * x + y * y + z * z)
|
|
101
|
+
|
|
102
|
+
if (distance < closestDistance) {
|
|
103
|
+
closestDistance = distance
|
|
104
|
+
closest = vehicle
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return closest
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const getNearest = {
|
|
112
|
+
player,
|
|
113
|
+
vehicle,
|
|
114
|
+
coords
|
|
115
|
+
}
|
package/client/nui.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// deno-lint-ignore-file no-undef
|
|
2
|
+
import type { Request } from '../shared/types.ts'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Sends an event from the game client to the NUI browser.
|
|
6
|
+
*/
|
|
7
|
+
export const emitEvent = <T extends [string, unknown[]]>(id: T[0], ...params: T[1]): void => {
|
|
8
|
+
if (
|
|
9
|
+
!SendNuiMessage(
|
|
10
|
+
JSON.stringify({
|
|
11
|
+
id,
|
|
12
|
+
params: [...params]
|
|
13
|
+
})
|
|
14
|
+
)
|
|
15
|
+
) {
|
|
16
|
+
throw new Error('SendNuiMessage returned falsy')
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Registers a typed NUI callback on the game client.
|
|
22
|
+
*/
|
|
23
|
+
export const onNui = <T extends Request<unknown, string, object>>(
|
|
24
|
+
id: T[1],
|
|
25
|
+
cb: (data: T[2]) => T[0]
|
|
26
|
+
): void => {
|
|
27
|
+
RegisterNuiCallback(id, (data: T[2], reply: (_: unknown) => void) => reply(cb(data)))
|
|
28
|
+
}
|
package/client/pool.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// deno-lint-ignore-file no-undef
|
|
2
|
+
const tasks = new Set<() => void>()
|
|
3
|
+
let running = false
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Adds a task to a shared FiveM tick pool.
|
|
7
|
+
*/
|
|
8
|
+
export const pool = (func: () => void): void => {
|
|
9
|
+
tasks.add(func)
|
|
10
|
+
|
|
11
|
+
if (running) return
|
|
12
|
+
running = true
|
|
13
|
+
|
|
14
|
+
const tick = setTick(() => {
|
|
15
|
+
if (!tasks.size) {
|
|
16
|
+
clearTick(tick)
|
|
17
|
+
running = false
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
for (const task of tasks) {
|
|
21
|
+
try {
|
|
22
|
+
task()
|
|
23
|
+
} catch (e) {
|
|
24
|
+
tasks.delete(task)
|
|
25
|
+
// deno-lint-ignore no-console
|
|
26
|
+
console.warn(e)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
}
|
package/client/timer.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// deno-lint-ignore-file no-undef
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Starts a client timer using FiveM game time.
|
|
5
|
+
*/
|
|
6
|
+
export const useTimer = (
|
|
7
|
+
duration: number,
|
|
8
|
+
updateInterval: number,
|
|
9
|
+
onTick: (timeLeft: number) => void,
|
|
10
|
+
onEnd: () => void
|
|
11
|
+
): () => void => {
|
|
12
|
+
const start = GetGameTimer()
|
|
13
|
+
|
|
14
|
+
const interval = setInterval(() => {
|
|
15
|
+
const elapsed = GetGameTimer() - start
|
|
16
|
+
const timeLeft = duration - elapsed
|
|
17
|
+
|
|
18
|
+
if (elapsed >= duration) {
|
|
19
|
+
clearInterval(interval)
|
|
20
|
+
onEnd()
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
onTick(timeLeft)
|
|
25
|
+
}, updateInterval)
|
|
26
|
+
|
|
27
|
+
return () => clearInterval(interval)
|
|
28
|
+
}
|
package/nui/focus.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { _InternalRequests } from '../shared/types.ts'
|
|
2
|
+
import { triggerNui } from './trigger.ts'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Requests keyboard and cursor focus for the NUI browser.
|
|
6
|
+
*/
|
|
7
|
+
export const focus = (
|
|
8
|
+
keyboard = true,
|
|
9
|
+
cursor = true
|
|
10
|
+
): Promise<true> => triggerNui<_InternalRequests['focus']>('__nuiFocus', { keyboard, cursor })
|
package/nui/index.ts
ADDED
package/nui/on.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Event } from '../shared/types.ts'
|
|
2
|
+
|
|
3
|
+
const cbs = new Map<string, (...params: unknown[]) => void>()
|
|
4
|
+
|
|
5
|
+
// TODO: remove the event
|
|
6
|
+
// deno-lint-ignore no-window no-window-prefix no-unused-vars
|
|
7
|
+
const handler = window.addEventListener('message', (event: MessageEvent) => {
|
|
8
|
+
const { id, params } = event.data
|
|
9
|
+
const cb = cbs.get(id)
|
|
10
|
+
if (!cb) throw new Error(`Callback<${id}> does not exist yet`)
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
cb(...(params ?? []))
|
|
14
|
+
} catch (e) {
|
|
15
|
+
throw new Error(`Error occured while receiving event<${id}>. \n${e}`)
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Registers a typed browser-side handler for events sent from the game client.
|
|
21
|
+
*/
|
|
22
|
+
export const onEvent = <
|
|
23
|
+
T extends Event<string, unknown[]>
|
|
24
|
+
>(
|
|
25
|
+
id: T[0],
|
|
26
|
+
cb: (...params: T[1]) => void
|
|
27
|
+
): void => {
|
|
28
|
+
cbs.set(id, cb)
|
|
29
|
+
}
|
package/nui/trigger.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Request } from '../shared/types.ts'
|
|
2
|
+
|
|
3
|
+
declare function GetParentResourceName(): string
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Sends a typed request from the NUI browser to the game client.
|
|
7
|
+
*/
|
|
8
|
+
export const triggerNui = async <T extends Request<unknown, string, object>>(
|
|
9
|
+
id: T[1],
|
|
10
|
+
data?: T[2]
|
|
11
|
+
): Promise<T[0]> => {
|
|
12
|
+
try {
|
|
13
|
+
const response = await fetch(`https://${GetParentResourceName()}/${id}`, {
|
|
14
|
+
method: 'post',
|
|
15
|
+
headers: {
|
|
16
|
+
'Content-Type': 'application/json; charset=UTF-8'
|
|
17
|
+
},
|
|
18
|
+
body: JSON.stringify(data ?? {})
|
|
19
|
+
})
|
|
20
|
+
return await response.json()
|
|
21
|
+
} catch (e) {
|
|
22
|
+
throw new Error(`Error occured while emiting an nui<${id}>.\n${e}`)
|
|
23
|
+
}
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lenix",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.7.0",
|
|
4
|
+
"description": "Typed FiveM utilities for client, server, shared, and NUI scripts",
|
|
5
5
|
"author": "Lenix",
|
|
6
|
-
"license": "
|
|
6
|
+
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
8
|
-
"readme": "modules/README.md",
|
|
9
8
|
"keywords": [
|
|
10
9
|
"lenix",
|
|
11
10
|
"modules",
|
|
@@ -22,45 +21,28 @@
|
|
|
22
21
|
],
|
|
23
22
|
"repository": {
|
|
24
23
|
"type": "git",
|
|
25
|
-
"url": "git+https://github.com/
|
|
24
|
+
"url": "git+https://github.com/jonahmr1/lenix.git"
|
|
26
25
|
},
|
|
27
|
-
"main": "./modules/src/index.ts",
|
|
28
|
-
"types": "./modules/src/index.ts",
|
|
29
26
|
"exports": {
|
|
30
|
-
".": "./
|
|
31
|
-
"./
|
|
27
|
+
".": "./shared/index.ts",
|
|
28
|
+
"./client": "./client/index.ts",
|
|
29
|
+
"./server": "./server/index.ts",
|
|
30
|
+
"./nui": "./nui/index.ts"
|
|
32
31
|
},
|
|
33
32
|
"files": [
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
33
|
+
"client",
|
|
34
|
+
"server",
|
|
35
|
+
"shared",
|
|
36
|
+
"nui"
|
|
38
37
|
],
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
},
|
|
43
|
-
"dependencies": {
|
|
44
|
-
"@clack/prompts": "^1.1.0"
|
|
38
|
+
"scripts": {
|
|
39
|
+
"prepare": "rm -rf ./client ./server ./shared ./nui && cp -R ../modules/client ./client && cp -R ../modules/server ./server && cp -R ../modules/shared ./shared && cp -R ../modules/nui ./nui && cp -R ../LICENSE ./",
|
|
40
|
+
"packpreview": "rm -rf ./package-preview && mkdir -p ./package-preview && bun pm pack --destination ./package-preview && tar -xzf ./package-preview/*.tgz -C ./package-preview && find ./package-preview/package -type f | sort"
|
|
45
41
|
},
|
|
46
42
|
"devDependencies": {
|
|
47
|
-
"@
|
|
48
|
-
"@types/eslint": "^9.6.1",
|
|
49
|
-
"@types/node": "^20.7.1",
|
|
50
|
-
"eslint": "^10.0.3",
|
|
51
|
-
"eslint-config-prettier": "10.1.8",
|
|
52
|
-
"globals": "^17.4.0",
|
|
53
|
-
"jiti": "^2.6.1",
|
|
54
|
-
"prettier": "3.8.1",
|
|
55
|
-
"tsc-alias": "^1.8.16",
|
|
56
|
-
"tsx": "^4.21.0",
|
|
57
|
-
"typescript": "^5.3.3",
|
|
58
|
-
"typescript-eslint": "^8.57.0"
|
|
43
|
+
"@types/bun": "latest"
|
|
59
44
|
},
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"bump": "pnpm version patch",
|
|
63
|
-
"format": "pnpm exec prettier . --write",
|
|
64
|
-
"lint": "pnpm exec eslint ."
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"typescript": "^5"
|
|
65
47
|
}
|
|
66
|
-
}
|
|
48
|
+
}
|
package/server/index.ts
ADDED
package/shared/assert.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asserts that a condition is true and narrows its type.
|
|
3
|
+
*/
|
|
4
|
+
export const asserts = <T extends boolean>(
|
|
5
|
+
condition: T,
|
|
6
|
+
errorMessage = 'assertion did not passed successfuly'
|
|
7
|
+
): asserts condition => {
|
|
8
|
+
if (condition) return
|
|
9
|
+
|
|
10
|
+
throw new Error(errorMessage)
|
|
11
|
+
}
|
package/shared/index.ts
ADDED
package/shared/types.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Three-dimensional coordinates plus heading.
|
|
3
|
+
*/
|
|
4
|
+
export type Vec4 = [number, number, number, number]
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Three-dimensional coordinates.
|
|
8
|
+
*/
|
|
9
|
+
export type Vec3 = [number, number, number]
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Typed event tuple containing an event id and positional parameters.
|
|
13
|
+
*/
|
|
14
|
+
export type Event<Id extends string, Params extends unknown[] = never> = [Id, Params]
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Typed request tuple containing a response type, request id, and object payload.
|
|
18
|
+
*/
|
|
19
|
+
export type Request<
|
|
20
|
+
Response,
|
|
21
|
+
Id extends string,
|
|
22
|
+
Params extends object = { [key: string]: unknown }
|
|
23
|
+
> = Params extends readonly unknown[] ? never
|
|
24
|
+
: [Response, Id, Params]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Internal NUI requests used by Lenix helpers. DO NOT USE
|
|
29
|
+
*/
|
|
30
|
+
export interface _InternalRequests {
|
|
31
|
+
focus: Request<true, '__nuiFocus', {
|
|
32
|
+
keyboard: boolean,
|
|
33
|
+
cursor: boolean
|
|
34
|
+
}>
|
|
35
|
+
}
|
|
36
|
+
|