bevy_ads_common/
lib.rs

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
25/// Write an event to the queue.
26/// In almost all cases this should be called only by the ads implementation plugin.
27pub fn write_event_to_queue(event: AdMessage) {
28    EVENT_QUEUE.push(event);
29}
30
31/// Events that can be triggered by Ad system operations
32#[derive(Message, Debug, Clone, Reflect, Serialize, Deserialize)]
33pub enum AdMessage {
34    /// Ad system completed initialization.
35    Initialized { success: bool },
36    /// Consent was gathered.
37    ConsentGathered { success: bool, error: String },
38    /// Ad was loaded.
39    AdLoaded { ad_type: AdType },
40    /// Ad failed to load.
41    AdFailedToLoad { ad_type: AdType, error: String },
42    /// Ad was opened.
43    AdOpened { ad_type: AdType },
44    /// Ad was closed.
45    AdClosed { ad_type: AdType },
46    /// Rewarded ad earned reward.
47    RewardedAdEarnedReward { amount: i32, reward_type: String },
48}
49
50/// Ad type description enum.
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
52pub enum AdType {
53    /// Banner ad type
54    Banner,
55    /// Interstitial ad type
56    Interstitial,
57    /// Rewarded ad type
58    Rewarded,
59}
60
61/// Error type for parsing ad type.
62#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Reflect)]
63pub enum ParsingAdTypeError {
64    /// Invalid value error.
65    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
91/// Trait for managing ads system.
92pub trait AdManager {
93    /// Initialize the AdManager.
94    /// Returns true if it was able to start initialization process.
95    fn initialize(&mut self) -> bool;
96    /// Check if the AdManager is initialized.
97    fn is_initialized(&self) -> bool;
98    /// Load an ad of the specified type and ID.
99    /// Returns true if the ad loading process was successfully started.
100    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    /// Show an ad of the specified type.
108    /// Returns true if the ad was successfully shown.
109    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    /// Hide an ad of the specified type.
120    /// Returns true if the ad was successfully hidden.
121    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    /// Check if an ad of the specified type is ready to be shown.
129    /// Returns true if the ad is ready.
130    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    /// Show a banner ad.
138    /// Returns true if the ad was successfully shown.
139    fn show_banner(&mut self) -> bool;
140    /// Show an interstitial ad.
141    /// Returns true if the ad was successfully shown.
142    fn show_interstitial(&mut self) -> bool;
143    /// Show a rewarded ad.
144    /// Returns true if the ad was successfully shown.
145    fn show_rewarded(&mut self) -> bool;
146    /// Hide a banner ad.
147    /// Returns true if the ad was successfully hidden.
148    fn hide_banner(&mut self) -> bool;
149    /// Hide an interstitial ad.
150    /// Returns true if the ad was successfully hidden.
151    fn hide_interstitial(&mut self) -> bool;
152    /// Hide a rewarded ad.
153    /// Returns true if the ad was successfully hidden.
154    fn hide_rewarded(&mut self) -> bool;
155    /// Load a banner ad.
156    /// Returns true if the ad was successfully loaded.
157    fn load_banner(&mut self, ad_id: &str) -> bool;
158    /// Load an interstitial ad.
159    /// Returns true if the ad was successfully loaded.
160    fn load_interstitial(&mut self, ad_id: &str) -> bool;
161    /// Load a rewarded ad.
162    /// Returns true if the ad was successfully loaded.
163    fn load_rewarded(&mut self, ad_id: &str) -> bool;
164    /// Is a banner ad ready to be shown?
165    fn is_banner_ready(&self) -> bool {
166        true
167    }
168    /// Is an interstitial ad ready to be shown?
169    fn is_interstitial_ready(&self) -> bool {
170        false
171    }
172    /// Is a rewarded ad ready to be shown?
173    fn is_rewarded_ready(&self) -> bool {
174        false
175    }
176
177    /// Get the width of the banner ad.
178    fn get_banner_width(&self, _ad_id: &str) -> i32 {
179        100
180    }
181
182    /// Get the height of the banner ad.
183    fn get_banner_height(&self, _ad_id: &str) -> i32 {
184        50
185    }
186}
187
188/// Basic plugin for managing ads.
189/// It provides a set of methods alongside a optional mockup ads implementation.
190pub 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}