diff --git a/docs/de/FAQ/Clearing-mods.md b/docs/de/FAQ/Clearing-mods.md new file mode 100644 index 00000000..eb4c9a06 --- /dev/null +++ b/docs/de/FAQ/Clearing-mods.md @@ -0,0 +1,38 @@ +## Warum muss ich meine Mods deaktivieren? + +In BeamMP stellt der Server, mit dem du dich verbindest, die notwendigen Mods bereit. Diese werden beim Verbinden automatisch heruntergeladen und aktiviert. Sind lokale Mods installiert und aktiviert, funktioniert BeamMP oft nicht richtig, selbst wenn du nur einen zusätzlichen Mod neben BeamMP hast. + +!!! warning + + Entferne alle Mods, die du im Ordner BeamNG.Drive\content\ oder in Unterordnern abgelegt hast. Dieser Speicherort ist NICHT für die Installation von Mods vorgesehen und kann zu Problemen führen, wie in der Datei `DO_NOT_INSTALL_MODS_HERE.txt` im selben Verzeichnis beschrieben. + + !!! quote "DO_NOT_INSTALL_MODS_HERE.txt" + + Kopiere KEINE Mods in diesen Ordner: Dies kann zu beschädigten Mods, einer langsameren Installation von Updates, einem beschädigten Mod-Manager, einem beschädigten abgesicherten Modus und anderen Problemen führen. + +## Wie deaktiviere/entferne ich meine Mods? + +Es gibt drei Möglichkeiten, mögliche Probleme bei der Verwendung von BeamMP zu lösen. + +### 1. Mods deaktivieren + +Bevor du einem Server beitrittst, stelle sicher, dass du außer „multiplayerbeammp“ keine Mods aktiviert hast. Wenn das Spiel einfriert oder weiterhin Probleme auftreten, probiere den nächsten Schritt. + +### 2. Erstellen eines neuen Benutzerordners + +Öffne den Benutzerordner von BeamNG.Drive und benenne den Ordner der neuesten Version (z. B. 0.35) um, z. B. in 0.xx_OLD (0.35_OLD). Schließe BeamNG.drive, bevor du ihn umbenennst.
![Bild](../../assets/content/new-userfolder.png) + +### 3. Entfernen von Mods aus dem content Ordnern. + +Um auf den Ordner Beamng.drive\content\ zuzugreifen und ihn von allen Mods zu bereinigen, öffne den Installationsort von BeamNG.drive. Klicke mit der rechten Maustaste auf den Ordner `content` und lösche ihn. Überprüfe anschließend die Spieldateien über Steam oder Epic Games. Dadurch werden die Dateien erneut heruntergeladen. + +Starte anschließend BeamNG.Drive über den BeamMP-Launcher. Im Repository sollte „multiplayerbeammp“ als einziger aktivierter Mod verfügbar sein. Im Hauptmenü findest du außerdem die Schaltfläche zum Aufrufen von BeamMP. Solltest du weiterhin Probleme beim Beitritt zu gemoddeten Servern haben, liegen wahrscheinlich defekte/veraltete Mods vor. + +### 4. BeamMP-Launcher Cache bereinigen + +Um zwischengespeicherte Mods aus den BeamMP-Verzeichnissen zu entfernen, gehe zum Installationsort deines BeamMP-Launchers. Standardmäßig lautet der Pfad „C:\Benutzer\AppData\BeamMP-Launcher“. Dort findest du den Ordner „Ressourcen“. Lösche diesen Ordner, um alle zwischengespeicherten Mods zu löschen. Dies kann hilfreich sein, wenn du mehr Speicherplatz auf deiner Festplatte benötigst oder veraltete BeamNG-Mods entfernen möchtest. + +!!! question "Meine Einstellungen und Konfigurationen sind weg! Wie kann ich sie wiederherstellen?" + + Wenn du den Benutzerordner umbenannt hast, wurde das Spiel gezwungen, einen neuen, sauberen Benutzerordner zu erstellen. Du kannst die beiden Ordner „Einstellungen“ und „Fahrzeuge“ aus dem umbenannten Ordner (z. B. 0.34_OLD) in den neu erstellten Ordner kopieren. + Stelle sicher, dass BeamNG.Drive geschlossen ist, und ersetze alle Elemente am Zielort. Alle Konfigurationen und Einstellungen sollten nun wiederhergestellt sein. diff --git a/docs/de/server/create-a-server.md b/docs/de/server/create-a-server.md index e1ffc306..fe3477da 100644 --- a/docs/de/server/create-a-server.md +++ b/docs/de/server/create-a-server.md @@ -28,17 +28,22 @@ Das Aufsetzen besteht aus folgenden Schritten. Du solltest alle davon durchgehen !!! info - Wenn du auf einem VPS (Virtual Private Server), Rotoserver bist, oder planst einen Server lokal zu hosten (mit Spielern im selben Haus wie du), kannst du diesen Schritt überspringen. - Dieser Schritt ist jedoch notwendig, wenn Spieler **außerhalb** deines Haushaltes deinem heim-gehosteten Server beitreten sollen (außerhalb von deinem lokalen Netzwerk) +``` +Wenn du einen VPS (Virtual Private Server), einen Rootserver nutzt oder planst, diesen Server lokal zu hosten (mit Spielern im selben Haus wie du), kannst du diesen Schritt überspringen. +Dieser Schritt ist notwendig, wenn, jemand **außerhalb** des Haushalts einem selbst gehosteten Server beitreten soll (außerhalb Ihres lokalen Netzwerks). - !!! danger ":material-scale-balance: ACHTUNG:" - **Port forwarding ist ein Risiko**. - Wenn du Ports weiterleitest, sind dir die Risiken beim öffnen von Ports von deinem Heimnetz ins öffentliche Netz bewusst und verlierst das Recht, BeamMP für **jegliche Schäden**, welche deinem Haushalt passieren könnten, Haften zu lassen. - Wir übernehmen keine Haftung für jegliche extern gelinkten Dienste oder Webseiten. +!!! danger :material-scale-balance: HAFTUNGSAUSSCHLUSS:" -Es wird daher empfohlen, BeamMP Server mit einen unserer Partner Hosting Services zu hosten. +**Portweiterleitung birgt Risiken.** -*Sieh dir [diese Anleitung zum Port weiterleiten](port-forwarding.md) an* +Mit der Portweiterleitung erklären Sie sich mit den Risiken einverstanden, Ports in Ihrem Heimnetzwerk für die Öffentlichkeit zu öffnen, und verzichten daher auf das Recht, BeamMP für **jegliche** Schäden haftbar zu machen, die Ihnen oder Ihrem Haushalt entstehen könnten. + +Wir übernehmen keine Verantwortung für Inhalte auf extern verlinkten Diensten oder Websites. + +Es wird daher empfohlen, einen Server bei einem unserer Partnerdienste zu hosten! + +*Bitte lese [diese Anleitung zur Portweiterleitung](port-forwarding.md)* +``` #### Partner Hosting Services (bezahlt): @@ -71,8 +76,9 @@ Der Authentifizierungsschlüssel, auch “Authentication Key” oder “AuthKey !!! warning "WARNUNG" - TEILE ODER ZEIGE DEN SCHLÜSSEL NICHT. BEHANDLE IHN WIE EIN PASSWORT. - +``` + TEILE ODER ZEIGE DEN SCHLÜSSEL NICHT. BEHANDLE IHN WIE EIN PASSWORT. +``` Um einen Schlüssel zu erwerben ist ein [Discord](https://discord.com) Konto erforderlich. Das ist wichtig um gegen Spam zu schützen. @@ -141,7 +147,7 @@ Dieser Schritt funktioniert auf allen Distributionen für die wir Binaries anbie 6. Start den Server mittels `./BeamMP-Server-xxx`. Der Server erstellt automatisch die benötigten Dateien.
Wenn Text im Server Feld erscheint, kannst du diesen wieder schließen. Du solltest eine `ServerConfig.toml` neben dem `BeamMP-Server.exe` sehen. 7. (optional) Es wird empfohlen einen User namens `beammpserver` (oder ähnlich) zu erstellen, da wir empfehlen, NICHT den Server als root, sudo oder mit dem persönlichen Useraccount auszuführen. Du solltest dann die nötigen Schritte tätigen um den Server als den Server-user zu starten. -Nun springe zu Schritt "4. Konfiguration". +Fahre nun mit Schritt „4. Konfiguration“ fort. ##### Binary selbst erstellen @@ -183,9 +189,10 @@ ResourceFolder = "Resources" !!! info - Dies ist deine Konfigurationsdatei. Sie verwendet das Format TOML. Weitere Informationen zu dieser Datei und den Variablen findest du im Abschnitt [Serverwartung](server-maintenance.md). - Der Server wird **NICHT** in der Serverliste angezeigt, solange `Private = true` eingestellt ist. _Wenn_ er in der Liste angezeigt werden soll, setze die Einstellung auf **`Private = false`**. - +``` + Dies ist deine Konfigurationsdatei. Sie verwendet das Format TOML. Weitere Informationen zu dieser Datei und den Variablen findest du im Abschnitt [Serverwartung](server-maintenance.md). + Der Server wird **NICHT** in der Serverliste angezeigt, solange `Private = true` eingestellt ist. _Wenn_ er in der Liste angezeigt werden soll, setze die Einstellung auf **`Private = false`**. +``` Fürs Erste ist nur das Feld `AuthKey` relevant. Zwischen den Anführungszeichen `''` fügt man den AuthKey ein, den du im ersten Schritt kopiert hast. @@ -211,8 +218,10 @@ Fahrzeug- und Karten-Mods werden unterschiedlich installiert, müssen aber beide !!! warning - Solltest du beim Versuch, deinem Server nach dem Hinzufügen von Mods beizutreten, die Meldung „Fertig“ oder „Start“ erhalten, hast du wahrscheinlich eine inkompatible oder fehlerhafte Mod dem Server hinzugefügt. - Inkompatibilitäten zwischen zwei oder mehr Mods können ebenfalls auftreten. Falls du Client-Mods installiert hast, lies bitte [diese Anleitung](../../FAQ/How-to-deactivate-mods.md) zum Entfernen von Mods aus deinem Spiel. +``` + Solltest du beim Versuch, deinem Server nach dem Hinzufügen von Mods beizutreten, die Meldung „Fertig“ oder „Start“ erhalten, hast du wahrscheinlich eine inkompatible oder fehlerhafte Mod dem Server hinzugefügt. + Inkompatibilitäten zwischen zwei oder mehr Mods können ebenfalls auftreten. Falls du Client-Mods installiert hast, lies bitte [diese Anleitung](../../FAQ/How-to-deactivate-mods.md) zum Entfernen von Mods aus deinem Spiel. +``` #### 5.2 Allgemeine Mods @@ -272,12 +281,14 @@ Gib unten die öffentliche IPv4-Adresse und den Port des Servers ein und klicke !!! warning "Ich möchte einen VPN wie RadminVPN, Hamachi oder ähnlich verwenden" - BeamMP unterstützt diese VPNs nicht, weil diese oft Probleme verursachen. Eines davon ist das blockieren von UDP Verkehr. Um dies zu beheben, siehe Sektion 1. +``` + BeamMP unterstützt diese VPNs nicht, weil diese oft Probleme verursachen. Eines davon ist das blockieren von UDP Verkehr. Um dies zu beheben, siehe Sektion 1. - !!! question "Aber warum hat es vorher funktioniert?" - - Das passiert, weil die Entwickler dieser Applikationen updaten und Änderungen implementiere, über welche BeamMP keine Kontroll hat. - Es liegt an den Entwicklern diesen Applikationen, um Support für spezifische Fälle wie eines BeamMP-Servers zu unterstützen. + !!! question "Aber warum hat es vorher funktioniert?" + + Das passiert, weil die Entwickler dieser Applikationen updaten und Änderungen implementiere, über welche BeamMP keine Kontroll hat. + Es liegt an den Entwicklern diesen Applikationen, um Support für spezifische Fälle wie eines BeamMP-Servers zu unterstützen. +``` ## Immer noch Probleme? diff --git a/docs/de/server/port-forwarding.md b/docs/de/server/port-forwarding.md index 631c4a41..3ce0192d 100644 --- a/docs/de/server/port-forwarding.md +++ b/docs/de/server/port-forwarding.md @@ -2,17 +2,15 @@ !!! danger ":material-scale-balance: ACHTUNG" - **Das weiterleiten von Ports ist ein Risiko**. + **Das weiterleiten von Ports ist ein Risiko**. + Durch die Portweiterleitung bist du dir den Risiken bewusst, die mit der Öffnung von Ports in das öffentliche Netz verbunden sind. Daher verlierst du das Recht, BeamMP für **alle** Schäden haftbar zu machen, die dir oder deinem Haushalt entstehen können. + Wir übernehmen keine Verantwortung für Inhalte auf extern verlinkten Diensten oder Websites. + **Wenn du diese Anleitung nicht verstehst, ziehe die Nutzung eines unserer Hosting Partner in Betracht.** - Durch die Portweiterleitung bist du dir den Risiken bewusst, die mit der Öffnung von Ports in das öffentliche Netz verbunden sind. Daher verlierst du das Recht, BeamMP für **alle** Schäden haftbar zu machen, die dir oder deinem Haushalt entstehen können. + !!! warning "Warnung" - Wir übernehmen keine Verantwortung für Inhalte auf extern verlinkten Diensten oder Websites. + Bitte stelle sicher, dass dein Router kein 4G/5G-exklusives Gerät ist. Wenn es sich um ein Hybridgerät handelt, wähle später in Abschnitt 3 dieser Anleitung unbedingt den kabelgebundenen Adapter aus! - **Wenn du diese Anleitung nicht verstehst, ziehe die Nutzung eines unserer Hosting Partner in Betracht.** - -!!! warning "Warnung" - - Bitte stelle sicher, dass dein Router kein 4G/5G-exklusives Gerät ist. Wenn es sich um ein Hybridgerät handelt, wähle später in Abschnitt 3 dieser Anleitung unbedingt den kabelgebundenen Adapter aus! ## So richtest du die Portweiterleitung ein. @@ -23,41 +21,40 @@ Diese Anleitung besteht aus vier Hauptschritten. ## Eine Kurzanleitung. (Eine ausführlichere Anleitung findest du weiter unten.)
- -- :material-dns:{ .lg .middle } __Weisen Sie Ihrem Computer oder Ihren Geräten eine statische IP-Adresse zu__ - - --- - ies ist erforderlich, um zu verhindern, dass sich die IP deines Geräts ändert und die Portweiterleitung funktioniert. - - - [:octicons-arrow-right-24: Informationen zu Ihrem Router anzeigen](https://portforward.com/router.htm#1) - -- :material-router-wireless:{ .lg .middle } __Melde dich bei deinem Router an__ - - --- - - Dies kann normalerweise durch Suchen der IP des „Standard-Gateways“ erfolgen, die gefunden werden kann, wenn `ipconfig` in einer Eingabeaufforderung ausgeführt und in die Adressleiste eines Webbrowsers eingegeben wird. - -- :material-lan-connect:{ .lg .middle } __Leite Ports an deinen Computer weiter__ - - --- - - Suche in der Weboberfläche Ihres Routers nach dem Abschnitt zur Portweiterleitung. Bei den meisten Routern findet man dies unter „Netzwerk“, „Erweitert“ oder „LAN“. - -- :material-test-tube:{ .lg .middle } __Teste, ob dein Port richtig weitergeleitet wird__ - - --- - - Verwenden Sie ein Tool wie CheckBeamMP, um zu testen, ob die Regel funktioniert. - -
- -
- -
- -
+ +
## Die ausführliche Anleitung @@ -73,18 +70,14 @@ Wenn du dies geschafft hast, fahre direkt mit [Schritt 2](port-forwarding.md#2-l #### 1.1. Finde deine aktuelle IP-Adresse, Gateway und DNS-Server: -Bevor wir eine statische IP-Adresse einrichten können, benötigen wir deine aktuellen Netzwerkeinstellungen. -Notiere diese, halte also ein Notepadfenster bereit. -Für diesen Schritt verwenden wir die Eingabeaufforderung. +Bevor wir eine statische IP-Adresse einrichten können, benötigen wir deine aktuellen Netzwerkeinstellungen. Notiere diese, halte also ein Notepadfenster bereit. Für diesen Schritt verwenden wir die Eingabeaufforderung. Öffne die Eingabeaufforderung. Die drei wichtigsten Möglichkeiten sind: - Drücke die Windows-Taste, gebe dann „cmd“ ein und drücke die Eingabetaste, wenn „Eingabeaufforderung“ hervorgehoben ist. -
-![](../../assets/content/win11-open-cmd.png) -
+
![](../../assets/content/win11-open-cmd.png)
Sobald du dich in der Eingabeaufforderung befindest, führe den folgenden Befehl aus: @@ -92,31 +85,22 @@ Sobald du dich in der Eingabeaufforderung befindest, führe den folgenden Befehl ipconfig /all ``` -Du wirst eine Menge an Daten sehen. -Wenn du virtuelle oder mehrere Netzwerkadapter verwendest, werden noch mehr Daten angezeigt. -Bei der Installation von Hyper-V oder Docker werden häufig viele virtuelle Adapter angezeigt. +Du wirst eine Menge an Daten sehen. Wenn du virtuelle oder mehrere Netzwerkadapter verwendest, werden noch mehr Daten angezeigt. Bei der Installation von Hyper-V oder Docker werden häufig viele virtuelle Adapter angezeigt. -
-![](../../assets/content/win11-command-prompt-ipconfig-highlighted.png) -
-Es wird empfohlen, für den Betrieb dieses Servers eine kabelgebundene Netzwerkverbindung zu verwenden. Eine drahtlose Verbindung funktioniert jedoch auch. -Suche in dieser Liste nach einem Adapter mit aktiver Internetverbindung. Scrolle durch die Liste und suche einen Adapter mit zugewiesenem Standard-Gateway. -Viele virtuelle Adapter verfügen über kein Standard-Gateway. +
![](../../assets/content/win11-command-prompt-ipconfig-highlighted.png)
-Nachfolgend findest du Beispiele für lokale IPv4-Adressen, die mindestens einer der Adapter haben sollte. -Notiere dir die Informationen deines Adapters. +Es wird empfohlen, für den Betrieb dieses Servers eine kabelgebundene Netzwerkverbindung zu verwenden. Eine drahtlose Verbindung funktioniert jedoch auch. Suche in dieser Liste nach einem Adapter mit aktiver Internetverbindung. Scrolle durch die Liste und suche einen Adapter mit zugewiesenem Standard-Gateway. Viele virtuelle Adapter verfügen über kein Standard-Gateway. + +Nachfolgend findest du Beispiele für lokale IPv4-Adressen, die mindestens einer der Adapter haben sollte. Notiere dir die Informationen deines Adapters. - 192.168.xx - 10.xxx - 172.16.xx – 172.31.xx -Subnetzmaske (höchstwahrscheinlich 255.255.255.0) -
-Standard-Gateway (höchstwahrscheinlich 192.168.0.1 oder 192.168.1.1) +Subnetzmaske (höchstwahrscheinlich 255.255.255.0) Standard-Gateway (höchstwahrscheinlich 192.168.0.1 oder 192.168.1.1) -!!! Info „Beachte“ - BeamMP unterstützt derzeit kein IPv6 zum Hosten eines Servers. +!!! Info „Beachte“ BeamMP unterstützt derzeit kein IPv6 zum Hosten eines Servers. #### 1.2. Adaptereinstellungen ändern @@ -127,24 +111,17 @@ Nun müssen wir die Einstellungen deines Netzwerkadapters ändern, damit dein P - Drücke die Eingabetaste -
-![](../../assets/content/win11-start-menu-view-network-connections.png) -
+
![](../../assets/content/win11-start-menu-view-network-connections.png)
-Du solltest eine Liste der Netzwerkverbindungen auf deinem Computer sehen. -Wenn du einen Hyper-V oder Docker installiert hast, kann es viele davon geben. Suche nach Adaptern, die nicht „Hyper-V“ heißen. +Du solltest eine Liste der Netzwerkverbindungen auf deinem Computer sehen. Wenn du einen Hyper-V oder Docker installiert hast, kann es viele davon geben. Suche nach Adaptern, die nicht „Hyper-V“ heißen. -
-![](../../assets/content/win11-network-connections.png) -
+
![](../../assets/content/win11-network-connections.png)
Klicke mit der rechten Maustaste auf deinen Adapter und wähle Eigenschaften. Wenn `Internet Protocol Version 4` nicht aktiviert ist, handelt es sich um den falschen Adapter. Wähle einen anderen. -
-![](../../assets/content/win11-ethernet-properties-highlighted.png) -
+
![](../../assets/content/win11-ethernet-properties-highlighted.png)
Doppelklicken Sie auf `Internet Protocol Version 4` Ändern `Obtain an IP address automatically` beziehen in `Use the following IP address` . @@ -156,9 +133,7 @@ Alternativ kannst du anstelle deines DNS-Servers entweder die CloudFlare- oder G - Google DNS: 8.8.8.8, 8.8.4.4 -
-![](../../assets/content/win11-network-settings-static-ip.png) -
+
![](../../assets/content/win11-network-settings-static-ip.png)
Klicke auf „OK“ und dann erneut auf „OK“. Dein Adapter ist nun von DHCP auf statisch umgestellt. Surfe im Internet, um sicherzustellen, dass du weiterhin eine Internetverbindung hast. Falls nicht, ändere deine Einstellungen wieder auf „IP-Adresse automatisch beziehen“ und versuche die nächste Methode. @@ -177,12 +152,12 @@ Du solltest nun den Anmeldebildschirm deines Routers sehen. Nicht alle Router er Einige der gebräuchlichsten werkseitigen Benutzernamen und Passwörter sind hier aufgeführt: -| Username | Passwort | -| -------- | --- | -| admin | admin | -| admin | password | -| {leer} | admin | -| {leer} | password | +Username | Passwort +--- | --- +admin | admin +admin | password +{leer} | admin +{leer} | password Probiere verschiedene Kombinationen aus „Administrator“, „Passwort“ und lasse die Einträge leer. *Lasse den Wert bei „Leer“ leer.* @@ -202,17 +177,11 @@ Suche den Abschnitt zur Portweiterleitung in der Weboberfläche deines Routers. #### 3.2. Gebe die Details ein -Sobald du den Bereich für die Portweiterleitung gefunden hast, kannst du die erforderlichen Informationen eingeben. -Dein Router bietet dir die Möglichkeit, die weiterzuleitenden Ports und die Ziel-IP-Adresse für diese Portweiterleitung einzugeben. Wenn dein Router sowohl interne als auch externe Ports angibt, achte darauf, dass diese identisch sind. +Sobald du den Bereich für die Portweiterleitung gefunden hast, kannst du die erforderlichen Informationen eingeben. Dein Router bietet dir die Möglichkeit, die weiterzuleitenden Ports und die Ziel-IP-Adresse für diese Portweiterleitung einzugeben. Wenn dein Router sowohl interne als auch externe Ports angibt, achte darauf, dass diese identisch sind. BeamMP erfordert sowohl UDP- als auch TCP-Port 30814 (es sei denn, du hast diesen in deiner [ServerConfig.toml](create-a-server.md#4-configuration) geändert). -!!!! info "Hinweis" - Der **Standardport** ist **30814.** Du kannst aber auch andere Ports größer als 1024 und kleiner als 65535 wählen. Notiere dir deine Wahl, falls diese nicht 30814 ist. Du musst sowohl **TCP** als auch **UDP** weiterleiten. -
- Es wird empfohlen, den Standardport beizubehalten, da dieser höchstwahrscheinlich nicht von einem anderen Dienst auf Ihrem PC verwendet wird. -
- Wenn du jedoch mehrere Server auf einem Rechner hostest, benötigt jeder Server einen anderen Port. Server 1: 30814, Server 2: 30815 zum Beispiel. +!!!! info "Hinweis" Der **Standardport** ist **30814.** Du kannst aber auch andere Ports größer als 1024 und kleiner als 65535 wählen. Notiere dir deine Wahl, falls diese nicht 30814 ist. Du musst sowohl **TCP** als auch **UDP** weiterleiten. Es wird empfohlen, den Standardport beizubehalten, da dieser höchstwahrscheinlich nicht von einem anderen Dienst auf Ihrem PC verwendet wird. Wenn du jedoch mehrere Server auf einem Rechner hostest, benötigt jeder Server einen anderen Port. Server 1: 30814, Server 2: 30815 zum Beispiel. Auf einigen Routern muss man möglicherweise zwei Regeln erstellen, eine für UDP und eine für TCP, während andere praktisch sind und dir beides mit einer einzigen Regel ermöglichen! @@ -225,7 +194,7 @@ Es gibt verschiedene Möglichkeiten, die Verbindung zu testen. Wir empfehlen die Verwendung unseres Tools **CheckBeamMP**, da dieses auf BeamMP-spezifische Probleme und Protokolle testet.
- +

@@ -234,17 +203,17 @@ Wir empfehlen die Verwendung unseres Tools **CheckBeamMP**, da dieses auf BeamMP Dies kann durch die Abfrage deiner öffentlichen IPv4-Adresse erfolgen. Auch hierfür gibt es verschiedene Möglichkeiten. Die häufigste Methode ist die Nutzung der Website [whatsmyip.org](https://whatsmyip.org/) . Diese einfache Website zeigt deine öffentliche IP-Adresse an. Du solltest nach einer IP-Adresse im Format xxx.xxx.xxx.xxx suchen. -Besuche den folgenden Link und ersetzen Sie "IP" durch deine tatsächliche IPv4-Adresse und "Port" durch den Port deines Servers. Achte darauf, keine Leerzeichen zu hinterlassen. -https://check.beammp.com/api/v2/beammp/ip/port +Besuche den folgenden Link und ersetzen Sie "IP" durch deine tatsächliche IPv4-Adresse und "Port" durch den Port deines Servers. Achte darauf, keine Leerzeichen zu hinterlassen. https://check.beammp.com/api/v2/beammp/ip/port !!! success "status: ok" - Wenn du die obige Ausgabe erhältst, kannst du des jetzt deinem Server beitreten! - Es gibt zwei Möglichkeiten, beizutreten: entweder direkt mit den Daten, die du in probablyup eingegeben hast, oder, wenn dein Server auf „öffentlich“ eingestellt ist, über die Serverliste. - Da du einen Server vor Ort hostest, verwenden 127.0.0.1 (localhost), wenn der Server auf demselben PC läuft, auf dem du spielst, oder die LAN-IPv4 des lokalen Computers, auf dem der Server läuft. + Wenn du die obige Ausgabe erhältst, kannst du des jetzt deinem Server beitreten! + Es gibt zwei Möglichkeiten, beizutreten: entweder direkt mit den Daten, die du in probablyup eingegeben hast, oder, wenn dein Server auf „öffentlich“ eingestellt ist, über die Serverliste. + Da du einen Server vor Ort hostest, verwenden 127.0.0.1 (localhost), wenn der Server auf demselben PC läuft, auf dem du spielst, oder die LAN-IPv4 des lokalen Computers, auf dem der Server läuft. !!! failure "status: error" - Wenn die Verbindung vollständig fehlschlägt, verwendet dein Anbieter möglicherweise CGNAT (Carrier Grade Network Address Translation). Weitere Informationen findest du unter [Wie kann ich auf CGNAT prüfen?](../FAQ/How-to-check-for-CGNAT.md), - oder öffne ein Server-Support-Ticket auf unserem [Discord-Server](https://discord.gg/beammp) im Kanal „#support“ und einer unserer Mitarbeiter wird sich um dein Ticket kümmern! - Wenn du nur siehst, dass TCP funktioniert und UDP fehlschlägt, überprüfe die Firewall- und Portweiterleitungsregeln erneut. + Wenn die Verbindung vollständig fehlschlägt, verwendet dein Anbieter möglicherweise CGNAT (Carrier Grade Network Address Translation). Weitere Informationen findest du unter [Wie kann ich auf CGNAT prüfen?](../FAQ/How-to-check-for-CGNAT.md), + oder öffne ein Server-Support-Ticket auf unserem [Discord-Server](https://discord.gg/beammp) im Kanal „#support“ und einer unserer Mitarbeiter wird sich um dein Ticket kümmern! + Wenn du nur siehst, dass TCP funktioniert und UDP fehlschlägt, überprüfe die Firewall- und Portweiterleitungsregeln erneut. + diff --git a/docs/en/FAQ/march-28-outage.md b/docs/en/FAQ/march-28-outage.md deleted file mode 100644 index 0b0d42ef..00000000 --- a/docs/en/FAQ/march-28-outage.md +++ /dev/null @@ -1,30 +0,0 @@ -# March 28, 2026 BeamMP Outage FAQ - -Temporary FAQ for the (currently) ongoing BeamMP outage that started on March 28th, 2026. - -**Last updated April 1st, 2026.** - -=== Help! My BeamMP launcher doesn't work! - Try reinstalling the BeamMP launcher. To do so, follow the instructions below: - 1. Go to [beammp.com](https://beammp.com/) - 2. Click *Download Now* - 3. Run the installer and follow instructions - - !!! note - - As of April 1st, 2026, the MSI installer is an "unrecognized app" according to Windows Defender SmartScreen. - - To bypass this warning, click *More info*, then click *Run anyway*. - -=== Help! My authkey(s) don't work anymore! - As of April 1st, 2026, keymaster and auth systems are offline. This means that your authkeys won't work. To get around this, follow the instructions below: - 1. Open your `ServerConfig.toml`, or wherever your server config is modified - 2. Set `Private` to `true`. It should look like this: `Private = true` - 3. This should fix the authkey issue. - - !!! note - - As of April 1st, 2026, BeamMP's auth systems are offline. Only guest accounts are available. - - Make sure your server allows guests. - diff --git a/docs/en/beamng/css-snippets.md b/docs/en/beamng/css-snippets.md index 6d0a6a9a..a941961c 100644 --- a/docs/en/beamng/css-snippets.md +++ b/docs/en/beamng/css-snippets.md @@ -10,170 +10,266 @@ ## Common variables -=== BeamNG Orange +=== "BeamNG CEF Orange" - ```css - var(--bng-orange) /*Common orange*/ - var(--bng-orange-shade1) /*70% opacity*/ - var(--bng-orange-shade2) /*40% opacity*/ - var(--bng-orange-shade1opaque) - var(--bng-orange-shade2opaque) - ``` + ```css + var(--bng-orange) /*Common orange*/ + var(--bng-orange-shade1) /*70% opacity*/ + var(--bng-orange-shade2) /*40% opacity*/ + var(--bng-orange-shade1opaque) + var(--bng-orange-shade2opaque) + ``` -=== Monochrome +=== "Monochrome" - ```css - --- Monochrome - var(--bng-black-8) /*80% opacity (duplicate --bng-black-o8)*/ - var(--bng-black-6) /*60% opacity (duplicate --bng-black-o6)*/ - var(--bng-black-4) /*40% opacity (duplicate --bng-black-o4)*/ - var(--bng-black-2) /*20% opacity (duplicate --bng-black-o2)*/ + ```css + --- Monochrome + var(--bng-black-8) /*80% opacity (duplicate --bng-black-o8)*/ + var(--bng-black-6) /*60% opacity (duplicate --bng-black-o6)*/ + var(--bng-black-4) /*40% opacity (duplicate --bng-black-o4)*/ + var(--bng-black-2) /*20% opacity (duplicate --bng-black-o2)*/ - var(--dark-neutral-grey) - var(--neutral-grey) - var(--light-neutral-grey) - var(--dark-grey) - var(--dark-grey-alpha) /*80% opacity*/ + var(--dark-neutral-grey) + var(--neutral-grey) + var(--light-neutral-grey) + var(--dark-grey) + var(--dark-grey-alpha) /*80% opacity*/ - var(--black-1) /*70% opacity*/ - var(--black-2) /*40% opacity (duplicate --bng-black-o4)*/ + var(--black-1) /*70% opacity*/ + var(--black-2) /*40% opacity (duplicate --bng-black-o4)*/ - var(--white-1) /*80% opacity*/ - var(--white-2) /*40% opacity*/ - var(--white-3) /*20% opacity*/ - ``` + var(--white-1) /*80% opacity*/ + var(--white-2) /*40% opacity*/ + var(--white-3) /*20% opacity*/ + ``` -=== BeamNG UI Color Palette +=== "BeamNG Vue UI Color Palette" - === Orange + All of these support adding `-rgb` to the end of the variable name to convert them to raw red, green, blue values. Use -rgb like so: `rgba(var(--bng-orange-500-rgb), 0.5)` for 50% opacity bng-orange-500. - ```css - var(--bng-orange-50) - var(--bng-orange-100) - var(--bng-orange-200) - var(--bng-orange-300) - var(--bng-orange-b400) - var(--bng-orange-500) - var(--bng-orange-600) - var(--bng-orange-700) - var(--bng-orange-800) - var(--bng-orange-900) - ``` - - === Cool Gray + === "Add Red" - ```css - var(--bng-cool-gray-50) - var(--bng-cool-gray-100) - var(--bng-cool-gray-200) - var(--bng-cool-gray-300) - var(--bng-cool-gray-400) - var(--bng-cool-gray-500) - var(--bng-cool-gray-600) - var(--bng-cool-gray-700) - var(--bng-cool-gray-800) - var(--bng-cool-gray-900) - ``` - - === Ter Blue - ```css - var(--bng-ter-blue-50) - var(--bng-ter-blue-100) - var(--bng-ter-blue-200) - var(--bng-ter-blue-300) - var(--bng-ter-blue-400) - var(--bng-ter-blue-500) - var(--bng-ter-blue-600) - var(--bng-ter-blue-700) - var(--bng-ter-blue-800) - var(--bng-ter-blue-900) - ``` - - === Add Blue - ```css - var(--bng-add-blue-50) - var(--bng-add-blue-100) - var(--bng-add-blue-200) - var(--bng-add-blue-300) - var(--bng-add-blue-400) - var(--bng-add-blue-500) - var(--bng-add-blue-600) - var(--bng-add-blue-700) - var(--bng-add-blue-800) - var(--bng-add-blue-900) - ``` - - === Add Green - ```css - var(--bng-add-green-50) - var(--bng-add-green-100) - var(--bng-add-green-200) - var(--bng-add-green-300) - var(--bng-add-green-400) - var(--bng-add-green-500) - var(--bng-add-green-600) - var(--bng-add-green-700) - var(--bng-add-green-800) - var(--bng-add-green-900) - ``` - - === Add Yellow - ```css - var(--bng-add-yellow-50) - var(--bng-add-yellow-100) - var(--bng-add-yellow-200) - var(--bng-add-yellow-300) - var(--bng-add-yellow-400) - var(--bng-add-yellow-500) - var(--bng-add-yellow-600) - var(--bng-add-yellow-700) - var(--bng-add-yellow-800) - var(--bng-add-yellow-900) - ``` - - === Add Peach - ```css - var(--bng-add-peach-50) - var(--bng-add-peach-100) - var(--bng-add-peach-200) - var(--bng-add-peach-300) - var(--bng-add-peach-400) - var(--bng-add-peach-500) - var(--bng-add-peach-600) - var(--bng-add-peach-700) - var(--bng-add-peach-800) - var(--bng-add-peach-900) - ``` + ```css + var(--bng-add-red-50) + var(--bng-add-red-100) + var(--bng-add-red-200) + var(--bng-add-red-300) + var(--bng-add-red-400) + var(--bng-add-red-500) + var(--bng-add-red-550) + var(--bng-add-red-600) + var(--bng-add-red-650) + var(--bng-add-red-700) + var(--bng-add-red-750) + var(--bng-add-red-800) + var(--bng-add-red-850) + var(--bng-add-red-900) + ``` + + === "Orange" - === Add Red - ```css - var(--bng-add-red-50) - var(--bng-add-red-100) - var(--bng-add-red-200) - var(--bng-add-red-300) - var(--bng-add-red-400) - var(--bng-add-red-500) - var(--bng-add-red-600) - var(--bng-add-red-700) - var(--bng-add-red-800) - var(--bng-add-red-900) - ``` - + ```css + var(--bng-orange-50) + var(--bng-orange-100) + var(--bng-orange-200) + var(--bng-orange-300) + var(--bng-orange-400) + var(--bng-orange-500) + var(--bng-orange-550) + var(--bng-orange-600) + var(--bng-orange-650) + var(--bng-orange-700) + var(--bng-orange-750) + var(--bng-orange-800) + var(--bng-orange-850) + var(--bng-orange-900) + ``` + + === "Ter Peach" -=== Extra color presets + ```css + var(--bng-ter-peach-50) + var(--bng-ter-peach-100) + var(--bng-ter-peach-200) + var(--bng-ter-peach-300) + var(--bng-ter-peach-400) + var(--bng-ter-peach-500) + var(--bng-ter-peach-550) + var(--bng-ter-peach-600) + var(--bng-ter-peach-650) + var(--bng-ter-peach-700) + var(--bng-ter-peach-750) + var(--bng-ter-peach-800) + var(--bng-ter-peach-850) + var(--bng-ter-peach-900) + ``` + + === "Ter Yellow" - ```css - var(--bng-filter-orange) /*Filter preset to force SVGs to use bng-orange*/ - var(--bng-black-o8) /*80% opacity*/ - var(--bng-black-o6) /*60% opacity*/ - var(--bng-black-o4) /*40% opacity*/ - var(--bng-black-o2) /*20% opacity*/ - ``` + ```css + var(--bng-ter-yellow-50) + var(--bng-ter-yellow-100) + var(--bng-ter-yellow-200) + var(--bng-ter-yellow-300) + var(--bng-ter-yellow-400) + var(--bng-ter-yellow-500) + var(--bng-ter-yellow-550) + var(--bng-ter-yellow-600) + var(--bng-ter-yellow-650) + var(--bng-ter-yellow-700) + var(--bng-ter-yellow-750) + var(--bng-ter-yellow-800) + var(--bng-ter-yellow-850) + var(--bng-ter-yellow-900) + ``` + + === "Add Green" -=== Corner rounding presets + ```css + var(--bng-add-green-50) + var(--bng-add-green-100) + var(--bng-add-green-200) + var(--bng-add-green-300) + var(--bng-add-green-400) + var(--bng-add-green-500) + var(--bng-add-green-600) + var(--bng-add-green-700) + var(--bng-add-green-800) + var(--bng-add-green-900) + ``` + + === "Baby Blue" - ```css - var(--bng-corners-1) /*0.25rem*/ - var(--bng-corners-2) /*0.50rem*/ - var(--bng-corners-3) /*1.00rem*/ - ``` + ```css + var(--bng-add-babyblue-50) + var(--bng-add-babyblue-100) + var(--bng-add-babyblue-200) + var(--bng-add-babyblue-300) + var(--bng-add-babyblue-400) + var(--bng-add-babyblue-500) + var(--bng-add-babyblue-550) + var(--bng-add-babyblue-600) + var(--bng-add-babyblue-650) + var(--bng-add-babyblue-700) + var(--bng-add-babyblue-750) + var(--bng-add-babyblue-800) + var(--bng-add-babyblue-850) + var(--bng-add-babyblue-900) + ``` + + === "Add Blue" + + ```css + var(--bng-add-blue-50) + var(--bng-add-blue-100) + var(--bng-add-blue-200) + var(--bng-add-blue-300) + var(--bng-add-blue-400) + var(--bng-add-blue-500) + var(--bng-add-blue-600) + var(--bng-add-blue-700) + var(--bng-add-blue-800) + var(--bng-add-blue-900) + ``` + + === "Indigo Blue" + + ```css + var(--bng-add-indigoblue-50) + var(--bng-add-indigoblue-100) + var(--bng-add-indigoblue-200) + var(--bng-add-indigoblue-300) + var(--bng-add-indigoblue-400) + var(--bng-add-indigoblue-500) + var(--bng-add-indigoblue-550) + var(--bng-add-indigoblue-600) + var(--bng-add-indigoblue-650) + var(--bng-add-indigoblue-700) + var(--bng-add-indigoblue-750) + var(--bng-add-indigoblue-800) + var(--bng-add-indigoblue-850) + var(--bng-add-indigoblue-900) + ``` + + === "Add Magenta" + + ```css + var(--bng-add-magenta-50) + var(--bng-add-magenta-100) + var(--bng-add-magenta-200) + var(--bng-add-magenta-300) + var(--bng-add-magenta-400) + var(--bng-add-magenta-500) + var(--bng-add-magenta-550) + var(--bng-add-magenta-600) + var(--bng-add-magenta-650) + var(--bng-add-magenta-700) + var(--bng-add-magenta-750) + var(--bng-add-magenta-800) + var(--bng-add-magenta-850) + var(--bng-add-magenta-900) + ``` + + === "Ter Blue Gray" + + ```css + var(--bng-ter-blue-gray-50) + var(--bng-ter-blue-gray-100) + var(--bng-ter-blue-gray-200) + var(--bng-ter-blue-gray-300) + var(--bng-ter-blue-gray-400) + var(--bng-ter-blue-gray-500) + var(--bng-ter-blue-gray-550) + var(--bng-ter-blue-gray-600) + var(--bng-ter-blue-gray-650) + var(--bng-ter-blue-gray-700) + var(--bng-ter-blue-gray-750) + var(--bng-ter-blue-gray-800) + var(--bng-ter-blue-gray-850) + var(--bng-ter-blue-gray-900) + ``` + + === "Cool Gray" + + ```css + var(--bng-cool-gray-50) + var(--bng-cool-gray-100) + var(--bng-cool-gray-200) + var(--bng-cool-gray-300) + var(--bng-cool-gray-400) + var(--bng-cool-gray-500) + var(--bng-cool-gray-550) + var(--bng-cool-gray-600) + var(--bng-cool-gray-650) + var(--bng-cool-gray-700) + var(--bng-cool-gray-750) + var(--bng-cool-gray-800) + var(--bng-cool-gray-850) + var(--bng-cool-gray-900) + ``` + + === "Other" + + ```css + var(--bng-off-black) /*Used in Vue for buttons and some headers*/ + var(--bng-off-white) /*Used in Vue for interactable elements*/ + var(--bng-off-white-brighter) /*Used in Vue for headers*/ + ``` + +=== "Extra color presets" + + ```css + var(--bng-filter-orange) /*Filter preset to force SVGs to use bng-orange*/ + var(--bng-black-o8) /*80% opacity*/ + var(--bng-black-o6) /*60% opacity*/ + var(--bng-black-o4) /*40% opacity*/ + var(--bng-black-o2) /*20% opacity*/ + ``` + +=== "Corner rounding presets" + + ```css + var(--bng-corners-1) /*0.25rem*/ + var(--bng-corners-2) /*0.50rem*/ + var(--bng-corners-3) /*1.00rem*/ + ``` diff --git a/docs/en/beamng/imgui-snippets.md b/docs/en/beamng/imgui-snippets.md index 69464e3a..15524201 100644 --- a/docs/en/beamng/imgui-snippets.md +++ b/docs/en/beamng/imgui-snippets.md @@ -31,47 +31,47 @@ im.End() ## General -=== Basic Formatting +=== "Basic Formatting" - ```lua - im.Text("") - im.TextWrapped("") -- automatic word wrap - im.TextColored(im.ImVec4(0,1,0,1), "") -- R,G,B,A - im.TextDisabled("") -- predefined style for disabled text + ```lua + im.Text("") + im.TextWrapped("") -- automatic word wrap + im.TextColored(im.ImVec4(0,1,0,1), "") -- R,G,B,A + im.TextDisabled("") -- predefined style for disabled text - im.LabelText("", "") - im.BulletText("") -- Bullet point with text - im.SeparatorText("") -- Separator with centered text + im.LabelText("", "") + im.BulletText("") -- Bullet point with text + im.SeparatorText("") -- Separator with centered text - im.Separator() -- might want a NewLine before these - im.SameLine() -- horizontally append the following element to the previous element - im.NewLine() + im.Separator() -- might want a NewLine before these + im.SameLine() -- horizontally append the following element to the previous element + im.NewLine() - im.Spacing() -- small padding - im.Indent() - im.Unindent() - ``` + im.Spacing() -- small padding + im.Indent() + im.Unindent() + ``` -=== Inputs +=== "Inputs" - ```lua - im.Button("", im.ImVec2(0,0)) -- 0 = fit to content - im.SmallButton("") -- Fit to content and slightly less padding - im.ArrowButton("", 0) -- arg 1: string is not actually used? arg 2: 0 = left, 1 = right, 2 = up, 3 = down - im.InvisibleButton("", im.ImVec2(0,0), ...) -- used for imgui cursor positioning? + ```lua + im.Button("", im.ImVec2(0,0)) -- 0 = fit to content + im.SmallButton("") -- Fit to content and slightly less padding + im.ArrowButton("", 0) -- arg 1: string is not actually used? arg 2: 0 = left, 1 = right, 2 = up, 3 = down + im.InvisibleButton("", im.ImVec2(0,0), ...) -- used for imgui cursor positioning? - im.Checkbox("", im.BoolPtr(false)) + im.Checkbox("", im.BoolPtr(false)) - im.RadioButton1("", im.BoolPtr(false)) - im.RadioButton2("", im.IntPtr(), 0) -- arg. 3: 0 or 1 for disabled or enabled - ``` + im.RadioButton1("", im.BoolPtr(false)) + im.RadioButton2("", im.IntPtr(), 0) -- arg. 3: 0 or 1 for disabled or enabled + ``` -=== Other +=== "Other" - ```lua - im.Bullet() + ```lua + im.Bullet() - im.ProgressBar(0.5, im.ImVec2(0,0), "") -- arg 2: 0 for default width and/or height + im.ProgressBar(0.5, im.ImVec2(0,0), "") -- arg 2: 0 for default width and/or height - im.TextUnformatted("", "") -- Second argument seems to crash the game - ``` + im.TextUnformatted("", "") -- Second argument seems to crash the game + ``` diff --git a/docs/en/beamng/lua-snippets.md b/docs/en/beamng/lua-snippets.md index 41a0c80e..a4c0ccc4 100644 --- a/docs/en/beamng/lua-snippets.md +++ b/docs/en/beamng/lua-snippets.md @@ -347,4 +347,35 @@ If multiple pages are provided, or the hook is triggered multiple times, then th ### Dialogue -todo +Dialogue is used in the *A Rocky Start* campaign to display information about a mission. It is a centered, vertically aligned popup with a specific layout. It does not support embedding HTML. + +```lua +ui_missionInfo.openDialogue({ + title = "Dialogue title", + type = "Custom", -- isn't actually displayed + typeName = "typeName", + data = { + {label = "objective", value = "reward"} + -- add more... + }, + buttons = { + {action = "accept", text = "Accept", cmd = ""}, + {action = 'decline',text = "Decline", cmd = ""} + -- add more... + } +}) + +ui_missionInfo.closeDialogue() +``` + +
+ ![The Dialogue snippet displayed in BeamNG.drive](../../assets/content/Dialogue.png) +
+ +Only one Dialogue can be displayed at once. Any existing Dialogue is overridden. + +!!! info + + `#!lua ui_missionInfo.closeDialogue()` must be used to close a dialogue. + + Make sure you call this function when any button is pressed. diff --git a/docs/en/beamng/snippets.md b/docs/en/beamng/snippets.md deleted file mode 100644 index e5dea764..00000000 --- a/docs/en/beamng/snippets.md +++ /dev/null @@ -1,391 +0,0 @@ -!!! warning "This site is under construction!" - - This site is being actively worked on. - - Feel you could help? Please do by clicking on the page with a pencil on the right! - - This can be done any page too. - -# BeamNG.drive Code Snippets - -## Lua Code Snippets - -### World - -#### Drawing a marker & Vehicle detection - -Drawing markers in the map can be one of the best ways to indicate to the user that there is some form of interaction that they can do there. - -Drawing a marker is fairly easy. Here is an example of how the bus route marker is drawn: - -```lua - local function createBusMarker(markerName) - local marker = createObject('TSStatic') - marker:setField('shapeName', 0, "art/shapes/interface/position_marker.dae") - marker:setPosition(vec3(0, 0, 0)) - marker.scale = vec3(1, 1, 1) - marker:setField('rotation', 0, '1 0 0 0') - marker.useInstanceRenderData = true - marker:setField('instanceColor', 0, '1 1 1 0') - marker:setField('collisionType', 0, "Collision Mesh") - marker:setField('decalType', 0, "Collision Mesh") - marker:setField('playAmbient', 0, "1") - marker:setField('allowPlayerStep', 0, "1") - marker:setField('canSave', 0, "0") - marker:setField('canSaveDynamicFields', 0, "1") - marker:setField('renderNormals', 0, "0") - marker:setField('meshCulling', 0, "0") - marker:setField('originSort', 0, "0") - marker:setField('forceDetail', 0, "-1") - marker.canSave = false - marker:registerObject(markerName) - scenetree.MissionGroup:addObject(marker) - return marker - end - - -- this can then be called in a loop to setup your markers. - -- NOTE: You should only do this once as part of your setup and not called on each frame. - if #markers == 0 then - for k,v in pairs(nameMarkers) do - local mk = scenetree.findObject(v) - if mk == nil then - log('I', logTag,'Creating marker '..tostring(v)) - mk = createBusMarker(v) - ScenarioObjectsGroup:addObject(mk.obj) - end - table.insert(markers, mk) - end - end -``` - -Here is a custom marker example from [BeamNG-FuelStations](https://github.com/BeamMP/BeamNG-FuelStations/tree/master): - -```lua - local stations = [ - { "location": [ -778.813, 485.973, 23.46 ], "type":"gas" }, - { "location": [ 617.164, -192.107, 53.2 ], "type":"ev" }, - ] - - local function IsEntityInsideArea(pos1, pos2, radius) - return pos1:distance(pos2) < radius - end - - local onUpdate = function (dt) - for k, spot in pairs(stations) do -- loop through all spots on the current map - local bottomPos = vec3(spot.location[1], spot.location[2], spot.location[3]) - local topPos = bottomPos + vec3(0,0,2) -- offset vec to get top position (2m tall) - - local spotInRange = false -- is this spot in range? used for color - local spotCompatible = false -- is this spot compatible? - - if activeVeh then -- we have a car and its ours (if in mp) - local vehPos = activeVeh:getPosition() - - spotInRange = IsEntityInsideArea(vec3(vehPos.x, vehPos.y,vehPos.z), bottomPos, 1.5) - - spotCompatible = activeFuelType == "any" or spot.type == "any" or activeFuelType == spot.type - end - - local spotColor = (spotInRange and spotCompatible) and activeColorMap[spot.type] or inactiveColorMap[spot.type] or ColorF(1,1,1,0.5) - - debugDrawer:drawCylinder(bottomPos:toPoint3F(), topPos:toPoint3F(), 1, spotColor) --bottom, top, radius, color - end - end -``` - -### UI snippets - -#### Toast Notifications, Top right of screen - -
- ![image](https://github.com/StanleyDudek/Docs/assets/49531350/c8a87842-b95a-4eca-84dc-93072ecc9158) -
- -```lua ---guihooks.trigger('toastrMsg', {type, title, msg, config = {timeOut}}) -guihooks.trigger('toastrMsg', {type = "info", title = "Info Message:", msg = "Info Message Text Here", config = {timeOut = 5000}}) -guihooks.trigger('toastrMsg', {type = "warning", title = "Warning Message:", msg = "Warning Message Text Here", config = {timeOut = 5000}}) -guihooks.trigger('toastrMsg', {type = "error", title = "Error Message:", msg = "Error Message Text Here", config = {timeOut = 5000}}) -``` - -#### Message notifications, top left of screen by default in Messages app - -This requires the 'Messages' or 'Messages & Tasks' UI app. Icons can be found at `ui\ui-vue\src\assets\fonts\bngIcons\svg\` - -
- ![image](https://github.com/StanleyDudek/Docs/assets/49531350/6baef813-50cb-43c3-9c59-0de550b014b6) -
- -```lua ---guihooks.trigger('Message', {msg, ttl, category, icon}) ---ui_message(msg, ttl, category, icon) -guihooks.trigger('Message', {msg = "Message Text Here", ttl = 5.0, category = "arrow_upward", icon = "arrow_upward"}) -guihooks.trigger('Message', {msg = "Message Text Here", ttl = 5.0, category = "arrow_downward", icon = "arrow_downward"}) -guihooks.trigger('Message', {msg = "Message Text Here", ttl = 5.0, category = "flag", icon = "flag"}) -guihooks.trigger('Message', {msg = "Message Text Here", ttl = 5.0, category = "check", icon = "check"}) -guihooks.trigger('Message', {msg = "Message Text Here", ttl = 5.0, category = "check_circle", icon = "check_circle"}) -guihooks.trigger('Message', {msg = "Message Text Here", ttl = 5.0, category = "warning", icon = "warning"}) -guihooks.trigger('Message', {msg = "Message Text Here", ttl = 5.0, category = "error", icon = "error"}) -guihooks.trigger('Message', {msg = "Message Text Here", ttl = 5.0, category = "directions_car", icon = "directions_car"}) -guihooks.trigger('Message', {msg = "Message Text Here", ttl = 5.0, category = "star", icon = "star"}) -guihooks.trigger('Message', {msg = "Message Text Here", ttl = 5.0, category = "timeline", icon = "timeline"}) -guihooks.trigger('Message', {msg = "Message Text Here", ttl = 5.0, category = "save", icon = "save"}) -guihooks.trigger('Message', {msg = "Message Text Here", ttl = 5.0, category = "settings", icon = "settings"}) -``` - -#### Center large or small display flash - -
- ![image](https://github.com/StanleyDudek/Docs/assets/49531350/d0cf754f-83f8-4d15-9159-27350da127de) -
- -
- ![image](https://github.com/StanleyDudek/Docs/assets/49531350/1df6fc9b-756f-484e-b8d9-5df346dc4c26) -
- -```lua ---guihooks.trigger('ScenarioFlashMessage', {{msg, ttl, sound, big}} ) -- requires RaceCountdown ui app -guihooks.trigger('ScenarioFlashMessage', {{"Message", 5.0, 0, true}} ) -guihooks.trigger('ScenarioFlashMessage', {{"Message Text Here", 5.0, 0, false}} ) - ---countdown example, when all executed at once, the items are queued and will follow eachother after the previous ttl expires -guihooks.trigger('ScenarioFlashMessage', {{"3", 1.0, "Engine.Audio.playOnce('AudioGui', 'event:UI_Countdown1')", true}}) -guihooks.trigger('ScenarioFlashMessage', {{"2", 1.0, "Engine.Audio.playOnce('AudioGui', 'event:UI_Countdown2')", true}}) -guihooks.trigger('ScenarioFlashMessage', {{"1", 1.0, "Engine.Audio.playOnce('AudioGui', 'event:UI_Countdown3')", true}}) -guihooks.trigger('ScenarioFlashMessage', {{"GO!", 3.0, "Engine.Audio.playOnce('AudioGui', 'event:UI_CountdownGo')", true}}) - ---another sound example -guihooks.trigger('ScenarioFlashMessage', {{"Teleported!", 3.0, "Engine.Audio.playOnce('AudioGui', 'event:UI_Checkpoint')", false}}) -``` - -#### Center mid-size persistent display - -This requires the 'Race Realtime Display' UI app. - -
- ![image](https://github.com/StanleyDudek/Docs/assets/49531350/6290e018-6b3d-4674-98f2-34282a723258) -
- -```lua ---guihooks.trigger('ScenarioRealtimeDisplay', {msg = msg} ) -- requires Race Realtime Display ui app -guihooks.trigger('ScenarioRealtimeDisplay', {msg = "Message Text Here"} ) ---these messages persist, clear with a blank string ---if you are running live data, this is a good one to update rapidly (think timers, distance calcs, et cetera) -guihooks.trigger('ScenarioRealtimeDisplay', {msg = ""} ) -``` - -#### Confirmation Dialog - -ConfirmationDialog is a simplistic popup with up to two buttons. - -```lua --- Open a ConfirmationDialog with a title, body text, and up to two buttons -guihooks.trigger("ConfirmationDialogOpen", - "Example Title", - "Example Body Text", - "Okay", - "", --gelua. empty string - "Cancel", - "" --gelua -) - --- Close any open ConfirmationDialog with the provided title -guihooks.trigger("ConfirmationDialogClose", "Example Title") -``` - -
- ![Example of a ConfirmationDialog](../../assets/content/ConfirmationDialog.png) -
- -Both fields of a button must be strings in order for the button to appear. - -If the Okay button is provided, pressing the *OK / Primary action* action is equivalent to pressing the Okay button. - -If the Cancel button is provided, pressing the *Menu* action is equivalent to pressing the Cancel button. - -HTML is supported and can be used to add images/icons, for example. - -Multiple can be displayed at once, displayed sequentially. - -!!! bug - - Providing no buttons prevents the player from escaping the dialog without using the console. - -!!! bug - - The SDF parts of the Minimap UI app remain visible while a ConfirmationDialog is active. - - `#!lua guihooks.trigger('ShowApps', false)` to hide UI apps can be used as a hacky workaround. - -
- ![ConfirmationDialog being used for an inactivity kick system](../../assets/content/ConfirmationDialog_Example.png) -
- -#### introPopupTutorial - -introPopupTutorial is a highly customizable popup that is largely defined with embedded HTML. It is standard to load from a standalone HTML file located in `/gameplay/tutorials/pages/*/content.html`. - -```lua -guihooks.trigger("introPopupTutorial", { - { - content = readFile("/gameplay/tutorials/pages/template/content.html"):gsub("\r\n",""), - flavour = "onlyOk" - } -}) - -guihooks.trigger("introPopupClose") -``` - -
- ![The introPopupTutorial snippet displayed in BeamNG.drive](../../assets/content/introPopupTutorial.png) -
- -Flavours controls which buttons are displayed. Four flavours exist: - -* `withLogbook` - * Buttons: Career Logbook, Okay -* `onlyOk` - * Buttons: Okay -* `onlyLogbook` - * Buttons: Career Logbook -* `noButtons` - * Provides no buttons - -!!! warning - - When using the noButtons flavour on the page, providing no extra JavaScript in the page content to close the popup causes a softlock. Pages are not combined into one popup in this flavour. It is not recommended to use this flavour. - -If multiple pages are provided, or the hook is triggered multiple times, then the pages are combined into the same popup. If the hook is triggered while a introPopup is active, or when a different introPopup type has already been triggered, then it is displayed in a separate popup after the existing popup is closed. - -#### introPopupCareer - -introPopupCareer is an easy to use, but open ended popup that supports embedding HTML, if needed. - -Flavours control which buttons are displayed and the default image aspect ratio. Four flavours exist: - -* `default` - * Default image aspect ratio: 16x9 - * Buttons: Later, Okay -* `welcome` - * Default image aspect ratio: 16x9 - * Buttons: Career Logbook, Okay -* `branch-info` - * Default image aspect ratio: 16x9 - * Buttons: Career Logbook, Okay -* `garage` - * Buttons: Later, Okay - -```lua -guihooks.trigger("introPopupCareer", { - { - title = "Example title", - text = "Example text", - image = "/gameplay/tutorials/pages/template/image.jpg", - ratio = "16x9", - flavour = "default" - } -}) - -guihooks.trigger("introPopupClose") -``` - -
- ![The introPopupCareer snippet displayed in BeamNG.drive](../../assets/content/introPopupCareer.png) -
- -If multiple pages are provided, or the hook is triggered multiple times, then the pages are combined into the same popup. If the hook is triggered while a introPopup is active, or when a different introPopup type has already been triggered, then it is displayed in a separate popup after the existing popup is closed. - -!!! bug - - The background blur has a minimum height, causing popups with short content to have excess blur below its window. Two main workarounds exist: - - * Repeat `\n` and end with `#!html
` until the window covers the blur - * Use an empty or missing `image` path and adjust the aspect ratio until the window covers the blur - -#### introPopupMission - -introPopupMission is almost identical to introPopupCareer, but needs buttons to be defined rather than picking a preset for buttons. - -Button styles are combined as *bng-button-*`style`. Built-in button styles are: - -* `main` - orange -* `secondary` - cyan -* `attention` - red -* `white` - white -* `link` - translucent -* `outline` - orange outline - -```lua -guihooks.trigger('introPopupMission', { - title = "introPopupMission title", - text = "introPopupMission description", - image = "/gameplay/tutorials/pages/template/image.jpg", - ratio = "16x9", - buttons = { - { default=true, class="main", label="main button", clickLua="" }, - { default=false, class="secondary", label="secondary button", clickLua="" }, - { default=false, class="attention", label="attention button", clickLua="" }, - { default=false, class="white", label="white button", clickLua="" }, - { default=false, class="link", label="link button", clickLua="" }, - { default=false, class="outline", label="outline button", clickLua="" } - } -}) - -guihooks.trigger("introPopupClose") -``` - -
- ![The introPopupMission snippet displayed in BeamNG.drive](../../assets/content/introPopupMission.png) -
- -If multiple pages are provided, or the hook is triggered multiple times, then the pages are combined into the same popup. If the hook is triggered while a introPopup is active, or when a different introPopup type has already been triggered, then it is displayed in a separate popup after the existing popup is closed. - -!!! bug - - The background blur has a minimum height, causing popups with short content to have excess blur below its window. Two main workarounds exist: - - * Repeat `\n` and end with `#!html
` until the window covers the blur - * Use an empty or missing `image` path and adjust the aspect ratio until the window covers the blur - -#### Dialogue - -Dialogue is used in the *A Rocky Start* campaign to display information about a mission. It is a centered, vertically aligned popup with a specific layout. It does not support embedding HTML. - -```lua -ui_missionInfo.openDialogue({ - title = "Dialogue title", - type = "Custom", -- isn't actually displayed - typeName = "typeName", - data = { - {label = "objective", value = "reward"} - -- add more... - }, - buttons = { - {action = "accept", text = "Accept", cmd = ""}, - {action = 'decline',text = "Decline", cmd = ""} - -- add more... - } -}) - -ui_missionInfo.closeDialogue() -``` - -
- ![The Dialogue snippet displayed in BeamNG.drive](../../assets/content/Dialogue.png) -
- -Only one Dialogue can be displayed at once. Any existing Dialogue is overridden. - -!!! info - - `#!lua ui_missionInfo.closeDialogue()` must be used to close a dialogue. - - Make sure you call this function when any button is pressed. - -## IMGUI Code Snippets - -todo - -## CEF UI Code Snippets - -todo diff --git a/docs/en/scripting/server/latest-server-reference.md b/docs/en/scripting/server/latest-server-reference.md index 33e6c1f1..e5d81a8a 100644 --- a/docs/en/scripting/server/latest-server-reference.md +++ b/docs/en/scripting/server/latest-server-reference.md @@ -1171,14 +1171,419 @@ Triggered when a player sends a chat message. When cancelled, it will not show t Arguments: `player_id: number`, `vehicle_id: number`, `data: string` Cancellable: YES -Triggered when a player spawns a new vehicle. The `data` argument contains the car's configuration and positional/rotational data for the vehicle as a json string. +Triggered when a player spawns a new vehicle. Note that vehicle swaps/replacements instead fire [`onVehicleEdited`](#onvehicleedited). The `data` argument contains the car's configuration and positional/rotational data for the vehicle as a json string. + +
+ +Example data value + +The data string begins with a unique vehicle identifier, which is the player's ID, a hyphen, and then the vehicle ID. This is followed by a JSON object containing information about the vehicles configuration and positioning. + +``` +0-0: { + "abs": "realistic", + "ign": 3, + "jbm": "van", + "pid": 0, + "pos": [ + 907.93902587891, + 773.50201416016, + 238.87800598145 + ], + "pro": "0", + "rot": [ + 0, + 0, + 0.99999994039536, + 0 + ], + "vcf": { + "licenseName": "H30 9VV", + "mainPartName": "van", + "mainPartPath": "/van", + "model": "van", + "paints": [ + { + "baseColor": [ + 0.21999999880791, + 0.37000000476837003, + 0.33000001311302, + 1.2000000476837 + ], + "clearcoat": 0, + "clearcoatRoughness": 0, + "metallic": 0, + "roughness": 0.070000000298023 + }, + { + "baseColor": [ + 0.62300002574921, + 0.62300002574921, + 0.62300002574921, + 1.2000000476837 + ], + "clearcoat": 0.80000001192093, + "clearcoatRoughness": 0.070000000298023, + "metallic": 0.80000001192093, + "roughness": 0.64999997615814 + }, + { + "baseColor": [ + 0.21999999880791, + 0.37000000476837003, + 0.33000001311302, + 1.2000000476837 + ], + "clearcoat": 0, + "clearcoatRoughness": 0, + "metallic": 0, + "roughness": 0.070000000298023 + } + ], + "partConfigFilename": "vehicles/van/h15_xt_passenger.pc", + "parts": { + "brakepad_F": "brakepad_F_premium", + "brakepad_R": "brakepad_R_premium", + "gps": "", + "licenseplate_design_2_1": "", + "linelock": "", + "load_seat_FR": "", + "n2o_system": "", + "paint_design": "van_skin_twotone", + "pickup_engine_v8_ecu": "pickup_engine_v8_ecu", + "pickup_engine_v8_internals": "pickup_engine_v8_internals", + "pickup_enginemounts": "pickup_enginemounts", + "pickup_oilpan_v8": "pickup_oilpan_v8", + "pickup_reversewarn": "", + "pickup_sparetire": "pickup_sparetire_5l", + "pickup_towhitch": "", + "skin_glass": "van_skin_glass_tint", + "skin_interior": "van_skin_interior_black", + "soundscape_horn": "soundscape_horn_115", + "tire_F_16x7_alt": "tire_F_225_75_16_alt_standard", + "tire_R_16x7_alt": "tire_R_225_75_16_alt_standard", + "van_ABS": "van_ABS", + "van_ESC": "", + "van_ac": "van_ac", + "van_body": "van_body_passenger", + "van_brake_F": "van_brake_F", + "van_brake_R": "van_brake_R_drum", + "van_bumper_F": "van_bumper_F_altb", + "van_bumper_F_lip": "", + "van_bumper_R": "van_bumper_R_altb", + "van_bumper_accessory_F": "", + "van_bumpersignal_FL": "van_bumpersignal_FL", + "van_bumpersignal_FR": "van_bumpersignal_FR", + "van_coilover_IFS": "van_coilover_IFS", + "van_converter": "van_converter", + "van_differential_F": "", + "van_differential_R": "van_differential_R", + "van_door_FL": "van_door_FL", + "van_door_FR": "van_door_FR", + "van_doordetent_FL": "van_doordetent_FL", + "van_doordetent_FR": "van_doordetent_FR", + "van_doordetent_RL": "van_doordetent_RL", + "van_doordetent_RR": "van_doordetent_RR", + "van_doorglass_L": "van_doorglass_L", + "van_doorglass_R": "van_doorglass_R", + "van_doorpanel_FL": "van_doorpanel_FL", + "van_doorpanel_FR": "van_doorpanel_FR", + "van_driveshaft_R": "van_driveshaft_R", + "van_engine": "van_engine_v8_4.5", + "van_exhaust_v8": "van_exhaust_v8", + "van_fascia_F": "van_fascia_F_high", + "van_fender_L": "van_fender_L", + "van_fender_R": "van_fender_R", + "van_fenderflare_FL": "", + "van_fenderflare_FR": "", + "van_fenderflare_RL": "", + "van_fenderflare_RR_sidedoor": "", + "van_finaldrive_R": "van_finaldrive_R_355", + "van_frame": "van_frame", + "van_fueltank": "van_fueltank", + "van_header": "van_exhmanifold", + "van_headlight_L_high": "van_headlight_L_high", + "van_headlight_R_high": "van_headlight_R_high", + "van_hood": "van_hood", + "van_hub_F": "van_hub_F_5", + "van_hub_R": "van_hub_R_5", + "van_intake_v8": "van_intake_v8", + "van_intcarpet_roof": "van_intcarpet_roof", + "van_interior": "van_interior", + "van_lettering_doors_F": "van_lettering_doors_F_h15", + "van_lettering_reardoor_L": "van_lettering_gavril_reardoor_L", + "van_lettering_reardoor_R": "van_lettering_h15_xt_reardoor_R", + "van_licenseplate_F": "van_licenseplate_F", + "van_licenseplate_R": "van_licenseplate_R", + "van_lightbar": "", + "van_mirror_L": "van_mirror_L", + "van_mirror_R": "van_mirror_R", + "van_mod": "", + "van_muffler": "van_muffler", + "van_power_steering": "", + "van_radiator": "van_radiator", + "van_radio": "van_radio", + "van_reardoor_L": "van_reardoor_L", + "van_reardoor_R": "van_reardoor_R", + "van_reardoorglass_L": "van_reardoorglass_L", + "van_reardoorglass_R": "van_reardoorglass_R", + "van_reardoorpanel_L": "van_reardoorpanel_L", + "van_reardoorpanel_R": "van_reardoorpanel_R", + "van_rollcage": "", + "van_roof": "van_roof", + "van_roof_accessory": "", + "van_runningboard": "", + "van_seat_1R": "van_seat_1R", + "van_seat_2R": "van_seat_2R", + "van_seat_3R": "van_seat_3R", + "van_seat_FL": "van_seat_FL", + "van_seat_FR": "van_seat_FR", + "van_shifter": "van_shifter_A", + "van_shock_R": "van_shock_R", + "van_sidedoor_FR": "van_sidedoor_FR_alt", + "van_sidedoor_RR": "van_sidedoor_RR_alt", + "van_sidedoorglass_FR": "van_sidedoorglass_FR", + "van_sidedoorglass_RR": "van_sidedoorglass_RR", + "van_sidedoorpanel_FR": "van_sidedoorpanel_FR", + "van_sidedoorpanel_RR": "van_sidedoorpanel_RR", + "van_sideglass_FL": "van_sideglass_FL", + "van_sideglass_ML": "van_sideglass_ML", + "van_sideglass_RL": "van_sideglass_RL", + "van_sideglass_RR": "van_sideglass_RR", + "van_snorkel": "", + "van_spring_R": "van_spring_R", + "van_steer": "van_steer", + "van_steering": "van_steering", + "van_suspension_F": "van_IFS", + "van_suspension_R": "van_axle_R", + "van_swaybar_F": "van_swaybar_F", + "van_swaybar_R": "", + "van_taillight_L": "van_taillight_L", + "van_taillight_R": "van_taillight_R", + "van_taillightguard_L": "", + "van_taillightguard_R": "", + "van_transfer_case": "van_transfer_case_RWD", + "van_transmission": "van_transmission_4A", + "van_tubs": "van_tubs", + "van_valance_F": "van_valance_F", + "van_wheeldata_F": "van_wheeldata_F", + "van_wheeldata_R": "van_wheeldata_R", + "van_windshield": "van_windshield", + "wheel_F_5": "wheel_25a_16x7_5_F", + "wheel_R_5": "wheel_25a_16x7_5_R" + }, + "vars": {} + }, + "vid": 29339 +} +``` + +
##### `onVehicleEdited` Arguments: `player_id: number`, `vehicle_id: number`, `data: string` Cancellable: YES -Triggered when a player edits their vehicle and applies the edit. The `data` argument contains the car's updated configuration as a json string but does **not** include positional or rotational data. You can use [MP.GetPositionRaw](#mpgetpositionrawpid-number-vid-number-tablestring) to get positional and rotational data. +Triggered when a player edits or replaces their vehicle. The `data` argument contains the car's updated configuration as a json string but does **not** include positional or rotational data. You can use [MP.GetPositionRaw](#mpgetpositionrawpid-number-vid-number-tablestring) to get positional and rotational data. + +
+ +Example data value + +The data string begins with a unique vehicle identifier, which is the player's ID, a hyphen, and then the vehicle ID. This is followed by a JSON object containing information about the vehicles configuration. + +``` +0-0: { + "abs": "realistic", + "ign": 3, + "jbm": "van", + "pid": 0, + "pro": "0", + "vcf": { + "licenseName": "P60 1EP", + "mainPartName": "van", + "mainPartPath": "/van", + "model": "van", + "paints": [ + { + "baseColor": [ + 0.40000000596046, + 0.050000000745058, + 0.050000000745058, + 1.2000000476837 + ], + "clearcoat": 0, + "clearcoatRoughness": 0, + "metallic": 0, + "roughness": 0.070000000298023 + }, + { + "baseColor": [ + 0.40000000596046, + 0.050000000745058, + 0.050000000745058, + 1.2000000476837 + ], + "clearcoat": 0, + "clearcoatRoughness": 0, + "metallic": 0, + "roughness": 0.070000000298023 + }, + { + "baseColor": [ + 0.40000000596046, + 0.050000000745058, + 0.050000000745058, + 1.2000000476837 + ], + "clearcoat": 0, + "clearcoatRoughness": 0, + "metallic": 0, + "roughness": 0.070000000298023 + } + ], + "partConfigFilename": "vehicles/van/h15_passenger.pc", + "parts": { + "brakepad_F": "brakepad_F_premium", + "brakepad_R": "brakepad_R_premium", + "gps": "", + "hubcap_F_16": "hubcap_09c_F_altd", + "hubcap_R_16": "hubcap_09c_R_altd", + "licenseplate_design_2_1": "", + "linelock": "", + "load_seat_FR": "", + "n2o_system": "", + "paint_design": "", + "pickup_engine_v8_ecu": "pickup_engine_v8_ecu_late", + "pickup_engine_v8_internals": "pickup_engine_v8_internals", + "pickup_enginemounts": "pickup_enginemounts", + "pickup_oilpan_v8": "pickup_oilpan_v8", + "pickup_reversewarn": "", + "pickup_sparetire": "pickup_sparetire_6l", + "pickup_towhitch": "", + "skin_glass": "", + "skin_interior": "van_skin_interior_ivory", + "soundscape_horn": "soundscape_horn_115", + "tire_F_16x7_alt": "tire_F_225_75_16_alt_standard", + "tire_R_16x7_alt": "tire_R_225_75_16_alt_standard", + "trimring_F_16x7": "", + "trimring_R_16x7": "", + "van_ABS": "van_ABS", + "van_ac": "van_ac", + "van_body": "van_body_passenger", + "van_brake_F": "van_brake_F", + "van_brake_R": "van_brake_R", + "van_bumper_accessory_F_late": "", + "van_bumper_F": "van_bumper_F_late_alt", + "van_bumper_F_lip_late": "", + "van_bumper_R": "van_bumper_R_late_alt", + "van_coilover_IFS": "van_coilover_IFS", + "van_converter": "van_converter", + "van_differential_F": "", + "van_differential_R": "van_differential_R", + "van_door_FL": "van_door_FL", + "van_door_FR": "van_door_FR", + "van_doordetent_FL": "van_doordetent_FL", + "van_doordetent_FR": "van_doordetent_FR", + "van_doordetent_RL": "van_doordetent_RL", + "van_doordetent_RR": "van_doordetent_RR", + "van_doorglass_L": "van_doorglass_L", + "van_doorglass_R": "van_doorglass_R", + "van_doorpanel_FL": "van_doorpanel_FL", + "van_doorpanel_FR": "van_doorpanel_FR", + "van_driveshaft_R": "van_driveshaft_R", + "van_engine": "van_engine_v8_4.5", + "van_ESC": "van_ESC", + "van_exhaust_v8": "van_exhaust_v8", + "van_fascia_F": "van_fascia_F_late", + "van_fender_L": "van_fender_L", + "van_fender_R": "van_fender_R", + "van_fenderflare_FL": "", + "van_fenderflare_FR": "", + "van_fenderflare_RL": "", + "van_fenderflare_RR_sidedoor": "", + "van_finaldrive_R": "van_finaldrive_R_355", + "van_frame": "van_frame", + "van_fueltank": "van_fueltank", + "van_grille_F_late": "van_grille_F_late", + "van_header": "van_exhmanifold", + "van_headlight_L_late": "van_headlight_L_late", + "van_headlight_R_late": "van_headlight_R_late", + "van_hood": "van_hood_late", + "van_hub_F": "van_hub_F_6", + "van_hub_R": "van_hub_R_6", + "van_intake_v8": "van_intake_v8_late", + "van_intcarpet_roof": "van_intcarpet_roof", + "van_interior": "van_interior", + "van_lettering_doors_F": "van_lettering_doors_F_h15", + "van_lettering_reardoor_L": "van_lettering_gavril_reardoor_L", + "van_lettering_reardoor_R": "van_lettering_h15_reardoor_R", + "van_licenseplate_F_late": "van_licenseplate_F_late", + "van_licenseplate_R_late": "van_licenseplate_R_late", + "van_lightbar": "", + "van_mirror_L": "van_mirror_L", + "van_mirror_R": "van_mirror_R", + "van_mod": "", + "van_muffler": "van_muffler", + "van_power_steering": "", + "van_radiator": "van_radiator", + "van_radio": "van_radio", + "van_reardoor_L": "van_reardoor_L", + "van_reardoor_R": "van_reardoor_R", + "van_reardoorglass_L": "van_reardoorglass_L", + "van_reardoorglass_R": "van_reardoorglass_R", + "van_reardoorpanel_L": "van_reardoorpanel_L", + "van_reardoorpanel_R": "van_reardoorpanel_R", + "van_rollcage": "", + "van_roof": "van_roof", + "van_roof_accessory": "", + "van_runningboard": "", + "van_seat_1R": "van_seat_1R", + "van_seat_2R": "van_seat_2R", + "van_seat_3R": "van_seat_3R", + "van_seat_FL": "van_seat_FL", + "van_seat_FR": "van_seat_FR", + "van_shifter": "van_shifter_A", + "van_shock_R": "van_shock_R", + "van_sidedoor_FR": "van_sidedoor_FR_alt", + "van_sidedoor_RR": "van_sidedoor_RR_alt", + "van_sidedoorglass_FR": "van_sidedoorglass_FR", + "van_sidedoorglass_RR": "van_sidedoorglass_RR", + "van_sidedoorpanel_FR": "van_sidedoorpanel_FR", + "van_sidedoorpanel_RR": "van_sidedoorpanel_RR", + "van_sideglass_FL": "van_sideglass_FL", + "van_sideglass_ML": "van_sideglass_ML", + "van_sideglass_RL": "van_sideglass_RL", + "van_sideglass_RR": "van_sideglass_RR", + "van_snorkel": "", + "van_spring_R": "van_spring_R", + "van_steer": "van_steer_facelift", + "van_steering": "van_steering", + "van_suspension_F": "van_IFS", + "van_suspension_R": "van_axle_R", + "van_swaybar_F": "van_swaybar_F", + "van_swaybar_R": "", + "van_taillight_L": "van_taillight_L", + "van_taillight_R": "van_taillight_R", + "van_taillightguard_L": "", + "van_taillightguard_R": "", + "van_transfer_case": "van_transfer_case_RWD", + "van_transmission": "van_transmission_4A", + "van_tubs": "van_tubs", + "van_valance_F": "van_valance_F_late", + "van_wheeldata_F": "van_wheeldata_F", + "van_wheeldata_R": "van_wheeldata_R", + "van_windshield": "van_windshield", + "wheel_F_6": "steelwheel_02b_16x7_F", + "wheel_R_6": "steelwheel_02b_16x7_R" + }, + "vars": {} + } +} +``` + +
##### `onVehicleDeleted` diff --git a/docs/fr/FAQ/Clearing-mods.md b/docs/fr/FAQ/Clearing-mods.md new file mode 100644 index 00000000..386325e7 --- /dev/null +++ b/docs/fr/FAQ/Clearing-mods.md @@ -0,0 +1,38 @@ +## Pourquoi dois-je désactiver mes mods ? + +Dans BeamMP, le serveur auquel vous vous connectez fournit les mods nécessaires. Ceux-ci sont téléchargés et activés automatiquement lors de la connexion. L'installation et l'activation de mods locaux peuvent souvent entraîner un dysfonctionnement de BeamMP, même avec un seul mod supplémentaire. + +!!! warning + +Supprimez tous les mods placés dans le dossier BeamNG.Drive\content\ ou ses sous-dossiers. Cet emplacement n'est PAS destiné à l'installation de mods et peut entraîner des problèmes, comme indiqué dans le fichier « DO_NOT_INSTALL_MODS_HERE.txt », situé dans le même répertoire. + +!!! quote 'DO_NOT_INSTALL_MODS_HERE.txt" + +NE copiez PAS les mods dans ce dossier : cela peut entraîner des mods cassés, une installation plus lente des mises à jour, un gestionnaire de mods cassé, un mode sans échec cassé et autres. + +## Comment désactiver/supprimer mes mods ? + +Il existe 3 options pour résoudre les problèmes possibles lors de l'utilisation de BeamMP. + +### 1. Désactiver les mods + +Avant de rejoindre un serveur, assurez-vous qu'aucun mod autre que « multiplayerbeammp » n'est activé. Si le jeu se bloque ou si vous rencontrez toujours des problèmes, reportez-vous à l'option suivante. + +### 2. Création d'un nouveau dossier utilisateur + +Ouvrez le dossier utilisateur BeamNG.Drive et renommez le dossier de la dernière version (par exemple 0.35) en 0.xx_OLD (0.35_OLD). Fermez BeamNG.drive avant de le renommer. ![image](../../assets/content/new-userfolder.png) + +### 3. Suppression des mods des dossiers de contenu. + +Pour accéder au dossier Beamng.drive\content\ et le vider de tout mod, ouvrez l'emplacement d'installation de BeamNG.drive. Faites un clic droit sur le dossier `content` et supprimez-le. Procédez à la vérification des fichiers du jeu via Steam ou Epic Games. Les fichiers seront alors à nouveau téléchargés. + +Une fois terminé, lancez BeamNG.Drive via le lanceur BeamMP. Le seul mod activé dans le dépôt devrait être « multiplayerbeammp », ainsi que le bouton d'accès à BeamMP dans le menu principal. Si vous rencontrez toujours des difficultés pour rejoindre un serveur moddé, il est probable que des mods défectueux ou obsolètes soient disponibles. + +### 4. Nettoyage du cache de BeamMP-Launcher + +Pour nettoyer les mods en cache des répertoires BeamMP, accédez à l'emplacement d'installation de votre lanceur BeamMP. Par défaut, le chemin est « C:\Users\AppData\BeamMP-Launcher ». Vous y trouverez un dossier « Resources ». Supprimez ce dossier pour supprimer tous les mods en cache. Cela peut être utile si vous avez besoin de plus d'espace disque ou si vous souhaitez supprimer des mods BeamNG obsolètes. + +!!! question "Mes paramètres et configurations ont disparu ! Comment puis-je les restaurer ?" + +Si vous avez renommé le dossier utilisateur, vous avez forcé le jeu à créer un nouveau dossier utilisateur propre. Vous pouvez copier les dossiers « paramètres » et « véhicules » du dossier renommé (par exemple, 0.34_OLD) vers le nouveau dossier créé. +Assurez-vous que BeamNG.Drive est fermé et remplacez tous les éléments à l'emplacement où vous souhaitez copier les dossiers. Toutes les configurations et tous les paramètres devraient maintenant être identiques. diff --git a/docs/zh/API documentation/Client-Side.md b/docs/zh/API documentation/Client-Side.md new file mode 100644 index 00000000..b1106a18 --- /dev/null +++ b/docs/zh/API documentation/Client-Side.md @@ -0,0 +1,1502 @@ +## 目录 + +### MPVehicleGE + +- [Vehicle Functions(车辆函数)](#vehicle-functions) +- [Player Functions(玩家函数)](#player-functions) +- [Nametag Functions(昵称标签函数)](#nametag-functions) +- [Role Functions(角色函数)](#role-functions) +- [Navigation Functions(导航函数)](#navigation-functions) +- [Object Methods(对象方法)](#object-methods) +- [Event Hooks(事件钩子)](#event-hooks) + +### MPConfig + +- [MPConfig Functions(MPConfig函数)](#mpconfig-functions) + +### MPCoreNetwork + +- [MPCoreNetwork Functions](#mpcorenetwork-functions) + +### MPGameNetwork + +- [事件系统功能](#event-system-functions) +- [按键功能](#keypress-functions) +- [用户界面功能](#ui-functions) +- [MPGameNetwork 回调](#mpgamenetwork-callbacks) + +### MPHelpers + +- [编码函数](#encoding-functions) +- [颜色函数](#color-functions) +- [字符串函数](#string-functions) +- [表函数](#table-functions) +- [调试函数](#debug-functions) + +--- + +## 车辆函数 + +### `getGameVehicleID(serverVehicleID)` + +将 serverVehicleID 解析为 gameVehicleID + +**参数:** + +- `serverVehicleID` (字符串)- 格式:“X-Y”,其中 X 为玩家 ID,Y 为车辆 ID + +**返回:** + +- (数字)- 游戏内部车辆 ID +- (编号) `-1` - 如果车辆未知 + +**用法:** + +```lua +local gameID = extensions.MPVehicleGE.getGameVehicleID("0-0") +``` + +--- + +### `getServerVehicleID(gameVehicleID)` + +将 gameVehicleID 解析为 serverVehicleID + +**参数:** + +- `gameVehicleID` (数字)- 游戏内部车辆 ID + +**返回:** + +- (字符串)- 服务器车辆 ID(例如,“0-0”) +- (nil)- 如果 gameVehicleID 未知 + +**用法:** + +```lua +local serverID = extensions.MPVehicleGE.getServerVehicleID(11171) +``` + +--- + +### `getVehicleByServerID(serverVehicleID)` + +返回此车辆的完整车辆表 + +**参数:** + +- `serverVehicleID` (字符串)- 格式:“X-Y” + +**返回:** + +- (表)- 车辆信息(名称、游戏车辆 ID、jbeam、所有者 ID、所有者名称、是否本地、是否已生成等) +- (nil)- 如果服务器车辆 ID 无效 + +**用法:** + +```lua +local vehicle = extensions.MPVehicleGE.getVehicleByServerID("0-0") +if vehicle then + print("Owner: " .. vehicle.ownerName) +end +``` + +--- + +### `getVehicleByGameID(gameVehicleID)` + +返回此车辆的完整车辆表 + +**参数:** + +- `gameVehicleID` (数字)- 游戏内部车辆 ID + +**返回:** + +- (表)- 车辆信息 +- (nil) - 如果 gameVehicleID 无效 + +**用法:** + +```lua +local vehicle = extensions.MPVehicleGE.getVehicleByGameID(11171) +``` + +--- + +### `isOwn(gameVehicleID)` + +检查给定车辆是否属于此客户端 + +**参数:** + +- `gameVehicleID` (数字)- 游戏内部车辆 ID + +**返回:** + +- (boolean)- 如果车辆属于此客户端,则返回 true + +**用法:** + +```lua +if extensions.MPVehicleGE.isOwn(11171) then + print("This is my vehicle") +end +``` + +--- + +### `getOwnMap()` + +返回一个包含此客户端拥有的所有车辆的表。 + +**参数:** + +- 无 + +**返回:** + +- (表)- 已拥有车辆地图`{[gameVehicleID] = vehicles_subtable}` + +**用法:** + +```lua +local myVehicles = extensions.MPVehicleGE.getOwnMap() +``` + +--- + +### `getVehicleMap()` + +返回一个包含所有已知多人游戏载具的表。 + +**参数:** + +- 无 + +**返回:** + +- (表)- 所有车辆的地图`{[serverVehicleID] = gameVehicleID}` + +**用法:** + +```lua +local allVehicles = extensions.MPVehicleGE.getVehicleMap() +``` + +--- + +### `getDistanceMap()` + +返回每个多人游戏载具到此客户端视角的距离 + +**参数:** + +- 无 + +**返回:** + +- (表)- 距离地图`{[gameVehicleID] = distanceInMeters}` + +**用法:** + +```lua +local distances = extensions.MPVehicleGE.getDistanceMap() +``` + +--- + +### `getNicknameMap()` + +返回所有多人游戏车辆 ID 及其所有者名称 + +**参数:** + +- 无 + +**返回:** + +- (表)- 昵称映射表`{[gameVehicleID] = ownerName}` + +**用法:** + +```lua +local nicknameMap = extensions.MPVehicleGE.getNicknameMap() +``` + +--- + +### `getVehicles()` + +返回完整的车辆表 + +**参数:** + +- 无 + +**返回:** + +- (表)- 所有车辆`{[serverVehicleID] = vehicles_subtable}` + +**用法:** + +```lua +local vehicles = extensions.MPVehicleGE.getVehicles() +for serverID, vehicle in pairs(vehicles) do + print("Vehicle: " .. vehicle.jbeam) +end +``` + +--- + +## 玩家函数 + +### `getPlayerByName(name)` + +返回该玩家的信息表和 ID + +**参数:** + +- `name` (字符串)- 玩家名称 + +**返回:** + +- (表)- 玩家信息(姓名、玩家ID、角色、载具等) +- (数字)- 玩家 ID +- (nil)- 如果未找到玩家 + +**用法:** + +```lua +local player, playerID = extensions.MPVehicleGE.getPlayerByName("John") +if player then + print("Player ID: " .. playerID) +end +``` + +--- + +### `getPlayers()` + +返回完整的玩家表 + +**参数:** + +- 无 + +**返回:** + +- (表)- 所有玩家`{[playerID] = players_subtable}` + +**用法:** + +```lua +local players = extensions.MPVehicleGE.getPlayers() +for playerID, player in pairs(players) do + print("Player: " .. player.name) +end +``` + +--- + +## 昵称标签函数 + +### `setPlayerNickPrefix(targetName, tagSource, text)` + +给玩家的名字标签添加前缀(显示在名字前面) + +**参数:** + +- `targetName` (字符串)- 玩家名称 +- `tagSource` (字符串)- 此前缀的唯一标识符 +- `text` (字符串)- 显示在名称之前的文本 + +**用法:** + +```lua +extensions.MPVehicleGE.setPlayerNickPrefix("John", "RANK", "1st.") +-- Result: "1st. John" +``` + +--- + +### `setPlayerNickSuffix(targetName, tagSource, text)` + +给玩家的名字标签添加后缀(显示在名字后面) + +**参数:** + +- `targetName` (字符串)- 玩家名称 +- `tagSource` (字符串)- 此后缀的唯一标识符 +- `text` (字符串)- 名称后显示的文本 + +**用法:** + +```lua +extensions.MPVehicleGE.setPlayerNickSuffix("John", "STATUS", "[AFK]") +-- Result: "John [AFK]" +``` + +--- + +### `hideNicknames(hide)` + +开启或关闭 BeamMP 的姓名标签绘制功能 + +**参数:** + +- `hide` (boolean) - True 表示隐藏姓名标签,False 表示显示姓名标签 + +**用法:** + +```lua +extensions.MPVehicleGE.hideNicknames(true) -- Hide +extensions.MPVehicleGE.hideNicknames(false) -- Show +``` + +--- + +### `toggleNicknames()` + +切换显示姓名标签 + +**参数:** + +- 无 + +**用法:** + +```lua +extensions.MPVehicleGE.toggleNicknames() +``` + +--- + +## 角色函数 + +### `setPlayerRole(playerID, tag, shorttag, red, green, blue)` + +为玩家设置自定义角色 + +**参数:** + +- `playerID` (数字)- 玩家的ID +- `tag` (字符串)- 角色标签(例如,“VIP”) +- `shorttag` (字符串)- 简短版本(例如,“V”) +- `red` (数字)- 红色通道(0-255) +- `green` (数字)- 绿色通道(0-255) +- `blue` (数字)- 蓝色通道(0-255) + +**返回:** + +- (布尔值,字符串)- 如果玩家不存在`false, "player not found"` +- (布尔值,字符串)- 如果参数无效,则`false, error` +- (无)——关于成功无信息 + +**用法:** + +```lua +local success, error = extensions.MPVehicleGE.setPlayerRole(0, "VIP", "V", 255, 215, 0) +if success == false then + print("Error: " .. error) +end +``` + +--- + +### `clearPlayerRole(playerID)` + +清除玩家的自定义角色 + +**参数:** + +- `playerID` (数字)- 玩家的ID + +**返回:** + +- (布尔值)- 始终返回`false` (实现怪癖 - 用于检查玩家是否存在) + +**用法:** + +```lua +extensions.MPVehicleGE.clearPlayerRole(0) +``` + +--- + +### `setVehicleRole(playerIDVehicleID, tag, shorttag, red, green, blue)` + +为特定车辆设置自定义角色 + +**参数:** + +- `playerIDVehicleID` (字符串)- 车辆 ID(格式:“0-0”) +- `tag` (字符串)- 角色标签 +- `shorttag` (字符串)- 短版本 +- `red` (数字)- 红色(0-255) +- `green` (数字)- 绿色(0-255) +- `blue` (数字)- 蓝色(0-255) + +**返回:** + +- (布尔值,字符串)- 如果车辆不存在`false, "vehicle not found"` +- (布尔值,字符串)- 如果参数无效,则`false, error` +- (无)——关于成功无信息 + +**用法:** + +```lua +local success, error = extensions.MPVehicleGE.setVehicleRole("0-0", "Police", "POL", 0, 0, 255) +if success == false then + print("Error: " .. error) +end +``` + +--- + +### `clearVehicleRole(playerIDVehicleID)` + +清除车辆的自定义角色 + +**参数:** + +- `playerIDVehicleID` (字符串)- 车辆 ID(格式:“0-0”) + +**返回:** + +- (布尔值)- 始终返回`false` (实现特性 - 用于检查车辆是否存在) + +**用法:** + +```lua +extensions.MPVehicleGE.clearVehicleRole("0-0") +``` + +--- + +## 导航函数 + +### `groundmarkerToPlayer(targetName)` + +设置一条通往目标玩家位置的地面标记路线(静态) + +**参数:** + +- `targetName` (字符串)- 玩家名称,如果为 nil 则清除 + +**用法:** + +```lua +extensions.MPVehicleGE.groundmarkerToPlayer("John") -- Set +extensions.MPVehicleGE.groundmarkerToPlayer(nil) -- Clear +``` + +--- + +### `groundmarkerFollowPlayer(targetName, dontfollow)` + +设置跟随目标玩家的地面标记路线 + +**参数:** + +- `targetName` (字符串)- 玩家名称,如果为 nil 则停止 +- `dontfollow` (布尔值)- 如果为true,则创建静态标记 + +**用法:** + +```lua +extensions.MPVehicleGE.groundmarkerFollowPlayer("John") -- Follow +extensions.MPVehicleGE.groundmarkerFollowPlayer("John", true) -- Static +extensions.MPVehicleGE.groundmarkerFollowPlayer(nil) -- Stop +``` + +--- + +### `queryRoadNodeToPosition(targetPosition, owner)` + +查找距离目标位置最近的道路节点 + +**参数:** + +- `targetPosition` (vec3 或表)- 目标位置,包含 x、y、z 坐标 +- `owner` (字符串)- 可选标识符(默认值:“target”) + +**返回:** + +- (布尔值)- 成功状态 +- (数字)- 节点 ID(如果成功) + +**用法:** + +```lua +local pos = vec3(100, 200, 50) +local success, nodeID = extensions.MPVehicleGE.queryRoadNodeToPosition(pos) +``` + +--- + +## 对象方法 + +### 玩家对象方法 + +#### `player:setNickPrefix(tagSource, text)` + +设置该玩家名称标签的前缀 + +**参数:** + +- `tagSource` (字符串)- 唯一标识符 +- `text` (字符串)- 要显示的文本(或 nil 表示不显示) + +**用法:** + +```lua +local player = extensions.MPVehicleGE.getPlayerByName("John") +if player then + player:setNickPrefix("STATUS", "[AFK]") +end +``` + +--- + +#### `player:setNickSuffix(tagSource, text)` + +为该玩家的姓名标签设置后缀 + +**参数:** + +- `tagSource` (字符串)- 唯一标识符 +- `text` (字符串)- 要显示的文本(或 nil 表示不显示) + +**用法:** + +```lua +local player = extensions.MPVehicleGE.getPlayerByName("John") +if player then + player:setNickSuffix("MISSION", "[In Mission]") +end +``` + +--- + +#### `player:setCustomRole(role)` + +为该玩家设置自定义角色 + +**参数:** + +- `role` (表)- 角色表: `{backcolor = {r, g, b}, tag = string, shorttag = string}` + +**用法:** + +```lua +local player = extensions.MPVehicleGE.getPlayerByName("John") +if player then + player:setCustomRole({ + backcolor = {r = 255, g = 0, b = 0}, + tag = " [VIP]", + shorttag = " [V]" + }) +end +``` + +--- + +#### `player:clearCustomRole()` + +清除该玩家的自定义角色 + +**用法:** + +```lua +local player = extensions.MPVehicleGE.getPlayerByName("John") +if player then + player:clearCustomRole() +end +``` + +--- + +### 车辆对象方法 + +#### `vehicle:getOwner()` + +返回此车辆的所有者 + +**返回:** + +- (表)- 玩家对象 +- (数字)- 玩家 ID + +**用法:** + +```lua +local vehicle = extensions.MPVehicleGE.getVehicleByServerID("0-0") +if vehicle then + local owner, ownerID = vehicle:getOwner() + print("Owner: " .. owner.name) +end +``` + +--- + +#### `vehicle:setCustomRole(role)` + +为这辆车设置自定义角色 + +**参数:** + +- `role` (表)- 角色表: `{backcolor = {r, g, b}, tag = string, shorttag = string}` + +**用法:** + +```lua +local vehicle = extensions.MPVehicleGE.getVehicleByServerID("0-0") +if vehicle then + vehicle:setCustomRole({ + backcolor = {r = 0, g = 0, b = 255}, + tag = " [Police]", + shorttag = " [POL]" + }) +end +``` + +--- + +#### `vehicle:clearCustomRole()` + +清除此车辆的自定义角色 + +**用法:** + +```lua +local vehicle = extensions.MPVehicleGE.getVehicleByServerID("0-0") +if vehicle then + vehicle:clearCustomRole() +end +``` + +--- + +#### `vehicle:setDisplayName(displayName)` + +为这辆车设置自定义显示名称 + +**参数:** + +- `displayName` (字符串)- 要显示的自定义名称 + +**用法:** + +```lua +local vehicle = extensions.MPVehicleGE.getVehicleByServerID("0-0") +if vehicle then + vehicle:setDisplayName("Patrol Car #1") +end +``` + +--- + +## 事件钩子 + +BeamMP 提供事件钩子,您可以重写这些钩子以在特定事件发生时执行自定义代码。**请勿直接调用这些函数**,而应在保留原始功能的前提下对其进行重写。 + +### 钩子模式 + +重写函数时,务必保留原函数: + +```lua +-- Save the original function +local originalCallback = MPVehicleGE.onVehicleSpawned + +-- Override with your custom logic +MPVehicleGE.onVehicleSpawned = function(gameVehicleID) + -- Call the original first + originalCallback(gameVehicleID) + + -- Your custom code here + print("Vehicle spawned: " .. gameVehicleID) +end +``` + +--- + +### 可用的事件钩子 + +#### `onUpdate(dt)` + +连接到多人游戏时,每一帧都会被调用 + +**参数:** + +- `dt` (数值)- 自上一帧以来的时间间隔(秒)。 + +**用法:** + +```lua +local originalOnUpdate = MPVehicleGE.onUpdate +MPVehicleGE.onUpdate = function(dt) + originalOnUpdate(dt) + -- Your frame-by-frame logic here +end +``` + +--- + +#### `onPreRender(dt)` + +在渲染每一帧之前调用 + +**参数:** + +- `dt` (数值)- 时间差(秒) + +**注意:**此功能在内部处理名称标签渲染、距离计算和地面标记。 + +**用法:** + +```lua +local originalOnPreRender = MPVehicleGE.onPreRender +MPVehicleGE.onPreRender = function(dt) + originalOnPreRender(dt) + -- Your pre-render logic here +end +``` + +--- + +#### `onVehicleSpawned(gameVehicleID)` + +当车辆生成时调用(包括本地和远程车辆) + +**参数:** + +- `gameVehicleID` (数字)- 游戏内部车辆 ID + +**用法:** + +```lua +local originalOnVehicleSpawned = MPVehicleGE.onVehicleSpawned +MPVehicleGE.onVehicleSpawned = function(gameVehicleID) + originalOnVehicleSpawned(gameVehicleID) + + local vehicle = extensions.MPVehicleGE.getVehicleByGameID(gameVehicleID) + if vehicle then + print(vehicle.ownerName .. " spawned a " .. vehicle.jbeam) + end +end +``` + +--- + +#### `onVehicleDestroyed(gameVehicleID)` + +车辆被销毁/移除时调用 + +**参数:** + +- `gameVehicleID` (数字)- 游戏内部车辆 ID + +**用法:** + +```lua +local originalOnVehicleDestroyed = MPVehicleGE.onVehicleDestroyed +MPVehicleGE.onVehicleDestroyed = function(gameVehicleID) + local vehicle = extensions.MPVehicleGE.getVehicleByGameID(gameVehicleID) + if vehicle then + print("Vehicle " .. vehicle.jbeam .. " was destroyed") + end + + originalOnVehicleDestroyed(gameVehicleID) +end +``` + +--- + +#### `onVehicleSwitched(oldGameVehicleID, newGameVehicleID)` + +当玩家切换车辆时调用 + +**参数:** + +- `oldGameVehicleID` (数字)- 先前的车辆 ID(或 -1) +- `newGameVehicleID` (数字)- 新车辆 ID(或 -1) + +**用法:** + +```lua +local originalOnVehicleSwitched = MPVehicleGE.onVehicleSwitched +MPVehicleGE.onVehicleSwitched = function(oldID, newID) + originalOnVehicleSwitched(oldID, newID) + + print("Switched from vehicle " .. oldID .. " to " .. newID) +end +``` + +--- + +#### `onVehicleResetted(gameVehicleID)` + +车辆重置时调用(仅限本地车辆) + +**参数:** + +- `gameVehicleID` (数字)- 游戏内部车辆 ID + +**用法:** + +```lua +local originalOnVehicleResetted = MPVehicleGE.onVehicleResetted +MPVehicleGE.onVehicleResetted = function(gameVehicleID) + originalOnVehicleResetted(gameVehicleID) + + print("Vehicle " .. gameVehicleID .. " was reset") +end +``` + +--- + +#### `onVehicleColorChanged(gameVehicleID, index, paint)` + +当车辆的涂装颜色发生变化时调用 + +**参数:** + +- `gameVehicleID` (数字)- 游戏内部车辆 ID +- `index` (数字)- 涂装槽索引(0、1 或 2) +- `paint` (表) - 包含颜色信息的油漆数据 + +**用法:** + +```lua +local originalOnVehicleColorChanged = MPVehicleGE.onVehicleColorChanged +MPVehicleGE.onVehicleColorChanged = function(gameVehicleID, index, paint) + originalOnVehicleColorChanged(gameVehicleID, index, paint) + + print("Vehicle " .. gameVehicleID .. " changed paint slot " .. index) +end +``` + +--- + +#### `onVehicleReady(gameVehicleID)` + +当车辆的扩展设备加载完毕且车辆完全准备就绪时,就会发出此通知。 + +**参数:** + +- `gameVehicleID` (数字)- 游戏内部车辆 ID + +**注意:**如果需要加载车辆扩展,请使用此方法代替`onVehicleSpawned` 。 + +**用法:** + +```lua +local originalOnVehicleReady = MPVehicleGE.onVehicleReady +MPVehicleGE.onVehicleReady = function(gameVehicleID) + originalOnVehicleReady(gameVehicleID) + + -- Safe to interact with vehicle extensions here + local veh = be:getObjectByID(gameVehicleID) + if veh then + veh:queueLuaCommand("print('Vehicle is ready!')") + end +end +``` + +--- + +#### `onUIInitialised()` + +在 BeamMP 用户界面初始化时调用 + +**用法:** + +```lua +local originalOnUIInitialised = MPVehicleGE.onUIInitialised +MPVehicleGE.onUIInitialised = function() + originalOnUIInitialised() + + print("BeamMP UI initialized") +end +``` + +--- + +#### `onSettingsChanged()` + +当 BeamMP 设置更改时调用 + +**用法:** + +```lua +local originalOnSettingsChanged = MPVehicleGE.onSettingsChanged +MPVehicleGE.onSettingsChanged = function() + originalOnSettingsChanged() + + print("BeamMP settings changed") +end +``` + +--- + +## MPConfig 函数 + +### `MPConfig.getPlayerServerID()` + +返回本地玩家的服务器分配 ID + +**返回:** + +- (数字)- 玩家的服务器 ID(如果未设置则为 -1) + +**用法:** + +```lua +local myID = extensions.MPConfig.getPlayerServerID() +``` + +--- + +### `MPConfig.getNickname()` + +返回本地玩家的昵称 + +**返回:** + +- (字符串)- 玩家当前的昵称 + +**用法:** + +```lua +local name = extensions.MPConfig.getNickname() +``` + +--- + +### `MPConfig.getConfig()` + +返回 BeamMP 配置设置 + +**返回:** + +- (表)- 包含所有 BeamMP 设置的配置表 +- (nil)- 如果配置文件不存在 + +**用法:** + +```lua +local config = extensions.MPConfig.getConfig() +``` + +--- + +### `MPConfig.setConfig(settingName, settingVal)` + +设置特定配置值 + +**参数:** + +- `settingName` (字符串)- 设置的名称 +- `settingVal` (任意值) - 要设置的值 + +**用法:** + +```lua +extensions.MPConfig.setConfig("myCustomSetting", true) +``` + +--- + +## MPCoreNetwork 函数 + +### `MPCoreNetwork.getCurrentServer()` + +返回有关当前连接服务器的信息 + +**返回:** + +- (表)- 服务器数据(IP 地址、端口、名称、映射) +- (nil)- 如果未连接 + +**用法:** + +```lua +local server = extensions.MPCoreNetwork.getCurrentServer() +if server then + print("Server: " .. server.name) + print("IP: " .. server.ip .. ":" .. server.port) +end +``` + +--- + +## 事件系统功能 + +### `TriggerServerEvent(name, data)` + +向服务器发送事件 + +**参数:** + +- `name` (字符串)- 事件名称 +- `data` (字符串)- 要发送的数据 + +**注意:**这是一个全局函数。服务器必须已注册此事件的处理程序。 + +**用法:** + +```lua +TriggerServerEvent("playerReady", "ready") + +-- With JSON +local data = {position = {x=100, y=200, z=50}} +TriggerServerEvent("updatePlayer", jsonEncode(data)) +``` + +--- + +### `TriggerClientEvent(name, data)` + +触发本地客户端事件 + +**参数:** + +- `name` (字符串)- 事件名称 +- `data` (字符串)- 要发送的数据 + +**注意:**全局函数。在本地触发,无需发送到服务器。 + +**用法:** + +```lua +TriggerClientEvent("localUpdate", "data") +``` + +--- + +### `AddEventHandler(event_name, func, name)` + +注册一个用于处理特定事件的函数 + +**参数:** + +- `event_name` (字符串)- 要处理的事件的名称 +- `func` (函数)- 处理函数(接收事件数据) +- `name` (字符串)- 可选的内部名称 + +**注意:**全局函数。 + +**用法:** + +```lua +AddEventHandler("playerDamage", function(data) + print("Damage: " .. data) +end) + +-- With JSON +AddEventHandler("vehicleSpawned", function(data) + local vehData = jsonDecode(data) + print("Spawned: " .. vehData.model) +end) +``` + +--- + +### `RemoveEventHandler(event_name, name)` + +移除事件处理程序 + +**参数:** + +- `event_name` (字符串)- 事件名称 +- `name` (字符串)- 可选的内部名称 + +**注意:**全局函数。 + +**用法:** + +```lua +RemoveEventHandler("playerDamage") +``` + +--- + +## 按键功能 + +### `onKeyPressed(keyname, func)` + +注册一个按键按下时要调用的函数 + +**参数:** + +- `keyname` (字符串)- 按键的名称(例如,“NUMPAD1”、“F1”) +- `func` (函数)- 要调用的函数(接收布尔值) + +**注意:**全局函数。 + +**用法:** + +```lua +onKeyPressed("NUMPAD1", function(state) + print("NUMPAD1 pressed!") +end) +``` + +--- + +### `onKeyReleased(keyname, func)` + +注册一个在按键释放时要调用的函数 + +**参数:** + +- `keyname` (字符串)- 键的名称 +- `func` (函数)- 要调用的函数(接收布尔值) + +**注意:**全局函数。 + +**用法:** + +```lua +onKeyReleased("NUMPAD1", function(state) + print("NUMPAD1 released!") +end) +``` + +--- + +### `addKeyEventListener(keyname, func, type)` + +注册一个具有可自定义触发类型的按键事件监听器 + +**参数:** + +- `keyname` (字符串)- 键的名称 +- `func` (函数)- 要调用的函数 +- `type` (字符串)- 事件类型:“向下”、“向上”或“两者”(默认值:“两者”) + +**注意:**全局函数。 + +**用法:** + +```lua +addKeyEventListener("F1", function(isPressed) + if isPressed then + print("F1 pressed") + else + print("F1 released") + end +end, "both") +``` + +--- + +### `getKeyState(keyname)` + +返回键的当前状态 + +**参数:** + +- `keyname` (字符串)- 按键名称 + +**返回:** + +- (布尔值)- 按下时为true,否则为false + +**注意:**全局函数。仅适用于通过 addKeyEventListener 注册的键。 + +**用法:** + +```lua +local isPressed = getKeyState("NUMPAD1") +if isPressed then + print("NUMPAD1 is held down") +end +``` + +--- + +## UI 函数 + +### `MPGameNetwork.spawnUiDialog(dialogInfo)` + +创建自定义交互式对话框 + +**参数:** + +- `dialogInfo` (表)- 对话框配置: + - `title` (字符串)- 对话框标题(可选) + - `body` (字符串)- 对话框消息(可选) + - `buttons` (表) - 按钮配置(可选) + - `class` (字符串)- “experimental” 用于危险警示线(可选) + - `interactionID` (字符串)- 交互标识符(可选) + - `reportToServer` (布尔值)- 发送到服务器(可选,默认值:false) + - `reportToExtensions` (布尔值)- 触发本地事件(可选,默认值:false) + +**用法:** + +```lua +-- Simple dialog +extensions.MPGameNetwork.spawnUiDialog({ + title = "Welcome", + body = "Welcome to the server!" +}) + +-- Choice dialog +extensions.MPGameNetwork.spawnUiDialog({ + title = "Choose Team", + body = "Which team?", + buttons = { + {label = "Red", key = "joinRed"}, + {label = "Blue", key = "joinBlue"} + }, + interactionID = "teamSelection", + reportToServer = true +}) +``` + +--- + +## MPGameNetwork 回调 + +### `MPGameNetwork.onUpdate(dt)` + +连接到多人游戏时,每一帧都会被调用 + +**参数:** + +- `dt` (数值)- 时间差(秒) + +**用法:** + +```lua +local originalOnUpdate = MPGameNetwork.onUpdate +MPGameNetwork.onUpdate = function(dt) + originalOnUpdate(dt) + -- Your code here +end +``` + +--- + +### `MPGameNetwork.onVehicleReady(gameVehicleID)` + +当车辆准备就绪且扩展设备已加载完毕时,会发出呼叫。 + +**参数:** + +- `gameVehicleID` (数字)- 游戏内部车辆 ID + +**用法:** + +```lua +local originalOnVehicleReady = MPGameNetwork.onVehicleReady +MPGameNetwork.onVehicleReady = function(gameVehicleID) + originalOnVehicleReady(gameVehicleID) + -- Your code here +end +``` + +--- + +## 编码函数 + +### `MPHelpers.b64encode(string)` + +将字符串编码为 Base64(RFC 2045) + +**参数:** + +- `string` (字符串)- 要编码的字符串 + +**返回:** + +- (字符串)- Base64 编码的字符串 + +**用法:** + +```lua +local encoded = extensions.MPHelpers.b64encode("Hello World") + +-- Encoding JSON +local data = {name = "Player", score = 100} +local encoded = extensions.MPHelpers.b64encode(jsonEncode(data)) +TriggerServerEvent("sendData", encoded) +``` + +--- + +### `MPHelpers.b64decode(string)` + +解码 Base64 字符串(RFC 2045) + +**参数:** + +- `string` (string)- Base64 编码的字符串 + +**返回:** + +- (字符串)- 解码后的字符串 + +**用法:** + +```lua +local decoded = extensions.MPHelpers.b64decode("SGVsbG8gV29ybGQ=") + +-- Decoding JSON +AddEventHandler("receiveData", function(data) + local decoded = extensions.MPHelpers.b64decode(data) + local jsonData = jsonDecode(decoded) +end) +``` + +--- + +## 颜色函数 + +### `MPHelpers.hex2rgb(hex)` + +将十六进制颜色代码转换为 RGB 值 + +**参数:** + +- `hex` (字符串)- 十六进制颜色代码(例如,“#FF5733”或“#F57”) + +**返回:** + +- (表)- RGB 值`{r, g, b}`范围为 0-1 +- (表)- 如果无效则为`{0, 0, 0}` + +**注意:**支持 3 位和 6 位十六进制代码。 + +**用法:** + +```lua +local rgb = extensions.MPHelpers.hex2rgb("#FF5733") +print(rgb[1], rgb[2], rgb[3]) -- 1.0, 0.341, 0.2 + +-- Short format +local rgb = extensions.MPHelpers.hex2rgb("#F57") +``` + +--- + +## 字符串函数 + +### `MPHelpers.splitStringToTable(string, delimiter, convert_into)` + +按分隔符拆分字符串,并可选择转换值 + +**参数:** + +- `string` (字符串)- 要拆分的字符串 +- `delimiter` (字符串) - 用于分割的分隔符 +- `convert_into` (number) - 转换类型(可选): + - `nil`或`0` - 保留为字符串(默认值) + - `1` - 转换为数字 + - `2` - 转换为布尔值 + +**返回:** + +- (表)- 分割值的数组 + +**用法:** + +```lua +-- Strings +local parts = extensions.MPHelpers.splitStringToTable("Hello,World", ",") +-- {"Hello", "World"} + +-- Numbers +local nums = extensions.MPHelpers.splitStringToTable("10,20,30", ",", 1) +-- {10, 20, 30} + +-- Parse coordinates +local coords = extensions.MPHelpers.splitStringToTable("100,200,50", ",", 1) +local x, y, z = coords[1], coords[2], coords[3] +``` + +--- + +## 表函数 + +### `MPHelpers.tableDiff(old, new)` + +比较两个表并返回它们的差异 + +**参数:** + +- `old`表 - 要比较的第一个表 +- `new` (表)——第二个用于比较的表 + +**返回:** + +- (表) `diff` - 所有不同的键 +- (表) `o` - 与旧值不同的值 +- (表) `n` - 与新值不同的数值 + +**用法:** + +```lua +local oldConfig = {speed = 100, damage = 50, armor = 30} +local newConfig = {speed = 120, damage = 50, armor = 40} + +local diff, oldVals, newVals = extensions.MPHelpers.tableDiff(oldConfig, newConfig) +-- diff = {speed = 120, armor = 40} + +for key, value in pairs(diff) do + print(key .. " changed from " .. oldVals[key] .. " to " .. newVals[key]) +end +``` + +--- + +## 调试函数 + +### `MPHelpers.simpletraces(level)` + +返回格式化的调用者信息字符串。 + +**参数:** + +- `level` (数字)- 堆栈层级(可选,默认值:2) + +**返回:** + +- (字符串)- 格式化字符串: `"source:line, namewhat name"` +- (字符串)- 如果信息不可用,则为`"unknown"` + +**用法:** + +```lua +local function myFunction() + local caller = extensions.MPHelpers.simpletraces() + print("Called from: " .. caller) +end +``` + +--- + +### `MPHelpers.simpletrace(level)` + +将调用者信息记录到控制台 + +**参数:** + +- `level` (数字)- 堆栈层级(可选,默认值:1) + +**注意:**将调用位置记录到控制台。 + +**用法:** + +```lua +local function myFunction() + extensions.MPHelpers.simpletrace() + -- Logs: "Code was called from: lua/ge/extensions/mymod.lua:42" +end +``` + +--- + +*最后更新日期:2026年1月1日* diff --git a/docs/zh/API documentation/Server-Side.md b/docs/zh/API documentation/Server-Side.md new file mode 100644 index 00000000..73f5cf3f --- /dev/null +++ b/docs/zh/API documentation/Server-Side.md @@ -0,0 +1,1549 @@ +## 目录 + +### 全局 + +- [全局函数](#global-functions) + +### MP + +- [玩家](#mp--players) +- [车辆](#mp--vehicles) +- [通信](#mp--communication) +- [事件](#mp--events) +- [工具](#mp--utilities) + +### Util + +- [日志记录](#util--logging) +- [JSON](#util--json) +- [随机数](#util--random) +- [性能分析](#util--profiling) + +### Http + +- [HTTP 函数](#http) + +### FS + +- [检查](#fs--checks) +- [操作](#fs--operations) +- [路径](#fs--paths) + +### 事件 + +- [事件参考](#events) + +--- + +## 全局函数 + +### `print(...)` + +打印到服务器控制台,以日期、时间和`[LUA]`为前缀。 + +**参数:** + +- `...` (任意)——任何类型的值。表会连同其内容一起打印出来。 + +**用法:** + +```lua +local name = "John Doe" +print("Hello, I'm", name, "and I'm", 32) +``` + +--- + +### `printRaw(...)` + +不带任何前缀直接打印到服务器控制台。 + +**参数:** + +- `...` (任意)——任何类型的值。 + +--- + +### `exit()` + +正常关闭服务器。触发`onShutdown`事件。 + +--- + +## MP — 玩家 + +### `MP.GetPlayerCount() -> number` + +返回当前在线玩家的数量。 + +**返回:** + +- (数字)- 玩家人数。 + +--- + +### `MP.GetPlayers() -> table` + +返回所有已连接玩家的表。 + +**返回:** + +- (表)- `{[playerID] = playerName}`的映射。 + +--- + +### `MP.GetPlayerName(playerID) -> string` + +根据ID返回玩家名称。 + +**参数:** + +- `playerID` (数字)- 玩家的 ID。 + +**返回:** + +- (字符串)- 玩家名称,如果找不到则为`""` 。 + +**用法:** + +```lua +local player_id = 4 +print(MP.GetPlayerName(player_id)) +``` + +--- + +### `MP.GetPlayerIDByName(name) -> number` + +根据名称返回玩家的 ID。 + +**参数:** + +- `name` (字符串)- 玩家的名称。 + +**返回:** + +- (数字)- 玩家 ID,如果找不到则为`-1` 。 + +--- + +### `MP.GetPlayerIdentifiers(playerID) -> table` + +返回玩家的标识符,例如 IP 地址、BeamMP 论坛 ID 和 Discord ID。 + +**参数:** + +- `playerID` (数字)- 玩家的 ID。 + +**返回:** + +- (表)- 包含键`ip` 、 `beammp` 、 `discord` (仅当已链接时)的表。 +- (nil)- 如果未找到玩家。 + +**用法:** + +```lua +local player_id = 5 +print(MP.GetPlayerIdentifiers(player_id)) +-- { ip: "127.0.0.1", discord: "12345678987654321", beammp: "1234567" } +``` + +--- + +### `MP.GetPlayerRole(playerID) -> string|nil` + +返回玩家在 BeamMP 后端设置的角色。 + +**参数:** + +- `playerID` (数字)- 玩家的 ID。 + +**返回:** + +- (字符串)- 玩家的角色。 +- (nil)- 如果未找到玩家。 + +--- + +### `MP.IsPlayerConnected(playerID) -> boolean` + +返回是否已收到来自玩家的 UDP 数据包,即连接是否已完全建立。 + +**参数:** + +- `playerID` (数字)- 玩家的 ID。 + +**返回:** + +- (布尔值)- 如果完全连接, `true` 。 + +**用法:** + +```lua +local player_id = 8 +print(MP.IsPlayerConnected(player_id)) +``` + +--- + +### `MP.IsPlayerGuest(playerID) -> boolean` + +返回玩家是否为访客(未在 BeamMP 论坛注册)。 + +**参数:** + +- `playerID` (数字)- 玩家的 ID。 + +**返回:** + +- (布尔值)- 如果是访客`true` 。 + +--- + +### `MP.DropPlayer(playerID, reason?)` + +将玩家踢出服务器。 + +**参数:** + +- `playerID` (数字)- 玩家的 ID。 +- `reason` (字符串,可选)- 被踢的原因。 + +**用法:** + +```lua +function ChatHandler(player_id, player_name, message) + if string.match(message, "darn") then + MP.DropPlayer(player_id, "Profanity is not allowed") + return 1 + end +end +``` + +--- + +## MP — 车辆 + +### `MP.GetPlayerVehicles(playerID) -> table` + +返回玩家的所有车辆。 + +**参数:** + +- `playerID` (数字)- 玩家的 ID。 + +**返回:** + +- (表)- `{[vehicleID] = dataString}`的映射,其中 dataString 是原始 JSON 字符串。 +- (nil)- 如果玩家没有车辆或未被找到。 + +**用法:** + +```lua +local player_id = 3 +local player_vehicles = MP.GetPlayerVehicles(player_id) + +for vehicle_id, vehicle_data in pairs(player_vehicles) do + local start = string.find(vehicle_data, "{") + local formattedVehicleData = string.sub(vehicle_data, start, -1) + print(Util.JsonDecode(formattedVehicleData)) +end +``` + +--- + +### `MP.GetPositionRaw(playerID, vehicleID) -> table, string` + +返回车辆的当前原始位置。 + +**参数:** + +- `playerID` (数字)- 玩家的 ID。 +- `vehicleID` (number)- 车辆的 ID。 + +**返回:** + +- (表)- 包含以下键的表: `pos` 、 `rot` 、 `vel` 、 `rvel` 、 `tim` 、 `ping` 。 +- (字符串)- 如果发生错误,则显示错误信息;成功时显示空字符串。 + +**注意:** `pos` 、 `rot` 、 `vel` 、 `rvel`中的每个值都是一个表,索引为`1, 2, 3` ( `rot`的索引为`4` )。 + +**用法:** + +```lua +local player_id = 4 +local vehicle_id = 0 + +local raw_pos, error = MP.GetPositionRaw(player_id, vehicle_id) +if error == "" then + local x, y, z = table.unpack(raw_pos["pos"]) + print("X:", x, "Y:", y, "Z:", z) +else + print(error) +end +``` + +--- + +### `MP.RemoveVehicle(playerID, vehicleID)` + +移除玩家拥有的车辆。 + +**参数:** + +- `playerID` (数字)- 玩家的 ID。 +- `vehicleID` (number)- 车辆的 ID。 + +**用法:** + +```lua +local player_id = 3 +local player_vehicles = MP.GetPlayerVehicles(player_id) + +for vehicle_id, vehicle_data in pairs(player_vehicles) do + MP.RemoveVehicle(player_id, vehicle_id) +end +``` + +--- + +## MP — 通讯 + +### `MP.SendChatMessage(playerID, message, logChat?)` + +向特定玩家或所有人发送聊天消息。 + +**参数:** + +- `playerID` (数字)- 玩家的 ID, `-1`表示所有人。 +- `message` (字符串)- 消息内容。 +- `logChat` (布尔值,可选)- 是否记录到服务器日志(默认值: `true` )。 + +**注意:**此函数不返回值。 + +**用法:** + +```lua +-- To a specific player +function ChatHandler(player_id, player_name, msg) + if string.match(msg, "darn") then + MP.SendChatMessage(player_id, "Please do not use profanity.") + return 1 + end +end + +-- To everyone +MP.SendChatMessage(-1, "Hello World!") +``` + +--- + +### `MP.SendNotification(playerID, message, icon?, category?)` + +向特定玩家或所有玩家发送通知(弹出窗口)。 + +**参数:** + +- `playerID` (数字)- 玩家的 ID, `-1`表示所有人。 +- `message` (字符串)- 通知内容。 +- `icon` (字符串,可选)- 通知图标。 +- `category` (字符串,可选)- 通知类别。 + +**注意:**此函数不返回值。当仅使用 3 个参数(不指定类别)调用时,类别会自动设置为消息的值。 + +--- + +### `MP.ConfirmationDialog(playerID, title, body, buttons, interactionID, warning?, reportToServer?, reportToExtensions?)` + +向玩家发送带有按钮的确认对话框。 + +**参数:** + +- `playerID` (数字)- 玩家的 ID, `-1`表示所有人。 +- `title` (字符串)- 对话框标题。 +- `body` (字符串)- 对话框正文。 +- `buttons` (表)- 按钮数组。 +- `interactionID` (字符串)- 此交互的唯一标识符。 +- `warning` (布尔值,可选)- 显示警告样式(默认值: `false` )。 +- `reportToServer` (布尔值,可选)- 向服务器发送响应(默认值: `true` )。 +- `reportToExtensions` (布尔值,可选)- 触发本地事件(默认值: `true` )。 + +**注意:**当只使用 5 个参数调用该函数时,该函数不返回值。当使用 6-8 个参数调用该函数时,它会返回`boolean, string` 。 + +--- + +### `MP.TriggerClientEvent(playerID, eventName, data) -> boolean, string` + +将事件发送给特定客户端或所有人。 + +**参数:** + +- `playerID` (数字)- 玩家的 ID, `-1`表示所有人。 +- `eventName` (字符串)- 事件名称。 +- `data` (字符串)- 要发送的数据。 + +**返回:** + +- (布尔值)- 如果发送成功则为`true` `-1`始终为`true` 。 +- (字符串)- 如果失败,则显示错误消息。 + +--- + +### `MP.TriggerClientEventJson(playerID, eventName, data) -> boolean, string` + +与`TriggerClientEvent`相同,但接受 Lua 表并自动将其编码为 JSON。 + +**参数:** + +- `playerID` (数字)- 玩家的 ID, `-1`表示所有人。 +- `eventName` (字符串)- 事件名称。 +- `data` (表) - 要进行 JSON 编码并发送的 Lua 表。 + +**返回:** + +- (布尔值)- 成功时为`true` 。 +- (字符串)- 如果失败,则显示错误消息。 + +--- + +## MP — 事件 + +### `MP.RegisterEvent(eventName, functionName)` + +将函数注册为事件的处理程序。 + +**参数:** + +- `eventName` (字符串)- 事件名称。 +- `functionName` (字符串)- 要注册的 Lua 函数的名称。 + +**注意:**如果事件不存在,则会创建该事件。可以为同一个事件注册多个处理程序。 + +**用法:** + +```lua +function ChatHandler(player_id, player_name, msg) + if msg == "hello" then + print("Hello World!") + return 0 + end +end + +MP.RegisterEvent("onChatMessage", "ChatHandler") +``` + +--- + +### `MP.TriggerLocalEvent(eventName, ...) -> table` + +仅在当前状态下触发事件。同步。 + +**参数:** + +- `eventName` (字符串)- 事件名称。 +- `...` (任意,可选)——传递给处理程序的参数。 + +**返回:** + +- (表)- 所有处理程序的返回值表。 + +**用法:** + +```lua +local Results = MP.TriggerLocalEvent("MyEvent") +print(Results) +``` + +--- + +### `MP.TriggerGlobalEvent(eventName, ...) -> table` + +在所有状态下触发事件。异步执行。本地处理程序同步且立即运行。 + +**参数:** + +- `eventName` (字符串)- 事件名称。 +- `...` (任意,可选)- 参数。支持的类型:字符串、数字、布尔值、表。 + +**返回:** + +- (表)- 类似 Future 的对象,包含: + - `:IsDone() -> boolean` — 所有处理程序是否已完成。 + - `:GetResults() -> table` — 返回所有处理程序的值。 + +**注意:**调用这些方法时要用冒号`:`不要用句点`.` 。 + +**用法:** + +```lua +local Future = MP.TriggerGlobalEvent("MyEvent") +while not Future:IsDone() do + MP.Sleep(100) +end +local Results = Future:GetResults() +print(Results) +``` + +--- + +### `MP.CreateEventTimer(eventName, intervalMS, strategy?)` + +创建一个定时器,重复触发一个事件。 + +**参数:** + +- `eventName` (字符串)- 要触发的事件。 +- `intervalMS` (数值)- 触发之间的间隔,以毫秒为单位。 +- `strategy` (数字,可选)- `MP.CallStrategy.BestEffort` (默认值)或`MP.CallStrategy.Precise` 。 + +**注意:**不建议使用低于 25 毫秒的间隔,且服务可能无法稳定运行。 + +**用法:** + +```lua +local seconds = 0 + +function CountSeconds() + seconds = seconds + 1 +end + +MP.RegisterEvent("EverySecond", "CountSeconds") +MP.CreateEventTimer("EverySecond", 1000) +``` + +--- + +### `MP.CancelEventTimer(eventName)` + +取消已存在的事件计时器。 + +**参数:** + +- `eventName` (字符串)- 事件名称。 + +**注意:**由于异步行为,该事件在被取消之前可能会再次触发一次。 + +--- + +## MP — 工具 + +### `MP.CreateTimer() -> table` + +创建一个计时器对象,用于测量经过的时间。 + +**返回:** + +- (表)- 对象包含: + - `:GetCurrent() -> float` — 自上次开始以来经过的秒数。 + - `:Start()` — 重置计时器。 + +**用法:** + +```lua +local mytimer = MP.CreateTimer() +-- do stuff here that needs to be timed +print(mytimer:GetCurrent()) +``` + +--- + +### `MP.GetOSName() -> string` + +返回服务器操作系统的名称。 + +**返回:** + +- (字符串)- `"Windows"` 、 `"Linux"`或`"Other"` 。 + +--- + +### `MP.GetServerVersion() -> number, number, number` + +返回服务器版本。 + +**返回:** + +- (数字)- 主要 +- (数字)- 次要 +- (number)- 补丁 + +**用法:** + +```lua +local major, minor, patch = MP.GetServerVersion() +print(major, minor, patch) +``` + +--- + +### `MP.Get(configID) -> value` + +通过 ID 读取服务器配置设置。 + +**参数:** + +- `configID` (数字)- 来自`MP.Settings`的 ID。 + +**返回:** + +- (值)- 设置的当前值。 + +--- + +### `MP.Set(configID, value)` + +临时更改服务器配置设置。更改不会保存到配置文件中。 + +**参数:** + +- `configID` (数字)- 来自`MP.Settings`的 ID。 +- `value` (任意)- 新值。类型必须与设置匹配。 + +**用法:** + +```lua +MP.Set(MP.Settings.Debug, true) +``` + +--- + +### `MP.Settings` + +用于`MP.Get`和`MP.Set`设置 ID 枚举。 + +```lua +MP.Settings.Debug -- 0 (boolean) +MP.Settings.Private -- 1 (boolean) +MP.Settings.MaxCars -- 2 (number) +MP.Settings.MaxPlayers -- 3 (number) +MP.Settings.Map -- 4 (string) +MP.Settings.Name -- 5 (string) +MP.Settings.Description -- 6 (string) +MP.Settings.InformationPacket -- 7 (boolean) +``` + +--- + +### `MP.CallStrategy` + +用于`MP.CreateEventTimer`枚举。 + +```lua +MP.CallStrategy.BestEffort -- Skip trigger if previous handler hasn't finished (default) +MP.CallStrategy.Precise -- Always trigger, even if it causes the queue to build up +``` + +--- + +### `MP.Sleep(ms)` + +暂停当前​​ Lua 状态若干毫秒。 + +**参数:** + +- `ms` (数字)- 睡眠时间(以毫秒为单位)。 + +**注意:**睡眠状态下不会执行任何操作。如果注册了事件处理程序,**请勿让服务器睡眠超过 500 毫秒**——睡眠状态会显著降低整个服务器的运行速度。 + +**用法:** + +```lua +local Future = MP.TriggerGlobalEvent("MyEvent") +while not Future:IsDone() do + MP.Sleep(100) +end +``` + +--- + +### `MP.GetStateMemoryUsage() -> number` + +返回当前 Lua 状态的内存使用情况。 + +**返回:** + +- (数字)- 内存大小(以字节为单位)。 + +--- + +### `MP.GetLuaMemoryUsage() -> number` + +返回所有 Lua 状态的总内存使用量。 + +**返回:** + +- (数字)- 内存大小(以字节为单位)。 + +--- + +## Util — 日志记录 + +### `Util.LogInfo(...)` 、 `Util.LogWarn(...)` 、 `Util.LogError(...)` 、 `Util.LogDebug(...)` + +在相应级别的服务器日志中打印信息。 + +**参数:** + +- `...` (任意)——任何类型的值。 + +**注意:**仅当启用`MP.Settings.Debug`时才会显示`Util.LogDebug` 。 + +**用法:** + +```lua +Util.LogInfo("Hello, World!") +Util.LogWarn("Cool warning") +Util.LogError("Oh no!") +Util.LogDebug("hi") +``` + +--- + +## Util — JSON + +### `Util.JsonEncode(table) -> string` + +将 Lua 表编码为 JSON 字符串。 + +**参数:** + +- `table` (table) - 要编码的表。 + +**返回:** + +- (字符串)- 压缩后的 JSON 字符串。 + +**注意:**根据键类型自动检测数组还是对象。函数、用户数据和不支持的类型将被忽略。 + +**用法:** + +```lua +local player = { + name = "Lion", + age = 69, + skills = { "skill A", "skill B" } +} +local json = Util.JsonEncode(player) +-- '{"name":"Lion","age":69,"skills":["skill A","skill B"]}' +``` + +--- + +### `Util.JsonDecode(json) -> table` + +将 JSON 字符串解码为 Lua 表。 + +**参数:** + +- `json` (字符串)- 有效的 JSON 字符串。 + +**返回:** + +- (表)——解码后的表。 +- (nil)- 如果 JSON 无效。 + +**用法:** + +```lua +local json = "{\"message\":\"OK\",\"code\":200}" +local tbl = Util.JsonDecode(json) +-- { message = "OK", code = 200 } +``` + +--- + +### `Util.JsonPrettify(json) -> string` + +为 JSON 字符串添加缩进和换行符,以提高可读性(缩进为 4)。 + +**参数:** + +- `json` (字符串)- 有效的 JSON 字符串。 + +**返回:** + +- (字符串)- 美化打印的 JSON。 + +**用法:** + +```lua +local myjson = Util.JsonEncode({ name="Lion", age = 69, skills = { "skill A", "skill B" } }) +print(Util.JsonPrettify(myjson)) +``` + +--- + +### `Util.JsonMinify(json) -> string` + +从 JSON 字符串中删除不必要的空格和换行符。 + +**参数:** + +- `json` (字符串)- 有效的 JSON 字符串。 + +**返回:** + +- (字符串)- 压缩后的 JSON。 + +**用法:** + +```lua +local pretty = Util.JsonPrettify(Util.JsonEncode({ name="Lion", age = 69 })) +print(Util.JsonMinify(pretty)) +``` + +--- + +### `Util.JsonFlatten(json) -> string` + +根据 RFC 6901 将嵌套的 JSON 扁平化为`/a/b/c`样式的键。 + +**参数:** + +- `json` (字符串)- 有效的 JSON 字符串。 + +**返回:** + +- (字符串)- 扁平化的 JSON。 + +**用法:** + +```lua +local json = Util.JsonEncode({ name="Lion", skills = { "skill A", "skill B" } }) +print(Util.JsonFlatten(json)) +-- '{"/name":"Lion","/skills/0":"skill A","/skills/1":"skill B"}' +``` + +--- + +### `Util.JsonUnflatten(json) -> string` + +将扁平化的 JSON 恢复到其嵌套结构。 + +**参数:** + +- `json` (字符串)- 扁平化的 JSON 字符串。 + +**返回:** + +- (字符串)- 嵌套 JSON。 + +--- + +### `Util.JsonDiff(a, b) -> string` + +根据 RFC 6902 计算两个 JSON 字符串之间的差异。 + +**参数:** + +- `a` (字符串)- 第一个 JSON 字符串。 +- `b` (字符串)- 第二个 JSON 字符串。 + +**返回:** + +- (字符串)- 表示差异的 JSON 补丁。 + +--- + +## Util — 随机 + +### `Util.Random() -> float` + +返回一个介于 0 和 1 之间的随机浮点数。 + +**返回:** + +- (float) + +**用法:** + +```lua +local rand = Util.Random() +print("rand: " .. rand) +-- rand: 0.135477 +``` + +--- + +### `Util.RandomRange(min, max) -> float` + +返回给定范围内的随机浮点数。 + +**参数:** + +- `min` (数字)- 下限。 +- `max` (数字)- 上限。 + +**返回:** + +- (float) + +**用法:** + +```lua +local randFloat = Util.RandomRange(1, 1000) +print("randFloat: " .. randFloat) +-- randFloat: 420.6969 +``` + +--- + +### `Util.RandomIntRange(min, max) -> number` + +返回给定范围内的随机整数。 + +**参数:** + +- `min` (数字)- 下限。 +- `max` (数字)- 上限。 + +**返回:** + +- (数字)- 整数。 + +**用法:** + +```lua +local randInt = Util.RandomIntRange(1, 100) +print("randInt: " .. randInt) +-- randInt: 69 +``` + +--- + +## Util — 分析 + +### `Util.DebugStartProfile(name)` + +启动一个命名执行时间测量。 + +**参数:** + +- `name` (字符串)- 此测量的标识符。 + +--- + +### `Util.DebugStopProfile(name)` + +停止指定名称的测量。必须在调用`DebugStartProfile`之后,并使用相同的名称调用此函数。 + +**参数:** + +- `name` (字符串)- 此测量的标识符。 + +--- + +### `Util.DebugExecutionTime() -> table` + +返回每个已运行处理程序的执行时间统计信息。 + +**返回:** + +- (表)- 每个处理程序: `mean` 、 `stdev` 、 `min` 、 `max` 、 `n` (全部以毫秒为单位)。 + +**用法:** + +```lua +function printDebugExecutionTime() + local stats = Util.DebugExecutionTime() + local pretty = "DebugExecutionTime:\n" + local longest = 0 + for name, t in pairs(stats) do + if #name > longest then + longest = #name + end + end + for name, t in pairs(stats) do + pretty = pretty .. string.format("%" .. longest + 1 .. "s: %12f +/- %12f (min: %12f, max: %12f) (called %d time(s))\n", name, t.mean, t.stdev, t.min, t.max, t.n) + end + print(pretty) +end +``` + +--- + +## Http + +### `Http.CreateConnection(host, port) -> table` + +创建与外部服务器的HTTP连接。 + +**参数:** + +- `host` (字符串)- 服务器地址。 +- `port` (number)- 端口号。 + +**返回:** + +- (表)- 具有方法的连接对象`:Get(path, headers)` 。 + +--- + +### `connection:Get(path, headers)` + +发送HTTP GET请求。 + +**参数:** + +- `path` (字符串)- 请求路径。 +- `headers` (表)- 表头作为`{[string] = string}` 。 + +--- + +## FS — 检查 + +### `FS.Exists(path) -> boolean` + +返回路径是否存在。 + +**参数:** + +- `path` (字符串)- 要检查的路径。 + +**返回:** + +- (布尔值)- 如果存在`true` 。 + +--- + +### `FS.IsDirectory(path) -> boolean` + +返回指定路径是否为目录。 + +**参数:** + +- `path` (字符串)- 要检查的路径。 + +**返回:** + +- (布尔值)- 如果目录为`true` 。 + +**注意:** `false`并不意味着该路径就是一个文件。请使用`FS.IsFile`单独进行检查。 + +--- + +### `FS.IsFile(path) -> boolean` + +返回指定路径是否为普通文件。 + +**参数:** + +- `path` (字符串)- 要检查的路径。 + +**返回:** + +- (布尔值)- 如果是普通文件`true` 。 + +**注意:** `false`并不意味着该路径是一个目录。 + +--- + +## FS — 操作 + +### `FS.CreateDirectory(path) -> boolean, string` + +创建目录,包括任何缺失的父目录(类似于`mkdir -p` )。 + +**参数:** + +- `path` (字符串)- 要创建的目录的路径。 + +**返回:** + +- (布尔值)- 成功时为`true` 。 +- (字符串)- 失败时显示错误消息,成功时显示`""` 。 + +**用法:** + +```lua +local success, error_message = FS.CreateDirectory("data/mystuff/somefolder") + +if not success then + print("failed to create directory: " .. error_message) +end +``` + +--- + +### `FS.Remove(path) -> boolean, string` + +删除文件或空目录。 + +**参数:** + +- `path` (字符串)- 要删除的路径。 + +**返回:** + +- (布尔值)- 成功时为`true` 。 +- (字符串)- 失败时的错误信息。 + +--- + +### `FS.Rename(path, newPath) -> boolean, string` + +重命名或移动文件或目录。 + +**参数:** + +- `path` (字符串)- 当前路径。 +- `newPath` (字符串)- 新路径。 + +**返回:** + +- (布尔值)- 成功时为`true` 。 +- (字符串)- 失败时的错误信息。 + +--- + +### `FS.Copy(path, newPath) -> boolean, string` + +复制文件或目录(递归)。 + +**参数:** + +- `path` (字符串)- 源路径。 +- `newPath` (字符串)- 目标路径。 + +**返回:** + +- (布尔值)- 成功时为`true` 。 +- (字符串)- 失败时的错误信息。 + +--- + +### `FS.ListFiles(path) -> table` + +返回目录中的文件名列表(非递归)。 + +**参数:** + +- `path` (字符串)- 目录路径。 + +**返回:** + +- (表)- 文件名数组。 +- (nil)- 如果路径不存在。 + +**用法:** + +```lua +print(FS.ListFiles("Resources/Server/examplePlugin")) +-- { 1: "example.json", 2: "example.lua" } +``` + +--- + +### `FS.ListDirectories(path) -> table` + +返回目录内所有目录名称的列表(非递归)。 + +**参数:** + +- `path` (字符串)- 目录路径。 + +**返回:** + +- (表格)- 目录名称数组。 +- (nil)- 如果路径不存在。 + +**用法:** + +```lua +print(FS.ListDirectories("Resources")) +-- { 1: "Client", 2: "Server" } +``` + +--- + +## FS — 路径 + +### `FS.GetFilename(path) -> string` + +从指定路径返回带扩展名的文件名。 + +**参数:** + +- `path` (字符串)- 路径字符串。 + +**返回:** + +- (字符串)- 文件名。 + +**用法:** + +```lua +"my/path/a.txt" -> "a.txt" +"somefile.txt" -> "somefile.txt" +"/awesome/path" -> "path" +``` + +--- + +### `FS.GetExtension(path) -> string` + +返回包含点号的文件扩展名。 + +**参数:** + +- `path` (字符串)- 路径字符串。 + +**返回:** + +- (字符串)- 扩展名(例如`".json"` ),如果没有扩展名,则为`""` 。 + +**用法:** + +```lua +"myfile.txt" -> ".txt" +"somefile." -> "." +"/awesome/path" -> "" +"/awesome/path/file.zip.txt" -> ".txt" +``` + +--- + +### `FS.GetParentFolder(path) -> string` + +返回包含目录的路径。 + +**参数:** + +- `path` (字符串)- 路径字符串。 + +**返回:** + +- (字符串)- 父文件夹路径。 + +**用法:** + +```lua +"/var/tmp/example.txt" -> "/var/tmp" +"/" -> "/" +"mydir/a/b/c.txt" -> "mydir/a/b" +``` + +--- + +### `FS.ConcatPaths(...) -> string` + +使用系统首选分隔符将路径段连接起来,并解析存在的`..`分隔符。 + +**参数:** + +- `...` (字符串)- 路径段。 + +**返回:** + +- (字符串)- 连接路径。 + +**用法:** + +```lua +FS.ConcatPaths("a", "b", "/c/d/e/", "/f/", "g", "h.txt") +-- "a/b/c/d/e/f/g/h.txt" +``` + +--- + +## 事件 + +### 玩家连接顺序 + +``` +onPlayerAuth → onPlayerConnecting → onPlayerJoining → onPlayerJoin +``` + +--- + +### `onInit` + +插件文件全部加载完毕后立即触发。 + +**参数:**无**可取消:**否 + +--- + +### `onConsoleInput` + +当服务器控制台收到输入时触发。 + +**参数:** + +- `input` (字符串)- 输入的文本。 + +**可取消:**否 + +**用法:** + +```lua +function handleConsoleInput(cmd) + local delim = cmd:find(' ') + if delim then + local message = cmd:sub(delim+1) + if cmd:sub(1, delim-1) == "print" then + return message + end + end +end + +MP.RegisterEvent("onConsoleInput", "handleConsoleInput") +``` + +--- + +### `onShutdown` + +服务器关闭且所有玩家都被踢出后触发。 + +**参数:**无**可取消:**否 + +--- + +### `onPlayerAuth` + +当玩家尝试连接时触发,在任何其他连接事件之前触发。 + +**参数:** + +- `name` (字符串)- 玩家名称。 +- `role` (字符串)- 后端玩家角色。 +- `isGuest` (布尔值)- 玩家是否为访客。 +- `identifiers` (表) - 标识符: `ip` 、 `beammp` 、 `discord` 。 + +**可取消:**是 + +- 返回`1` — 以通用消息拒绝。 +- 返回`string` ——拒绝,拒绝理由为该字符串。 +- 返回`2` — 即使服务器已满也允许进入。 + +**用法:** + +```lua +function myPlayerAuthorizer(name, role, is_guest, identifiers) + return "Sorry, you cannot join at this time." +end + +MP.RegisterEvent("onPlayerAuth", "myPlayerAuthorizer") +``` + +--- + +### `postPlayerAuth` + +无论玩家是否被接受或拒绝,都会在`onPlayerAuth`之后触发。 + +**参数:** + +- `wasRejected` (布尔值)- 玩家是否被拒绝。 +- `reason` (字符串)- 如果被拒绝,则显示拒绝原因。 +- `name` (字符串)- 玩家名称。 +- `role` (字符串)- 玩家角色。 +- `isGuest` (布尔值)- 是否为访客。 +- `identifiers` (表)- 标识符。 + +**可取消:**否 + +--- + +### `onPlayerConnecting` + +当玩家开始连接时触发,在`onPlayerAuth`之后。 + +**参数:** + +- `playerID` (数字) + +**可取消:**否 + +--- + +### `onPlayerJoining` + +玩家完成所有模组下载后触发。 + +**参数:** + +- `playerID` (数字) + +**可取消:**否 + +--- + +### `onPlayerJoin` + +玩家完成同步并进入游戏后触发。 + +**参数:** + +- `playerID` (数字) + +**可取消:**否 + +--- + +### `onPlayerDisconnect` + +当玩家断开连接时触发。 + +**参数:** + +- `playerID` (数字) + +**可取消:**否 + +--- + +### `onChatMessage` + +当玩家发送聊天消息时触发。 + +**参数:** + +- `playerID` (数字) +- `playerName` (字符串) +- `message` (字符串) + +**可取消:**是——返回`1`可防止消息显示给任何人。 + +**用法:** + +```lua +function MyChatMessageHandler(sender_id, sender_name, message) + if message == "darn" then + return 1 + else + return 0 + end +end + +MP.RegisterEvent("onChatMessage", "MyChatMessageHandler") +``` + +--- + +### `postChatMessage` + +在`onChatMessage`之后触发。 + +**参数:** + +- `wasSent` (布尔值)- 消息是否已发送。 +- `playerID` (数字) +- `playerName` (字符串) +- `message` (字符串) + +**可取消:**否 + +--- + +### `onVehicleSpawn` + +当玩家生成新车辆时触发。 + +**参数:** + +- `playerID` (数字) +- `vehicleID` (number) +- `data` (字符串)- 包含车辆配置和位置数据的 JSON 字符串。 + +**可取消:**是——返回非`0`值可阻止生成。 + +--- + +### `postVehicleSpawn` + +在`onVehicleSpawn`之后触发。 + +**参数:** + +- `wasSpawned` (布尔值)- 车辆是否实际生成。 +- `playerID` (数字) +- `vehicleID` (number) +- `data` (字符串) + +**可取消:**否 + +--- + +### `onVehicleEdited` + +当玩家编辑现有车辆时触发。 + +**参数:** + +- `playerID` (数字) +- `vehicleID` (number) +- `data` (字符串)- 新配置的 JSON 字符串(不包含位置数据)。 + +**可取消:**是——返回非`0`值将取消编辑。 + +--- + +### `postVehicleEdited` + +在`onVehicleEdited`之后触发。 + +**参数:** + +- `wasAllowed` (布尔值)- 是否允许编辑。 +- `playerID` (数字) +- `vehicleID` (number) +- `data` (字符串) + +**可取消:**否 + +--- + +### `onVehicleDeleted` + +当车辆被删除时触发。 + +**参数:** + +- `playerID` (数字) +- `vehicleID` (number) + +**可取消:**否 + +--- + +### `onVehicleReset` + +当玩家重置车辆时触发。 + +**参数:** + +- `playerID` (数字) +- `vehicleID` (number) +- `data` (字符串)- 新位置和旋转的 JSON 字符串(不包含配置)。 + +**可取消:**否 + +--- + +### `onVehiclePaintChanged` + +当车辆油漆发生变化时触发。 + +**参数:** + +- `playerID` (数字) +- `vehicleID` (number) +- `data` (字符串)- 包含新绘制数据的 JSON 字符串。 + +**可取消:**否 + +--- + +### `onFileChanged` + +当插件目录中的文件发生更改时触发。 + +**参数:** + +- `path` (字符串)- 相对于服务器根目录的已更改文件的路径。 + +**可取消:**否 + +**注意:**服务器启动后添加的文件不会被跟踪。 diff --git a/docs/zh/FAQ/Change-launcher-port.md b/docs/zh/FAQ/Change-launcher-port.md new file mode 100644 index 00000000..7e2a9a8d --- /dev/null +++ b/docs/zh/FAQ/Change-launcher-port.md @@ -0,0 +1,25 @@ +# 问题 + +启动器没有连接到游戏。这个快速指南解释了如何手动更改启动器端口。 + +# 手动修改端口 + +1. 启动 BeamNG +2. 在主菜单, 打开设置, 然后找到 “Multiplayer” +3. 在 “multiplayer” 设置, 启用 `显示高级选项` +4. 向下滚动 +5. 在`启动端口号`中,将端口号更改为其他内容,例如4567 +6. 关闭 BeamNG +7. 右键单击BeamMP-launcher快捷方式,并在上下文菜单中选择`“Open file location”` +8. 用文本编辑器打开`launcher.cfg`文件 +9. 将`“port”:4444,`的编号更改为与之前在游戏内选项中使用的相同,在本例中为4567 +10. 保存设置并关闭文本编辑器 +11. 启动 BeamMP-launcher + +如果仍然没有连接,用另一个端口再试一次。在~2000到65535之间的任何数字都是有效的端口 + +## 还面临问题吗? + +在我们的[Discord服务器](https://discord.gg/BeamMP)上创建一个支持票。 + +标签: Launcher, Connection Failed, Port Number diff --git a/docs/zh/FAQ/Clearing-mods.md b/docs/zh/FAQ/Clearing-mods.md new file mode 100644 index 00000000..74378f70 --- /dev/null +++ b/docs/zh/FAQ/Clearing-mods.md @@ -0,0 +1,42 @@ +## 为什么我需要停用我的模组 ? + +在BeamMP中,您决定连接的服务器提供了必要的模块。这些会在连接时被下载并自动启用。安装本地模组并启用通常会导致BeamMP不能正常工作,即使您除了BeamMP之外只有一个额外的模组。 + +!!! 警告 + +``` +移除你安装在BeamNG.Drive\content\或子文件夹的任何模组。这个位置不是用来安装mod的,可能会导致位于同一目录下的“DO_NOT_INSTALL_MODS_HERE.txt”文件中所述的问题。 + +!!! 引用“DO_NOT_INSTALL_MODS_HERE.txt” + +不要复制mod到这个文件夹:它会导致损坏的mod,更新的安装速度变慢,损坏的mod管理器,损坏的安全模式和其他。 +``` + +## 我如何停用/删除我的模组 ? + +在使用BeamMP时,有3个选项可以解决可能出现的问题。 + +### 1. 禁用模组 + +在加入任何服务器之前,请确保除了启用“multiplayerbeammp”之外没有其他mod。如果游戏死机或者你仍然有问题,请参考下一个选项 + +### 2. 创建一个新的用户文件夹 + +打开BeamNG.Drive的用户文件夹,并将`current`文件夹重命名为例如`current_old`。在重命名之前记得关闭BeamNG。![image](../../assets/content/new-userfolder.png) + +### 3. 从内容文件夹中删除模组。 + +访问BeamNG.Drive\content\文件夹并删除文件夹中的任何模组,打开安装位置的BeamNG.Drive。右键单击`content`文件夹并删除它。继续通过Steam或Epic Games验证游戏文件。这将再次下载文件。 + +完成后,通过BeamMP- launcher启动BeamNG,您应该将“multiplayerbeammp”作为模组管理器中唯一启用的mod,然后点击主菜单上的按钮进入BeamMP。如果你在加入mod服务器时仍然有问题,他们可能提供了损坏/未更新的mod。 + +### 4. 清理BeamMP-Launcher缓存 + +要从BeamMP目录中清除缓存的模组,请转到BeamMP- launcher的安装位置。默认情况下,路径为“C:\Users\AppData\BeamMP-Launcher”。在那里,你会发现一个“Resources”文件夹。删除这个文件夹来删除所有缓存的mod。如果您需要更多磁盘空间或想要删除过时的BeamNG模组,这将非常有用。 + +!!! 问题 "我的设置和配置文件都不见了! 我怎样才能恢复呢?" + +``` +如果你重命名了用户文件夹,你就会让游戏创建一个新的、干净的用户文件夹。你可以从你重命名的文件夹中复制“settings”和“vehicles”文件夹。‘ current_old ’)到它创建的新文件夹。 +确保BeamNG已关闭,并替换要复制文件夹到的位置中的所有要素。现在,您应该就会有与以前一样的所有配置和设置。 +``` diff --git a/docs/zh/FAQ/How-to-check-for-CGNAT.md b/docs/zh/FAQ/How-to-check-for-CGNAT.md new file mode 100644 index 00000000..ff3fb171 --- /dev/null +++ b/docs/zh/FAQ/How-to-check-for-CGNAT.md @@ -0,0 +1,45 @@ +如何检查CGNAT? + +## 问题 + +所有防火墙规则和端口转发规则设置正确,但没有人可以加入您的家庭托管服务器? + +如果您有连接问题,并且您正在使用托管服务,请联系他们寻求帮助。如果您想使用VPS或无法在家中托管服务器,请查看我们的[合作托管服务列表](../../server/create-a-server/#partnered-hosting-services-paid)(服务器设置文档)。 + +# CGNAT是什么? + +有关CGNAT是什么以及在尝试在家中托管服务器时为什么会出现问题的详细解释,请查看[这个页面](https://en.wikipedia.org/wiki/Carrier-grade_NAT)。 + +# 怎么检查CGNAT? + +## 方法 1: + +打开命令提示符,运行`tracert -4 beammp.com`。这将输出一系列网络跳数。等待操作完成(可能需要30跳)。检查路由器/调制解调器/网关IP后的前几个IP地址。如果有多个IP地址在`100.64.x.x`—`100.127.x。X `或`10.xx.xx。xx`出现在第一跳之后,您很可能在CGNAT后面。 + +!!! 注意 + +``` +第一跳将是你的路由器/调制解调器/网关,不同的设备不同。 +本地网络的官方范围如下:‘ ’ 10.0.0。Xxx ' ' - ' ' 192.168.xxx. Xxx。Xxx ' ' - ' ' ' 172.16.xxx.xxx ' ‘ ’ +``` + +## 方法 2: + +在路由器的接口上查找WAN IP。将其与发布在例如https://whatsmyip.org上的IP进行比较。如果它们不一样,你就落后于CGNAT。 + +## 方法 3/解决方案: + +请致电互联网服务提供商寻求帮助。根据您的ISP,他们可能不提供专用的*动态* IP地址。请记住,静态IP不是必需的。 + +!!! 警告 + +``` +互联网服务提供商可能只提供专用IP地址作为**付费选项**。 +请检查我们的合作托管服务的价格,因为他们可能比这个费用便宜! +``` + +非CGNAT网络的示例: + +![图像](https://github.com/user-attachments/assets/fee21a50-cbb0-4322-9c26-d9f04f88ae37) + +标签: Server, 10060 10061, CGNAT, Connection Failed, Port Forward, Firewall diff --git a/docs/zh/FAQ/How-to-deactivate-mods.md b/docs/zh/FAQ/How-to-deactivate-mods.md new file mode 100644 index 00000000..74378f70 --- /dev/null +++ b/docs/zh/FAQ/How-to-deactivate-mods.md @@ -0,0 +1,42 @@ +## 为什么我需要停用我的模组 ? + +在BeamMP中,您决定连接的服务器提供了必要的模块。这些会在连接时被下载并自动启用。安装本地模组并启用通常会导致BeamMP不能正常工作,即使您除了BeamMP之外只有一个额外的模组。 + +!!! 警告 + +``` +移除你安装在BeamNG.Drive\content\或子文件夹的任何模组。这个位置不是用来安装mod的,可能会导致位于同一目录下的“DO_NOT_INSTALL_MODS_HERE.txt”文件中所述的问题。 + +!!! 引用“DO_NOT_INSTALL_MODS_HERE.txt” + +不要复制mod到这个文件夹:它会导致损坏的mod,更新的安装速度变慢,损坏的mod管理器,损坏的安全模式和其他。 +``` + +## 我如何停用/删除我的模组 ? + +在使用BeamMP时,有3个选项可以解决可能出现的问题。 + +### 1. 禁用模组 + +在加入任何服务器之前,请确保除了启用“multiplayerbeammp”之外没有其他mod。如果游戏死机或者你仍然有问题,请参考下一个选项 + +### 2. 创建一个新的用户文件夹 + +打开BeamNG.Drive的用户文件夹,并将`current`文件夹重命名为例如`current_old`。在重命名之前记得关闭BeamNG。![image](../../assets/content/new-userfolder.png) + +### 3. 从内容文件夹中删除模组。 + +访问BeamNG.Drive\content\文件夹并删除文件夹中的任何模组,打开安装位置的BeamNG.Drive。右键单击`content`文件夹并删除它。继续通过Steam或Epic Games验证游戏文件。这将再次下载文件。 + +完成后,通过BeamMP- launcher启动BeamNG,您应该将“multiplayerbeammp”作为模组管理器中唯一启用的mod,然后点击主菜单上的按钮进入BeamMP。如果你在加入mod服务器时仍然有问题,他们可能提供了损坏/未更新的mod。 + +### 4. 清理BeamMP-Launcher缓存 + +要从BeamMP目录中清除缓存的模组,请转到BeamMP- launcher的安装位置。默认情况下,路径为“C:\Users\AppData\BeamMP-Launcher”。在那里,你会发现一个“Resources”文件夹。删除这个文件夹来删除所有缓存的mod。如果您需要更多磁盘空间或想要删除过时的BeamNG模组,这将非常有用。 + +!!! 问题 "我的设置和配置文件都不见了! 我怎样才能恢复呢?" + +``` +如果你重命名了用户文件夹,你就会让游戏创建一个新的、干净的用户文件夹。你可以从你重命名的文件夹中复制“settings”和“vehicles”文件夹。‘ current_old ’)到它创建的新文件夹。 +确保BeamNG已关闭,并替换要复制文件夹到的位置中的所有要素。现在,您应该就会有与以前一样的所有配置和设置。 +``` diff --git a/docs/zh/FAQ/Update-launcher.md b/docs/zh/FAQ/Update-launcher.md new file mode 100644 index 00000000..8fc0ce26 --- /dev/null +++ b/docs/zh/FAQ/Update-launcher.md @@ -0,0 +1,20 @@ +# 问题 + +启动器无法更新或显示空白屏幕?这个快速指南解释了如何手动更新启动器。 + +!!! 注意 + +``` +请确保您已通过官网(https://beammp.com)提供的安装程序完成BeamMP的安装及运行,然后再继续后续操作。 +``` + +# 下载并安装一个新的启动器 + +1. 直接从[GitHub仓库](https://github.com/BeamMP/BeamMP-Launcher/releases/latest/download/BeamMP-Launcher.exe)下载最新版启动器 +2. 定位至BeamMP-Launcher.exe所在目录:默认安装路径:`C:\Users\<用户名>\AppData\Roaming\`(将 <用户名> 替换为您的Windows账户名称)
自定义安装路径:若您此前将BeamMP安装至其他位置(例如 `D:\BeamMP-Launcher`),请将新启动器文件放入对应文件夹 +3. 在BeamMP-Launcher文件夹内,使用新启动器覆盖替换旧版本(如存在) +4. 按常规方式启动BeamMP-Launcher以验证运行状态 + +## 还面临问题? + +请于我们的[Discord服务器](https://discord.gg/BeamMP)创建技术支持工单,标签标注:Launcher, download diff --git a/docs/zh/FAQ/game-faq.md b/docs/zh/FAQ/game-faq.md new file mode 100644 index 00000000..97a4d248 --- /dev/null +++ b/docs/zh/FAQ/game-faq.md @@ -0,0 +1,64 @@ +# 常见问题解答 + +常见问题列表。 + +--- + +## **客户端** + +--- + +### **如何安装 BeamMP?** + +关于如何在Windows上安装BeamMP的完整指南,您可以在[这里](https://docs.beammp.com/game/getting-started/)找到它。 + +--- + +### **BeamMP可以运行在盗版或者修改版的BeamNG吗?** + +BeamMP 无法在盗版或旧版本的 BeamNG.drive 上运行。包括但不限于第三方插件在内的修改可能会干扰 BeamMP 的正常运行(请参阅[如何清理插件](https://github.com/Protogen187/Docs/blob/main/docs/en/FAQ/Clearing-mods.md))。
BeamMP 支持团队无法针对盗版、旧版本或其他经过修改的 BeamNG.drive 所产生的问题提供技术支持。 + +--- + +### **BeamMP 可以在 Linux 上运行吗?** + +客户端在Linux上不被官方支持。但是,您可以查看我们的[指南来了解如何在Linux上使用BeamMP](../game/getting-started/#2b-linux-installation)。 + +--- + +### **为什么启动器被我的杀毒软件或Windows Defender标记** + +由于 BeamMP 需要与网络进行交互及其他因素,部分杀毒软件可能会将其标记为威胁。但请放心,所有代码中均不含任何病毒。启动器、服务器以及 Lua 客户端的源代码均可在我们的 [GitHub](https://github.com/BeamMP) 页面上找到。 + +--- + +### **我的游戏性能很差,我该怎么办?** + +我们正在努力使多人游戏体验尽可能稳定。如果您已经降低了图形设置,但性能仍然很差,请考虑在玩家较少的服务器上进行游戏。当你和很多人一起玩游戏时,游戏主要是CPU受限的,所以旧的CPU(甚至是四核)在多人的情况下会受到影响。(一般经验法则:每个CPU线程1辆车) + +--- + +## **其他** + +--- + +### **我在哪里可以找到代码?** + +所有源代码都可以在我们的[GitHub](https://github.com/BeamMP)上找到。在进行任何更改之前,请记住代码受我们的[使用条款](https://forum.beammp.com/t/terms-of-use-v1-0/43)和许可的约束: + +代码 | 许可证 +--- | :-: +服务器 | [许可证](https://github.com/BeamMP/BeamMP-Server/blob/master/LICENSE) +启动器 | [许可证](https://github.com/BeamMP/BeamMP-Launcher/blob/master/LICENSE) +客户端 Lua | [许可证](https://github.com/BeamMP/BeamMP/blob/development/LICENSE) + +--- + +### **我发现了错误或漏洞,我该怎么办?** + +如果问题与代码相关,并且您知道如何使用Github,请在[ Github ](https://github.com/BeamMP)上的相应存储库中打开一个新的“issue”。我们使用基于问题的工作流程,所以即使你已经修复了bug,也要考虑打开一个新的“Issue”,然后打开一个包含问题解决方案的“Pull Request”。更多关于贡献的信息可以在[中找到](https://github.com/BeamMP/BeamMP/blob/development/CONTRIBUTING.md)。 + +如果您没有GitHub帐户,或者您不知道如何使用GitHub,或者您有任何其他问题,您可以通过以下方式与我们联系: + +- 如果不是敏感内容,您可以在我们的[BeamMP 论坛](https://forum.beammp.com)上创建帖子,或者您可以在我们[的官方 Discord](https://discord.gg/beammp)上报告此问题。 +- 如果信息敏感,您可以直接向我们的[Discord](https://discord.gg/beammp)上的工作人员报告问题。 diff --git a/docs/zh/FAQ/march-28-outage.md b/docs/zh/FAQ/march-28-outage.md new file mode 100644 index 00000000..d6aecb91 --- /dev/null +++ b/docs/zh/FAQ/march-28-outage.md @@ -0,0 +1,25 @@ +# 2026年3月28日 BeamMP 停机故障常见问题解答(FAQ) + +针对 2026 年 3 月 28 日开始且目前仍在持续的BeamMP停机故障的临时常见问题解答(FAQ)。 + +**最后更新于2026年4月1日** + +=== 我需要帮助!我的 BeamMP 启动器无法运行!
请尝试重新安装 BeamMP 启动器。具体操作步骤如下:

1. 访问 [beammp.com](https://beammp.com/)
2. 点击 *Download Now(立即下载)*
3. 运行安装程序并按照提示进行操作 + +``` +!!! 注意 + +截至 2026 年 4 月 1 日,Windows Defender SmartScreen 会将该 MSI 安装程序识别为“未知应用”。 + +若要跳过此警告,请点击 *更多信息*,然后点击 *仍要运行*。 +``` + +=== 我需要帮助!我的授权密钥 (authkey) 失效了!

截至 2026 年 4 月 1 日,Keymaster 和认证系统处于离线状态。这意味着您的 authkeys 将无法正常工作。若要解决此问题,请按照以下步骤操作:

1. 打开您的 ``ServerConfig.toml`` 文件,或任何存放服务器配置的地方。
2. 将 ``Private`` 设置为 ``true``。设置后应如下所示:``Private = true``。
3. 这应该能解决 authkey 的失效问题。 + +``` +!!! 注意 + +截至 2026 年 4 月 1 日,BeamMP 的认证系统处于离线状态。目前仅支持游客账号(Guest accounts)登录。 + +请确保您的服务器已开启游客访问权限。 +``` diff --git a/docs/zh/FAQ/server-faq.md b/docs/zh/FAQ/server-faq.md new file mode 100644 index 00000000..32dd2b38 --- /dev/null +++ b/docs/zh/FAQ/server-faq.md @@ -0,0 +1,89 @@ +# 常见问题和已知问题 + +常见问题和已知bug列表。 + +--- + +## **服务器** + +--- + +### **我怎么设置自己的服务器** + +设置您自己的服务器的所有信息可以在[中找到](https://docs.beammp.com/server/create-a-server/)。 + +--- + +### **服务器端兼容LINUX吗?** + +我们在这里提供了许多Linux发行版的二进制文件[。如果您的操作系统/发行版没有二进制文件,您可以通过在](https://github.com/BeamMP/BeamMP-Server/releases/latest)[GitHub](https://github.com/BeamMP/BeamMP-Server)上下载源代码来自己编译它,教程可以在中找到[。](https://github.com/BeamMP/BeamMP-Server#build-instructions) + +--- + +### **运行BeamMP服务器的最低系统要求是什么?** + +- 内存: 50+ MiB可用(不计算操作系统开销) +- CPU: >1GHz, 最好是多核 +- 操作系统: Windows、Linux(理论上兼容所有POSIX系统) +- GPU: 不需要 +- 硬盘:10 MiB + 模组/插件 +- 带宽:上传5 ~ 10m /s + +--- + +## **局域网外的玩家无法加入我的自托管服务器** + +请查阅[此处](https://docs.beammp.com/server/port-forwarding/)提供的端口转发指南,下文为其关键步骤摘要。若其他玩家通过BeamMP启动器连接您的服务器时出现错误代码10060、10061或10038,请按以下流程排查: + +- 转发端口30814(或在ServerConfig.toml中配置的其他端口),需同时开放TCP与UDP协议。 +- 请在Windows防火墙中为BeamMP放行入站与出站连接,直接关闭防火墙通常无法解决问题。 +- 确认你现在没有在使用VPN (这可能是问题的原因). +- 确保服务器确实在运行,没有任何错误或警告。 + +您可以在服务器运行时使用CheckBeamMP检查您是否成功地进行了端口转发。 + + + +
+ +
+ + + +注意: + +- 一些互联网提供商不为您的连接(CGNAT)提供专用的IPv4地址,因此端口转发可能不成功,尽管它在路由器中是可以的。 +- 如果你使用的是移动(4G/5G)互联网连接,端口转发是不可能的。 + +--- + +### **我可以在服务器列表中看到我的自托管服务器,但我不能自己加入它** + +若服务器与游戏运行于同一台设备,您需通过在直接连接中输入IP地址 127.0.0.1 和服务器端口来加入。若要通过服务器列表加入自托管服务器,您的路由器需支持NAT环回功能,但多数家用路由器未提供此功能。 + +--- + +## **其他** + +--- + +### **我在哪里可以找到源码?** + +所有源代码都可以在我们的[GitHub](https://github.com/BeamMP)上找到。在做任何事情之前,请记住代码受我们的[使用条款](https://forum.beammp.com/t/terms-of-use-v1-0/43)和许可的约束: + +代码 | 许可证 +--- | :-: +服务器 | [许可证](https://github.com/BeamMP/BeamMP-Server/blob/master/LICENSE) +启动器 | [许可证](https://github.com/BeamMP/BeamMP-Launcher/blob/master/README.md) +客户端 Lua | [许可证](https://github.com/BeamMP/BeamMP/blob/development/LICENSE.md) + +--- + +### **我发现了一个bug或漏洞,我应该怎么做?** + +如果问题与代码相关,并且您知道如何使用Github,请在[ Github ](https://github.com/BeamMP)上的适当存储库中创建一个新的“issue”。我们使用基于问题的工作流程,所以即使你已经修复了bug,也可以考虑打开一个新的“Issue”,然后请求一个“Pull Request”来解决你的“问题”。更多关于贡献的信息可以在[中找到](https://github.com/BeamMP/BeamMP/blob/development/CONTRIBUTING.md)。 + +如果您没有GitHub帐户或者您不知道如何使用GitHub,您可以通过以下方式与我们联系: + +- 如果问题不涉及隐私或安全,您可以在我们的[BeamMP论坛](https://forum.beammp.com)发帖反馈,或前往[官方Discord服务器](https://discord.gg/beammp)提交报告。 +- 如果信息敏感,您可以直接向我们的[Discord](https://discord.gg/beammp)上的工作人员报告问题。 diff --git a/docs/zh/FAQ/where-to-find-my-IP.md b/docs/zh/FAQ/where-to-find-my-IP.md new file mode 100644 index 00000000..68027b9d --- /dev/null +++ b/docs/zh/FAQ/where-to-find-my-IP.md @@ -0,0 +1,25 @@ +# 我怎样才能找到我服务器的IP ? + +## 对于VPS托管服务器 + +如果您使用的我们的合作托管服务之一的托管服务器,其IP地址将显示在对应服务商的服务器管理界面中。您还可以在[Keymaster](https://keymaster.beammp.com/login)网站上找到服务器的IP。 + +## 对于家庭托管服务器 + +对于家庭自托管服务器,请在浏览器中访问[whatsmyip.org](https://whatsmyip.org),该网站将显示互联网访问您时所使用的公网IPv4地址。 + +注意,127.0.0.1是本地主机地址,如果服务器托管在同一台计算机上,则只能由您自己使用。如果您与家庭托管服务器的连接仍然有问题,请检查[端口转发](https://docs.beammp.com/server/port-forwarding/)以及使用CheckBeamMP + +
+ +
+ +
+ +
+ +## 怎么检查CGNAT? + +请查看[这个页面](https://docs.beammp.com/FAQ/How-to-check-for-CGNAT/),以确定您是否可以在家中托管服务器。 + +标签: IP, Server, Connection Failed, 10060/10061 diff --git a/docs/zh/beamng/cef-snippets.md b/docs/zh/beamng/cef-snippets.md new file mode 100644 index 00000000..8dc6d15f --- /dev/null +++ b/docs/zh/beamng/cef-snippets.md @@ -0,0 +1,12 @@ +!!! warning "本页面正在建设中!" + +``` +本站点目前正处于积极开发与维护阶段。 + +觉得您可以提供帮助?请点击页面右侧的铅笔图标参与编辑! +此操作适用于站内的任何页面。 +``` + +# BeamNG.drive CEF Code 的片段 + +to-do diff --git a/docs/zh/beamng/css-snippets.md b/docs/zh/beamng/css-snippets.md new file mode 100644 index 00000000..d9d0ac06 --- /dev/null +++ b/docs/zh/beamng/css-snippets.md @@ -0,0 +1,189 @@ +!!! warning "本页面正在建设中!" + +``` +本站点目前正处于积极开发与维护阶段。 + +觉得您可以提供帮助?请点击页面右侧的铅笔图标参与编辑! +此操作适用于站内的任何页面。 +``` + +# BeamNG.drive CSS Code 的片段 + +## 常见变量 + +=== BeamNG Orange + +``` +```css +var(--bng-orange) /*Common orange*/ +var(--bng-orange-shade1) /*70% opacity*/ +var(--bng-orange-shade2) /*40% opacity*/ +var(--bng-orange-shade1opaque) +var(--bng-orange-shade2opaque) +``` +``` + +=== Monochrome + +``` +```css +--- Monochrome +var(--bng-black-8) /*80% opacity (duplicate --bng-black-o8)*/ +var(--bng-black-6) /*60% opacity (duplicate --bng-black-o6)*/ +var(--bng-black-4) /*40% opacity (duplicate --bng-black-o4)*/ +var(--bng-black-2) /*20% opacity (duplicate --bng-black-o2)*/ + +var(--dark-neutral-grey) +var(--neutral-grey) +var(--light-neutral-grey) +var(--dark-grey) +var(--dark-grey-alpha) /*80% opacity*/ + +var(--black-1) /*70% opacity*/ +var(--black-2) /*40% opacity (duplicate --bng-black-o4)*/ + +var(--white-1) /*80% opacity*/ +var(--white-2) /*40% opacity*/ +var(--white-3) /*20% opacity*/ +``` +``` + +=== BeamNG 界面颜色调色板 + +``` +=== Orange + + ```css + var(--bng-orange-50) + var(--bng-orange-100) + var(--bng-orange-200) + var(--bng-orange-300) + var(--bng-orange-b400) + var(--bng-orange-500) + var(--bng-orange-600) + var(--bng-orange-700) + var(--bng-orange-800) + var(--bng-orange-900) + ``` + +=== Cool Gray + + ```css + var(--bng-cool-gray-50) + var(--bng-cool-gray-100) + var(--bng-cool-gray-200) + var(--bng-cool-gray-300) + var(--bng-cool-gray-400) + var(--bng-cool-gray-500) + var(--bng-cool-gray-600) + var(--bng-cool-gray-700) + var(--bng-cool-gray-800) + var(--bng-cool-gray-900) + ``` + +=== Ter Blue + ```css + var(--bng-ter-blue-50) + var(--bng-ter-blue-100) + var(--bng-ter-blue-200) + var(--bng-ter-blue-300) + var(--bng-ter-blue-400) + var(--bng-ter-blue-500) + var(--bng-ter-blue-600) + var(--bng-ter-blue-700) + var(--bng-ter-blue-800) + var(--bng-ter-blue-900) + ``` + +=== Add Blue + ```css + var(--bng-add-blue-50) + var(--bng-add-blue-100) + var(--bng-add-blue-200) + var(--bng-add-blue-300) + var(--bng-add-blue-400) + var(--bng-add-blue-500) + var(--bng-add-blue-600) + var(--bng-add-blue-700) + var(--bng-add-blue-800) + var(--bng-add-blue-900) + ``` + +=== Add Green + ```css + var(--bng-add-green-50) + var(--bng-add-green-100) + var(--bng-add-green-200) + var(--bng-add-green-300) + var(--bng-add-green-400) + var(--bng-add-green-500) + var(--bng-add-green-600) + var(--bng-add-green-700) + var(--bng-add-green-800) + var(--bng-add-green-900) + ``` + +=== Add Yellow + ```css + var(--bng-add-yellow-50) + var(--bng-add-yellow-100) + var(--bng-add-yellow-200) + var(--bng-add-yellow-300) + var(--bng-add-yellow-400) + var(--bng-add-yellow-500) + var(--bng-add-yellow-600) + var(--bng-add-yellow-700) + var(--bng-add-yellow-800) + var(--bng-add-yellow-900) + ``` + +=== Add Peach + ```css + var(--bng-add-peach-50) + var(--bng-add-peach-100) + var(--bng-add-peach-200) + var(--bng-add-peach-300) + var(--bng-add-peach-400) + var(--bng-add-peach-500) + var(--bng-add-peach-600) + var(--bng-add-peach-700) + var(--bng-add-peach-800) + var(--bng-add-peach-900) + ``` + +=== Add Red + ```css + var(--bng-add-red-50) + var(--bng-add-red-100) + var(--bng-add-red-200) + var(--bng-add-red-300) + var(--bng-add-red-400) + var(--bng-add-red-500) + var(--bng-add-red-600) + var(--bng-add-red-700) + var(--bng-add-red-800) + var(--bng-add-red-900) + ``` +``` + +=== 额外颜色预设 + +``` +```css +var(--bng-filter-orange) /*Filter preset to force SVGs to use bng-orange*/ +var(--bng-black-o8) /*80% opacity*/ +var(--bng-black-o6) /*60% opacity*/ +var(--bng-black-o4) /*40% opacity*/ +var(--bng-black-o2) /*20% opacity*/ +``` +``` + +=== 圆角预设 + +``` +```css +var(--bng-corners-1) /*0.25rem*/ +var(--bng-corners-2) /*0.50rem*/ +var(--bng-corners-3) /*1.00rem*/ +``` +``` diff --git a/docs/zh/beamng/dev/content/vehicles.md b/docs/zh/beamng/dev/content/vehicles.md new file mode 100644 index 00000000..b86ed313 --- /dev/null +++ b/docs/zh/beamng/dev/content/vehicles.md @@ -0,0 +1,21 @@ +!!!警告“此网站正在建设中!” + +``` +这个网站正在积极建设中。 + +觉得你能帮忙吗?请用右边的铅笔点击页面! + +这也可以在任何页面上完成。 +``` + +# 车祸模拟器 创建车辆 + +。。。 + +## 介绍 + +。。。 + +## 开始进行 + +。。。 diff --git a/docs/zh/beamng/dev/modding/imgui-window-tutorial.md b/docs/zh/beamng/dev/modding/imgui-window-tutorial.md new file mode 100644 index 00000000..3c247fd1 --- /dev/null +++ b/docs/zh/beamng/dev/modding/imgui-window-tutorial.md @@ -0,0 +1,88 @@ +!!! warning "本页面正在建设中!" + +``` +本站点目前正处于积极开发与维护阶段。 + +觉得您可以提供帮助?请点击页面右侧的铅笔图标参与编辑! +此操作适用于站内的任何页面。 + +#创建 ImGui 窗口 +``` + +本页将介绍如何创建一个基础的 ImGui 窗口。 + +## 设置 + +在开始使用 ImGui 之前,需要进行一些基础设置: + +```lua +local im = ui_imgui -- 缩写以避免频繁查找。这有助于性能优化 +local imguiExampleWindowOpen = im.BoolPtr(true) +``` + +`imguiExampleWindowOpen` 将用于决定该示例窗口是否应当进行渲染。 + +## 窗口渲染 + +ImGui 窗口及其内容必须在每一个需要显示的帧中重新创建。这意味着,若要使用 ImGui,必须通过某种形式的onUpdate函数来实现。 + +```lua +local function onUpdate() + if worldReadyState == 2 then + if imguiExampleWindowOpen[0] == true then + imguiExample() + end + end +end +M.onUpdate = onUpdate +``` + +只要关卡已完全加载,且示例窗口处于应当显示的状态,这段代码就会运行一个函数来创建该示例窗口。 + +## 窗口内容 + +如果您是初次编写 ImGui,可以把ImGui看作 HTML 的亲戚: + +- `im.SetNextWindowSize(im.ImVec2(x, y), im.Cond_FirstUseEver)` 用于在尚未定义视口尺寸的情况下,对其进行初始化定义。 +- `im.Begin()` 和 `im.End()`相当于你的 `` 和 `` +- `im.Text()` 相当于你的 `

` + +```lua +local buttonPresses = 0 + +local function imguiExample() + im.SetNextWindowSize(im.ImVec2(366, 100), im.Cond_FirstUseEver) -- 准备窗口尺寸 + im.Begin("Hello World, I am a window") -- 创建一个标题为“Hello World, I am a window”的窗口 + im.Indent() -- 缩进元素(类似于内边距) + im.Text("Hello World, I am text.") -- 添加一行文本,类似于

标签 + im.SameLine() -- 与 HTML 不同,这会将下一个元素附加到上一个元素的同一行 + if im.Button("The Hello World Button") then -- 类似于