Merge remote-tracking branch 'origin/master' into feat/pricing-plan
Project CI / Backend tests (pull_request) Failing after 48s
Project CI / Native shell tests (pull_request) Failing after 3m50s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 5m21s
Project CI / Frontend tests (pull_request) Failing after 47s
Project CI / AI game creator shell web tests (pull_request) Failing after 39s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m6s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m23s
Project CI / Repository checks (pull_request) Successful in 6m31s
Project CI / Backend tests (pull_request) Failing after 48s
Project CI / Native shell tests (pull_request) Failing after 3m50s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 5m21s
Project CI / Frontend tests (pull_request) Failing after 47s
Project CI / AI game creator shell web tests (pull_request) Failing after 39s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m6s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m23s
Project CI / Repository checks (pull_request) Successful in 6m31s
# Conflicts: # docs/project-memory/shared-memory/decision-log.md
This commit is contained in:
@@ -16,8 +16,8 @@ use crate::{
|
||||
pub struct FrontendRuntimeConfigResponse {
|
||||
pub image_editor_agent_sidebar_enabled: bool,
|
||||
pub agc_template_library_enabled: bool,
|
||||
/// 游戏发布灰度:运营未配置 `game-distribution:publish` 时对已登录作者默认开放,
|
||||
/// 显式收紧后只有白名单/灰度命中的作者拿到 `true`;匿名一律 `false`(发布必须先登录)。
|
||||
/// 游戏发布灰度:未配置 `game-distribution:publish` 或 `enabled=false` 时不开放;
|
||||
/// 显式配置后按 enabled、白名单、标签和灰度比例判定,匿名一律 `false`(发布必须先登录)。
|
||||
pub game_distribution_publish_enabled: bool,
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+43
@@ -0,0 +1,43 @@
|
||||
(function () {
|
||||
function makeStorage() {
|
||||
var values = Object.create(null);
|
||||
return {
|
||||
get length() {
|
||||
return Object.keys(values).length;
|
||||
},
|
||||
key: function (index) {
|
||||
return Object.keys(values)[index] || null;
|
||||
},
|
||||
getItem: function (key) {
|
||||
key = String(key);
|
||||
return Object.prototype.hasOwnProperty.call(values, key)
|
||||
? values[key]
|
||||
: null;
|
||||
},
|
||||
setItem: function (key, value) {
|
||||
values[String(key)] = String(value);
|
||||
},
|
||||
removeItem: function (key) {
|
||||
delete values[String(key)];
|
||||
},
|
||||
clear: function () {
|
||||
values = Object.create(null);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function install(name) {
|
||||
try {
|
||||
Object.defineProperty(window, name, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
value: makeStorage(),
|
||||
});
|
||||
} catch (_) {
|
||||
// opaque-origin native storage may reject property replacement; keep the sandbox intact.
|
||||
}
|
||||
}
|
||||
|
||||
install('localStorage');
|
||||
install('sessionStorage');
|
||||
})();
|
||||
@@ -3,7 +3,7 @@ use std::sync::Mutex;
|
||||
#[cfg(test)]
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
collections::{BTreeMap, HashMap},
|
||||
error::Error,
|
||||
fmt,
|
||||
sync::{
|
||||
@@ -137,6 +137,12 @@ impl BackpressureState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct GameDistributionPreviewSession {
|
||||
pub(crate) version_id: String,
|
||||
pub(crate) expires_at: OffsetDateTime,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState(Arc<AppStateInner>);
|
||||
|
||||
@@ -307,6 +313,8 @@ pub struct AppStateInner {
|
||||
ai_task_service: AiTaskService,
|
||||
spacetime_client: SpacetimeClient,
|
||||
external_api_auth_state: ExternalApiAuthState,
|
||||
game_distribution_preview_sessions:
|
||||
Arc<AsyncMutex<HashMap<String, GameDistributionPreviewSession>>>,
|
||||
editor_project_state: EditorProjectState,
|
||||
#[cfg(any())]
|
||||
puzzle_gallery_cache: PuzzleGalleryCache,
|
||||
@@ -698,6 +706,7 @@ impl AppState {
|
||||
test_fail_editor_generation_enqueue: AtomicBool::new(false),
|
||||
#[cfg(test)]
|
||||
test_external_background_removal_enqueue: Arc::new(Mutex::new(None)),
|
||||
game_distribution_preview_sessions: Arc::new(AsyncMutex::new(HashMap::new())),
|
||||
oss_client,
|
||||
project_snapshot_oss_client,
|
||||
template_library_store,
|
||||
@@ -759,6 +768,35 @@ impl AppState {
|
||||
&self.refresh_cookie_config
|
||||
}
|
||||
|
||||
pub(crate) async fn create_game_distribution_preview_session(
|
||||
&self,
|
||||
version_id: String,
|
||||
expires_at: OffsetDateTime,
|
||||
) -> String {
|
||||
let token = uuid::Uuid::new_v4().to_string();
|
||||
let now = OffsetDateTime::now_utc();
|
||||
let mut sessions = self.game_distribution_preview_sessions.lock().await;
|
||||
sessions.retain(|_, session| session.expires_at > now);
|
||||
sessions.insert(
|
||||
token.clone(),
|
||||
GameDistributionPreviewSession {
|
||||
version_id,
|
||||
expires_at,
|
||||
},
|
||||
);
|
||||
token
|
||||
}
|
||||
|
||||
pub(crate) async fn get_game_distribution_preview_session(
|
||||
&self,
|
||||
token: &str,
|
||||
) -> Option<GameDistributionPreviewSession> {
|
||||
let now = OffsetDateTime::now_utc();
|
||||
let mut sessions = self.game_distribution_preview_sessions.lock().await;
|
||||
sessions.retain(|_, session| session.expires_at > now);
|
||||
sessions.get(token).cloned()
|
||||
}
|
||||
|
||||
pub fn http_request_permit_pools(&self) -> HttpRequestPermitPools {
|
||||
self.http_request_permit_pools.clone()
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::{
|
||||
commands::{CreateGameInput, CreateVersionInput, IdempotencyRequest, ReviewDecision},
|
||||
domain::{
|
||||
GameDistributionAction, GameSnapshot, GameVersionSnapshot, GameVersionStatus,
|
||||
GameVisibility, normalize_id,
|
||||
GameVisibility, normalize_id, resolve_version_number,
|
||||
},
|
||||
errors::{GameDistributionError, GameDistributionFieldError},
|
||||
};
|
||||
@@ -146,14 +146,16 @@ impl GameDistributionService {
|
||||
if state.versions.contains_key(&version_id) {
|
||||
return Err(GameDistributionError::VersionAlreadyExists);
|
||||
}
|
||||
let version_number = state
|
||||
.versions
|
||||
.values()
|
||||
.filter(|version| version.game_id == game_id)
|
||||
.map(|version| version.version_number)
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
.saturating_add(1);
|
||||
let version_number = resolve_version_number(
|
||||
state
|
||||
.versions
|
||||
.values()
|
||||
.filter(|version| version.game_id == game_id)
|
||||
.map(|version| version.version_number)
|
||||
.max()
|
||||
.unwrap_or(0),
|
||||
input.version_number,
|
||||
)?;
|
||||
let snapshot = GameVersionSnapshot {
|
||||
version_id: version_id.clone(),
|
||||
game_id,
|
||||
@@ -880,6 +882,7 @@ mod tests {
|
||||
game_id: "game-1".to_string(),
|
||||
owner_user_id: "owner-1".to_string(),
|
||||
version_id: "version-1".to_string(),
|
||||
version_number: None,
|
||||
package_sha256: "a".repeat(64),
|
||||
package_bytes: 42,
|
||||
idempotency: idem("create-version-1", "version-1"),
|
||||
@@ -1159,6 +1162,7 @@ mod tests {
|
||||
game_id: "game-1".to_string(),
|
||||
owner_user_id: "owner-1".to_string(),
|
||||
version_id: "version-2".to_string(),
|
||||
version_number: None,
|
||||
package_sha256: "b".repeat(64),
|
||||
package_bytes: 7,
|
||||
idempotency: idem("create-version-2", "version-2"),
|
||||
@@ -1270,4 +1274,61 @@ mod tests {
|
||||
assert_eq!(snapshot.status, GameVersionStatus::Uploaded);
|
||||
assert_eq!(repeated.snapshot, snapshot);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_version_number_accepts_repeats_and_reverts_of_the_same_label() {
|
||||
assert_eq!(resolve_version_number(0, Some(1)).expect("首版显式 1"), 1);
|
||||
// 重复标签:线上已有 3 也要能再提交一次 3,并生成新的版本记录。
|
||||
assert_eq!(resolve_version_number(3, Some(3)).expect("重复标签"), 3);
|
||||
// 回退:线上最大 5,也允许提交 2。
|
||||
assert_eq!(resolve_version_number(5, Some(2)).expect("回退标签"), 2);
|
||||
assert_eq!(
|
||||
resolve_version_number(0, None).expect("旧客户端自动递增"),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_version_number(7, None).expect("旧客户端按最大值递增"),
|
||||
8
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_number_must_be_positive_and_auto_increment_fails_closed() {
|
||||
assert_eq!(
|
||||
resolve_version_number(0, Some(0)).expect_err("0 不是合法标签"),
|
||||
GameDistributionFieldError::InvalidVersionNumber
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_version_number(u64::MAX, None).expect_err("自动递增溢出必须失败关闭"),
|
||||
GameDistributionFieldError::VersionNumberExhausted
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeating_the_same_label_creates_a_new_version_record() {
|
||||
let service = service();
|
||||
let first = create_version(&service);
|
||||
assert_eq!(first.version_number, 1);
|
||||
|
||||
// 同一个版本标签再提交一次:旧的已公开实例不原地修改,而是新增一条版本记录。
|
||||
let repeated = service
|
||||
.create_version(CreateVersionInput {
|
||||
game_id: "game-1".to_string(),
|
||||
owner_user_id: "owner-1".to_string(),
|
||||
version_id: "version-1-again".to_string(),
|
||||
version_number: Some(first.version_number),
|
||||
package_sha256: "b".repeat(64),
|
||||
package_bytes: 9,
|
||||
idempotency: idem("create-version-again", "version-again"),
|
||||
created_at_micros: 12,
|
||||
})
|
||||
.expect("同一个版本标签可以重复提交")
|
||||
.snapshot;
|
||||
assert_eq!(repeated.version_number, first.version_number);
|
||||
assert_ne!(repeated.version_id, first.version_id);
|
||||
assert_eq!(
|
||||
service.get_version(&first.version_id).expect("旧版本仍在"),
|
||||
first
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,10 @@ pub struct CreateVersionInput {
|
||||
pub game_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub version_id: String,
|
||||
/// 用户可见的正整数版本标签;`None` 时按该游戏已有最大版本号 +1(旧客户端兼容)。
|
||||
/// 传入时只要求 `>= 1`:同一 gameId 的同一标签可以反复提交,每次都是一条新版本记录。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub version_number: Option<u64>,
|
||||
pub package_sha256: String,
|
||||
pub package_bytes: u64,
|
||||
pub idempotency: IdempotencyRequest,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::errors::GameDistributionFieldError;
|
||||
use shared_kernel::{build_prefixed_seed_id, normalize_required_string};
|
||||
|
||||
pub const GAME_ID_PREFIX: &str = "game_";
|
||||
@@ -17,6 +18,25 @@ pub fn generate_game_version_id(seed_micros: i64, version_number: u64) -> String
|
||||
)
|
||||
}
|
||||
|
||||
/// 解析本次创建的版本号(用户可见的版本标签,不是不可变的 `version_id`)。
|
||||
///
|
||||
/// - `None`:旧客户端兼容路径,取该游戏已有最大版本号 +1;溢出时失败关闭,不回绕成 0。
|
||||
/// - `Some(requested)`:只要求正整数。同一个 gameId 的同一个版本号可以反复提交——每次提交都是
|
||||
/// 一条新的版本记录(新的 `version_id`),旧公开实例不原地修改——所以**不与已有最大值比较**,
|
||||
/// 允许重复标签,也允许回退到更小的标签。
|
||||
pub fn resolve_version_number(
|
||||
max_existing: u64,
|
||||
requested: Option<u64>,
|
||||
) -> Result<u64, GameDistributionFieldError> {
|
||||
match requested {
|
||||
Some(0) => Err(GameDistributionFieldError::InvalidVersionNumber),
|
||||
Some(requested) => Ok(requested),
|
||||
None => max_existing
|
||||
.checked_add(1)
|
||||
.ok_or(GameDistributionFieldError::VersionNumberExhausted),
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the lowercase SHA-256 digest used by the idempotency contract.
|
||||
pub fn compute_request_digest(payload: impl AsRef<[u8]>) -> String {
|
||||
let digest = Sha256::digest(payload.as_ref());
|
||||
|
||||
@@ -13,6 +13,8 @@ pub enum GameDistributionFieldError {
|
||||
InvalidPackageDigest,
|
||||
InvalidPackageBytes,
|
||||
MissingReviewReason,
|
||||
InvalidVersionNumber,
|
||||
VersionNumberExhausted,
|
||||
}
|
||||
|
||||
impl fmt::Display for GameDistributionFieldError {
|
||||
@@ -27,6 +29,8 @@ impl fmt::Display for GameDistributionFieldError {
|
||||
Self::InvalidPackageDigest => "packageSha256 必须是 64 位十六进制摘要",
|
||||
Self::InvalidPackageBytes => "packageBytes 必须大于 0",
|
||||
Self::MissingReviewReason => "拒绝审核必须提供理由",
|
||||
Self::InvalidVersionNumber => "版本号必须是大于 0 的整数",
|
||||
Self::VersionNumberExhausted => "版本号已达上限,无法自动递增",
|
||||
};
|
||||
formatter.write_str(message)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ pub use application::{
|
||||
pub use commands::{CreateGameInput, CreateVersionInput, IdempotencyRequest, ReviewDecision};
|
||||
pub use domain::{
|
||||
GameDistributionAction, GameSnapshot, GameVersionSnapshot, GameVersionStatus, GameVisibility,
|
||||
compute_request_digest, generate_game_id, generate_game_version_id,
|
||||
compute_request_digest, generate_game_id, generate_game_version_id, resolve_version_number,
|
||||
};
|
||||
pub use errors::{GameDistributionError, GameDistributionFieldError};
|
||||
pub use events::GameDistributionEvent;
|
||||
|
||||
@@ -87,7 +87,7 @@ pub fn creation_entry_feature_gate_key(creation_type_id: &str) -> String {
|
||||
|
||||
pub const IMAGE_EDITOR_AGENT_SIDEBAR_GATE_KEY: &str = "image-editor:agent-sidebar";
|
||||
pub const AGC_TEMPLATE_LIBRARY_GATE_KEY: &str = "agc:template-library";
|
||||
/// 游戏分发发布开关:`gate 行缺失`或 `enabled=false` 都表示**未开放**(灰度默认关闭);
|
||||
/// 游戏分发发布开关:gate 行缺失或 `enabled=false` 都表示**未开放**;
|
||||
/// 只有 `enabled=true` 且白名单 / 灰度比例 / 用户标签命中时才允许发布。
|
||||
pub const GAME_DISTRIBUTION_PUBLISH_GATE_KEY: &str = "game-distribution:publish";
|
||||
|
||||
|
||||
@@ -1070,6 +1070,23 @@ pub fn validate_game_iteration_versions(versions: &[GameIterationVersion]) -> Re
|
||||
/// 代价与配套:未知顶层字段本身就是「这个文件比本客户端新」的信号。客户端读到直接报错并拒绝改写,
|
||||
/// 由调用方提示升级;同一读路径上还有 `schemaVersion` 的失败关闭门,两者互补——`schemaVersion` 管
|
||||
/// 「文件明说自己是新版本」,本属性管「版本号没升但字段已经变了」。
|
||||
/// AGC 本地发布绑定:记录该本地项目对应哪个平台账号/origin 的哪个游戏与最近版本。
|
||||
/// 缺失即未发布;accountId/apiBaseUrl 用于按实际会话隔离,避免切换账号后接管他人作品。
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GameCreationAppPublicationBinding {
|
||||
pub account_id: String,
|
||||
pub api_base_url: String,
|
||||
pub game_id: String,
|
||||
pub revision: u64,
|
||||
#[serde(default)]
|
||||
pub latest_version_id: Option<String>,
|
||||
#[serde(default)]
|
||||
pub latest_version_number: Option<u64>,
|
||||
#[serde(default)]
|
||||
pub status: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
||||
pub struct GameCreationAppManifest {
|
||||
@@ -1091,6 +1108,15 @@ pub struct GameCreationAppManifest {
|
||||
pub godot_project_root: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub cocos_project_root: Option<String>,
|
||||
/// 遗留位:历史清单写过的用户发行版本标签,仅为兼容旧清单可解析而保留。
|
||||
///
|
||||
/// 它**不再**是发行版本来源:发布面板的「项目版本」由 AGC 工程内部版本记录 `versions[]`
|
||||
/// 派生(标签 = 存活内部版本条数),原生不再写入或回填这个字段。字段本身不能删除:
|
||||
/// 本结构体带 `deny_unknown_fields`,删掉它会让所有含该键的存量清单解析失败。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub project_version: Option<u64>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub publication: Option<GameCreationAppPublicationBinding>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
@@ -1224,6 +1250,8 @@ pub fn new_game_creation_app_manifest(
|
||||
command_runs: Vec::new(),
|
||||
versions: Vec::new(),
|
||||
godot_project_root: None,
|
||||
project_version: None,
|
||||
publication: None,
|
||||
cocos_project_root: None,
|
||||
}
|
||||
}
|
||||
@@ -2162,6 +2190,50 @@ mod tests {
|
||||
assert_eq!(payload["godotProjectRoot"], json!("game-source"));
|
||||
}
|
||||
|
||||
/// `projectVersion` 降级为遗留兼容位:存量清单里带这个键时必须照旧可读、可往返
|
||||
/// (本结构体带 `deny_unknown_fields`,拒掉它等于让老项目打不开),但它不再是发行版本
|
||||
/// 来源——Rust 侧不再有任何解析或回填入口,发布面板的「项目版本」改由工程内部版本
|
||||
/// `versions[]` 派生。
|
||||
#[test]
|
||||
fn manifest_project_version_is_a_legacy_compatibility_field() {
|
||||
let manifest = new_game_creation_app_manifest("project-1", "像素动作原型");
|
||||
let payload = serde_json::to_value(&manifest).expect("manifest should serialize");
|
||||
assert!(
|
||||
payload.get("projectVersion").is_none(),
|
||||
"原生不再写入版本标签,新清单不带这个键"
|
||||
);
|
||||
|
||||
let mut legacy = payload.clone();
|
||||
legacy["projectVersion"] = json!(6);
|
||||
let parsed: GameCreationAppManifest =
|
||||
serde_json::from_value(legacy).expect("含遗留 projectVersion 的存量清单必须仍可解析");
|
||||
assert_eq!(parsed.project_version, Some(6));
|
||||
assert!(
|
||||
parsed.publication.is_none(),
|
||||
"遗留键与发布绑定互相独立,不会凭它推导任何东西"
|
||||
);
|
||||
|
||||
let mut published = parsed.clone();
|
||||
published.publication = Some(GameCreationAppPublicationBinding {
|
||||
account_id: "user-1".to_string(),
|
||||
api_base_url: "https://dev.test".to_string(),
|
||||
game_id: "game-1".to_string(),
|
||||
revision: 3,
|
||||
latest_version_id: Some("gamever-1".to_string()),
|
||||
latest_version_number: Some(4),
|
||||
status: Some("pending_review".to_string()),
|
||||
});
|
||||
let payload = serde_json::to_value(&published).expect("published manifest serializes");
|
||||
assert_eq!(payload["projectVersion"], json!(6));
|
||||
assert_eq!(payload["publication"]["latestVersionNumber"], json!(4));
|
||||
assert_eq!(
|
||||
serde_json::from_value::<GameCreationAppManifest>(payload)
|
||||
.expect("legacy project version must round-trip")
|
||||
.project_version,
|
||||
Some(6)
|
||||
);
|
||||
}
|
||||
|
||||
/// 未知顶层字段必须失败关闭。这条契约级断言钉住的是本结构体本身的取向,而不是某个调用方:
|
||||
/// AGC 客户端读写 manifest 的方式是「整结构体反序列化 + 整结构体重新序列化覆盖落盘」,
|
||||
/// 一旦放行未知顶层字段,「读一次 + 任意一次写」就会静默抹掉未来版本新增的字段。
|
||||
|
||||
@@ -14,6 +14,14 @@ pub const GAME_DISTRIBUTION_CATEGORIES: [&str; 7] =
|
||||
/// 客户端要么放行服务端必拒的包(白等一次上传),要么拦掉服务端能收的包。
|
||||
pub const GAME_DISTRIBUTION_MAX_PACKAGE_BYTES: u64 = 200 * 1024 * 1024;
|
||||
|
||||
/// 版本号无法分配时的统一错误前缀(只在自动递增溢出时使用)。
|
||||
///
|
||||
/// 旧的「必须严格大于当前最大版本号」限制已经取消:同一 gameId 的同一个版本号可以反复提交,
|
||||
/// 每次生成新的 versionId,也允许回退到更小的标签。显式版本号只要求 `>= 1`,`0` 这类非法值
|
||||
/// 走普通 400;只有旧客户端缺省时按最大版本号自动递增、撞上 `u64::MAX` 才会带上此前缀,
|
||||
/// api-server 据此映射成 HTTP 409。
|
||||
pub const GAME_DISTRIBUTION_VERSION_NUMBER_CONFLICT: &str = "VERSION_NUMBER_CONFLICT";
|
||||
|
||||
/// 发布页免费生成简介与分类的输入。
|
||||
///
|
||||
/// 只传经过裁剪的项目摘要,不传本地绝对路径、聊天记录、凭据或完整 manifest。
|
||||
@@ -224,6 +232,14 @@ pub struct GameDistributionCreateGameRequest {
|
||||
pub struct GameDistributionCreateVersionRequest {
|
||||
#[serde(default)]
|
||||
pub local_project_id: Option<String>,
|
||||
/// 用户可见的正整数版本标签(AGC 发布面板由工程内部版本序数派生后原样提交)。
|
||||
///
|
||||
/// 传入时只要求 `>= 1`:同一 gameId 的同一个版本号可以反复提交,每次提交生成新的
|
||||
/// versionId,允许重复标签与回退到更小的版本号,不与已有最大值比较。缺省时保留旧客户端
|
||||
/// 兼容行为:服务端按该游戏已有最大版本号 +1(此时溢出才会返回 409)。
|
||||
/// 缺省时序列化省略该字段,保证旧客户端请求的幂等摘要与升级前一致。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub version_number: Option<u64>,
|
||||
pub package_sha256: String,
|
||||
pub package_bytes: u64,
|
||||
pub package_file_count: u32,
|
||||
@@ -231,6 +247,31 @@ pub struct GameDistributionCreateVersionRequest {
|
||||
pub game_metadata: GameDistributionCreateGameRequest,
|
||||
}
|
||||
|
||||
/// 作者编辑游戏级展示资料的请求体。
|
||||
///
|
||||
/// 只覆盖游戏行上的展示字段,随版本冻结的包摘要与资料快照不受影响;
|
||||
/// `expected_publication_revision` 是公开切换 CAS,并发变化返回 409。
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GameDistributionUpdateGameMetadataRequest {
|
||||
pub expected_publication_revision: u64,
|
||||
pub title: String,
|
||||
pub summary: String,
|
||||
#[serde(default)]
|
||||
pub description: Option<String>,
|
||||
pub category: String,
|
||||
#[serde(default)]
|
||||
pub tags: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub cover_asset_id: Option<String>,
|
||||
/// 截图素材 ID(最多 6 张,复用平台图片上传与归属校验)。
|
||||
#[serde(default)]
|
||||
pub screenshots: Vec<String>,
|
||||
pub device_support: GameDistributionDeviceSupport,
|
||||
pub input_modes: Vec<GameDistributionInputMode>,
|
||||
pub orientation: GameDistributionOrientation,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GameDistributionPrivateVersion {
|
||||
@@ -239,11 +280,14 @@ pub struct GameDistributionPrivateVersion {
|
||||
pub version_number: u64,
|
||||
pub package_sha256: String,
|
||||
pub package_bytes: u64,
|
||||
pub package_file_count: u32,
|
||||
pub status: GameDistributionVersionStatus,
|
||||
/// 这一版所属游戏的公开切换版本号(撤回与审核回传用)。
|
||||
pub publication_revision: u64,
|
||||
#[serde(default)]
|
||||
pub review_reason: Option<String>,
|
||||
#[serde(default)]
|
||||
pub entry_url: Option<String>,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
@@ -334,4 +378,68 @@ mod tests {
|
||||
.expect("版本请求应可解析");
|
||||
assert_eq!(payload.local_project_id.as_deref(), Some("proj-1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_version_request_version_number_is_optional() {
|
||||
let legacy: GameDistributionCreateVersionRequest =
|
||||
serde_json::from_value(serde_json::json!({
|
||||
"packageSha256": "a".repeat(64),
|
||||
"packageBytes": 10,
|
||||
"packageFileCount": 1,
|
||||
"packageEntryPath": "index.html",
|
||||
"gameMetadata": {
|
||||
"title": "游戏",
|
||||
"summary": "简介",
|
||||
"category": "益智",
|
||||
"deviceSupport": { "desktop": true, "mobile": false, "touch": false },
|
||||
"inputModes": ["keyboard"],
|
||||
"orientation": "responsive",
|
||||
},
|
||||
}))
|
||||
.expect("旧客户端不传版本号应继续可解析");
|
||||
assert_eq!(legacy.version_number, None);
|
||||
|
||||
let explicit: GameDistributionCreateVersionRequest =
|
||||
serde_json::from_value(serde_json::json!({
|
||||
"versionNumber": 5,
|
||||
"packageSha256": "a".repeat(64),
|
||||
"packageBytes": 10,
|
||||
"packageFileCount": 1,
|
||||
"packageEntryPath": "index.html",
|
||||
"gameMetadata": {
|
||||
"title": "游戏",
|
||||
"summary": "简介",
|
||||
"category": "益智",
|
||||
"deviceSupport": { "desktop": true, "mobile": false, "touch": false },
|
||||
"inputModes": ["keyboard"],
|
||||
"orientation": "responsive",
|
||||
},
|
||||
}))
|
||||
.expect("显式版本号应可解析");
|
||||
assert_eq!(explicit.version_number, Some(5));
|
||||
let serialized = serde_json::to_value(&explicit).expect("显式版本号应可序列化");
|
||||
assert_eq!(serialized["versionNumber"], 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_game_metadata_request_round_trips_publication_revision() {
|
||||
let payload: GameDistributionUpdateGameMetadataRequest =
|
||||
serde_json::from_value(serde_json::json!({
|
||||
"expectedPublicationRevision": 3,
|
||||
"title": "游戏",
|
||||
"summary": "简介",
|
||||
"category": "益智",
|
||||
"coverAssetId": "asset-cover",
|
||||
"screenshots": ["asset-shot"],
|
||||
"deviceSupport": { "desktop": true, "mobile": false, "touch": false },
|
||||
"inputModes": ["keyboard"],
|
||||
"orientation": "responsive",
|
||||
}))
|
||||
.expect("资料编辑请求应可解析");
|
||||
assert_eq!(payload.expected_publication_revision, 3);
|
||||
assert_eq!(payload.cover_asset_id.as_deref(), Some("asset-cover"));
|
||||
let serialized = serde_json::to_value(&payload).expect("资料编辑请求应可序列化");
|
||||
assert_eq!(serialized["expectedPublicationRevision"], 3);
|
||||
assert_eq!(serialized["coverAssetId"], "asset-cover");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,13 +25,14 @@ pub use game_distribution::{
|
||||
GameDistributionAdminGameListRecordInput, GameDistributionAdminUserReviewListRecordInput,
|
||||
GameDistributionApproveRecordInput, GameDistributionCancelVersionRecordInput,
|
||||
GameDistributionConfirmPackageRecordInput, GameDistributionCreateGameRecordInput,
|
||||
GameDistributionCreateVersionRecordInput, GameDistributionFailUploadRecordInput,
|
||||
GameDistributionGetGameRecordInput, GameDistributionOwnerGameListRecordInput,
|
||||
GameDistributionPlayCountIncrementRecordInput, GameDistributionPublicGameListRecordInput,
|
||||
GameDistributionRejectRecordInput, GameDistributionRestoreRecordInput,
|
||||
GameDistributionReviewGameListRecordInput, GameDistributionReviewModerationRecordInput,
|
||||
GameDistributionSubmitReviewRecordInput, GameDistributionSuspendRecordInput,
|
||||
GameDistributionUnpublishRecordInput,
|
||||
GameDistributionCreateVersionRecordInput, GameDistributionDeleteGameRecordInput,
|
||||
GameDistributionFailUploadRecordInput, GameDistributionGetGameRecordInput,
|
||||
GameDistributionOwnerGameListRecordInput, GameDistributionPlayCountIncrementRecordInput,
|
||||
GameDistributionPublicGameListRecordInput, GameDistributionRejectRecordInput,
|
||||
GameDistributionRestoreRecordInput, GameDistributionReviewGameListRecordInput,
|
||||
GameDistributionReviewModerationRecordInput, GameDistributionSubmitReviewRecordInput,
|
||||
GameDistributionSuspendRecordInput, GameDistributionUnpublishRecordInput,
|
||||
GameDistributionUpdateMetadataRecordInput,
|
||||
};
|
||||
#[path = "active/external_generation.rs"]
|
||||
pub mod external_generation;
|
||||
|
||||
@@ -179,6 +179,7 @@ pub struct GameDistributionGameRecord {
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
pub cover_object_key: Option<String>,
|
||||
pub local_project_id: Option<String>,
|
||||
pub screenshots_json: Option<String>,
|
||||
}
|
||||
|
||||
@@ -232,6 +233,8 @@ pub struct GameDistributionAdminGameRecord {
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
pub versions: Vec<GameDistributionAdminVersionRecord>,
|
||||
/// 软删除时间;为空表示未删除。
|
||||
pub deleted_at: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
@@ -272,6 +275,7 @@ fn map_game(
|
||||
play_count: value.play_count,
|
||||
created_at: shared_kernel::format_timestamp_micros(value.created_at_micros),
|
||||
updated_at: shared_kernel::format_timestamp_micros(value.updated_at_micros),
|
||||
local_project_id: value.local_project_id,
|
||||
cover_object_key: value.cover_object_key,
|
||||
screenshots_json: value.screenshots_json,
|
||||
}
|
||||
@@ -338,16 +342,22 @@ fn map_admin_game(
|
||||
created_at: shared_kernel::format_timestamp_micros(value.created_at_micros),
|
||||
updated_at: shared_kernel::format_timestamp_micros(value.updated_at_micros),
|
||||
versions: value.versions.into_iter().map(map_admin_version).collect(),
|
||||
deleted_at: value
|
||||
.deleted_at_micros
|
||||
.map(shared_kernel::format_timestamp_micros),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn map_game_distribution_admin_game_list_result(
|
||||
result: crate::module_bindings::GameDistributionAdminGameListResult,
|
||||
) -> Result<Vec<GameDistributionAdminGameRecord>, SpacetimeClientError> {
|
||||
) -> Result<(Vec<GameDistributionAdminGameRecord>, Option<String>), SpacetimeClientError> {
|
||||
if !result.ok {
|
||||
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
||||
}
|
||||
Ok(result.games.into_iter().map(map_admin_game).collect())
|
||||
Ok((
|
||||
result.games.into_iter().map(map_admin_game).collect(),
|
||||
result.next_cursor,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn map_game_distribution_game_result(
|
||||
|
||||
@@ -37,6 +37,14 @@ pub struct GameDistributionPublicGameListRecordInput {
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct GameDistributionAdminGameListRecordInput {
|
||||
pub limit: u32,
|
||||
/// 关键词:匹配标题、gameId 或作者 user ID。
|
||||
pub keyword: Option<String>,
|
||||
/// 只返回该作者名下的游戏。
|
||||
pub owner_user_id: Option<String>,
|
||||
/// `published` / `unpublished` / `suspended` / `deleted`;为空时排除已软删除游戏。
|
||||
pub status: Option<String>,
|
||||
/// 上一页返回的游标。
|
||||
pub cursor: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@@ -87,6 +95,9 @@ pub struct GameDistributionCreateVersionRecordInput {
|
||||
pub game_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub version_id: String,
|
||||
/// 用户可见的正整数版本标签;`None` 时由服务端按已有最大版本号自动 +1(旧客户端兼容)。
|
||||
/// 传入时只要求 `>= 1`:同一 gameId 的同一标签可以反复提交,每次生成新的 version_id。
|
||||
pub version_number: Option<u64>,
|
||||
/// api-server 校验并解析素材后生成的资料快照 JSON。
|
||||
pub metadata_json: String,
|
||||
pub package_sha256: String,
|
||||
@@ -198,6 +209,41 @@ pub struct GameDistributionSuspendRecordInput {
|
||||
pub now_micros: i64,
|
||||
}
|
||||
|
||||
/// 作者编辑游戏级展示资料;对象键由 api-server 从素材记录派生后传入。
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct GameDistributionUpdateMetadataRecordInput {
|
||||
pub game_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub expected_publication_revision: u64,
|
||||
pub title: String,
|
||||
pub summary: String,
|
||||
pub description: Option<String>,
|
||||
pub category: String,
|
||||
pub tags_json: String,
|
||||
pub cover_asset_id: Option<String>,
|
||||
pub cover_object_key: Option<String>,
|
||||
pub screenshots_json: Option<String>,
|
||||
pub device_support_desktop: bool,
|
||||
pub device_support_mobile: bool,
|
||||
pub device_support_touch: bool,
|
||||
pub input_modes_json: String,
|
||||
pub orientation: String,
|
||||
pub idempotency_key: String,
|
||||
pub request_digest: String,
|
||||
pub now_micros: i64,
|
||||
}
|
||||
|
||||
/// 作者软删除游戏;只标记删除时间并下线公开投影。
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct GameDistributionDeleteGameRecordInput {
|
||||
pub game_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub expected_publication_revision: u64,
|
||||
pub idempotency_key: String,
|
||||
pub request_digest: String,
|
||||
pub now_micros: i64,
|
||||
}
|
||||
|
||||
impl SpacetimeClient {
|
||||
pub async fn list_admin_game_distribution_user_reviews(
|
||||
&self,
|
||||
@@ -664,6 +710,7 @@ impl SpacetimeClient {
|
||||
game_id: input.game_id,
|
||||
owner_user_id: input.owner_user_id,
|
||||
version_id: input.version_id,
|
||||
version_number: input.version_number,
|
||||
metadata_json: input.metadata_json,
|
||||
package_sha_256: input.package_sha256,
|
||||
package_bytes: input.package_bytes,
|
||||
@@ -1035,13 +1082,18 @@ impl SpacetimeClient {
|
||||
.await
|
||||
}
|
||||
|
||||
/// 后台游戏管理页的全量游戏列表:含版本数与最近版本历史,作者名/头像由事务内读时联。
|
||||
/// 后台游戏管理页列表:关键词/作者/状态过滤 + 游标分页,作者名/头像由事务内读时联。
|
||||
pub async fn list_admin_game_distribution_games(
|
||||
&self,
|
||||
input: GameDistributionAdminGameListRecordInput,
|
||||
) -> Result<Vec<GameDistributionAdminGameRecord>, SpacetimeClientError> {
|
||||
let procedure_input =
|
||||
crate::module_bindings::GameDistributionAdminGameListInput { limit: input.limit };
|
||||
) -> Result<(Vec<GameDistributionAdminGameRecord>, Option<String>), SpacetimeClientError> {
|
||||
let procedure_input = crate::module_bindings::GameDistributionAdminGameListInput {
|
||||
limit: input.limit,
|
||||
keyword: input.keyword,
|
||||
owner_user_id: input.owner_user_id,
|
||||
status: input.status,
|
||||
cursor: input.cursor,
|
||||
};
|
||||
self.call_after_connect(
|
||||
"list_admin_game_distribution_games",
|
||||
move |connection, sender| {
|
||||
@@ -1061,6 +1113,93 @@ impl SpacetimeClient {
|
||||
.await
|
||||
}
|
||||
|
||||
/// 作者编辑游戏级展示资料;版本包与冻结资料不受影响。
|
||||
pub async fn update_game_distribution_game_metadata(
|
||||
&self,
|
||||
input: GameDistributionUpdateMetadataRecordInput,
|
||||
) -> Result<(GameDistributionGameRecord, bool), SpacetimeClientError> {
|
||||
let procedure_input = crate::module_bindings::GameDistributionUpdateMetadataInput {
|
||||
game_id: input.game_id,
|
||||
owner_user_id: input.owner_user_id,
|
||||
expected_publication_revision: input.expected_publication_revision,
|
||||
title: input.title,
|
||||
summary: input.summary,
|
||||
description: input.description,
|
||||
category: input.category,
|
||||
tags_json: input.tags_json,
|
||||
cover_asset_id: input.cover_asset_id,
|
||||
cover_object_key: input.cover_object_key,
|
||||
screenshots_json: input.screenshots_json,
|
||||
device_support_desktop: input.device_support_desktop,
|
||||
device_support_mobile: input.device_support_mobile,
|
||||
device_support_touch: input.device_support_touch,
|
||||
input_modes_json: input.input_modes_json,
|
||||
orientation: input.orientation,
|
||||
idempotency_key: input.idempotency_key,
|
||||
request_digest: input.request_digest,
|
||||
now_micros: input.now_micros,
|
||||
};
|
||||
self.call_after_connect(
|
||||
"update_game_distribution_game_metadata",
|
||||
move |connection, sender| {
|
||||
connection
|
||||
.procedures()
|
||||
.update_game_distribution_game_metadata_and_return_then(
|
||||
procedure_input,
|
||||
move |_, result| {
|
||||
let mapped = result
|
||||
.map_err(SpacetimeClientError::from_sdk_error)
|
||||
.and_then(map_game_distribution_game_result)
|
||||
.and_then(|(game, _, replayed)| {
|
||||
game.map(|game| (game, replayed)).ok_or_else(|| {
|
||||
SpacetimeClientError::missing_snapshot("游戏资料更新结果")
|
||||
})
|
||||
});
|
||||
send_once(&sender, mapped);
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// 作者软删除游戏;只下线公开投影,版本行与冻结资料保留。
|
||||
pub async fn delete_game_distribution_game(
|
||||
&self,
|
||||
input: GameDistributionDeleteGameRecordInput,
|
||||
) -> Result<(GameDistributionGameRecord, bool), SpacetimeClientError> {
|
||||
let procedure_input = crate::module_bindings::GameDistributionDeleteGameInput {
|
||||
game_id: input.game_id,
|
||||
owner_user_id: input.owner_user_id,
|
||||
expected_publication_revision: input.expected_publication_revision,
|
||||
idempotency_key: input.idempotency_key,
|
||||
request_digest: input.request_digest,
|
||||
now_micros: input.now_micros,
|
||||
};
|
||||
self.call_after_connect(
|
||||
"delete_game_distribution_game",
|
||||
move |connection, sender| {
|
||||
connection
|
||||
.procedures()
|
||||
.delete_game_distribution_game_and_return_then(
|
||||
procedure_input,
|
||||
move |_, result| {
|
||||
let mapped = result
|
||||
.map_err(SpacetimeClientError::from_sdk_error)
|
||||
.and_then(map_game_distribution_game_result)
|
||||
.and_then(|(game, _, replayed)| {
|
||||
game.map(|game| (game, replayed)).ok_or_else(|| {
|
||||
SpacetimeClientError::missing_snapshot("游戏删除结果")
|
||||
})
|
||||
});
|
||||
send_once(&sender, mapped);
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// 管理员解除安全下架:重新激活最近一次曾公开的版本。
|
||||
pub async fn restore_game_distribution_game(
|
||||
&self,
|
||||
|
||||
@@ -202,6 +202,7 @@ pub mod delete_editor_asset_and_return_procedure;
|
||||
pub mod delete_editor_asset_folder_and_return_procedure;
|
||||
pub mod delete_editor_project_and_return_procedure;
|
||||
pub mod delete_error_report_and_return_procedure;
|
||||
pub mod delete_game_distribution_game_and_return_procedure;
|
||||
pub mod editor_agent_conversation_create_input_type;
|
||||
pub mod editor_agent_conversation_delete_input_type;
|
||||
pub mod editor_agent_conversation_get_input_type;
|
||||
@@ -421,6 +422,7 @@ pub mod game_distribution_cancel_version_input_type;
|
||||
pub mod game_distribution_confirm_package_input_type;
|
||||
pub mod game_distribution_create_game_input_type;
|
||||
pub mod game_distribution_create_version_input_type;
|
||||
pub mod game_distribution_delete_game_input_type;
|
||||
pub mod game_distribution_fail_upload_input_type;
|
||||
pub mod game_distribution_game_list_procedure_result_type;
|
||||
pub mod game_distribution_game_procedure_result_type;
|
||||
@@ -455,6 +457,7 @@ pub mod game_distribution_review_type;
|
||||
pub mod game_distribution_submit_review_input_type;
|
||||
pub mod game_distribution_suspend_input_type;
|
||||
pub mod game_distribution_unpublish_input_type;
|
||||
pub mod game_distribution_update_metadata_input_type;
|
||||
pub mod game_distribution_user_review_input_type;
|
||||
pub mod game_distribution_user_review_list_input_type;
|
||||
pub mod game_distribution_user_review_result_type;
|
||||
@@ -875,6 +878,7 @@ pub mod update_editor_project_resource_showcase_and_return_procedure;
|
||||
pub mod update_editor_showcase_asset_display_and_return_procedure;
|
||||
pub mod update_error_report_and_return_procedure;
|
||||
pub mod update_external_generation_job_phase_and_return_procedure;
|
||||
pub mod update_game_distribution_game_metadata_and_return_procedure;
|
||||
pub mod update_payment_app_and_return_procedure;
|
||||
pub mod upload_agc_analytics_batch_procedure;
|
||||
pub mod upsert_editor_generation_pricing_config_and_return_procedure;
|
||||
@@ -1084,6 +1088,7 @@ pub use delete_editor_asset_and_return_procedure::delete_editor_asset_and_return
|
||||
pub use delete_editor_asset_folder_and_return_procedure::delete_editor_asset_folder_and_return;
|
||||
pub use delete_editor_project_and_return_procedure::delete_editor_project_and_return;
|
||||
pub use delete_error_report_and_return_procedure::delete_error_report_and_return;
|
||||
pub use delete_game_distribution_game_and_return_procedure::delete_game_distribution_game_and_return;
|
||||
pub use editor_agent_conversation_create_input_type::EditorAgentConversationCreateInput;
|
||||
pub use editor_agent_conversation_delete_input_type::EditorAgentConversationDeleteInput;
|
||||
pub use editor_agent_conversation_get_input_type::EditorAgentConversationGetInput;
|
||||
@@ -1303,6 +1308,7 @@ pub use game_distribution_cancel_version_input_type::GameDistributionCancelVersi
|
||||
pub use game_distribution_confirm_package_input_type::GameDistributionConfirmPackageInput;
|
||||
pub use game_distribution_create_game_input_type::GameDistributionCreateGameInput;
|
||||
pub use game_distribution_create_version_input_type::GameDistributionCreateVersionInput;
|
||||
pub use game_distribution_delete_game_input_type::GameDistributionDeleteGameInput;
|
||||
pub use game_distribution_fail_upload_input_type::GameDistributionFailUploadInput;
|
||||
pub use game_distribution_game_list_procedure_result_type::GameDistributionGameListProcedureResult;
|
||||
pub use game_distribution_game_procedure_result_type::GameDistributionGameProcedureResult;
|
||||
@@ -1337,6 +1343,7 @@ pub use game_distribution_review_type::GameDistributionReview;
|
||||
pub use game_distribution_submit_review_input_type::GameDistributionSubmitReviewInput;
|
||||
pub use game_distribution_suspend_input_type::GameDistributionSuspendInput;
|
||||
pub use game_distribution_unpublish_input_type::GameDistributionUnpublishInput;
|
||||
pub use game_distribution_update_metadata_input_type::GameDistributionUpdateMetadataInput;
|
||||
pub use game_distribution_user_review_input_type::GameDistributionUserReviewInput;
|
||||
pub use game_distribution_user_review_list_input_type::GameDistributionUserReviewListInput;
|
||||
pub use game_distribution_user_review_result_type::GameDistributionUserReviewResult;
|
||||
@@ -1757,6 +1764,7 @@ pub use update_editor_project_resource_showcase_and_return_procedure::update_edi
|
||||
pub use update_editor_showcase_asset_display_and_return_procedure::update_editor_showcase_asset_display_and_return;
|
||||
pub use update_error_report_and_return_procedure::update_error_report_and_return;
|
||||
pub use update_external_generation_job_phase_and_return_procedure::update_external_generation_job_phase_and_return;
|
||||
pub use update_game_distribution_game_metadata_and_return_procedure::update_game_distribution_game_metadata_and_return;
|
||||
pub use update_payment_app_and_return_procedure::update_payment_app_and_return;
|
||||
pub use upload_agc_analytics_batch_procedure::upload_agc_analytics_batch;
|
||||
pub use upsert_editor_generation_pricing_config_and_return_procedure::upsert_editor_generation_pricing_config_and_return;
|
||||
|
||||
+8
-8
@@ -31,10 +31,10 @@ pub trait approve_game_distribution_version_and_return {
|
||||
input: GameDistributionApproveInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,10 +44,10 @@ impl approve_game_distribution_version_and_return for super::RemoteProcedures {
|
||||
input: GameDistributionApproveInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, GameDistributionProcedureResult>(
|
||||
|
||||
+8
-8
@@ -34,10 +34,10 @@ pub trait cancel_game_distribution_version_and_return {
|
||||
input: GameDistributionCancelVersionInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,10 +47,10 @@ impl cancel_game_distribution_version_and_return for super::RemoteProcedures {
|
||||
input: GameDistributionCancelVersionInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, GameDistributionProcedureResult>(
|
||||
|
||||
+8
-8
@@ -34,10 +34,10 @@ pub trait confirm_game_distribution_package_and_return {
|
||||
input: GameDistributionConfirmPackageInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,10 +47,10 @@ impl confirm_game_distribution_package_and_return for super::RemoteProcedures {
|
||||
input: GameDistributionConfirmPackageInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, GameDistributionProcedureResult>(
|
||||
|
||||
+8
-8
@@ -31,10 +31,10 @@ pub trait create_game_distribution_game_and_return {
|
||||
input: GameDistributionCreateGameInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,10 +44,10 @@ impl create_game_distribution_game_and_return for super::RemoteProcedures {
|
||||
input: GameDistributionCreateGameInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, GameDistributionProcedureResult>(
|
||||
|
||||
+8
-8
@@ -34,10 +34,10 @@ pub trait create_game_distribution_version_and_return {
|
||||
input: GameDistributionCreateVersionInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,10 +47,10 @@ impl create_game_distribution_version_and_return for super::RemoteProcedures {
|
||||
input: GameDistributionCreateVersionInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, GameDistributionProcedureResult>(
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::game_distribution_delete_game_input_type::GameDistributionDeleteGameInput;
|
||||
use super::game_distribution_procedure_result_type::GameDistributionProcedureResult;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
struct DeleteGameDistributionGameAndReturnArgs {
|
||||
pub input: GameDistributionDeleteGameInput,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for DeleteGameDistributionGameAndReturnArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the procedure `delete_game_distribution_game_and_return`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteProcedures`].
|
||||
pub trait delete_game_distribution_game_and_return {
|
||||
fn delete_game_distribution_game_and_return(&self, input: GameDistributionDeleteGameInput) {
|
||||
self.delete_game_distribution_game_and_return_then(input, |_, _| {});
|
||||
}
|
||||
|
||||
fn delete_game_distribution_game_and_return_then(
|
||||
&self,
|
||||
input: GameDistributionDeleteGameInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
impl delete_game_distribution_game_and_return for super::RemoteProcedures {
|
||||
fn delete_game_distribution_game_and_return_then(
|
||||
&self,
|
||||
input: GameDistributionDeleteGameInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, GameDistributionProcedureResult>(
|
||||
"delete_game_distribution_game_and_return",
|
||||
DeleteGameDistributionGameAndReturnArgs { input },
|
||||
__callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -31,10 +31,10 @@ pub trait fail_game_distribution_upload_and_return {
|
||||
input: GameDistributionFailUploadInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,10 +44,10 @@ impl fail_game_distribution_upload_and_return for super::RemoteProcedures {
|
||||
input: GameDistributionFailUploadInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, GameDistributionProcedureResult>(
|
||||
|
||||
+4
@@ -8,6 +8,10 @@ use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
#[sats(crate = __lib)]
|
||||
pub struct GameDistributionAdminGameListInput {
|
||||
pub limit: u32,
|
||||
pub keyword: Option<String>,
|
||||
pub owner_user_id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub cursor: Option<String>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for GameDistributionAdminGameListInput {
|
||||
|
||||
+1
@@ -12,6 +12,7 @@ pub struct GameDistributionAdminGameListResult {
|
||||
pub ok: bool,
|
||||
pub games: Vec<GameDistributionAdminGameSnapshot>,
|
||||
pub error_message: Option<String>,
|
||||
pub next_cursor: Option<String>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for GameDistributionAdminGameListResult {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user