mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Refactor item purchase logic into core, implement promo codes #70
This commit is contained in:
78
rc_services_room/src/operations/code_redeem.rs
Normal file
78
rc_services_room/src/operations/code_redeem.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||
use polariton::operation::{ParameterTable, Typed};
|
||||
|
||||
const CODE: u8 = 107;
|
||||
|
||||
const CODE_NAME_PARAM_KEY: u8 = 122; // str; in
|
||||
const SUCCESS_PARAM_KEY: u8 = 123; // bool; out
|
||||
const RESULT_CODE_PARAM_KEY: u8 = 124; // int; out
|
||||
const IS_SERIAL_PARAM_KEY: u8 = 125; // bool; out
|
||||
const VALUE_PARAM_KEY: u8 = 126; // float; out
|
||||
const PROMO_ID_PARAM_KEY: u8 = 127; // str; out
|
||||
const CUBES_AWARDED_PARAM_KEY: u8 = 128; // json as str; out
|
||||
const MSG_PARAM_KEY: u8 = 133; // str; out
|
||||
const BUNDLE_ID_PARAM_KEY: u8 = 208; // str; out
|
||||
const ROBOPASS_PARAM_KEY: u8 = 4; // bool; out
|
||||
const PAID_CURRENCY_PARAM_KEY: u8 = 86; // long; out
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u8)]
|
||||
enum PromoResultCode {
|
||||
Success = 0,
|
||||
InvalidBundleId = 1,
|
||||
InvalidPromotionId = 2,
|
||||
Expired = 3,
|
||||
NotStarted = 4,
|
||||
AlreadyAwarded = 5,
|
||||
Consumed = 6,
|
||||
BundleIdAlreadyAwarded = 7
|
||||
}
|
||||
|
||||
pub(super) struct PromoCodeApplier {
|
||||
code_map: std::collections::HashMap<String, oj_rc_core::persist::config::PromoCode>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl SimpleOperation<()> for PromoCodeApplier {
|
||||
type User = crate::UserTy;
|
||||
const CODE: u8 = CODE;
|
||||
|
||||
async fn handle(&self, mut params: ParameterTable, user: &Self::User) -> Result<ParameterTable, SimpleOpError> {
|
||||
if let Some(Typed::Str(promo_code)) = params.remove(&CODE_NAME_PARAM_KEY) {
|
||||
let user_info = user.user()?;
|
||||
if let Some(code_info) = self.code_map.get(&promo_code.string) {
|
||||
let result = user_info.apply_purchase(&code_info.transaction).await?;
|
||||
params.insert(SUCCESS_PARAM_KEY, Typed::Bool(result.success));
|
||||
params.insert(RESULT_CODE_PARAM_KEY, Typed::Int(PromoResultCode::Success as _));
|
||||
params.insert(IS_SERIAL_PARAM_KEY, Typed::Bool(code_info.is_serial));
|
||||
params.insert(VALUE_PARAM_KEY, Typed::Float(code_info.value));
|
||||
params.insert(PROMO_ID_PARAM_KEY, Typed::Str(code_info.promo_id.clone().into()));
|
||||
params.insert(CUBES_AWARDED_PARAM_KEY, Typed::Str(serde_json::to_string(&result.cube_awards).unwrap().into()));
|
||||
params.insert(MSG_PARAM_KEY, Typed::Str(code_info.message.clone().unwrap_or_default().into()));
|
||||
params.insert(BUNDLE_ID_PARAM_KEY, Typed::Str(code_info.bundle_id.clone().into()));
|
||||
params.insert(ROBOPASS_PARAM_KEY, Typed::Bool(result.robopass_award));
|
||||
params.insert(PAID_CURRENCY_PARAM_KEY, Typed::Long(result.paid_currency_award));
|
||||
log::debug!("Code \"{}\" redeemed by {} (success? {}) {} rewards", promo_code.string, user_info.public_id(), result.success, code_info.transaction.gives.len());
|
||||
} else {
|
||||
params.insert(SUCCESS_PARAM_KEY, Typed::Bool(false));
|
||||
params.insert(RESULT_CODE_PARAM_KEY, Typed::Int(PromoResultCode::InvalidPromotionId as _));
|
||||
params.insert(IS_SERIAL_PARAM_KEY, Typed::Bool(false));
|
||||
params.insert(VALUE_PARAM_KEY, Typed::Float(0.0));
|
||||
params.insert(PROMO_ID_PARAM_KEY, Typed::Str(promo_code.clone()));
|
||||
params.insert(CUBES_AWARDED_PARAM_KEY, Typed::Str("{}".into()));
|
||||
params.insert(MSG_PARAM_KEY, Typed::Str("".into()));
|
||||
params.insert(BUNDLE_ID_PARAM_KEY, Typed::Str("RE_bundle_id_01".into()));
|
||||
params.insert(ROBOPASS_PARAM_KEY, Typed::Bool(false));
|
||||
params.insert(PAID_CURRENCY_PARAM_KEY, Typed::Long(0));
|
||||
log::debug!("Code \"{}\" not redeemed by {} (code not found)", promo_code.string, user_info.public_id());
|
||||
}
|
||||
}
|
||||
Ok(params)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn code_redeem_provider(conf: &oj_rc_core::ConfigImpl) -> SimpleOpImpl<(), crate::UserTy, PromoCodeApplier> {
|
||||
SimpleOpImpl::new(PromoCodeApplier {
|
||||
code_map: <oj_rc_core::ConfigImpl as oj_rc_core::ConfigProvider<()>>::promo_codes(conf),
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||
use polariton::operation::{ParameterTable, Typed, Arr};
|
||||
use polariton::operation::{ParameterTable, Typed};
|
||||
|
||||
const CODE: u8 = 189;
|
||||
|
||||
@@ -23,65 +23,19 @@ impl SimpleOperation<()> for ItemBundleBuyer {
|
||||
if let Some(Typed::Int(price)) = params.remove(&PRICE_PARAM_KEY) {
|
||||
let user_info = user.user()?;
|
||||
let transactions = self.resolver.resolve_transactions().await;
|
||||
// TODO refactor this into oj_rc_core in one of the User traits
|
||||
if let Some(transaction) = transactions.get(&sku.string) {
|
||||
let mut is_ok = false;
|
||||
if currency.string == "Robits" && transaction.cost_free == price {
|
||||
let ty = oj_rc_core::persist::user::CurrencyType::Free;
|
||||
let op = oj_rc_core::persist::user::CurrencyOp::AddSub(-price as _);
|
||||
user_info.currency(ty, op).await?;
|
||||
is_ok = true;
|
||||
} else if currency.string == "CosmeticCredits" && transaction.cost_paid == price {
|
||||
let ty = oj_rc_core::persist::user::CurrencyType::Paid;
|
||||
let op = oj_rc_core::persist::user::CurrencyOp::AddSub(-price as _);
|
||||
user_info.currency(ty, op).await?;
|
||||
is_ok = true;
|
||||
}
|
||||
let is_ok = (currency.string == "Robits" && transaction.cost_free == price)
|
||||
|| (currency.string == "CosmeticCredits" && transaction.cost_paid == price);
|
||||
if is_ok {
|
||||
let mut new_cubes = Vec::new();
|
||||
for award in transaction.gives.iter() {
|
||||
match award {
|
||||
oj_rc_core::persist::config::ShopGain::Cube(x) => {
|
||||
new_cubes.push(*x);
|
||||
},
|
||||
oj_rc_core::persist::config::ShopGain::Experience(xp) => {
|
||||
user_info.currency(
|
||||
oj_rc_core::persist::user::CurrencyType::Experience,
|
||||
oj_rc_core::persist::user::CurrencyOp::AddSub(*xp as _),
|
||||
).await?;
|
||||
},
|
||||
oj_rc_core::persist::config::ShopGain::FreeCurrency(c) => {
|
||||
user_info.currency(
|
||||
oj_rc_core::persist::user::CurrencyType::Free,
|
||||
oj_rc_core::persist::user::CurrencyOp::AddSub(*c as _),
|
||||
).await?;
|
||||
},
|
||||
oj_rc_core::persist::config::ShopGain::PaidCurrency(c) => {
|
||||
user_info.currency(
|
||||
oj_rc_core::persist::user::CurrencyType::Paid,
|
||||
oj_rc_core::persist::user::CurrencyOp::AddSub(*c as _),
|
||||
).await?;
|
||||
},
|
||||
oj_rc_core::persist::config::ShopGain::TechPoints(tp) => {
|
||||
user_info.currency(
|
||||
oj_rc_core::persist::user::CurrencyType::TechPoints,
|
||||
oj_rc_core::persist::user::CurrencyOp::AddSub(*tp as _),
|
||||
).await?;
|
||||
},
|
||||
}
|
||||
}
|
||||
let new_cubes_len = new_cubes.len();
|
||||
if !new_cubes.is_empty() {
|
||||
user_info.unlock_parts(&new_cubes).await?;
|
||||
}
|
||||
|
||||
let new_cubes_typed: Vec<_> = new_cubes.into_iter().map(|x| Typed::Str(hex::encode((x as i32).to_be_bytes()).into())).collect();
|
||||
//params.insert(NEW_CUBES_PARAM_KEY, Typed::Arr(new_cubes_i32.into()));
|
||||
params.insert(NEW_CUBES_PARAM_KEY, Typed::Arr(Arr {
|
||||
let purchase_result = user_info.apply_purchase(transaction).await?;
|
||||
let new_cubes = polariton::operation::Typed::Arr(polariton::operation::Arr {
|
||||
ty: polariton::serdes::TypePrefix::Str,
|
||||
items: new_cubes_typed,
|
||||
}));
|
||||
log::info!("SKU {} purchased (ok? {}) {} ops received, {} cubes unlocked", sku.string, is_ok, transaction.gives.len(), new_cubes_len);
|
||||
items: purchase_result.cube_awards.into_keys()
|
||||
.map(|k| polariton::operation::Typed::Str(k.into()))
|
||||
.collect(),
|
||||
});
|
||||
params.insert(NEW_CUBES_PARAM_KEY, new_cubes);
|
||||
log::debug!("SKU {} purchased (ok? {}) {} ops", sku.string, is_ok, transaction.gives.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ mod garage_slot_copy;
|
||||
mod steam_promo;
|
||||
mod campaign_save_result;
|
||||
mod item_shop_purchase;
|
||||
mod code_redeem;
|
||||
|
||||
use polariton_server::operations::OperationsHandler;
|
||||
|
||||
@@ -225,4 +226,5 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler<crate::UserTy>
|
||||
//.add(polariton_server::operations::Ack::<97, _>::default()) // TODO handle UpdateShopRobotOffsetRequest instead of ignoring it (this seems to break newly-uploaded vehicles for now)
|
||||
.add(steam_promo::steam_promos_provider())
|
||||
.add(item_shop_purchase::item_purchase_provider(&init_ctx.cubes))
|
||||
.add(code_redeem::code_redeem_provider(&init_ctx.cubes))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user