diff --git a/assets/robocraft/config.json b/assets/robocraft/config.json index 45ed08b..7dd73b7 100644 --- a/assets/robocraft/config.json +++ b/assets/robocraft/config.json @@ -14447,6 +14447,22 @@ "main", "sys", "jam_club" + ], + "commands": [ + { + "regex": "\\?online", + "op": { + "type": "BuiltIn", + "built_in": "OnlineUsers" + } + }, + { + "regex": "\\?users", + "op": { + "type": "BuiltIn", + "built_in": "TotalUsers" + } + } ] }, "factory": { @@ -14515,4 +14531,4 @@ } ] } -} +} \ No newline at end of file diff --git a/rc_chat_room/src/data/mod.rs b/rc_chat_room/src/data/mod.rs index ff02972..d819045 100644 --- a/rc_chat_room/src/data/mod.rs +++ b/rc_chat_room/src/data/mod.rs @@ -1 +1 @@ -pub mod channel; +pub use rc_core::data::channel; diff --git a/rc_chat_room/src/main.rs b/rc_chat_room/src/main.rs index 8f19175..c112b16 100644 --- a/rc_chat_room/src/main.rs +++ b/rc_chat_room/src/main.rs @@ -1,7 +1,6 @@ #![forbid(unsafe_code)] mod cli; mod state; -mod persist; mod op_handler; pub use op_handler::SimpleChatFunc; @@ -10,6 +9,7 @@ mod operations; mod events; use polariton_auth::Handshake; +use rc_core::ConfigProvider; use tokio::net; use polariton::packet::{Data, Message, Packet, StandardMessage}; @@ -26,9 +26,9 @@ async fn main() -> std::io::Result<()> { let cubes = rc_core::persist::config::ConfigImpl::load(&args.assets).expect("Bad config data"); let users = std::sync::Arc::new(rc_core::persist::user::UserImpl::load(&args.data, &cubes).await.expect("Bad user data")); - let chat_system = state::chat::ChatImpl::new(&args.assets, &args.data).expect("Bad chat config data"); + let chat_system = state::chat::ChatImpl::new(>::chat_system_config(&cubes)).expect("Bad chat config data"); - let server = std::sync::Arc::new(polariton_server::Server::new(operations::handler(chat_system, &args.data, &cubes), polariton_server::events::EventsHandler::new())); + let server = std::sync::Arc::new(polariton_server::Server::new(operations::handler(chat_system, &cubes), polariton_server::events::EventsHandler::new())); let ip_addr: std::net::IpAddr = args.ip.parse().expect("Invalid IP address"); diff --git a/rc_chat_room/src/operations/all_joined_channels.rs b/rc_chat_room/src/operations/all_joined_channels.rs index bb26d8d..b553688 100644 --- a/rc_chat_room/src/operations/all_joined_channels.rs +++ b/rc_chat_room/src/operations/all_joined_channels.rs @@ -1,61 +1,35 @@ -use crate::SimpleChatFunc; -use crate::persist::chat_user::ChatUser; -use polariton::operation::ParameterTable; +//use rc_core::persist::user::ChatUser; +use polariton::operation::{ParameterTable, OperationResponse}; + +const CODE: u8 = 11; const PARAM_KEY: u8 = 18; -pub(super) fn all_channels_provider(chat_system: crate::state::ChatImpl) -> SimpleChatFunc<11, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy, &crate::state::ChatImpl) -> Result) + Sync + Sync> { - SimpleChatFunc::new(|params, user: &crate::UserTy, _chat_system: &crate::state::ChatImpl| { - let mut params = params.to_dict(); - let user_trait = user.user()?; - let chat_user = super::get_chat_user(user_trait.as_ref().as_ref()); - //let chat_user: &ChatUserImpl = user_trait.ext(std::any::TypeId::of::()).unwrap().downcast_ref().unwrap(); - params.insert(PARAM_KEY, chat_user.subscribed_channels()); - /*params.insert(PARAM_KEY, Typed::Arr(Arr { - ty: polariton::serdes::TypePrefix::HashMap, // hashtable - items: vec![ - ChatChannelInfo { - channel_name: "RE_public_channel0".to_owned(), - members: vec![ - ChatChannelMember { - name: "RE_chat0_username0".to_owned(), - use_custom_avatar: false, - state: ChatPlayerState::Idk1, - custom_avatar: Vec::default(), - avatar_id: 2, - }, - ChatChannelMember { - name: "RE_chat0_username1".to_owned(), - use_custom_avatar: false, - state: ChatPlayerState::Idk2, - custom_avatar: Vec::default(), - avatar_id: 3, - }, - ], - channel_ty: ChatChannelType::Public, - }.as_transmissible(), - ChatChannelInfo { - channel_name: "RE_custom_channel1".to_owned(), - members: vec![ - ChatChannelMember { - name: "RE_chat1_username0".to_owned(), - use_custom_avatar: false, - state: ChatPlayerState::Idk0, - custom_avatar: Vec::default(), - avatar_id: 2, - }, - ChatChannelMember { - name: "RE_chat1_username1".to_owned(), - use_custom_avatar: false, - state: ChatPlayerState::Idk1, - custom_avatar: Vec::default(), - avatar_id: 3, - }, - ], - channel_ty: ChatChannelType::Custom, - }.as_transmissible(), - ], - }));*/ - Ok(params.into()) - }, chat_system) +async fn do_handling(params: ParameterTable<()>, user: &crate::UserTy) -> Result { + let mut params = params.to_dict(); + let user_info = user.user()?; + params.insert(PARAM_KEY, user_info.subscribed_channels().await?); + Ok(params.into()) +} + +pub struct JoinedChannelsProvider; + +#[async_trait::async_trait] +impl polariton_server::operations::Operation<()> for JoinedChannelsProvider { + type User = crate::UserTy; + + async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> { + polariton_server::operations::result_to_op_resp::(do_handling(params, user).await) + } +} + +impl polariton_server::operations::OperationCode for JoinedChannelsProvider { + fn op_code() -> u8 { + CODE + } +} + + +pub(super) fn all_channels_provider() -> JoinedChannelsProvider { + JoinedChannelsProvider } diff --git a/rc_chat_room/src/operations/join_channel.rs b/rc_chat_room/src/operations/join_channel.rs index 9f15d8b..b8b9f42 100644 --- a/rc_chat_room/src/operations/join_channel.rs +++ b/rc_chat_room/src/operations/join_channel.rs @@ -1,41 +1,85 @@ -use crate::{persist::chat_user::ChatUser, SimpleChatFunc}; -use polariton::operation::{ParameterTable, Typed}; +use polariton::operation::{ParameterTable, Typed, OperationResponse}; + +const JOIN_CODE: u8 = 1; +const LEAVE_CODE: u8 = 5; const CHANNEL_NAME_PARAM_KEY: u8 = 3; // str; in const CHANNEL_TYPE_PARAM_KEY: u8 = 1; // int; in const CHANNEL_PASSWORD_PARAM_KEY: u8 = 16; // str; in const CHANNEL_INFO_PARAM_KEY: u8 = 17; // hashtable; out -pub(super) fn join_channel_provider(chat_system: crate::state::ChatImpl) -> SimpleChatFunc<1, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy, &crate::state::ChatImpl) -> Result) + Sync + Sync> { - SimpleChatFunc::new(|params, user: &crate::UserTy, chat_system| { - let mut params = params.to_dict(); - if let Some(Typed::Str(chann_name)) = params.remove(&CHANNEL_NAME_PARAM_KEY) { - if let Some(Typed::Int(chann_ty)) = params.remove(&CHANNEL_TYPE_PARAM_KEY) { - if let Some(Typed::Str(channel_pwd)) = params.remove(&CHANNEL_PASSWORD_PARAM_KEY) { - log::warn!("Received channel password {} which is unsupported", channel_pwd.string); - } - let user_info = user.user()?; - let chat_user = super::get_chat_user(user_info.as_ref().as_ref()); - chat_system.system_mut().join_channel(user_info.token().uuid.clone(), chann_name.string.clone()); - let response = chat_user.add_subscribed_channel(chann_name.string, crate::data::channel::ChatChannelType::from_u8(chann_ty as _)?); - params.insert(CHANNEL_INFO_PARAM_KEY, response); - } - } - Ok(params.into()) - }, chat_system) +pub struct JoinChannelProvider { + chat_system: crate::state::ChatImpl, } -pub(super) fn leave_channel_provider(chat_system: crate::state::ChatImpl) -> SimpleChatFunc<5, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy, &crate::state::ChatImpl) -> Result) + Sync + Sync> { - SimpleChatFunc::new(|params, user: &crate::UserTy, chat_system| { - let mut params = params.to_dict(); - if let Some(Typed::Str(chann_name)) = params.remove(&CHANNEL_NAME_PARAM_KEY) { - if let Some(Typed::Int(chann_ty)) = params.remove(&CHANNEL_TYPE_PARAM_KEY) { - let user_info = user.user()?; - let chat_user = super::get_chat_user(user_info.as_ref().as_ref()); - chat_system.system_mut().leave_channel(user_info.token().uuid.clone(), chann_name.string.clone()); - chat_user.remove_subscribed_channel(chann_name.string, crate::data::channel::ChatChannelType::from_u8(chann_ty as _)?); +pub(super) fn join_channel_provider(chat_system: crate::state::ChatImpl) -> JoinChannelProvider { + JoinChannelProvider { chat_system } +} + +async fn do_join_handling(params: ParameterTable<()>, user: &crate::UserTy, chat_system: &crate::state::ChatImpl) -> Result { + let mut params = params.to_dict(); + if let Some(Typed::Str(chann_name)) = params.remove(&CHANNEL_NAME_PARAM_KEY) { + if let Some(Typed::Int(chann_ty)) = params.remove(&CHANNEL_TYPE_PARAM_KEY) { + if let Some(Typed::Str(channel_pwd)) = params.remove(&CHANNEL_PASSWORD_PARAM_KEY) { + log::warn!("Received channel password {} which is unsupported", channel_pwd.string); } + let user_info = user.user()?; + //let chat_user = super::get_chat_user(user_info.as_ref().as_ref()); + chat_system.system_mut().join_channel(user_info.token().uuid.clone(), chann_name.string.clone()); + let response = user_info.add_subscribed_channel(chann_name.string, crate::data::channel::ChatChannelType::from_u8(chann_ty as _)?).await?; + params.insert(CHANNEL_INFO_PARAM_KEY, response); } - Ok(params.into()) - }, chat_system) + } + Ok(params.into()) +} + +#[async_trait::async_trait] +impl polariton_server::operations::Operation<()> for JoinChannelProvider { + type User = crate::UserTy; + + async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> { + polariton_server::operations::result_to_op_resp::(do_join_handling(params, user, &self.chat_system).await) + } +} + +impl polariton_server::operations::OperationCode for JoinChannelProvider { + fn op_code() -> u8 { + JOIN_CODE + } +} + +pub struct LeaveChannelProvider { + chat_system: crate::state::ChatImpl, +} + +pub(super) fn leave_channel_provider(chat_system: crate::state::ChatImpl) -> LeaveChannelProvider { + LeaveChannelProvider { chat_system } +} + +async fn do_leave_handling(params: ParameterTable<()>, user: &crate::UserTy, chat_system: &crate::state::ChatImpl) -> Result { + let mut params = params.to_dict(); + if let Some(Typed::Str(chann_name)) = params.remove(&CHANNEL_NAME_PARAM_KEY) { + if let Some(Typed::Int(chann_ty)) = params.remove(&CHANNEL_TYPE_PARAM_KEY) { + let user_info = user.user()?; + //let chat_user = super::get_chat_user(user_info.as_ref().as_ref()); + chat_system.system_mut().leave_channel(user_info.token().uuid.clone(), chann_name.string.clone()); + user_info.remove_subscribed_channel(chann_name.string, crate::data::channel::ChatChannelType::from_u8(chann_ty as _)?).await?; + } + } + Ok(params.into()) +} + +#[async_trait::async_trait] +impl polariton_server::operations::Operation<()> for LeaveChannelProvider { + type User = crate::UserTy; + + async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> { + polariton_server::operations::result_to_op_resp::(do_leave_handling(params, user, &self.chat_system).await) + } +} + +impl polariton_server::operations::OperationCode for LeaveChannelProvider { + fn op_code() -> u8 { + LEAVE_CODE + } } diff --git a/rc_chat_room/src/operations/mod.rs b/rc_chat_room/src/operations/mod.rs index 7446e7c..e997475 100644 --- a/rc_chat_room/src/operations/mod.rs +++ b/rc_chat_room/src/operations/mod.rs @@ -6,26 +6,24 @@ mod send_message; mod public_channels; mod join_channel; mod user_online; +mod subscribed_channels; use polariton_server::operations::OperationsHandler; -pub fn handler(chat_system: crate::state::chat::ChatImpl, data_root: impl AsRef, conf: &rc_core::persist::config::ConfigImpl) -> OperationsHandler { +pub fn handler(chat_system: crate::state::chat::ChatImpl, conf: &rc_core::persist::config::ConfigImpl) -> OperationsHandler { OperationsHandler::new() .modify(rc_core::polariton::OpIdCopy) - .add(more_auth::MoreLobbyAuth::new(chat_system.clone(), data_root)) + .add(more_auth::MoreLobbyAuth::new(chat_system.clone())) .add(chat_ignores::ignores_provider()) .add(pending_sanctions::pending_sanctions_checker()) - .add(all_joined_channels::all_channels_provider(chat_system.clone())) - .add(polariton_server::operations::Ack::<12, _>::default()) + .add(all_joined_channels::all_channels_provider()) + //.add(polariton_server::operations::Ack::<12, _>::default()) .add(send_message::send_public_message_handler(chat_system.clone())) .add(public_channels::public_channels_provider(conf)) .add(join_channel::join_channel_provider(chat_system.clone())) .add(user_online::is_online_provider(chat_system.clone())) .add(send_message::send_private_message_handler(chat_system.clone())) .add(join_channel::leave_channel_provider(chat_system.clone())) + .add(subscribed_channels::all_subbed_channels_provider()) //.add(polariton_server::operations::Ack::<00000, _>::default()) } - -pub(self) fn get_chat_user<'a, C>(user: &'a dyn rc_core::persist::user::User) -> &'a crate::persist::chat_user::ChatUserImpl { - user.ext(std::any::TypeId::of::()).unwrap().downcast_ref().unwrap() -} diff --git a/rc_chat_room/src/operations/more_auth.rs b/rc_chat_room/src/operations/more_auth.rs index 435d83a..629bd5a 100644 --- a/rc_chat_room/src/operations/more_auth.rs +++ b/rc_chat_room/src/operations/more_auth.rs @@ -1,24 +1,23 @@ use polariton::operation::Typed; use polariton_server::operations::{Operation, OperationCode}; -use crate::persist::chat_user::{ChatUser, ChatUserImpl}; +//use crate::persist::chat_user::{ChatUser, ChatUserImpl}; +//use rc_core::persist::user::ChatUser; pub struct MoreLobbyAuth { chat_system: crate::state::chat::ChatImpl, - root: std::path::PathBuf, } impl MoreLobbyAuth { const AUTH_PAYLOAD_KEY: u8 = 245; - pub fn new(chat_system: crate::state::chat::ChatImpl, root: impl AsRef) -> Self { + pub fn new(chat_system: crate::state::chat::ChatImpl) -> Self { Self { chat_system, - root: root.as_ref().to_path_buf(), } } - fn build_ext_map(&self, token: &rc_core::persist::user::UserToken) -> Option>> { + /*fn build_ext_map(&self, token: &rc_core::persist::user::UserToken) -> Option>> { let user_dir = self.root.join(rc_core::persist::user::USERS_DIR).join(&token.uuid); let data = if let Ok(data) = ChatUserImpl::load(&user_dir) { data @@ -29,15 +28,15 @@ impl MoreLobbyAuth { let mut map = std::collections::HashMap::with_capacity(1); map.insert(std::any::TypeId::of::(), Box::new(data) as _); Some(map) - } + }*/ async fn do_auth(&self, params: std::collections::HashMap>, user: &crate::UserTy) -> Result, i16> { if let Some(Typed::Str(auth_payload)) = params.get(&Self::AUTH_PAYLOAD_KEY) { - if user.update_with_auth_ext(&auth_payload.string, |t| self.build_ext_map(t)).await { + if user.update_with_auth(&auth_payload.string).await { let user_impl = user.user()?; let name = user_impl.token().uuid.clone(); - let chat_user = super::get_chat_user(user_impl.as_ref().as_ref()); - let channels = chat_user.subscribed_channels_strings(); + //let chat_user = super::get_chat_user(user_impl.as_ref().as_ref()); + let channels = user_impl.subscribed_channels_strings().await?; let event_tx = user.event_sender(); self.chat_system.system_mut().connect_user(name, channels, event_tx); let mut resp_params = std::collections::HashMap::new(); diff --git a/rc_chat_room/src/operations/subscribed_channels.rs b/rc_chat_room/src/operations/subscribed_channels.rs new file mode 100644 index 0000000..be42e94 --- /dev/null +++ b/rc_chat_room/src/operations/subscribed_channels.rs @@ -0,0 +1,35 @@ +//use rc_core::persist::user::ChatUser; +use polariton::operation::{ParameterTable, OperationResponse}; + +const CODE: u8 = 12; + +const PARAM_KEY: u8 = 18; + +async fn do_handling(params: ParameterTable<()>, user: &crate::UserTy) -> Result { + let mut params = params.to_dict(); + let user_info = user.user()?; + params.insert(PARAM_KEY, user_info.subscribed_channels().await?); + Ok(params.into()) +} + +pub struct SubscribedChannelsProvider; + +#[async_trait::async_trait] +impl polariton_server::operations::Operation<()> for SubscribedChannelsProvider { + type User = crate::UserTy; + + async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> { + polariton_server::operations::result_to_op_resp::(do_handling(params, user).await) + } +} + +impl polariton_server::operations::OperationCode for SubscribedChannelsProvider { + fn op_code() -> u8 { + CODE + } +} + + +pub(super) fn all_subbed_channels_provider() -> SubscribedChannelsProvider { + SubscribedChannelsProvider +} diff --git a/rc_chat_room/src/persist/chat_user/chat_json.rs b/rc_chat_room/src/persist/chat_user/chat_json.rs deleted file mode 100644 index 5fc91c7..0000000 --- a/rc_chat_room/src/persist/chat_user/chat_json.rs +++ /dev/null @@ -1,111 +0,0 @@ -use serde::{Serialize, Deserialize}; -use polariton::operation::{Typed, Arr}; - -use crate::data::channel::{ChatChannelInfo, ChatChannelType}; - -pub struct ChatUserInfo { - data: std::sync::RwLock, - root: std::path::PathBuf, -} - -impl ChatUserInfo { - pub fn load(root: impl AsRef) -> std::io::Result { - let data = ChatUserData::load(root.as_ref())?; - Ok(Self { - data: std::sync::RwLock::new(data), - root: root.as_ref().to_path_buf(), - }) - } - - /*pub fn save(&self) -> std::io::Result<()> { - self.data.read().unwrap().save(&self.root) - }*/ - - pub fn default_load(root: impl AsRef) -> Self { - let data = ChatUserData::default_load(); - if let Err(e) = data.save(root.as_ref()) { - log::error!("Failed to save default chat data to {}: {}", root.as_ref().display(), e); - } - Self { - data: std::sync::RwLock::new(data), - root: root.as_ref().to_path_buf(), - } - } -} - -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct ChatUserData { - subscribed_channels: Vec, -} - -impl ChatUserData { - fn load(root: impl AsRef) -> std::io::Result { - let file = std::fs::File::open(root.as_ref().join(super::CHAT_USER_FILE))?; - let buffered = std::io::BufReader::new(file); - let result = serde_json::from_reader(buffered)?; - Ok(result) - } - - fn save(&self, root: impl AsRef) -> std::io::Result<()> { - let file = std::fs::File::create(root.as_ref().join(super::CHAT_USER_FILE))?; - let buffered = std::io::BufWriter::new(file); - serde_json::to_writer_pretty(buffered, self)?; - Ok(()) - } - - fn default_load() -> Self { - Self { - subscribed_channels: vec![ - "main".to_owned(), - "sys".to_owned(), - ] - } - } -} - -impl super::ChatUser for ChatUserInfo { - fn subscribed_channels(&self) -> polariton::operation::Typed<()> { - Typed::Arr(Arr { - ty: polariton::serdes::TypePrefix::HashMap, // hashtable - items: self.data.read().unwrap().subscribed_channels.iter().map(|name| ChatChannelInfo { - channel_name: name.to_owned(), - members: Vec::default(), - channel_ty: ChatChannelType::Public, - }.as_transmissible()).collect() - }) - } - - fn subscribed_channels_strings(&self) -> Vec { - self.data.read().unwrap().subscribed_channels.clone() - } - - fn add_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Typed<()> { - if let crate::data::channel::ChatChannelType::Public = channel_ty { - let mut lock = self.data.write().unwrap(); - lock.subscribed_channels.push(channel.clone()); - if let Err(e) = lock.save(&self.root) { - log::error!("Failed to save chat data to {}: {}", self.root.display(), e); - } - } - crate::data::channel::ChatChannelInfo { - channel_name: channel, - members: Vec::default(), - channel_ty, - }.as_transmissible() - } - - fn remove_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> bool { - if let crate::data::channel::ChatChannelType::Public = channel_ty { - let mut lock = self.data.write().unwrap(); - if let Some(index) = lock.subscribed_channels.iter().position(|chann| chann == &channel) { - lock.subscribed_channels.swap_remove(index); - if let Err(e) = lock.save(&self.root) { - log::error!("Failed to save chat data to {}: {}", self.root.display(), e); - } else { - return true; - } - } - } - false - } -} diff --git a/rc_chat_room/src/persist/chat_user/mod.rs b/rc_chat_room/src/persist/chat_user/mod.rs deleted file mode 100644 index cb57a59..0000000 --- a/rc_chat_room/src/persist/chat_user/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod chat_json; -pub use chat_json::ChatUserInfo; - -mod traits; -pub use traits::ChatUser; - -pub const CHAT_USER_FILE: &str = "chat.json"; -pub type ChatUserImpl = ChatUserInfo; diff --git a/rc_chat_room/src/persist/chat_user/traits.rs b/rc_chat_room/src/persist/chat_user/traits.rs deleted file mode 100644 index 0772cd1..0000000 --- a/rc_chat_room/src/persist/chat_user/traits.rs +++ /dev/null @@ -1,8 +0,0 @@ -use polariton::operation::Typed; - -pub trait ChatUser { - fn subscribed_channels(&self) -> Typed<()>; - fn subscribed_channels_strings(&self) -> Vec; - fn add_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Typed<()>; - fn remove_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> bool; -} diff --git a/rc_chat_room/src/persist/config/chat.rs b/rc_chat_room/src/persist/config/chat.rs deleted file mode 100644 index 8f6607f..0000000 --- a/rc_chat_room/src/persist/config/chat.rs +++ /dev/null @@ -1,43 +0,0 @@ -use serde::{Serialize, Deserialize}; - -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct ChatSystemConfig { - #[serde(default = "default_command_chann")] - pub command_channel: String, - pub commands: Vec, -} - -fn default_command_chann() -> String { - "sys".to_owned() -} - -impl ChatSystemConfig { - pub fn load(asset_root: impl AsRef) -> std::io::Result { - let file = std::fs::File::open(asset_root.as_ref().join(super::CHAT_CONFIG_FILE))?; - let buffered = std::io::BufReader::new(file); - let config = serde_json::from_reader(buffered)?; - Ok(config) - } -} - -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct ChatCommand { - pub regex: String, - pub op: ChatOperation, -} - -#[derive(Serialize, Deserialize, Clone, Debug)] -#[serde(tag = "type")] -pub enum ChatOperation { - BuiltIn(BuiltInChatOperation), - Custom, - #[serde(alias = "No-op")] - Nop, -} - -#[derive(Serialize, Deserialize, Clone, Debug)] -#[serde(tag = "built_in")] -pub enum BuiltInChatOperation { - OnlineUsers, - TotalUsers, -} diff --git a/rc_chat_room/src/persist/config/mod.rs b/rc_chat_room/src/persist/config/mod.rs deleted file mode 100644 index 68ccf99..0000000 --- a/rc_chat_room/src/persist/config/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod chat; -pub use chat::{ChatSystemConfig, ChatCommand, ChatOperation, BuiltInChatOperation}; - -pub const CHAT_CONFIG_FILE: &str = "chat.json"; diff --git a/rc_chat_room/src/persist/mod.rs b/rc_chat_room/src/persist/mod.rs deleted file mode 100644 index 9015d83..0000000 --- a/rc_chat_room/src/persist/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod chat_user; -pub mod config; diff --git a/rc_chat_room/src/state/chat/chat.rs b/rc_chat_room/src/state/chat/chat.rs index cf4021e..94bc741 100644 --- a/rc_chat_room/src/state/chat/chat.rs +++ b/rc_chat_room/src/state/chat/chat.rs @@ -6,9 +6,9 @@ pub struct ChatProvider { } impl ChatProvider { - pub fn new(asset_root: impl AsRef, data_root: impl AsRef) -> std::io::Result { + pub fn new(conf: rc_core::persist::config::ChatSystemConfig) -> std::io::Result { Ok(Self { - chat_system: std::sync::Arc::new(std::sync::RwLock::new(crate::state::chat::ChatSystem::new(asset_root, data_root)?)), + chat_system: std::sync::Arc::new(std::sync::RwLock::new(crate::state::chat::ChatSystem::new(conf)?)), }) } @@ -161,12 +161,11 @@ impl ChatSystem { handle.send_private_message(response); } - pub fn new(asset_root: impl AsRef, data_root: impl AsRef) -> std::io::Result { - let config_persist = crate::persist::config::ChatSystemConfig::load(&asset_root)?; + pub fn new(config: rc_core::persist::config::ChatSystemConfig) -> std::io::Result { Ok(Self { chats: HashMap::new(), online_users: HashMap::new(), - config: super::ChatSystemConfig::from_persist(config_persist, asset_root.as_ref().to_path_buf(), data_root.as_ref().to_path_buf())?, + config: super::ChatSystemConfig::from_persist(config)?, }) } diff --git a/rc_chat_room/src/state/chat/config.rs b/rc_chat_room/src/state/chat/config.rs index 97bb594..e11e07b 100644 --- a/rc_chat_room/src/state/chat/config.rs +++ b/rc_chat_room/src/state/chat/config.rs @@ -1,21 +1,17 @@ pub struct ChatSystemConfig { command_channel: String, commands: Vec, - asset_root: std::path::PathBuf, - data_root: std::path::PathBuf, } #[allow(dead_code)] #[derive(Clone, Copy)] -struct CommandContext<'a, 'b, 'c> { +struct CommandContext<'a, 'b> { chat_system: &'a super::ChatSystem, user: &'b dyn rc_core::persist::user::User<()>, - asset_root: &'c std::path::PathBuf, - data_root: &'c std::path::PathBuf, } impl ChatSystemConfig { - pub fn from_persist(config: crate::persist::config::ChatSystemConfig, asset_root: std::path::PathBuf, data_root: std::path::PathBuf) -> std::io::Result { + pub fn from_persist(config: rc_core::persist::config::ChatSystemConfig) -> std::io::Result { let mut compiled_commands = Vec::with_capacity(config.commands.len()); for (i, cmd) in config.commands.into_iter().enumerate() { let compiled_command = ChatCommand::compile_command(cmd).map_err(|e| { @@ -27,8 +23,6 @@ impl ChatSystemConfig { Ok(Self { command_channel: config.command_channel, commands: compiled_commands, - asset_root, - data_root, }) } @@ -36,8 +30,6 @@ impl ChatSystemConfig { let ctx = CommandContext { chat_system, user, - asset_root: &self.asset_root, - data_root: &self.data_root, }; for cmd in self.commands.iter() { if let Some(result) = cmd.perform_if_match(text, ctx) { @@ -66,7 +58,7 @@ pub struct ChatCommand { } impl ChatCommand { - fn compile_command(command: crate::persist::config::ChatCommand) -> Result { + fn compile_command(command: rc_core::persist::ChatCommand) -> Result { Ok(Self { regex: regex::RegexBuilder::new(&command.regex).build()?, op: ChatOperation::from_persist(command.op) @@ -89,11 +81,11 @@ enum ChatOperation { } impl ChatOperation { - fn from_persist(op: crate::persist::config::ChatOperation) -> Self { + fn from_persist(op: rc_core::persist::ChatOperation) -> Self { match op { - crate::persist::config::ChatOperation::BuiltIn(b_in) => Self::BuiltIn(BuiltIn::from_persist(b_in)), - crate::persist::config::ChatOperation::Custom => Self::Custom, - crate::persist::config::ChatOperation::Nop => Self::Nop, + rc_core::persist::ChatOperation::BuiltIn(b_in) => Self::BuiltIn(BuiltIn::from_persist(b_in)), + rc_core::persist::ChatOperation::Custom => Self::Custom, + rc_core::persist::ChatOperation::Nop => Self::Nop, } } @@ -112,10 +104,10 @@ enum BuiltIn { } impl BuiltIn { - fn from_persist(b_in: crate::persist::config::BuiltInChatOperation) -> Self { + fn from_persist(b_in: rc_core::persist::BuiltInChatOperation) -> Self { match b_in { - crate::persist::config::BuiltInChatOperation::OnlineUsers => Self::OnlineUsers, - crate::persist::config::BuiltInChatOperation::TotalUsers => Self::TotalUsers, + rc_core::persist::BuiltInChatOperation::OnlineUsers => Self::OnlineUsers, + rc_core::persist::BuiltInChatOperation::TotalUsers => Self::TotalUsers, } } @@ -130,14 +122,7 @@ impl BuiltIn { } }, Self::TotalUsers => { - let user_path = ctx.data_root.join(rc_core::persist::user::USERS_DIR); - let user_count = user_path.read_dir().map_or(0, |dir| dir.count()).clamp(1, usize::MAX) - 1; - if user_count == 1 { - "1 user exists".to_owned() - } else { - format!("{} users exist", user_count) - } - + format!("User count is not supported") }, } } diff --git a/rc_chat_room/src/data/channel.rs b/rc_core/src/data/channel.rs similarity index 96% rename from rc_chat_room/src/data/channel.rs rename to rc_core/src/data/channel.rs index 5fc556a..30f76a0 100644 --- a/rc_chat_room/src/data/channel.rs +++ b/rc_core/src/data/channel.rs @@ -69,7 +69,7 @@ impl ChatChannelType { 6 => Ok(Self::Clan), 7 => Ok(Self::Private), 8 => Ok(Self::CustomGame), - _ => Err(rc_core::data::error_codes::ChatErrorCodes::UnexpectedError as i16) + _ => Err(crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16) } } } diff --git a/rc_core/src/data/mod.rs b/rc_core/src/data/mod.rs index 1af29fe..2d1092c 100644 --- a/rc_core/src/data/mod.rs +++ b/rc_core/src/data/mod.rs @@ -11,6 +11,7 @@ pub mod voting; pub mod weapon_list; pub mod weapon_upgrade; pub mod crf; +pub mod channel; pub mod error_codes; diff --git a/rc_core/src/persist/chat.rs b/rc_core/src/persist/chat.rs index bb6c33c..0226bb7 100644 --- a/rc_core/src/persist/chat.rs +++ b/rc_core/src/persist/chat.rs @@ -4,8 +4,34 @@ use serde::{Serialize, Deserialize}; pub struct ChatConfig { #[serde(default = "default_pub_channs")] pub public_channels: Vec, + #[serde(default = "default_command_chann")] + pub command_channel: String, + pub commands: Vec, } +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ChatCommand { + pub regex: String, + pub op: ChatOperation, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(tag = "type")] +pub enum ChatOperation { + BuiltIn(BuiltInChatOperation), + Custom, + #[serde(alias = "No-op")] + Nop, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(tag = "built_in")] +pub enum BuiltInChatOperation { + OnlineUsers, + TotalUsers, +} + + fn default_pub_channs() -> Vec { vec![ "main".to_owned(), @@ -13,3 +39,8 @@ fn default_pub_channs() -> Vec { "openjam_worship".to_owned(), ] } + + +fn default_command_chann() -> String { + "sys".to_owned() +} diff --git a/rc_core/src/persist/config/cubes_json.rs b/rc_core/src/persist/config/cubes_json.rs index facba01..faea074 100644 --- a/rc_core/src/persist/config/cubes_json.rs +++ b/rc_core/src/persist/config/cubes_json.rs @@ -281,4 +281,11 @@ impl super::ConfigProvider for CubeConfig { fn cubes(&self) -> &'_ std::collections::HashMap { &self.cubes } + + fn chat_system_config(&self) -> super::ChatSystemConfig { + super::ChatSystemConfig { + command_channel: self.chat.command_channel.clone(), + commands: self.chat.commands.clone(), + } + } } diff --git a/rc_core/src/persist/config/mod.rs b/rc_core/src/persist/config/mod.rs index f3ac795..10dbe41 100644 --- a/rc_core/src/persist/config/mod.rs +++ b/rc_core/src/persist/config/mod.rs @@ -2,7 +2,7 @@ mod cubes_json; pub use cubes_json::CubeConfig; mod traits; -pub use traits::{ConfigProvider, CompleteCampaignProvider, DevMessageProvider, ServerConfig, GarageUpgrades, GarageUpgradeIncrement}; +pub use traits::{ConfigProvider, CompleteCampaignProvider, DevMessageProvider, ServerConfig, GarageUpgrades, GarageUpgradeIncrement, ChatSystemConfig}; pub type ConfigImpl = CubeConfig; diff --git a/rc_core/src/persist/config/traits.rs b/rc_core/src/persist/config/traits.rs index 4dd0a15..766a0f7 100644 --- a/rc_core/src/persist/config/traits.rs +++ b/rc_core/src/persist/config/traits.rs @@ -23,6 +23,7 @@ pub trait ConfigProvider { fn garage_upgrades(&self) -> GarageUpgrades; async fn factory(&self) -> Result>; fn cubes(&self) -> &'_ std::collections::HashMap; + fn chat_system_config(&self) -> ChatSystemConfig; } pub struct CompleteCampaignProvider { @@ -121,3 +122,9 @@ impl GarageUpgrades { ].into()) } } + +#[derive(Clone, Debug)] +pub struct ChatSystemConfig { + pub command_channel: String, + pub commands: Vec, +} diff --git a/rc_core/src/persist/mod.rs b/rc_core/src/persist/mod.rs index 79143b3..daecb4c 100644 --- a/rc_core/src/persist/mod.rs +++ b/rc_core/src/persist/mod.rs @@ -30,7 +30,7 @@ mod settings; pub use settings::Settings; mod chat; -pub use chat::ChatConfig; +pub use chat::{ChatConfig, ChatCommand, ChatOperation, BuiltInChatOperation}; mod vehicle_factory; pub use vehicle_factory::{FactoryConfig, AdapterSettings, ArcFactorySettings}; diff --git a/rc_core/src/persist/user/account_json.rs b/rc_core/src/persist/user/account_json.rs index 9716312..040d6ae 100644 --- a/rc_core/src/persist/user/account_json.rs +++ b/rc_core/src/persist/user/account_json.rs @@ -527,3 +527,78 @@ impl super::User for UserData { } } } + +#[async_trait::async_trait] +impl super::ChatUser for UserData { + async fn subscribed_channels(&self) -> Result, i16> { + let channels = self.subscribed_channels_strings().await?; + Ok(polariton::operation::Typed::Arr(polariton::operation::Arr { + ty: polariton::serdes::TypePrefix::HashMap, // hashtable + items: channels.iter().map(|name| crate::data::channel::ChatChannelInfo { + channel_name: name.to_owned(), + members: Vec::default(), + channel_ty: crate::data::channel::ChatChannelType::Public, + }.as_transmissible()).collect() + })) + } + + async fn subscribed_channels_strings(&self) -> Result, i16> { + let channels = self.db.user_aux_by_user_id_and_descriptor(self.account.id, rc_database::schema::user_aux::Descriptor::SubscribedChannels).await.map_err(|e| { + log::error!("Failed to retrieve SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e); + crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16 + })?.ok_or_else(|| { + log::error!("Failed to find SubscribedChannels (user_aux) for user_id {}", self.account.id); + crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16 + })?; + let channels = serde_json::from_str::>(&channels.data).map_err(|e| { + log::error!("Failed to parse SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e); + crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16 + })?; + Ok(channels) + } + + async fn add_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Result, i16> { + if let crate::data::channel::ChatChannelType::Public = channel_ty { + let mut sub_channels = self.subscribed_channels_strings().await?; + sub_channels.push(channel.clone()); + let new_data = serde_json::to_string(&sub_channels).map_err(|e| { + log::error!("Failed to convert to JSON SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e); + crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16 + })?; + self.db.update_user_aux_by_user_id_and_descriptor(rc_database::schema::user_aux::ActiveModel { + data: rc_database::sea_orm::ActiveValue::Set(new_data), + ..Default::default() + }, self.account.id, rc_database::schema::user_aux::Descriptor::SubscribedChannels).await.map_err(|e| { + log::error!("Failed to update SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e); + crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16 + })?; + } + + Ok(crate::data::channel::ChatChannelInfo { + channel_name: channel, + members: Vec::default(), + channel_ty, + }.as_transmissible()) + } + + async fn remove_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Result<(), i16> { + if let crate::data::channel::ChatChannelType::Public = channel_ty { + let mut sub_channels = self.subscribed_channels_strings().await?; + if let Some(index) = sub_channels.iter().position(|chann| chann == &channel) { + sub_channels.swap_remove(index); + let new_data = serde_json::to_string(&sub_channels).map_err(|e| { + log::error!("Failed to convert to JSON SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e); + crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16 + })?; + self.db.update_user_aux_by_user_id_and_descriptor(rc_database::schema::user_aux::ActiveModel { + data: rc_database::sea_orm::ActiveValue::Set(new_data), + ..Default::default() + }, self.account.id, rc_database::schema::user_aux::Descriptor::SubscribedChannels).await.map_err(|e| { + log::error!("Failed to update SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e); + crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16 + })?; + } + } + Ok(()) + } +} diff --git a/rc_core/src/persist/user/initial_data.rs b/rc_core/src/persist/user/initial_data.rs index 66d597d..e36fe9b 100644 --- a/rc_core/src/persist/user/initial_data.rs +++ b/rc_core/src/persist/user/initial_data.rs @@ -69,25 +69,26 @@ fn default_user_data(info: &super::RegistrationInfo) -> rc_database::schema::use } fn default_user_aux_data(user_id: u32) -> Vec { + let current_time = current_unix_time(); vec![ rc_database::schema::user_aux::ActiveModel { id: Default::default(), user_id: rc_database::sea_orm::ActiveValue::Set(user_id), - creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_time), descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::UserXP), data: rc_database::sea_orm::ActiveValue::Set("0".to_owned()), }, rc_database::schema::user_aux::ActiveModel { id: Default::default(), user_id: rc_database::sea_orm::ActiveValue::Set(user_id), - creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_time), descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::PremiumExpiry), - data: rc_database::sea_orm::ActiveValue::Set(current_unix_time().to_string()), + data: rc_database::sea_orm::ActiveValue::Set(current_time.to_string()), }, rc_database::schema::user_aux::ActiveModel { id: Default::default(), user_id: rc_database::sea_orm::ActiveValue::Set(user_id), - creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_time), descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::UnlockedParts), data: rc_database::sea_orm::ActiveValue::Set( r#"{ @@ -98,37 +99,44 @@ r#"{ rc_database::schema::user_aux::ActiveModel { id: Default::default(), user_id: rc_database::sea_orm::ActiveValue::Set(user_id), - creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_time), descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::TechPoints), data: rc_database::sea_orm::ActiveValue::Set("1337".to_owned()), }, rc_database::schema::user_aux::ActiveModel { id: Default::default(), user_id: rc_database::sea_orm::ActiveValue::Set(user_id), - creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_time), descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::UserRank), data: rc_database::sea_orm::ActiveValue::Set("1".to_owned()), }, rc_database::schema::user_aux::ActiveModel { id: Default::default(), user_id: rc_database::sea_orm::ActiveValue::Set(user_id), - creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_time), descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::UserFreeCurrency), data: rc_database::sea_orm::ActiveValue::Set("10000".to_owned()), }, rc_database::schema::user_aux::ActiveModel { id: Default::default(), user_id: rc_database::sea_orm::ActiveValue::Set(user_id), - creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_time), descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::UserPaidCurrency), data: rc_database::sea_orm::ActiveValue::Set("1000".to_owned()), }, rc_database::schema::user_aux::ActiveModel { id: Default::default(), user_id: rc_database::sea_orm::ActiveValue::Set(user_id), - creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_time), descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::GarageSlotOrder), data: rc_database::sea_orm::ActiveValue::Set("[0]".to_owned()), + }, + rc_database::schema::user_aux::ActiveModel { + id: Default::default(), + user_id: rc_database::sea_orm::ActiveValue::Set(user_id), + creation_time: rc_database::sea_orm::ActiveValue::Set(current_time), + descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::SubscribedChannels), + data: rc_database::sea_orm::ActiveValue::Set("[\"sys\"]".to_owned()), } ] } diff --git a/rc_core/src/persist/user/mod.rs b/rc_core/src/persist/user/mod.rs index c3449e0..cbdc21e 100644 --- a/rc_core/src/persist/user/mod.rs +++ b/rc_core/src/persist/user/mod.rs @@ -11,7 +11,7 @@ mod inventory; pub use inventory::UnlockedParts; mod traits; -pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData}; +pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser}; pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key"; diff --git a/rc_core/src/persist/user/traits.rs b/rc_core/src/persist/user/traits.rs index 89df593..517d579 100644 --- a/rc_core/src/persist/user/traits.rs +++ b/rc_core/src/persist/user/traits.rs @@ -54,7 +54,7 @@ pub trait UserAuthenticator { } #[async_trait::async_trait] -pub trait User { +pub trait User: ChatUser { fn ext(&self, ty: std::any::TypeId) -> Option<&'_ (dyn std::any::Any + Send + Sync + 'static)>; fn token(&self) -> &'_ super::UserToken; fn is_mod(&self) -> bool; @@ -121,3 +121,14 @@ pub struct VehicleUploadData { pub description: String, pub thumbnail: Vec, } + +use polariton::operation::Typed; + +#[async_trait::async_trait] +pub trait ChatUser { + async fn subscribed_channels(&self) -> Result, i16>; + async fn subscribed_channels_strings(&self) -> Result, i16>; + async fn add_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Result, i16>; + async fn remove_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Result<(), i16>; +} + diff --git a/rc_database/src/schema/user_aux.rs b/rc_database/src/schema/user_aux.rs index f4a591c..e8b7daa 100644 --- a/rc_database/src/schema/user_aux.rs +++ b/rc_database/src/schema/user_aux.rs @@ -39,6 +39,7 @@ pub enum Descriptor { UserRank, // u32 UserFreeCurrency, // u64 UserPaidCurrency, // u64 - GarageSlotOrder, // Vec, + GarageSlotOrder, // Vec, CSV LastSeen, // u64, seconds since Unix epoch + SubscribedChannels, // Vec, JSON } diff --git a/utils/cube_gen.py b/utils/cube_gen.py index b159c51..400d26b 100755 --- a/utils/cube_gen.py +++ b/utils/cube_gen.py @@ -454,6 +454,22 @@ def main(asset_in, cubes=None, weapons=None, movement=None): "sys", "jam_club", ], + "commands": [ + { + "regex": "\\?online", + "op": { + "type": "BuiltIn", + "built_in": "OnlineUsers" + } + }, + { + "regex": "\\?users", + "op": { + "type": "BuiltIn", + "built_in": "TotalUsers" + } + } + ] }, "factory": { "adapter": {