feat: prop moving.
This commit is contained in:
parent
a2841c413d
commit
6e637a29cd
7 changed files with 688 additions and 56 deletions
|
|
@ -898,6 +898,12 @@ pub struct LooseProp {
|
|||
pub prop_name: String,
|
||||
/// Asset path for rendering (JOINed from source prop).
|
||||
pub prop_asset_path: String,
|
||||
/// If true, only moderators can move/scale/pickup this prop.
|
||||
#[serde(default)]
|
||||
pub is_locked: bool,
|
||||
/// User ID of the moderator who locked this prop.
|
||||
#[serde(default)]
|
||||
pub locked_by: Option<Uuid>,
|
||||
}
|
||||
|
||||
/// A server-wide prop (global library).
|
||||
|
|
|
|||
|
|
@ -51,7 +51,9 @@ pub async fn list_channel_loose_props<'e>(
|
|||
lp.expires_at,
|
||||
lp.created_at,
|
||||
COALESCE(sp.name, rp.name) as prop_name,
|
||||
COALESCE(sp.asset_path, rp.asset_path) as prop_asset_path
|
||||
COALESCE(sp.asset_path, rp.asset_path) as prop_asset_path,
|
||||
lp.is_locked,
|
||||
lp.locked_by
|
||||
FROM scene.loose_props lp
|
||||
LEFT JOIN server.props sp ON lp.server_prop_id = sp.id
|
||||
LEFT JOIN realm.props rp ON lp.realm_prop_id = rp.id
|
||||
|
|
@ -236,6 +238,8 @@ pub async fn drop_prop_to_canvas<'e>(
|
|||
created_at,
|
||||
prop_name,
|
||||
prop_asset_path,
|
||||
is_locked: false,
|
||||
locked_by: None,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
|
|
@ -364,7 +368,9 @@ pub async fn update_loose_prop_scale<'e>(
|
|||
scale,
|
||||
dropped_by,
|
||||
expires_at,
|
||||
created_at
|
||||
created_at,
|
||||
is_locked,
|
||||
locked_by
|
||||
)
|
||||
SELECT
|
||||
u.id,
|
||||
|
|
@ -378,7 +384,9 @@ pub async fn update_loose_prop_scale<'e>(
|
|||
u.expires_at,
|
||||
u.created_at,
|
||||
COALESCE(sp.name, rp.name) as prop_name,
|
||||
COALESCE(sp.asset_path, rp.asset_path) as prop_asset_path
|
||||
COALESCE(sp.asset_path, rp.asset_path) as prop_asset_path,
|
||||
u.is_locked,
|
||||
u.locked_by
|
||||
FROM updated u
|
||||
LEFT JOIN server.props sp ON u.server_prop_id = sp.id
|
||||
LEFT JOIN realm.props rp ON u.realm_prop_id = rp.id
|
||||
|
|
@ -412,7 +420,9 @@ pub async fn get_loose_prop_by_id<'e>(
|
|||
lp.expires_at,
|
||||
lp.created_at,
|
||||
COALESCE(sp.name, rp.name) as prop_name,
|
||||
COALESCE(sp.asset_path, rp.asset_path) as prop_asset_path
|
||||
COALESCE(sp.asset_path, rp.asset_path) as prop_asset_path,
|
||||
lp.is_locked,
|
||||
lp.locked_by
|
||||
FROM scene.loose_props lp
|
||||
LEFT JOIN server.props sp ON lp.server_prop_id = sp.id
|
||||
LEFT JOIN realm.props rp ON lp.realm_prop_id = rp.id
|
||||
|
|
@ -427,6 +437,174 @@ pub async fn get_loose_prop_by_id<'e>(
|
|||
Ok(prop)
|
||||
}
|
||||
|
||||
/// Move a loose prop to a new position.
|
||||
pub async fn move_loose_prop<'e>(
|
||||
executor: impl PgExecutor<'e>,
|
||||
loose_prop_id: Uuid,
|
||||
x: f64,
|
||||
y: f64,
|
||||
) -> Result<LooseProp, AppError> {
|
||||
let prop = sqlx::query_as::<_, LooseProp>(
|
||||
r#"
|
||||
WITH updated AS (
|
||||
UPDATE scene.loose_props
|
||||
SET position = public.make_virtual_point($2::real, $3::real)
|
||||
WHERE id = $1
|
||||
AND (expires_at IS NULL OR expires_at > now())
|
||||
RETURNING
|
||||
id,
|
||||
instance_id as channel_id,
|
||||
server_prop_id,
|
||||
realm_prop_id,
|
||||
ST_X(position) as position_x,
|
||||
ST_Y(position) as position_y,
|
||||
scale,
|
||||
dropped_by,
|
||||
expires_at,
|
||||
created_at,
|
||||
is_locked,
|
||||
locked_by
|
||||
)
|
||||
SELECT
|
||||
u.id,
|
||||
u.channel_id,
|
||||
u.server_prop_id,
|
||||
u.realm_prop_id,
|
||||
u.position_x,
|
||||
u.position_y,
|
||||
u.scale,
|
||||
u.dropped_by,
|
||||
u.expires_at,
|
||||
u.created_at,
|
||||
COALESCE(sp.name, rp.name) as prop_name,
|
||||
COALESCE(sp.asset_path, rp.asset_path) as prop_asset_path,
|
||||
u.is_locked,
|
||||
u.locked_by
|
||||
FROM updated u
|
||||
LEFT JOIN server.props sp ON u.server_prop_id = sp.id
|
||||
LEFT JOIN realm.props rp ON u.realm_prop_id = rp.id
|
||||
"#,
|
||||
)
|
||||
.bind(loose_prop_id)
|
||||
.bind(x as f32)
|
||||
.bind(y as f32)
|
||||
.fetch_optional(executor)
|
||||
.await?
|
||||
.ok_or_else(|| AppError::NotFound("Loose prop not found or has expired".to_string()))?;
|
||||
|
||||
Ok(prop)
|
||||
}
|
||||
|
||||
/// Lock a loose prop (moderator only).
|
||||
pub async fn lock_loose_prop<'e>(
|
||||
executor: impl PgExecutor<'e>,
|
||||
loose_prop_id: Uuid,
|
||||
locked_by: Uuid,
|
||||
) -> Result<LooseProp, AppError> {
|
||||
let prop = sqlx::query_as::<_, LooseProp>(
|
||||
r#"
|
||||
WITH updated AS (
|
||||
UPDATE scene.loose_props
|
||||
SET is_locked = true, locked_by = $2
|
||||
WHERE id = $1
|
||||
AND (expires_at IS NULL OR expires_at > now())
|
||||
RETURNING
|
||||
id,
|
||||
instance_id as channel_id,
|
||||
server_prop_id,
|
||||
realm_prop_id,
|
||||
ST_X(position) as position_x,
|
||||
ST_Y(position) as position_y,
|
||||
scale,
|
||||
dropped_by,
|
||||
expires_at,
|
||||
created_at,
|
||||
is_locked,
|
||||
locked_by
|
||||
)
|
||||
SELECT
|
||||
u.id,
|
||||
u.channel_id,
|
||||
u.server_prop_id,
|
||||
u.realm_prop_id,
|
||||
u.position_x,
|
||||
u.position_y,
|
||||
u.scale,
|
||||
u.dropped_by,
|
||||
u.expires_at,
|
||||
u.created_at,
|
||||
COALESCE(sp.name, rp.name) as prop_name,
|
||||
COALESCE(sp.asset_path, rp.asset_path) as prop_asset_path,
|
||||
u.is_locked,
|
||||
u.locked_by
|
||||
FROM updated u
|
||||
LEFT JOIN server.props sp ON u.server_prop_id = sp.id
|
||||
LEFT JOIN realm.props rp ON u.realm_prop_id = rp.id
|
||||
"#,
|
||||
)
|
||||
.bind(loose_prop_id)
|
||||
.bind(locked_by)
|
||||
.fetch_optional(executor)
|
||||
.await?
|
||||
.ok_or_else(|| AppError::NotFound("Loose prop not found or has expired".to_string()))?;
|
||||
|
||||
Ok(prop)
|
||||
}
|
||||
|
||||
/// Unlock a loose prop (moderator only).
|
||||
pub async fn unlock_loose_prop<'e>(
|
||||
executor: impl PgExecutor<'e>,
|
||||
loose_prop_id: Uuid,
|
||||
) -> Result<LooseProp, AppError> {
|
||||
let prop = sqlx::query_as::<_, LooseProp>(
|
||||
r#"
|
||||
WITH updated AS (
|
||||
UPDATE scene.loose_props
|
||||
SET is_locked = false, locked_by = NULL
|
||||
WHERE id = $1
|
||||
AND (expires_at IS NULL OR expires_at > now())
|
||||
RETURNING
|
||||
id,
|
||||
instance_id as channel_id,
|
||||
server_prop_id,
|
||||
realm_prop_id,
|
||||
ST_X(position) as position_x,
|
||||
ST_Y(position) as position_y,
|
||||
scale,
|
||||
dropped_by,
|
||||
expires_at,
|
||||
created_at,
|
||||
is_locked,
|
||||
locked_by
|
||||
)
|
||||
SELECT
|
||||
u.id,
|
||||
u.channel_id,
|
||||
u.server_prop_id,
|
||||
u.realm_prop_id,
|
||||
u.position_x,
|
||||
u.position_y,
|
||||
u.scale,
|
||||
u.dropped_by,
|
||||
u.expires_at,
|
||||
u.created_at,
|
||||
COALESCE(sp.name, rp.name) as prop_name,
|
||||
COALESCE(sp.asset_path, rp.asset_path) as prop_asset_path,
|
||||
u.is_locked,
|
||||
u.locked_by
|
||||
FROM updated u
|
||||
LEFT JOIN server.props sp ON u.server_prop_id = sp.id
|
||||
LEFT JOIN realm.props rp ON u.realm_prop_id = rp.id
|
||||
"#,
|
||||
)
|
||||
.bind(loose_prop_id)
|
||||
.fetch_optional(executor)
|
||||
.await?
|
||||
.ok_or_else(|| AppError::NotFound("Loose prop not found or has expired".to_string()))?;
|
||||
|
||||
Ok(prop)
|
||||
}
|
||||
|
||||
/// Delete expired loose props.
|
||||
///
|
||||
/// Returns the number of props deleted.
|
||||
|
|
|
|||
|
|
@ -112,6 +112,28 @@ pub enum ClientMessage {
|
|||
/// New scale factor (0.1 - 10.0).
|
||||
scale: f32,
|
||||
},
|
||||
|
||||
/// Move a loose prop to a new position.
|
||||
MoveProp {
|
||||
/// The loose prop ID to move.
|
||||
loose_prop_id: Uuid,
|
||||
/// New X coordinate in scene space.
|
||||
x: f64,
|
||||
/// New Y coordinate in scene space.
|
||||
y: f64,
|
||||
},
|
||||
|
||||
/// Lock a loose prop (moderator only).
|
||||
LockProp {
|
||||
/// The loose prop ID to lock.
|
||||
loose_prop_id: Uuid,
|
||||
},
|
||||
|
||||
/// Unlock a loose prop (moderator only).
|
||||
UnlockProp {
|
||||
/// The loose prop ID to unlock.
|
||||
loose_prop_id: Uuid,
|
||||
},
|
||||
}
|
||||
|
||||
/// Server-to-client WebSocket messages.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue