Charakterauswahl: Vorschau-Crash, Blackscreen und Debug beheben
- Vorschau (3D-Ped/Kamera) in einen eigenen, verzoegerten Thread mit Laufnummer-Schutz ausgelagert. Damit laeuft die vernetzte Ped-Erzeugung nicht mehr zeitgleich mit dem Spieler-Teleport der Auswahl -> behebt "SendNetPacket called from multiple threads" (Crash beim Oeffnen). - Blackscreen nach dem Spawn behoben: der Fade wird nun allein vom Core gesteuert (Fade-out zu Beginn von spielerGeladen, Fade-in am Ende); SchliesseAuswahl blendet nicht mehr selbst -> kein Fade-Konflikt mehr. - Temporaere [AETHER-DEBUG]-Ausgaben wieder entfernt. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
cad096e7a6
commit
11cc043b6c
@ -18,8 +18,6 @@ local PARKPLATZ = { x = -1037.7, y = -2737.6, z = 20.2 }
|
|||||||
--- @param charaktere table Liste der Charaktere
|
--- @param charaktere table Liste der Charaktere
|
||||||
--- @param maxSlots number Maximale Anzahl Charaktere
|
--- @param maxSlots number Maximale Anzahl Charaktere
|
||||||
local function OeffneAuswahl(charaktere, maxSlots)
|
local function OeffneAuswahl(charaktere, maxSlots)
|
||||||
print(('[AETHER-DEBUG] client OeffneAuswahl: %d Charaktere')
|
|
||||||
:format(charaktere and #charaktere or -1))
|
|
||||||
auswahlOffen = true
|
auswahlOffen = true
|
||||||
|
|
||||||
-- Eigenen Spieler unsichtbar parken — sichtbar ist nur der Vorschau-Ped
|
-- Eigenen Spieler unsichtbar parken — sichtbar ist nur der Vorschau-Ped
|
||||||
@ -31,7 +29,6 @@ local function OeffneAuswahl(charaktere, maxSlots)
|
|||||||
-- 3D-Vorschau mit dem Aussehen des ersten Charakters starten
|
-- 3D-Vorschau mit dem Aussehen des ersten Charakters starten
|
||||||
local erstes = charaktere and charaktere[1] or nil
|
local erstes = charaktere and charaktere[1] or nil
|
||||||
Vorschau.Starten(erstes and erstes.aussehen or nil, 'koerper')
|
Vorschau.Starten(erstes and erstes.aussehen or nil, 'koerper')
|
||||||
print('[AETHER-DEBUG] client OeffneAuswahl: Vorschau gestartet, beende Ladebildschirm')
|
|
||||||
|
|
||||||
-- Ladebildschirm beenden, damit die Auswahl sichtbar wird
|
-- Ladebildschirm beenden, damit die Auswahl sichtbar wird
|
||||||
ShutdownLoadingScreen()
|
ShutdownLoadingScreen()
|
||||||
@ -53,10 +50,9 @@ local function SchliesseAuswahl()
|
|||||||
|
|
||||||
SendNuiMessage(json.encode({ aktion = 'schliessen' }))
|
SendNuiMessage(json.encode({ aktion = 'schliessen' }))
|
||||||
SetNuiFocus(false, false)
|
SetNuiFocus(false, false)
|
||||||
|
|
||||||
DoScreenFadeOut(400)
|
|
||||||
Wait(400)
|
|
||||||
Vorschau.Beenden()
|
Vorschau.Beenden()
|
||||||
|
-- Bewusst KEIN Fade hier: der Core blendet nach dem Spawn ein. Ein Fade
|
||||||
|
-- an dieser Stelle kollidiert mit dem Core-Fade -> Blackscreen.
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
|
|||||||
@ -22,6 +22,9 @@ local vorschauKamera= nil -- Handle der Kamera
|
|||||||
local aktuellesModell = nil -- Aktuell geladenes Ped-Modell (fuer Geschlechtswechsel)
|
local aktuellesModell = nil -- Aktuell geladenes Ped-Modell (fuer Geschlechtswechsel)
|
||||||
local aktuelleDrehung = 0.0 -- Zusaetzliche Drehung durch den Spieler
|
local aktuelleDrehung = 0.0 -- Zusaetzliche Drehung durch den Spieler
|
||||||
local aktuellerModus = 'koerper'
|
local aktuellerModus = 'koerper'
|
||||||
|
-- Laufnummer: entwertet noch laufende, verzoegerte Starts, wenn zwischendurch
|
||||||
|
-- beendet oder neu gestartet wird.
|
||||||
|
local aktuellerLauf = 0
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
-- Interne Helfer
|
-- Interne Helfer
|
||||||
@ -79,37 +82,53 @@ end
|
|||||||
--- @param aussehen table|nil Startaussehen (bestimmt auch das Geschlecht)
|
--- @param aussehen table|nil Startaussehen (bestimmt auch das Geschlecht)
|
||||||
--- @param modus string|nil Startansicht ('gesicht' | 'koerper')
|
--- @param modus string|nil Startansicht ('gesicht' | 'koerper')
|
||||||
function Vorschau.Starten(aussehen, modus)
|
function Vorschau.Starten(aussehen, modus)
|
||||||
-- Umgebung laden, damit der Ped nicht durch den Boden faellt
|
aktuellerLauf = aktuellerLauf + 1
|
||||||
RequestCollisionAtCoord(VORSCHAU_POS.x, VORSCHAU_POS.y, VORSCHAU_POS.z)
|
local lauf = aktuellerLauf
|
||||||
|
|
||||||
local modellName = AetherAussehen.HoleModell(aussehen and aussehen.geschlecht)
|
-- WICHTIG: Ped und Kamera in einem EIGENEN Thread mit kurzer Verzoegerung
|
||||||
local modell = LadeModell(modellName)
|
-- erzeugen. Sonst laeuft die (vernetzte) Ped-Erzeugung im selben Moment wie
|
||||||
if not modell then
|
-- der Spieler-Ped-Teleport der Auswahl -> zwei Threads schreiben Netz-State
|
||||||
print('^1[Aether]^7 Vorschau-Modell konnte nicht geladen werden: ' .. modellName)
|
-- ("SendNetPacket ... called from multiple threads") -> Client-Crash.
|
||||||
return
|
CreateThread(function()
|
||||||
end
|
Wait(250)
|
||||||
|
if lauf ~= aktuellerLauf then return end -- inzwischen beendet/neu gestartet
|
||||||
|
|
||||||
-- Ped nur lokal erzeugen (nicht im Netzwerk sichtbar)
|
-- Umgebung laden, damit der Ped nicht durch den Boden faellt
|
||||||
vorschauPed = CreatePed(4, modell, VORSCHAU_POS.x, VORSCHAU_POS.y, VORSCHAU_POS.z,
|
RequestCollisionAtCoord(VORSCHAU_POS.x, VORSCHAU_POS.y, VORSCHAU_POS.z)
|
||||||
0.0, false, false)
|
|
||||||
aktuellesModell = modellName
|
|
||||||
aktuelleDrehung = 0.0
|
|
||||||
|
|
||||||
SetEntityHeading(vorschauPed, 0.0) -- schaut Richtung Kamera
|
local modellName = AetherAussehen.HoleModell(aussehen and aussehen.geschlecht)
|
||||||
FreezeEntityPosition(vorschauPed, true)
|
local modell = LadeModell(modellName)
|
||||||
SetEntityInvincible(vorschauPed, true)
|
if not modell then
|
||||||
SetBlockingOfNonTemporaryEvents(vorschauPed, true)
|
print('^1[Aether]^7 Vorschau-Modell konnte nicht geladen werden: ' .. modellName)
|
||||||
SetModelAsNoLongerNeeded(modell)
|
return
|
||||||
|
end
|
||||||
|
if lauf ~= aktuellerLauf then SetModelAsNoLongerNeeded(modell); return end
|
||||||
|
|
||||||
if aussehen then
|
-- Ped nur lokal erzeugen (nicht im Netzwerk sichtbar)
|
||||||
AetherAussehen.Anwenden(vorschauPed, aussehen)
|
vorschauPed = CreatePed(4, modell, VORSCHAU_POS.x, VORSCHAU_POS.y, VORSCHAU_POS.z,
|
||||||
end
|
0.0, false, false)
|
||||||
|
aktuellesModell = modellName
|
||||||
|
aktuelleDrehung = 0.0
|
||||||
|
|
||||||
-- Kamera erzeugen und aktivieren
|
SetEntityHeading(vorschauPed, 0.0) -- schaut Richtung Kamera
|
||||||
vorschauKamera = CreateCam('DEFAULT_SCRIPTED_CAMERA', true)
|
FreezeEntityPosition(vorschauPed, true)
|
||||||
SetzeKameraAuf(modus or 'koerper', false)
|
SetEntityInvincible(vorschauPed, true)
|
||||||
SetCamActive(vorschauKamera, true)
|
SetBlockingOfNonTemporaryEvents(vorschauPed, true)
|
||||||
RenderScriptCams(true, false, 0, true, true)
|
SetModelAsNoLongerNeeded(modell)
|
||||||
|
|
||||||
|
if aussehen then
|
||||||
|
AetherAussehen.Anwenden(vorschauPed, aussehen)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Kamera erzeugen und aktivieren
|
||||||
|
vorschauKamera = CreateCam('DEFAULT_SCRIPTED_CAMERA', true)
|
||||||
|
SetzeKameraAuf(modus or 'koerper', false)
|
||||||
|
SetCamActive(vorschauKamera, true)
|
||||||
|
RenderScriptCams(true, false, 0, true, true)
|
||||||
|
|
||||||
|
-- Falls waehrenddessen beendet wurde: sofort wieder aufraeumen
|
||||||
|
if lauf ~= aktuellerLauf then Vorschau.Beenden() end
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Uebertraegt ein Aussehen auf den Vorschau-Ped.
|
--- Uebertraegt ein Aussehen auf den Vorschau-Ped.
|
||||||
@ -167,6 +186,9 @@ end
|
|||||||
|
|
||||||
--- Beendet die Vorschau und raeumt Ped und Kamera auf
|
--- Beendet die Vorschau und raeumt Ped und Kamera auf
|
||||||
function Vorschau.Beenden()
|
function Vorschau.Beenden()
|
||||||
|
-- Noch laufende, verzoegerte Starts entwerten
|
||||||
|
aktuellerLauf = aktuellerLauf + 1
|
||||||
|
|
||||||
if vorschauKamera then
|
if vorschauKamera then
|
||||||
RenderScriptCams(false, false, 0, true, true)
|
RenderScriptCams(false, false, 0, true, true)
|
||||||
DestroyCam(vorschauKamera, false)
|
DestroyCam(vorschauKamera, false)
|
||||||
|
|||||||
@ -111,13 +111,10 @@ end
|
|||||||
-- Auswahl anzeigen (wird vom Core ausgeloest)
|
-- Auswahl anzeigen (wird vom Core ausgeloest)
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
AddEventHandler('aether:server:charakterAuswahlNoetig', function(quelle)
|
AddEventHandler('aether:server:charakterAuswahlNoetig', function(quelle)
|
||||||
print(('[AETHER-DEBUG] character-server: charakterAuswahlNoetig empfangen fuer %s')
|
|
||||||
:format(tostring(quelle)))
|
|
||||||
local lizenz = HoleLizenz(quelle)
|
local lizenz = HoleLizenz(quelle)
|
||||||
if not lizenz then print('[AETHER-DEBUG] character-server: keine Lizenz!'); return end
|
if not lizenz then return end
|
||||||
TriggerClientEvent('aether:character:zeigeAuswahl', quelle,
|
TriggerClientEvent('aether:character:zeigeAuswahl', quelle,
|
||||||
BaueListe(lizenz), Config.MaxCharaktere)
|
BaueListe(lizenz), Config.MaxCharaktere)
|
||||||
print('[AETHER-DEBUG] character-server: zeigeAuswahl an Client gesendet')
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
|
|||||||
@ -52,6 +52,11 @@ end)
|
|||||||
RegisterNetEvent('aether:client:spielerGeladen', function(daten)
|
RegisterNetEvent('aether:client:spielerGeladen', function(daten)
|
||||||
Aether.SpielerDaten = daten
|
Aether.SpielerDaten = daten
|
||||||
|
|
||||||
|
-- Waehrend des Spawns schwarz halten (versteckt Modellwechsel und den
|
||||||
|
-- Schnitt von der Auswahl-Kamera). Der Fade wird am Ende wieder geoeffnet
|
||||||
|
-- — der Core steuert den Fade allein, damit es keinen Konflikt gibt.
|
||||||
|
DoScreenFadeOut(0)
|
||||||
|
|
||||||
-- Passendes Standardmodell laden (bis der Charakter-Ersteller migriert ist)
|
-- Passendes Standardmodell laden (bis der Charakter-Ersteller migriert ist)
|
||||||
local modellName = Config.StandardPed[daten.geschlecht] or Config.StandardPed.m
|
local modellName = Config.StandardPed[daten.geschlecht] or Config.StandardPed.m
|
||||||
local modell = GetHashKey(modellName)
|
local modell = GetHashKey(modellName)
|
||||||
|
|||||||
@ -87,8 +87,6 @@ end
|
|||||||
RegisterNetEvent('aether:server:spielerBereit', function()
|
RegisterNetEvent('aether:server:spielerBereit', function()
|
||||||
local quelle = source
|
local quelle = source
|
||||||
local lizenz = HoleLizenz(quelle)
|
local lizenz = HoleLizenz(quelle)
|
||||||
print(('[AETHER-DEBUG] spielerBereit: quelle=%s lizenz=%s AutoCharakter=%s')
|
|
||||||
:format(tostring(quelle), lizenz and 'ok' or 'nil', tostring(Config.AutoCharakter)))
|
|
||||||
if not lizenz then return end
|
if not lizenz then return end
|
||||||
|
|
||||||
-- Bereits geladen? Dann nichts tun (schuetzt vor doppeltem Laden)
|
-- Bereits geladen? Dann nichts tun (schuetzt vor doppeltem Laden)
|
||||||
@ -96,7 +94,6 @@ RegisterNetEvent('aether:server:spielerBereit', function()
|
|||||||
|
|
||||||
-- Standardweg: `aether-character` zeigt die Charakterauswahl
|
-- Standardweg: `aether-character` zeigt die Charakterauswahl
|
||||||
if not Config.AutoCharakter then
|
if not Config.AutoCharakter then
|
||||||
print('[AETHER-DEBUG] spielerBereit: triggere charakterAuswahlNoetig')
|
|
||||||
TriggerEvent('aether:server:charakterAuswahlNoetig', quelle)
|
TriggerEvent('aether:server:charakterAuswahlNoetig', quelle)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user