|
1 | 1 | //! Add an [`InputMarker<C>`] component automatically to [`Action`] entities that need it |
2 | 2 |
|
3 | | -use crate::setup::NetworkActionOf; |
4 | 3 | use bevy_ecs::prelude::*; |
5 | 4 | use bevy_ecs::relationship::Relationship; |
6 | 5 | use bevy_enhanced_input::prelude::*; |
7 | 6 | use bevy_replicon::client::confirm_history::ConfirmHistory; |
8 | 7 | use lightyear_connection::client::Client; |
9 | | -use lightyear_replication::prelude::{Controlled, ControlledBy, HasAuthority}; |
| 8 | +use lightyear_replication::prelude::{Controlled, ControlledBy}; |
10 | 9 |
|
11 | 10 | /// Marker component that indicates that the entity is actively listening for physical user inputs. |
12 | 11 | /// |
@@ -40,132 +39,83 @@ fn action_targets_local_client<C: Component>( |
40 | 39 |
|
41 | 40 | /// Propagate the InputMarker component from the Context entity to the Action entities |
42 | 41 | /// whenever an InputMarker is added to a Context entity. |
43 | | -/// Skip replicated action entities (those received from remote clients). |
| 42 | +/// |
| 43 | +/// `InputMarker<C>` on the context is the explicit local-input signal, so |
| 44 | +/// confirmed prespawned owner actions should still receive it. Remote |
| 45 | +/// rebroadcasted actions should be attached to contexts without this marker. |
44 | 46 | pub(crate) fn propagate_input_marker<C: Component>( |
45 | 47 | trigger: On<Add, InputMarker<C>>, |
46 | 48 | actions: Query<&Actions<C>>, |
47 | | - confirm: Query<(), With<ConfirmHistory>>, |
48 | 49 | mut commands: Commands, |
49 | 50 | ) { |
50 | 51 | if let Ok(actions) = actions.get(trigger.entity) { |
51 | 52 | actions.iter().for_each(|action| { |
52 | | - if confirm.get(action).is_ok() { |
53 | | - return; |
54 | | - } |
55 | 53 | commands.entity(action).insert(InputMarker::<C>::default()); |
56 | 54 | }); |
57 | 55 | } |
58 | 56 | } |
59 | 57 |
|
60 | 58 | /// When an Action entity is added to a Context entity that has an InputMarker, |
61 | 59 | /// add the InputMarker to the Action entity as well. |
62 | | -/// Skip replicated entities — those are received from remote clients and should not |
63 | | -/// be marked as local input sources. |
64 | 60 | pub(crate) fn add_input_marker_from_parent<C: Component>( |
65 | 61 | trigger: On<Add, ActionOf<C>>, |
66 | | - action_of: Query<&ActionOf<C>, Without<ConfirmHistory>>, |
| 62 | + action_of: Query<&ActionOf<C>>, |
67 | 63 | context: Query<(), With<InputMarker<C>>>, |
68 | 64 | mut commands: Commands, |
69 | 65 | ) { |
70 | | - if let Ok(action_of) = action_of.get(trigger.entity) |
71 | | - && context.get(action_of.get()).is_ok() |
72 | | - { |
| 66 | + let Ok(action_of) = action_of.get(trigger.entity) else { |
| 67 | + return; |
| 68 | + }; |
| 69 | + if context.get(action_of.get()).is_ok() { |
73 | 70 | commands |
74 | 71 | .entity(trigger.entity) |
75 | 72 | .insert(InputMarker::<C>::default()); |
76 | 73 | } |
77 | 74 | } |
78 | 75 |
|
79 | | -/// If Bindings or ActionMock is added to an Action entity, add the InputMarker to that Action entity. |
80 | | -/// Only add the marker on locally controlled action entities that already have a network-facing |
81 | | -/// action mapping. This avoids emitting inputs for entities that the server cannot resolve yet. |
| 76 | +/// If Bindings or ActionMock is added to an Action entity, add the InputMarker |
| 77 | +/// to that Action entity once the action has a network-resolvable identity. |
| 78 | +/// |
| 79 | +/// Replicated/prespawned actions become resolvable when [`ConfirmHistory`] is |
| 80 | +/// present, because the client's action entity can then be mapped back to the |
| 81 | +/// server action entity when an input message is sent. |
82 | 82 | pub(crate) fn add_input_marker_from_binding<C: Component>( |
83 | 83 | trigger: On<Add, (Bindings, ActionMock)>, |
84 | | - action: Query< |
85 | | - &ActionOf<C>, |
86 | | - ( |
87 | | - With<ActionOf<C>>, |
88 | | - With<NetworkActionOf<C>>, |
89 | | - Without<ConfirmHistory>, |
90 | | - ), |
91 | | - >, |
| 84 | + action: Query<&ActionOf<C>, (With<ConfirmHistory>, Without<InputMarker<C>>)>, |
92 | 85 | contexts: Query<Option<&ControlledBy>, With<Controlled>>, |
93 | 86 | clients: Query<(), With<Client>>, |
94 | 87 | mut commands: Commands, |
95 | 88 | ) { |
96 | | - if let Ok(action_of) = action.get(trigger.entity) |
97 | | - && action_targets_local_client(action_of, &contexts, &clients) |
98 | | - { |
| 89 | + let Ok(action_of) = action.get(trigger.entity) else { |
| 90 | + return; |
| 91 | + }; |
| 92 | + if action_targets_local_client(action_of, &contexts, &clients) { |
99 | 93 | commands |
100 | 94 | .entity(trigger.entity) |
101 | 95 | .insert(InputMarker::<C>::default()); |
102 | 96 | } |
103 | 97 | } |
104 | 98 |
|
105 | | -/// If authority is granted after the action entity already exists, add the InputMarker |
106 | | -/// once the entity becomes locally controlled. |
107 | | -pub(crate) fn add_input_marker_from_authority<C: Component>( |
108 | | - trigger: On<Add, HasAuthority>, |
109 | | - action: Query< |
110 | | - &ActionOf<C>, |
111 | | - ( |
112 | | - With<ActionOf<C>>, |
113 | | - With<NetworkActionOf<C>>, |
114 | | - Or<(With<Bindings>, With<ActionMock>)>, |
115 | | - Without<ConfirmHistory>, |
116 | | - ), |
117 | | - >, |
118 | | - contexts: Query<Option<&ControlledBy>, With<Controlled>>, |
119 | | - clients: Query<(), With<Client>>, |
120 | | - mut commands: Commands, |
121 | | -) { |
122 | | - if let Ok(action_of) = action.get(trigger.entity) |
123 | | - && action_targets_local_client(action_of, &contexts, &clients) |
124 | | - { |
125 | | - commands |
126 | | - .entity(trigger.entity) |
127 | | - .insert(InputMarker::<C>::default()); |
128 | | - } |
129 | | -} |
130 | | - |
131 | | -/// If the network-facing action mapping becomes available after the entity already has bindings, |
132 | | -/// start treating it as a local input source at that point. |
133 | | -pub(crate) fn add_input_marker_from_network_action<C: Component>( |
134 | | - trigger: On<Add, NetworkActionOf<C>>, |
| 99 | +/// If an existing bound action becomes network-resolvable, add the InputMarker |
| 100 | +/// once it targets the local client. |
| 101 | +pub(crate) fn add_input_marker_when_action_becomes_ready<C: Component>( |
| 102 | + trigger: On<Add, ConfirmHistory>, |
135 | 103 | action: Query< |
136 | 104 | &ActionOf<C>, |
137 | 105 | ( |
138 | | - With<ActionOf<C>>, |
139 | | - With<NetworkActionOf<C>>, |
| 106 | + With<ConfirmHistory>, |
140 | 107 | Or<(With<Bindings>, With<ActionMock>)>, |
141 | | - Without<ConfirmHistory>, |
| 108 | + Without<InputMarker<C>>, |
142 | 109 | ), |
143 | 110 | >, |
144 | 111 | contexts: Query<Option<&ControlledBy>, With<Controlled>>, |
145 | 112 | clients: Query<(), With<Client>>, |
146 | 113 | mut commands: Commands, |
147 | 114 | ) { |
148 | | - if let Ok(action_of) = action.get(trigger.entity) |
149 | | - && action_targets_local_client(action_of, &contexts, &clients) |
150 | | - { |
151 | | - commands |
152 | | - .entity(trigger.entity) |
153 | | - .insert(InputMarker::<C>::default()); |
154 | | - } |
155 | | -} |
156 | | - |
157 | | -/// If a prespawned action entity is confirmed but still targets a locally controlled context, |
158 | | -/// keep using it as a local input source. |
159 | | -pub(crate) fn add_input_marker_from_confirmed_controlled_action<C: Component>( |
160 | | - trigger: On<Add, ConfirmHistory>, |
161 | | - action: Query<&ActionOf<C>, (With<ConfirmHistory>, Or<(With<Bindings>, With<ActionMock>)>)>, |
162 | | - contexts: Query<Option<&ControlledBy>, With<Controlled>>, |
163 | | - clients: Query<(), With<Client>>, |
164 | | - mut commands: Commands, |
165 | | -) { |
166 | | - if let Ok(action_of) = action.get(trigger.entity) |
167 | | - && action_targets_local_client(action_of, &contexts, &clients) |
168 | | - { |
| 115 | + let Ok(action_of) = action.get(trigger.entity) else { |
| 116 | + return; |
| 117 | + }; |
| 118 | + if action_targets_local_client(action_of, &contexts, &clients) { |
169 | 119 | commands |
170 | 120 | .entity(trigger.entity) |
171 | 121 | .insert(InputMarker::<C>::default()); |
|
0 commit comments