feat: prop moving.

This commit is contained in:
Evan Carroll 2026-01-23 17:11:12 -06:00
parent a2841c413d
commit 6e637a29cd
7 changed files with 688 additions and 56 deletions

View file

@ -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.