Last active
October 18, 2022 18:00
-
-
Save Xion/98763ce5daa21c83740f1218f5b88fba to your computer and use it in GitHub Desktop.
Count installed Bevy plugins
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// License: BSD-3 | |
use std::io::{self, Write}; | |
use std::string::FromUtf8Error; | |
use std::sync::Arc; | |
use antidote::RwLock; | |
use bevy::prelude::*; | |
use bevy::reflect::FromReflect; | |
use bevy::utils::tracing::{self, Dispatch}; | |
use regex::Regex; | |
use tracing_subscriber::fmt::MakeWriter; | |
/// Resource storing the results of [`trace_app_bootstrap`]. | |
#[derive(Debug, Default, FromReflect, Reflect)] | |
#[reflect(Default)] | |
pub struct AppBootstrapTrace { | |
/// Names of installed Bevy [`Plugin`]s. | |
pub plugin_names: Vec<String>, | |
} | |
/// Trace the bootstrap of a Bevy [`App`]. | |
/// | |
/// Gathered information is stored as [`AppBootstrapTrace`] resource. | |
pub fn trace_app_bootstrap(f: impl FnOnce() -> App) -> App { | |
let tracer = BootstrapTracer::default(); | |
let subscriber = tracing_subscriber::fmt() | |
.with_env_filter("off,bevy_app=debug") | |
.with_level(false) | |
.with_line_number(false) | |
.with_writer(tracer.clone()) | |
.finish(); | |
let dispatcher = Dispatch::new(subscriber); | |
let mut app = tracing::dispatcher::with_default(&dispatcher, f); | |
let output: String = tracer.try_into().unwrap(); | |
let mut trace = AppBootstrapTrace::default(); | |
for plugin in ADDED_PLUGIN_LOG_RE.captures_iter(&output) { | |
trace.plugin_names.push(plugin["name"].to_string()); | |
} | |
// If we haven't captured anything, then it's almost certainly be Bevy changing the debug!() | |
// log in [`App::add_plugin`] (as it's obviously not part of the API). | |
if trace.plugin_names.is_empty() { | |
warn!("No Bevy plugin names captured! Check for changes in bevy_app::App::add_plugin()"); | |
} else { | |
app.insert_resource(trace); | |
} | |
app | |
} | |
lazy_static! { | |
/// Regex extracting logs emitted by [`bevy_app::App::add_plugin`]. | |
static ref ADDED_PLUGIN_LOG_RE: Regex = Regex::new("added plugin: (?P<name>.+)\n").unwrap(); | |
} | |
/// Gathers [`tracing`] output and allows to retrieve it as a string. | |
#[derive(Clone, Debug)] | |
struct BootstrapTracer { | |
output: Arc<RwLock<Vec<SyncVecWriter>>>, | |
} | |
impl Default for BootstrapTracer { | |
fn default() -> Self { | |
Self { output: Arc::new(RwLock::new(Vec::new())) } | |
} | |
} | |
impl<'w> MakeWriter<'w> for BootstrapTracer { | |
type Writer = SyncVecWriter; | |
fn make_writer(&'w self) -> Self::Writer { | |
let mut output = self.output.write(); | |
output.push(SyncVecWriter::default()); | |
output.last().unwrap().clone() | |
} | |
} | |
impl TryFrom<BootstrapTracer> for String { | |
type Error = FromUtf8Error; | |
fn try_from(tracer: BootstrapTracer) -> Result<String, Self::Error> { | |
let mut string = String::new(); | |
for chunk in tracer.output.read().iter() { | |
string += &String::try_from(chunk.clone())?; | |
} | |
Ok(string) | |
} | |
} | |
/// A thread-safe writer to an in-memory [`Vec`] of bytes. | |
#[derive(Clone, Debug)] | |
struct SyncVecWriter { | |
buf: Arc<RwLock<Vec<u8>>>, | |
} | |
impl Default for SyncVecWriter { | |
fn default() -> Self { | |
Self { buf: Arc::new(RwLock::new(Vec::new())) } | |
} | |
} | |
impl Write for SyncVecWriter { | |
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | |
self.buf.write().write(buf) | |
} | |
fn flush(&mut self) -> io::Result<()> { | |
self.buf.write().flush() | |
} | |
} | |
impl TryFrom<SyncVecWriter> for String { | |
type Error = FromUtf8Error; | |
fn try_from(writer: SyncVecWriter) -> Result<String, Self::Error> { | |
String::from_utf8(writer.buf.read().clone()) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment