Login notices show after the loading screen, as in 1.12.1 (#1809)
The red "GM mode is ON" line did not show on a session's first login, only on a relog. In 1.12.1 the interface and the world load before `CMSG_PLAYER_LOGIN` goes out (`0x46c272`), and the loading screen drops when the player is created (`0x5debbd`). vmangos sends that create ahead of the login notices (`Handlers/CharacterHandler.cpp:675`), so the line is added after the reveal and gets its full time on screen. benilla's cover stays up through its own warm-up, about 6 s on a cold login, and the line faded underneath it. A relog is warm, so the line survived. Server notice lines (`SMSG_NOTIFICATION`, area-trigger messages) now wait while the cover is up and fire on the frame it drops. Test: a notice under the cover fires once, at the reveal. A live cold login shows the line the frame after the cover clears. Fixes #1252 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 86c71919284d51473602a5ee668b5f818d71d980)
This commit is contained in:
@@ -71,8 +71,13 @@ pub(super) fn feed_actions(
|
||||
mut cast_errors: ResMut<CastErrors>,
|
||||
mut mount_errors: ResMut<MountErrors>,
|
||||
mut pet_tame_failures: ResMut<PetTameFailures>,
|
||||
// The by-key and resolved-text queues as one parameter, at Bevy's 16-parameter limit.
|
||||
ui_errors: (ResMut<UiErrorKeys>, ResMut<UiErrorTexts>),
|
||||
// The by-key and resolved-text queues and the cover that holds the latter, as one parameter,
|
||||
// at Bevy's 16-parameter limit.
|
||||
ui_errors: (
|
||||
ResMut<UiErrorKeys>,
|
||||
ResMut<UiErrorTexts>,
|
||||
Option<Res<crate::loading_screen::LoadingScreen>>,
|
||||
),
|
||||
spells: Option<Res<Spells>>,
|
||||
self_q: Query<&ObjectStore, With<SelfPlayer>>,
|
||||
objects: crate::net::Objects,
|
||||
@@ -87,7 +92,7 @@ pub(super) fn feed_actions(
|
||||
let Some(mut script) = script else {
|
||||
return;
|
||||
};
|
||||
let (mut ui_error_keys, mut ui_error_texts) = ui_errors;
|
||||
let (mut ui_error_keys, mut ui_error_texts, cover) = ui_errors;
|
||||
let memory = memory.get(&script);
|
||||
|
||||
// Cast failures become error lines through [`cast_fail`], all resolved before any event
|
||||
@@ -296,13 +301,19 @@ pub(super) fn feed_actions(
|
||||
.collect();
|
||||
show_messages(&mut script, &mut sink, "ui_action", key_lines);
|
||||
|
||||
// Already-resolved lines ([`UiErrorTexts`]); the queued kind is the `0x4945b0` flag.
|
||||
let resolved: Vec<Shown> = ui_error_texts
|
||||
.0
|
||||
.drain(..)
|
||||
.map(|(text, kind)| Shown::unkeyed(kind, text))
|
||||
.collect();
|
||||
show_messages(&mut script, &mut sink, "ui_action", resolved);
|
||||
// Already-resolved lines ([`UiErrorTexts`]); the queued kind is the `0x4945b0` flag. Held
|
||||
// while the loading cover is up, so a login notice's hold starts at the reveal: the reference
|
||||
// sends `CMSG_PLAYER_LOGIN` only after its world load (`0x46c272`), and its cover drops at the
|
||||
// player's create (`0x5debbd`), which vmangos sends ahead of the notices (`Maps/Map.cpp:432`,
|
||||
// `Handlers/CharacterHandler.cpp:675`).
|
||||
if !cover.is_some_and(|c| c.covering()) {
|
||||
let resolved: Vec<Shown> = ui_error_texts
|
||||
.0
|
||||
.drain(..)
|
||||
.map(|(text, kind)| Shown::unkeyed(kind, text))
|
||||
.collect();
|
||||
show_messages(&mut script, &mut sink, "ui_action", resolved);
|
||||
}
|
||||
|
||||
// The engine's own by-key refusals (`ERR_PASSIVE_ABILITY`): `benilla_ui` cannot reach
|
||||
// [`UiErrorKeys`], so it queues keys that show here, one frame late.
|
||||
|
||||
@@ -434,6 +434,53 @@ fn pre_resolved_lines_land_on_the_errors_frame_in_the_arms_colour() {
|
||||
);
|
||||
}
|
||||
|
||||
/// A login notice queued while the loading cover is up waits for the reveal: the reference's
|
||||
/// cover drops at the player's create (`0x5debbd`), ahead of the notice, so its `UIErrorsFrame`
|
||||
/// hold is never spent under the cover.
|
||||
#[test]
|
||||
fn a_notice_under_the_loading_cover_fires_at_the_reveal() {
|
||||
let (mut app, _rx) = app_with_food_on_the_bar();
|
||||
app.insert_resource(crate::loading_screen::LoadingScreen::test_covering());
|
||||
app.world_mut()
|
||||
.non_send_resource_mut::<UiScript>()
|
||||
.run(
|
||||
r#"
|
||||
BENILLA_TEST_ERRORS = {}
|
||||
local f = CreateFrame("Frame")
|
||||
f:RegisterEvent("UI_ERROR_MESSAGE")
|
||||
f:SetScript("OnEvent", function() table.insert(BENILLA_TEST_ERRORS, arg1) end)
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let fired = |app: &mut App| {
|
||||
app.world_mut()
|
||||
.non_send_resource::<UiScript>()
|
||||
.eval::<Vec<String>>("return BENILLA_TEST_ERRORS")
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
app.world_mut()
|
||||
.resource_mut::<UiErrorTexts>()
|
||||
.error("GM mode is ON".to_string());
|
||||
app.update();
|
||||
app.update();
|
||||
assert!(fired(&mut app).is_empty(), "nothing fires under the cover");
|
||||
assert_eq!(
|
||||
app.world().resource::<UiErrorTexts>().0.len(),
|
||||
1,
|
||||
"the notice stays queued"
|
||||
);
|
||||
|
||||
app.insert_resource(crate::loading_screen::LoadingScreen::default());
|
||||
app.update();
|
||||
assert_eq!(
|
||||
fired(&mut app),
|
||||
["GM mode is ON"],
|
||||
"the reveal fires it once"
|
||||
);
|
||||
assert!(app.world().resource::<UiErrorTexts>().0.is_empty());
|
||||
}
|
||||
|
||||
/// `HandleCastFailed 0x6e1a00` logs through `0x62c360`; `HandlePetCastFailed 0x6e8eb0` calls only
|
||||
/// the packet readers and `0x496720`, with no log line and no error sound. Both entries name the
|
||||
/// same spell, so only the caster differs.
|
||||
|
||||
@@ -250,10 +250,20 @@ impl Plugin for UiActionPlugin {
|
||||
.in_set(UnitFeed)
|
||||
.after(feed::feed_actions),
|
||||
),
|
||||
)
|
||||
.add_systems(
|
||||
OnExit(crate::client_state::ClientState::InWorld),
|
||||
drop_held_lines,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// `OnExit(InWorld)`: a line still held under the cover dies with the world's interface, as the
|
||||
/// reference's, drawn at once by `0x4945b0`, dies in the UI teardown (`0x490bd0`).
|
||||
fn drop_held_lines(mut texts: ResMut<UiErrorTexts>) {
|
||||
texts.0.clear();
|
||||
}
|
||||
|
||||
fn load_spells(mut commands: Commands, assets: Option<Res<WorldAssets>>) {
|
||||
let Some(assets) = assets else { return };
|
||||
let loaded = {
|
||||
|
||||
Reference in New Issue
Block a user