diff --git a/Cargo.lock b/Cargo.lock index 563beac..c9fc5e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,6 +103,15 @@ dependencies = [ "wasi", ] +[[package]] +name = "inventory" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" +dependencies = [ + "rustversion", +] + [[package]] name = "itoa" version = "1.0.18" @@ -257,6 +266,12 @@ version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + [[package]] name = "scroll-server" version = "0.1.0" @@ -419,6 +434,7 @@ dependencies = [ name = "titan" version = "0.1.0" dependencies = [ + "inventory", "thiserror", "titan-derive", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 1bcea77..5d172f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" rand = "0.8" async-trait = "0.1" +inventory = "0.3" proc-macro2 = "1" quote = "1" syn = { version = "2", features = ["full", "extra-traits"] } diff --git a/logic/logic/src/factory.rs b/logic/logic/src/factory.rs index 1037c1e..5ace186 100644 --- a/logic/logic/src/factory.rs +++ b/logic/logic/src/factory.rs @@ -1,36 +1,11 @@ -use titan::{message_registry, MessageRegistry}; -use crate::messages::{ - AskForAvatarStreamMessage, AskForBattleReplayStreamMessage, - AskForPlayingFacebookFriendsMessage, AskForTVContentMessage, AvailableServerCommandMessage, - ClientCapabilitiesMessage, EndClientTurnMessage, GoHomeMessage, KeepAliveMessage, - KeepAliveServerMessage, LoginFailedMessage, LoginMessage, LoginOkMessage, OwnHomeDataMessage, - OutOfSyncMessage, ServerErrorMessage, SetDeviceTokenMessage, StartMissionMessage, -}; +use std::sync::Arc; +use titan::MessageRegistry; pub struct LogicScrollMessageFactory; impl LogicScrollMessageFactory { - pub fn registry() -> MessageRegistry { + pub fn registry() -> Arc { scroll_message_registry() } } -pub fn scroll_message_registry() -> MessageRegistry { - message_registry![ - LoginMessage, - ClientCapabilitiesMessage, - KeepAliveMessage, - GoHomeMessage, - EndClientTurnMessage, - StartMissionMessage, - SetDeviceTokenMessage, - AskForPlayingFacebookFriendsMessage, - AskForTVContentMessage, - AskForAvatarStreamMessage, - AskForBattleReplayStreamMessage, - LoginOkMessage, - LoginFailedMessage, - KeepAliveServerMessage, - OwnHomeDataMessage, - AvailableServerCommandMessage, - OutOfSyncMessage, - ServerErrorMessage, - ] +pub fn scroll_message_registry() -> Arc { + MessageRegistry::global() } diff --git a/services/gateway/src/bin/scroll-probe.rs b/services/gateway/src/bin/scroll-probe.rs index dae09c9..f8d5253 100644 --- a/services/gateway/src/bin/scroll-probe.rs +++ b/services/gateway/src/bin/scroll-probe.rs @@ -66,6 +66,10 @@ async fn main() -> Result<(), Box> { } Err(error) => println!("no data tables ({error}), printing raw ids"), } + println!( + "message registry: {} type(s)", + logic::scroll_message_registry().len() + ); println!("connecting to {endpoint}"); let mut probe = Probe::connect(&endpoint).await?; let login = LoginMessage { diff --git a/services/gateway/src/session.rs b/services/gateway/src/session.rs index cfc2f51..e55cf8c 100644 --- a/services/gateway/src/session.rs +++ b/services/gateway/src/session.rs @@ -23,7 +23,7 @@ impl Session { config: Arc, backends: Arc, ) -> Result<(), SessionError> { - let registry: Arc = Arc::new(logic::scroll_message_registry()); + let registry: Arc = logic::scroll_message_registry(); let messaging_config = MessagingConfig { read_timeout: config.read_timeout, max_payload_len: config.max_payload_len, diff --git a/titan/titan-derive/src/lib.rs b/titan/titan-derive/src/lib.rs index 66ef706..7946265 100644 --- a/titan/titan-derive/src/lib.rs +++ b/titan/titan-derive/src/lib.rs @@ -20,9 +20,11 @@ pub fn derive_message(input: TokenStream) -> TokenStream { Ok(tokens) => tokens, Err(error) => return error.into_compile_error().into(), }; + let registration = meta::expand_registration(&input); quote::quote! { #payload #meta + #registration } .into() } diff --git a/titan/titan-derive/src/meta.rs b/titan/titan-derive/src/meta.rs index 9bca6d8..fc2ddf1 100644 --- a/titan/titan-derive/src/meta.rs +++ b/titan/titan-derive/src/meta.rs @@ -87,3 +87,14 @@ fn parse_message_attr(input: &DeriveInput) -> Result { name, }) } +pub fn expand_registration(input: &DeriveInput) -> TokenStream { + if !input.generics.params.is_empty() { + return TokenStream::new(); + } + let ident = &input.ident; + quote! { + ::titan::inventory::submit! { + ::titan::factory::RegistryEntry::of::<#ident>() + } + } +} diff --git a/titan/titan/Cargo.toml b/titan/titan/Cargo.toml index f57f117..a573d4b 100644 --- a/titan/titan/Cargo.toml +++ b/titan/titan/Cargo.toml @@ -9,5 +9,6 @@ description = "Engine layer of the scroll server: byte streams, checksum encoder [dependencies] titan-derive = { workspace = true } thiserror = { workspace = true } +inventory = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } diff --git a/titan/titan/src/factory.rs b/titan/titan/src/factory.rs index 6a70aa6..d954ad2 100644 --- a/titan/titan/src/factory.rs +++ b/titan/titan/src/factory.rs @@ -1,7 +1,8 @@ use std::collections::HashMap; use std::fmt::Debug; +use std::sync::{Arc, OnceLock}; use crate::error::{Error, Result}; -use crate::message::{Direction, Message, MessageMeta}; +use crate::message::{Direction, Message, MessageMeta, Payload}; type DecodeFn = fn(&[u8]) -> Result>; #[derive(Clone, Copy)] pub struct RegistryEntry { @@ -20,7 +21,7 @@ impl Debug for RegistryEntry { } } impl RegistryEntry { - pub fn of() -> Self + pub const fn of() -> Self where T: MessageMeta + Debug + Send + Sync + 'static, { @@ -28,13 +29,21 @@ impl RegistryEntry { message_type: T::MESSAGE_TYPE, name: T::NAME, direction: T::DIRECTION, - decode: |bytes| Ok(Box::new(T::from_bytes(bytes)?)), + decode: decode_into_box::, } } pub fn decode(&self, payload: &[u8]) -> Result> { (self.decode)(payload) } } +fn decode_into_box(payload: &[u8]) -> Result> +where + T: MessageMeta + Debug + Send + Sync + 'static, +{ + Ok(Box::new(T::from_bytes(payload)?)) +} +inventory::collect!(RegistryEntry); +static GLOBAL: OnceLock> = OnceLock::new(); pub trait MessageFactory: Send + Sync { fn create(&self, message_type: u16, message_version: u16, payload: &[u8]) -> Result>; fn lookup(&self, message_type: u16) -> Option<&RegistryEntry>; @@ -54,8 +63,25 @@ impl MessageRegistry { } registry } + pub fn collect() -> Self { + Self::from_entries(inventory::iter::.into_iter().copied()) + } + pub fn global() -> Arc { + Arc::clone(GLOBAL.get_or_init(|| { + let registry = Self::collect(); + tracing::debug!(messages = registry.len(), "message registry collected"); + Arc::new(registry) + })) + } pub fn insert(&mut self, entry: RegistryEntry) { - self.entries.insert(entry.message_type, entry); + if let Some(previous) = self.entries.insert(entry.message_type, entry) { + tracing::warn!( + message_type = entry.message_type, + previous = previous.name, + replacement = entry.name, + "two messages declare the same type" + ); + } } pub fn len(&self) -> usize { self.entries.len() @@ -76,11 +102,3 @@ impl MessageFactory for MessageRegistry { self.entries.get(&message_type) } } -#[macro_export] -macro_rules! message_registry { - ($($ty:ty),* $(,)?) => { - $crate::factory::MessageRegistry::from_entries([ - $($crate::factory::RegistryEntry::of::<$ty>()),* - ]) - }; -} diff --git a/titan/titan/src/lib.rs b/titan/titan/src/lib.rs index 58444c4..1374c69 100644 --- a/titan/titan/src/lib.rs +++ b/titan/titan/src/lib.rs @@ -26,6 +26,7 @@ pub use json::JsonValue; pub use logic_long::LogicLong; pub use message::{Direction, Message, MessageMeta, Payload}; pub use net::{Incoming, Messaging, MessagingConfig, MessagingError, MessagingSender, OutboundMessage}; +pub use inventory; pub use titan_derive::{Message, Payload}; pub mod prelude { pub use crate::codec::{ @@ -37,5 +38,6 @@ pub mod prelude { pub use crate::io::{ByteStreamReader, ByteStreamWriter}; pub use crate::logic_long::LogicLong; pub use crate::message::{Direction, Message, MessageMeta, Payload}; - pub use titan_derive::{Message, Payload}; + pub use inventory; +pub use titan_derive::{Message, Payload}; }