use std::sync::atomic::{AtomicUsize, Ordering};
use bevy::ecs::message::Messages;
use pybevy_core::registry::global_registry;
pub use pybevy_core::{PyMessage, PyMessageId};
use pyo3::{
IntoPyObjectExt,
exceptions::{PyStopIteration, PyTypeError},
prelude::*,
types::PyType,
};
use super::{
messages::{
CustomMessage1, CustomMessage2, CustomMessage3, CustomMessage4, CustomMessage5,
CustomMessage6, CustomMessage7, CustomMessage8, CustomMessage9, CustomMessage10,
CustomMessage11, CustomMessage12, CustomMessage13, CustomMessage14, CustomMessage15,
CustomMessage16, CustomMessage17, CustomMessage18, CustomMessage19, CustomMessage20,
MessageRegistry, MessageType, PyMessages,
},
world::PyWorld,
};
use crate::ecs::messages::PyMessageType;
macro_rules! write_message_arms {
($message_num:expr, $world:expr, $py:expr, $message:expr, $($idx:literal => $type_num:literal),+ $(,)?) => {
paste::paste! {
match $message_num {
$(
$idx => {
let mut messages = $world.get_resource_or_insert_with(|| Messages::<[<CustomMessage $type_num>]>::default());
Box::new(messages.write([<CustomMessage $type_num>]($message.clone_ref($py))))
}
)+
_ => return Err(PyTypeError::new_err("Invalid message number")),
}
}
};
}
#[derive(Debug, Clone, PartialEq)]
pub enum MessageClass {
Reader,
Writer,
}
#[pyclass(frozen)]
#[derive(Debug, Clone, PartialEq)]
pub struct MessageTypeParam {
pub(crate) ty: MessageClass,
pub(crate) message_type: MessageType,
}
#[pyclass(name = "MessageWriter", frozen)]
pub struct PyMessageWriter {
pub(crate) message_type: MessageType,
pub(crate) world: PyWorld,
}
impl PyMessageWriter {
}
#[pymethods]
impl PyMessageWriter {
#[classmethod]
#[pyo3(signature = (key, /))]
pub fn __class_getitem__(
_cls: &Bound<'_, PyType>,
key: &Bound<'_, PyAny>,
) -> PyResult<Py<PyAny>> {
let py = key.py();
let type_obj = key.cast::<PyType>()?;
let py_message_type = PyMessageType::from_message_type(&type_obj)?;
let param = MessageTypeParam {
ty: MessageClass::Writer,
message_type: py_message_type.0,
};
param.into_py_any(py)
}
pub fn write(&self, py: Python, message: Py<PyAny>) -> PyResult<PyMessageId> {
let bound_message = message.bind(py);
match &self.message_type {
MessageType::KeyboardInput => Err(PyTypeError::new_err(
"KeyboardInput events are read-only (generated by input system)",
)),
MessageType::GamepadRumbleRequest => Err(PyTypeError::new_err(
"GamepadRumbleRequest events are write-only via rumble API",
)),
MessageType::WindowEvent => Err(PyTypeError::new_err(
"WindowEvent events are read-only (generated by window system)",
)),
MessageType::SceneInstanceReady => Err(PyTypeError::new_err(
"SceneInstanceReady events are read-only (generated by scene system)",
)),
MessageType::AssetEventImage => Err(PyTypeError::new_err(
"AssetEvent<Image> not yet implemented",
)),
MessageType::AssetEventMesh => {
Err(PyTypeError::new_err("AssetEvent<Mesh> not yet implemented"))
}
MessageType::Custom(py_type) => {
let msg_type = bound_message.get_type();
let expected_type = py_type.bind(py);
if !msg_type.is(&expected_type) {
return Err(PyTypeError::new_err(format!(
"Expected message of type {}, got {}",
expected_type.name()?,
msg_type.name()?,
)));
}
let world = self.world.world_mut()?;
let type_ptr = expected_type.as_type_ptr();
let message_num = {
let registry = world.resource::<MessageRegistry>();
registry
.get(type_ptr)
.ok_or_else(|| PyTypeError::new_err("Message type not registered"))?
.message_num
};
let event_id: Box<dyn std::any::Any + Send + Sync> = write_message_arms!(message_num, world, py, message,
0=>1, 1=>2, 2=>3, 3=>4, 4=>5, 5=>6, 6=>7, 7=>8, 8=>9, 9=>10,
10=>11, 11=>12, 12=>13, 13=>14, 14=>15, 15=>16, 16=>17, 17=>18, 18=>19, 19=>20
);
Ok(PyMessageId::from_boxed(event_id))
}
MessageType::Dynamic(type_ptr) => {
let bridge =
global_registry::get_message_bridge_by_py_type(*type_ptr).ok_or_else(|| {
PyTypeError::new_err("Message type not registered in global registry")
})?;
if bridge.is_read_only() {
return Err(PyTypeError::new_err(format!(
"{} messages are read-only (generated by system)",
bridge.name()
)));
}
let world = self.world.world_mut()?;
let event_id = bridge.write_message(py, world, &bound_message)?;
Ok(PyMessageId::from_boxed(event_id))
}
}
}
pub fn write_batch(&self, py: Python, messages: Vec<Py<PyAny>>) -> PyResult<Vec<PyMessageId>> {
let mut message_ids = Vec::new();
for message in messages {
let message_id = self.write(py, message)?;
message_ids.push(message_id);
}
Ok(message_ids)
}
pub fn write_default(&self, py: Python) -> PyResult<PyMessageId> {
match &self.message_type {
MessageType::KeyboardInput => Err(PyTypeError::new_err(
"KeyboardInput events are read-only (generated by input system)",
)),
MessageType::GamepadRumbleRequest => Err(PyTypeError::new_err(
"GamepadRumbleRequest events are read-only (generated by gamepad system)",
)),
MessageType::WindowEvent => Err(PyTypeError::new_err(
"WindowEvent events are read-only (generated by window system)",
)),
MessageType::SceneInstanceReady => Err(PyTypeError::new_err(
"SceneInstanceReady events are read-only (generated by scene system)",
)),
MessageType::AssetEventImage => Err(PyTypeError::new_err(
"AssetEvent<Image> has no default value",
)),
MessageType::AssetEventMesh => Err(PyTypeError::new_err(
"AssetEvent<Mesh> has no default value",
)),
MessageType::Custom(type_obj) => {
let instance = type_obj.bind(py).call0()?;
self.write(py, instance.unbind())
}
MessageType::Dynamic(type_ptr) => {
let bridge =
global_registry::get_message_bridge_by_py_type(*type_ptr).ok_or_else(|| {
PyTypeError::new_err("Message type not registered in global registry")
})?;
if bridge.is_read_only() {
return Err(PyTypeError::new_err(format!(
"{} messages are read-only (generated by system)",
bridge.name()
)));
}
Err(PyTypeError::new_err(format!(
"{} messages don't support write_default",
bridge.name()
)))
}
}
}
}
#[pyclass(name = "MessageReader", frozen)]
pub struct PyMessageReader {
#[allow(dead_code)] pub(crate) world: PyWorld,
pub(crate) messages: PyMessages,
}
impl PyMessageReader {
}
#[pymethods]
impl PyMessageReader {
#[classmethod]
#[pyo3(signature = (key, /))]
pub fn __class_getitem__(
_cls: &Bound<'_, PyType>,
key: &Bound<'_, PyAny>,
) -> PyResult<Py<PyAny>> {
let py = key.py();
let type_obj = key.cast::<PyType>()?;
let py_message_type = PyMessageType::from_message_type(&type_obj)?;
let param = MessageTypeParam {
ty: MessageClass::Reader,
message_type: py_message_type.0,
};
param.into_py_any(py)
}
pub fn clear(&self) -> PyResult<()> {
self.messages.clear()
}
pub fn is_empty(&self) -> PyResult<bool> {
self.messages.is_empty()
}
pub fn len(&self) -> PyResult<usize> {
self.messages.len()
}
pub fn read(&self, py: Python) -> PyResult<PyMessageReaderIter> {
let cached_messages = self.messages.iter_to_python(py)?;
Ok(PyMessageReaderIter {
cursor: AtomicUsize::new(0),
cached_messages,
})
}
pub fn __iter__(&self, py: Python) -> PyResult<PyMessageReaderIter> {
self.read(py)
}
}
#[pyclass(name = "MessageReaderIter")]
pub struct PyMessageReaderIter {
cursor: AtomicUsize,
cached_messages: Vec<Py<PyAny>>,
}
#[pymethods]
impl PyMessageReaderIter {
pub fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
slf
}
pub fn __next__(&self, py: Python) -> PyResult<Py<PyAny>> {
let index = self.cursor.fetch_add(1, Ordering::Relaxed);
if index < self.cached_messages.len() {
Ok(self.cached_messages[index].clone_ref(py))
} else {
Err(PyStopIteration::new_err(""))
}
}
}
fn write_custom_python_message(
world: &mut bevy::ecs::world::World,
py: Python,
msg: &Bound<'_, PyAny>,
) -> PyResult<()> {
use pyo3::types::PyTypeMethods;
let type_ptr = msg.get_type().as_type_ptr();
let message_num = {
let registry = world
.get_resource::<MessageRegistry>()
.ok_or_else(|| PyTypeError::new_err("MessageRegistry not found in world"))?;
registry
.get(type_ptr)
.ok_or_else(|| PyTypeError::new_err("Message type not registered"))?
.message_num
};
let msg_py: Py<PyAny> = msg.clone().unbind();
let _: Box<dyn std::any::Any + Send + Sync> = write_message_arms!(message_num, world, py, msg_py,
0=>1, 1=>2, 2=>3, 3=>4, 4=>5, 5=>6, 6=>7, 7=>8, 8=>9, 9=>10,
10=>11, 11=>12, 12=>13, 13=>14, 14=>15, 15=>16, 16=>17, 17=>18, 18=>19, 19=>20
);
Ok(())
}
pub(crate) fn register_message_write_fn() {
global_registry::register_message_write_fn(|world, py, msg| {
write_custom_python_message(world, py, msg).map_err(|e| format!("{e}"))
});
}
pub(crate) fn register_run_system_once_fn() {
global_registry::register_run_system_once_fn(|world, py, func| {
PyWorld::with_temporary(world, py, |py_world| py_world.run_system_once(func.clone()))
.map_err(|e| format!("{e}"))
});
}