Add MineGeo gameplay systems and interface
This commit is contained in:
100
supabase/migrations/20261009133431_discord_announcements.sql
Normal file
100
supabase/migrations/20261009133431_discord_announcements.sql
Normal file
@@ -0,0 +1,100 @@
|
||||
-- Durable, server-only announcements. One event per public map or shared party.
|
||||
create table public.discord_announcements (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
kind text not null check (kind in ('map','party')),
|
||||
target_id uuid not null,
|
||||
state text not null default 'pending' check (state in ('pending','sending','sent','skipped','failed')),
|
||||
attempts integer not null default 0 check (attempts>=0),
|
||||
next_attempt_at timestamptz not null default clock_timestamp(),
|
||||
lease_owner text,
|
||||
lease_until timestamptz,
|
||||
channel_id text,
|
||||
message_id text,
|
||||
last_error text,
|
||||
created_at timestamptz not null default clock_timestamp(),
|
||||
sent_at timestamptz,
|
||||
unique(kind,target_id)
|
||||
);
|
||||
create index discord_announcements_due on public.discord_announcements(next_attempt_at,created_at)
|
||||
where state in ('pending','sending');
|
||||
alter table public.discord_announcements enable row level security;
|
||||
revoke all on public.discord_announcements from public,anon,authenticated;
|
||||
grant all on public.discord_announcements to service_role;
|
||||
create policy "Server manages Discord delivery" on public.discord_announcements
|
||||
for all to service_role using (true) with check (true);
|
||||
|
||||
create function public.queue_new_map_discord() returns trigger
|
||||
language plpgsql security invoker set search_path='' as $$
|
||||
begin
|
||||
if new.state='published' and old.state<>'published' and old.revision_id is null
|
||||
and new.parent_dataset_id is null and new.imported_from_id is null then
|
||||
insert into public.discord_announcements(kind,target_id) values('map',new.id)
|
||||
on conflict(kind,target_id) do nothing;
|
||||
end if;
|
||||
return new;
|
||||
end; $$;
|
||||
create trigger queue_new_map_discord after update of state on public.datasets
|
||||
for each row execute function public.queue_new_map_discord();
|
||||
|
||||
create function public.publish_discord_party(p_room uuid,p_access_hash text) returns uuid
|
||||
language plpgsql security invoker set search_path='' as $$
|
||||
declare room public.multiplayer_rooms; event_id uuid; active integer;
|
||||
begin
|
||||
select * into room from public.multiplayer_rooms where id=p_room for update;
|
||||
if not found then raise exception 'Room not found'; end if;
|
||||
if not exists(select 1 from public.multiplayer_players where room_id=p_room
|
||||
and id=room.host_player_id and access_hash=p_access_hash and not has_left) then
|
||||
raise exception 'Only the room owner can publish this party';
|
||||
end if;
|
||||
if room.status<>'lobby' or room.created_at<clock_timestamp()-interval '24 hours' then
|
||||
raise exception 'Only open lobbies can be published';
|
||||
end if;
|
||||
select count(*) into active from public.multiplayer_players where room_id=p_room and not has_left;
|
||||
if active>=room.player_limit then raise exception 'This room is full'; end if;
|
||||
if not exists(select 1 from public.revisions r join public.datasets d on d.id=r.dataset_id
|
||||
where r.id=room.revision_id and d.state='published' and d.parent_dataset_id is null and d.imported_from_id is null) then
|
||||
raise exception 'Only parties on published maps can be shared on Discord';
|
||||
end if;
|
||||
insert into public.discord_announcements(kind,target_id) values('party',p_room)
|
||||
on conflict(kind,target_id) do nothing;
|
||||
select id into event_id from public.discord_announcements where kind='party' and target_id=p_room;
|
||||
return event_id;
|
||||
end; $$;
|
||||
|
||||
create function public.claim_discord_announcements(p_worker text,p_kinds text[],p_target uuid default null)
|
||||
returns setof public.discord_announcements language sql security invoker set search_path='' as $$
|
||||
with exhausted as (
|
||||
update public.discord_announcements set state='failed',lease_owner=null,lease_until=null,
|
||||
last_error='Discord delivery could not be confirmed. Check the channel before retrying.'
|
||||
where state='sending' and attempts>=8 and lease_until<clock_timestamp() returning id
|
||||
)
|
||||
update public.discord_announcements e set state='sending',attempts=e.attempts+1,
|
||||
lease_owner=p_worker,lease_until=clock_timestamp()+interval '3 minutes'
|
||||
where e.id in (
|
||||
select id from public.discord_announcements where kind=any(p_kinds)
|
||||
and (p_target is null or target_id=p_target) and state in ('pending','sending')
|
||||
and attempts<8 and next_attempt_at<=clock_timestamp()
|
||||
and (lease_until is null or lease_until<clock_timestamp())
|
||||
order by created_at,id limit 5 for update skip locked
|
||||
) returning e.*;
|
||||
$$;
|
||||
|
||||
create function public.retry_discord_announcements(p_reviewer uuid) returns integer
|
||||
language plpgsql security invoker set search_path='' as $$
|
||||
declare changed integer;
|
||||
begin
|
||||
if not exists(select 1 from public.profiles where id=p_reviewer and role='admin') then
|
||||
raise exception 'Admin permission required';
|
||||
end if;
|
||||
update public.discord_announcements set state='pending',attempts=0,next_attempt_at=clock_timestamp(),last_error=null,
|
||||
lease_owner=null,lease_until=null where state='failed' or (state='pending' and lease_owner is null)
|
||||
or (state='sending' and lease_until<clock_timestamp());
|
||||
get diagnostics changed=row_count;
|
||||
return changed;
|
||||
end; $$;
|
||||
|
||||
revoke all on function public.queue_new_map_discord(),public.publish_discord_party(uuid,text),
|
||||
public.claim_discord_announcements(text,text[],uuid),public.retry_discord_announcements(uuid)
|
||||
from public,anon,authenticated;
|
||||
grant execute on function public.queue_new_map_discord(),public.publish_discord_party(uuid,text),
|
||||
public.claim_discord_announcements(text,text[],uuid),public.retry_discord_announcements(uuid) to service_role;
|
||||
Reference in New Issue
Block a user