FiveM TypeScript resources: fxmanifest, @citizenfx types ve esbuild

FiveM resources'ları TypeScript veya JavaScript'te yazın: dist dosyaları ile fxmanifest, @citizenfx types, esbuild bundle, events, setTick ve Wait, Lua exports'ını çağırma.

FiveM script'inizde types, async/await ve npm paketleri istiyorsunuz, ancak resource'unuz sadece .lua dosyalarıdır. FiveM kendi runtime'ında JavaScript çalıştırır, bu nedenle TypeScript, JavaScript'e ilk derlendiği sürece çalışır. Bu kılavuz işçi düzeni verir: manifest, types, bundler, events ve ticks ve Lua ile nasıl karıştırılır.

Proje düzeni

text
my_resource/
  fxmanifest.lua
  package.json
  tsconfig.json
  src/
    client.ts
    server.ts
  dist/            (build output, loaded by FiveM)

src/ yazarsınız, dist/ öğesine derleyin ve manifest yalnızca dist/ öğesi hakkında bilir.

Fxmanifest

lua
fx_version 'cerulean'
game 'gta5'

client_script 'dist/client.js'
server_script 'dist/server.js'

Her ikisi de düz JavaScript dosyasıdır. Resource'unuz belirli bir JavaScript runtime sürümü veya server'da Node özellikleri gerekiyorsa, manifest seçenekleri için mevcut Cfx.re docs kontrol edin, desteklenen runtime'lar zaman içinde değişmiştir.

İpucu: build'i dışarıda bırakırsanız, FiveM dist/client.js bulamadığını söyler. Kişileştir veya yeniden başlama öncesinde her zaman build'i çalıştırın.

Types ve bundler kurun

bash
npm init -y
npm install --save-dev typescript esbuild @citizenfx/client @citizenfx/server

@citizenfx/client ve @citizenfx/server her tarafta natives ve Cfx.re fonksiyonları için type paketleridir. Onları kullanan bir tsconfig.json:

json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "noEmit": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}

TypeScript burada sadece type kontrol eder (noEmit). esbuild asıl bundling'i yapar:

json
{
  "scripts": {
    "build": "esbuild src/client.ts --bundle --outfile=dist/client.js --target=es2020 && esbuild src/server.ts --bundle --outfile=dist/server.js --target=es2020 --platform=node",
    "watch": "npm run build -- --watch",
    "typecheck": "tsc"
  }
}

Bundling, aldığınız npm paketlerinin tek çıktı dosyasına kopyalandığı anlamına gelir, bu nedenle server'ın runtime'da node_modules gerekmez. Runtime yapılarınızın kullandığı doğru --platform ve --target için Cfx.re docs kontrol edin.

Events

JavaScript Lua gibi dört yapı taşına sahiptir, başka adlarla:

Lua JavaScript
AddEventHandler on
RegisterNetEvent + handler onNet
TriggerEvent emit
TriggerServerEvent / TriggerClientEvent emitNet
ts
// src/server.ts
onNet('my_resource:requestData', () => {
  const src = (global as any).source as number
  emitNet('my_resource:data', src, { hello: 'world' })
})
ts
// src/client.ts
on('onClientResourceStart', (resourceName: string) => {
  if (GetCurrentResourceName() !== resourceName) return
  emitNet('my_resource:requestData')
})

onNet('my_resource:data', (data: { hello: string }) => {
  console.log(data.hello)
})

Server'da, olayı tetikleyen oyuncu, Lua gibi global source içindedir. Handler başında bunu bir sabite okuyun: await sonra, global başka bir oyuncuyu işaret edebilir. Server olaylarını güvence altına alma ile aynı güvenlik kuralları geçerli, çünkü oyuncu onNet'inizi dışarıdan ateşleyebilir. Event modeline ilişkin daha fazla bilgi client ve server events.

Ticks ve bekleme

Varsayılan olarak frame loop'u yoktur. setTick her frame'de bir fonksiyon çalıştırır ve Lua'dan Wait, setTimeout çevresinde bir promise olur:

ts
const Delay = (ms: number) => new Promise<void>(resolve => setTimeout(resolve, ms))

setTick(async () => {
  const ped = PlayerPedId()
  const [x, y, z] = GetEntityCoords(ped, true)
  // ... check something about the position
  await Delay(500)   // without this, it runs every frame
})

Gecikmesiz, handler her frame'de çalışır, Lua döngüsü olmadan aynı problem Wait(0): Wait ve thread'ler açıklanmış bakın. Hiçbir şey her frame'de kontrol edilmesi gerekmediğinde daha uzun bir gecikme kullanın.

Tek seferlik bir gecikme için, setTick atlayın ve sadece async fonksiyon içinde await Delay(1000) yapın.

Lua exports ile karıştırma

Exports diller arasında çalışır. JavaScript'te bir tane kaydedin:

ts
exports('getGreeting', (name: string) => `Hello ${name}`)

Ve Lua resource'ından çağırın:

lua
local text = exports['my_resource']:getGreeting('Mic')

Diğer yön, TypeScript'ten Lua export'u çağırma, exports nesnesi aracılığıyla gider:

ts
const ok = exports['ox_inventory'].GetItemCount(source, 'bread')

Türler diğer resource'u bilmiyorsa, örneğin declare const exports: Record<string, any> ile kendini beyan edin. Lua'da exports hedef resource yeniden başlatıldığında nasıl davrandığını görün.

Başlamadan önce derleyin

Çalışırken watcher'ı çalıştırın ve her build sonra resource'u konsolda yeniden başlatın:

text
npm run watch
restart my_resource

Yeniden başlama yeni bir şey yapmazsa, çıktı dosyasının değiştiğini, manifestin onu işaret ettiğini ve resource'un cache/ içinde eski bir kopyadan sunulmadığını kontrol edin.

Kontrol listesi

Belirti Fix
FiveM dist/client.js bulamıyor Build'i çalıştırın; manifest çıktıyı yükler, src/ değil
Natives editörde bilinmiyor @citizenfx/client ve @citizenfx/server kurun
import runtime'da başarısız olur esbuild ile bundle'layın, böylece import'lar satır içi olur
Handler yanlış oyuncuyu kullanıyor Herhangi bir await öncesinde source öğesini sabite okuyun
Tick'ten yüksek resmon setTick handler'a await Delay(ms) ekleyin
Lua işlevinizi görmüyor exports('name', fn) ile kaydedin ve resource adıyla çağırın

Kısa yanıtlar

TypeScript'ı bir FiveM resource'unda doğrudan kullanabilir miyim?

Hayır. FiveM JavaScript çalıştırır. TypeScript yazarsınız, esbuild gibi bir araçla düz JavaScript'e derleyin ve client_script ve server_script çıktı dosyalarını işaret edin.

Bir TypeScript resource'u Lua exports'ını çağırabilir mi?

Evet. Exports diller arasında paylaşılır: JavaScript'ten exports['my_resource'].functionName() çağırın ve Lua, JavaScript'te exports('name', fn) ile kaydettiğiniz exports'ları çağırabilir.

JavaScript'te Wait() yerine ne var?

Async fonksiyon içinde setTimeout veya setTick handler'ında bir promise kullanın. Ticking loop kendisi setTick'tir ve her tick, her frame'de çalıştığı için bir gecikmeyi beklemelidir.

Bu sorunu yaşatmayan scriptler

Quest CreatorGörevler ve NPC diyalogları için oyun içinde düğüm düğüm kurulan görsel editör.Scripti gör →Item Creator V2Animasyonlar, proplar, efektler ve daha fazlasıyla kullanılabilir eşyalar oluşturun — kod yazmadan.Scripti gör →Shop CreatorBir dakikadan kısa sürede dükkân kurun — sahipler, çalışanlar, kasalar ve soygunlar dahil.Scripti gör →

Okumaya devam edin