lenix 1.6.4 → 1.7.1
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 +50 -0
- package/client/cam.ts +141 -0
- package/client/client.ts +98 -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 +7 -0
- package/server/server.ts +11 -0
- package/shared/assert.ts +11 -0
- package/shared/index.ts +8 -0
- package/shared/types.ts +34 -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,50 @@
|
|
|
1
|
+
// deno-lint-ignore-file no-undef
|
|
2
|
+
|
|
3
|
+
import type { Vec4 } from '../shared/types.ts'
|
|
4
|
+
|
|
5
|
+
export interface Blip {
|
|
6
|
+
/**
|
|
7
|
+
* Blip world coordinates and heading.
|
|
8
|
+
*/
|
|
9
|
+
coords: Vec4
|
|
10
|
+
/**
|
|
11
|
+
* FiveM blip sprite id.
|
|
12
|
+
*/
|
|
13
|
+
icon: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const blips = new Set<number>()
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a tracked map blip.
|
|
20
|
+
*/
|
|
21
|
+
const create = ({
|
|
22
|
+
coords,
|
|
23
|
+
icon
|
|
24
|
+
}: Blip): number => {
|
|
25
|
+
const blip = AddBlipForCoord(coords[0], coords[1], coords[2])
|
|
26
|
+
|
|
27
|
+
SetBlipSprite(blip, icon)
|
|
28
|
+
blips.add(blip)
|
|
29
|
+
|
|
30
|
+
return blip
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Removes a tracked map blip.
|
|
35
|
+
*/
|
|
36
|
+
const destroy = (blip: number): void => {
|
|
37
|
+
RemoveBlip(blip)
|
|
38
|
+
blips.delete(blip)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const blip = {
|
|
42
|
+
create,
|
|
43
|
+
destroy
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
on('onResourceStop', (resourceName: string) => {
|
|
47
|
+
if (resourceName !== GetCurrentResourceName()) return
|
|
48
|
+
|
|
49
|
+
blips.forEach(destroy)
|
|
50
|
+
})
|
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/client.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { Vec3, Vec4 } from '../shared/types.ts'
|
|
2
|
+
|
|
3
|
+
export const client: {
|
|
4
|
+
entity: {
|
|
5
|
+
handle: (entityNetId?: number) => number;
|
|
6
|
+
netId: (entity?: number) => number;
|
|
7
|
+
coords: {
|
|
8
|
+
(excludeH: true, entity?: number, isAlive?: boolean): Vec3;
|
|
9
|
+
(excludeH?: false, entity?: number, isAlive?: boolean): Vec4;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
player: {
|
|
13
|
+
serverId: (playerId?: number) => number;
|
|
14
|
+
id: (serverId?: number) => number;
|
|
15
|
+
};
|
|
16
|
+
} = {
|
|
17
|
+
entity: {
|
|
18
|
+
/**
|
|
19
|
+
* Gets the local entity handle from a network ID.
|
|
20
|
+
*
|
|
21
|
+
* Network IDs are shared, but entity handles are local.
|
|
22
|
+
* The returned handle is only valid on the current machine.
|
|
23
|
+
*
|
|
24
|
+
* If no network ID is provided, this resolves your own player ped
|
|
25
|
+
* by first getting its network ID.
|
|
26
|
+
*
|
|
27
|
+
* Examples:
|
|
28
|
+
* - entity(vehicleNetId) -> local vehicle handle
|
|
29
|
+
* - entity(pedNetId) -> local ped handle
|
|
30
|
+
* - entity() -> your own player ped handle
|
|
31
|
+
*/
|
|
32
|
+
handle: (entityNetId = client.entity.netId()) => NetworkGetEntityFromNetworkId(entityNetId),
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Gets the network ID for an entity.
|
|
36
|
+
*
|
|
37
|
+
* Entity handles are local to the current machine.
|
|
38
|
+
* Network IDs are shared references for networked entities.
|
|
39
|
+
*
|
|
40
|
+
* If no entity is provided, this uses your own player ped.
|
|
41
|
+
*
|
|
42
|
+
* Examples:
|
|
43
|
+
* - netId(vehicle) -> network ID of that vehicle
|
|
44
|
+
* - netId(targetPed) -> network ID of that ped
|
|
45
|
+
* - netId() -> network ID of your own player ped
|
|
46
|
+
*/
|
|
47
|
+
netId: (entity = PlayerPedId()) => NetworkGetNetworkIdFromEntity(entity),
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Gets entity coordinates and heading.
|
|
51
|
+
*/
|
|
52
|
+
coords: (() => {
|
|
53
|
+
function coords(excludeH: true, entity?: number, isAlive?: boolean): Vec3
|
|
54
|
+
function coords(excludeH?: false, entity?: number, isAlive?: boolean): Vec4
|
|
55
|
+
function coords(excludeH = false, entity = client.entity.handle(), isAlive = true) {
|
|
56
|
+
const entityCoords = GetEntityCoords(entity, isAlive) as Vec3
|
|
57
|
+
|
|
58
|
+
if (excludeH) return entityCoords
|
|
59
|
+
|
|
60
|
+
return [
|
|
61
|
+
...entityCoords,
|
|
62
|
+
GetEntityHeading(entity)
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return coords
|
|
67
|
+
})()
|
|
68
|
+
},
|
|
69
|
+
player: {
|
|
70
|
+
/**
|
|
71
|
+
* Gets a client player ID.
|
|
72
|
+
*
|
|
73
|
+
* If a server ID is provided, this converts that server ID into a client
|
|
74
|
+
* player ID on the current client.
|
|
75
|
+
*
|
|
76
|
+
* If no server ID is provided, this returns your own client player ID.
|
|
77
|
+
*
|
|
78
|
+
* Examples:
|
|
79
|
+
* - id(targetServerId) -> target player's client player ID
|
|
80
|
+
* - id() -> your own client player ID
|
|
81
|
+
*/
|
|
82
|
+
id: (serverId?: number) => serverId ? GetPlayerFromServerId(serverId) : PlayerId(),
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Gets a player's server ID from their client player ID.
|
|
86
|
+
*
|
|
87
|
+
* Client player IDs are local to each client.
|
|
88
|
+
* Server IDs, also called source IDs, identify connected players on the server.
|
|
89
|
+
*
|
|
90
|
+
* If no client player ID is provided, this uses your own client player ID.
|
|
91
|
+
*
|
|
92
|
+
* Examples:
|
|
93
|
+
* - serverId(targetPlayerId) -> target player's server ID
|
|
94
|
+
* - serverId() -> your own server ID
|
|
95
|
+
*/
|
|
96
|
+
serverId: (playerId = client.player.id()) => GetPlayerServerId(playerId),
|
|
97
|
+
}
|
|
98
|
+
}
|
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 './timer.ts'
|
|
15
|
+
export * from './client.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 = vehicle === 0
|
|
55
|
+
? getEntity.coords(true, playerPed)
|
|
56
|
+
: GetWorldPositionOfEntityBone(playerPed, 0) as Vec3
|
|
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.1",
|
|
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/server/server.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const server: {
|
|
2
|
+
entity: {
|
|
3
|
+
handle: (netId: number) => number;
|
|
4
|
+
handleFromSource: (source: number) => number;
|
|
5
|
+
};
|
|
6
|
+
} = {
|
|
7
|
+
entity: {
|
|
8
|
+
handle: (netId: number) => NetworkGetEntityFromNetworkId(netId),
|
|
9
|
+
handleFromSource: (source: number) => GetPlayerPed(source.toString())
|
|
10
|
+
}
|
|
11
|
+
}
|