1#![doc = include_str!("../README.md")]
2use std::fmt::Display;
3
4use bevy_app::{App, FixedUpdate, Plugin};
5use bevy_ecs::prelude::*;
6use bevy_reflect::prelude::*;
7use crossbeam::queue::SegQueue;
8use once_cell::sync::Lazy;
9use serde::{Deserialize, Serialize};
10
11#[cfg(feature = "mockup")]
12mod mockup;
13
14pub mod prelude {
15 #[cfg(feature = "mockup")]
16 pub use crate::mockup::{
17 AdDisplay, AdDisplaySettings, MockupAdComponent, MockupAdTimeLeftText, MockupAdType,
18 MockupAds, MockupAdsSystem,
19 };
20 pub use crate::{AdManager, AdMessage, AdType, AdsCommonPlugin};
21}
22
23static EVENT_QUEUE: Lazy<SegQueue<AdMessage>> = Lazy::new(SegQueue::new);
24
25pub fn write_event_to_queue(event: AdMessage) {
28 EVENT_QUEUE.push(event);
29}
30
31#[derive(Message, Debug, Clone, Reflect, Serialize, Deserialize)]
33pub enum AdMessage {
34 Initialized { success: bool },
36 ConsentGathered { success: bool, error: String },
38 AdLoaded { ad_type: AdType },
40 AdFailedToLoad { ad_type: AdType, error: String },
42 AdOpened { ad_type: AdType },
44 AdClosed { ad_type: AdType },
46 RewardedAdEarnedReward { amount: i32, reward_type: String },
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
52pub enum AdType {
53 Banner,
55 Interstitial,
57 Rewarded,
59}
60
61#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Reflect)]
63pub enum ParsingAdTypeError {
64 InvalidValue,
66}
67
68impl TryFrom<&str> for AdType {
69 type Error = ParsingAdTypeError;
70
71 fn try_from(value: &str) -> Result<Self, Self::Error> {
72 match value {
73 "banner" | "Banner" => Ok(AdType::Banner),
74 "interstitial" | "Interstitial" => Ok(AdType::Interstitial),
75 "rewarded" | "Rewarded" => Ok(AdType::Rewarded),
76 _ => Err(ParsingAdTypeError::InvalidValue),
77 }
78 }
79}
80
81impl Display for AdType {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 match self {
84 AdType::Banner => write!(f, "banner"),
85 AdType::Interstitial => write!(f, "interstitial"),
86 AdType::Rewarded => write!(f, "rewarded"),
87 }
88 }
89}
90
91pub trait AdManager {
93 fn initialize(&mut self) -> bool;
96 fn is_initialized(&self) -> bool;
98 fn load_ad(&mut self, ad_type: AdType, ad_id: &str) -> bool {
101 match ad_type {
102 AdType::Banner => self.load_banner(ad_id),
103 AdType::Interstitial => self.load_interstitial(ad_id),
104 AdType::Rewarded => self.load_rewarded(ad_id),
105 }
106 }
107 fn show_ad(&mut self, ad_type: AdType) -> bool {
110 if !self.is_ad_ready(ad_type) {
111 return false;
112 }
113 match ad_type {
114 AdType::Banner => self.show_banner(),
115 AdType::Interstitial => self.show_interstitial(),
116 AdType::Rewarded => self.show_rewarded(),
117 }
118 }
119 fn hide_ad(&mut self, ad_type: AdType) -> bool {
122 match ad_type {
123 AdType::Banner => self.hide_banner(),
124 AdType::Interstitial => self.hide_interstitial(),
125 AdType::Rewarded => self.hide_rewarded(),
126 }
127 }
128 fn is_ad_ready(&self, ad_type: AdType) -> bool {
131 match ad_type {
132 AdType::Banner => self.is_banner_ready(),
133 AdType::Interstitial => self.is_interstitial_ready(),
134 AdType::Rewarded => self.is_rewarded_ready(),
135 }
136 }
137 fn show_banner(&mut self) -> bool;
140 fn show_interstitial(&mut self) -> bool;
143 fn show_rewarded(&mut self) -> bool;
146 fn hide_banner(&mut self) -> bool;
149 fn hide_interstitial(&mut self) -> bool;
152 fn hide_rewarded(&mut self) -> bool;
155 fn load_banner(&mut self, ad_id: &str) -> bool;
158 fn load_interstitial(&mut self, ad_id: &str) -> bool;
161 fn load_rewarded(&mut self, ad_id: &str) -> bool;
164 fn is_banner_ready(&self) -> bool {
166 true
167 }
168 fn is_interstitial_ready(&self) -> bool {
170 false
171 }
172 fn is_rewarded_ready(&self) -> bool {
174 false
175 }
176
177 fn get_banner_width(&self, _ad_id: &str) -> i32 {
179 100
180 }
181
182 fn get_banner_height(&self, _ad_id: &str) -> i32 {
184 50
185 }
186}
187
188pub struct AdsCommonPlugin;
191
192impl Plugin for AdsCommonPlugin {
193 fn build(&self, app: &mut App) {
194 app.add_message::<AdMessage>()
195 .add_systems(FixedUpdate, handle_events)
196 .register_type::<AdMessage>();
197 #[cfg(feature = "mockup")]
198 app.add_plugins(mockup::plugin);
199 }
200}
201
202fn handle_events(mut writer: MessageWriter<AdMessage>) {
203 while let Some(ev) = EVENT_QUEUE.pop() {
204 writer.write(ev);
205 }
206}