mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add avatar saving functionality (without CDN support)
This commit is contained in:
17
Cargo.lock
generated
17
Cargo.lock
generated
@@ -62,7 +62,6 @@ dependencies = [
|
|||||||
"flate2",
|
"flate2",
|
||||||
"foldhash",
|
"foldhash",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"h2",
|
|
||||||
"http 0.2.12",
|
"http 0.2.12",
|
||||||
"httparse",
|
"httparse",
|
||||||
"httpdate",
|
"httpdate",
|
||||||
@@ -100,7 +99,6 @@ dependencies = [
|
|||||||
"bytestring",
|
"bytestring",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"http 0.2.12",
|
"http 0.2.12",
|
||||||
"regex",
|
|
||||||
"regex-lite",
|
"regex-lite",
|
||||||
"serde",
|
"serde",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -171,7 +169,6 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"bytestring",
|
"bytestring",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cookie 0.16.2",
|
|
||||||
"derive_more 2.0.1",
|
"derive_more 2.0.1",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"foldhash",
|
"foldhash",
|
||||||
@@ -184,7 +181,6 @@ dependencies = [
|
|||||||
"mime",
|
"mime",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"regex",
|
|
||||||
"regex-lite",
|
"regex-lite",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -841,17 +837,6 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cookie"
|
|
||||||
version = "0.16.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
|
|
||||||
dependencies = [
|
|
||||||
"percent-encoding",
|
|
||||||
"time",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cookie"
|
name = "cookie"
|
||||||
version = "0.18.1"
|
version = "0.18.1"
|
||||||
@@ -3471,7 +3456,7 @@ version = "0.5.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e274915a20ee3065f611c044bd63c40757396b6dbc057d6046aec27f14f882b9"
|
checksum = "e274915a20ee3065f611c044bd63c40757396b6dbc057d6046aec27f14f882b9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cookie 0.18.1",
|
"cookie",
|
||||||
"either",
|
"either",
|
||||||
"futures",
|
"futures",
|
||||||
"http 0.2.12",
|
"http 0.2.12",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ members = [
|
|||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
rocket = { version = "0.5.1", features = [ "json" ] }
|
rocket = { version = "0.5.1", features = [ "json" ] }
|
||||||
actix-web = { version = "4" }
|
actix-web = { version = "4", default-features = false, features = [ "macros", "compress-brotli", "compress-gzip", "compress-zstd"] }
|
||||||
actix-files = "0.6"
|
actix-files = "0.6"
|
||||||
libfj = { version = "0.7.5", path = "../libfj" }
|
libfj = { version = "0.7.5", path = "../libfj" }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|||||||
@@ -526,6 +526,40 @@ impl <C: Clone> super::User<C> for UserData {
|
|||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_avatar_info(&self) -> Result<super::GetAvatarInfo<C>, i16> {
|
||||||
|
let avatar_id_aux = self.db.user_aux_by_user_id_and_descriptor(self.account.id, rc_database::schema::user_aux::Descriptor::AvatarId).await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to retrieve AvatarId (user_aux) for user_id {}: {}", self.account.id, e);
|
||||||
|
DATABASE_ERR
|
||||||
|
})?
|
||||||
|
.ok_or_else(|| {
|
||||||
|
log::error!("Failed to find AvatarId (user_aux) for user_id {}", self.account.id);
|
||||||
|
DATABASE_ERR
|
||||||
|
})?;
|
||||||
|
let avatar_id: u32 = avatar_id_aux.data.parse()
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to parse AvatarId (user_aux) for user_id {}: {}", self.account.id, e);
|
||||||
|
crate::data::error_codes::WebServicesError::UnexpectedError as i16
|
||||||
|
})?;
|
||||||
|
Ok(super::GetAvatarInfo {
|
||||||
|
avatar_id: polariton::operation::Typed::Int(if avatar_id == u32::MAX { 0 } else { avatar_id as i32 }),
|
||||||
|
use_custom: polariton::operation::Typed::Bool(avatar_id == u32::MAX),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_avatar_info(&self, info: super::AvatarInfo) -> Result<(), i16> {
|
||||||
|
let to_update = rc_database::schema::user_aux::ActiveModel {
|
||||||
|
data: rc_database::sea_orm::ActiveValue::Set(if info.use_custom { u32::MAX } else { info.avatar_id as u32 }.to_string()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
self.db.update_user_aux_by_user_id_and_descriptor(to_update, self.account.id, rc_database::schema::user_aux::Descriptor::AvatarId).await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to update AvatarId (user_aux) for user_id {}: {}", self.account.id, e);
|
||||||
|
DATABASE_ERR
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
|||||||
@@ -137,6 +137,13 @@ r#"{
|
|||||||
creation_time: rc_database::sea_orm::ActiveValue::Set(current_time),
|
creation_time: rc_database::sea_orm::ActiveValue::Set(current_time),
|
||||||
descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::SubscribedChannels),
|
descriptor: rc_database::sea_orm::ActiveValue::Set(rc_database::schema::user_aux::Descriptor::SubscribedChannels),
|
||||||
data: rc_database::sea_orm::ActiveValue::Set("[\"sys\"]".to_owned()),
|
data: rc_database::sea_orm::ActiveValue::Set("[\"sys\"]".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::AvatarId),
|
||||||
|
data: rc_database::sea_orm::ActiveValue::Set((current_time % 16).to_string()),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ mod inventory;
|
|||||||
pub use inventory::UnlockedParts;
|
pub use inventory::UnlockedParts;
|
||||||
|
|
||||||
mod traits;
|
mod traits;
|
||||||
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser};
|
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser, AvatarInfo, GetAvatarInfo};
|
||||||
|
|
||||||
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
|
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ pub trait User<C>: ChatUser {
|
|||||||
async fn singleplayer_robots(&self) -> Result<polariton::operation::Typed<C>, i16>;
|
async fn singleplayer_robots(&self) -> Result<polariton::operation::Typed<C>, i16>;
|
||||||
async fn prepare_factory_upload(&self, vehicle: VehicleUploadData) -> Result<rc_factory::VehicleUploadInfo, i16>;
|
async fn prepare_factory_upload(&self, vehicle: VehicleUploadData) -> Result<rc_factory::VehicleUploadInfo, i16>;
|
||||||
async fn last_seen(&self) -> Result<u64, i16>;
|
async fn last_seen(&self) -> Result<u64, i16>;
|
||||||
|
async fn get_avatar_info(&self) -> Result<GetAvatarInfo<C>, i16>;
|
||||||
|
async fn set_avatar_info(&self, info: AvatarInfo) -> Result<(), i16>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UserSlots<C> {
|
pub struct UserSlots<C> {
|
||||||
@@ -122,13 +124,21 @@ pub struct VehicleUploadData {
|
|||||||
pub thumbnail: Vec<u8>,
|
pub thumbnail: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
use polariton::operation::Typed;
|
pub struct GetAvatarInfo<C> {
|
||||||
|
pub avatar_id: polariton::operation::Typed<C>,
|
||||||
|
pub use_custom: polariton::operation::Typed<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AvatarInfo {
|
||||||
|
pub avatar_id: i32,
|
||||||
|
pub use_custom: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait ChatUser {
|
pub trait ChatUser {
|
||||||
async fn subscribed_channels(&self) -> Result<Typed<()>, i16>;
|
async fn subscribed_channels(&self) -> Result<polariton::operation::Typed<()>, i16>;
|
||||||
async fn subscribed_channels_strings(&self) -> Result<Vec<String>, i16>;
|
async fn subscribed_channels_strings(&self) -> Result<Vec<String>, i16>;
|
||||||
async fn add_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Result<Typed<()>, i16>;
|
async fn add_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Result<polariton::operation::Typed<()>, i16>;
|
||||||
async fn remove_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>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,4 +42,5 @@ pub enum Descriptor {
|
|||||||
GarageSlotOrder, // Vec<u32>, CSV
|
GarageSlotOrder, // Vec<u32>, CSV
|
||||||
LastSeen, // u64, seconds since Unix epoch
|
LastSeen, // u64, seconds since Unix epoch
|
||||||
SubscribedChannels, // Vec<String>, JSON
|
SubscribedChannels, // Vec<String>, JSON
|
||||||
|
AvatarId, // u32, u32::MAX means custom avatar
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,36 @@
|
|||||||
use polariton_server::operations::SimpleFunc;
|
use polariton::operation::{ParameterTable, OperationResponse};
|
||||||
use polariton::operation::{ParameterTable, Typed};
|
|
||||||
|
|
||||||
const IS_CUSTOM_PARAM_KEY: u8 = 130;
|
const CODE: u8 = 110;
|
||||||
const AVATAR_ID_PARAM_KEY: u8 = 129;
|
|
||||||
|
|
||||||
pub(super) fn get_avatar_provider() -> SimpleFunc<110, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
|
const IS_CUSTOM_PARAM_KEY: u8 = 130; // bool
|
||||||
SimpleFunc::new(|params, _| {
|
const AVATAR_ID_PARAM_KEY: u8 = 129; // int
|
||||||
let mut params = params.to_dict();
|
|
||||||
params.insert(IS_CUSTOM_PARAM_KEY, Typed::Bool(false.into()));
|
pub(super) fn avatar_get_provider() -> AvatarGetProvider {
|
||||||
params.insert(AVATAR_ID_PARAM_KEY, Typed::Int(1));
|
AvatarGetProvider
|
||||||
Ok(params.into())
|
}
|
||||||
})
|
|
||||||
|
async fn do_save(params: ParameterTable<()>, user: &crate::UserTy) -> Result<ParameterTable, i16> {
|
||||||
|
let mut params = params.to_dict();
|
||||||
|
let user_info = user.user()?;
|
||||||
|
let info = user_info.get_avatar_info().await?;
|
||||||
|
params.insert(IS_CUSTOM_PARAM_KEY, info.use_custom);
|
||||||
|
params.insert(AVATAR_ID_PARAM_KEY, info.avatar_id);
|
||||||
|
Ok(params.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct AvatarGetProvider;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl polariton_server::operations::Operation<()> for AvatarGetProvider {
|
||||||
|
type User = crate::UserTy;
|
||||||
|
|
||||||
|
async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> {
|
||||||
|
polariton_server::operations::result_to_op_resp::<CODE, ()>(do_save(params, user).await)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl polariton_server::operations::OperationCode for AvatarGetProvider {
|
||||||
|
fn op_code() -> u8 {
|
||||||
|
CODE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
42
rc_services_room/src/operations/avatar_set.rs
Normal file
42
rc_services_room/src/operations/avatar_set.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
use polariton::operation::{ParameterTable, Typed, OperationResponse};
|
||||||
|
|
||||||
|
const CODE: u8 = 111;
|
||||||
|
|
||||||
|
const IS_CUSTOM_PARAM_KEY: u8 = 130; // bool; in
|
||||||
|
const AVATAR_ID_PARAM_KEY: u8 = 129; // int; in
|
||||||
|
|
||||||
|
pub(super) fn avatar_set_provider() -> AvatarSetProvider {
|
||||||
|
AvatarSetProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_save(params: ParameterTable<()>, user: &crate::UserTy) -> Result<ParameterTable, i16> {
|
||||||
|
let mut params = params.to_dict();
|
||||||
|
let user_info = user.user()?;
|
||||||
|
if let Some(Typed::Int(avatar)) = params.remove(&AVATAR_ID_PARAM_KEY) {
|
||||||
|
if let Some(Typed::Bool(is_custom)) = params.remove(&IS_CUSTOM_PARAM_KEY) {
|
||||||
|
let info = rc_core::persist::user::AvatarInfo {
|
||||||
|
avatar_id: avatar,
|
||||||
|
use_custom: is_custom,
|
||||||
|
};
|
||||||
|
user_info.set_avatar_info(info).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(params.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct AvatarSetProvider;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl polariton_server::operations::Operation<()> for AvatarSetProvider {
|
||||||
|
type User = crate::UserTy;
|
||||||
|
|
||||||
|
async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> {
|
||||||
|
polariton_server::operations::result_to_op_resp::<CODE, ()>(do_save(params, user).await)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl polariton_server::operations::OperationCode for AvatarSetProvider {
|
||||||
|
fn op_code() -> u8 {
|
||||||
|
CODE
|
||||||
|
}
|
||||||
|
}
|
||||||
44
rc_services_room/src/operations/avatar_set_custom.rs
Normal file
44
rc_services_room/src/operations/avatar_set_custom.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
use polariton::operation::{ParameterTable, Typed, OperationResponse};
|
||||||
|
|
||||||
|
const CODE: u8 = 112;
|
||||||
|
|
||||||
|
const IMG_PARAM_KEY: u8 = 131; // int; in
|
||||||
|
const FORMAT_PARAM_KEY: u8 = 132; // int enum; in
|
||||||
|
|
||||||
|
pub(super) fn custom_avatar_upload_handler() -> CustomAvatarHandler {
|
||||||
|
CustomAvatarHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_save(params: ParameterTable<()>, _user: &crate::UserTy) -> Result<ParameterTable, i16> {
|
||||||
|
let mut params = params.to_dict();
|
||||||
|
//let user_info = user.user()?;
|
||||||
|
if let Some(Typed::Bytes(image)) = params.remove(&IMG_PARAM_KEY) {
|
||||||
|
if let Some(Typed::Int(format)) = params.remove(&FORMAT_PARAM_KEY) {
|
||||||
|
log::debug!("Got custom avatar ({}B) with format {}", image.vec.len(), format);
|
||||||
|
// TODO actually save image
|
||||||
|
/*let info = rc_core::persist::user::AvatarInfo {
|
||||||
|
avatar_id: 0,
|
||||||
|
use_custom: true,
|
||||||
|
};
|
||||||
|
user_info.set_avatar_info(info).await?;*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(params.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct CustomAvatarHandler;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl polariton_server::operations::Operation<()> for CustomAvatarHandler {
|
||||||
|
type User = crate::UserTy;
|
||||||
|
|
||||||
|
async fn handle_async(&self, params: ParameterTable<()>, user: &Self::User) -> OperationResponse<()> {
|
||||||
|
polariton_server::operations::result_to_op_resp::<CODE, ()>(do_save(params, user).await)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl polariton_server::operations::OperationCode for CustomAvatarHandler {
|
||||||
|
fn op_code() -> u8 {
|
||||||
|
CODE
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -91,6 +91,8 @@ mod crf_list_query;
|
|||||||
mod crf_vehicle_data;
|
mod crf_vehicle_data;
|
||||||
mod crf_purchase;
|
mod crf_purchase;
|
||||||
mod crf_upload;
|
mod crf_upload;
|
||||||
|
mod avatar_set_custom;
|
||||||
|
mod avatar_set;
|
||||||
|
|
||||||
use polariton_server::operations::OperationsHandler;
|
use polariton_server::operations::OperationsHandler;
|
||||||
|
|
||||||
@@ -136,7 +138,7 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler<crate::UserTy>
|
|||||||
.add(owned_cosmetics::selected_cosmetics_provider())
|
.add(owned_cosmetics::selected_cosmetics_provider())
|
||||||
.add(dev_message::dev_message_provider(&init_ctx.cubes))
|
.add(dev_message::dev_message_provider(&init_ctx.cubes))
|
||||||
.add(custom_games_maps::allowed_maps_provider())
|
.add(custom_games_maps::allowed_maps_provider())
|
||||||
.add(avatar_info::get_avatar_provider())
|
.add(avatar_info::avatar_get_provider())
|
||||||
.add(custom_game_session::get_custom_session_provider())
|
.add(custom_game_session::get_custom_session_provider())
|
||||||
.add(user_xp::get_user_xp_provider())
|
.add(user_xp::get_user_xp_provider())
|
||||||
.add(garage_upgrades::garage_upgrades_provider(&init_ctx.cubes))
|
.add(garage_upgrades::garage_upgrades_provider(&init_ctx.cubes))
|
||||||
@@ -204,4 +206,6 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler<crate::UserTy>
|
|||||||
.add(crf_vehicle_data::crf_item_data_provider(&init_ctx.factory))
|
.add(crf_vehicle_data::crf_item_data_provider(&init_ctx.factory))
|
||||||
.add(crf_purchase::crf_copy_to_bay_provider(&init_ctx.factory, init_ctx.parsers.weapon_order()))
|
.add(crf_purchase::crf_copy_to_bay_provider(&init_ctx.factory, init_ctx.parsers.weapon_order()))
|
||||||
.add(crf_upload::crf_upload_provider(&init_ctx.factory))
|
.add(crf_upload::crf_upload_provider(&init_ctx.factory))
|
||||||
|
.add(avatar_set_custom::custom_avatar_upload_handler())
|
||||||
|
.add(avatar_set::avatar_set_provider())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ pub(super) fn premium_remaining_provider() -> SimpleFunc<15, crate::UserTy, impl
|
|||||||
params.insert(HOURS_PARAM_KEY, Typed::Int(0));
|
params.insert(HOURS_PARAM_KEY, Typed::Int(0));
|
||||||
params.insert(MINUTES_PARAM_KEY, Typed::Int(0));
|
params.insert(MINUTES_PARAM_KEY, Typed::Int(0));
|
||||||
params.insert(SECONDS_PARAM_KEY, Typed::Int(0));
|
params.insert(SECONDS_PARAM_KEY, Typed::Int(0));
|
||||||
params.insert(LIFETIME_PARAM_KEY, Typed::Bool(false.into()));
|
params.insert(LIFETIME_PARAM_KEY, Typed::Bool(false));
|
||||||
Ok(params.into())
|
Ok(params.into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,4 +26,5 @@ pub fn handler() -> OperationsHandler<crate::UserTy, crate::data::custom::Custom
|
|||||||
.add(season_rewards::season_rewards_provider())
|
.add(season_rewards::season_rewards_provider())
|
||||||
.add(previous_battle_rewards::pending_battle_rewards_provider())
|
.add(previous_battle_rewards::pending_battle_rewards_provider())
|
||||||
.add(platoon_data::platoon_provider())
|
.add(platoon_data::platoon_provider())
|
||||||
|
.add(polariton_server::operations::Ack::<6, _>::default()) // AvatarUpdatedRequest, sent on services_room avatar_set success (just needs to be ack-ed; no params)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user