Wenn dein Server zu Stoßzeiten stottert, ist der erste Impuls, eine größere Maschine zu mieten. Warte damit. In fast jedem Server, zu dem ich zur Reparatur gerufen wurde, war nicht die Hardware das Problem. Es waren ein paar Resources, die zu viel Arbeit auf dem Main-Thread erledigten, plus ein Content-Ordner voller Fahrzeuge, die 4K-Texturen ausliefern, die niemand sieht.
So gehe ich FiveM Server Optimierung an: die echten Übeltäter mit den Tools finden, die FiveM dir ohnehin gibt, die größten zuerst beheben und messen, dass es funktioniert hat. Geschrieben ist es für Owner und Junior-Devs, die es selbst machen wollen. Ich bin oxince und entwickle und optimiere FiveM Resources beruflich, darunter RP-Server mit über 1000 Spielern, die Zahlen unten kommen also aus den Docs und der F8-Konsole, nicht von einer Marketing-Seite.
Es liegt fast immer an den Resources, nicht an der Hardware
Die Server-Runtime von FiveM ist für die Script-Ausführung praktisch single-threaded. Jede Resource, jedes Event und jeder Datenbankaufruf konkurriert um Zeit auf einem Main-Server-Thread, und dieser Thread muss seine Arbeit in jedem Tick abschließen, sonst gerät er ins Hintertreffen. Daraus folgen zwei Dinge.
Erstens: mehr Cores draufzuwerfen bringt fast nichts. Eine 4-Core-CPU mit 4,5 GHz schlägt für FiveM eine 16-Core mit 2,8 GHz, weil es auf Single-Core-Taktrate und IPC ankommt. Zusätzliche Cores machen einen ausgelasteten Thread nicht schneller. Zweitens: eine schnelle CPU kann eine while true Schleife ohne Wait nicht überholen. Wenn ein Script nie yieldet, retten dich keine GHz. Du reparierst den Code.
Die Reihenfolge lautet also: zuerst server-seitige Scripts, dann Entity- und Streaming-Last, dann Client-Assets und erst danach die Hardware. Hardware kauft dir Reserve zu Stoßzeiten. Sie repariert keinen ineffizienten Code.
Client-FPS und Server-Tick sind nicht dasselbe
Diese beiden werden ständig verwechselt, also trenne sie zuerst. Client-FPS ist, wie viele Frames der PC eines Spielers rendert. Das hängt von seiner GPU ab, von seinen Einstellungen und davon, wie schwer deine client-seitigen Resources und gestreamten Assets sind, der FPS-Drop eines Spielers kann also komplett lokal bei ihm liegen.
Server-Tick ist, wie der Server in jedem Zyklus mit seinem eigenen Workload Schritt hält. Wenn eine Resource innerhalb eines Ticks zu lange braucht, stockt der Thread und FiveM gibt eine Hitch-Warnung aus, die dir sagt, wie viele Millisekunden verloren gingen. Beide hängen zusammen: ein schlechtes Client-Script drückt die FPS für jeden, der es ausführt, und ein stockender Server lässt sich die ganze Welt rubberbanden. Aber die Fixes liegen an verschiedenen Stellen, wenn also jemand Lag meldet, kläre, was von beidem er meint, bevor du irgendetwas anfasst.
Eine Anmerkung zur Tick-Rate: du legst keine feste Server-Tickrate in einer Config fest. Client-seitige Ticks sind frame-gebunden, und die Docs sind ausdrücklich: die Zeit zwischen ihnen ist nicht-deterministisch, weil sie frame-gebunden ist. Ein Spieler mit 180 FPS bekommt etwa alle 5,5 ms einen Tick; einer mit 60 FPS einen alle 16,6 ms. In der Praxis bedeutet eine bessere Tick-Rate, die Arbeit pro Tick zu reduzieren, damit der Thread innerhalb seines Budgets bleibt. Darum geht es im Rest dieses Guides.
Schritt 1: profilen, bevor du irgendetwas anfasst
Optimiere nicht nach Bauchgefühl. Miss zuerst. Öffne die Konsole mit F8 und führe aus:
resmon 1Der Resource Monitor zeigt CPU- und Speicherauslastung pro Resource: eine Live-Tabelle jeder laufenden Resource mit ihrer CPU-Zeit in Millisekunden. So liest du sie.
- Die Zahl, auf die es ankommt, ist die CPU-Zeit in Millisekunden. Ein voller Frame bei 60 FPS sind insgesamt nur etwa 16,6 ms, eine Resource bei 1 ms ist also schon ein echtes Stück, und mehrere davon summieren sich schnell.
- Vergleiche Leerlauf gegen Aktivität. Beobachte eine Resource, während nichts passiert, und dann während das relevante Gameplay läuft (Fahren, ein Inventar öffnen, in einer Menschenmenge stehen). Etwas, das im Leerlauf günstig ist und unter Last hochspringt, ist dein echter Verdächtiger.
- Eine brauchbare Faustregel: alles, was merklich über dem Leerlauf liegt, grob 0,5 ms im Ruhezustand oder bis in den Mehr-ms-Bereich unter Last, verdient einen genaueren Blick. Das sind keine von der Engine erzwungenen Limits, nur die richtige Größenordnung fürs Triage.
Zwei verwandte Befehle solltest du kennen: strmem 1 für den Streaming-Speicher, wo sich Textur- und Modell-Bloat zeigt, und netgraph 1 für Live-Netzwerk-Metriken. Sortiere nach CPU, mach einen Screenshot und notiere dir deine drei bis fünf größten Übeltäter. Diese Liste ist deine Arbeits-Queue.
Schritt 2: die größten Script-Übeltäter beheben
Schleifen, die nicht genug yielden
Das ist der Klassiker: ein Thread, der per-Frame Arbeit erledigt, die er nicht braucht.
-- Schlecht: schwere Arbeit in jedem einzelnen Frame
Citizen.CreateThread(function()
while true do
Citizen.Wait(0)
local ped = PlayerPedId()
DoExpensiveCheck(ped) -- läuft grundlos in jedem Tick
end
end)Die harte Regel aus den Docs: eine while true do Schleife ohne Wait crasht den Client. Yielde immer. Aber mit Wait(0) zu yielden, wenn du keine per-Frame-Präzision brauchst, ist fast genauso verschwenderisch, da es trotzdem in jedem Tick läuft. Teile die Arbeit danach auf, wie oft sie tatsächlich laufen muss, und nutze adaptive Waits.
-- Gut: meistens günstig, reaktionsschnell, wenn es darauf ankommt
Citizen.CreateThread(function()
while true do
local nearby = GetNearbyThings()
if #nearby > 0 then
Citizen.Wait(1) -- nur bei Bedarf reaktionsschnell
else
Citizen.Wait(500) -- sonst günstig im Leerlauf
end
end
end)Ein echtes Beispiel aus einem Community-Optimierungs-Writeup: eine Marker-Resource ging von 0,40 ms auf zwischen 0,01 und 0,05 ms zurück, indem das teure Filtern in einen langsamen 500-ms-Thread verlagert wurde, nur das Zeichnen in einer schnellen Schleife blieb und Marker zuerst nach Zone gefiltert wurden. Gleiches Feature, rund 10- bis 40-mal günstiger.
Native-Aufrufe und Distanzberechnung
- Cache Natives, die sich selten ändern.
PlayerPedId()undPlayerId()müssen nicht in jedem Frame neu geholt werden. Cache sie in einem langsameren Thread. - Nutze kein Native für einfache Berechnungen. Ersetze
GetDistanceBetweenCoords(...)durch Lua-Vektorrechnung:#(coordsA - coordsB). In demselben Writeup sparte diese eine Änderung etwa 0,15 ms an einer 500-Marker-Schleife. - Halte teure Natives vom per-Frame-Pfad fern. Shape-Tests und Line-of-Sight-Probes sollten gedrosselt oder event-gesteuert sein, niemals in jedem Tick laufen, wenn du es vermeiden kannst.
Events und Netz-Traffic
Mit vielen Spielern werden Server-Events schnell teuer, weil jedes ausgelöste Event Arbeit ist, multipliziert mit denen, die es betrifft. Broadcaste nicht an alle, wenn du einen Client meinst, und feuere kein Net-Event in jedem Frame, wenn eine State-Änderung reichen würde. Validiere und rate-limitiere alles, was ein Client auslösen kann. Ein unbegrenztes, client-auslösbares Event ist sowohl ein Performance- als auch ein Sicherheitsloch.
Schritt 3: zähme deine Datenbank
Datenbankaufrufe sind eine der Hauptquellen für Thread-Stalls, weil eine blockierende Abfrage den Thread festhält, während sie auf MySQL wartet.
- Nutze async, nicht-blockierende Abfragen.
oxmysqlist standardmäßig async; seine synchronen Varianten können den Main-Thread blockieren, halte sie also aus Hot-Paths heraus. Der Umstieg vom veraltetenmysql-asyncsenkt meist die ms pro Resource und die Tail-Latenz bei abfragegebundenen Aktionen. - Indiziere die Spalten, nach denen du filterst, wie
identifier,citizenidoder Plate. Ein nicht indiziertesWHEREauf einer großen Tabelle wird zu einem Full-Scan, der langsamer wird, je größer deine Spielerbasis wächst. - Batche deine Writes. Führe nicht ein INSERT oder UPDATE pro Element in einer Schleife aus; baue eine gebatchte Abfrage, nutze Prepared Statements und poole Verbindungen.
Schritt 4: Fahrzeug- und Asset-Streaming reparieren
Wenn deine Script-CPU sauber ist, die Spieler aber trotzdem stottern und langsam laden, besonders beim Fahren in dichte Gebiete, liegt es fast immer an gestreamtem Content. Auf einem RP-Server mit ein paar hundert Add-on-Fahrzeugen kann jeder verbindende Client mehrere Gigabyte an Texturdaten streamen, und das meiste davon ist verschwendet.
Hier ist der Teil, den die Leute falsch verstehen, korrekt formuliert: die Engine lehnt überdimensionierte Texturen nicht ab. Viele Add-on-Autos liefern 2K- bis 4K-Diffuse- und Normal-Maps innerhalb ihrer .ytd aus. Die Engine skaliert sie herunter, aber erst, nachdem sie sie beim Streaming in voller Auflösung in den VRAM geladen hat. Du zahlst also die Bandbreite, die Ladezeit und den VRAM und bekommst nichts auf dem Bildschirm dafür. Etwa 1024px ist die praktische Obergrenze für die meisten Fahrzeug-Texturen; behandle sie als Zielwert, nicht als hartes Limit, das die Engine erzwingt.
- Verkleinere Texturen auf vernünftige Größen. Rund 1024px für Fahrzeugkarosserie und Diffuse; 512px reichen für viele sekundäre Maps. Liveries, die lesbar bleiben müssen, dürfen bewusst höher gehen.
- Nutze ordentliche Block-Komprimierung mit Mipmaps: BC7 oder DXT5/BC3 für Diffuse, BC5 für Normals. Halte Texturen power-of-two (512, 1024, 2048), was für RAGE-Assets eine Sicherheitsregel ist, keine Stilentscheidung, sonst riskierst du Texture Bleeding.
- Inspiziere jede schwere
.ytd. Ein Texture Dictionary, das auf 16 MB zusteuert, ist in der Community ein bekannter Auslöser zum Reinschauen, keine Pass-oder-Fail-Regel der Engine, aber ein starkes Signal, dass sich das Asset zu öffnen lohnt. - Auditiere MLOs genauso. Custom-Interiors tragen denselben Textur-Bloat.
Das ist Fleißarbeit, aber oft der größte Einzelgewinn gegen Stutter und für Ladezeiten auf einem asset-lastigen Server.
Schritt 5: Entity-Last mit OneSync kontrollieren
Über statische Assets hinaus erzeugt die lebende Welt Last. Verwaiste gescriptete Entities, außer Kontrolle geratene Prop-Spawns und hohe AI-Dichte kosten alle Sync-Arbeit in jedem Tick.
OneSync ist das server-seitige Entity-Synchronisationssystem, und ab 48 Slots ist es praktisch verpflichtend (bis 48 Spieler ist es kostenlos; größer braucht eine Subscription-Stufe). Sein zentraler Performance-Trick ist Culling: der Server synct nur Entities innerhalb einer relevanten Reichweite um jeden Spieler, eine in den Docs mit 424 Units dokumentierte Focus-Zone, damit Clients nicht für Entities quer über die Map zahlen. Genau dieses Culling ist der Grund, warum OneSync auf hohe Spielerzahlen skaliert. Wo es wehtun kann, sind dichte Scopes: Scope-Events tragen Kosten proportional dazu, wie viele Spieler sich zusammendrängen, ein 200-Spieler-Nachtclub auf einem Fleck ist also tatsächlich härter für den Server als 200 Spieler verteilt über die Map.
- Räume Entities auf, die du erzeugst. Server-erstellte Entities, die nie gelöscht werden, stapeln sich. Nutze server-autoritative Erstellung und verwalte die Lebensdauer von Entities bewusst.
- Nutze Routing Buckets fürs Instancing, damit ein instanzierter Job oder ein separater Game-Mode nicht gegen die Hauptpopulation synct.
- Spawne nicht zu viel. Begrenze Props und gescriptete Peds und dünne die AI-Dichte in belebten Zonen aus.
Schritt 6: erneut messen
Optimierung ist nicht fertig, wenn du eine Änderung gemacht hast. Sie ist fertig, wenn du bewiesen hast, dass die Änderung geholfen hat. Geh zurück zu F8, führe resmon 1 unter denselben Bedingungen aus (gleiche Zone, gleiche Spielerlast, wenn du kannst) und vergleiche mit den Zahlen aus Schritt 1. Bestätige, dass der Übeltäter gesunken ist und dass du die Kosten nicht einfach woanders hingeschoben hast.
Die ganze Schleife in einem Satz: profilen mit resmon 1, den größten Übeltäter beheben, erneut messen, wiederholen. Wähle in jedem Durchgang das Schlimmste. Mikro-optimiere keine 0,02-ms-Resource, während eine mit 6 ms unangetastet bleibt.
Die Hardware-Realität, kurz
Sobald deine Resources sauber sind, gibt dir Hardware Reserve, in dieser Reihenfolge:
- Single-Core-CPU-Leistung, also hohe Taktrate und starke IPC. Das ist die, die FiveM tatsächlich bewegt.
- Eine NVMe-SSD. Deutlich schnellere Asset-Reads als SATA, was bei Ladezeiten hilft und Sync-Aussetzer beim Streaming reduziert.
- RAM, in ausreichender Menge und skaliert mit Spielerzahl und Content, aber am unwahrscheinlichsten dein Flaschenhals.
Wenn du die Script- und Asset-Arbeit erledigt hast und zu Stoßzeiten trotzdem an eine Wand stößt, dann ist ein schnellerer Single-Core-Host ein echtes Upgrade. Nicht vorher.
Wann du Hilfe holen solltest
Mit resmon 1, einem methodischen Durchgang durch deine Resources und einem Wochenende Textur-Aufräumen kommst du ziemlich weit. Es gibt einen Punkt, ab dem es sich lohnt, jemanden zu haben, der das täglich macht: Resources, die du nicht selbst geschrieben hast, Escrow-Assets, die du nicht vollständig auditieren kannst, oder einen Server mit über 300 Spielern, wo die Interaktionen wirklich komplex werden. Das ist die Arbeit, die ich mache, mit einem profil-getriebenen Audit, das dir das Vorher und Nachher in resmon zeigt.
FAQ
Warum laggt mein FiveM Server, obwohl die Hardware gut ist?
Weil die Script-Ausführung in FiveM praktisch single-threaded ist. Deine Performance wird von einem Main-Thread begrenzt, nicht von der Gesamtzahl der Cores. Eine einzige unoptimierte Resource (eine Schleife ohne sauberen Wait, eine blockierende Datenbankabfrage, ein per-Frame Native) kann diesen Thread blockieren, egal wie stark die CPU ist. Profile mit resmon in der F8-Konsole; der Lag lässt sich fast immer auf eine bestimmte Resource zurückführen.
Was ist ein guter CPU-ms-Wert in resmon?
Es gibt keine von der Engine erzwungene Zahl, aber als Faustregel fürs Triage: eine Resource, die im Leerlauf konstant über etwa 0,5 ms liegt, lohnt einen Blick, und alles, was bei normalem Spielbetrieb in den Mehr-ms-Bereich springt, ist ein Hauptverdächtiger für Hitches. Ein voller Frame bei 60 FPS sind nur etwa 16,6 ms, also summieren sich ein paar Resources mit je 1 bis 2 ms schnell. Vergleiche die Kosten im Leerlauf immer mit den Kosten, während das relevante Gameplay aktiv ist.
Liegen FiveM FPS-Drops am Server oder am Client?
Oft die des Clients. Client-FPS hängen von der GPU des Spielers ab, von seinen Grafikeinstellungen und davon, wie schwer deine client-seitigen Scripts und gestreamten Assets sind, ein Drop kann also lokal bei einem einzelnen Spieler liegen. Ein überlasteter Server zeigt sich anders: Hitch-Warnungen in der Konsole und Rubber-Banding für alle gleichzeitig. Finde heraus, was von beidem du vor dir hast, bevor du irgendetwas änderst.
Spielen überdimensionierte Fahrzeug-Texturen wirklich eine Rolle, wenn das Spiel sie ohnehin herunterskaliert?
Ja. Die Engine skaliert Texturen über etwa 1024px herunter, aber erst nachdem sie sie beim Streaming in voller Auflösung in den VRAM geladen hat. Eine 4K-Autotextur kostet dich also volle Bandbreite, Ladezeit und VRAM ohne sichtbaren Nutzen. Auf einem Server mit hunderten Add-on-Fahrzeugen sind das mehrere Gigabyte, die zu jedem Client gestreamt werden. Das Verkleinern auf rund 1024px mit passender Komprimierung ist einer der größten Einzelgewinne gegen Stutter und für Ladezeiten.
Quellen
- Cfx.re Docs: OneSync (Entity-Sync, Culling und Routing Buckets).
- Cfx.re Docs: Citizen.Wait (Tick-Timing und die No-Wait-Crash-Regel).
- oxmysql (async Datenbank-Handling für FiveM).