50'den fazla oyuncunun yer aldığı oyunlarda varsayılan RemoteEvents, tablo serileştirme yükü sebebiyle bant genişliğini hızla tüketir.
Luau'nun yerel `buffer` kütüphanesiyle koordinatları 16 bitlik tam sayılara sıkıştırıp yalnızca değişen delta verilerini göndererek bant genişliğinden %90 tasarruf sağlayabilirsiniz.
1. Varsayılan RemoteEvent Yapısının Tıkanma Nedenleri
Klasik serileştirmenin yapısal kısıtlamaları:
- Tablo yükü: Anahtar metinleri ve 64-bit sayılar nesne başına 50 bayttan fazla israfa yol açar.
- Yüksek frekans tıkanıklığı: 50 oyuncuda 250 KB/s'yi aşan trafik paket kuyruklarını şişirir.
- Bufferbloat gecikmesi: Biriken paketler mermi isabetlerinde desenskronizasyona yol açar.
- Luau Buffer avantajı: Serileştirme yükü olmadan doğrudan ham bayt dizisi aktarımı.
2. Koordinat Kuantizasyonu ve Bit Düzeyinde Paketleme
64-bit verileri minimum bayt boyutuna indirme yöntemleri:
- Sabit noktalı kuantizasyon: 2048 studluk haritayı 1 cm hassasiyetle 16-bit tam sayılara dönüştürme.
- Açı sıkıştırması: 360 derecelik rotasyonu tek bir 8-bitlik bayta sığdırma.
- Bitmask durumları: 8 boolean bayrağını tek bir bayt içinde birleştirme.
- VarInt kodlama: Küçük sayılarda değişken uzunluklu bayt kullanımı.
--!strict
local NetcodeCompressor = {}
local WORLD_BOUNDS = 2048
local MAX_UINT16 = 65535
function NetcodeCompressor.WritePosition(buf: buffer, offset: number, pos: Vector3): number
local normX = math.clamp((pos.X + (WORLD_BOUNDS / 2)) / WORLD_BOUNDS, 0, 1)
local normY = math.clamp((pos.Y + 500) / 2000, 0, 1)
local normZ = math.clamp((pos.Z + (WORLD_BOUNDS / 2)) / WORLD_BOUNDS, 0, 1)
buffer.writeu16(buf, offset, math.floor(normX * MAX_UINT16))
buffer.writeu16(buf, offset + 2, math.floor(normY * MAX_UINT16))
buffer.writeu16(buf, offset + 4, math.floor(normZ * MAX_UINT16))
return offset + 6
end
function NetcodeCompressor.ReadPosition(buf: buffer, offset: number): (Vector3, number)
local qX = buffer.readu16(buf, offset)
local qY = buffer.readu16(buf, offset + 2)
local qZ = buffer.readu16(buf, offset + 4)
local x = (qX / MAX_UINT16) * WORLD_BOUNDS - (WORLD_BOUNDS / 2)
local y = (qY / MAX_UINT16) * 2000 - 500
local z = (qZ / MAX_UINT16) * WORLD_BOUNDS - (WORLD_BOUNDS / 2)
return Vector3.new(x, y, z), offset + 6
end
return NetcodeCompressor
3. Delta Snapshot Sıkıştırması ve Değişiklik Takibi
Sadece değişen niteliklerin ağ üzerinden aktarılması:
- Anahtar kareler ve deltalar: Her 2 saniyede bir tam kare, aralarda ise sadece değişimler.
- Dirty Mask başlığı: Değişen verileri belirten kontrol bayrakları.
- İstemci enterpolasyonu: Kareler arasında akıcı hareket geçişleri.
- Hareketsiz nesneleri ayıklama: Durduğu yerde bekleyen nesnelerin veri tüketimini sıfırlama.
4. Mekânsal İlgi Yönetimi ve Mesafe Kısması
Mesafeye göre dinamik bant genişliği dağıtımı:
- Mekânsal ızgara: Haritayı 128 studluk hücrelere ayırma.
- Frekans kademelendirmesi: Yakında 30Hz, uzakta 3Hz güncelleme.
- Görüş alanı dışı eleme: Oyuncunun arkasında kalan nesnelerin önceliğini düşürme.
- UnreliableRemoteEvent kullanımı: Hızlı konum güncellemelerinde kuyruk tıkanmalarını önleme.
5. Profesyonel Netcode Kontrol Listesi
Sorunsuz çok oyunculu deneyim yönergeleri:
- Garbage Collector duraklamalarını engellemek için buffer havuzları kullanın.
- Paket başlığına protokol sürüm numarasını ekleyin.
- Işınlanma hilelerini engellemek için sınır değerleri doğrulayın.
- Oyuncu başına veri akışını 35 KB/s altında tutun.
Frequently Asked Questions
Neden tablolar yerine Luau bufferları kullanılmalı?
Bufferlar doğrudan ham bellek ileterek metin işleme yükünü ortadan kaldırır ve %80 tasarruf sağlar.
UnreliableRemoteEvent ne zaman tercih edilmelidir?
Tek bir paketin kaybolmasının sorun yaratmadığı anlık konum ve açı güncellemelerinde.
16-bit kuantizasyon oyun hassasiyetini bozar mı?
Hayır, yaklaşık 1 cm hassasiyet sağlar ve oyuncular tarafından asla fark edilmez.