From 624fee6d720593d62eba08dd7f90be00769720c6 Mon Sep 17 00:00:00 2001 From: "NG (Graham)" Date: Wed, 13 Aug 2025 22:17:24 -0400 Subject: [PATCH] Fix auto-selecting chat channel, add some more config options --- assets/robocraft/default.png | Bin 0 -> 39689 bytes rc_chat_room/src/events/chat_message.rs | 56 +++++++++++++++++ rc_chat_room/src/events/mod.rs | 2 + rc_chat_room/src/events/player_update.rs | 44 ++++++++++++++ rc_chat_room/src/events/room_join.rs | 53 ++++++++++++++++ .../src/operations/all_joined_channels.rs | 21 +++++-- rc_chat_room/src/operations/mod.rs | 4 +- rc_chat_room/src/operations/more_auth.rs | 21 +++---- .../src/operations/subscribed_channels.rs | 3 +- rc_chat_room/src/state/chat/chat.rs | 2 +- rc_chat_room/src/state/chat/room.rs | 21 ++++--- rc_chat_room/src/state/chat/user.rs | 11 ++++ rc_core/src/persist/chat.rs | 14 ++++- rc_core/src/persist/config/cubes_json.rs | 10 +++ rc_core/src/persist/config/mod.rs | 2 +- rc_core/src/persist/config/traits.rs | 10 +++ rc_core/src/persist/settings.rs | 23 ++++++- rc_core/src/persist/user/account_json.rs | 15 ++++- rc_services_room/src/operations/mod.rs | 2 +- .../src/operations/platform_config.rs | 57 +++++++++++++----- 20 files changed, 316 insertions(+), 55 deletions(-) create mode 100644 assets/robocraft/default.png create mode 100644 rc_chat_room/src/events/player_update.rs create mode 100644 rc_chat_room/src/events/room_join.rs diff --git a/assets/robocraft/default.png b/assets/robocraft/default.png new file mode 100644 index 0000000000000000000000000000000000000000..5eb849cb27fae8222296ac152cfc866267cdb10c GIT binary patch literal 39689 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 A?*IS* literal 0 HcmV?d00001 diff --git a/rc_chat_room/src/events/chat_message.rs b/rc_chat_room/src/events/chat_message.rs index 44c9d86..42daa58 100644 --- a/rc_chat_room/src/events/chat_message.rs +++ b/rc_chat_room/src/events/chat_message.rs @@ -10,6 +10,8 @@ pub struct PublicMessage { } impl PublicMessage { + pub const CODE: u8 = 1; + pub fn as_event_params(&self) -> polariton::operation::ParameterTable { let mut params = std::collections::HashMap::with_capacity(8); params.insert(5, polariton::operation::Typed::Str(self.sender_name.clone().into())); @@ -24,6 +26,32 @@ impl PublicMessage { } } +impl polariton_server::events::IntoEvent for PublicMessage { + const CHANNEL: u8 = 0; + const ENCRYPT: bool = true; + const RELIABLE: bool = true; + + fn into_event(self) -> polariton::operation::Event { + polariton::operation::Event { + code: Self::CODE, + params: self.as_event_params().into(), + } + } +} + +impl polariton_server::events::IntoEvent for &PublicMessage { + const CHANNEL: u8 = 0; + const ENCRYPT: bool = true; + const RELIABLE: bool = true; + + fn into_event(self) -> polariton::operation::Event { + polariton::operation::Event { + code: PublicMessage::CODE, + params: self.as_event_params().into(), + } + } +} + pub struct PrivateMessage { pub sender_name: String, pub sender_display_name: String, @@ -34,6 +62,8 @@ pub struct PrivateMessage { } impl PrivateMessage { + pub const CODE: u8 = 2; + pub fn as_event_params(&self) -> polariton::operation::ParameterTable { let mut params = std::collections::HashMap::with_capacity(6); params.insert(5, polariton::operation::Typed::Str(self.sender_name.clone().into())); @@ -45,3 +75,29 @@ impl PrivateMessage { params.into() } } + +impl polariton_server::events::IntoEvent for PrivateMessage { + const CHANNEL: u8 = 0; + const ENCRYPT: bool = true; + const RELIABLE: bool = true; + + fn into_event(self) -> polariton::operation::Event { + polariton::operation::Event { + code: Self::CODE, + params: self.as_event_params().into(), + } + } +} + +impl polariton_server::events::IntoEvent for &PrivateMessage { + const CHANNEL: u8 = 0; + const ENCRYPT: bool = true; + const RELIABLE: bool = true; + + fn into_event(self) -> polariton::operation::Event { + polariton::operation::Event { + code: PrivateMessage::CODE, + params: self.as_event_params().into(), + } + } +} diff --git a/rc_chat_room/src/events/mod.rs b/rc_chat_room/src/events/mod.rs index 8fc12b7..1005ca6 100644 --- a/rc_chat_room/src/events/mod.rs +++ b/rc_chat_room/src/events/mod.rs @@ -1 +1,3 @@ pub mod chat_message; +pub mod player_update; +pub mod room_join; diff --git a/rc_chat_room/src/events/player_update.rs b/rc_chat_room/src/events/player_update.rs new file mode 100644 index 0000000..3660cd6 --- /dev/null +++ b/rc_chat_room/src/events/player_update.rs @@ -0,0 +1,44 @@ +pub struct PlayerUpdated { + pub channel_name: String, + pub player_name: String, + pub player_state: oj_rc_core::data::channel::ChatPlayerState, +} + +impl PlayerUpdated { + pub const CODE: u8 = 6; + pub const CHANNEL: u8 = 0; + + pub fn as_event_params(&self) -> polariton::operation::ParameterTable { + let mut params = std::collections::HashMap::with_capacity(3); + params.insert(3, polariton::operation::Typed::Str(self.channel_name.clone().into())); + params.insert(22, polariton::operation::Typed::Str(self.player_name.clone().into())); + params.insert(23, polariton::operation::Typed::Int(self.player_state as _)); + params.into() + } +} + +impl polariton_server::events::IntoEvent for PlayerUpdated { + const CHANNEL: u8 = Self::CHANNEL; + const ENCRYPT: bool = true; + const RELIABLE: bool = true; + + fn into_event(self) -> polariton::operation::Event { + polariton::operation::Event { + code: Self::CODE, + params: self.as_event_params().into(), + } + } +} + +impl polariton_server::events::IntoEvent for &PlayerUpdated { + const CHANNEL: u8 = PlayerUpdated::CHANNEL; + const ENCRYPT: bool = true; + const RELIABLE: bool = true; + + fn into_event(self) -> polariton::operation::Event { + polariton::operation::Event { + code: PlayerUpdated::CODE, + params: self.as_event_params().into(), + } + } +} diff --git a/rc_chat_room/src/events/room_join.rs b/rc_chat_room/src/events/room_join.rs new file mode 100644 index 0000000..0e89821 --- /dev/null +++ b/rc_chat_room/src/events/room_join.rs @@ -0,0 +1,53 @@ +pub struct RoomJoined { + pub channel_name: String, + pub player_name: String, + pub player_state: oj_rc_core::data::channel::ChatPlayerState, + pub use_custom_avatar: bool, + pub custom_avatar: Vec, + pub avatar_id: i32, +} + +impl RoomJoined { + pub const CODE: u8 = 4; + pub const CHANNEL: u8 = 0; + + pub fn as_event_params(&self) -> polariton::operation::ParameterTable { + let mut params = std::collections::HashMap::with_capacity(8); + params.insert(3, polariton::operation::Typed::Str(self.channel_name.clone().into())); + params.insert(22, polariton::operation::Typed::Str(self.player_name.clone().into())); + params.insert(23, polariton::operation::Typed::Int(self.player_state as _)); + params.insert(24, polariton::operation::Typed::Bool(self.use_custom_avatar)); + if self.use_custom_avatar { + params.insert(26, polariton::operation::Typed::Bytes(self.custom_avatar.clone().into())); + } else { + params.insert(25, polariton::operation::Typed::Int(self.avatar_id)); + } + params.into() + } +} + +impl polariton_server::events::IntoEvent for RoomJoined { + const CHANNEL: u8 = Self::CHANNEL; + const ENCRYPT: bool = true; + const RELIABLE: bool = true; + + fn into_event(self) -> polariton::operation::Event { + polariton::operation::Event { + code: Self::CODE, + params: self.as_event_params().into(), + } + } +} + +impl polariton_server::events::IntoEvent for &RoomJoined { + const CHANNEL: u8 = RoomJoined::CHANNEL; + const ENCRYPT: bool = true; + const RELIABLE: bool = true; + + fn into_event(self) -> polariton::operation::Event { + polariton::operation::Event { + code: RoomJoined::CODE, + params: self.as_event_params().into(), + } + } +} diff --git a/rc_chat_room/src/operations/all_joined_channels.rs b/rc_chat_room/src/operations/all_joined_channels.rs index 1a35d0c..434cbf9 100644 --- a/rc_chat_room/src/operations/all_joined_channels.rs +++ b/rc_chat_room/src/operations/all_joined_channels.rs @@ -1,25 +1,32 @@ //use oj_rc_core::persist::user::ChatUser; use polariton::operation::{ParameterTable, OperationResponse}; -const CODE: u8 = 11; +const CODE: u8 = 11; // subscribe all const PARAM_KEY: u8 = 18; -async fn do_handling(params: ParameterTable<()>, user: &crate::UserTy) -> Result { +async fn do_handling(params: ParameterTable<()>, user: &crate::UserTy, chat_system: &crate::state::chat::ChatImpl) -> Result { + log::info!("Adding joined user to channels"); let mut params = params.to_dict(); let user_info = user.user()?; + let name = user_info.public_id().to_owned(); + let channels = user_info.subscribed_channels_strings().await?; + let event_tx = user.event_chann(); + chat_system.system_mut().connect_user(name, channels, event_tx); params.insert(PARAM_KEY, user_info.subscribed_channels().await?); Ok(params.into()) } -pub struct JoinedChannelsProvider; +pub struct JoinedChannelsProvider { + chat_system: crate::state::chat::ChatImpl, +} #[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) + polariton_server::operations::result_to_op_resp::(do_handling(params, user, &self.chat_system).await) } } @@ -30,6 +37,8 @@ impl polariton_server::operations::OperationCode for JoinedChannelsProvider { } -pub(super) fn all_channels_provider() -> JoinedChannelsProvider { - JoinedChannelsProvider +pub(super) fn all_channels_provider(chat_system: crate::state::chat::ChatImpl) -> JoinedChannelsProvider { + JoinedChannelsProvider { + chat_system, + } } diff --git a/rc_chat_room/src/operations/mod.rs b/rc_chat_room/src/operations/mod.rs index 16c371a..b61f223 100644 --- a/rc_chat_room/src/operations/mod.rs +++ b/rc_chat_room/src/operations/mod.rs @@ -15,10 +15,10 @@ use polariton_server::operations::OperationsHandler; pub fn handler(chat_system: crate::state::chat::ChatImpl, conf: &oj_rc_core::persist::config::ConfigImpl) -> OperationsHandler { OperationsHandler::new() .modify(oj_rc_core::polariton::RcOpModifier) - .add(more_auth::MoreLobbyAuth::new(chat_system.clone())) + .add(more_auth::MoreLobbyAuth::new()) .add(chat_ignores::ignores_provider()) .add(pending_sanctions::pending_sanctions_checker()) - .add(all_joined_channels::all_channels_provider()) + .add(all_joined_channels::all_channels_provider(chat_system.clone())) //.add(polariton_server::operations::Ack::<12, _>::default()) .add(send_message::send_public_message_handler(chat_system.clone())) .add(public_channels::public_channels_provider(conf)) diff --git a/rc_chat_room/src/operations/more_auth.rs b/rc_chat_room/src/operations/more_auth.rs index 5f563f3..2077f7f 100644 --- a/rc_chat_room/src/operations/more_auth.rs +++ b/rc_chat_room/src/operations/more_auth.rs @@ -4,17 +4,14 @@ use polariton_server::operations::{Operation, OperationCode}; //use crate::persist::chat_user::{ChatUser, ChatUserImpl}; //use oj_rc_core::persist::user::ChatUser; -pub struct MoreLobbyAuth { - chat_system: crate::state::chat::ChatImpl, -} +pub struct MoreLobbyAuth; impl MoreLobbyAuth { const AUTH_PAYLOAD_KEY: u8 = 245; - pub fn new(chat_system: crate::state::chat::ChatImpl) -> Self { - Self { - chat_system, - } + #[inline] + pub fn new() -> Self { + Self } /*fn build_ext_map(&self, token: &oj_rc_core::persist::user::UserToken) -> Option>> { @@ -33,12 +30,12 @@ impl MoreLobbyAuth { 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(&auth_payload.string).await { - let user_impl = user.user()?; - let name = user_impl.public_id().to_owned(); + //let user_impl = user.user()?; + //let name = user_impl.public_id().to_owned(); //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_chann(); - self.chat_system.system_mut().connect_user(name, channels, event_tx); + //let channels = user_impl.subscribed_channels_strings().await?; + //let event_tx = user.event_chann(); + //self.chat_system.system_mut().connect_user(name, channels, event_tx); let mut resp_params = std::collections::HashMap::new(); resp_params.insert(Self::AUTH_PAYLOAD_KEY, polariton::operation::Typed::Byte(0)); return Ok(resp_params.into()); diff --git a/rc_chat_room/src/operations/subscribed_channels.rs b/rc_chat_room/src/operations/subscribed_channels.rs index 9e18683..12df639 100644 --- a/rc_chat_room/src/operations/subscribed_channels.rs +++ b/rc_chat_room/src/operations/subscribed_channels.rs @@ -1,11 +1,12 @@ //use oj_rc_core::persist::user::ChatUser; use polariton::operation::{ParameterTable, OperationResponse}; -const CODE: u8 = 12; +const CODE: u8 = 12; // get all subscribed const PARAM_KEY: u8 = 18; async fn do_handling(params: ParameterTable<()>, user: &crate::UserTy) -> Result { + log::info!("Getting subscribed user's channels"); let mut params = params.to_dict(); let user_info = user.user()?; params.insert(PARAM_KEY, user_info.subscribed_channels().await?); diff --git a/rc_chat_room/src/state/chat/chat.rs b/rc_chat_room/src/state/chat/chat.rs index 874ab46..79c2b38 100644 --- a/rc_chat_room/src/state/chat/chat.rs +++ b/rc_chat_room/src/state/chat/chat.rs @@ -91,7 +91,7 @@ impl ChatSystem { } else if let Some(room) = self.chats.get(&channel) { let event_params = crate::events::chat_message::PublicMessage { sender_name: user.public_id().to_owned(), - sender_display_name: user.public_id().to_owned(), + sender_display_name: user.display_name().to_owned(), text, is_dev: user.is_dev(), is_mod: user.is_mod(), diff --git a/rc_chat_room/src/state/chat/room.rs b/rc_chat_room/src/state/chat/room.rs index 101dad8..f72ba99 100644 --- a/rc_chat_room/src/state/chat/room.rs +++ b/rc_chat_room/src/state/chat/room.rs @@ -1,5 +1,6 @@ pub struct ChatRoom { name: String, + #[allow(dead_code)] channel: crate::data::channel::ChatChannelType, online_users: Vec, } @@ -35,11 +36,13 @@ impl ChatRoom { } pub fn send_public_message(&self, message: crate::events::chat_message::PublicMessage) { + let user_id = &message.sender_display_name; let event = polariton::operation::Event { code: 1, params: message.as_event_params(), }; for user in self.online_users.iter() { + if user.name() == user_id { continue; } user.send(polariton_server::ToSend::Data { data: polariton::packet::Data::Event(event.clone()), encrypt: true, @@ -60,22 +63,20 @@ impl ChatRoom { pub fn connect_user(&mut self, handle: super::UserHandle) { self.cleanup(); let event = polariton::operation::Event { - code: 1, - params: crate::events::chat_message::PublicMessage { - sender_name: "system".to_owned(), - sender_display_name: "system".to_owned(), + code: crate::events::room_join::RoomJoined::CODE, + params: crate::events::room_join::RoomJoined { channel_name: self.name.clone(), - channel_ty: self.channel, - text: "joined".to_owned(), - is_dev: false, - is_mod: false, - is_admin: false, + player_name: handle.name().to_owned(), + player_state: oj_rc_core::data::channel::ChatPlayerState::Idk0, + use_custom_avatar: false, + custom_avatar: Vec::default(), + avatar_id: 0, }.as_event_params(), }; handle.send(polariton_server::ToSend::Data { data: polariton::packet::Data::Event(event), encrypt: true, - channel: 0, + channel: crate::events::room_join::RoomJoined::CHANNEL, reliable: true, }); self.online_users.push(handle); diff --git a/rc_chat_room/src/state/chat/user.rs b/rc_chat_room/src/state/chat/user.rs index 9524761..b0502b1 100644 --- a/rc_chat_room/src/state/chat/user.rs +++ b/rc_chat_room/src/state/chat/user.rs @@ -24,6 +24,17 @@ impl UserHandle { } } + /*pub fn send_later(&self, to_send: polariton_server::ToSend, wait: std::time::Duration) { + tokio::spawn(Self::send_after(self.event_tx.clone(), to_send, wait)); + } + + async fn send_after(event_tx: tokio::sync::mpsc::WeakUnboundedSender, to_send: polariton_server::ToSend, wait: std::time::Duration) { + tokio::time::sleep(wait).await; + if let Some(event_tx) = event_tx.upgrade() { + event_tx.send(to_send).unwrap_or_default(); + } + }*/ + pub fn send_private_message(&self, message: crate::events::chat_message::PrivateMessage) { let event = polariton::operation::Event { code: 2, diff --git a/rc_core/src/persist/chat.rs b/rc_core/src/persist/chat.rs index 0226bb7..ddd8396 100644 --- a/rc_core/src/persist/chat.rs +++ b/rc_core/src/persist/chat.rs @@ -7,6 +7,10 @@ pub struct ChatConfig { #[serde(default = "default_command_chann")] pub command_channel: String, pub commands: Vec, + #[serde(default = "default_selected_chann")] + pub default_channel: String, + #[serde(default = "default_true")] + pub can_create_channels: bool, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -36,7 +40,7 @@ fn default_pub_channs() -> Vec { vec![ "main".to_owned(), "sys".to_owned(), - "openjam_worship".to_owned(), + "jam_club".to_owned(), ] } @@ -44,3 +48,11 @@ fn default_pub_channs() -> Vec { fn default_command_chann() -> String { "sys".to_owned() } + +fn default_selected_chann() -> String { + "jam_club".to_owned() +} + +fn default_true() -> bool { + true +} diff --git a/rc_core/src/persist/config/cubes_json.rs b/rc_core/src/persist/config/cubes_json.rs index 42025f7..0fb0baf 100644 --- a/rc_core/src/persist/config/cubes_json.rs +++ b/rc_core/src/persist/config/cubes_json.rs @@ -288,6 +288,8 @@ impl super::ConfigProvider for CubeConfig { super::ChatSystemConfig { command_channel: self.chat.command_channel.clone(), commands: self.chat.commands.clone(), + default_channel: self.chat.default_channel.clone(), + can_create_channels: self.chat.can_create_channels, } } @@ -408,4 +410,12 @@ impl super::ConfigProvider for CubeConfig { (map.into_conf(), map_conf) }).collect() } + + fn url_links(&self) -> super::LinksConfig { + super::LinksConfig { + feedback_url: self.settings.server.feedback_url.clone(), + support_url: self.settings.server.support_url.clone(), + wiki_url: self.settings.server.wiki_url.clone(), + } + } } diff --git a/rc_core/src/persist/config/mod.rs b/rc_core/src/persist/config/mod.rs index a04f80f..17dcbd0 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, ChatSystemConfig, GameEventSequence, GameEvents, GameRotationStrategy, GameEvent, GameMap, GameVisibility, GameType, SingleplayerConfig, VehicleInfo, VehicleDescriptor, QueueChangeMode, Point, Sphere, MapConfig}; +pub use traits::{ConfigProvider, CompleteCampaignProvider, DevMessageProvider, ServerConfig, GarageUpgrades, GarageUpgradeIncrement, ChatSystemConfig, GameEventSequence, GameEvents, GameRotationStrategy, GameEvent, GameMap, GameVisibility, GameType, SingleplayerConfig, VehicleInfo, VehicleDescriptor, QueueChangeMode, Point, Sphere, MapConfig, LinksConfig}; pub type ConfigImpl = CubeConfig; diff --git a/rc_core/src/persist/config/traits.rs b/rc_core/src/persist/config/traits.rs index 11f64e0..44bc0ac 100644 --- a/rc_core/src/persist/config/traits.rs +++ b/rc_core/src/persist/config/traits.rs @@ -32,6 +32,7 @@ pub trait ConfigProvider { // FIXME don't use serializable types in traits fn network_config(&self) -> crate::persist::NetworkConf; fn maps(&self) -> std::collections::HashMap; + fn url_links(&self) -> LinksConfig; } pub struct CompleteCampaignProvider { @@ -153,6 +154,8 @@ impl GarageUpgrades { pub struct ChatSystemConfig { pub command_channel: String, pub commands: Vec, + pub default_channel: String, + pub can_create_channels: bool, } #[derive(Clone, Debug)] @@ -355,3 +358,10 @@ pub struct MapConfig { pub spawns: std::collections::HashMap>, // team -> points pub bases: std::collections::HashMap, // team -> base } + +#[derive(Clone, Debug)] +pub struct LinksConfig { + pub feedback_url: String, + pub support_url: String, + pub wiki_url: String, +} diff --git a/rc_core/src/persist/settings.rs b/rc_core/src/persist/settings.rs index dabe163..9f0595c 100644 --- a/rc_core/src/persist/settings.rs +++ b/rc_core/src/persist/settings.rs @@ -22,7 +22,7 @@ fn default_gameplay_settings() -> super::GameplaySettings { shield_hps: 2_000, request_review_level: 10_000, critical_ratio: 5.0, - cross_promo_image: "https://git.ngram.ca/OpenJam/servers/raw/branch/main/assets/robocraft/favicon.jpg".to_owned(), + cross_promo_image: "https://git.ngram.ca/OpenJam/servers/raw/branch/main/assets/robocraft/default.png".to_owned(), cross_promo_link: "https://git.ngram.ca/OpenJam/servers".to_owned(), } } @@ -78,6 +78,12 @@ pub struct ServerSettings { pub queue_mode: QueueMode, #[serde(default = "default_cdn_root_url")] pub cdn_url: String, + #[serde(default = "default_feedback_url")] + pub feedback_url: String, + #[serde(default = "default_support_url")] + pub support_url: String, + #[serde(default = "default_wiki_url")] + pub wiki_url: String, } #[derive(Serialize, Deserialize, Clone, Debug, Default)] @@ -98,9 +104,24 @@ fn default_server_conf() -> ServerSettings { auto_signup: false, queue_mode: QueueMode::Notify, cdn_url: default_cdn_root_url(), + feedback_url: default_feedback_url(), + support_url: default_support_url(), + wiki_url: default_wiki_url(), } } fn default_cdn_root_url() -> String { "http://127.0.0.1:8010".to_owned() } + +fn default_feedback_url() -> String { + "https://mstdn.ca/@ngram".to_owned() +} + +fn default_support_url() -> String { + "https://rvlt.gg/jtVE0pD5".to_owned() +} + +fn default_wiki_url() -> String { + "https://git.ngram.ca/OpenJam/servers/wiki".to_owned() +} diff --git a/rc_core/src/persist/user/account_json.rs b/rc_core/src/persist/user/account_json.rs index edbe0aa..a476d30 100644 --- a/rc_core/src/persist/user/account_json.rs +++ b/rc_core/src/persist/user/account_json.rs @@ -1142,11 +1142,20 @@ impl super::GameEventSetter for GameEventSetterImpl { impl super::ChatUser for UserData { async fn subscribed_channels(&self) -> Result, i16> { let channels = self.subscribed_channels_strings().await?; + log::info!("User is subscribed to channels {:?}", channels); 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(), + items: channels.into_iter().map(|name| crate::data::channel::ChatChannelInfo { + channel_name: name, + members: vec![ + crate::data::channel::ChatChannelMember { + name: self.account.display_name.clone(), + use_custom_avatar: false, + state: crate::data::channel::ChatPlayerState::Idk0, + custom_avatar: Vec::default(), + avatar_id: 0, + }, + ], channel_ty: crate::data::channel::ChatChannelType::Public, }.as_transmissible()).collect() })) diff --git a/rc_services_room/src/operations/mod.rs b/rc_services_room/src/operations/mod.rs index a64c280..c67981d 100644 --- a/rc_services_room/src/operations/mod.rs +++ b/rc_services_room/src/operations/mod.rs @@ -113,7 +113,7 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler .add(polariton_server::operations::Ack::<132, _>::default()) // verify user level .add(load_analytics::NoAnalytics) .add(polariton_server::operations::Ack::<131, _>::default()) // analytics updated notification - .add(platform_config::platform_config_provider()) + .add(platform_config::platform_config_provider(&init_ctx.cubes)) .add(tier_banding::tiers_banding_provider()) .add(cube_list::cube_list_provider(&init_ctx.cubes)) .add(special_items::special_item_list_provider()) diff --git a/rc_services_room/src/operations/platform_config.rs b/rc_services_room/src/operations/platform_config.rs index a0b6763..851b03e 100644 --- a/rc_services_room/src/operations/platform_config.rs +++ b/rc_services_room/src/operations/platform_config.rs @@ -1,29 +1,54 @@ -use polariton_server::operations::SimpleFunc; +use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl}; use polariton::{operation::{Dict, ParameterTable, Typed}, serdes::TypePrefix}; +const CODE: u8 = 165; + const PLATFORM_CONFIG_KEY: u8 = 197; -pub(super) fn platform_config_provider() -> SimpleFunc<165, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result) + Sync + Sync> { - SimpleFunc::new(|params, _| { +pub(super) fn platform_config_provider(conf: &oj_rc_core::ConfigImpl) -> SimpleOpImpl { + SimpleOpImpl::new(PlatformConfigProvider { + chat_config: >::chat_system_config(conf), + links: >::url_links(conf), + }) +} + +pub(super) struct PlatformConfigProvider { + chat_config: oj_rc_core::persist::config::ChatSystemConfig, + links: oj_rc_core::persist::config::LinksConfig, +} + +#[async_trait::async_trait] +impl SimpleOperation for PlatformConfigProvider { + type User = crate::UserTy; + const CODE: u8 = CODE; + + async fn handle(&self, params: ParameterTable, user: &Self::User) -> Result, SimpleOpError> { + let user = user.user()?; + let mut connected_channels = user.subscribed_channels_strings().await?; + let client_selected_channel = if connected_channels.is_empty() || connected_channels.contains(&self.chat_config.default_channel) { + self.chat_config.default_channel.clone() + } else { + connected_channels.remove(0) + }; let mut params = params.to_dict(); params.insert(PLATFORM_CONFIG_KEY, Typed::Dict(Dict { key_ty: TypePrefix::Any, // obj val_ty: TypePrefix::Any, // obj items: vec![ - (Typed::Str("BuyPremiumAvailable".into()), Typed::Bool(false.into())), - (Typed::Str("MainShopButtonAvailable".into()), Typed::Bool(false.into())), - (Typed::Str("RoboPassButtonAvailable".into()), Typed::Bool(false.into())), - (Typed::Str("LanguageSelectionAvailable".into()), Typed::Bool(false.into())), - (Typed::Str("AutoJoinPublicChatRoom".into()), Typed::Bool(true.into())), // TODO maybe? - (Typed::Str("CanCreateChatRooms".into()), Typed::Bool(true.into())), // TODO - (Typed::Str("CurseVoiceEnabled".into()), Typed::Bool(false.into())), - (Typed::Str("DeltaDNAEnabled".into()), Typed::Bool(false.into())), - (Typed::Str("UseDecimalSystem".into()), Typed::Bool(true.into())), - (Typed::Str("FeedbackURL".into()), Typed::Str("https://mstdn.ca/@ngram".into())), - (Typed::Str("SupportURL".into()), Typed::Str("https://git.ngni.us/OpenJam/servers".into())), - (Typed::Str("WikiURL".into()), Typed::Str("https://git.ngram.ca/OpenJam/servers/wiki".into())), + (Typed::Str("BuyPremiumAvailable".into()), Typed::Bool(false)), + (Typed::Str("MainShopButtonAvailable".into()), Typed::Bool(false)), + (Typed::Str("RoboPassButtonAvailable".into()), Typed::Bool(false)), + (Typed::Str("LanguageSelectionAvailable".into()), Typed::Bool(false)), + (Typed::Str("AutoJoinPublicChatRoom".into()), Typed::Str(client_selected_channel.into())), + (Typed::Str("CanCreateChatRooms".into()), Typed::Bool(self.chat_config.can_create_channels)), + (Typed::Str("CurseVoiceEnabled".into()), Typed::Bool(false)), + (Typed::Str("DeltaDNAEnabled".into()), Typed::Bool(false)), + (Typed::Str("UseDecimalSystem".into()), Typed::Bool(true)), + (Typed::Str("FeedbackURL".into()), Typed::Str(self.links.feedback_url.clone().into())), + (Typed::Str("SupportURL".into()), Typed::Str(self.links.support_url.clone().into())), + (Typed::Str("WikiURL".into()), Typed::Str(self.links.wiki_url.clone().into())), ].into(), })); Ok(params.into()) - }) + } }