Files
Vantz Stockwell 651a35d4be
All checks were successful
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Successful in 39s
CI / integration (push) Successful in 22s
docs(reference): import Dune: Awakening server-manager references
Phase 2 references for the host-agent Dune adapter, moved out of volatile /tmp
into docs/reference-repos/ (per Commander). Three upstream projects, .git +
node_modules + compiled binaries stripped (16MB source). Nested AI-instruction
files (.claude/, CLAUDE.md) removed so they don't pollute Corrosion sessions.

- icehunter/    dune-admin (Go+React) — 4 control planes; SETUP_DOCKER.md is the
                closest analog to our agent's Dune docker control plane (compose
                lifecycle, docker logs, RabbitMQ-via-exec, dune Postgres schema)
- adainrivers/  Rust/Tauri desktop — SSH+k8s BattleGroup control, maintenance
                daemon, in-game admin console (Rust idiom reference)
- the4rchangel/ Node web UI replacing battlegroup.bat — matches the Commander's
                Hyper-V self-host path + game-config schema

See docs/reference-repos/README.md for the full index + how we use each.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 21:08:05 -04:00

14244 lines
568 KiB
PL/PgSQL

-- All routines in dune schema, concatenated. Generated by _export.sh
-- _add_item_delete_log(in_item_id bigint, in_inventory_id bigint, in_template_id text) -> void
-- oid: 58085 kind: FUNCTION category: player_persistence
CREATE OR REPLACE FUNCTION dune._add_item_delete_log(in_item_id bigint, in_inventory_id bigint, in_template_id text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
PERFORM _add_item_trace_log('delete_item', in_item_id, in_inventory_id, in_template_id);
END;
$function$
-- _add_item_trace_log(in_function_name dune.itemtrackingfunctiontype, in_item_locations dune.inventoryitemlocation[]) -> void
-- oid: 58086 kind: FUNCTION category: player_persistence
CREATE OR REPLACE FUNCTION dune._add_item_trace_log(in_function_name dune.itemtrackingfunctiontype, in_item_locations dune.inventoryitemlocation[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
IF coalesce(current_setting('dune.item_tracking_enabled', true)::BOOLEAN, false) IS FALSE THEN
return;
END IF;
INSERT INTO item_operations_staging_table (
function_name,
item_id,
account_id,
inventory_id,
template_id,
event_time,
position_index
)
SELECT
in_function_name,
(loc).item_id,
act.owner_account_id,
(loc).inventory_id,
NULL, -- template_id
now(),
(loc).position_index
FROM UNNEST(in_item_locations) AS loc
JOIN inventories inv ON inv.id = (loc).inventory_id
JOIN actors act ON act.id = inv.actor_id;
END;
$function$
-- _add_item_trace_log(in_function_name dune.itemtrackingfunctiontype, in_item_id bigint, in_inventory_id bigint, in_template_id text, in_position_index bigint) -> void
-- oid: 58087 kind: FUNCTION category: player_persistence
CREATE OR REPLACE FUNCTION dune._add_item_trace_log(in_function_name dune.itemtrackingfunctiontype, in_item_id bigint, in_inventory_id bigint, in_template_id text, in_position_index bigint DEFAULT NULL::bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
owner_account_id BIGINT;
BEGIN
IF coalesce(current_setting('dune.item_tracking_enabled', true)::BOOLEAN, false) IS FALSE THEN
return;
END IF;
-- get item owner's account id
SELECT act.owner_account_id
INTO owner_account_id
FROM inventories inv
JOIN actors act ON act.id = inv.actor_id
WHERE inv.id = in_inventory_id;
INSERT INTO item_operations_staging_table (
function_name,
item_id,
account_id,
inventory_id,
template_id,
event_time,
position_index
) VALUES (
in_function_name,
in_item_id,
owner_account_id,
in_inventory_id,
in_template_id,
now(),
in_position_index
);
END;
$function$
-- _building_validate_totem_owner_id(in_totem_owner_id bigint) -> bigint
-- oid: 58094 kind: FUNCTION category: building_blueprint
CREATE OR REPLACE FUNCTION dune._building_validate_totem_owner_id(in_totem_owner_id bigint)
RETURNS bigint
LANGUAGE sql
BEGIN ATOMIC
SELECT
CASE
WHEN (in_totem_owner_id = 0) THEN NULL::bigint
WHEN (EXISTS ( SELECT 1
FROM dune.fgl_entities
WHERE (fgl_entities.entity_id = _building_validate_totem_owner_id.in_totem_owner_id))) THEN in_totem_owner_id
ELSE NULL::bigint
END AS "case";
END
-- _character_transfer_allocate_id(kind dune._charactertransferentrykind, data jsonb) -> bigint
-- oid: 58095 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune._character_transfer_allocate_id(kind dune._charactertransferentrykind, data jsonb)
RETURNS bigint
LANGUAGE sql
IMMUTABLE
AS $function$
select case
when kind = 'acc' then nextval('encrypted_accounts_id_seq')
when kind = 'act' then nextval('actors_id_seq')
when kind = 'inv' then nextval('inventories_id_seq')
when kind = 'itm' then nextval('items_id_seq')
when kind = 'fgl' then nextval('character_transfer_fgl_entities_entity_id_seq')
when kind = 'bbp' then nextval('building_blueprints_id_seq')
when kind = 'VehicleModule' then nextval('vehicle_modules_id_seq')
when kind = 'Faction' then (select id from factions where "name" = data->>'name')
when kind = 'Tutorial' then (select id from tutorials where "name" = data->>'name')
when kind = 'Keystone' then (select id from specialization_keystones_map where "name" = data->>'name')
when kind = 'BaseBackup' then nextval('base_backups_id_seq')
when kind = 'TaxInvoice' then nextval('tax_invoice_id_seq')
when kind = 'DungeonCompletion' then nextval('dungeon_completion_completion_id_seq')
else null
end;
$function$
-- _character_transfer_create_data_table() -> void
-- oid: 58096 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune._character_transfer_create_data_table()
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
if not exists (
select 1 from pg_catalog.pg_tables
where schemaname=(select nspname from pg_catalog.pg_namespace where oid=pg_my_temp_schema())
and tablename = 'export_data'
) then
create temporary table if not exists pg_temp.export_data (
"id" BigInt DEFAULT NULL,
"transfer_id" BigSerial PRIMARY KEY NOT NULL,
"kind" _CharacterTransferEntryKind NOT NULL,
"data" JsonB NOT NULL
) on commit drop;
create index on pg_temp.export_data("id");
create index on pg_temp.export_data("kind");
end if;
alter sequence pg_temp.export_data_transfer_id_seq restart with 1;
truncate pg_temp.export_data;
end
$function$
-- _character_transfer_data_filter(id text, removed text[], VARIADIC refs dune._charactertransferdatafilterref[]) -> dune._charactertransferdatafilter
-- oid: 58097 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune._character_transfer_data_filter(id text, removed text[], VARIADIC refs dune._charactertransferdatafilterref[] DEFAULT '{}'::dune._charactertransferdatafilterref[])
RETURNS dune._charactertransferdatafilter
LANGUAGE sql
IMMUTABLE
AS $function$
select (id, removed, refs)::_CharacterTransferDataFilter;
$function$
-- _character_transfer_data_table_load(entries jsonb) -> void
-- oid: 58098 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune._character_transfer_data_table_load(entries jsonb)
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
with parsed as (
select
(entry->>'id')::BigInt as transfer_id,
(entry->>'kind')::_CharacterTransferEntryKind as kind,
(entry->'data') as data
from jsonb_array_elements(entries) as entry
)
insert into pg_temp.export_data("id", "transfer_id", "kind", "data")
select _character_transfer_allocate_id(kind, data) as id, transfer_id, kind, data
from parsed;
end
$function$
-- _character_transfer_data_table_save() -> jsonb
-- oid: 58099 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune._character_transfer_data_table_save()
RETURNS jsonb
LANGUAGE plpgsql
AS $function$
begin
return (select jsonb_agg(jsonb_build_object('id', transfer_id, 'kind', kind, 'data', data)) from pg_temp.export_data);
end
$function$
-- _character_transfer_ensure_player_is_owner_of_vbt_vehicle(in_vehicle_id bigint[]) -> void
-- oid: 58100 kind: FUNCTION category: vehicle
CREATE OR REPLACE FUNCTION dune._character_transfer_ensure_player_is_owner_of_vbt_vehicle(in_vehicle_id bigint[])
RETURNS void
LANGUAGE sql
AS $function$
update permission_actor_rank set "rank" = 1 where permission_actor_id = any(in_vehicle_id);
$function$
-- _character_transfer_get_filter(kind dune._charactertransferentrykind) -> dune._charactertransferdatafilter
-- oid: 58101 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune._character_transfer_get_filter(kind dune._charactertransferentrykind)
RETURNS dune._charactertransferdatafilter
LANGUAGE sql
IMMUTABLE
AS $function$
select case
when kind = 'acc' then _character_transfer_data_filter('id', '{user,funcom_id}')
when kind = 'act' then _character_transfer_data_filter('id', '{}', ('owner_account_id', 'acc', false))
when kind = 'inv' then _character_transfer_data_filter(
'id',
'{exchange_id,item_id}',
('actor_id', 'act', false),
('vehicle_module_id', 'VehicleModule', false)
)
when kind = 'itm' then _character_transfer_data_filter(
'id', '{}',
('inventory_id', 'inv', true)
)
when kind = 'fgl' then _character_transfer_data_filter(
'entity_id', '{}',
('actor_id', 'act', true)
)
when kind = 'bbp' then _character_transfer_data_filter(
'id', '{}',
('item_id', 'itm', true),
('player_id', 'act', false)
)
when kind = 'Faction' then _character_transfer_data_filter('id', '{}')
when kind = 'Tutorial' then _character_transfer_data_filter('id', '{}')
when kind = 'Keystone' then _character_transfer_data_filter('id', '{}')
when kind = 'Character' then _character_transfer_data_filter(
null, '{character_name}',
('account_id', 'acc', true),
('player_pawn_id', 'act', true),
('player_controller_id', 'act', true),
('player_state_id', 'act', true)
)
when kind = 'RespawnLocation' then _character_transfer_data_filter(
null, '{}',
('account_id', 'acc', true)
)
when kind = 'PlayerMarker' then _character_transfer_data_filter(null, '{}', ('player_id', 'act', true))
when kind = 'Marker' then _character_transfer_data_filter(null, '{}')
when kind = 'DialogueMetNpc' then _character_transfer_data_filter(null, '{}', ('player_id', 'act', true))
when kind = 'DialogueTakenNode' then _character_transfer_data_filter(null, '{}', ('player_id', 'act', true))
when kind = 'PlayerFaction' then _character_transfer_data_filter(
null, '{}',
('actor_id', 'act', true), ('faction_id', 'Faction', true)
)
when kind = 'PlayerFactionReputation' then _character_transfer_data_filter(
null, '{}',
('actor_id', 'act', true), ('faction_id', 'Faction', true)
)
when kind = 'ConsumedLore' then _character_transfer_data_filter(null, '{}', ('actor_id', 'act', true))
when kind = 'PlayerTutorial' then _character_transfer_data_filter(null, '{}', ('player_id', 'act', true), ('tutorial_id', 'Tutorial', true))
when kind = 'PurchasedKeystone' then _character_transfer_data_filter(null, '{}', ('player_id', 'act', true), ('keystone_id', 'Keystone', true))
when kind = 'SpecializationTracks' then _character_transfer_data_filter(null, '{}', ('player_id', 'act', true))
when kind = 'SpecializationRefund' then _character_transfer_data_filter(null, '{}', ('player_id', 'act', true))
when kind = 'BuildingFavorite' then _character_transfer_data_filter(null, '{}', ('account_id', 'acc', true))
when kind = 'BuildingProgression' then _character_transfer_data_filter(null, '{}', ('account_id', 'acc', true))
when kind = 'CommuninetPlayer' then _character_transfer_data_filter(null, '{}', ('account_id', 'acc', true))
when kind = 'CommuninetPlayerChannel' then _character_transfer_data_filter(null, '{}', ('account_id', 'acc', true))
when kind = 'JourneyStoryNode' then _character_transfer_data_filter(null, '{}', ('account_id', 'acc', true))
when kind = 'MapArea' then _character_transfer_data_filter(null, '{}', ('account_id', 'acc', true))
when kind = 'PlayerAccessCode' then _character_transfer_data_filter(null, '{}', ('account_id', 'acc', true))
when kind = 'PlayerTag' then _character_transfer_data_filter(null, '{}', ('account_id', 'acc', true))
when kind = 'ActorInventory' then _character_transfer_data_filter(null, '{}', ('inventory_id', 'inv', true))
when kind = 'Sinkchart' then _character_transfer_data_filter(null, '{}', ('item_id', 'itm', true))
when kind = 'BackupVehicle' then _character_transfer_data_filter(null, '{}', ('account_id', 'acc', true), ('vehicle_id', 'act', true))
when kind = 'RecoveredVehicle' then _character_transfer_data_filter(null, '{}', ('account_id', 'acc', true), ('vehicle_id', 'act', true))
when kind = 'Vehicle' then _character_transfer_data_filter(null, '{}', ('id', 'act', true))
when kind = 'VehicleModule' then _character_transfer_data_filter('id', '{}', ('vehicle_id', 'act', true))
when kind = 'VehicleModuleInventory' then _character_transfer_data_filter(null, '{}', ('inventory_id', 'inv', true))
when kind = 'ActorState' then _character_transfer_data_filter(null, '{}', ('actor_id', 'act', true))
when kind = 'PermissionActor' then _character_transfer_data_filter(null, '{}', ('actor_id', 'act', true))
when kind = 'PermissionActorRank' then _character_transfer_data_filter(null, '{}', ('permission_actor_id', 'act', true), ('player_id', 'act', true))
when kind = 'BuildingBlueprintInstance' then _character_transfer_data_filter(null, '{}', ('building_blueprint_id', 'bbp', true))
when kind = 'BuildingBlueprintPlaceable' then _character_transfer_data_filter(null, '{}', ('building_blueprint_id', 'bbp', true))
when kind = 'BuildingBlueprintPentashield' then _character_transfer_data_filter(null, '{}', ('building_blueprint_id', 'bbp', true))
when kind = 'Building' then _character_transfer_data_filter(null, '{}', ('id', 'act', true))
when kind = 'BuildingInstance' then _character_transfer_data_filter(null, '{}', ('building_id', 'act', true), ('owner_entity_id', 'fgl', false))
when kind = 'Placeable' then _character_transfer_data_filter(null, '{}', ('id', 'act', true), ('owner_entity_id', 'fgl', false))
when kind = 'BaseBackup' then _character_transfer_data_filter('id', '{}', ('player_id', 'act', true))
when kind = 'BaseBackupLinkedActor' then _character_transfer_data_filter(null, '{}', ('id', 'BaseBackup', true), ('actor_id', 'act', true))
when kind = 'LandclaimSegment' then _character_transfer_data_filter(null, '{}', ('totem_id', 'act', true))
when kind = 'TaxInvoice' then _character_transfer_data_filter('id', '{}', ('totem_id', 'act', true))
when kind = 'PlayerVirtualCurrencyBalance' then _character_transfer_data_filter(null, '{}', ('player_controller_id', 'act', true))
when kind = 'DungeonCompletion' then _character_transfer_data_filter('completion_id', '{}')
when kind = 'DungeonCompletionPlayer' then _character_transfer_data_filter(null, '{}', ('player_id', 'act', true), ('completion_id', 'DungeonCompletion', true))
when kind = 'Totem' then _character_transfer_data_filter(null, '{}', ('id', 'act', true))
when kind = 'LandsraadHouseRewards' then _character_transfer_data_filter(null, '{}', ('player_id', 'act', true))
end;
$function$
-- _character_transfer_get_patches_checksum() -> text
-- oid: 58102 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune._character_transfer_get_patches_checksum()
RETURNS text
LANGUAGE plpgsql
AS $function$
begin
return (SELECT md5(coalesce(string_agg("name", ',' ORDER BY "name"), '')) FROM applied_patches);
end
$function$
-- _character_transfer_pre_export_validation(in_fls_id text) -> TABLE(out_acc_id bigint, out_funcom_id text, out_player_controller_id bigint, out_player_pawn_id bigint)
-- oid: 58103 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune._character_transfer_pre_export_validation(in_fls_id text)
RETURNS TABLE(out_acc_id bigint, out_funcom_id text, out_player_controller_id bigint, out_player_pawn_id bigint)
LANGUAGE plpgsql
AS $function$
DECLARE
v_acc_id BigInt;
v_funcom_id Text;
v_player_controller_id BigInt;
v_player_pawn_id BigInt;
BEGIN
if not (select is_player_offline(in_fls_id)) then
raise exception 'sbRF3$ - player_online: Player % must be Offline', in_fls_id;
end if;
v_acc_id := (select id from accounts where "user" = in_fls_id);
IF v_acc_id is null THEN
RAISE EXCEPTION 'sbFV2$ - unknown_fls_id: FLS ID % not found', in_fls_id;
END IF;
v_funcom_id := (select funcom_id from accounts where "user" = in_fls_id);
IF v_funcom_id is null THEN
RAISE EXCEPTION 'sb9X3$ - missing_funcom_id: Player % does not have a funcomId', in_fls_id;
END IF;
select into v_player_controller_id, v_player_pawn_id
player_controller_id, player_pawn_id from player_state where account_id=v_acc_id;
if v_player_controller_id is null then
raise exception 'sbH84$ - missing_controller: Player % does not have a controller', in_fls_id;
end if;
if v_player_pawn_id is null then
raise exception 'sbHF3$ - missing_character: Player % does not have a character', in_fls_id;
end if;
return query select v_acc_id, v_funcom_id, v_player_controller_id, v_player_pawn_id;
END;
$function$
-- _character_transfer_property_not_exported_is_expected(path text) -> boolean
-- oid: 58104 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune._character_transfer_property_not_exported_is_expected(path text)
RETURNS boolean
LANGUAGE sql
IMMUTABLE
AS $function$
select (path = ANY(ARRAY[
'.properties.BP_DunePlayerCharacter_C.m_CurrentVehicleId',
'.properties.WeaponActorComponent.m_FavoriteWeaponItemDatabaseId',
'.properties.ContractsCoordinatorComponent.m_TrackedContractItemUid',
'.components.FItemCraftingComponent.*.RequestsQueue.*.InstigatorActorId',
'.stats.FSinkchartsStats.*.CreatorPlayerId',
'.components.FItemCraftingComponent.*.RequestsQueue.*.IngredientAllocations.*.ItemAllocNodes.*.AllocatedItems.*.ItemUniqueId', -- TODO [DA-4712]: Remove once this item loss is fixed
'.stats.FBuildingBlueprintItemStats.*.PlayerBlueprintId', -- TODO [DA-4721]: Remove once this item loss is fixed
'.properties.BP_TransportOrnithopter_CHOAM_C.m_HarnessedVehicleId',
'.stats.FReferenceItemStats.*.ReferenceDatabaseId', -- These often point to items that no longer exist
'.components.FTotemLandclaimComponent.*.m_PendingStakingUnitsEntityIds.*', -- Staking units are not transferred with the base backup. This is missing a cleanup
'.components.FTotemLandclaimComponent.*.m_PendingVerticalStakingUnitsEntityIds.*' -- Staking units are not transferred with the base backup. This is missing a cleanup
]::text[]));
$function$
-- _character_transfer_replace_local_id_with_transfer_id(data text, path text) -> text
-- oid: 58105 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune._character_transfer_replace_local_id_with_transfer_id(data text, path text)
RETURNS text
LANGUAGE plpgsql
AS $function$
declare
v_kind _CharacterTransferEntryKind;
v_raw_kind Text;
v_id BigInt;
v_transfer_id BigInt;
begin
v_raw_kind = substring(data from 3 for 3);
begin
v_kind := v_raw_kind::_CharacterTransferEntryKind;
exception
when invalid_text_representation then
raise exception 'sbPH2$ - Invalid transfer id in data "%" at "%", unknown kind: %', data, path, v_raw_kind;
end;
v_id := (substring(data from 7))::BigInt;
if v_id = 0 then
return format('!!%s@0', v_kind::text);
end if;
if v_id < 0 and v_id >= -2147483648 then
v_id := v_id + 4294967296;
end if;
v_transfer_id := (select transfer_id from pg_temp.export_data where id=v_id and kind=v_kind);
if v_transfer_id is null then
if _character_transfer_property_not_exported_is_expected(path) then
return format('!!%s@0', v_kind::text);
else
raise exception 'sbQ73$ - Id % by % was not exported', data, path;
end if;
end if;
return format('!!%s@%s', v_kind::text, v_transfer_id::text);
end
$function$
-- _character_transfer_replace_local_id_with_transfer_id_in_json(data jsonb, path text) -> jsonb
-- oid: 58106 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune._character_transfer_replace_local_id_with_transfer_id_in_json(data jsonb, path text)
RETURNS jsonb
LANGUAGE sql
AS $function$
select case
when data is json array then
(select coalesce(jsonb_agg(_character_transfer_replace_local_id_with_transfer_id_in_json(
element, path || '.*'
)), '[]'::jsonb) from jsonb_array_elements(data) as element)
when data is json object then
(select coalesce(jsonb_object_agg(key, _character_transfer_replace_local_id_with_transfer_id_in_json(
value, path || '.' || key
)), '{}'::jsonb) from jsonb_each(data))
when data is json scalar and jsonb_typeof(data) = 'string' and (data #>> '{}') like '!!___#%' then
(select to_jsonb(_character_transfer_replace_local_id_with_transfer_id(
data #>> '{}', path
)))
else
data
end;
$function$
-- _character_transfer_replace_transfer_id_with_local_id(data text, path text) -> text
-- oid: 58107 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune._character_transfer_replace_transfer_id_with_local_id(data text, path text)
RETURNS text
LANGUAGE plpgsql
AS $function$
declare
v_kind _CharacterTransferEntryKind;
v_id BigInt;
v_transfer_id BigInt;
begin
if substring(data from 6 for 1) != '@' then
raise exception 'sbFM3$ - Invalid transfer id % at %: expected @ separator', data, path;
end if;
v_kind := substring(data from 3 for 3)::_CharacterTransferEntryKind;
v_transfer_id := (substring(data from 7))::BigInt;
if v_transfer_id = 0 then
return format('!!%s#0', v_kind::text);
end if;
v_id := (select id from pg_temp.export_data where transfer_id=v_transfer_id and kind=v_kind);
if v_id is null then
raise exception 'sbQ64$ - Transfer id % at % was not imported', data, path;
end if;
return format('!!%s#%s', v_kind::text, v_id::text);
end
$function$
-- _character_transfer_replace_transfer_id_with_local_id_in_json(data jsonb, path text) -> jsonb
-- oid: 58108 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune._character_transfer_replace_transfer_id_with_local_id_in_json(data jsonb, path text)
RETURNS jsonb
LANGUAGE plpgsql
AS $function$
declare
v_id_str Text;
begin
if data is json array then
return (
select coalesce(jsonb_agg(_character_transfer_replace_transfer_id_with_local_id_in_json(
element, path || '.*'
)), '[]'::jsonb) from jsonb_array_elements(data) as element
);
end if;
if data is json object then
return (
select coalesce(jsonb_object_agg(key, _character_transfer_replace_transfer_id_with_local_id_in_json(
value, path || '.' || key
)), '{}'::jsonb) from jsonb_each(data)
);
end if;
if data is json scalar then
if jsonb_typeof(data) = 'string' then
v_id_str := (data #>> '{}');
if v_id_str like '!!___#%' then
raise exception 'sbJ34$ - Non-transfer id % is found at %', v_id_str, path;
end if;
if v_id_str like '!!___@%' then
return (select to_jsonb(_character_transfer_replace_transfer_id_with_local_id(v_id_str, path)));
end if;
if v_id_str = '!!@0' then
raise exception 'sb5G3$ - Transfer id missing kind marker at %: %', path, v_id_str;
end if;
end if;
end if;
return data;
end
$function$
-- _character_transfer_store_in_world_owned_vehicles_into_recovery(in_player_id bigint) -> void
-- oid: 58109 kind: FUNCTION category: vehicle
CREATE OR REPLACE FUNCTION dune._character_transfer_store_in_world_owned_vehicles_into_recovery(in_player_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
to_store RECORD;
BEGIN
-- remove any vehicles in 'Travel' state, so that they get re-added as recovered vehicles
DELETE FROM actor_state ast
WHERE ast.state = 'Travel'
AND ast.actor_id IN (
SELECT v.id
FROM actors a
INNER JOIN vehicles v ON a.id = v.id
INNER JOIN permission_actor p ON a.id = p.actor_id
INNER JOIN permission_actor_rank r ON p.actor_id = r.permission_actor_id
WHERE r.player_id = in_player_id
AND r.rank = 1::smallint
);
FOR to_store IN
SELECT v.id AS vehicle_id
FROM actors a
INNER JOIN vehicles v ON a.id = v.id
INNER JOIN permission_actor p ON a.id = p.actor_id
INNER JOIN permission_actor_rank r ON p.actor_id = r.permission_actor_id
WHERE r.player_id = in_player_id
AND r.rank = 1::smallint
AND NOT EXISTS
(
SELECT 1 FROM actor_state
WHERE actor_state.actor_id = v.id
AND actor_state.state IS DISTINCT FROM 'Default'
)
LOOP
-- note: storing hardcoded chassis durability because its not available from pure database :(
-- this will only be shown incorrectly in UI though, the spawned vehicle will get the correct value
PERFORM store_recovered_vehicle(to_store.vehicle_id, 1.0, 'None', true);
-- we delete the permissions for this vehicle so that they don't get exported and cause duplicated key failures
-- when the player tries to recover their vehicle on the target battlegroup
-- note: the server will destroy the vehicle as soon as it gets the pg_notify emitted from the store_recovered_vehicle call above, so this would happen regardless
DELETE FROM permission_actor pa WHERE pa.actor_id = to_store.vehicle_id;
END LOOP;
END
$function$
-- _character_transfer_top_level_export(in_kind dune._charactertransferentrykind, data jsonb) -> jsonb
-- oid: 58110 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune._character_transfer_top_level_export(in_kind dune._charactertransferentrykind, data jsonb)
RETURNS jsonb
LANGUAGE plpgsql
AS $function$
declare
v_id BigInt;
v_transfer_id BigInt;
v_ref _CharacterTransferDataFilterRef;
v_filter _CharacterTransferDataFilter;
begin
v_filter := _character_transfer_get_filter(in_kind);
data := data - (v_filter).removed;
if (v_filter).id is not null then
data := data - (v_filter).id;
end if;
foreach v_ref in array (v_filter).refs loop
v_id := (data->>((v_ref).key))::BigInt;
if v_id is null then
if (v_ref).required then
raise exception 'sbP23$ - Required reference % not found in %', v_ref, data;
else
continue;
end if;
end if;
v_transfer_id := (select transfer_id from pg_temp.export_data where kind=(v_ref).kind and id=v_id);
if v_transfer_id is null then
raise exception 'sbJG2$ - Id % in % not mapped into transfer id of kind %. Id exists as kind: %',
v_id, data, (v_ref).kind, (select kind from pg_temp.export_data where id=v_id);
end if;
data := jsonb_set(data, array[(v_ref).key], to_jsonb(v_transfer_id));
end loop;
return data;
end
$function$
-- _character_transfer_top_level_import(in_kind dune._charactertransferentrykind, data jsonb, in_id bigint) -> jsonb
-- oid: 58111 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune._character_transfer_top_level_import(in_kind dune._charactertransferentrykind, data jsonb, in_id bigint)
RETURNS jsonb
LANGUAGE plpgsql
AS $function$
declare
v_ref_transfer_id BigInt;
v_ref_id BigInt;
v_ref _CharacterTransferDataFilterRef;
v_filter _CharacterTransferDataFilter;
begin
v_filter := _character_transfer_get_filter(in_kind);
if (v_filter).id is null then
if in_id is not null then
raise exception 'sbGM2$ - Transfer logic error: not-null in_id for secondary kind: %, data: %', in_kind, data;
end if;
else
if in_id is null then
raise exception 'sb8V2$ - Transfer logic error: null in_id for primary kind: %, data: %', in_kind, data;
end if;
data := data || jsonb_build_object((v_filter).id, in_id);
end if;
foreach v_ref in array (v_filter).refs loop
v_ref_transfer_id := (data->>((v_ref).key))::BigInt;
if v_ref_transfer_id is null then
if (v_ref).required then
raise exception 'sb2C2$ - Missing reference % in import of kind %: %', (v_ref).key, in_kind, data;
else
continue;
end if;
end if;
v_ref_id := (select to_jsonb(id) from pg_temp.export_data where kind=(v_ref).kind and transfer_id=v_ref_transfer_id);
if v_ref_id is null then
raise exception 'sb3W3$ - Unknown reference %s with transfer id % in import for kind %, reference id %: %', (v_ref).key, v_ref_transfer_id, in_kind, v_ref_id, data;
end if;
data := jsonb_set(data, array[(v_ref).key], to_jsonb(v_ref_id));
end loop;
return data;
end
$function$
-- _placeable_validate_totem_owner_id(in_totem_owner_id bigint) -> bigint
-- oid: 58112 kind: FUNCTION category: building_blueprint
CREATE OR REPLACE FUNCTION dune._placeable_validate_totem_owner_id(in_totem_owner_id bigint)
RETURNS bigint
LANGUAGE sql
BEGIN ATOMIC
SELECT
CASE
WHEN (in_totem_owner_id = 0) THEN NULL::bigint
WHEN (EXISTS ( SELECT 1
FROM dune.fgl_entities
WHERE (fgl_entities.entity_id = _placeable_validate_totem_owner_id.in_totem_owner_id))) THEN in_totem_owner_id
ELSE NULL::bigint
END AS "case";
END
-- _user_data_encryption_initially_encrypt_existing_data() -> void
-- oid: 58113 kind: FUNCTION category: encryption
CREATE OR REPLACE FUNCTION dune._user_data_encryption_initially_encrypt_existing_data()
RETURNS void
LANGUAGE sql
AS $function$
update encrypted_accounts set encrypted_funcom_id=encrypt_user_data(convert_from(encrypted_funcom_id, 'utf8'));
update encrypted_player_state set
encrypted_character_name=encrypt_user_data(convert_from(encrypted_character_name, 'utf8'));
$function$
-- _user_data_encryption_setup_enabled(key_hash bytea) -> void
-- oid: 58114 kind: FUNCTION category: encryption
CREATE OR REPLACE FUNCTION dune._user_data_encryption_setup_enabled(key_hash bytea)
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
if key_hash is null then
raise exception 'User-data encryption requested but the server does not have the encryption key set';
end if;
if (select get_stored_user_data_encryption_taint_xmax()) is not null then
-- should have been filtered by the main setup function
raise exception 'Trying to enable encryption on a tainted data';
end if;
execute format($x$
create or replace function get_stored_user_data_encryption_key_hash() returns bytea immutable
as $y$select '%s'::bytea;$y$ language sql;
$x$, key_hash::text);
-- the data is encrypted and we have the key
create or replace function get_stored_user_data_encryption_status() returns UserDataEncryptionStatus immutable
as $x$select 'Enabled'::UserDataEncryptionStatus;$x$ language sql;
-- We may bake the key into the function code but then dumping the functions will reveal the key
create or replace function encrypt_user_data(in_data text) returns bytea immutable as $x$
select ext.encrypt(convert_to(in_data, 'utf8'), current_setting('funcom.user_data_encryption_key')::bytea, 'aes')::bytea;
$x$ language sql;
create or replace function decrypt_user_data(in_encrypted_data bytea) returns text immutable as $x$
select convert_from(
ext.decrypt(in_encrypted_data, current_setting('funcom.user_data_encryption_key')::bytea, 'aes'), 'utf8'
);
$x$ language sql;
end
$function$
-- _user_data_encryption_setup_tainted() -> void
-- oid: 58115 kind: FUNCTION category: encryption
CREATE OR REPLACE FUNCTION dune._user_data_encryption_setup_tainted()
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
-- we not replacing get_stored_user_data_encryption_key_hash()
if (select get_stored_user_data_encryption_key_hash()) is null then
-- should have been filtered by the main setup function
raise exception 'Tainted but the data is not encrypted in the first place';
end if;
execute format($x$
create or replace function get_stored_user_data_encryption_taint_xmax() returns int8 immutable
as 'select %s;' language sql;
$x$, pg_current_xact_id());
-- the data is encrypted but we don't have the key (or don't want to)
create or replace function get_stored_user_data_encryption_status() returns UserDataEncryptionStatus immutable
as $x$select 'Tainted'::UserDataEncryptionStatus;$x$ language sql;
create or replace function encrypt_user_data(in_data text) returns bytea immutable
as $x$select convert_to(in_data, 'utf8')$x$ language sql;
-- We may use the taint xmax to differentiate between encrypted/unencrypted data for that we would have to pass the
-- xmin into the function (which is xid and not xid8)
create or replace function decrypt_user_data(in_encrypted_data bytea) returns text immutable
as $x$select encode(in_encrypted_data, 'hex');$x$ language sql;
end
$function$
-- accept_guild_invite(in_invite_id bigint, in_role_id smallint, in_max_guild_count_per_player integer, in_max_members_per_guild integer, in_neutral_faction_id smallint) -> void
-- oid: 58116 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.accept_guild_invite(in_invite_id bigint, in_role_id smallint, in_max_guild_count_per_player integer, in_max_members_per_guild integer, in_neutral_faction_id smallint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
member_count INTEGER := 0;
player_id BIGINT := 0;
found_guild_id BIGINT := 0;
BEGIN
PERFORM guilds_get_exclusive_operation_lock();
-- check if invite exists
SELECT guild_invites.player_id, guild_invites.guild_id FROM guild_invites WHERE invite_id = in_invite_id INTO player_id, found_guild_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Trying to accept non exiting invite %.', in_invite_id;
END IF;
-- delete invite
DELETE FROM guild_invites WHERE invite_id = in_invite_id;
SELECT INTO member_count COUNT(*) FROM guild_members where guild_members.guild_id = found_guild_id;
-- check if we've reached guild member limit
IF member_count >= in_max_members_per_guild THEN
RAISE EXCEPTION 'Cannot insert more than % members per guild.', in_max_members_per_guild;
END IF;
-- add member
PERFORM add_guild_member(player_id, found_guild_id, in_role_id, in_max_guild_count_per_player, in_max_members_per_guild, in_neutral_faction_id);
END
$function$
-- accept_party_invite(in_invite_id bigint, in_platform_session_id text, in_max_party_member_count integer) -> dune.partyacceptinviteresult
-- oid: 58117 kind: FUNCTION category: party
CREATE OR REPLACE FUNCTION dune.accept_party_invite(in_invite_id bigint, in_platform_session_id text, in_max_party_member_count integer)
RETURNS dune.partyacceptinviteresult
LANGUAGE plpgsql
AS $function$
DECLARE
out_player_id BIGINT;
out_sender_id BIGINT;
out_party_id BIGINT;
out_player_platform_name TEXT;
out_player_platform_id TEXT;
out_sender_platform_name TEXT;
out_sender_platform_session_id TEXT;
out_accept_error PartyAcceptInviteResult DEFAULT 'Success'::PartyAcceptInviteResult;
BEGIN
PERFORM parties_get_exclusive_operation_lock();
-- check if invite exists
SELECT party_id, player_id, sender_player_id, sender_platform_name, sender_platform_session_id FROM party_invites
WHERE invite_id = in_invite_id INTO out_party_id, out_player_id, out_sender_id, out_sender_platform_name, out_sender_platform_session_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Trying to accept non exiting party invite %.', in_invite_id;
out_accept_error = 'NonExistingInvite'::PartyAcceptInviteResult;
END IF;
-- query receiver platform data
SELECT acc.platform_name, acc.platform_id INTO out_player_platform_name, out_player_platform_id
FROM accounts acc LEFT JOIN player_state ps ON acc.id=ps.account_id
WHERE ps.player_controller_id = out_player_id;
IF out_party_id IS NULL THEN
PERFORM internal_create_party(in_invite_id, out_sender_id, out_sender_platform_session_id, out_sender_platform_name,
out_player_id, in_platform_session_id, out_player_platform_name);
ELSE
out_accept_error := internal_add_party_member(in_invite_id, out_party_id, out_player_id, in_platform_session_id, out_player_platform_name, in_max_party_member_count);
END IF;
-- Delete any sent or reiceived invites from the player who's accepting the invitation
DELETE FROM party_invites WHERE sender_player_id = out_player_id OR player_id = out_player_id;
RETURN out_accept_error;
END
$function$
-- add_actor_audit(in_id bigint, in_class text) -> void
-- oid: 58118 kind: FUNCTION category: anticheat
CREATE OR REPLACE FUNCTION dune.add_actor_audit(in_id bigint, in_class text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO actor_audit("id", "class") VALUES(in_id, in_class) ON CONFLICT(id) DO NOTHING;
END
$function$
-- add_event_log_data(in_game_event_owner bigint, in_universe_time bigint, in_map_name text, in_partition_id bigint, in_event_type integer, in_x_location double precision, in_y_location double precision, in_z_location double precision, in_is_player_facing boolean, in_custom_data text) -> void
-- oid: 58119 kind: FUNCTION category: event_log
CREATE OR REPLACE FUNCTION dune.add_event_log_data(in_game_event_owner bigint, in_universe_time bigint, in_map_name text, in_partition_id bigint, in_event_type integer, in_x_location double precision, in_y_location double precision, in_z_location double precision, in_is_player_facing boolean, in_custom_data text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO game_events(actor_id, universe_time, map, partition_id, event_type, x, y, z, custom_data, player_facing_event) VALUES(in_game_event_owner, to_timestamp(in_universe_time), in_map_name, in_partition_id, in_event_type, in_x_location, in_y_location, in_z_location, in_custom_data::JsonB, in_is_player_facing);
END
$function$
-- add_event_log_data_batched(in_data dune.eventlogbulkentrydata[]) -> void
-- oid: 58120 kind: FUNCTION category: event_log
CREATE OR REPLACE FUNCTION dune.add_event_log_data_batched(in_data dune.eventlogbulkentrydata[])
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
data_entry record = NULL;
BEGIN
FOREACH data_entry IN ARRAY in_data
LOOP
PERFORM add_event_log_data(data_entry.game_event_owner, data_entry.universe_time, data_entry.map_name, data_entry.partition_id, data_entry.event_type,
data_entry.x_location, data_entry.y_location, data_entry.z_location, data_entry.player_facing_event, data_entry.custom_args);
END LOOP;
END $function$
-- add_guild_invite(in_player_id bigint, in_guild_id bigint, in_sender_player_id bigint, in_invite_sent_timespan bigint, in_max_guild_invites_per_guild integer) -> void
-- oid: 58121 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.add_guild_invite(in_player_id bigint, in_guild_id bigint, in_sender_player_id bigint, in_invite_sent_timespan bigint, in_max_guild_invites_per_guild integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
invite_count INTEGER := 0;
out_invite_id INTEGER;
out_guild_name TEXT;
out_guild_description TEXT;
out_player_name TEXT;
out_sender_name TEXT;
BEGIN
PERFORM guilds_get_exclusive_operation_lock();
-- check if guild exists
SELECT guild_name, guild_description INTO out_guild_name, out_guild_description FROM guilds WHERE guild_id = in_guild_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Trying to add invite to non existing guild %.', in_guild_id;
END IF;
-- check if we've reached the invite limit
SELECT INTO invite_count COUNT(*) FROM guild_invites WHERE guild_invites.guild_id = in_guild_id;
IF invite_count >= in_max_guild_invites_per_guild THEN
RAISE EXCEPTION 'Cannot insert more than % guild invites per guild.', in_max_guild_invites_per_guild;
END IF;
-- check if this player already has an invite to this guild
IF EXISTS (SELECT FROM guild_invites WHERE guild_id = in_guild_id AND player_id = in_player_id) THEN
RAISE EXCEPTION 'Trying to add invite to a player that already has an invite to this guild.';
END IF;
-- add invite
INSERT INTO guild_invites("guild_id", "player_id", "sender_player_id", "invite_sent_timespan") VALUES(in_guild_id, in_player_id, in_sender_player_id, in_invite_sent_timespan) RETURNING "invite_id" INTO out_invite_id;
SELECT player_state.character_name INTO out_player_name
FROM player_state
WHERE player_state.player_controller_id = in_player_id;
SELECT player_state.character_name INTO out_sender_name
FROM player_state
WHERE player_state.player_controller_id = in_sender_player_id;
PERFORM pg_notify('guild_notify_channel', format(
'add_invite#{"InviteId" : %s, "PlayerId" : %s , "GuildId" : %s, "GuildName" : "%s", "PlayerName" : "%s", "GuildDescription" : "%s", "SenderPlayerId" : %s, "SenderPlayerName" : "%s", "InviteSentUniverseTime" : %s}',
out_invite_id, in_player_id, in_guild_id, out_guild_name, out_player_name, out_guild_description, in_sender_player_id, out_sender_name, in_invite_sent_timespan));
END
$function$
-- add_guild_member(in_player_id bigint, in_guild_id bigint, in_role_id smallint, in_max_guild_count_per_player integer, in_max_members_per_guild integer, in_neutral_faction_id smallint) -> void
-- oid: 58122 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.add_guild_member(in_player_id bigint, in_guild_id bigint, in_role_id smallint, in_max_guild_count_per_player integer, in_max_members_per_guild integer, in_neutral_faction_id smallint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
guild_count INTEGER := 0;
should_clear_invites SMALLINT := 0;
player_faction_id SMALLINT;
guild_record record;
BEGIN
PERFORM guilds_get_exclusive_operation_lock();
-- we need to check if the player is already part of max amount of guilds before being able to add them
SELECT INTO guild_count COUNT(*) FROM guild_members WHERE player_id = in_player_id;
IF guild_count >= in_max_guild_count_per_player THEN
RAISE EXCEPTION 'Cannot insert more than % guild entries for each user.', in_max_guild_count_per_player;
END IF;
SELECT * INTO guild_record
FROM guilds
WHERE guild_id = in_guild_id;
-- check if guild exists
IF guild_record IS NULL THEN
RAISE EXCEPTION 'Trying to add user to non existing guild %.', in_guild_id;
END IF;
player_faction_id := get_player_faction(in_player_id, in_neutral_faction_id);
IF player_faction_id != in_neutral_faction_id AND guild_record.guild_faction != in_neutral_faction_id AND player_faction_id != guild_record.guild_faction THEN
RAISE EXCEPTION 'Trying to add user to with non compatible. player faction: %, guild faction: %', player_faction_id, guild_record.guild_faction;
END IF;
IF (SELECT COUNT(*) FROM guild_members where guild_members.guild_id = in_guild_id) = in_max_members_per_guild - 1 THEN
should_clear_invites := 1;
END IF;
-- insert member
INSERT INTO guild_members("player_id", "guild_id", "role_id") VALUES(in_player_id, in_guild_id, in_role_id);
-- delete invite
IF should_clear_invites = 1 THEN
DELETE FROM guild_invites WHERE guild_id = in_guild_id;
ELSE
DELETE FROM guild_invites WHERE guild_id = in_guild_id AND player_id = in_player_id;
END IF;
PERFORM pg_notify('guild_notify_channel', format('add_player#{"PlayerId" : %s , "PlayerFactionId" : %s, "GuildId" : %s, "RoleId" : %s, "ShouldClearInvites" : %s}', in_player_id, player_faction_id, in_guild_id, in_role_id, should_clear_invites));
END
$function$
-- add_landclaim_segment(in_totem_id bigint, in_grid_location_x bigint, in_grid_location_y bigint) -> void
-- oid: 58123 kind: FUNCTION category: landclaim
CREATE OR REPLACE FUNCTION dune.add_landclaim_segment(in_totem_id bigint, in_grid_location_x bigint, in_grid_location_y bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO landclaim_segments(totem_id, grid_location_x, grid_location_y)
VALUES(in_totem_id, in_grid_location_x, in_grid_location_y);
END; $function$
-- add_map_areas_surveyed_items(in_account_id bigint, in_area_id smallint, in_survey_point_marker_id bigint, in_surveyed_items_target jsonb, in_surveyed_items_progress jsonb, in_map_name text) -> void
-- oid: 58124 kind: FUNCTION category: map_areas
CREATE OR REPLACE FUNCTION dune.add_map_areas_surveyed_items(in_account_id bigint, in_area_id smallint, in_survey_point_marker_id bigint, in_surveyed_items_target jsonb, in_surveyed_items_progress jsonb, in_map_name text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO map_areas("account_id", "area_id", "survey_point_marker_id", "items_surveyed_target", "items_surveyed_progress", "map_name")
VALUES(in_account_id, in_area_id, in_survey_point_marker_id, in_surveyed_items_target, in_surveyed_items_progress, in_map_name)
ON CONFLICT ("account_id", "area_id", "map_name") DO UPDATE SET "survey_point_marker_id" = in_survey_point_marker_id, "items_surveyed_target" = in_surveyed_items_target, "items_surveyed_progress" = in_surveyed_items_progress
WHERE map_areas.account_id = in_account_id AND map_areas.area_id = in_area_id and map_areas.map_name = in_map_name;
END;
$function$
-- add_map_areas_time_discovered(in_account_id bigint, in_area_id smallint, in_time_discovered timestamp without time zone, in_map_name text) -> void
-- oid: 58125 kind: FUNCTION category: map_areas
CREATE OR REPLACE FUNCTION dune.add_map_areas_time_discovered(in_account_id bigint, in_area_id smallint, in_time_discovered timestamp without time zone, in_map_name text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO map_areas("account_id", "area_id", "time_discovered", "map_name")
VALUES(in_account_id, in_area_id, in_time_discovered, in_map_name)
ON CONFLICT ("account_id", "area_id", "map_name") DO UPDATE SET "time_discovered" = in_time_discovered
WHERE map_areas.account_id = in_account_id AND map_areas.area_id = in_area_id and map_areas.map_name = in_map_name;
END;
$function$
-- add_map_areas_time_first_entered(in_account_id bigint, in_area_id smallint, in_time_first_entered timestamp without time zone, in_map_name text) -> void
-- oid: 58126 kind: FUNCTION category: map_areas
CREATE OR REPLACE FUNCTION dune.add_map_areas_time_first_entered(in_account_id bigint, in_area_id smallint, in_time_first_entered timestamp without time zone, in_map_name text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO map_areas("account_id", "area_id", "time_first_entered", "map_name")
VALUES(in_account_id, in_area_id, in_time_first_entered, in_map_name)
ON CONFLICT ("account_id", "area_id", "map_name") DO UPDATE SET "time_first_entered" = in_time_first_entered
WHERE map_areas.account_id = in_account_id AND map_areas.area_id = in_area_id and map_areas.map_name = in_map_name;
END;
$function$
-- add_partition_unique(in_map text, in_definition jsonb, in_dimension bigint, in_label text) -> bigint
-- oid: 58127 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.add_partition_unique(in_map text, in_definition jsonb, in_dimension bigint, in_label text DEFAULT NULL::text)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
v_partition_id bigint;
BEGIN
-- Don't use a constraint right now, this is only a dev-only helper function.
-- We could add a constraint, but would need to check the performance of constraint on the jsonb field.
insert into world_partition (map, partition_definition, dimension_index, label)
select in_map, in_definition, in_dimension, in_label
where not exists (
select 1 from world_partition where map = in_map and partition_definition = in_definition and dimension_index = in_dimension
) returning partition_id into v_partition_id;
return v_partition_id;
END;
$function$
-- add_party_invite(in_sender_player_id bigint, in_sender_platform_name text, in_sender_platform_session_id text, in_player_id bigint, in_max_party_member_count integer, in_invite_sent_timespan bigint) -> void
-- oid: 58128 kind: FUNCTION category: party
CREATE OR REPLACE FUNCTION dune.add_party_invite(in_sender_player_id bigint, in_sender_platform_name text, in_sender_platform_session_id text, in_player_id bigint, in_max_party_member_count integer, in_invite_sent_timespan bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
out_already_in_party BOOLEAN;
out_already_invited BOOLEAN;
out_sender_existing_party_id BIGINT;
out_sender_party_count INTEGER;
out_invite_id BIGINT;
out_player_name TEXT;
out_sender_name TEXT;
BEGIN
-- check if invited player is already invited
SELECT INTO out_already_invited EXISTS (SELECT 1 FROM party_invites where player_id = in_player_id AND sender_player_id = in_sender_player_id);
IF out_already_invited THEN
RAISE EXCEPTION 'The player % already has an invite from %.', in_player_id, in_sender_player_id;
END IF;
-- check if the sender party is full
SELECT party_id INTO out_sender_existing_party_id from parties where party_leader_id = in_sender_player_id;
SELECT INTO out_sender_party_count COUNT(*) FROM party_members WHERE party_id = out_sender_existing_party_id;
IF out_sender_existing_party_id IS NOT NULL AND out_sender_party_count >= in_max_party_member_count THEN
RAISE EXCEPTION 'Trying to invite player % for a party id % that is full.', in_player_id, out_sender_existing_party_id;
END IF;
-- add invite
INSERT INTO party_invites("player_id", "party_id", "sender_player_id", "sender_platform_name", "sender_platform_session_id", "invite_sent_timespan")
VALUES(in_player_id, out_sender_existing_party_id, in_sender_player_id, in_sender_platform_name, in_sender_platform_session_id, in_invite_sent_timespan) RETURNING "invite_id" INTO out_invite_id;
SELECT player_state.character_name INTO out_player_name
FROM player_state WHERE player_state.player_controller_id = in_player_id;
SELECT player_state.character_name INTO out_sender_name
FROM player_state WHERE player_state.player_controller_id = in_sender_player_id;
PERFORM pg_notify('party_notify_channel', format(
'add_invite#{"InviteId" : %s, "SenderId" : %s, "SenderName" : "%s", "PlayerId" : %s , "PlayerName" : "%s", "InviteSentUniverseTime" : %s}',
out_invite_id, in_sender_player_id, out_sender_name, in_player_id, out_player_name, in_invite_sent_timespan));
END
$function$
-- adjust_player_virtual_currency_balance(in_controller_id bigint, in_currency_id smallint, in_delta bigint) -> bigint
-- oid: 58129 kind: FUNCTION category: currency
CREATE OR REPLACE FUNCTION dune.adjust_player_virtual_currency_balance(in_controller_id bigint, in_currency_id smallint, in_delta bigint)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
current_balance BIGINT;
current_delta BIGINT;
new_delta BIGINT;
fls_id TEXT;
function_oid oid;
BEGIN
SELECT INTO current_balance balance from player_virtual_currency_balances WHERE player_controller_id = in_controller_id AND currency_id = in_currency_id;
INSERT INTO player_virtual_currency_balances("player_controller_id", "currency_id", "balance")
VALUES (in_controller_id, in_currency_id, in_delta)
ON CONFLICT (player_controller_id, currency_id) DO UPDATE SET balance = (player_virtual_currency_balances.balance + in_delta)
RETURNING balance INTO current_balance;
IF in_currency_id = get_solaris_id() THEN
GET DIAGNOSTICS function_oid = PG_ROUTINE_OID;
PERFORM log_event_solaris(function_oid, 'update_solaris', in_controller_id, current_balance, in_delta);
END IF;
current_delta = 0;
IF current_balance < 0 THEN
SELECT acc."user"
INTO fls_id
FROM accounts acc
JOIN player_state ps on ps.account_id = acc.id
WHERE ps.account_id = in_player_id
LIMIT 1;
PERFORM log_cheating(COALESCE(fls_id, in_player_id::text), 'negative_solaris');
INSERT INTO player_virtual_currency_balances("player_controller_id", "currency_id", "balance")
VALUES (in_controller_id, in_currency_id, 0)
ON CONFLICT (player_controller_id, currency_id) DO UPDATE SET balance = 0;
current_delta = current_balance;
END IF;
new_delta = in_delta + current_delta;
RETURN new_delta;
END;
$function$
-- admin_get_character_details(in_account_id bigint) -> TABLE(account_id bigint, player_id text, character_name text, online_status text, last_avatar_activity timestamp with time zone, class text, map text, transform dune.transform, server_id text, partition_id bigint, partition_label text, dimension_index integer, gas_attributes jsonb, properties jsonb, slot_name text, fgl_data text)
-- oid: 58130 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.admin_get_character_details(in_account_id bigint)
RETURNS TABLE(account_id bigint, player_id text, character_name text, online_status text, last_avatar_activity timestamp with time zone, class text, map text, transform dune.transform, server_id text, partition_id bigint, partition_label text, dimension_index integer, gas_attributes jsonb, properties jsonb, slot_name text, fgl_data text)
LANGUAGE plpgsql
AS $function$
begin
return query SELECT player_state.account_id, accounts.user As player_id, player_state.character_name, CASE WHEN is_player_offline(accounts.user) THEN 'Offline' ELSE 'Online' END AS online_status, player_state.last_avatar_activity,
actors.class, actors.map, actors.transform, player_state.server_id, actors.partition_id, world_partition.label, actors.dimension_index,
actors.gas_attributes, actors.properties, actor_fgl_entities.slot_name,
string_agg(cast(to_json((select d from (select fgl_entities.components) d)) as varchar), ', ') as fgl_data
from fgl_entities
left join actor_fgl_entities on fgl_entities.entity_id = actor_fgl_entities.entity_id
left join actors on actor_fgl_entities.actor_id = actors.id
left join player_state on player_state.player_pawn_id = actors.id
left join accounts on accounts.id = player_state.account_id
left join world_partition on world_partition.partition_id = actors.partition_id
where player_state.account_id = in_account_id
and actor_fgl_entities.slot_name = 'DuneCharacter'
group by player_state.account_id, player_state.character_name, player_state.last_avatar_activity, accounts.user,
actors.class, actors.map, actors.transform, player_state.server_id, actors.partition_id, world_partition.label, actors.dimension_index,
actors.gas_attributes, actors.properties, actor_fgl_entities.slot_name;
end
$function$
-- admin_get_character_ids(in_search_term text) -> TABLE(id bigint, "user" text, character_name text)
-- oid: 58131 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.admin_get_character_ids(in_search_term text)
RETURNS TABLE(id bigint, "user" text, character_name text)
LANGUAGE plpgsql
AS $function$
begin
return query
select accounts.id, accounts.user, player_state.character_name
from accounts
left join player_state on player_state.account_id = accounts.id
where lower(accounts.user) like in_search_term or lower(player_state.character_name) like in_search_term;
end
$function$
-- admin_get_inventory_details(in_account_id bigint) -> TABLE(inventory_id bigint, item_id bigint, stack_size integer, template_id text, acquisition_time bigint)
-- oid: 58132 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.admin_get_inventory_details(in_account_id bigint)
RETURNS TABLE(inventory_id bigint, item_id bigint, stack_size integer, template_id text, acquisition_time bigint)
LANGUAGE sql
AS $function$
select
inventories.id as inventory_id, items.id as item_id, items.stack_size, items.template_id, items.acquisition_time
from
items
left join
inventories on inventories.id = items.inventory_id
left join
player_state on inventories.actor_id = player_state.player_pawn_id
where
player_state.account_id = in_account_id
order by
template_id;
$function$
-- admin_get_journey_details(in_player_id text, in_story_node_id text) -> TABLE(out_story_node_id text, out_override_reward_block boolean, out_has_pending_reward boolean, out_complete_condition_state jsonb, out_reveal_condition_state jsonb, out_fail_condition_state jsonb, out_metadata_state jsonb, out_reset_group dune.journeystoryresetgroup)
-- oid: 58133 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.admin_get_journey_details(in_player_id text, in_story_node_id text DEFAULT '%%'::text)
RETURNS TABLE(out_story_node_id text, out_override_reward_block boolean, out_has_pending_reward boolean, out_complete_condition_state jsonb, out_reveal_condition_state jsonb, out_fail_condition_state jsonb, out_metadata_state jsonb, out_reset_group dune.journeystoryresetgroup)
LANGUAGE sql
AS $function$
select story_node_id, override_reward_block, has_pending_reward, complete_condition_state, reveal_condition_state, fail_condition_state, metadata_state, reset_group
from journey_story_node
where story_node_id like in_story_node_id
and account_id in (
select id
from accounts a
where a.user = in_player_id
);
$function$
-- admin_get_mnemonic_recall_details(in_account_id bigint) -> TABLE(mnemonic_recall_id bigint, lesson_id text, lesson_state bigint, lesson_progress integer)
-- oid: 58134 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.admin_get_mnemonic_recall_details(in_account_id bigint)
RETURNS TABLE(mnemonic_recall_id bigint, lesson_id text, lesson_state bigint, lesson_progress integer)
LANGUAGE sql
AS $function$
select
mnemonic_recall.id, lesson_id, lesson_state, lesson_progress
from
mnemonic_recall
where
mnemonic_recall.account_id = in_account_id
$function$
-- admin_get_partitions() -> TABLE(out_partition_id bigint, out_server_id text, out_partition_definition jsonb, out_dimension_index integer, out_blocked boolean, out_label text, out_map text)
-- oid: 58135 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.admin_get_partitions()
RETURNS TABLE(out_partition_id bigint, out_server_id text, out_partition_definition jsonb, out_dimension_index integer, out_blocked boolean, out_label text, out_map text)
LANGUAGE sql
AS $function$
select * from load_partition_definition_map();
$function$
-- admin_move_offline_player(in_fls_id text, in_target_partition_name text, in_target_location dune.vector) -> void
-- oid: 58136 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.admin_move_offline_player(in_fls_id text, in_target_partition_name text, in_target_location dune.vector)
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
if not (select is_player_offline(in_fls_id)) then
raise exception 'Player must be Offline';
end if;
if not exists(select 1 from world_partition where label = in_target_partition_name) then
raise exception 'Partition with name % not found', in_target_partition_name;
end if;
perform (with target_partition as (
select partition_id, map, dimension_index
from world_partition
where label = in_target_partition_name
limit 1
)
select admin_move_offline_player_to_partition(in_fls_id, target_partition.partition_id, in_target_location)
from target_partition);
end
$function$
-- admin_move_offline_player_to_partition(in_fls_id text, in_target_partition_id bigint, in_target_location dune.vector) -> void
-- oid: 58137 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.admin_move_offline_player_to_partition(in_fls_id text, in_target_partition_id bigint, in_target_location dune.vector)
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
if not (select is_player_offline(in_fls_id)) then
-- CAVEAT: DirectorDbApi.TryMoveOfflinePlayerToPartition depends on this string.
raise exception 'Player must be Offline';
end if;
if not exists(select 1 from world_partition where partition_id = in_target_partition_id) then
raise exception 'Partition with ID % not found', in_target_partition_id;
end if;
raise notice 'Moving player % to partition % location x=%, y=%, z=%', in_fls_id, in_target_partition_id, in_target_location.x, in_target_location.y, in_target_location.z;
with target_partition as (
select partition_id, map, dimension_index
from world_partition
where partition_id = in_target_partition_id
limit 1
), target_pawn_id as (
select player_state.player_pawn_id as id
from accounts, player_state
where accounts.user = in_fls_id and accounts.id = player_state.account_id
limit 1
), update_overmap_player_location as (
select
case
when target_partition.map = 'Overmap' then overmap_save_player_survival_data(target_pawn_id.id, null, false, in_target_location)
else null
end
from target_pawn_id, target_partition
)
update actors
set
transform = (in_target_location, (transform).rotation),
map = upgrade_map_name(target_partition.map),
dimension_index = target_partition.dimension_index,
partition_id = target_partition.partition_id
from
target_pawn_id, target_partition, update_overmap_player_location
where actors.id = target_pawn_id.id;
end
$function$
-- admin_read_player_tags(in_account_id bigint) -> TABLE(tags text)
-- oid: 58138 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.admin_read_player_tags(in_account_id bigint)
RETURNS TABLE(tags text)
LANGUAGE sql
AS $function$
select
pt.tag
from
player_tags as pt
where
pt.account_id = in_account_id
$function$
-- advance_items_id_sequencer(count bigint) -> bigint
-- oid: 58139 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.advance_items_id_sequencer(count bigint DEFAULT 1)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
next_val BIGINT;
next_free BIGINT;
BEGIN
IF count < 1 THEN
RAISE EXCEPTION 'count must be >= 1';
END IF;
PERFORM pg_advisory_xact_lock(('items_id_seq'::regclass)::oid::bigint);
next_val := nextval('items_id_seq'::regclass);
-- next free id after reserving `count` ids starting at next_val
next_free := next_val + count;
-- make the next nextval() return next_free
PERFORM setval('items_id_seq'::regclass, next_free, false);
RETURN next_val;
END $function$
-- assign_actor_id(in_class text) -> bigint
-- oid: 58140 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.assign_actor_id(in_class text)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
new_id BIGINT;
BEGIN
INSERT INTO actors(id) VALUES(DEFAULT) RETURNING id INTO new_id;
PERFORM add_actor_audit(new_id, in_class);
RETURN new_id;
END
$function$
-- base_backup_delete(in_base_backup_id bigint) -> void
-- oid: 58141 kind: FUNCTION category: base_backup
CREATE OR REPLACE FUNCTION dune.base_backup_delete(in_base_backup_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
actors_to_destroy BIGINT[];
BEGIN
DELETE FROM actors a WHERE id = ANY(
SELECT actor_id
FROM base_backup_linked_actors bbla
WHERE bbla.id = in_base_backup_id
);
DELETE FROM base_backups WHERE id = in_base_backup_id;
END
$function$
-- base_backup_find_totems_from_player_owner(in_player_id bigint) -> TABLE(totem_id bigint)
-- oid: 58142 kind: FUNCTION category: base_backup
CREATE OR REPLACE FUNCTION dune.base_backup_find_totems_from_player_owner(in_player_id bigint)
RETURNS TABLE(totem_id bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT t.id
FROM totems t
JOIN permission_actor_rank par ON par.permission_actor_id = t.id
WHERE par.player_id = in_player_id AND par.rank = 1;
END;
$function$
-- base_backup_finish_placing(in_base_backup_id bigint) -> void
-- oid: 58143 kind: FUNCTION category: base_backup
CREATE OR REPLACE FUNCTION dune.base_backup_finish_placing(in_base_backup_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
WITH base_info AS (
SELECT
bb.id AS base_backup_id,
a.partition_id,
a.dimension_index,
a.map
FROM
base_backups bb
JOIN actors a on bb.player_id = a.id
WHERE
bb.id = in_base_backup_id
)
UPDATE actors
SET
partition_id = base_info.partition_id,
dimension_index = base_info.dimension_index,
map = base_info.map
FROM
base_backup_linked_actors bbl
JOIN base_info ON bbl.id = base_info.base_backup_id
WHERE
actors.id = bbl.actor_id;
DELETE FROM actor_state a
WHERE actor_id = ANY(
SELECT actor_id
FROM base_backup_linked_actors bbla
WHERE bbla.id = in_base_backup_id
);
DELETE FROM base_backups
WHERE id = in_base_backup_id;
END
$function$
-- base_backup_get_actors_to_spawn(in_base_backup_id bigint) -> SETOF dune.actorspawninfo
-- oid: 58144 kind: FUNCTION category: base_backup
CREATE OR REPLACE FUNCTION dune.base_backup_get_actors_to_spawn(in_base_backup_id bigint)
RETURNS SETOF dune.actorspawninfo
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT a.id, a.class as class_name, a.transform, a.partition_id, a.dimension_index
FROM actors as a
WHERE a.id IN (
SELECT actor_id FROM base_backup_linked_actors as bbla WHERE bbla.id = in_base_backup_id
);
END
$function$
-- base_backup_get_available_backups(in_player_id bigint) -> TABLE(id bigint, base_backup_name text, totem_id bigint, totem_buildable_type text, landclaim_original_global_location real[], base_backup_map text)
-- oid: 58145 kind: FUNCTION category: base_backup
CREATE OR REPLACE FUNCTION dune.base_backup_get_available_backups(in_player_id bigint)
RETURNS TABLE(id bigint, base_backup_name text, totem_id bigint, totem_buildable_type text, landclaim_original_global_location real[], base_backup_map text)
LANGUAGE plpgsql
AS $function$
begin
RETURN QUERY
SELECT
bb.id,
bb.base_backup_name,
t.id AS totem_id,
p.building_type,
t.landclaim_original_global_location,
a.map
FROM base_backups bb
JOIN base_backup_linked_actors bbla ON bbla.id = bb.id
JOIN totems t ON bbla.actor_id = t.id
JOIN actors a ON a.id = t.id
JOIN placeables p ON p.id = a.id
WHERE bb.player_id = in_player_id;
END
$function$
-- base_backup_get_buildable_data(in_base_backup_id bigint) -> TABLE(buildable_type text, total_count integer)
-- oid: 58146 kind: FUNCTION category: base_backup
CREATE OR REPLACE FUNCTION dune.base_backup_get_buildable_data(in_base_backup_id bigint)
RETURNS TABLE(buildable_type text, total_count integer)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT t.buildable_type, SUM(t.cnt)::INT AS total_count
FROM (
SELECT bi.building_type AS buildable_type, COUNT(*) AS cnt
FROM base_backup_linked_actors bla
JOIN building_instances bi ON bla.actor_id = bi.building_id
WHERE
bla.id = in_base_backup_id AND
(bi.building_flags IS NULL OR (bi.building_flags & (1 << 2) = 0 AND bi.building_flags & (1 << 7) = 0)) -- flag 2 and 7 not enabled, which relates to holograms and extensions
GROUP BY bi.building_type
UNION ALL
SELECT p.building_type AS buildable_type, COUNT(*) AS cnt
FROM base_backup_linked_actors bla
JOIN placeables p ON bla.actor_id = p.id
WHERE bla.id = in_base_backup_id AND p.is_hologram = FALSE
GROUP BY p.building_type
) t
GROUP BY t.buildable_type;
END
$function$
-- base_backup_get_data(in_base_backup_id bigint) -> dune.getbasebackupdata
-- oid: 58147 kind: FUNCTION category: base_backup
CREATE OR REPLACE FUNCTION dune.base_backup_get_data(in_base_backup_id bigint)
RETURNS dune.getbasebackupdata
LANGUAGE plpgsql
AS $function$
DECLARE
base_backup_name TEXT;
totem_data BaseBackupTotemData;
buildings_array BaseBackupBuildingItem[];
placeables_array BaseBackupPlaceableItem[];
BEGIN
SELECT bb.base_backup_name
INTO base_backup_name
FROM base_backups bb
WHERE bb.id = in_base_backup_id;
totem_data := base_backup_get_totem_data(in_base_backup_id);
-- building pieces
SELECT array_agg((bi.building_id, bi.instance_id, bi.building_type, bi.transform, bi.building_flags)::BaseBackupBuildingItem)
into buildings_array
FROM
building_instances bi
JOIN base_backup_linked_actors bbla ON bi.building_id = bbla.actor_id
WHERE bbla.id = in_base_backup_id;
-- placeables
SELECT array_agg((p.building_type, a.transform)::BaseBackupPlaceableItem)
into placeables_array
FROM
placeables p
JOIN actors a ON p.id = a.id
JOIN base_backup_linked_actors bbla ON a.id = bbla.actor_id
WHERE
bbla.id = in_base_backup_id;
return ROW(base_backup_name, totem_data, buildings_array, placeables_array)::GetBaseBackupData;
END
$function$
-- base_backup_get_totem_data(in_base_backup_id bigint) -> dune.basebackuptotemdata
-- oid: 58148 kind: FUNCTION category: base_backup
CREATE OR REPLACE FUNCTION dune.base_backup_get_totem_data(in_base_backup_id bigint)
RETURNS dune.basebackuptotemdata
LANGUAGE plpgsql
AS $function$
DECLARE
totem_id BIGINT;
result BaseBackupTotemData;
BEGIN
SELECT t.id
INTO totem_id
FROM totems t JOIN base_backup_linked_actors bbla ON t.id = bbla.actor_id
WHERE bbla.id = in_base_backup_id
LIMIT 1;
IF totem_id IS NULL THEN
RAISE EXCEPTION 'No totem found for base_backup id %', in_base_backup_id;
END IF;
result := base_backup_get_totem_data_from_totem_id(totem_id);
RETURN result;
END;
$function$
-- base_backup_get_totem_data_from_totem_id(in_totem_id bigint) -> dune.basebackuptotemdata
-- oid: 58149 kind: FUNCTION category: base_backup
CREATE OR REPLACE FUNCTION dune.base_backup_get_totem_data_from_totem_id(in_totem_id bigint)
RETURNS dune.basebackuptotemdata
LANGUAGE plpgsql
AS $function$
DECLARE
result BaseBackupTotemData;
BEGIN
SELECT
t.id,
p.building_type,
a.map,
t.landclaim_original_global_location,
t.landclaim_original_global_yaw_rotation,
t.landclaim_vertical_level
INTO
result.totem_actor_id,
result.totem_building_type,
result.totem_map,
result.landclaim_original_global_location,
result.landclaim_original_global_yaw_rotation,
result.landclaim_vertical_level
FROM totems t
JOIN placeables p ON p.id = t.id
JOIN actors a ON a.id = t.id
WHERE t.id = in_totem_id
LIMIT 1;
IF result.totem_actor_id IS NULL THEN
RAISE EXCEPTION 'No totem found for totem_id %', in_totem_id;
END IF;
SELECT array_agg(ROW(grid_location_x, grid_location_y)::SMALLINTPOINT)
INTO result.landclaim_grid
FROM landclaim_segments s
WHERE s.totem_id = result.totem_actor_id;
RETURN result;
END;
$function$
-- base_backup_get_totem_id(backup_id bigint) -> bigint
-- oid: 58150 kind: FUNCTION category: base_backup
CREATE OR REPLACE FUNCTION dune.base_backup_get_totem_id(backup_id bigint)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
result BIGINT;
BEGIN
SELECT t.id
INTO result
FROM totems t
JOIN base_backup_linked_actors bbla ON t.id = bbla.actor_id
WHERE bbla.id = backup_id
LIMIT 1;
IF result IS NULL THEN
RAISE EXCEPTION 'No totem found for base_backup id %', backup_id;
END IF;
RETURN result;
END;
$function$
-- base_backup_recycle(in_base_backup_id bigint, in_target_inventory_id bigint) -> integer
-- oid: 58151 kind: FUNCTION category: base_backup
CREATE OR REPLACE FUNCTION dune.base_backup_recycle(in_base_backup_id bigint, in_target_inventory_id bigint)
RETURNS integer
LANGUAGE plpgsql
AS $function$
DECLARE
base_backup_items_moved INT;
BEGIN
UPDATE items
SET inventory_id = in_target_inventory_id
FROM
inventories inv
JOIN base_backup_linked_actors bbla ON inv.actor_id = bbla.actor_id
WHERE
items.inventory_id = inv.id
AND bbla.id = in_base_backup_id;
get diagnostics base_backup_items_moved = ROW_COUNT;
-- Re-organize the index of all the items
UPDATE items
SET position_index = new_index
FROM (
SELECT
id,
ROW_NUMBER() OVER (ORDER BY position_index) - 1 AS new_index
FROM items
WHERE inventory_id = in_target_inventory_id
) AS sub
WHERE items.id = sub.id;
PERFORM base_backup_delete(in_base_backup_id);
return base_backup_items_moved;
END
$function$
-- base_backup_save(in_player_actor_id bigint, in_base_backup_name text, in_building_pieces_to_link dune.basebackupbuildingitem[], in_placeables_to_link bigint[], in_placeables_to_remove_totem_owner bigint[]) -> bigint
-- oid: 58152 kind: FUNCTION category: base_backup
CREATE OR REPLACE FUNCTION dune.base_backup_save(in_player_actor_id bigint, in_base_backup_name text, in_building_pieces_to_link dune.basebackupbuildingitem[], in_placeables_to_link bigint[], in_placeables_to_remove_totem_owner bigint[])
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
v_backup_id BIGINT;
totem_id BIGINT;
BEGIN
-- Find and Validate the Totem exists in the list
SELECT t.id INTO totem_id
FROM totems t
JOIN unnest(in_placeables_to_link) AS ai(actor_id) ON t.id = ai.actor_id
LIMIT 1;
IF totem_id IS NULL THEN
RAISE EXCEPTION 'No totem found for base_backup_save';
END IF;
INSERT INTO base_backups(player_id, base_backup_name)
VALUES (in_player_actor_id, in_base_backup_name) RETURNING id INTO v_backup_id;
-- for each building_id, create a new actor for those building pieces and then assign that new building_id to the building pieces.
with
input as (select DISTINCT building_id from unnest(in_building_pieces_to_link)),
instances_input as (select building_id, instance_id from unnest(in_building_pieces_to_link)),
new_actor_ids as (select nextval('actors_id_seq') as new_id, building_id as old_id from input),
_copy_building_actors as (
insert into actors("id", "class", "map", "transform", "partition_id", "dimension_index")
select i.new_id, a."class", a."map", a."transform", a."partition_id", a."dimension_index"
from new_actor_ids i join actors a on (i.old_id = a.id)),
_insert_actor_states as (insert into actor_state(actor_id, state) select new_id, 'BaseBackup' from new_actor_ids),
_insert_buildings as (insert into buildings("id") select new_id from new_actor_ids),
_insert_base_backup_linked_actors as (insert into base_backup_linked_actors("id", "actor_id") select v_backup_id, new_id from new_actor_ids)
update building_instances bi set building_id = ids.new_id from new_actor_ids ids join instances_input i on (ids.old_id = i.building_id) where bi.building_id = ids.old_id and bi.instance_id = i.instance_id;
-- Link all placeables to the linked_base_backup_id and set them to BaseBackup ActorState in actor_state
INSERT INTO base_backup_linked_actors(id, actor_id)
SELECT v_backup_id, unnest(in_placeables_to_link);
INSERT INTO actor_state(actor_id, state)
SELECT unnest(in_placeables_to_link), 'BaseBackup'::ActorState;
UPDATE placeables
SET owner_entity_id = NULL
WHERE id = ANY(in_placeables_to_remove_totem_owner);
-- We need to remove permissions for the Totem
PERFORM permission_actor_destroy(totem_id);
-- Remove all invoices from the totem
PERFORM taxation_remove_invoices_from_totem(totem_id);
RETURN v_backup_id;
END
$function$
-- base_backup_save_all_totems_from_player_owner(in_player_id bigint) -> TABLE(base_backup_id bigint)
-- oid: 58153 kind: FUNCTION category: base_backup
CREATE OR REPLACE FUNCTION dune.base_backup_save_all_totems_from_player_owner(in_player_id bigint)
RETURNS TABLE(base_backup_id bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT base_backup_save_from_totem(in_player_id, totem_id)
FROM base_backup_find_totems_from_player_owner(in_player_id);
END;
$function$
-- base_backup_save_from_totem(in_player_id bigint, totem_id bigint) -> bigint
-- oid: 58154 kind: FUNCTION category: base_backup
CREATE OR REPLACE FUNCTION dune.base_backup_save_from_totem(in_player_id bigint, totem_id bigint)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
totem_entity_id BIGINT;
totem_name TEXT;
building_pieces_to_link BaseBackupBuildingItem[];
placeables_to_link BIGINT[];
placeables_to_remove_totem_owner BIGINT[];
BEGIN
SELECT entity_id INTO totem_entity_id
FROM actor_fgl_entities
WHERE actor_id = totem_id;
SELECT COALESCE(actor_name, '') INTO totem_name
FROM permission_actor
WHERE actor_id = totem_id
LIMIT 1;
SELECT array_agg((bi.building_id, bi.instance_id, bi.building_type, bi.transform, bi.building_flags)::BaseBackupBuildingItem)
INTO building_pieces_to_link
FROM building_instances bi
WHERE bi.owner_entity_id = totem_entity_id;
SELECT array_agg(p.id)
INTO placeables_to_link
FROM placeables p
WHERE (p.owner_entity_id = totem_entity_id AND p.has_buildable_support = TRUE) OR p.id = totem_id;
SELECT array_agg(p.id)
INTO placeables_to_remove_totem_owner
FROM placeables p
WHERE p.owner_entity_id = totem_entity_id AND p.has_buildable_support = FALSE AND p.id != totem_id;
RETURN base_backup_save(
in_player_id,
totem_name,
building_pieces_to_link,
placeables_to_link,
placeables_to_remove_totem_owner
);
END;
$function$
-- break_guild_allegiance(in_guild_id bigint, in_neutral_faction_id smallint) -> void
-- oid: 58155 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.break_guild_allegiance(in_guild_id bigint, in_neutral_faction_id smallint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
guild_data_record record;
BEGIN
PERFORM guilds_get_exclusive_operation_lock();
SELECT * INTO guild_data_record FROM guilds WHERE guild_id = in_guild_id;
IF guild_data_record IS NULL THEN
RAISE EXCEPTION 'Trying to break guild allegiance of a non existing guild: %', in_guild_id;
END IF;
if guild_data_record.guild_faction = in_neutral_faction_id THEN
RAISE EXCEPTION 'Guild already has neutral faction';
END IF;
UPDATE guilds SET guild_faction = in_neutral_faction_id WHERE guilds.guild_id = in_guild_id;
PERFORM pg_notify('guild_notify_channel', format('break_guild_allegiance#{"GuildId" : %s , "OldGuildFactionDbId" : %s, "NewGuildFactionDbId" : %s}', in_guild_id, guild_data_record.guild_faction, in_neutral_faction_id));
END
$function$
-- can_takeover_account(in_user_id text) -> boolean
-- oid: 58156 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune.can_takeover_account(in_user_id text)
RETURNS boolean
LANGUAGE sql
AS $function$
select takeoverable from accounts WHERE "user" = in_user_id;
$function$
-- change_player_faction(in_player_id bigint, in_faction_id smallint, neutral_faction_id smallint, in_utc_time_faction_change timestamp without time zone) -> void
-- oid: 58157 kind: FUNCTION category: faction
CREATE OR REPLACE FUNCTION dune.change_player_faction(in_player_id bigint, in_faction_id smallint, neutral_faction_id smallint, in_utc_time_faction_change timestamp without time zone)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
found_role_id SMALLINT;
BEGIN
if in_faction_id = neutral_faction_id THEN
DELETE FROM player_faction WHERE actor_id = in_player_id;
PERFORM pg_notify('faction_notify_channel', format('remove_player#{"PlayerId" : %s}', in_player_id));
ELSE
INSERT INTO player_faction(actor_id, faction_id, utc_time_faction_change) VALUES(in_player_id, in_faction_id, in_utc_time_faction_change)
ON CONFLICT (actor_id) DO UPDATE SET faction_id = in_faction_id, utc_time_faction_change = in_utc_time_faction_change;
PERFORM pg_notify('faction_notify_channel', format('add_player#{"PlayerId" : %s, "FactionId" : %s}', in_player_id, in_faction_id));
END IF;
PERFORM handle_player_faction_guild_effects(in_player_id, in_faction_id, neutral_faction_id);
END
$function$
-- character_migration_export(in_fls_id text) -> jsonb
-- oid: 58158 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune.character_migration_export(in_fls_id text)
RETURNS jsonb
LANGUAGE plpgsql
AS $function$
DECLARE
v_player_controller_id BigInt;
BEGIN
select out_player_controller_id
into v_player_controller_id
from _character_transfer_pre_export_validation(in_fls_id);
perform _character_transfer_store_in_world_owned_vehicles_into_recovery(v_player_controller_id);
perform base_backup_save_all_totems_from_player_owner(v_player_controller_id);
-- Add any other migration-specific actions here before the export call
return character_transfer_export(in_fls_id);
END;
$function$
-- character_migration_import(in_data jsonb, in_fls_id text, in_character_name text) -> bigint
-- oid: 58159 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune.character_migration_import(in_data jsonb, in_fls_id text, in_character_name text)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
BEGIN
-- Add any migration-specific actions here before the import call
RETURN character_transfer_import(in_data, in_fls_id, in_character_name);
END
$function$
-- character_transfer_export(in_fls_id text) -> jsonb
-- oid: 58160 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune.character_transfer_export(in_fls_id text)
RETURNS jsonb
LANGUAGE plpgsql
AS $function$
DECLARE
v_acc_id BigInt;
v_funcom_id Text;
v_player_controller_id BigInt;
v_player_pawn_id BigInt;
v_vehicle_ids BigInt[];
BEGIN
select out_acc_id, out_funcom_id, out_player_controller_id, out_player_pawn_id
into v_acc_id, v_funcom_id, v_player_controller_id, v_player_pawn_id
from _character_transfer_pre_export_validation(in_fls_id);
perform _character_transfer_create_data_table();
-- accounts
insert into pg_temp.export_data("id", "kind", "data")
select id, 'acc', _character_transfer_top_level_export('acc', to_jsonb(accounts))
from accounts where accounts.id=v_acc_id;
-- Vehicle and player actors
insert into pg_temp.export_data("id", "kind", "data")
select id, 'act', _character_transfer_top_level_export('act', to_jsonb(actors) - 'partition_id' #- '{properties,LandsraadCharacterComponent,m_NextContractAbandonUniverseTimeLimit}')
from actors where id IN (
-- player actors
select unnest(array[player_controller_id, player_state_id, player_pawn_id]) as id
from player_state where account_id=v_acc_id
union
-- backup vehicles
select vehicle_id as id from backup_vehicles where account_id=v_acc_id
union
-- recovered vehicles
select vehicle_id as id from recovered_vehicles where account_id=v_acc_id
union
-- base backup actors
select actor_id as id from base_backup_linked_actors where id IN (
select id from base_backups where player_id=v_player_controller_id
)
);
insert into pg_temp.export_data("id", "kind", "data")
select entity_id, 'fgl', _character_transfer_top_level_export(
'fgl', to_jsonb(fgl_entities) || to_jsonb(actor_fgl_entities)
) from actor_fgl_entities join fgl_entities using (entity_id) where actor_id IN (
select id from pg_temp.export_data where kind='act'
);
-- Actor state entries tied to actors we export
insert into pg_temp.export_data("id", "kind", "data")
select actor_id, 'ActorState', _character_transfer_top_level_export('ActorState', to_jsonb(actor_state))
from actor_state where
actor_id IN (select id from pg_temp.export_data where kind = 'act');
-- Permission data for the transferred actors
insert into pg_temp.export_data("id", "kind", "data")
select actor_id, 'PermissionActor', _character_transfer_top_level_export('PermissionActor', to_jsonb(permission_actor))
from permission_actor where actor_id IN (select id from pg_temp.export_data where kind = 'act');
insert into pg_temp.export_data("id", "kind", "data")
select permission_actor_id, 'PermissionActorRank', _character_transfer_top_level_export('PermissionActorRank', to_jsonb(permission_actor_rank))
from permission_actor_rank where
permission_actor_id IN (select id from pg_temp.export_data where kind = 'act')
and player_id IN (select id from pg_temp.export_data where kind = 'act');
-- vehicles and vehicle modules
insert into pg_temp.export_data("kind", "data")
select 'Vehicle', _character_transfer_top_level_export('Vehicle', to_jsonb(vehicles))
-- vehicle ids are aliases for actor ids
from vehicles where id IN (select id from pg_temp.export_data where kind = 'act');
insert into pg_temp.export_data("id", "kind", "data")
select id, 'VehicleModule', _character_transfer_top_level_export('VehicleModule', to_jsonb(vehicle_modules))
-- vehicle ids are aliases for actor ids
from vehicle_modules where vehicle_id IN (select id from pg_temp.export_data where kind = 'act');
insert into pg_temp.export_data("kind", "data")
select 'BackupVehicle', _character_transfer_top_level_export('BackupVehicle', to_jsonb(backup_vehicles))
from backup_vehicles where account_id=v_acc_id;
insert into pg_temp.export_data("kind", "data")
select 'RecoveredVehicle', _character_transfer_top_level_export('RecoveredVehicle', to_jsonb(recovered_vehicles))
from recovered_vehicles where account_id=v_acc_id;
-- inventories and items
insert into pg_temp.export_data("id", "kind", "data")
select id, 'inv', _character_transfer_top_level_export('inv', to_jsonb(inventories))
from inventories where
actor_id IN (select id from pg_temp.export_data where kind = 'act')
or vehicle_module_id IN (select id from pg_temp.export_data where kind = 'VehicleModule');
insert into pg_temp.export_data("id", "kind", "data")
select id, 'itm', _character_transfer_top_level_export('itm', to_jsonb(items))
from items where inventory_id IN (select id from pg_temp.export_data where kind = 'inv');
insert into pg_temp.export_data("id", "kind", "data")
select null, 'ActorInventory', _character_transfer_top_level_export('ActorInventory', to_jsonb(actor_inventories))
from actor_inventories where inventory_id IN (select id from pg_temp.export_data where kind = 'inv');
insert into pg_temp.export_data("id", "kind", "data")
select null, 'VehicleModuleInventory',
_character_transfer_top_level_export('VehicleModuleInventory', to_jsonb(vehicle_module_inventories))
from vehicle_module_inventories where inventory_id IN (select id from pg_temp.export_data where kind = 'inv');
-- Base backups (related actors are already added in the actors section)
insert into pg_temp.export_data("id", "kind", "data")
select null, 'Building', _character_transfer_top_level_export('Building', to_jsonb(buildings))
from buildings where id in (select id from pg_temp.export_data where kind = 'act');
insert into pg_temp.export_data("id", "kind", "data")
select null, 'BuildingInstance', _character_transfer_top_level_export('BuildingInstance', to_jsonb(building_instances))
from building_instances where building_id in (select id from pg_temp.export_data where kind = 'act');
insert into pg_temp.export_data("id", "kind", "data")
select null, 'Placeable', _character_transfer_top_level_export('Placeable', to_jsonb(placeables))
from placeables where id in (select id from pg_temp.export_data where kind = 'act');
insert into pg_temp.export_data("id", "kind", "data")
select null, 'Totem', _character_transfer_top_level_export('Totem', to_jsonb(totems))
from totems where id in (select id from pg_temp.export_data where kind = 'act');
insert into pg_temp.export_data("id", "kind", "data")
select id, 'BaseBackup', _character_transfer_top_level_export('BaseBackup', to_jsonb(base_backups))
from base_backups where player_id=v_player_controller_id;
insert into pg_temp.export_data("id", "kind", "data")
select id, 'BaseBackupLinkedActor', _character_transfer_top_level_export('BaseBackupLinkedActor', to_jsonb(base_backup_linked_actors))
from base_backup_linked_actors where id IN (
select id from pg_temp.export_data where kind='BaseBackup'
);
insert into pg_temp.export_data("id", "kind", "data")
select null, 'LandclaimSegment', _character_transfer_top_level_export('LandclaimSegment', to_jsonb(landclaim_segments))
from landclaim_segments where totem_id IN (
select id from pg_temp.export_data where kind='act'
);
insert into pg_temp.export_data("id", "kind", "data")
select id, 'TaxInvoice', _character_transfer_top_level_export('TaxInvoice', to_jsonb(tax_invoice))
from tax_invoice where totem_id IN (
select id from pg_temp.export_data where kind='act'
);
-- Other stuff
insert into pg_temp.export_data("id", "kind", "data")
select id, 'Faction', _character_transfer_top_level_export('Faction', to_jsonb(factions))
from factions;
insert into pg_temp.export_data("id", "kind", "data")
select id, 'Tutorial', _character_transfer_top_level_export('Tutorial', to_jsonb(tutorials))
from tutorials where id IN (select distinct tutorial_id from tutorial_per_player where player_id=v_player_controller_id);
insert into pg_temp.export_data("id", "kind", "data")
select id, 'Keystone', _character_transfer_top_level_export('Keystone', to_jsonb(specialization_keystones_map))
from specialization_keystones_map where id IN (select distinct keystone_id from purchased_specialization_keystones where player_id=v_player_controller_id);
insert into pg_temp.export_data("id", "kind", "data")
select null, 'RespawnLocation',
_character_transfer_top_level_export('RespawnLocation', to_jsonb(player_respawn_locations))
|| jsonb_build_object('id', gen_random_uuid())
-- TODO: add default to id and just delete it by filter
from player_respawn_locations
where account_id=v_acc_id and "group" = ANY('{PlayerStart,Checkpoint,CheckpointSafe}');
insert into pg_temp.export_data("kind", "data")
select 'Character', _character_transfer_top_level_export('Character', to_jsonb(player_state) - 'last_avatar_activity' - 'reconnect_grace_period_end' - 'previous_server_partition_id')
from player_state where player_state.account_id=v_acc_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'PlayerMarker', _character_transfer_top_level_export('PlayerMarker', to_jsonb(player_markers))
from player_markers join markers using (marker_hash_id, dimension_index, map_name_id) join map_names using (map_name_id)
where dimension_index=-1 and player_id=v_player_controller_id and map_name <> 'DeepDesert'
and ((marker).payload_type <> 'EMarkerPayloadType::Default' or ((marker).payload_type = 'EMarkerPayloadType::Default' and (marker).marker_type like 'FlourSand%'));
insert into pg_temp.export_data("id", "kind", "data")
select null, 'Marker', _character_transfer_top_level_export('Marker', to_jsonb(markers))
from markers join player_markers using (marker_hash_id, dimension_index, map_name_id) join map_names using (map_name_id)
where dimension_index=-1 and player_id=v_player_controller_id and map_name <> 'DeepDesert'
and ((marker).payload_type <> 'EMarkerPayloadType::Default' or ((marker).payload_type = 'EMarkerPayloadType::Default' and (marker).marker_type like 'FlourSand%'));
insert into pg_temp.export_data("id", "kind", "data")
select null, 'DialogueMetNpc', _character_transfer_top_level_export('DialogueMetNpc', to_jsonb(dialogue_met_npcs))
from dialogue_met_npcs where player_id=v_player_controller_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'DialogueTakenNode', _character_transfer_top_level_export('DialogueTakenNode', to_jsonb(dialogue_taken_nodes))
from dialogue_taken_nodes where player_id=v_player_controller_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'PlayerFaction', _character_transfer_top_level_export('PlayerFaction', to_jsonb(player_faction))
from player_faction where actor_id=v_player_controller_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'PlayerFactionReputation', _character_transfer_top_level_export('PlayerFactionReputation', to_jsonb(player_faction_reputation))
from player_faction_reputation where actor_id=v_player_controller_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'ConsumedLore', _character_transfer_top_level_export('ConsumedLore', to_jsonb(consumed_per_player_lore))
from consumed_per_player_lore where actor_id=v_player_controller_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'PlayerTutorial', _character_transfer_top_level_export('PlayerTutorial', to_jsonb(tutorial_per_player))
from tutorial_per_player where player_id=v_player_controller_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'PurchasedKeystone', _character_transfer_top_level_export('PurchasedKeystone', to_jsonb(purchased_specialization_keystones))
from purchased_specialization_keystones where player_id=v_player_controller_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'SpecializationTracks', _character_transfer_top_level_export('SpecializationTracks', to_jsonb(specialization_tracks))
from specialization_tracks where player_id=v_player_controller_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'SpecializationRefund', _character_transfer_top_level_export('SpecializationRefund', to_jsonb(specialization_refund_id))
from specialization_refund_id where player_id=v_player_controller_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'BuildingFavorite', _character_transfer_top_level_export('BuildingFavorite', to_jsonb(building_favorites))
from building_favorites where account_id=v_acc_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'BuildingProgression', _character_transfer_top_level_export('BuildingProgression', to_jsonb(building_progression))
from building_progression where account_id=v_acc_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'CommuninetPlayer', _character_transfer_top_level_export('CommuninetPlayer', to_jsonb(communinet_player))
from communinet_player where account_id=v_acc_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'CommuninetPlayerChannel', _character_transfer_top_level_export('CommuninetPlayerChannel', to_jsonb(communinet_player_channels))
from communinet_player_channels where account_id=v_acc_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'JourneyStoryNode', _character_transfer_top_level_export('JourneyStoryNode', to_jsonb(journey_story_node))
from journey_story_node where account_id=v_acc_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'MapArea', _character_transfer_top_level_export('MapArea', to_jsonb(map_areas))
from map_areas where account_id=v_acc_id and map_name <> 'DeepDesert';
insert into pg_temp.export_data("id", "kind", "data")
select null, 'PlayerAccessCode', _character_transfer_top_level_export('PlayerAccessCode', to_jsonb(player_access_codes))
from player_access_codes where account_id=v_acc_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'PlayerTag', _character_transfer_top_level_export('PlayerTag', to_jsonb(player_tags))
from player_tags where account_id=v_acc_id;
insert into pg_temp.export_data("id", "kind", "data")
select null, 'Sinkchart', _character_transfer_top_level_export('Sinkchart', to_jsonb(sinkcharts))
from sinkcharts where item_id IN (select id from pg_temp.export_data where kind='itm');
insert into pg_temp.export_data("id", "kind", "data")
select id, 'bbp', _character_transfer_top_level_export('bbp', to_jsonb(building_blueprints))
from building_blueprints where item_id IN (select id from pg_temp.export_data where kind='itm');
insert into pg_temp.export_data("id", "kind", "data")
select null, 'BuildingBlueprintInstance', _character_transfer_top_level_export('BuildingBlueprintInstance', to_jsonb(building_blueprint_instances))
from building_blueprint_instances where building_blueprint_id IN (select id from pg_temp.export_data where kind='bbp');
insert into pg_temp.export_data("id", "kind", "data")
select null, 'BuildingBlueprintPlaceable', _character_transfer_top_level_export('BuildingBlueprintPlaceable', to_jsonb(building_blueprint_placeables))
from building_blueprint_placeables where building_blueprint_id IN (select id from pg_temp.export_data where kind='bbp');
insert into pg_temp.export_data("id", "kind", "data")
select null, 'BuildingBlueprintPentashield', _character_transfer_top_level_export('BuildingBlueprintPentashield', to_jsonb(building_blueprint_pentashields))
from building_blueprint_pentashields where building_blueprint_id IN (select id from pg_temp.export_data where kind='bbp');
insert into pg_temp.export_data("id", "kind", "data")
select player_controller_id, 'PlayerVirtualCurrencyBalance', _character_transfer_top_level_export('PlayerVirtualCurrencyBalance', to_jsonb(player_virtual_currency_balances))
from player_virtual_currency_balances where player_controller_id = v_player_controller_id;
insert into pg_temp.export_data("id", "kind", "data")
select completion_id, 'DungeonCompletion', _character_transfer_top_level_export('DungeonCompletion', to_jsonb(dungeon_completion))
from dungeon_completion where completion_id IN (select completion_id from dungeon_completion_players where player_id = v_player_controller_id);
insert into pg_temp.export_data("id", "kind", "data")
select completion_id, 'DungeonCompletionPlayer', _character_transfer_top_level_export('DungeonCompletionPlayer', to_jsonb(dungeon_completion_players))
from dungeon_completion_players where player_id = v_player_controller_id;
insert into pg_temp.export_data("id","kind","data")
select null, 'LandsraadHouseRewards', _character_transfer_top_level_export('LandsraadHouseRewards', to_jsonb(landsraad_house_rewards))
from landsraad_house_rewards where player_id = v_player_controller_id;
update pg_temp.export_data set data=_character_transfer_replace_local_id_with_transfer_id_in_json(data, '');
return (select jsonb_build_object(
'_patches_checksum', (_character_transfer_get_patches_checksum()),
'funcom_id', (v_funcom_id),
'player_controller_id', (v_player_controller_id),
'entries', (_character_transfer_data_table_save())
));
END;
$function$
-- character_transfer_get_unsaved_counts(in_fls_id text) -> TABLE(unsaved_bases_count bigint, unsaved_vehicles_count bigint)
-- oid: 58162 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune.character_transfer_get_unsaved_counts(in_fls_id text)
RETURNS TABLE(unsaved_bases_count bigint, unsaved_vehicles_count bigint)
LANGUAGE plpgsql
AS $function$
DECLARE
v_acc_id BIGINT;
BEGIN
v_acc_id := (SELECT acc.id FROM encrypted_accounts acc WHERE acc.user = in_fls_id);
RETURN QUERY
WITH unbacked_bases AS (
SELECT COUNT(*) AS count
FROM get_unsaved_base_totem_ids_for_account(v_acc_id)
),
unsaved_vehicles AS (
SELECT COUNT(*) AS count
FROM get_unbacked_up_vehicle_ids_for_account(v_acc_id)
)
SELECT
ub.count AS unsaved_bases_count,
uv.count AS unsaved_vehicles_count
FROM unbacked_bases ub, unsaved_vehicles uv;
END
$function$
-- character_transfer_import(in_data jsonb, in_fls_id text, in_character_name text) -> bigint
-- oid: 58163 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune.character_transfer_import(in_data jsonb, in_fls_id text, in_character_name text)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
v_checksum TEXT;
v_coriolis_seed BigInt;
v_transfer_coriolis_seed BigInt;
v_id BigInt;
v_kind _CharacterTransferEntryKind;
v_new_player_controller_id BigInt;
v_vehicle_ids BigInt[];
BEGIN
if not (select is_player_offline(in_fls_id)) then
raise exception 'sbRP2$ - Player must be Offline';
end if;
PERFORM delete_account(in_fls_id, 'incoming char transfer');
v_checksum := (select _character_transfer_get_patches_checksum());
IF NOT in_data->>'_patches_checksum' = v_checksum THEN
raise exception 'sb9R2$ - Patches checksum mismatch, expected: %, got: %', v_checksum, in_data->>'_patches_checksum';
END IF;
perform _character_transfer_create_data_table();
perform _character_transfer_data_table_load(in_data->'entries');
update pg_temp.export_data set data=_character_transfer_replace_transfer_id_with_local_id_in_json(data, '');
-- accounts
insert into encrypted_accounts
select (jsonb_populate_record(
null::encrypted_accounts,
_character_transfer_top_level_import(kind, data, id)
|| jsonb_build_object(
'encrypted_funcom_id', encrypt_user_data(in_data->>'funcom_id'),
'user', in_fls_id
)
)).*
from pg_temp.export_data
where kind = 'acc';
-- player and vehicle actors
insert into actors
select (jsonb_populate_record(null::actors, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'act';
insert into fgl_entities
select (jsonb_populate_record(null::fgl_entities, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'fgl';
insert into actor_fgl_entities
select (jsonb_populate_record(null::actor_fgl_entities, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'fgl';
insert into actor_state
select (jsonb_populate_record(null::actor_state, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'ActorState';
insert into permission_actor
select (jsonb_populate_record(null::permission_actor, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'PermissionActor';
insert into permission_actor_rank
select (jsonb_populate_record(null::permission_actor_rank, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'PermissionActorRank';
-- vehicle and vehicle modules
insert into vehicles
select (jsonb_populate_record(null::vehicles, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'Vehicle';
insert into vehicle_modules
select (jsonb_populate_record(null::vehicle_modules, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'VehicleModule';
-- You can VBT as co-owner, but then there will be no owner in target BG. This is a state that permission system doesn't support.
-- So for backed up vehicles we also update the players ownership so they are now set as owner.
WITH inserted_backup_vehicles AS (
INSERT INTO backup_vehicles
SELECT (jsonb_populate_record(null::backup_vehicles, _character_transfer_top_level_import(kind, data, id))).*
FROM pg_temp.export_data
WHERE kind = 'BackupVehicle'
RETURNING vehicle_id
)
SELECT array_agg(vehicle_id) INTO v_vehicle_ids FROM inserted_backup_vehicles;
perform _character_transfer_ensure_player_is_owner_of_vbt_vehicle(v_vehicle_ids);
insert into recovered_vehicles
select (jsonb_populate_record(null::recovered_vehicles, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'RecoveredVehicle';
-- inventories and items
insert into inventories
select (jsonb_populate_record(null::inventories, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data e
where kind = 'inv';
insert into items
select (jsonb_populate_record(null::items, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'itm';
insert into actor_inventories
select (jsonb_populate_record(null::actor_inventories, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'ActorInventory';
insert into vehicle_module_inventories
select (jsonb_populate_record(null::vehicle_module_inventories, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'VehicleModuleInventory';
-- Base backups
insert into buildings
select (jsonb_populate_record(null::buildings, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'Building';
insert into building_instances
select (jsonb_populate_record(null::building_instances, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'BuildingInstance';
insert into placeables
select (jsonb_populate_record(null::placeables, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'Placeable';
insert into totems
select (jsonb_populate_record(null::totems, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'Totem';
insert into base_backups
select (jsonb_populate_record(null::base_backups, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'BaseBackup';
insert into base_backup_linked_actors
select (jsonb_populate_record(null::base_backup_linked_actors, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'BaseBackupLinkedActor';
insert into landclaim_segments
select (jsonb_populate_record(null::landclaim_segments, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'LandclaimSegment';
insert into tax_invoice
select (jsonb_populate_record(null::tax_invoice, _character_transfer_top_level_import(kind, data, id))).*
from pg_temp.export_data
where kind = 'TaxInvoice';
-- other stuff
insert into encrypted_player_state
select (jsonb_populate_record(
null::encrypted_player_state,
_character_transfer_top_level_import(kind, data, id)
|| jsonb_build_object(
'encrypted_character_name', encrypt_user_data(in_character_name)
)
)).*
from pg_temp.export_data
where kind = 'Character'
returning player_controller_id into v_new_player_controller_id;
insert into player_respawn_locations
select (
jsonb_populate_record(null::player_respawn_locations, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'RespawnLocation';
insert into markers
select (
jsonb_populate_record(null::markers, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'Marker'
on conflict ("marker_hash_id", "dimension_index", "map_name_id") do nothing;
insert into player_markers
select (
jsonb_populate_record(null::player_markers, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'PlayerMarker';
insert into dialogue_met_npcs
select (
jsonb_populate_record(null::dialogue_met_npcs, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'DialogueMetNpc';
insert into dialogue_taken_nodes
select (
jsonb_populate_record(null::dialogue_taken_nodes, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'DialogueTakenNode';
insert into player_faction
select (
jsonb_populate_record(null::player_faction, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'PlayerFaction';
insert into player_faction_reputation
select (
jsonb_populate_record(null::player_faction_reputation, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'PlayerFactionReputation';
insert into consumed_per_player_lore
select (
jsonb_populate_record(null::consumed_per_player_lore, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'ConsumedLore';
insert into tutorial_per_player
select (
jsonb_populate_record(null::tutorial_per_player, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'PlayerTutorial';
insert into purchased_specialization_keystones
select (
jsonb_populate_record(null::purchased_specialization_keystones, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'PurchasedKeystone';
insert into specialization_tracks
select (
jsonb_populate_record(null::specialization_tracks, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'SpecializationTracks';
insert into specialization_refund_id
select (
jsonb_populate_record(null::specialization_refund_id, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'SpecializationRefund';
insert into building_favorites
select (
jsonb_populate_record(null::building_favorites, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'BuildingFavorite';
insert into building_progression
select (
jsonb_populate_record(null::building_progression, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'BuildingProgression';
insert into communinet_player
select (
jsonb_populate_record(null::communinet_player, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'CommuninetPlayer';
insert into communinet_player_channels
select (
jsonb_populate_record(null::communinet_player_channels, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'CommuninetPlayerChannel';
insert into journey_story_node
select (
jsonb_populate_record(null::journey_story_node, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'JourneyStoryNode';
insert into map_areas
select (
jsonb_populate_record(null::map_areas, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'MapArea';
insert into player_access_codes
select (
jsonb_populate_record(null::player_access_codes, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'PlayerAccessCode';
insert into player_tags
select (
jsonb_populate_record(null::player_tags, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'PlayerTag';
insert into sinkcharts
select (
jsonb_populate_record(null::sinkcharts, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'Sinkchart';
insert into building_blueprints
select (
jsonb_populate_record(null::building_blueprints, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'bbp';
insert into building_blueprint_instances
select (
jsonb_populate_record(null::building_blueprint_instances, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'BuildingBlueprintInstance';
insert into building_blueprint_placeables
select (
jsonb_populate_record(null::building_blueprint_placeables, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'BuildingBlueprintPlaceable';
insert into building_blueprint_pentashields
select (
jsonb_populate_record(null::building_blueprint_pentashields, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'BuildingBlueprintPentashield';
insert into player_virtual_currency_balances
select (
jsonb_populate_record(null::player_virtual_currency_balances, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'PlayerVirtualCurrencyBalance';
insert into dungeon_completion
select (
jsonb_populate_record(null::dungeon_completion, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'DungeonCompletion';
insert into dungeon_completion_players
select (
jsonb_populate_record(null::dungeon_completion_players, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'DungeonCompletionPlayer';
insert into landsraad_house_rewards
select (
jsonb_populate_record(null::landsraad_house_rewards, _character_transfer_top_level_import(kind, data, id))
).*
from pg_temp.export_data
where kind = 'LandsraadHouseRewards';
PERFORM set_character_import_state(in_fls_id, 'Complete'::TransferImportState);
return v_new_player_controller_id;
END;
$function$
-- clean_expired_party_invites(in_invite_expire_seconds integer) -> void
-- oid: 58165 kind: FUNCTION category: party
CREATE OR REPLACE FUNCTION dune.clean_expired_party_invites(in_invite_expire_seconds integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
PERFORM parties_get_exclusive_operation_lock();
PERFORM remove_party_invite(invite_id, 0::smallint) FROM party_invites
WHERE CURRENT_TIMESTAMP > TO_TIMESTAMP(invite_sent_timespan) + INTERVAL '1 second' * in_invite_expire_seconds;
END
$function$
-- clean_guild_invites_with_incompatible_faction(in_player_id bigint, in_faction_id smallint, neutral_faction_id smallint) -> void
-- oid: 58166 kind: FUNCTION category: faction
CREATE OR REPLACE FUNCTION dune.clean_guild_invites_with_incompatible_faction(in_player_id bigint, in_faction_id smallint, neutral_faction_id smallint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
row_record record; -- Variable to hold individual rows
out_guild_faction_id SMALLINT;
BEGIN
PERFORM guilds_get_exclusive_operation_lock();
FOR row_record IN
SELECT invite_id, guild_invites.guild_id, guilds.guild_faction FROM get_player_guild_invites(in_player_id) as guild_invites
JOIN guilds ON guilds.guild_id = guild_invites.guild_id
LOOP
IF row_record.guild_faction != neutral_faction_id AND in_faction_id != neutral_faction_id AND row_record.guild_faction != in_faction_id THEN
PERFORM reject_guild_invite(row_record.invite_id);
END IF;
END LOOP;
END
$function$
-- clean_old_guild_invites(in_cutoff_timespan bigint) -> void
-- oid: 58167 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.clean_old_guild_invites(in_cutoff_timespan bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM guild_invites WHERE invite_sent_timespan < in_cutoff_timespan;
END
$function$
-- clean_stock_for_player(in_player_id bigint) -> void
-- oid: 58168 kind: FUNCTION category: stock_vendor
CREATE OR REPLACE FUNCTION dune.clean_stock_for_player(in_player_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM vendor_stock_cycle WHERE player_id = in_player_id;
DELETE FROM vendor_stock_state WHERE player_id = in_player_id;
END
$function$
-- clean_stock_for_vendors(in_vendor_ids text[]) -> void
-- oid: 58169 kind: FUNCTION category: stock_vendor
CREATE OR REPLACE FUNCTION dune.clean_stock_for_vendors(in_vendor_ids text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM vendor_stock_cycle WHERE vendor_id = ANY(in_vendor_ids);
DELETE FROM vendor_stock_state WHERE vendor_id = ANY(in_vendor_ids);
END
$function$
-- clean_vendors_older_than_timestamp(in_reference_timestamp bigint) -> void
-- oid: 58170 kind: FUNCTION category: stock_vendor
CREATE OR REPLACE FUNCTION dune.clean_vendors_older_than_timestamp(in_reference_timestamp bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
vendors_to_delete TEXT[];
BEGIN
SELECT array_agg(vendor_id) INTO vendors_to_delete FROM vendor_stock_cycle WHERE last_interacted_timestamp <= in_reference_timestamp;
DELETE FROM vendor_stock_cycle WHERE vendor_id = ANY(vendors_to_delete);
DELETE FROM vendor_stock_state WHERE vendor_id = ANY(vendors_to_delete);
END
$function$
-- cleanup_account_log_and_orphaned_actors() -> void
-- oid: 58171 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune.cleanup_account_log_and_orphaned_actors()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
delete from actors
WHERE
-- not is null instead of is not null to match the index expression
not owner_account_id is null
AND NOT EXISTS(select 1 from accounts where id=owner_account_id);
truncate account_removal_log;
END
$function$
-- cleanup_accounts_marked_for_deletion_in_fls(in_account_ids text[]) -> void
-- oid: 58172 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune.cleanup_accounts_marked_for_deletion_in_fls(in_account_ids text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
perform delete_account(id, 'deleted in fls') from unnest(in_account_ids) as id;
end
$function$
-- cleanup_orphaned_entities() -> trigger
-- oid: 58173 kind: FUNCTION category: cleanup
CREATE OR REPLACE FUNCTION dune.cleanup_orphaned_entities()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM fgl_entities WHERE fgl_entities.entity_id = OLD.entity_id;
RETURN NULL;
END
$function$
-- clear_map_areas_data_for_player(in_id bigint) -> void
-- oid: 58174 kind: FUNCTION category: map_areas
CREATE OR REPLACE FUNCTION dune.clear_map_areas_data_for_player(in_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM map_areas WHERE account_id = in_id;
END
$function$
-- complete_journey_nodes_where_prerequisite_nodes_are_complete(story_ids_to_complete text[], prerequisite_completed_story_ids text[]) -> void
-- oid: 58175 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.complete_journey_nodes_where_prerequisite_nodes_are_complete(story_ids_to_complete text[], prerequisite_completed_story_ids text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
IF ARRAY_LENGTH(story_ids_to_complete, 1) = 0 OR ARRAY_LENGTH(prerequisite_completed_story_ids, 1) = 0 THEN
RAISE EXCEPTION 'The story ids to complete array and/or the prerequisite completed story ids array is empty - neither array may be empty';
END IF;
WITH account_ids_to_modify AS (
SELECT account_id
FROM journey_story_node
WHERE story_node_id = ANY(prerequisite_completed_story_ids)
AND complete_condition_state = to_jsonb(true)
GROUP BY account_id
HAVING COUNT(DISTINCT story_node_id) = ARRAY_LENGTH(prerequisite_completed_story_ids, 1)
)
INSERT INTO journey_story_node(account_id, story_node_id, override_reward_block, has_pending_reward, complete_condition_state, reveal_condition_state, metadata_state, reset_group, fail_condition_state)
SELECT ids.account_id, completed_node.story_node_id, false, false, to_jsonb(true), to_jsonb(true), '{}', 'Default', '{}'
FROM account_ids_to_modify AS ids
CROSS JOIN (
SELECT story_node_id
FROM UNNEST(story_ids_to_complete) AS story_node_id
) completed_node(story_node_id)
ON CONFLICT ON CONSTRAINT journey_story_node_pkey
DO UPDATE SET
override_reward_block = EXCLUDED.override_reward_block,
has_pending_reward = EXCLUDED.has_pending_reward,
complete_condition_state = EXCLUDED.complete_condition_state,
reveal_condition_state = EXCLUDED.reveal_condition_state;
END;
$function$
-- complete_journey_story_nodes_for_player(in_player_id text, in_story_node_ids text[]) -> void
-- oid: 58176 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.complete_journey_story_nodes_for_player(in_player_id text, in_story_node_ids text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
IF NOT is_player_offline(in_player_id) THEN
RAISE EXCEPTION 'Cannot execute query because the player is online - they must be offline in order for the journey data to be updated correctly without risking it being overwritten by player actions.';
END IF;
WITH player_account_id AS (
SELECT id
FROM accounts a
WHERE a.user = in_player_id
)
INSERT INTO journey_story_node(account_id, story_node_id, override_reward_block, has_pending_reward, complete_condition_state, reveal_condition_state, fail_condition_state, metadata_state, reset_group)
SELECT player_account_id.id, completed_node.story_node_id, completed_node.override_reward_block, completed_node.has_pending_reward, completed_node.complete_condition_state, completed_node.reveal_condition_state, completed_node.fail_condition_state, completed_node.metadata_state, completed_node.reset_group
FROM player_account_id
CROSS JOIN (
SELECT story_node_id, false, false, to_jsonb(true), to_jsonb(true), jsonb_object(ARRAY[]::text[]), jsonb_object(ARRAY[]::text[]), 'Default'::JourneyStoryResetGroup
FROM UNNEST(in_story_node_ids) AS story_node_id
) completed_node(story_node_id, override_reward_block, has_pending_reward, complete_condition_state, reveal_condition_state, fail_condition_state, metadata_state, reset_group)
ON CONFLICT ON CONSTRAINT journey_story_node_pkey
DO UPDATE SET
complete_condition_state = EXCLUDED.complete_condition_state,
reveal_condition_state = EXCLUDED.reveal_condition_state,
fail_condition_state = EXCLUDED.fail_condition_state,
metadata_state = EXCLUDED.metadata_state;
END $function$
-- corilis_cleanup_map(in_server_info dune.serverinfo, in_map_info dune.coriolismapinfo) -> void
-- oid: 58177 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.corilis_cleanup_map(in_server_info dune.serverinfo, in_map_info dune.coriolismapinfo)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
PERFORM delete_markers_for_all_players(in_map_info.marker_types_to_keep, in_server_info.map);
IF in_map_info.should_clear_surveyed_areas
THEN
DELETE FROM map_areas WHERE map_name = in_server_info.map;
END IF;
IF in_map_info.is_outside_shieldwall
THEN
DELETE FROM resourcefield_state WHERE map = in_server_info.map;
END IF;
END
$function$
-- coriolis_cleanup_farm(in_server_info dune.serverinfo, in_map_info dune.coriolismapinfo) -> void
-- oid: 58178 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.coriolis_cleanup_farm(in_server_info dune.serverinfo, in_map_info dune.coriolismapinfo)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM lore_pickups_temporary;
DELETE FROM consumed_temporary_per_player_lore;
UPDATE player_state SET is_coriolis_processed = FALSE;
END
$function$
-- coriolis_cleanup_partition(in_server_info dune.serverinfo, in_map_info dune.coriolismapinfo) -> void
-- oid: 58179 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.coriolis_cleanup_partition(in_server_info dune.serverinfo, in_map_info dune.coriolismapinfo)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
IF in_map_info.is_outside_shieldwall
THEN
PERFORM delete_actors_and_respawns_on_server(in_server_info, (in_map_info).vehicle_classes_spawned_on_map, TRUE);
update player_state set life_state='DeadByCoriolis'
from actors
where player_state.player_pawn_id=actors.id AND server_info_match(actors, in_server_info) and
not exists (SELECT 1 FROM actor_state WHERE actor_state.actor_id = player_state.player_pawn_id AND actor_state.state = 'Travel');
-- Move players that died to Hagga Basin in their respective dimension and partition
UPDATE actors
SET
map = 'HaggaBasin',
dimension_index = player_state.return_dimension_index,
partition_id = (
SELECT world_partition.partition_id
FROM world_partition
WHERE player_state.return_dimension_index = world_partition.dimension_index AND world_partition.map = 'Survival_1'
)
FROM player_state
WHERE
(player_state.player_controller_id = actors.id OR player_state.player_pawn_id = actors.id OR player_state.player_state_id = actors.id) AND
server_info_match(actors, in_server_info) AND
NOT EXISTS (SELECT 1 FROM actor_state WHERE actor_state.actor_id = player_state.player_pawn_id AND actor_state.state = 'Travel');
END IF;
END
$function$
-- coriolis_update_seed(in_server_info dune.serverinfo, in_new_coriolis_seed integer, in_map_info dune.coriolismapinfo) -> void
-- oid: 58180 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.coriolis_update_seed(in_server_info dune.serverinfo, in_new_coriolis_seed integer, in_map_info dune.coriolismapinfo)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
old_farm_coriolis_seed Integer;
old_map_coriolis_seed Integer;
old_partition_coriolis_seed Integer;
BEGIN
LOCK TABLE world_farm_reset_seed, world_map_reset_seed, world_partition_reset_seed IN EXCLUSIVE MODE;
SELECT INTO old_farm_coriolis_seed world_reset_seed FROM world_farm_reset_seed WHERE onerow_id = TRUE limit 1;
UPDATE world_farm_reset_seed SET world_reset_seed = in_new_coriolis_seed WHERE onerow_id = TRUE;
SELECT INTO old_map_coriolis_seed world_reset_seed FROM world_map_reset_seed WHERE map = in_server_info.map limit 1;
INSERT INTO world_map_reset_seed (map, world_reset_seed) Values(in_server_info.map, in_new_coriolis_seed)
ON CONFLICT(map) DO
UPDATE SET world_reset_seed = in_new_coriolis_seed;
SELECT INTO old_partition_coriolis_seed world_reset_seed FROM world_partition_reset_seed WHERE partition_id = in_server_info.partition_id limit 1;
IF in_server_info.partition_id IS NOT NULL
THEN
INSERT INTO world_partition_reset_seed (partition_id, world_reset_seed) Values(in_server_info.partition_id, in_new_coriolis_seed)
ON CONFLICT(partition_id) DO
UPDATE SET world_reset_seed = in_new_coriolis_seed;
END IF;
IF old_farm_coriolis_seed IS NULL OR old_farm_coriolis_seed <> in_new_coriolis_seed
THEN
PERFORM coriolis_cleanup_farm(in_server_info, in_map_info);
END IF;
IF in_map_info.is_affected_by_coriolis
THEN
IF old_map_coriolis_seed IS NULL OR old_map_coriolis_seed <> in_new_coriolis_seed
THEN
PERFORM corilis_cleanup_map(in_server_info, in_map_info);
END IF;
IF (in_server_info.partition_id IS NOT NULL AND (old_partition_coriolis_seed IS NULL OR old_partition_coriolis_seed <> in_new_coriolis_seed)) OR
(old_map_coriolis_seed IS NULL OR old_map_coriolis_seed <> in_new_coriolis_seed)
THEN
PERFORM coriolis_cleanup_partition(in_server_info, in_map_info);
END IF;
END IF;
END
$function$
-- create_event_log_partition() -> trigger
-- oid: 58181 kind: FUNCTION category: event_log
CREATE OR REPLACE FUNCTION dune.create_event_log_partition()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
CALL create_event_log_partition_table('event_log', NEW.partition_id);
RETURN NEW;
END;
$function$
-- create_event_log_partition_table(IN table_name text, IN partition_id bigint) -> void
-- oid: 58182 kind: PROCEDURE category: event_log
CREATE OR REPLACE PROCEDURE dune.create_event_log_partition_table(IN table_name text, IN partition_id bigint)
LANGUAGE plpgsql
AS $procedure$
DECLARE
start_range BIGINT;
end_range BIGINT;
partition_table_name TEXT;
BEGIN
start_range := partition_id;
end_range := partition_id + 1;
partition_table_name := format('%s_p%s', table_name, partition_id);
EXECUTE format('
CREATE TABLE IF NOT EXISTS %I PARTITION OF %I
FOR VALUES FROM (%s) TO (%s);
', partition_table_name, table_name, start_range, end_range);
END;
$procedure$
-- create_guild(in_player_id bigint, in_neutral_faction smallint, in_guild_name text, in_guild_desc text, in_max_guild_count_per_player integer, OUT out_guild_id bigint, OUT out_success boolean, OUT out_fail_reason dune.guildcreatefailreason) -> record
-- oid: 58183 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.create_guild(in_player_id bigint, in_neutral_faction smallint, in_guild_name text, in_guild_desc text, in_max_guild_count_per_player integer, OUT out_guild_id bigint, OUT out_success boolean, OUT out_fail_reason dune.guildcreatefailreason)
RETURNS record
LANGUAGE plpgsql
AS $function$
DECLARE
guild_count integer;
BEGIN
PERFORM guilds_get_exclusive_operation_lock();
IF EXISTS (SELECT 1 FROM guilds WHERE guild_name ILIKE in_guild_name) THEN
out_guild_id := 0;
out_success := FALSE;
out_fail_reason := 'NameAlreadyTaken'::GuildCreateFailReason; -- 1 represents NAME_ALREADY_EXISTS
RETURN;
END IF;
-- we need to check if the player is already part of max amount of guilds before being able to add them
SELECT INTO guild_count COUNT(*) FROM guild_members WHERE player_id = in_player_id;
IF guild_count >= in_max_guild_count_per_player THEN
out_guild_id := 0;
out_success := FALSE;
out_fail_reason := 'QueryError'::GuildCreateFailReason;
RETURN;
END IF;
INSERT INTO guilds("guild_id", "guild_name", "guild_faction", "guild_description") VALUES(DEFAULT, in_guild_name, in_neutral_faction , in_guild_desc) RETURNING "guild_id" INTO out_guild_id;
INSERT INTO guild_members("player_id", "guild_id", "role_id") VALUES(in_player_id, out_guild_id, 100);
PERFORM pg_notify('guild_notify_channel', format('add_player#{"PlayerId" : %s , "PlayerFactionId" : %s, "GuildId" : %s, "RoleId" : 100, "ShouldClearInvites" : 0}', in_player_id, get_player_faction(in_player_id, in_neutral_faction), out_guild_id));
out_success := TRUE;
out_fail_reason := 'None'::GuildCreateFailReason;
END
$function$
-- create_or_update_tutorial_entry(in_player_id bigint, in_tutorial_id smallint, in_tutorial_state smallint) -> void
-- oid: 58184 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.create_or_update_tutorial_entry(in_player_id bigint, in_tutorial_id smallint, in_tutorial_state smallint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO tutorial_per_player("player_id", "tutorial_id", "tutorial_state") VALUES(in_player_id, in_tutorial_id, in_tutorial_state)
ON CONFLICT (player_id, tutorial_id) DO UPDATE SET "player_id" = in_player_id, "tutorial_id" = in_tutorial_id, "tutorial_state" = in_tutorial_state
WHERE tutorial_per_player.player_id = in_player_id AND tutorial_per_player.tutorial_id = in_tutorial_id;
END
$function$
-- create_server_player_access_codes(in_account_id bigint, in_access_code integer, in_access_code_type integer, in_is_resettable boolean) -> void
-- oid: 58185 kind: FUNCTION category: server
CREATE OR REPLACE FUNCTION dune.create_server_player_access_codes(in_account_id bigint, in_access_code integer, in_access_code_type integer, in_is_resettable boolean)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO player_access_codes(account_id, access_code, access_code_type,is_resettable)
VALUES(in_account_id, in_access_code, in_access_code_type, in_is_resettable);
END; $function$
-- create_sinkchart_for_map_area_id(in_item_id bigint, in_creator_id bigint, in_map_name text, in_area_id smallint) -> integer
-- oid: 58186 kind: FUNCTION category: map_areas
CREATE OR REPLACE FUNCTION dune.create_sinkchart_for_map_area_id(in_item_id bigint, in_creator_id bigint, in_map_name text, in_area_id smallint)
RETURNS integer
LANGUAGE plpgsql
AS $function$
DECLARE
sinkchart_marker_ids BIGINT[];
sinkchart_array_length INT;
BEGIN
-- Aggregate marker hash IDs into an array based on the conditions
SELECT ARRAY_AGG(markers.marker_hash_id) INTO sinkchart_marker_ids
FROM player_markers,
markers JOIN map_names USING(map_name_id)
WHERE player_markers.player_id = in_creator_id
AND map_names.map_name = in_map_name
AND player_markers.map_name_id = markers.map_name_id
AND player_markers.marker_hash_id = markers.marker_hash_id
AND markers.area_id = in_area_id
AND (player_markers.discovery_level = 2 OR player_markers.discovery_level = 3); -- 'EMarkerDiscoveryLevel::Mysterious' OR 'EMarkerDiscoveryLevel::Discovered'
-- Check if is NOT NULL before proceeding with the INSERT
IF array_length(sinkchart_marker_ids, 1) IS NOT NULL THEN
INSERT INTO sinkcharts (item_id, marker_hash_ids)
VALUES (in_item_id, sinkchart_marker_ids)
RETURNING array_length(marker_hash_ids, 1) INTO sinkchart_array_length;
RETURN sinkchart_array_length;
ELSE
RETURN 0;
END IF;
END
$function$
-- debug_add_test_table_data(in_entry text) -> void
-- oid: 58187 kind: FUNCTION category: debug
CREATE OR REPLACE FUNCTION dune.debug_add_test_table_data(in_entry text)
RETURNS void
LANGUAGE sql
AS $function$
insert into debug_test_table("entry") VALUES (in_entry);
$function$
-- debug_collect_test_table_data() -> SETOF text
-- oid: 58188 kind: FUNCTION category: debug
CREATE OR REPLACE FUNCTION dune.debug_collect_test_table_data()
RETURNS SETOF text
LANGUAGE sql
AS $function$
select entry from debug_test_table;
$function$
-- debug_echo(in_text text, in_notices text[]) -> text
-- oid: 58189 kind: FUNCTION category: debug
CREATE OR REPLACE FUNCTION dune.debug_echo(in_text text, in_notices text[])
RETURNS text
LANGUAGE plpgsql
AS $function$
BEGIN
perform debug_raise_notices(in_notices);
return in_text;
END;
$function$
-- debug_get_coriolis_seeds() -> TABLE(farm_seed integer, map_names text[], map_seeds integer[], partitions_ids bigint[], partitions_map text[], partitions_seeds integer[])
-- oid: 58190 kind: FUNCTION category: debug
CREATE OR REPLACE FUNCTION dune.debug_get_coriolis_seeds()
RETURNS TABLE(farm_seed integer, map_names text[], map_seeds integer[], partitions_ids bigint[], partitions_map text[], partitions_seeds integer[])
LANGUAGE plpgsql
AS $function$
begin
RETURN QUERY
WITH
map_seeds as (
SELECT array_agg(map_name) as map_name, array_agg(seed) as seed
FROM (
SELECT COALESCE(map_seed.map, partition.map) AS map_name, COALESCE(map_seed.world_reset_seed, -1) AS seed
FROM world_map_reset_seed AS map_seed FULL JOIN world_partition as partition ON map_seed.map = partition.map
GROUP BY map_seed.map, partition.map, map_seed.world_reset_seed
ORDER BY map_name ASC
) as maps_temp
),
partitions_seeds as (
SELECT array_agg(partition_id) as partition_id, array_agg(map_name) as map_name, array_agg(seed) as seed
FROM (
SELECT partition.partition_id as partition_id, partition.map as map_name, COALESCE(partition_seed.world_reset_seed, -1) AS seed
FROM world_partition_reset_seed AS partition_seed FULL JOIN world_partition as partition ON partition_seed.partition_id = partition.partition_id
GROUP BY partition.map, partition.partition_id, partition_seed.world_reset_seed
ORDER BY partition.partition_id ASC
) as partitions_temp
)
SELECT
COALESCE(world_reset_seed, -1),
COALESCE(map_seeds.map_name, array[]::TEXT[]),
COALESCE(map_seeds.seed, array[]::Integer[]),
COALESCE(partitions_seeds.partition_id, array[]::BigInt[]),
COALESCE(partitions_seeds.map_name, array[]::TEXT[]),
COALESCE(partitions_seeds.seed, array[]::Integer[])
FROM
world_farm_reset_seed,
map_seeds,
partitions_seeds;
end
$function$
-- debug_raise_exception(in_exception text, in_notices text[]) -> void
-- oid: 58191 kind: FUNCTION category: debug
CREATE OR REPLACE FUNCTION dune.debug_raise_exception(in_exception text, in_notices text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
perform debug_raise_notices(in_notices);
RAISE EXCEPTION '%', in_exception;
END;
$function$
-- debug_raise_notices(in_notices text[]) -> void
-- oid: 58192 kind: FUNCTION category: debug
CREATE OR REPLACE FUNCTION dune.debug_raise_notices(in_notices text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
IF array_length(in_notices, 1) IS NOT NULL THEN
FOR i IN 0..array_length(in_notices, 1)-1
LOOP
RAISE NOTICE '%', in_notices[i];
END LOOP;
END IF;
END;
$function$
-- debug_reset_test_table() -> void
-- oid: 58193 kind: FUNCTION category: debug
CREATE OR REPLACE FUNCTION dune.debug_reset_test_table()
RETURNS void
LANGUAGE sql
AS $function$
truncate debug_test_table;
$function$
-- debug_set_farm_seed(in_new_coriolis_seed integer) -> void
-- oid: 58194 kind: FUNCTION category: debug
CREATE OR REPLACE FUNCTION dune.debug_set_farm_seed(in_new_coriolis_seed integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
UPDATE world_farm_reset_seed SET world_reset_seed = in_new_coriolis_seed WHERE onerow_id = TRUE;
UPDATE world_map_reset_seed SET world_reset_seed = in_new_coriolis_seed;
INSERT INTO world_map_reset_seed SELECT map, in_new_coriolis_seed FROM world_partition GROUP BY map
ON CONFLICT(map) DO
UPDATE SET world_reset_seed = in_new_coriolis_seed;
UPDATE world_partition_reset_seed SET world_reset_seed = in_new_coriolis_seed;
INSERT INTO world_partition_reset_seed SELECT partition_id, in_new_coriolis_seed FROM world_partition GROUP BY partition_id
ON CONFLICT(partition_id) DO
UPDATE SET world_reset_seed = in_new_coriolis_seed;
end
$function$
-- debug_set_map_seed(in_map text, in_new_coriolis_seed integer) -> void
-- oid: 58195 kind: FUNCTION category: debug
CREATE OR REPLACE FUNCTION dune.debug_set_map_seed(in_map text, in_new_coriolis_seed integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
INSERT INTO world_map_reset_seed (map, world_reset_seed) Values(in_server_info.map, in_new_coriolis_seed)
ON CONFLICT(map) DO
UPDATE SET world_reset_seed = in_new_coriolis_seed;
UPDATE world_partition_reset_seed SET world_reset_seed = in_new_coriolis_seed WHERE map = in_map;
INSERT INTO world_partition_reset_seed SELECT partition_id, in_new_coriolis_seed FROM world_partition WHERE map = in_map GROUP BY partition_id
ON CONFLICT(partition_id) DO
UPDATE SET world_reset_seed = in_new_coriolis_seed;
end
$function$
-- debug_set_partition_seed(in_partition_id bigint, in_new_coriolis_seed integer) -> void
-- oid: 58196 kind: FUNCTION category: debug
CREATE OR REPLACE FUNCTION dune.debug_set_partition_seed(in_partition_id bigint, in_new_coriolis_seed integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
INSERT INTO world_partition_reset_seed (partition_id, world_reset_seed) Values(in_server_info.partition_id, in_new_coriolis_seed)
ON CONFLICT(partition_id) DO
UPDATE SET world_reset_seed = in_new_coriolis_seed;
end
$function$
-- decrypt_user_data(in_encrypted_data bytea) -> text
-- oid: 58197 kind: FUNCTION category: encryption
CREATE OR REPLACE FUNCTION dune.decrypt_user_data(in_encrypted_data bytea)
RETURNS text
LANGUAGE sql
IMMUTABLE
AS $function$select convert_from(in_encrypted_data, 'utf8');$function$
-- delete_account(in_user_id text, in_reason text) -> boolean
-- oid: 58198 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune.delete_account(in_user_id text, in_reason text)
RETURNS boolean
LANGUAGE plpgsql
AS $function$
DECLARE
was_found Boolean;
BEGIN
WITH referenced_actor_ids as (
SELECT ARRAY[player_controller_id, player_pawn_id, player_state_id] as ids
FROM player_state ps JOIN accounts a ON (ps.account_id = a.id)
WHERE a.user = in_user_id
),
deleted_actors AS
(
DELETE FROM actors USING referenced_actor_ids WHERE id IN (SELECT id FROM actors WHERE id = ANY(referenced_actor_ids.ids) ORDER BY id FOR UPDATE) returning id
),
selected_respawn_beacons AS
(
SELECT player_respawn_locations.locator_actor_id FROM player_respawn_locations
INNER JOIN accounts ON accounts.id = player_respawn_locations.account_id
WHERE accounts.user = in_user_id AND player_respawn_locations.group = 'RespawnBeacon'
),
delete_respawn_beacons AS
(
DELETE FROM actors
WHERE actors.id IN (SELECT * FROM selected_respawn_beacons)
),
delete_accounts as (
DELETE FROM accounts WHERE accounts.user = in_user_id returning id as account_id
),
insert_into_removal_log as (
insert into account_removal_log("fls_id", "account_id", "reason") select in_user_id, account_id, in_reason from delete_accounts
),
delete_from_cascades as (
SELECT referenced_actor_ids.ids[1] as id,
guild_handle_actor_delete(referenced_actor_ids.ids[1]),
remove_party_member(referenced_actor_ids.ids[1], 0::SMALLINT),
ownership_handle_actor_delete(referenced_actor_ids.ids[1]),
perform_notify_on_character_delete(in_user_id)
FROM referenced_actor_ids
)
SELECT count(*) > 0 INTO was_found FROM delete_from_cascades;
return was_found;
END;
$function$
-- delete_actor_states_travel(in_actor_id bigint) -> void
-- oid: 58199 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.delete_actor_states_travel(in_actor_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
WITH
traveling_actor_ids AS (
SELECT t.id FROM get_traveling_non_player_actor_ids(in_actor_id) AS t
)
DELETE FROM actor_state WHERE (actor_id IN (SELECT t.id FROM traveling_actor_ids AS t(id)) OR actor_id = in_actor_id) AND state = 'Travel';
END;
$function$
-- delete_actors(in_ids bigint[]) -> void
-- oid: 58200 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.delete_actors(in_ids bigint[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM actors WHERE id = ANY(in_ids);
END
$function$
-- delete_actors_and_respawns_on_server(in_server_info dune.serverinfo, in_vehicle_classes_spawned_on_map text[], in_allow_vehicle_recovery boolean) -> void
-- oid: 58201 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.delete_actors_and_respawns_on_server(in_server_info dune.serverinfo, in_vehicle_classes_spawned_on_map text[], in_allow_vehicle_recovery boolean)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
WITH actors_to_delete AS (
SELECT a.id
FROM actors a
LEFT JOIN actor_state s ON a.id = s.actor_id
WHERE owner_account_id IS NULL
AND s.state IS DISTINCT FROM 'Travel'
AND s.state IS DISTINCT FROM 'VehicleBackup'
AND s.state IS DISTINCT FROM 'VehicleRecovery'
AND server_info_match(a, in_server_info)
AND (
-- Actors that are not vehicles should always be deleted
NOT EXISTS (SELECT 1 FROM vehicles v WHERE v.id = a.id)
-- Only vehicles that are allowed to be spawned on this map should be deleted
OR in_vehicle_classes_spawned_on_map IS NULL -- If the list is NULL all vehicles are allowed
OR a.class = ANY(in_vehicle_classes_spawned_on_map) -- Vehicle type is explicitly allowed on this map
)
ORDER BY a.id FOR UPDATE OF a
),
vehicles_to_recover AS (
SELECT COALESCE(ARRAY_AGG(v.id), ARRAY[]::BIGINT[]) AS ids FROM actors_to_delete a JOIN vehicles v ON (a.id = v.id)
WHERE in_allow_vehicle_recovery
),
recovered_vehicles AS (
SELECT ids, store_recovered_vehicles_wiped_before_spawn(ids) FROM vehicles_to_recover
)
DELETE FROM actors a USING recovered_vehicles rv
WHERE a.id = ANY(SELECT id FROM actors_to_delete)
AND NOT a.id = ANY(rv.ids);
with
deleted_ids as (
DELETE from player_respawn_locations
WHERE map = in_server_info.map AND dimension = in_server_info.dimension_index
returning id
)
update player_state set pending_respawn_location_id=null
where pending_respawn_location_id in (select * from deleted_ids);
END
$function$
-- delete_all_dungeon_completions(in_dungeon_id text) -> void
-- oid: 58202 kind: FUNCTION category: dungeon
CREATE OR REPLACE FUNCTION dune.delete_all_dungeon_completions(in_dungeon_id text)
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
DELETE FROM dungeon_completion WHERE dungeon_id = in_dungeon_id;
end
$function$
-- delete_all_dungeon_completions_by_player(in_dungeon_id text, in_player_id bigint, in_keep_completion_for_other_players boolean) -> void
-- oid: 58203 kind: FUNCTION category: dungeon
CREATE OR REPLACE FUNCTION dune.delete_all_dungeon_completions_by_player(in_dungeon_id text, in_player_id bigint, in_keep_completion_for_other_players boolean)
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
DELETE FROM dungeon_completion WHERE
NOT in_keep_completion_for_other_players AND
dungeon_id = in_dungeon_id AND
completion_id IN (SELECT completion_id FROM dungeon_completion_players WHERE player_id = in_player_id);
DELETE FROM dungeon_completion_players
WHERE
in_keep_completion_for_other_players AND
player_id = in_player_id AND
completion_id IN (SELECT completion_id FROM dungeon_completion WHERE dungeon_id = in_dungeon_id);
end
$function$
-- delete_all_dungeon_completions_for_all_dungeons_by_player(in_player_id bigint, in_keep_completion_for_other_players boolean) -> void
-- oid: 58204 kind: FUNCTION category: dungeon
CREATE OR REPLACE FUNCTION dune.delete_all_dungeon_completions_for_all_dungeons_by_player(in_player_id bigint, in_keep_completion_for_other_players boolean)
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
DELETE FROM dungeon_completion WHERE
NOT in_keep_completion_for_other_players AND
completion_id IN (SELECT completion_id FROM dungeon_completion_players WHERE player_id = in_player_id);
DELETE FROM dungeon_completion_players WHERE in_keep_completion_for_other_players AND player_id = in_player_id;
end
$function$
-- delete_all_inactive_farms() -> void
-- oid: 58205 kind: FUNCTION category: farm
CREATE OR REPLACE FUNCTION dune.delete_all_inactive_farms()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM farm_state WHERE server_id NOT IN (SELECT * FROM active_server_ids);
END
$function$
-- delete_all_journey_story_nodes(in_account_id bigint) -> void
-- oid: 58206 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.delete_all_journey_story_nodes(in_account_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM journey_story_node WHERE account_id = in_account_id;
END $function$
-- delete_all_static_shifting_sand() -> void
-- oid: 58207 kind: FUNCTION category: shifting_sand
CREATE OR REPLACE FUNCTION dune.delete_all_static_shifting_sand()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
TRUNCATE shiftingsands_data;
END $function$
-- delete_all_tutorial_entries(in_player_id bigint) -> void
-- oid: 58208 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.delete_all_tutorial_entries(in_player_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM tutorial_per_player WHERE tutorial_per_player.player_id = in_player_id;
END
$function$
-- delete_building_blueprint(in_building_item_id bigint) -> void
-- oid: 58209 kind: FUNCTION category: building_blueprint
CREATE OR REPLACE FUNCTION dune.delete_building_blueprint(in_building_item_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM building_blueprints WHERE id = in_building_item_id;
END
$function$
-- delete_character(in_actor_id bigint) -> void
-- oid: 58210 kind: FUNCTION category: character_mod
CREATE OR REPLACE FUNCTION dune.delete_character(in_actor_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM actors WHERE id = in_actor_id;
DELETE FROM properties WHERE object_id = in_actor_id;
DELETE FROM fgl_data WHERE object_id = in_actor_id;
DELETE FROM actor_transform WHERE actor_id = in_actor_id;
END
$function$
-- delete_crafted_map(in_item_id bigint) -> void
-- oid: 58211 kind: FUNCTION category: map_areas
CREATE OR REPLACE FUNCTION dune.delete_crafted_map(in_item_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM crafted_maps WHERE item_id = in_item_id;
END
$function$
-- delete_dialogue_data(in_player_controller_id bigint) -> void
-- oid: 58212 kind: FUNCTION category: dialogue
CREATE OR REPLACE FUNCTION dune.delete_dialogue_data(in_player_controller_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM dialogue_met_npcs
WHERE player_id = in_player_controller_id;
DELETE FROM dialogue_taken_nodes
WHERE player_id = in_player_controller_id;
END
$function$
-- delete_inventory_item(in_item_id bigint, in_count bigint) -> bigint
-- oid: 58213 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.delete_inventory_item(in_item_id bigint, in_count bigint)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
remaining_stack_size BIGINT;
BEGIN
SELECT INTO STRICT remaining_stack_size stack_size FROM items WHERE id = in_item_id;
remaining_stack_size := remaining_stack_size - in_count;
IF remaining_stack_size < 0 THEN
RETURN NULL;
END IF;
IF remaining_stack_size > 0 THEN
UPDATE items SET stack_size = remaining_stack_size WHERE id = in_item_id;
ELSE
PERFORM delete_item(in_item_id);
END IF;
RETURN remaining_stack_size;
END $function$
-- delete_item(in_id bigint) -> void
-- oid: 58214 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.delete_item(in_id bigint)
RETURNS void
LANGUAGE sql
AS $function$
DELETE FROM items i
USING inventories inv
WHERE i.inventory_id = inv.id
AND i.id = in_id
RETURNING _add_item_delete_log(
i.id,
inv.id,
i.template_id
);
$function$
-- delete_items(in_ids bigint[]) -> void
-- oid: 58215 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.delete_items(in_ids bigint[])
RETURNS void
LANGUAGE sql
AS $function$
DELETE FROM items i
USING inventories inv
WHERE i.inventory_id = inv.id
AND i.id = ANY (in_ids)
RETURNING _add_item_delete_log(
i.id,
inv.id,
i.template_id
);
$function$
-- delete_items_from_actor(in_actor_id bigint) -> void
-- oid: 58216 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.delete_items_from_actor(in_actor_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
PERFORM delete_items(
(
SELECT array_agg(i.id)
FROM items i
JOIN inventories inv ON inv.id = i.inventory_id
WHERE inv.actor_id = in_actor_id
)
);
END $function$
-- delete_journey_story_ids(story_ids text[]) -> void
-- oid: 58217 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.delete_journey_story_ids(story_ids text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM journey_story_node
WHERE story_node_id = ANY(story_ids);
DELETE FROM journey_story_node_cooldown
WHERE story_node_id = ANY(story_ids);
END;
$function$
-- delete_journey_story_node(in_account_id bigint, in_story_node_id text) -> void
-- oid: 58218 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.delete_journey_story_node(in_account_id bigint, in_story_node_id text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM journey_story_node WHERE account_id = in_account_id AND story_node_id = in_story_node_id;
END $function$
-- delete_journey_story_nodes_for_group_for_player(in_account_id bigint, in_reset_group dune.journeystoryresetgroup) -> void
-- oid: 58219 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.delete_journey_story_nodes_for_group_for_player(in_account_id bigint, in_reset_group dune.journeystoryresetgroup)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM journey_story_node_cooldown
WHERE story_node_id IN (
SELECT story_node_id
FROM journey_story_node a
WHERE a.account_id = in_account_id AND a.reset_group = in_reset_group
)
AND account_id = in_account_id;
DELETE FROM journey_story_node
WHERE reset_group = in_reset_group AND account_id = in_account_id;
END $function$
-- delete_journey_story_nodes_for_player(in_player_id text, in_story_node_ids text[]) -> void
-- oid: 58220 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.delete_journey_story_nodes_for_player(in_player_id text, in_story_node_ids text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM journey_story_node
WHERE story_node_id = ANY(in_story_node_ids)
AND account_id IN (
SELECT id
FROM accounts a
WHERE a.user = in_player_id
);
DELETE FROM journey_story_node_cooldown
WHERE story_node_id = ANY(in_story_node_ids)
AND account_id IN (
SELECT id
FROM accounts a
WHERE a.user = in_player_id
);
END $function$
-- delete_journey_story_nodes_for_player_account(in_account_id bigint, in_story_node_ids text[]) -> void
-- oid: 58221 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.delete_journey_story_nodes_for_player_account(in_account_id bigint, in_story_node_ids text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM journey_story_node
WHERE story_node_id = ANY(in_story_node_ids)
AND account_id = in_account_id;
DELETE FROM journey_story_node_cooldown
WHERE story_node_id = ANY(in_story_node_ids)
AND account_id = in_account_id;
END $function$
-- delete_map_markers(in_dimension_index integer, in_map_name text, in_player_marker_data dune.deleteplayermarkerdata[]) -> void
-- oid: 58222 kind: FUNCTION category: markers
CREATE OR REPLACE FUNCTION dune.delete_map_markers(in_dimension_index integer, in_map_name text, in_player_marker_data dune.deleteplayermarkerdata[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
WITH player_hash_ids AS (SELECT player_id, marker_hash_ids FROM UNNEST(in_player_marker_data)),
player_markers_to_delete AS (
SELECT player_markers.* FROM player_hash_ids, player_markers JOIN map_names USING(map_name_id)
WHERE (player_markers.dimension_index = in_dimension_index OR player_markers.dimension_index = -1)
AND map_names.map_name = in_map_name
AND player_markers.player_id = player_hash_ids.player_id
AND player_markers.marker_hash_id = ANY(player_hash_ids.marker_hash_ids)
ORDER BY player_markers.player_id, player_markers.marker_hash_id, player_markers.dimension_index FOR UPDATE -- Ordering for deadlock avoidance
)
DELETE FROM player_markers USING player_markers_to_delete
WHERE player_markers.dimension_index = player_markers_to_delete.dimension_index
AND player_markers.map_name_id = player_markers_to_delete.map_name_id
AND player_markers.player_id = player_markers_to_delete.player_id
AND player_markers.marker_hash_id = player_markers_to_delete.marker_hash_id;
END;
$function$
-- delete_markers_by_id(in_marker_ids integer[]) -> void
-- oid: 58223 kind: FUNCTION category: markers
CREATE OR REPLACE FUNCTION dune.delete_markers_by_id(in_marker_ids integer[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM markers WHERE marker_hash_id = ANY (in_marker_ids);
END
$function$
-- delete_markers_by_static_location_key(p_location_key text) -> void
-- oid: 58224 kind: FUNCTION category: markers
CREATE OR REPLACE FUNCTION dune.delete_markers_by_static_location_key(p_location_key text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM markers WHERE (payload #>> '{LocationKey}') = p_location_key;
END;
$function$
-- delete_markers_for_all_players(in_marker_types_to_keep text[], in_map text) -> void
-- oid: 58225 kind: FUNCTION category: markers
CREATE OR REPLACE FUNCTION dune.delete_markers_for_all_players(in_marker_types_to_keep text[], in_map text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
-- Lock markers matching query
WITH affected_markers AS (
SELECT * FROM markers JOIN map_names USING(map_name_id)
WHERE (in_map IS NULL OR map_names.map_name = in_map) AND NOT (marker).marker_type = ANY(in_marker_types_to_keep)
ORDER BY marker_hash_id, dimension_index, map_name_id FOR UPDATE -- Ordering to avoid deadlocks
),
-- Lock player_markers to be deleted on cascade
referencing_player_markers AS (
SELECT player_markers.* FROM player_markers, affected_markers
WHERE affected_markers.marker_hash_id = player_markers.marker_hash_id
AND affected_markers.dimension_index = player_markers.dimension_index
AND affected_markers.map_name_id = player_markers.map_name_id
ORDER BY player_id, player_markers.marker_hash_id, player_markers.dimension_index, player_markers.map_name_id FOR UPDATE -- Ordering to avoid deadlocks
)
-- Delete markers
DELETE FROM markers USING affected_markers
WHERE affected_markers.marker_hash_id = markers.marker_hash_id
AND affected_markers.dimension_index = markers.dimension_index
AND affected_markers.map_name_id = markers.map_name_id;
END;
$function$
-- delete_markers_return_actor_ids(in_dimension_index integer, in_map_name text, in_marker_ids integer[]) -> TABLE(actor_id bigint, marker_id integer)
-- oid: 58226 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.delete_markers_return_actor_ids(in_dimension_index integer, in_map_name text, in_marker_ids integer[])
RETURNS TABLE(actor_id bigint, marker_id integer)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
-- Lock markers matching query
WITH affected_markers AS (
SELECT * FROM markers JOIN map_names USING(map_name_id)
WHERE (dimension_index = in_dimension_index OR dimension_index = -1)
AND map_names.map_name = in_map_name
AND marker_hash_id = ANY(in_marker_ids)
ORDER BY marker_hash_id, dimension_index FOR UPDATE -- Ordering to avoid deadlocks
),
-- Lock player_markers to be deleted on cascade
referencing_player_markers AS (
SELECT player_id, player_markers.marker_hash_id FROM player_markers, affected_markers
WHERE affected_markers.marker_hash_id = player_markers.marker_hash_id
AND affected_markers.dimension_index = player_markers.dimension_index
AND affected_markers.map_name_id = player_markers.map_name_id
ORDER BY player_id, player_markers.marker_hash_id, player_markers.dimension_index FOR UPDATE -- Ordering to avoid deadlocks
),
-- Delete markers
deleted_markers AS (
DELETE FROM markers USING affected_markers
WHERE affected_markers.marker_hash_id = markers.marker_hash_id
AND affected_markers.dimension_index = markers.dimension_index
AND affected_markers.map_name_id = markers.map_name_id
)
SELECT * FROM referencing_player_markers;
END
$function$
-- delete_mnemonic_recall_lesson(in_account_id bigint, in_lesson_id text) -> void
-- oid: 58227 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.delete_mnemonic_recall_lesson(in_account_id bigint, in_lesson_id text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM mnemonic_recall WHERE account_id = in_account_id AND lesson_id = in_lesson_id;
END
$function$
-- delete_mnemonic_recall_lesson_all(in_account_id bigint) -> void
-- oid: 58228 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.delete_mnemonic_recall_lesson_all(in_account_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM mnemonic_recall WHERE account_id = in_account_id;
END
$function$
-- delete_server_player_access_codes(in_account_id bigint, in_access_code integer, in_access_code_type integer) -> void
-- oid: 58229 kind: FUNCTION category: server
CREATE OR REPLACE FUNCTION dune.delete_server_player_access_codes(in_account_id bigint, in_access_code integer, in_access_code_type integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM player_access_codes
WHERE account_id = in_account_id
AND access_code = in_access_code
AND access_code_type = in_access_code_type;
END
$function$
-- delete_spawner(in_map text, in_name text, in_dimension_index integer) -> void
-- oid: 58230 kind: FUNCTION category: spawner
CREATE OR REPLACE FUNCTION dune.delete_spawner(in_map text, in_name text, in_dimension_index integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM actor_spawners WHERE map = in_map AND name = in_name AND dimension_index = in_dimension_index;
END
$function$
-- delete_static_location_markers(p_location_keys text[]) -> void
-- oid: 58231 kind: FUNCTION category: markers
CREATE OR REPLACE FUNCTION dune.delete_static_location_markers(p_location_keys text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM markers WHERE (payload #>> '{LocationKey}') = ANY(p_location_keys);
END;
$function$
-- delete_world_partition_by_map_id(in_map_id text) -> void
-- oid: 58232 kind: FUNCTION category: map_areas
CREATE OR REPLACE FUNCTION dune.delete_world_partition_by_map_id(in_map_id text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM world_partition WHERE "map"=in_map_id;
END
$function$
-- demote_guild_member(in_guild_id bigint, in_player_id bigint, in_new_role smallint) -> void
-- oid: 58233 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.demote_guild_member(in_guild_id bigint, in_player_id bigint, in_new_role smallint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
PERFORM guilds_get_exclusive_operation_lock();
-- check if new admin is actualy in guild
IF NOT EXISTS(SELECT FROM guild_members WHERE player_id = in_player_id AND guild_id = in_guild_id) THEN
RAISE EXCEPTION 'Trying to demote player not in guild %.', in_player_id;
END IF;
IF is_player_guild_admin(in_player_id, in_guild_id) THEN
RAISE EXCEPTION 'Trying to demote admin. promote a member to admin instead.';
END IF;
if in_new_role = 100 THEN
RAISE EXCEPTION 'Trying to demote to admin.';
END IF;
-- set new player to new role
UPDATE guild_members SET role_id = in_new_role WHERE player_id = in_player_id AND guild_id = in_guild_id;
PERFORM pg_notify('guild_notify_channel', format('demote_player#{"PlayerId" : %s , "GuildId" : %s, "NewRole" : %s}', in_player_id, in_guild_id, in_new_role));
END
$function$
-- determine_partition_label(in_map text, in_dimension_index integer, in_label text, in_allow_overwrite boolean, in_partition_id bigint) -> text
-- oid: 58234 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.determine_partition_label(in_map text, in_dimension_index integer, in_label text DEFAULT NULL::text, in_allow_overwrite boolean DEFAULT true, in_partition_id bigint DEFAULT NULL::bigint)
RETURNS text
LANGUAGE plpgsql
AS $function$
DECLARE
result_label TEXT := in_label;
tmp_count INTEGER;
tmp_box_max_x TEXT;
tmp_box_max_y TEXT;
tmp_box_min_x TEXT;
tmp_box_min_y TEXT;
BEGIN
-- If label is provided and we don't want to overwrite, return it
IF result_label IS NOT NULL AND in_allow_overwrite = FALSE THEN
RETURN result_label;
END IF;
CASE in_map
WHEN 'SH_HarkoVillage' THEN return 'HarkoVillage' || '_' || in_dimension_index;
WHEN 'SH_Arrakeen' THEN return 'Arrakeen' || '_' || in_dimension_index;
WHEN 'SH_FallenLight' THEN return 'FallenLight' || '_' || in_dimension_index;
WHEN 'CB_Story_Hephaestus' THEN return 'WreckOfHephaestus' || '_' || in_dimension_index;
WHEN 'CB_Story_Ecolab_Carthag' THEN return 'BeneathCarthag' || '_' || in_dimension_index;
WHEN 'CB_SurvivalChallenge_Station_15' THEN return 'Station15' || '_' || in_dimension_index;
WHEN 'CB_Story_WaterFatManor' THEN return 'WaterFat' || '_' || in_dimension_index;
WHEN 'Story_ProcesVerbal' THEN return 'ProcesVerbal' || '_' || in_dimension_index;
WHEN 'DLC_Story_LostHarvest' THEN return 'LostHarvest' || '_' || in_dimension_index;
WHEN 'DLC_Story_LostHarvest_EcolabA' THEN return 'LostHarvest_EcolabA' || '_' || in_dimension_index;
WHEN 'DLC_Story_LostHarvest_EcolabB' THEN return 'LostHarvest_EcolabB' || '_' || in_dimension_index;
WHEN 'DLC_Story_LostHarvest_ForgottenLab' THEN return 'LostHarvest_ForgottenLab' || '_' || in_dimension_index;
WHEN 'Story_ArtOfKanly' THEN return 'ArtOfKanly' || '_' || in_dimension_index;
WHEN 'Story_HeighlinerDungeon' THEN return 'HeighlinerDungeon' || '_' || in_dimension_index;
WHEN 'CB_Dungeon_Hephaestus' THEN return 'WreckOfHephaestusDungeon' || '_' || in_dimension_index;
WHEN 'CB_Dungeon_OldCarthag' THEN return 'OldCarthagDungeon' || '_' || in_dimension_index;
WHEN 'CB_Story_BanditFortress01' THEN return 'SandfliesFortress' || '_' || in_dimension_index;
WHEN 'CB_Overland_S_05' THEN return 'ClosedOffTestingStationIsland' || '_' || in_dimension_index;
WHEN 'CB_Overland_S_06' THEN return 'GroundVehicleTimeTrialIsland' || '_' || in_dimension_index;
WHEN 'CB_Overland_S_04' THEN return 'ErythriteCaveIsland' || '_' || in_dimension_index;
WHEN 'CB_Overland_M_01' THEN return 'RadioactiveShipwreck' || '_' || in_dimension_index;
WHEN 'Story_Faction_Outpost_Hark' THEN return 'Story_Faction_Outpost_Hark' || '_' || in_dimension_index;
WHEN 'Story_Faction_Outpost_Atre' THEN return 'Story_Faction_Outpost_Atre' || '_' || in_dimension_index;
WHEN 'CB_Ecolab_Bronze_Green_089' THEN return 'RadiationDungeon' || '_' || in_dimension_index;
WHEN 'CB_Ecolab_Bronze_Green_152' THEN return 'ElectricityDungeon' || '_' || in_dimension_index;
WHEN 'CB_Ecolab_Bronze_Green_195' THEN return 'PoisonDungeon' || '_' || in_dimension_index;
WHEN 'CB_Ecolab_Bronze_Green_024' THEN return 'DarknessDungeon' || '_' || in_dimension_index;
WHEN 'CB_Ecolab_Bronze_Green_136' THEN return 'FireDungeon' || '_' || in_dimension_index;
WHEN 'CB_Overland_S_07' THEN return 'TheRuinsOfTsimpo' || '_' || in_dimension_index;
WHEN 'CB_Story_DestroyedZanovar' THEN return 'DestroyedZanovar' || '_' || in_dimension_index;
WHEN 'CB_Story_OrbitalMonitor' THEN return 'OrbitalMonitor' || '_' || in_dimension_index;
WHEN 'CB_Dungeon_TheFacility' THEN return 'FacilityDungeon' || '_' || in_dimension_index;
WHEN 'CB_Dungeon_ThePit' THEN return 'PitDungeon' || '_' || in_dimension_index;
WHEN 'Overmap' THEN
IF in_dimension_index = 0 THEN
return 'Overland';
END IF;
WHEN 'Survival_1' THEN
CASE in_dimension_index
WHEN 0 THEN return 'Abbir';
WHEN 1 THEN return 'Alraab';
WHEN 2 THEN return 'Barkan';
WHEN 3 THEN return 'Coanua';
WHEN 4 THEN return 'Fajr Kulon';
WHEN 5 THEN return 'Gara';
WHEN 6 THEN return 'Hajar';
WHEN 7 THEN return 'Jacurutu';
WHEN 8 THEN return 'Kathib';
WHEN 9 THEN return 'Legg';
WHEN 10 THEN return 'Makab';
WHEN 11 THEN return 'Nadir';
WHEN 12 THEN return 'Ramal';
WHEN 13 THEN return 'Rifana';
WHEN 14 THEN return 'Sandrat';
WHEN 15 THEN return 'Saajid';
WHEN 16 THEN return 'Tabr Sink';
WHEN 17 THEN return 'Tharwa';
WHEN 18 THEN return 'Umbu';
WHEN 19 THEN return 'Yaracuwan';
WHEN 20 THEN return 'al-Mut';
WHEN 21 THEN return 'Altuyur';
WHEN 22 THEN return 'Ammit';
WHEN 23 THEN return 'Ashia';
WHEN 24 THEN return 'Eaqrab';
WHEN 25 THEN return 'Hagga';
WHEN 26 THEN return 'Hua';
WHEN 27 THEN return 'Katal';
WHEN 28 THEN return 'Khafash';
WHEN 29 THEN return 'Matar';
WHEN 30 THEN return 'Rabie';
WHEN 31 THEN return 'Rajifiri';
WHEN 32 THEN return 'Remmel';
WHEN 33 THEN return 'Sahr';
WHEN 34 THEN return 'Saqer';
WHEN 35 THEN return 'Ta''lab';
WHEN 36 THEN return 'Tarl';
WHEN 37 THEN return 'Tasmin Sink';
WHEN 38 THEN return 'Thueban';
WHEN 39 THEN return 'Tuono';
ELSE
return 'Survival' || '_' || in_dimension_index;
END CASE;
ELSE
-- Do nothing
END CASE;
-- DeepDesert per-partition handling: if all 9 partitions exist, assign based on ordering
IF in_map = 'DeepDesert_1' THEN
SELECT count(*) INTO tmp_count FROM world_partition WHERE map = 'DeepDesert_1' AND dimension_index = in_dimension_index;
IF tmp_count = 9 AND in_partition_id IS NOT NULL THEN
RETURN (
WITH chess_notation AS (
SELECT label, ROW_NUMBER() OVER (ORDER BY null) AS row_num
FROM (select * from UNNEST(ARRAY['A1', 'B1', 'C1', 'A2', 'B2', 'C2', 'A3', 'B3', 'C3']) as label) as label_list
),
partitions AS (
SELECT partition_id, ROW_NUMBER() OVER (
order by
coalesce(partition_definition->'box', partition_definition->'boxes'->0)->>'max_x',
coalesce(partition_definition->'box', partition_definition->'boxes'->0)->>'max_y',
coalesce(partition_definition->'box', partition_definition->'boxes'->0)->>'min_x',
coalesce(partition_definition->'box', partition_definition->'boxes'->0)->>'min_y'
) AS row_num
FROM world_partition
where map = 'DeepDesert_1' AND dimension_index = in_dimension_index
)
SELECT 'DeepDesert_' || cn.label
FROM chess_notation cn
JOIN partitions p ON cn.row_num = p.row_num
WHERE p.partition_id = in_partition_id
LIMIT 1
);
END IF;
END IF;
IF in_partition_id IS NOT NULL THEN
SELECT coalesce(partition_definition->'box', partition_definition->'boxes'->0)->>'max_x', coalesce(partition_definition->'box', partition_definition->'boxes'->0)->>'max_y', coalesce(partition_definition->'box', partition_definition->'boxes'->0)->>'min_x', coalesce(partition_definition->'box', partition_definition->'boxes'->0)->>'min_y'
INTO tmp_box_max_x, tmp_box_max_y, tmp_box_min_x, tmp_box_min_y
FROM world_partition
WHERE partition_id = in_partition_id
LIMIT 1;
IF tmp_box_max_x IS NOT NULL AND tmp_box_max_x IN ('1.0','1') AND tmp_box_max_y IN ('1.0','1') AND tmp_box_min_x IN ('0.0','0') AND tmp_box_min_y IN ('0.0','0') THEN
RETURN upgrade_map_name(in_map) || '_' || in_dimension_index;
END IF;
ELSE
-- If we don't have a partition id, try to read any partition for that map/dimension
SELECT coalesce(partition_definition->'box', partition_definition->'boxes'->0)->>'max_x', coalesce(partition_definition->'box', partition_definition->'boxes'->0)->>'max_y', coalesce(partition_definition->'box', partition_definition->'boxes'->0)->>'min_x', coalesce(partition_definition->'box', partition_definition->'boxes'->0)->>'min_y'
INTO tmp_box_max_x, tmp_box_max_y, tmp_box_min_x, tmp_box_min_y
FROM world_partition
WHERE map = in_map AND dimension_index = in_dimension_index
LIMIT 1;
IF tmp_box_max_x IS NOT NULL AND tmp_box_max_x IN ('1.0','1') AND tmp_box_max_y IN ('1.0','1') AND tmp_box_min_x IN ('0.0','0') AND tmp_box_min_y IN ('0.0','0') THEN
RETURN upgrade_map_name(in_map) || '_' || in_dimension_index;
END IF;
END IF;
RETURN NULL;
END;
$function$
-- determine_partition_label_trigger() -> trigger
-- oid: 58236 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.determine_partition_label_trigger()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
IF NEW.label IS NULL THEN
UPDATE world_partition
SET label = determine_partition_label(NEW.map, NEW.dimension_index, NULL, false)
WHERE partition_id = NEW.partition_id;
END IF;
RETURN NULL; -- AFTER ROW trigger does not need to return a row
END;
$function$
-- disband_guild(in_guild_id bigint) -> void
-- oid: 58237 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.disband_guild(in_guild_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
out_guild_name TEXT;
members_list BIGINT[];
BEGIN
PERFORM guilds_get_exclusive_operation_lock();
-- check if guild exists
SELECT guild_name INTO out_guild_name FROM guilds WHERE guild_id = in_guild_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Trying to disband non existing guild %.', in_guild_id;
END IF;
-- get members list
members_list := ARRAY(SELECT player_id FROM guild_members WHERE guild_id = in_guild_id);
-- delete
DELETE FROM guilds WHERE guild_id = in_guild_id;
PERFORM pg_notify('guild_notify_channel', format('guild_disband#{"GuildId" : %s , "GuildName" : "%s", "PlayerIds" : %s}', in_guild_id, out_guild_name, to_json(members_list)::text));
END
$function$
-- disband_party(in_party_id bigint) -> void
-- oid: 58238 kind: FUNCTION category: party
CREATE OR REPLACE FUNCTION dune.disband_party(in_party_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
invite_ids BIGINT[];
sender_ids BIGINT[];
player_ids BIGINT[];
BEGIN
PERFORM parties_get_exclusive_operation_lock();
DELETE FROM party_members WHERE party_id = in_party_id;
DELETE FROM parties WHERE party_id = in_party_id;
DELETE FROM platform_parties_mapping WHERE dune_party_id = in_party_id;
WITH removed_invites AS (
DELETE FROM party_invites
WHERE party_id = in_party_id
RETURNING *
) SELECT array_agg(invite_id), array_agg(sender_player_id), array_agg(player_id) from removed_invites INTO invite_ids, sender_ids, player_ids;
PERFORM pg_notify('party_notify_channel', format('disband_party#{"PartyId" : %s, "InviteIds" : [%s] , "SenderIds" : [%s] , "PlayerIds" : [%s]}',
in_party_id, ARRAY_TO_STRING(invite_ids, ','), ARRAY_TO_STRING(sender_ids, ','), ARRAY_TO_STRING(player_ids, ',')));
END
$function$
-- downgrade_map_name(in_map_name text) -> text
-- oid: 58239 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.downgrade_map_name(in_map_name text)
RETURNS text
LANGUAGE plpgsql
AS $function$
BEGIN
return
CASE in_map_name
WHEN 'HaggaBasin' THEN 'Survival_1'
WHEN 'HarkoVillage' THEN 'SH_HarkoVillage'
WHEN 'DeepDesert' THEN 'DeepDesert_1'
WHEN 'Overland' THEN 'Overmap'
WHEN 'Arrakeen' THEN 'SH_Arrakeen'
WHEN 'WreckOfHephaestus' THEN 'CB_Story_Hephaestus'
WHEN 'BeneathCarthag' THEN 'CB_Story_Ecolab_Carthag'
WHEN 'Station15' THEN 'CB_SurvivalChallenge_Station_15'
WHEN 'WaterFat' THEN 'CB_Story_WaterFatManor'
WHEN 'FallenLight' THEN 'SH_FallenLight'
WHEN 'ProcesVerbal' THEN 'Story_ProcesVerbal'
WHEN 'LostHarvest' THEN 'DLC_Story_LostHarvest'
WHEN 'LostHarvest_EcolabA' THEN 'DLC_Story_LostHarvest_EcolabA'
WHEN 'LostHarvest_EcolabB' THEN 'DLC_Story_LostHarvest_EcolabB'
WHEN 'LostHarvest_ForgottenLab' THEN 'DLC_Story_LostHarvest_ForgottenLab'
WHEN 'ArtOfKanly' THEN 'Story_ArtOfKanly'
WHEN 'HeighlinerDungeon' THEN 'Story_HeighlinerDungeon'
WHEN 'WreckOfHephaestusDungeon' THEN 'CB_Dungeon_Hephaestus'
WHEN 'OldCarthagDungeon' THEN 'CB_Dungeon_OldCarthag'
WHEN 'SandfliesFortress' THEN 'CB_Story_BanditFortress01'
WHEN 'ClosedOffTestingStationIsland' THEN 'CB_Overland_S_05'
WHEN 'GroundVehicleTimeTrialIsland' THEN 'CB_Overland_S_06'
WHEN 'ErythriteCaveIsland' THEN 'CB_Overland_S_04'
WHEN 'RadioactiveShipwreck' THEN 'CB_Overland_M_01'
WHEN 'TheRuinsOfTsimpo' THEN 'CB_Overland_S_07'
WHEN 'Story_Faction_Outpost_Hark' THEN 'Story_Faction_Outpost_Hark'
WHEN 'Story_Faction_Outpost_Atre' THEN 'Story_Faction_Outpost_Atre'
WHEN 'RadiationDungeon' THEN 'CB_Ecolab_Bronze_Green_089'
WHEN 'ElectricityDungeon' THEN 'CB_Ecolab_Bronze_Green_152'
WHEN 'PoisonDungeon' THEN 'CB_Ecolab_Bronze_Green_195'
WHEN 'DarknessDungeon' THEN 'CB_Ecolab_Bronze_Green_024'
WHEN 'FireDungeon' THEN 'CB_Ecolab_Bronze_Green_136'
WHEN 'DestroyedZanovar' THEN 'CB_Story_DestroyedZanovar'
WHEN 'OrbitalMonitor' THEN 'CB_Story_OrbitalMonitor'
WHEN 'FacilityDungeon' THEN 'CB_Dungeon_TheFacility'
WHEN 'PitDungeon' THEN 'CB_Dungeon_ThePit'
WHEN 'WindPass' THEN 'CB_Overland_S_08'
ELSE in_map_name
END;
END;
$function$
-- drain_item_tracking_data() -> TABLE(function_name dune.itemtrackingfunctiontype, item_id bigint, account_id bigint, inventory_id bigint, template_id text, event_time timestamp without time zone, position_index bigint)
-- oid: 58240 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.drain_item_tracking_data()
RETURNS TABLE(function_name dune.itemtrackingfunctiontype, item_id bigint, account_id bigint, inventory_id bigint, template_id text, event_time timestamp without time zone, position_index bigint)
LANGUAGE sql
AS $function$
DELETE FROM item_operations_staging_table
RETURNING
function_name,
item_id,
account_id,
inventory_id,
template_id,
event_time AT TIME ZONE 'UTC',
position_index;
$function$
-- dune_exchange_add_sell_order(in_exchange_id bigint, in_access_point_id bigint, in_owner_id bigint, in_max_orders_per_player integer, in_expiration_time bigint, in_item_id bigint, in_count bigint, in_category_mask integer, in_category_depth smallint, in_durability_cur real, in_durability_max real, in_item_price bigint, in_wear_normalized_item_price bigint, in_quality_level bigint, in_solari_cost bigint) -> dune.duneexchangeaddsellorderresult
-- oid: 58241 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.dune_exchange_add_sell_order(in_exchange_id bigint, in_access_point_id bigint, in_owner_id bigint, in_max_orders_per_player integer, in_expiration_time bigint, in_item_id bigint, in_count bigint, in_category_mask integer, in_category_depth smallint, in_durability_cur real, in_durability_max real, in_item_price bigint, in_wear_normalized_item_price bigint, in_quality_level bigint, in_solari_cost bigint)
RETURNS dune.duneexchangeaddsellorderresult
LANGUAGE plpgsql
AS $function$
DECLARE
exchange_inventory_id BIGINT;
user_id BIGINT;
new_item_id BIGINT;
item_template_id TEXT;
result DuneExchangeAddSellOrderResult;
fls_id TEXT;
BEGIN
result.order_id = 0;
SELECT INTO result.order_slots_used get_dune_exchange_used_order_slots(in_owner_id);
IF result.order_slots_used >= in_max_orders_per_player THEN
RETURN result;
END IF;
PERFORM * from dune_exchange_orders where item_id = in_item_id;
IF FOUND THEN
-- Debug logging
SELECT acc."user"
INTO fls_id
FROM accounts acc
JOIN player_state ps on ps.account_id = acc.id
WHERE ps.player_controller_id = in_owner_id
LIMIT 1;
PERFORM log_cheating(fls_id, 'exchange_order_dupe');
RAISE WARNING 'Trying to dupe exchange sell orders FLS: %', fls_id;
RETURN result;
END IF;
SELECT INTO user_id dune_exchange_get_user_id(in_owner_id);
SELECT INTO exchange_inventory_id get_exchange_inventory_id(in_exchange_id);
IF exchange_inventory_id IS NULL THEN
RETURN result;
END IF;
UPDATE dune_exchange_users SET solari_balance = solari_balance - in_solari_cost WHERE id = user_id AND solari_balance >= in_solari_cost;
IF NOT FOUND THEN
RETURN result;
END IF;
SELECT INTO STRICT item_template_id template_id FROM items WHERE id = in_item_id FOR UPDATE;
INSERT INTO dune_exchange_orders(exchange_id, access_point_id, owner_id, expiration_time, template_id, durability_cur, durability_max, category_mask, category_depth, item_price, quality_level)
VALUES(in_exchange_id, in_access_point_id, in_owner_id, in_expiration_time, item_template_id, in_durability_cur, in_durability_max, in_category_mask, in_category_depth, in_item_price, in_quality_level)
RETURNING id INTO result.order_id;
INSERT INTO dune_exchange_sell_orders(order_id, initial_stack_size, wear_normalized_price) VALUES(result.order_id, in_count, in_wear_normalized_item_price);
SELECT INTO new_item_id move_inventory_item(in_item_id, exchange_inventory_id, result.order_id, in_count);
IF new_item_id IS NULL THEN
RAISE EXCEPTION 'Failed to move inventory item %', in_item_id;
END IF;
UPDATE dune_exchange_orders SET item_id = new_item_id WHERE id = result.order_id;
result.order_slots_used = result.order_slots_used + 1;
RETURN result;
END $function$
-- dune_exchange_cancel_order(in_order_id bigint, in_purge_time bigint, in_completion_type integer) -> void
-- oid: 58242 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.dune_exchange_cancel_order(in_order_id bigint, in_purge_time bigint, in_completion_type integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
BEGIN
DELETE FROM dune_exchange_sell_orders WHERE order_id = in_order_id;
IF NOT FOUND THEN
RETURN;
END IF;
WITH item_stack_size AS
(SELECT item.stack_size
FROM dune_exchange_orders ord
JOIN items item ON ord.item_id = item.id
WHERE ord.id = in_order_id)
INSERT INTO dune_exchange_fulfilled_orders(order_id, completion_type, stack_size, original_order_id) VALUES(in_order_id, in_completion_type, (SELECT * FROM item_stack_size), in_order_id);
UPDATE dune_exchange_orders SET expiration_time = in_purge_time, revision = revision + 1 WHERE id = in_order_id;
END $function$
-- dune_exchange_expire_orders(in_exchange_id bigint, in_current_time bigint, in_purge_time bigint, in_expired_completion_type integer) -> SETOF dune.exchangeexpiredorder
-- oid: 58243 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.dune_exchange_expire_orders(in_exchange_id bigint, in_current_time bigint, in_purge_time bigint, in_expired_completion_type integer)
RETURNS SETOF dune.exchangeexpiredorder
LANGUAGE plpgsql
AS $function$
DECLARE
cur_order RECORD;
order_is_npc_order BOOLEAN;
BEGIN
FOR cur_order IN
SELECT
ord.id AS order_id,
ord.owner_id AS owner_id,
ord.is_npc_order AS is_npc_order,
item.stack_size AS stack_size,
ord.item_price AS item_price,
ord.quality_level AS quality_level
FROM dune_exchange_orders ord
JOIN dune_exchange_sell_orders sord ON (ord.id = sord.order_id)
JOIN items item ON (ord.item_id = item.id)
WHERE ord.exchange_id = in_exchange_id AND ord.expiration_time IS NOT NULL AND in_current_time >= ord.expiration_time
FOR UPDATE
LOOP
IF NOT cur_order.is_npc_order THEN
-- Make an item_storage record for the order item.
DELETE FROM dune_exchange_sell_orders WHERE order_id = cur_order.order_id;
INSERT INTO dune_exchange_fulfilled_orders(order_id, completion_type, stack_size, original_order_id) VALUES(cur_order.order_id, in_expired_completion_type, cur_order.stack_size, cur_order.order_id);
UPDATE dune_exchange_orders SET revision = revision + 1, expiration_time = in_purge_time WHERE id = cur_order.order_id;
RETURN NEXT (
cur_order.order_id,
cur_order.owner_id,
in_expired_completion_type,
cur_order.stack_size,
cur_order.item_price,
cur_order.order_id,
cur_order.quality_level);
ELSE
-- Delete the order. Will cascade into the items table.
DELETE FROM dune_exchange_orders WHERE id = order_id;
END IF;
END LOOP;
END $function$
-- dune_exchange_fulfill_sell_order(in_exchange_id bigint, in_max_orders_per_player integer, in_purchased_completion_type integer, in_sold_completion_type integer, in_instigator_id bigint, in_order_id bigint, in_order_revision bigint, in_dst_inventory_id bigint, in_dst_index bigint, in_count bigint, in_solaris_fee bigint, in_purge_time bigint) -> dune.duneexchangefulfillsellorderresult
-- oid: 58244 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.dune_exchange_fulfill_sell_order(in_exchange_id bigint, in_max_orders_per_player integer, in_purchased_completion_type integer, in_sold_completion_type integer, in_instigator_id bigint, in_order_id bigint, in_order_revision bigint, in_dst_inventory_id bigint, in_dst_index bigint, in_count bigint, in_solaris_fee bigint, in_purge_time bigint)
RETURNS dune.duneexchangefulfillsellorderresult
LANGUAGE plpgsql
AS $function$
DECLARE
exchange_inventory_id BIGINT;
order_access_point_id BIGINT;
seller_actor_id BIGINT;
seller_user_id BIGINT;
buyer_user_id BIGINT;
user_solari_balance BIGINT;
per_item_price BIGINT;
total_cost BIGINT;
order_revision BIGINT;
order_item_id BIGINT;
order_is_npc_order BOOLEAN;
order_category_mask INT;
order_category_depth SMALLINT;
item_template_id TEXT;
item_durability_cur REAL;
item_durability_max REAL;
storage_order_id BIGINT;
log_order_id BIGINT;
result DuneExchangeFulfillSellOrderResult;
BEGIN
result.item_id = 0;
SELECT INTO result.order_slots_used get_dune_exchange_used_order_slots(in_instigator_id);
IF result.order_slots_used >= in_max_orders_per_player THEN
RETURN result;
END IF;
SELECT INTO exchange_inventory_id get_exchange_inventory_id(in_exchange_id);
SELECT INTO buyer_user_id dune_exchange_get_user_id(in_instigator_id);
BEGIN
SELECT INTO STRICT user_solari_balance solari_balance FROM dune_exchange_users WHERE id = buyer_user_id FOR UPDATE;
SELECT INTO STRICT
order_revision,
order_access_point_id,
order_item_id,
item_template_id,
item_durability_cur,
item_durability_max,
seller_actor_id,
order_category_mask,
order_category_depth,
order_is_npc_order,
per_item_price
ord.revision,
ord.access_point_id,
ord.item_id,
ord.template_id,
ord.durability_cur,
ord.durability_max,
ord.owner_id,
ord.category_mask,
ord.category_depth,
ord.is_npc_order,
ord.item_price
FROM
dune_exchange_orders ord
JOIN dune_exchange_sell_orders sord ON (ord.id = sord.order_id)
WHERE
id = in_order_id AND revision = in_order_revision
FOR UPDATE;
EXCEPTION
WHEN NO_DATA_FOUND THEN RETURN result;
END;
IF order_revision != in_order_revision THEN
RETURN result;
END IF;
IF NOT order_is_npc_order THEN
SELECT INTO seller_user_id dune_exchange_get_user_id(seller_actor_id);
END IF;
total_cost = per_item_price * in_count + in_solaris_fee;
IF total_cost > user_solari_balance THEN
RETURN result;
END IF;
IF in_dst_inventory_id IS NULL THEN
-- Item is to be transferred to exchange storage rather than an external inventory. Make a record for it.
INSERT INTO dune_exchange_orders(exchange_id, access_point_id, owner_id, template_id, expiration_time, durability_cur, durability_max, item_price, category_mask, category_depth)
VALUES(in_exchange_id, order_access_point_id, in_instigator_id, item_template_id, in_purge_time, item_durability_cur, item_durability_max, per_item_price, order_category_mask, order_category_depth)
RETURNING id INTO storage_order_id;
INSERT INTO dune_exchange_fulfilled_orders(order_id, completion_type, stack_size, original_order_id)
VALUES(storage_order_id, in_purchased_completion_type, in_count, in_order_id);
in_dst_inventory_id = exchange_inventory_id;
in_dst_index = storage_order_id;
END IF;
UPDATE dune_exchange_orders SET item_id = NULL WHERE id = in_order_id;
SELECT INTO result.item_id move_inventory_item(order_item_id, in_dst_inventory_id, in_dst_index, in_count);
IF result.item_id IS NULL THEN
IF storage_order_id IS NOT NULL THEN
DELETE FROM dune_exchange_orders WHERE id = storage_order_id;
END IF;
RETURN result;
END IF;
-- Create an entry for the fulfilled orders log.
SELECT INTO log_order_id order_id FROM dune_exchange_fulfilled_orders where source_order_id = in_order_id FOR SHARE;
IF log_order_id IS NOT NULL THEN
UPDATE dune_exchange_orders SET expiration_time = in_purge_time, revision = revision + 1 WHERE id = log_order_id;
UPDATE dune_exchange_fulfilled_orders SET stack_size = stack_size + in_count WHERE order_id = log_order_id;
ELSE
INSERT INTO dune_exchange_orders(exchange_id, access_point_id, owner_id, template_id, expiration_time, durability_cur, durability_max, item_price, category_mask, category_depth)
VALUES(in_exchange_id, order_access_point_id, seller_actor_id, item_template_id, in_purge_time, item_durability_cur, item_durability_max, per_item_price, order_category_mask, order_category_depth) RETURNING id INTO log_order_id;
INSERT INTO dune_exchange_fulfilled_orders(order_id, source_order_id, completion_type, stack_size, original_order_id)
VALUES(log_order_id, in_order_id, in_sold_completion_type, in_count, in_order_id);
END IF;
UPDATE dune_exchange_users SET solari_balance = solari_balance - total_cost WHERE id = buyer_user_id;
-- If the new ID is equal to the old ID the item/stack was moved rather than split and the order is now empty
IF result.item_id = order_item_id THEN
DELETE FROM dune_exchange_orders WHERE id = in_order_id;
ELSE
UPDATE dune_exchange_orders SET item_id = order_item_id, revision = revision + 1 WHERE id = in_order_id; -- Item was split. Restore reference to remaining items.
END IF;
-- If the item was transferred to exchange storage rather than an external inventory, update the record with the item ID.
IF storage_order_id IS NOT NULL THEN
UPDATE dune_exchange_orders SET item_id = result.item_id WHERE id = storage_order_id;
END IF;
result.order_slots_used = result.order_slots_used + 1;
RETURN result;
END $function$
-- dune_exchange_get_item_price_stats(in_template_ids text[]) -> TABLE(template_id text, minimum bigint, average bigint)
-- oid: 58246 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.dune_exchange_get_item_price_stats(in_template_ids text[])
RETURNS TABLE(template_id text, minimum bigint, average bigint)
LANGUAGE plpgsql
AS $function$
DECLARE
average_price REAL;
minimum_price REAL;
BEGIN
RETURN QUERY SELECT ord.template_id, MIN(sord.wear_normalized_price), CAST(SUM(sord.wear_normalized_price * item.stack_size) / SUM(item.stack_size) AS BIGINT)
FROM dune_exchange_orders ord
JOIN items item ON item.id = ord.item_id
JOIN dune_exchange_sell_orders sord ON sord.order_id = ord.id
WHERE ord.template_id = ANY(in_template_ids) GROUP BY ord.template_id;
END $function$
-- dune_exchange_get_user_id(in_owner_id bigint) -> bigint
-- oid: 58247 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.dune_exchange_get_user_id(in_owner_id bigint)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
new_user_id BIGINT;
user_id BIGINT;
BEGIN
INSERT INTO dune_exchange_users(owner_id) VALUES(in_owner_id) ON CONFLICT DO NOTHING RETURNING id INTO new_user_id;
SELECT INTO user_id COALESCE(new_user_id, id) FROM dune_exchange_users WHERE owner_id = in_owner_id;
return user_id;
END $function$
-- dune_exchange_modify_user_solari_balance(in_controller_id bigint, in_solari_delta bigint) -> void
-- oid: 58248 kind: FUNCTION category: currency
CREATE OR REPLACE FUNCTION dune.dune_exchange_modify_user_solari_balance(in_controller_id bigint, in_solari_delta bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
user_id BIGINT;
current_balance BIGINT;
new_balance BIGINT;
delta_balance BIGINT;
fls_id TEXT;
function_oid oid;
BEGIN
SELECT INTO user_id dune_exchange_get_user_id(in_controller_id);
SELECT INTO current_balance balance from player_virtual_currency_balances WHERE currency_id = get_solaris_id() AND player_controller_id = in_controller_id;
IF current_balance < 0 THEN
SELECT acc."user"
INTO fls_id
FROM accounts acc
JOIN player_state ps on ps.account_id = acc.id
WHERE ps.player_controller_id = in_controller_id
LIMIT 1;
PERFORM log_cheating(COALESCE(fls_id, in_controller_id::text), 'exchange_negative_solaris');
UPDATE player_virtual_currency_balances SET balance = 0 WHERE currency_id = get_solaris_id() AND player_controller_id = in_controller_id;
current_balance = 0;
END IF;
delta_balance = in_solari_delta;
IF current_balance < in_solari_delta THEN
delta_balance = current_balance;
END IF;
UPDATE dune_exchange_users SET solari_balance = solari_balance + delta_balance WHERE id = user_id;
UPDATE player_virtual_currency_balances SET balance = balance - delta_balance WHERE currency_id = get_solaris_id() AND player_controller_id = in_controller_id RETURNING player_virtual_currency_balances.balance INTO new_balance;
GET DIAGNOSTICS function_oid = PG_ROUTINE_OID;
PERFORM log_event_solaris(function_oid, 'update_solaris', in_controller_id, new_balance, delta_balance);
END $function$
-- dune_exchange_purge_completed_orders(in_exchange_id bigint, in_current_time bigint) -> SETOF dune.exchangeexpiredorder
-- oid: 58249 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.dune_exchange_purge_completed_orders(in_exchange_id bigint, in_current_time bigint)
RETURNS SETOF dune.exchangeexpiredorder
LANGUAGE plpgsql
AS $function$
DECLARE
cur_order RECORD;
order_is_npc_order BOOLEAN;
BEGIN
FOR cur_order IN
SELECT
ord.id AS order_id,
ord.owner_id AS owner_id,
ord.is_npc_order AS is_npc_order,
sord.stack_size AS stack_size,
ord.item_price AS item_price,
sord.completion_type AS completion_type,
sord.original_order_id AS original_order_id,
ord.quality_level AS quality_level
FROM dune_exchange_orders ord
JOIN dune_exchange_fulfilled_orders sord ON (ord.id = sord.order_id)
WHERE ord.exchange_id = in_exchange_id AND ord.expiration_time IS NOT NULL AND in_current_time >= ord.expiration_time
FOR UPDATE
LOOP
DELETE FROM dune_exchange_orders WHERE id = cur_order.order_id;
RETURN NEXT (
cur_order.order_id,
cur_order.owner_id,
cur_order.completion_type,
cur_order.stack_size,
cur_order.item_price,
cur_order.original_order_id,
cur_order.quality_level);
END LOOP;
END $function$
-- dune_exchange_query_storage_item(in_order_id bigint) -> TABLE(completion_type integer, id bigint, revision bigint, expiration_time bigint, access_point_id bigint, ap_name text, owner_id bigint, item_id bigint, template_id text, stack_size bigint, item_price bigint, quality_level bigint, durability_cur real, durability_max real, dynamic_stats jsonb)
-- oid: 58250 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.dune_exchange_query_storage_item(in_order_id bigint)
RETURNS TABLE(completion_type integer, id bigint, revision bigint, expiration_time bigint, access_point_id bigint, ap_name text, owner_id bigint, item_id bigint, template_id text, stack_size bigint, item_price bigint, quality_level bigint, durability_cur real, durability_max real, dynamic_stats jsonb)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT sord.completion_type, ord.id, ord.revision, ord.expiration_time, ord.access_point_id, ap.name, ord.owner_id, ord.item_id, ord.template_id, sord.stack_size, ord.item_price, ord.quality_level, ord.durability_cur, ord.durability_max, item.stats
FROM dune_exchange_orders ord
JOIN dune_exchange_fulfilled_orders sord ON (ord.id = sord.order_id)
JOIN dune_exchange_accesspoints ap ON (ord.access_point_id = ap.id)
LEFT JOIN items item ON ord.item_id = item.id
WHERE ord.id = in_order_id;
END; $function$
-- dune_exchange_query_storage_items(in_exchange_id bigint, in_owner_id bigint) -> TABLE(completion_type integer, id bigint, revision bigint, expiration_time bigint, access_point_id bigint, ap_name text, owner_id bigint, item_id bigint, template_id text, stack_size bigint, item_price bigint, quality_level bigint, durability_cur real, durability_max real, dynamic_stats jsonb)
-- oid: 58251 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.dune_exchange_query_storage_items(in_exchange_id bigint, in_owner_id bigint)
RETURNS TABLE(completion_type integer, id bigint, revision bigint, expiration_time bigint, access_point_id bigint, ap_name text, owner_id bigint, item_id bigint, template_id text, stack_size bigint, item_price bigint, quality_level bigint, durability_cur real, durability_max real, dynamic_stats jsonb)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT sord.completion_type, ord.id, ord.revision, ord.expiration_time, ord.access_point_id, ap.name, ord.owner_id, ord.item_id, ord.template_id, sord.stack_size, ord.item_price, ord.quality_level, ord.durability_cur, ord.durability_max, item.stats
FROM dune_exchange_orders ord
JOIN dune_exchange_fulfilled_orders sord ON (ord.id = sord.order_id)
JOIN dune_exchange_accesspoints ap ON (ord.access_point_id = ap.id)
LEFT JOIN items item ON ord.item_id = item.id
WHERE ord.exchange_id = in_exchange_id AND ord.owner_id = in_owner_id;
END; $function$
-- dune_exchange_relist_order(in_order_id bigint, in_expiration_time bigint, in_item_price bigint, in_wear_normalized_item_price bigint, in_solari_cost bigint) -> bigint
-- oid: 58252 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.dune_exchange_relist_order(in_order_id bigint, in_expiration_time bigint, in_item_price bigint, in_wear_normalized_item_price bigint, in_solari_cost bigint)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
user_id BIGINT;
initial_stack_size BIGINT;
BEGIN
WITH order_owner_id AS
(SELECT ord.owner_id
FROM dune_exchange_orders ord
JOIN dune_exchange_fulfilled_orders ford ON (ord.id = ford.order_id)
WHERE id = in_order_id AND item_id IS NOT NULL)
SELECT INTO user_id dune_exchange_get_user_id((SELECT * FROM order_owner_id));
UPDATE dune_exchange_users SET solari_balance = solari_balance - in_solari_cost WHERE id = user_id AND solari_balance >= in_solari_cost;
IF NOT FOUND THEN
RETURN 0;
END IF;
DELETE FROM dune_exchange_fulfilled_orders WHERE order_id = in_order_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Order % is not a fulfilled order', in_order_id;
END IF;
WITH item_stack_size AS
(SELECT item.stack_size
FROM dune_exchange_orders ord
JOIN items item ON ord.item_id = item.id
WHERE ord.id = in_order_id)
INSERT INTO dune_exchange_sell_orders(order_id, initial_stack_size, wear_normalized_price) VALUES(in_order_id, (SELECT * FROM item_stack_size), in_wear_normalized_item_price) RETURNING dune_exchange_sell_orders.initial_stack_size INTO initial_stack_size;
UPDATE dune_exchange_orders SET item_price = in_item_price, expiration_time = in_expiration_time WHERE id = in_order_id;
RETURN initial_stack_size;
END $function$
-- dune_exchange_retrieve_solari_balance(in_owner_id bigint) -> bigint
-- oid: 58253 kind: FUNCTION category: currency
CREATE OR REPLACE FUNCTION dune.dune_exchange_retrieve_solari_balance(in_owner_id bigint)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
current_balance BIGINT;
fls_id TEXT;
BEGIN
SELECT INTO current_balance solari_balance from dune_exchange_users WHERE owner_id = in_owner_id LIMIT 1;
IF current_balance < 0 THEN
SELECT acc."user"
INTO fls_id
FROM accounts acc
JOIN player_state ps on ps.account_id = acc.id
WHERE ps.player_controller_id = in_owner_id
LIMIT 1;
PERFORM log_cheating(COALESCE(fls_id, in_owner_id::text), 'exchange_negative_solaris');
UPDATE dune_exchange_users SET solari_balance = 0 WHERE owner_id = in_owner_id;
END IF;
RETURN (SELECT solari_balance FROM dune_exchange_users WHERE owner_id = in_owner_id LIMIT 1);
END; $function$
-- dune_exchange_retrieve_solaris_from_item(in_controller_id bigint, in_order_id bigint) -> dune.duneexchangeretrievesolarisfromitemresult
-- oid: 58254 kind: FUNCTION category: currency
CREATE OR REPLACE FUNCTION dune.dune_exchange_retrieve_solaris_from_item(in_controller_id bigint, in_order_id bigint)
RETURNS dune.duneexchangeretrievesolarisfromitemresult
LANGUAGE plpgsql
AS $function$
DECLARE
result DuneExchangeRetrieveSolarisFromItemResult;
new_balance BIGINT;
function_oid oid;
BEGIN
WITH
delete_orders_prices AS (
DELETE FROM dune_exchange_orders
USING dune_exchange_fulfilled_orders
WHERE (dune_exchange_orders.id = dune_exchange_fulfilled_orders.order_id)
AND id = in_order_id AND (item_id IS NULL OR item_id = 0)
RETURNING item_price * dune_exchange_fulfilled_orders.stack_size AS total_price
),
total_price AS (
SELECT SUM(total_price) AS delta FROM delete_orders_prices
)
UPDATE player_virtual_currency_balances
SET balance = balance + total_price.delta
FROM total_price
WHERE currency_id = get_solaris_id() AND player_controller_id = in_controller_id
RETURNING
player_virtual_currency_balances.balance,
total_price.delta,
(SELECT original_order_id FROM dune_exchange_fulfilled_orders WHERE order_id = in_order_id)
INTO
new_balance,
result.total_item_value,
result.original_order_id;
GET DIAGNOSTICS function_oid = PG_ROUTINE_OID;
PERFORM log_event_solaris(function_oid, 'update_solaris', in_controller_id, new_balance, result.total_item_value);
RETURN result;
END $function$
-- dune_exchange_retrieve_storage_item(in_exchange_id bigint, in_order_id bigint, in_dst_inventory_id bigint, in_dst_index bigint, in_count bigint) -> dune.duneexchangeretrievestorageorderresult
-- oid: 58255 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.dune_exchange_retrieve_storage_item(in_exchange_id bigint, in_order_id bigint, in_dst_inventory_id bigint, in_dst_index bigint, in_count bigint)
RETURNS dune.duneexchangeretrievestorageorderresult
LANGUAGE plpgsql
AS $function$
DECLARE
exchange_inventory_id BIGINT;
order_owner_id BIGINT;
order_item_id BIGINT;
result DuneExchangeRetrieveStorageOrderResult;
BEGIN
SELECT INTO exchange_inventory_id get_exchange_inventory_id(in_exchange_id);
IF exchange_inventory_id IS NULL THEN
RETURN NULL;
END IF;
SELECT INTO STRICT order_owner_id, order_item_id owner_id, item_id
FROM dune_exchange_orders
JOIN dune_exchange_fulfilled_orders ON (dune_exchange_orders.id = dune_exchange_fulfilled_orders.order_id)
WHERE id = in_order_id
FOR UPDATE;
UPDATE dune_exchange_orders SET item_id = NULL WHERE id = in_order_id;
SELECT INTO result.item_id move_inventory_item(order_item_id, in_dst_inventory_id, in_dst_index, in_count);
IF result.item_id IS NULL THEN
RAISE EXCEPTION 'Failed to move inventory item % on order %', in_item_id, in_order_id;
END IF;
SELECT INTO result.original_order_id original_order_id FROM dune_exchange_fulfilled_orders WHERE order_id = in_order_id;
-- If the new ID is equal to the old ID the item/stack was moved rather than split and the order is now empty
IF result.item_id = order_item_id THEN
DELETE FROM dune_exchange_orders WHERE id = in_order_id;
ELSE
UPDATE dune_exchange_orders SET item_id = order_item_id WHERE id = in_order_id; -- Item was split. Restore reference to remaining items.
UPDATE dune_exchange_fulfilled_orders SET stack_size = stack_size - in_count WHERE order_id = in_order_id;
END IF;
SELECT INTO result.order_slots_used get_dune_exchange_used_order_slots(order_owner_id);
RETURN result;
END $function$
-- dune_exchange_update_recurring_sell_order(in_exchange_id bigint, in_expiration_time bigint, in_access_point_id bigint, in_owner_id bigint, in_item_id bigint, in_increment bigint, in_max_count bigint, in_category_mask integer, in_category_depth smallint, in_durability_cur real, in_durability_max real, in_item_price bigint, in_wear_normalized_item_price bigint, in_quality_level bigint) -> bigint
-- oid: 58256 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.dune_exchange_update_recurring_sell_order(in_exchange_id bigint, in_expiration_time bigint, in_access_point_id bigint, in_owner_id bigint, in_item_id bigint, in_increment bigint, in_max_count bigint, in_category_mask integer, in_category_depth smallint, in_durability_cur real, in_durability_max real, in_item_price bigint, in_wear_normalized_item_price bigint, in_quality_level bigint)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
exchange_inventory_id BIGINT;
new_item_id BIGINT;
item_stack_size BIGINT;
new_order_id BIGINT;
item_template_id TEXT;
old_count BIGINT;
new_count BIGINT;
delta_count BIGINT;
BEGIN
LOCK TABLE dune_exchange_orders, items IN ROW EXCLUSIVE MODE;
SELECT INTO exchange_inventory_id get_exchange_inventory_id(in_exchange_id);
IF exchange_inventory_id IS NULL THEN
RETURN 0;
END IF;
SELECT INTO STRICT item_template_id template_id FROM items WHERE id = in_item_id FOR SHARE;
SELECT INTO new_order_id, new_item_id ord.id, ord.item_id
FROM dune_exchange_orders ord
JOIN dune_exchange_sell_orders sord ON (ord.id = sord.order_id)
WHERE ord.is_npc_order = TRUE AND ord.exchange_id = in_exchange_id AND ord.access_point_id = in_access_point_id AND ord.template_id = item_template_id AND ord.item_price = in_item_price AND ord.quality_level = in_quality_level
FOR SHARE;
IF new_order_id IS NULL THEN
INSERT INTO dune_exchange_orders(exchange_id, access_point_id, owner_id, is_npc_order, expiration_time, template_id, durability_cur, durability_max, category_mask, category_depth, item_price, quality_level)
VALUES(in_exchange_id, in_access_point_id, in_owner_id, TRUE, in_expiration_time, item_template_id, in_durability_cur, in_durability_max, in_category_mask, in_category_depth, in_item_price, in_quality_level)
RETURNING id INTO new_order_id;
INSERT INTO dune_exchange_sell_orders(order_id, initial_stack_size, wear_normalized_price) VALUES(new_order_id, new_count, in_wear_normalized_item_price);
SELECT INTO new_item_id move_inventory_item(in_item_id, exchange_inventory_id, new_order_id, in_increment);
IF new_item_id IS NULL THEN
DELETE FROM dune_exchange_orders WHERE id = new_order_id;
RETURN 0;
END IF;
UPDATE dune_exchange_orders SET item_id = new_item_id WHERE id = new_order_id;
RETURN in_increment;
ELSE
UPDATE dune_exchange_orders SET expiration_time = in_expiration_time WHERE id=new_order_id;
SELECT INTO STRICT old_count stack_size FROM items WHERE id = new_item_id FOR SHARE;
new_count = old_count + in_increment;
IF new_count > in_max_count THEN new_count = in_max_count; END IF;
IF new_count != old_count THEN
delta_count = new_count - old_count;
SELECT INTO new_item_id merge_or_move_inventory_item(in_item_id, exchange_inventory_id, new_order_id, delta_count);
RETURN delta_count;
END IF;
RETURN 0;
END IF;
END $function$
-- dune_get_account_id_by_user(in_user text) -> bigint
-- oid: 58257 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.dune_get_account_id_by_user(in_user text)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
account_id BIGINT;
BEGIN
SELECT INTO account_id id FROM accounts WHERE "user"=in_user;
RETURN account_id;
END $function$
-- edit_guild_description(in_guild_id bigint, in_guild_desc text) -> void
-- oid: 58258 kind: FUNCTION category: currency
CREATE OR REPLACE FUNCTION dune.edit_guild_description(in_guild_id bigint, in_guild_desc text)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
out_guild_description TEXT;
BEGIN
PERFORM guilds_get_exclusive_operation_lock();
-- check if guild exists
SELECT guild_description INTO out_guild_description FROM guilds WHERE guild_id = in_guild_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Trying to add invite to non existing guild %.', in_guild_id;
END IF;
UPDATE guilds SET guild_description = in_guild_desc WHERE guilds.guild_id = in_guild_id;
PERFORM pg_notify('guild_notify_channel', format('edit_guild_description#{"GuildId" : %s}', in_guild_id));
END
$function$
-- encrypt_user_data(in_data text) -> bytea
-- oid: 58259 kind: FUNCTION category: encryption
CREATE OR REPLACE FUNCTION dune.encrypt_user_data(in_data text)
RETURNS bytea
LANGUAGE sql
IMMUTABLE
AS $function$select convert_to(in_data, 'utf8')$function$
-- fetch_resourcefield_state(in_map text, in_dimension_index integer, in_field_kind_id smallint) -> TABLE(field_id bigint, spawn_time double precision, value_remaining bigint)
-- oid: 58260 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.fetch_resourcefield_state(in_map text, in_dimension_index integer, in_field_kind_id smallint)
RETURNS TABLE(field_id bigint, spawn_time double precision, value_remaining bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT resourcefield_state.field_id, resourcefield_state.spawn_time, resourcefield_state.value_remaining
FROM resourcefield_state
WHERE map = in_map AND dimension_index = in_dimension_index AND field_kind_id = in_field_kind_id;
END
$function$
-- fetch_server_spice_field_manifest(in_server_id text) -> TABLE(spicefield_type_id integer, inactive_fields_of_type integer, requested_spawned_of_type integer)
-- oid: 58261 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.fetch_server_spice_field_manifest(in_server_id text)
RETURNS TABLE(spicefield_type_id integer, inactive_fields_of_type integer, requested_spawned_of_type integer)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT t.spicefield_type_id, t.inactive_fields_of_type, t.requested_spawned_of_type
FROM spicefield_server_availability as t
WHERE t.server_id = in_server_id;
END; $function$
-- fetch_spicefie_id_types_with_global_info(in_map_name text, in_dimension_index integer) -> TABLE(spicefield_type_id integer, max_globally_active integer, max_globally_primed integer, current_globally_active integer, current_globally_primed integer, is_spawning_active boolean, field_type text)
-- oid: 58262 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.fetch_spicefie_id_types_with_global_info(in_map_name text, in_dimension_index integer)
RETURNS TABLE(spicefield_type_id integer, max_globally_active integer, max_globally_primed integer, current_globally_active integer, current_globally_primed integer, is_spawning_active boolean, field_type text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT t.spicefield_type_id, t.max_globally_active, t.max_globally_primed, t.current_globally_active, t.current_globally_primed, t.is_spawning_active, t.field_type
FROM spicefield_types as t
WHERE t.map_name = in_map_name AND t.dimension_index = in_dimension_index;
END; $function$
-- find_actor_by_id(in_id bigint) -> dune.actorspawninfo
-- oid: 58263 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.find_actor_by_id(in_id bigint)
RETURNS dune.actorspawninfo
LANGUAGE plpgsql
AS $function$
begin
return (select (id, class, transform, partition_id, dimension_index)::ActorSpawnInfo
FROM actors WHERE actors.id = in_id
LIMIT 1);
end
$function$
-- fix_broken_harkonnen_players_due_to_fooled_thufir() -> void
-- oid: 58264 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.fix_broken_harkonnen_players_due_to_fooled_thufir()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
-- Dropping tables due to them being created in temp_backup_tables.sql
drop table if exists da_6358_pre_broken_players;
drop table if exists da_6358_broken_players_12400;
drop table if exists da_6358_broken_players_1300;
---Fix players that are in a broken state but haven't been blocked yet
--Tag combo to check for:
--DialogueFlags.Factions.Hark_ThufirBetrayedComplete
--DialogueFlags.Faction.FooledThufir
--PlayerIsFactionTier Harkonnen 5
--TO FIX:
--Remove DialogueFlags.Faction.FooledThufir
create table if not exists da_6358_pre_broken_players as
select pt.account_id from player_tags pt
group by pt.account_id
HAVING array_agg(DISTINCT tag) @> ARRAY['DialogueFlags.Factions.Hark_ThufirBetrayedComplete', 'DialogueFlags.Faction.FooledThufir', 'Faction.Harkonnen.Tier5'];
delete from player_tags pt using da_6358_pre_broken_players where pt.account_id = da_6358_pre_broken_players.account_id and pt.tag = 'DialogueFlags.Faction.FooledThufir';
---Fix players that are in a broken state since 1.2.40
---Tag combo to check for:
---DialogueFlags.Factions.Hark_ThufirBetrayedComplete
---DialogueFlags.Faction.FooledThufir
---Contract.Tracking.FactionStory.R4C6Completed
---PlayerIsFactionTier Harkonnen 4
---TO FIX:
---Remove DialogueFlags.Faction.FooledThufir
---Promote player to Harkonnen 5
create table if not exists da_6358_broken_players_12400 as
with broken_accounts as (
select pt.account_id
from player_tags pt
group by pt.account_id
having array_agg(distinct pt.tag) @> array[
'DialogueFlags.Factions.Hark_ThufirBetrayedComplete',
'DialogueFlags.Faction.FooledThufir',
'Contract.Tracking.FactionStory.R4C6Completed',
'Faction.Harkonnen.Tier4'
]
and not array_agg(distinct pt.tag) && array['Faction.Harkonnen.Tier5']
),
one_state_row_per_account as (
-- If player_state can theoretically have duplicates per account_id,
-- pick one deterministically.
select distinct on (ps.account_id)
ps.account_id,
ps.player_controller_id
from player_state ps
join broken_accounts ba on ba.account_id = ps.account_id
order by ps.account_id, ps.player_controller_id desc
)
select
s.account_id,
s.player_controller_id,
a.properties as backup_properties
from one_state_row_per_account s
join actors a on a.id = s.player_controller_id;
INSERT INTO player_tags (account_id, tag)
SELECT account_id, 'Faction.Harkonnen.Tier5' as tag
FROM da_6358_broken_players_12400
ON CONFLICT (account_id, tag) DO NOTHING;
delete from player_tags pt using da_6358_broken_players_12400 where pt.account_id = da_6358_broken_players_12400.account_id and pt.tag = 'DialogueFlags.Faction.FooledThufir';
UPDATE actors
SET properties = jsonb_set(
properties,
'{FactionPlayerComponent,m_FactionDataArray}',
(
SELECT coalesce(
jsonb_agg(
CASE
WHEN elem->'Faction'->>'Name' = 'Harkonnen'
THEN jsonb_set(elem, '{ReputationAmount}', '2000'::jsonb)
ELSE elem
END
),
'[]'::jsonb
)
FROM jsonb_array_elements(properties->'FactionPlayerComponent'->'m_FactionDataArray') AS t(elem)
)
)
FROM da_6358_broken_players_12400 b12400
WHERE b12400.player_controller_id = actors.id;
---Fix players that are in a broken state in live aka 1.3.0
---Tag combo to check for:
---DialogueFlags.Factions.Hark_ThufirBetrayedComplete
---Contract.Tracking.FactionStory.R4C6Completed
---PlayerIsFactionTier Harkonnen 4
---NOT DialogueFlags.Faction.FooledThufir
create table if not exists da_6358_broken_players_1300 as
with broken_accounts as (
select pt.account_id
from player_tags pt
group by pt.account_id
HAVING array_agg(DISTINCT tag) @> ARRAY ['DialogueFlags.Factions.Hark_ThufirBetrayedComplete', 'Contract.Tracking.FactionStory.R4C6Completed', 'Faction.Harkonnen.Tier4']
and not array_agg(distinct tag) && array['DialogueFlags.Faction.FooledThufir', 'Faction.Harkonnen.Tier5'] -- Since Tier4 players can also have Tier5 we need to exclude that
),
one_state_row_per_account as (
-- If player_state can theoretically have duplicates per account_id,
-- pick one deterministically.
select distinct on (ps.account_id)
ps.account_id,
ps.player_controller_id
from player_state ps
join broken_accounts ba on ba.account_id = ps.account_id
order by ps.account_id, ps.player_controller_id desc
)
select
s.account_id,
s.player_controller_id,
a.properties as backup_properties
from one_state_row_per_account s
join actors a on a.id = s.player_controller_id;
INSERT INTO player_tags (account_id, tag)
SELECT account_id, 'Faction.Harkonnen.Tier5' as tag
FROM da_6358_broken_players_1300
ON CONFLICT (account_id, tag) DO NOTHING;
UPDATE actors
SET properties = jsonb_set(
properties,
'{FactionPlayerComponent,m_FactionDataArray}',
(
SELECT coalesce(
jsonb_agg(
CASE
WHEN elem->'Faction'->>'Name' = 'Harkonnen'
THEN jsonb_set(elem, '{ReputationAmount}', '2000'::jsonb)
ELSE elem
END
),
'[]'::jsonb
)
FROM jsonb_array_elements(properties->'FactionPlayerComponent'->'m_FactionDataArray') AS t(elem)
)
)
FROM da_6358_broken_players_1300 b1300
WHERE b1300.player_controller_id = actors.id;
END;
$function$
-- flag_player_as_cheater(in_account_id bigint, in_cheat_type dune.cheat_type_enum) -> void
-- oid: 58265 kind: FUNCTION category: anticheat
CREATE OR REPLACE FUNCTION dune.flag_player_as_cheater(in_account_id bigint, in_cheat_type dune.cheat_type_enum)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
v_FLS_id TEXT;
BEGIN
SELECT acc."user"
INTO v_FLS_id
FROM accounts acc
WHERE acc.id = in_account_id
LIMIT 1;
PERFORM log_cheating(v_FLS_id, in_cheat_type);
END;
$function$
-- gather_ownerless_actors_on_server(in_server_info dune.serverinfo) -> SETOF dune.actorspawninfo
-- oid: 58266 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.gather_ownerless_actors_on_server(in_server_info dune.serverinfo)
RETURNS SETOF dune.actorspawninfo
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT a.id, a.class as class_name, a.transform, a.partition_id, a.dimension_index FROM actors as a
WHERE a.owner_account_id is null AND server_info_match(a, in_server_info);
END;
$function$
-- gather_player_linked_actors(in_player_pawn_id bigint) -> SETOF dune.actorspawninfo
-- oid: 58267 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.gather_player_linked_actors(in_player_pawn_id bigint)
RETURNS SETOF dune.actorspawninfo
LANGUAGE plpgsql
AS $function$
begin
return query
select actors.id, actors.class as class_name, actors.transform, actors.partition_id, actors.dimension_index
from actors
left join actor_state on actor_state.actor_id = actors.id
where actors.id in (select id from get_traveling_non_player_actor_ids(in_player_pawn_id)) and actor_state.state = 'Travel' and actors.owner_account_id is null
order by actors.id;
end
$function$
-- gather_removed_accounts_that_left_orphaned_actors_on_server(in_server_info dune.serverinfo) -> TABLE(account_id bigint, removal_reason text, actors_left dune.orphanedplayeractorinfo[])
-- oid: 58268 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.gather_removed_accounts_that_left_orphaned_actors_on_server(in_server_info dune.serverinfo)
RETURNS TABLE(account_id bigint, removal_reason text, actors_left dune.orphanedplayeractorinfo[])
LANGUAGE plpgsql
AS $function$
BEGIN
return query with
orphaned_actors_per_account as (
SELECT
a.owner_account_id as account_id,
array_agg((a.id, a.class)::OrphanedPlayerActorInfo) as actors_left
FROM actors as a
WHERE
-- not is null instead of is not null to match the index expression
not a.owner_account_id is null
AND NOT EXISTS(select 1 from accounts where id=owner_account_id)
AND server_info_match(a, in_server_info)
GROUP BY a.owner_account_id
)
select orphans.account_id, log.reason, orphans.actors_left
from orphaned_actors_per_account as orphans left join account_removal_log as log using (account_id);
END
$function$
-- get_account_actor_ids(in_account_id bigint) -> dune.playeractorids
-- oid: 58269 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.get_account_actor_ids(in_account_id bigint)
RETURNS dune.playeractorids
LANGUAGE plpgsql
AS $function$
BEGIN
return (
select (player_controller_id, player_state_id, player_pawn_id)::PlayerActorIds from player_state
where account_id = in_account_id
limit 1
);
END
$function$
-- get_active_servers_for_gateway() -> TABLE(server_id text, map text, partition_id bigint, dimension_index integer, game_addr inet, game_port integer, revision integer)
-- oid: 58270 kind: FUNCTION category: server
-- comment: Used by the gateway service to monitor for active servers.
CREATE OR REPLACE FUNCTION dune.get_active_servers_for_gateway()
RETURNS TABLE(server_id text, map text, partition_id bigint, dimension_index integer, game_addr inet, game_port integer, revision integer)
LANGUAGE plpgsql
AS $function$
DECLARE
BEGIN
-- If we have no partitions, assume dimension 0
return query select fs.server_id, fs.map, wp.partition_id, coalesce(wp.dimension_index, 0), fs.game_addr, fs.game_port, fs.revision from active_server_ids as asi left join world_partition as wp on asi.server_id = wp.server_id join farm_state as fs on fs.server_id = asi.server_id;
END
$function$
-- get_actor_server_info(in_id bigint) -> dune.serverinfo
-- oid: 58271 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.get_actor_server_info(in_id bigint)
RETURNS dune.serverinfo
LANGUAGE plpgsql
AS $function$
begin
return (select (map, partition_id, dimension_index)::ServerInfo from actors where id=in_id limit 1);
end
$function$
-- get_actors_location_data_with_permission(in_actor_ids bigint[]) -> SETOF dune.actorpermissionlocationdata
-- oid: 58272 kind: FUNCTION category: permission
CREATE OR REPLACE FUNCTION dune.get_actors_location_data_with_permission(in_actor_ids bigint[])
RETURNS SETOF dune.actorpermissionlocationdata
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT actors.id, actors.partition_id, actors.map, actors.dimension_index, actors.transform
FROM actors
WHERE actors.id = ANY(in_actor_ids);
END
$function$
-- get_all_demo_players() -> TABLE(fls_ids text)
-- oid: 58273 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_all_demo_players()
RETURNS TABLE(fls_ids text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT fls_id
FROM demo_users
WHERE demo_playtime_seconds IS NOT NULL;
END
$function$
-- get_all_faction_members() -> TABLE(player_id bigint, fls_id text, faction_id smallint)
-- oid: 58274 kind: FUNCTION category: faction
CREATE OR REPLACE FUNCTION dune.get_all_faction_members()
RETURNS TABLE(player_id bigint, fls_id text, faction_id smallint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT ps.player_controller_id as player_id, acc.user as fls_id, f.faction_id
FROM accounts acc
LEFT JOIN player_state ps ON acc.id = ps.account_id
RIGHT JOIN player_faction f ON ps.player_controller_id = f.actor_id;
END
$function$
-- get_all_guild_members() -> TABLE(player_id bigint, fls_id text, guild_id bigint)
-- oid: 58275 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_all_guild_members()
RETURNS TABLE(player_id bigint, fls_id text, guild_id bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT gm.player_id, acc.user, gm.guild_id
FROM accounts acc
LEFT JOIN player_state ps ON acc.id = ps.account_id
RIGHT JOIN guild_members gm ON ps.player_controller_id = gm.player_id;
END
$function$
-- get_all_online_or_recently_disconnected_player_online_state() -> SETOF dune.playeronlinestateentry
-- oid: 58276 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_all_online_or_recently_disconnected_player_online_state()
RETURNS SETOF dune.playeronlinestateentry
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT actors.id, player_state.character_name, (actors.map, actors.partition_id, actors.dimension_index)::ServerInfo, (player_state.last_avatar_activity AT TIME ZONE 'UTC')::TIMESTAMP, player_state.online_status
FROM actors
JOIN player_state ON player_state.player_controller_id = actors.id
WHERE (player_state.online_status = 'Online'
or player_state.last_avatar_activity > NOW() - INTERVAL '1 minutes' );
END
$function$
-- get_all_parties() -> TABLE(party_id bigint, player_id bigint, player_name text, party_leader_id bigint, platform_session_id text, platform_name text, platform_players_count integer)
-- oid: 58277 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_all_parties()
RETURNS TABLE(party_id bigint, player_id bigint, player_name text, party_leader_id bigint, platform_session_id text, platform_name text, platform_players_count integer)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
parties.party_id,
party_members.player_id,
player_state.character_name,
parties.party_leader_id,
platform_parties_mapping.platform_session_id,
platform_parties_mapping.platform_name,
platform_parties_mapping.num_of_players
FROM party_members
JOIN parties ON party_members.party_id = parties.party_id
JOIN player_state ON player_state.player_controller_id = party_members.player_id
LEFT JOIN platform_parties_mapping ON platform_parties_mapping.dune_party_id = parties.party_id;
END
$function$
-- get_all_party_invites() -> TABLE(invite_id bigint, party_id bigint, sender_player_id bigint, sender_name text, player_id bigint, player_name text, invite_sent_timespan bigint)
-- oid: 58278 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_all_party_invites()
RETURNS TABLE(invite_id bigint, party_id bigint, sender_player_id bigint, sender_name text, player_id bigint, player_name text, invite_sent_timespan bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT party_invites.invite_id, party_invites.party_id, party_invites.sender_player_id, sender_player_state.character_name, party_invites.player_id, player_state.character_name, party_invites.invite_sent_timespan
FROM party_invites
JOIN player_state ON player_state.player_controller_id = party_invites.player_id
JOIN player_state AS sender_player_state ON sender_player_state.player_controller_id = party_invites.sender_player_id;
END
$function$
-- get_all_party_members() -> TABLE(player_id bigint, fls_id text, party_id bigint)
-- oid: 58279 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_all_party_members()
RETURNS TABLE(player_id bigint, fls_id text, party_id bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT pm.player_id, acc.user, pm.party_id
FROM party_members pm
LEFT JOIN player_state ps ON ps.player_controller_id = pm.player_id
LEFT JOIN accounts acc ON acc.id = ps.account_id;
END
$function$
-- get_all_player_character_home_dimensions() -> TABLE(fls_id text, home_dimension integer)
-- oid: 58280 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_all_player_character_home_dimensions()
RETURNS TABLE(fls_id text, home_dimension integer)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT accounts.user as fls_id, player_state.home_dimension_index as home_dimension
FROM accounts
LEFT JOIN player_state on player_state.account_id = accounts.id;
END
$function$
-- get_all_player_in_guild_online_state(in_guild_id bigint) -> SETOF dune.playeronlinestateentry
-- oid: 58281 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_all_player_in_guild_online_state(in_guild_id bigint)
RETURNS SETOF dune.playeronlinestateentry
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT actors.id, player_state.character_name, (actors.map, actors.partition_id, actors.dimension_index)::ServerInfo, (player_state.last_avatar_activity AT TIME ZONE 'UTC')::TIMESTAMP, player_state.online_status
FROM actors
JOIN player_state ON player_state.player_controller_id = actors.id
JOIN guild_members ON guild_members.player_id = actors.id
WHERE guild_members.guild_id = in_guild_id;
END
$function$
-- get_all_player_travel_states() -> TABLE(fls_id text, login_target_dimension_index integer)
-- oid: 58282 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_all_player_travel_states()
RETURNS TABLE(fls_id text, login_target_dimension_index integer)
LANGUAGE plpgsql
AS $function$
DECLARE
BEGIN
RETURN query SELECT pts.fls_id, pts.login_target_dimension_index FROM player_travel_state AS pts;
END;
$function$
-- get_all_tutorial_entries(in_player_id bigint) -> TABLE(tutorial_id smallint, tutorial_state smallint)
-- oid: 58283 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.get_all_tutorial_entries(in_player_id bigint)
RETURNS TABLE(tutorial_id smallint, tutorial_state smallint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT tutorial_per_player.tutorial_id, tutorial_per_player.tutorial_state FROM tutorial_per_player WHERE tutorial_per_player.player_id = in_player_id;
END
$function$
-- get_all_unresolved_character_imports() -> TABLE(flsid text, importstate dune.transferimportstate, lastupdatetime timestamp with time zone)
-- oid: 58284 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_all_unresolved_character_imports()
RETURNS TABLE(flsid text, importstate dune.transferimportstate, lastupdatetime timestamp with time zone)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT ti.fls_id, ti.transfer_state, ti.last_update
FROM character_transfer_imports ti;
END;
$function$
-- get_applied_patches() -> SETOF text
-- oid: 58285 kind: FUNCTION category: schema_meta
CREATE OR REPLACE FUNCTION dune.get_applied_patches()
RETURNS SETOF text
LANGUAGE sql
AS $function$
select "name" from applied_patches order by "date";
$function$
-- get_battlegroup_close_date() -> timestamp without time zone
-- oid: 58286 kind: FUNCTION category: battlegroup
CREATE OR REPLACE FUNCTION dune.get_battlegroup_close_date()
RETURNS timestamp without time zone
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN (SELECT farm_variables.battlegroup_close_date from farm_variables);
END
$function$
-- get_best_dungeon_completion(in_dungeon_id text) -> TABLE(out_difficulty integer, out_duration_ms integer, out_players_names text[])
-- oid: 58287 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_best_dungeon_completion(in_dungeon_id text)
RETURNS TABLE(out_difficulty integer, out_duration_ms integer, out_players_names text[])
LANGUAGE plpgsql
AS $function$
begin
RETURN QUERY SELECT
d.difficulty,
d.duration_ms,
COALESCE((SELECT array_agg(COALESCE(ps.character_name, ''))
FROM player_state as ps
FULL JOIN dungeon_completion_players as dcp ON dcp.player_id = ps.player_controller_id
WHERE dcp.completion_id = d.completion_id), ARRAY[]::TEXT[])
FROM dungeon_completion as d
WHERE d.dungeon_id = in_dungeon_id
ORDER BY d.difficulty DESC, d.players_num ASC, d.duration_ms ASC, d.completion_id ASC
LIMIT 1;
end
$function$
-- get_best_dungeons_completions_for_player(in_player_id bigint) -> TABLE(out_dungeon_id text, out_difficulty integer, out_duration_ms integer, out_players_num smallint)
-- oid: 58288 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_best_dungeons_completions_for_player(in_player_id bigint)
RETURNS TABLE(out_dungeon_id text, out_difficulty integer, out_duration_ms integer, out_players_num smallint)
LANGUAGE plpgsql
AS $function$
begin
RETURN QUERY SELECT DISTINCT ON (d.dungeon_id) d.dungeon_id, d.difficulty, d.duration_ms, d.players_num
FROM dungeon_completion as d
INNER JOIN dungeon_completion_players as p ON p.completion_id = d.completion_id
WHERE p.player_id = in_player_id
ORDER BY d.dungeon_id, d.difficulty DESC, d.players_num ASC, d.duration_ms ASC, d.completion_id ASC;
end
$function$
-- get_building_blueprint_copy_data(in_building_blueprint_id bigint) -> dune.buildingblueprintgetcopydata
-- oid: 58289 kind: FUNCTION category: building_blueprint
CREATE OR REPLACE FUNCTION dune.get_building_blueprint_copy_data(in_building_blueprint_id bigint)
RETURNS dune.buildingblueprintgetcopydata
LANGUAGE plpgsql
AS $function$
DECLARE
buildings_array BuildingBlueprintItem[];
placeables_array BuildingBlueprintPlaceableItem[];
pentashields_array BuildingBlueprintPentashieldItem[];
BEGIN
-- All Building Pieces
SELECT array_agg((instance_id, building_type, transform, provides_stability, health, hologram)::BuildingBlueprintItem)
into buildings_array
FROM building_blueprint_instances
WHERE building_blueprint_id = in_building_blueprint_id;
-- All Placeables
SELECT array_agg((placeable_id, building_type, transform, hologram)::BuildingBlueprintPlaceableItem)
into placeables_array
FROM building_blueprint_placeables
WHERE building_blueprint_id = in_building_blueprint_id;
-- Pentashields
SELECT array_agg((placeable_id, scale)::BuildingBlueprintPentashieldItem)
into pentashields_array
FROM building_blueprint_pentashields
WHERE building_blueprint_id = in_building_blueprint_id;
return ROW(buildings_array, placeables_array, pentashields_array)::BuildingBlueprintGetCopyData;
END
$function$
-- get_building_favorites(in_account_id bigint) -> TABLE(building_types text[])
-- oid: 58290 kind: FUNCTION category: building_blueprint
CREATE OR REPLACE FUNCTION dune.get_building_favorites(in_account_id bigint)
RETURNS TABLE(building_types text[])
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT building_favorites.building_types FROM building_favorites WHERE account_id = in_account_id;
END; $function$
-- get_building_id(in_actor_id bigint, in_class text) -> dune.buildinggetidcomposite
-- oid: 58291 kind: FUNCTION category: building_blueprint
CREATE OR REPLACE FUNCTION dune.get_building_id(in_actor_id bigint, in_class text)
RETURNS dune.buildinggetidcomposite
LANGUAGE plpgsql
AS $function$
BEGIN
IF not exists(select 1 from buildings where "id" = in_actor_id) THEN
if in_actor_id is null or in_actor_id = 0 then
in_actor_id := (SELECT assign_actor_id(in_class));
end if;
INSERT INTO buildings("id") VALUES(in_actor_id);
END IF;
return ROW(in_actor_id)::BuildingGetIdComposite;
END
$function$
-- get_character_import_state(in_fls_id text) -> dune.transferimportstate
-- oid: 58292 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_character_import_state(in_fls_id text)
RETURNS dune.transferimportstate
LANGUAGE plpgsql
AS $function$
DECLARE
v_state TransferImportState;
BEGIN
SELECT transfer_state INTO v_state FROM character_transfer_imports WHERE fls_id = in_fls_id;
RETURN v_state;
END;
$function$
-- get_character_transfer_related_items(in_fls_id text) -> jsonb
-- oid: 58293 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune.get_character_transfer_related_items(in_fls_id text)
RETURNS jsonb
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN (
WITH acct AS (
SELECT id
FROM accounts
WHERE "user" = in_FLS_ID
LIMIT 1
)
SELECT jsonb_build_object(
'items', COALESCE((
SELECT jsonb_agg(jsonb_build_object('name', sub.template_id, 'amount', sub.amount))
FROM (
SELECT i.template_id, SUM(i.stack_size) AS amount
FROM inventories inv
JOIN items i ON i.inventory_id = inv.id
JOIN player_state ps ON inv.actor_id = ps.player_pawn_id
WHERE ps.account_id = acct.id AND (
(inv.inventory_type = 0 AND i.template_id = 'SolarisCoin') OR
(i.template_id IN ('BaseBackupTool', 'VehicleBackupTool'))
)
GROUP BY i.template_id
) sub
), '[]'::jsonb),
'coin_balance', COALESCE((
SELECT vc.balance from player_virtual_currency_balances vc
JOIN player_state ps ON ps.account_id = acct.id
WHERE vc.currency_id = get_solaris_id()
AND vc.player_controller_id = ps.player_controller_id
LIMIT 1
), 0)
)
FROM acct
);
END
$function$
-- get_consumed_lore_pickups(in_actor_id bigint, in_use_temporary boolean) -> SETOF bit
-- oid: 58294 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.get_consumed_lore_pickups(in_actor_id bigint, in_use_temporary boolean)
RETURNS SETOF bit
LANGUAGE plpgsql
AS $function$
BEGIN
IF in_use_temporary THEN
RETURN query
SELECT consumed_bit_array
FROM consumed_temporary_per_player_lore
WHERE actor_id = in_actor_id;
ELSE
RETURN query
SELECT consumed_bit_array
FROM consumed_per_player_lore
WHERE actor_id = in_actor_id;
END IF;
END
$function$
-- get_controller_id_from_platform_id(in_platform_id text) -> bigint
-- oid: 58295 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.get_controller_id_from_platform_id(in_platform_id text)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
out_controller_id BIGINT;
BEGIN
SELECT ps.player_controller_id
INTO out_controller_id
FROM accounts acc LEFT JOIN player_state ps ON acc.id=ps.account_id
WHERE acc.platform_id = in_platform_id
LIMIT 1;
RETURN out_controller_id;
END
$function$
-- get_dune_exchange_accesspoint_id(in_exchange_id bigint, in_name text) -> bigint
-- oid: 58296 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.get_dune_exchange_accesspoint_id(in_exchange_id bigint, in_name text)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
new_ap_id BIGINT;
ap_id BIGINT;
BEGIN
INSERT INTO dune_exchange_accesspoints(exchange_id, name) VALUES(in_exchange_id, in_name) ON CONFLICT DO NOTHING RETURNING id INTO new_ap_id;
SELECT INTO ap_id COALESCE(new_ap_id, id) FROM dune_exchange_accesspoints WHERE exchange_id = in_exchange_id AND name = in_name;
return ap_id;
END $function$
-- get_dune_exchange_data(in_exchange_id bigint, in_controller_id bigint) -> dune.loadexchangedataresult
-- oid: 58297 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.get_dune_exchange_data(in_exchange_id bigint, in_controller_id bigint)
RETURNS dune.loadexchangedataresult
LANGUAGE plpgsql
AS $function$
DECLARE
result LoadExchangeDataResult;
BEGIN
SELECT INTO result.exchange_name exchange_name FROM dune_exchanges WHERE id=in_exchange_id;
SELECT INTO result.used_order_slots get_dune_exchange_used_order_slots(in_controller_id);
RETURN result;
END $function$
-- get_dune_exchange_id(in_name text) -> bigint
-- oid: 58298 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.get_dune_exchange_id(in_name text)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
new_exchange_id BIGINT;
exchange_id BIGINT;
BEGIN
INSERT INTO dune_exchanges(exchange_name, inventory_id) VALUES(in_name, NULL) ON CONFLICT DO NOTHING RETURNING id INTO new_exchange_id;
SELECT INTO exchange_id COALESCE(new_exchange_id, id) FROM dune_exchanges WHERE exchange_name = in_name;
RETURN exchange_id;
END $function$
-- get_dune_exchange_used_order_slots(in_controller_id bigint) -> integer
-- oid: 58299 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.get_dune_exchange_used_order_slots(in_controller_id bigint)
RETURNS integer
LANGUAGE plpgsql
AS $function$
DECLARE
result INT;
BEGIN
SELECT INTO result COUNT(*) FROM dune_exchange_orders where owner_id=in_controller_id AND item_id IS NOT NULL;
RETURN result;
END $function$
-- get_exchange_inventory_id(in_exchange_id bigint) -> bigint
-- oid: 58300 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.get_exchange_inventory_id(in_exchange_id bigint)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
inv_id BIGINT;
BEGIN
SELECT INTO inv_id id FROM inventories WHERE "exchange_id" = in_exchange_id;
IF inv_id IS NULL THEN
INSERT INTO inventories("id", exchange_id) VALUES(DEFAULT, in_exchange_id) RETURNING id INTO inv_id;
END IF;
RETURN inv_id;
END $function$
-- get_exchange_orders_by_mask(in_mask integer, in_depth smallint) -> SETOF bigint
-- oid: 58301 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.get_exchange_orders_by_mask(in_mask integer, in_depth smallint)
RETURNS SETOF bigint
LANGUAGE plpgsql
AS $function$
DECLARE
check_mask INT;
check_shift INT;
BEGIN
check_shift := (4 - in_depth) * 8;
check_mask := (in_mask >> check_shift);
RETURN query SELECT id FROM dune_exchange_orders WHERE category_depth >= in_depth AND (category_mask >> check_shift) = check_mask FOR SHARE;
END
$function$
-- get_exchange_sell_orders(in_id bigint, in_exchange_id bigint, in_min_item_price bigint, in_max_item_price bigint, in_template_id text, in_mask integer, in_depth smallint) -> TABLE(id bigint, revision bigint, expiration_time bigint, access_point_id bigint, ap_name text, owner_id bigint, template_id text, stack_size bigint, initial_stack_size bigint, item_price bigint, quality_level bigint, durability_cur real, durability_max real, dynamic_stats jsonb)
-- oid: 58302 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.get_exchange_sell_orders(in_id bigint, in_exchange_id bigint, in_min_item_price bigint, in_max_item_price bigint, in_template_id text, in_mask integer, in_depth smallint)
RETURNS TABLE(id bigint, revision bigint, expiration_time bigint, access_point_id bigint, ap_name text, owner_id bigint, template_id text, stack_size bigint, initial_stack_size bigint, item_price bigint, quality_level bigint, durability_cur real, durability_max real, dynamic_stats jsonb)
LANGUAGE plpgsql
AS $function$
DECLARE
BEGIN
RETURN query
SELECT ord.id, ord.revision, ord.expiration_time, ord.access_point_id, ap.name, ord.owner_id, item.template_id, item.stack_size, sord.initial_stack_size, ord.item_price, ord.quality_level, ord.durability_cur, ord.durability_max, item.stats
FROM dune_exchange_orders ord
JOIN dune_exchange_sell_orders sord ON (ord.id = sord.order_id)
JOIN items item ON (ord.item_id = item.id)
JOIN dune_exchange_accesspoints ap ON (ord.access_point_id = ap.id)
WHERE (in_id IS NOT NULL AND ord.id = in_id) OR
(in_id IS NULL AND ord.exchange_id = in_exchange_id AND ord.item_price >= in_min_item_price AND (in_max_item_price <= 0 OR ord.item_price <= in_max_item_price) AND
((in_template_id IS NOT NULL AND ord.template_id = in_template_id) OR
(in_template_id IS NULL AND ord.id IN (SELECT * FROM get_exchange_orders_by_mask(in_mask, in_depth))))
)
FOR SHARE;
END
$function$
-- get_exchange_sell_orders_by_item_type(in_exchange_id bigint, in_template_ids text[]) -> TABLE(id bigint, revision bigint, expiration_time bigint, access_point_id bigint, ap_name text, owner_id bigint, template_id text, stack_size bigint, initial_stack_size bigint, item_price bigint, quality_level bigint, durability_cur real, durability_max real, dynamic_stats jsonb)
-- oid: 58303 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.get_exchange_sell_orders_by_item_type(in_exchange_id bigint, in_template_ids text[])
RETURNS TABLE(id bigint, revision bigint, expiration_time bigint, access_point_id bigint, ap_name text, owner_id bigint, template_id text, stack_size bigint, initial_stack_size bigint, item_price bigint, quality_level bigint, durability_cur real, durability_max real, dynamic_stats jsonb)
LANGUAGE plpgsql
AS $function$
DECLARE
BEGIN
RETURN query
SELECT ord.id, ord.revision, ord.expiration_time, ord.access_point_id, ap.name, ord.owner_id, item.template_id, item.stack_size, sord.initial_stack_size, ord.item_price, ord.quality_level, ord.durability_cur, ord.durability_max, item.stats
FROM dune_exchange_orders ord
JOIN dune_exchange_sell_orders sord ON (ord.id = sord.order_id)
JOIN items item ON (ord.item_id = item.id)
JOIN dune_exchange_accesspoints ap ON (ord.access_point_id = ap.id)
WHERE ord.exchange_id = in_exchange_id AND ord.template_id = ANY(in_template_ids)
FOR SHARE;
END
$function$
-- get_exchange_sell_orders_by_owner(in_exchange_id bigint, in_owner_id bigint) -> TABLE(id bigint, revision bigint, expiration_time bigint, access_point_id bigint, ap_name text, owner_id bigint, template_id text, stack_size bigint, initial_stack_size bigint, item_price bigint, quality_level bigint, durability_cur real, durability_max real, dynamic_stats jsonb)
-- oid: 58304 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.get_exchange_sell_orders_by_owner(in_exchange_id bigint, in_owner_id bigint)
RETURNS TABLE(id bigint, revision bigint, expiration_time bigint, access_point_id bigint, ap_name text, owner_id bigint, template_id text, stack_size bigint, initial_stack_size bigint, item_price bigint, quality_level bigint, durability_cur real, durability_max real, dynamic_stats jsonb)
LANGUAGE plpgsql
AS $function$
DECLARE
BEGIN
RETURN query
SELECT ord.id, ord.revision, ord.expiration_time, ord.access_point_id, ap.name, ord.owner_id, item.template_id, item.stack_size, sord.initial_stack_size, ord.item_price, ord.quality_level, ord.durability_cur, ord.durability_max, item.stats
FROM dune_exchange_orders ord
JOIN dune_exchange_sell_orders sord ON (ord.id = sord.order_id)
JOIN items item ON (ord.item_id = item.id)
JOIN dune_exchange_accesspoints ap ON (ord.access_point_id = ap.id)
WHERE ord.owner_id = in_owner_id
FOR SHARE;
END
$function$
-- get_farm_state() -> TABLE(server_id text, farm_id text, outgoing_s2s_connections integer, incoming_s2s_connections integer, connected_players integer, igw_addr inet, igw_port integer, game_addr inet, game_port integer, ready boolean, alive boolean, map text, revision integer)
-- oid: 58305 kind: FUNCTION category: farm
CREATE OR REPLACE FUNCTION dune.get_farm_state()
RETURNS TABLE(server_id text, farm_id text, outgoing_s2s_connections integer, incoming_s2s_connections integer, connected_players integer, igw_addr inet, igw_port integer, game_addr inet, game_port integer, ready boolean, alive boolean, map text, revision integer)
LANGUAGE plpgsql
AS $function$
DECLARE
BEGIN
RETURN QUERY SELECT fs.server_id, fs.farm_id, fs.outgoing_s2s_connections, fs.incoming_s2s_connections, fs.connected_players, fs.igw_addr, fs.igw_port, fs.game_addr, fs.game_port, fs.ready, fs.alive, fs.map, fs.revision FROM farm_state as fs
JOIN active_server_ids USING(server_id);
END
$function$
-- get_friends_search(in_player_name text, in_max_players_count integer) -> TABLE(player_id bigint, character_name text, funcom_id text, platform_id text, platform_name text)
-- oid: 58306 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.get_friends_search(in_player_name text, in_max_players_count integer)
RETURNS TABLE(player_id bigint, character_name text, funcom_id text, platform_id text, platform_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT player_state.player_controller_id, player_state.character_name, accounts.funcom_id, accounts.platform_id, accounts.platform_name
FROM player_state
JOIN accounts ON player_state.account_id = accounts.id
WHERE player_state.character_name ILIKE '%' || in_player_name || '%'
ORDER BY ext.SIMILARITY(player_state.character_name, in_player_name) DESC
LIMIT in_max_players_count;
END
$function$
-- get_guild_data(in_guild_id bigint) -> TABLE(guild_name text, guild_faction_id smallint, guild_description text)
-- oid: 58307 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.get_guild_data(in_guild_id bigint)
RETURNS TABLE(guild_name text, guild_faction_id smallint, guild_description text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT guilds.guild_name, guilds.guild_faction, guilds.guild_description
FROM guilds
WHERE guild_id = in_guild_id;
END
$function$
-- get_guild_data_for_player(in_player_id bigint) -> TABLE(guild_id bigint, guild_factions_id smallint, guild_name text, guild_description text, player_id bigint, role_id smallint, player_faction_id smallint)
-- oid: 58308 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.get_guild_data_for_player(in_player_id bigint)
RETURNS TABLE(guild_id bigint, guild_factions_id smallint, guild_name text, guild_description text, player_id bigint, role_id smallint, player_faction_id smallint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT guilds.guild_id, guilds.guild_faction, guilds.guild_name, guilds.guild_description, guild_members.player_id, guild_members.role_id, player_faction.faction_id
FROM guilds JOIN guild_members on (guilds.guild_id = guild_members.guild_id)
LEFT JOIN player_faction on player_faction.actor_id = guild_members.player_id
WHERE guild_members.player_id = in_player_id;
END
$function$
-- get_guild_for_player(in_player_id bigint) -> bigint
-- oid: 58309 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.get_guild_for_player(in_player_id bigint)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
found_guild_id integer;
BEGIN
SELECT guild_id FROM guild_members WHERE player_id = in_player_id INTO found_guild_id;
IF NOT FOUND THEN
RETURN 0;
END IF;
RETURN found_guild_id;
END
$function$
-- get_guild_invites(in_guild_id bigint) -> TABLE(invite_id bigint, player_id bigint, sender_player_id bigint, invite_sent_timespan bigint, character_name text, sender_character_name text)
-- oid: 58310 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.get_guild_invites(in_guild_id bigint)
RETURNS TABLE(invite_id bigint, player_id bigint, sender_player_id bigint, invite_sent_timespan bigint, character_name text, sender_character_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT guild_invites.invite_id, guild_invites.player_id, guild_invites.sender_player_id, guild_invites.invite_sent_timespan, player_state.character_name, sender_player_state.character_name AS sender_character_name
FROM guild_invites
JOIN player_state ON player_state.player_controller_id = guild_invites.player_id
JOIN player_state AS sender_player_state ON sender_player_state.player_controller_id = guild_invites.sender_player_id
WHERE guild_id = in_guild_id;
END
$function$
-- get_guild_members(in_guild_id bigint) -> TABLE(player_id bigint, role_id smallint, player_faction_id smallint)
-- oid: 58311 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.get_guild_members(in_guild_id bigint)
RETURNS TABLE(player_id bigint, role_id smallint, player_faction_id smallint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT guild_members.player_id, guild_members.role_id, player_faction.faction_id
FROM guild_members
LEFT JOIN player_faction ON player_faction.actor_id = guild_members.player_id
WHERE guild_id = in_guild_id;
END
$function$
-- get_inventory_data(in_inventory_id bigint) -> dune.inventorydata
-- oid: 58312 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.get_inventory_data(in_inventory_id bigint)
RETURNS dune.inventorydata
LANGUAGE plpgsql
AS $function$
DECLARE
inventory_data InventoryData;
BEGIN
SELECT INTO
inventory_data.inventory_id, inventory_data.inventory_type, inventory_data.max_item_count, inventory_data.max_item_volume
id, inventory_type, max_item_count, max_item_volume
FROM inventories
WHERE id = in_inventory_id;
RETURN inventory_data;
END $function$
-- get_inventory_id(in_actor_id bigint, in_component_name_hash integer) -> bigint
-- oid: 58313 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.get_inventory_id(in_actor_id bigint, in_component_name_hash integer)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
inv_id BIGINT;
BEGIN
SELECT INTO inv_id inventory_id FROM actor_inventories ai JOIN inventories i ON (ai.inventory_id = i.id) WHERE i.actor_id = in_actor_id AND ai.component_name_hash = in_component_name_hash;
IF inv_id IS NULL THEN
INSERT INTO inventories("id", "actor_id") VALUES(DEFAULT, in_actor_id) RETURNING id INTO inv_id;
INSERT INTO actor_inventories("inventory_id", "component_name_hash") VALUES(inv_id, in_component_name_hash);
END IF;
RETURN inv_id;
END $function$
-- get_items_to_remove(items_to_remove text[]) -> text[]
-- oid: 58314 kind: FUNCTION category: items_purge
CREATE OR REPLACE FUNCTION dune.get_items_to_remove(items_to_remove text[])
RETURNS text[]
LANGUAGE plpgsql
AS $function$
declare
result text[];
begin
select array(
select unnest(items_to_remove)
except
select name
from removed_items
) into result;
return result;
end;
$function$
-- get_landclaim_segments(in_totem_id bigint) -> TABLE(grid_location_x bigint, grid_location_y bigint)
-- oid: 58315 kind: FUNCTION category: landclaim
CREATE OR REPLACE FUNCTION dune.get_landclaim_segments(in_totem_id bigint)
RETURNS TABLE(grid_location_x bigint, grid_location_y bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT t.grid_location_x, t.grid_location_y
FROM landclaim_segments AS t
WHERE t.totem_id = in_totem_id;
END; $function$
-- get_learned_building_sets(in_account_id bigint) -> SETOF text
-- oid: 58316 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_learned_building_sets(in_account_id bigint)
RETURNS SETOF text
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT UNNEST(learned_building_sets) FROM building_progression WHERE account_id = in_account_id;
END; $function$
-- get_learned_new_buildable_pieces(in_account_id bigint) -> SETOF text
-- oid: 58317 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_learned_new_buildable_pieces(in_account_id bigint)
RETURNS SETOF text
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT UNNEST(new_buildable_pieces) FROM building_progression WHERE account_id = in_account_id;
END; $function$
-- get_login_journey_nodes(in_account_id bigint) -> dune.journeynodeinfo[]
-- oid: 58318 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_login_journey_nodes(in_account_id bigint)
RETURNS dune.journeynodeinfo[]
LANGUAGE plpgsql
AS $function$
DECLARE
result JourneyNodeInfo[];
BEGIN
SELECT
array_agg(
(
res.story_node_id,
res.override_reward_block,
res.has_pending_reward,
res.complete_condition_state,
res.reveal_condition_state,
res.fail_condition_state,
res.metadata_state,
res.reset_group
)::JourneyNodeInfo
)
INTO result
FROM journey_story_node res
WHERE res.account_id = in_account_id;
RETURN result;
END
$function$
-- get_login_journey_nodes_cooldown(in_account_id bigint) -> dune.journeynodecooldowninfo[]
-- oid: 58319 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_login_journey_nodes_cooldown(in_account_id bigint)
RETURNS dune.journeynodecooldowninfo[]
LANGUAGE plpgsql
AS $function$
DECLARE
result JourneyNodeCooldownInfo[];
BEGIN
SELECT
array_agg(
(
res.story_node_id,
res.time_to_expire
)::JourneyNodeCooldownInfo
)
INTO result
FROM journey_story_node_cooldown res
WHERE res.account_id = in_account_id;
RETURN result;
END
$function$
-- get_mnemonic_recall_lessons(in_account_id bigint) -> TABLE(id bigint, lesson_id text, lession_state bigint, lesson_progress integer, is_new boolean)
-- oid: 58320 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.get_mnemonic_recall_lessons(in_account_id bigint)
RETURNS TABLE(id bigint, lesson_id text, lession_state bigint, lesson_progress integer, is_new boolean)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT t.id, t.lesson_id, t.lesson_state, t.lesson_progress, t.is_new
FROM mnemonic_recall as t
WHERE t.account_id = in_account_id
ORDER BY t.id;
END; $function$
-- get_online_player_controller_ids(in_map text) -> SETOF bigint
-- oid: 58321 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_online_player_controller_ids(in_map text)
RETURNS SETOF bigint
LANGUAGE plpgsql
AS $function$
BEGIN
return query SELECT DISTINCT ps.player_controller_id
FROM player_state ps
JOIN actors a ON (a.id = ps.player_controller_id)
WHERE ps.online_status = 'Online' AND a.map = in_map;
END;
$function$
-- get_online_player_controller_ids_on_farm() -> SETOF bigint
-- oid: 58322 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_online_player_controller_ids_on_farm()
RETURNS SETOF bigint
LANGUAGE plpgsql
AS $function$
BEGIN
return query SELECT DISTINCT ps.player_controller_id
FROM player_state ps
JOIN actors a ON (a.id = ps.player_controller_id)
WHERE ps.online_status = 'Online';
END;
$function$
-- get_partition_presets() -> SETOF text
-- oid: 58323 kind: FUNCTION category: partition
-- comment: Adds a partition only if its unique. Not using constraints, as this is only a helper function.
CREATE OR REPLACE FUNCTION dune.get_partition_presets()
RETURNS SETOF text
LANGUAGE plpgsql
AS $function$
BEGIN
return query SELECT routine_name::text as preset_function FROM information_schema.routines WHERE routine_type = 'FUNCTION' and routine_name ilike 'initialize_partitions_%';
END;
$function$
-- get_partitions(in_map text) -> SETOF bigint
-- oid: 58324 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.get_partitions(in_map text)
RETURNS SETOF bigint
LANGUAGE plpgsql
AS $function$
begin
SELECT partition_id FROM world_partition where map = in_map order by partition_id ASC;
end
$function$
-- get_party_members(in_party_id bigint) -> TABLE(player_id bigint, fls_id text, party_id bigint)
-- oid: 58325 kind: FUNCTION category: party
CREATE OR REPLACE FUNCTION dune.get_party_members(in_party_id bigint)
RETURNS TABLE(player_id bigint, fls_id text, party_id bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT pm.player_id, acc.user, pm.party_id
FROM party_members pm
LEFT JOIN player_state ps ON ps.player_controller_id = pm.player_id
LEFT JOIN accounts acc ON acc.id = ps.account_id
WHERE pm.party_id = in_party_id;
END
$function$
-- get_permission_actors_for_server(in_server_info dune.serverinfo) -> SETOF dune.actorpermissioncombineddata
-- oid: 58326 kind: FUNCTION category: permission
CREATE OR REPLACE FUNCTION dune.get_permission_actors_for_server(in_server_info dune.serverinfo)
RETURNS SETOF dune.actorpermissioncombineddata
LANGUAGE plpgsql
AS $function$
BEGIN
return query
with ids as (
select array_agg(actors.id) as ids
from permission_actor join actors on actors.id = permission_actor.actor_id
where server_info_match(actors, in_server_info) and actors.owner_account_id is null
)
select permissions.* from ids, get_permission_for_actors(ids.ids) as permissions;
END
$function$
-- get_permission_for_actor(in_actor_id bigint) -> dune.actorpermissioncombineddata
-- oid: 58327 kind: FUNCTION category: permission
CREATE OR REPLACE FUNCTION dune.get_permission_for_actor(in_actor_id bigint)
RETURNS dune.actorpermissioncombineddata
LANGUAGE plpgsql
AS $function$
BEGIN
return (select get_permission_for_actors(array[in_actor_id]) limit 1);
END
$function$
-- get_permission_for_actors(in_actor_id bigint[]) -> SETOF dune.actorpermissioncombineddata
-- oid: 58328 kind: FUNCTION category: permission
CREATE OR REPLACE FUNCTION dune.get_permission_for_actors(in_actor_id bigint[])
RETURNS SETOF dune.actorpermissioncombineddata
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
-- ActorPermissionData
ROW(
ROW(
permission_actor.actor_id,
permission_actor.actor_name,
actors.class,
permission_actor.actor_type,
permission_actor.access_level,
permission_actor.is_child
)::ActorPermissionEntry,
array_agg(
ROW(
permission_actor_rank.rank,
permission_actor_rank.player_id
)::ActorPermissionRankData
) FILTER (WHERE permission_actor_rank.player_id IS NOT NULL),
array_agg(guild_members.guild_id) FILTER (WHERE permission_actor_rank.player_id IS NOT NULL)
)::ActorPermissionData AS data,
-- ActorPermissionLocationData
ROW(
actors.id,
actors.partition_id,
actors.map,
actors.dimension_index,
actors.transform
)::ActorPermissionLocationData AS loc
FROM
permission_actor
LEFT JOIN permission_actor_rank on permission_actor.actor_id = permission_actor_rank.permission_actor_id
LEFT JOIN guild_members ON guild_members.player_id = permission_actor_rank.player_id
LEFT JOIN actors ON actors.id = permission_actor.actor_id
WHERE
permission_actor.actor_id = ANY(in_actor_id)
GROUP BY
permission_actor.actor_id,
actors.id;
END
$function$
-- get_permission_for_player_actors(in_player_id bigint, in_min_rank smallint) -> SETOF dune.actorpermissioncombineddata
-- oid: 58329 kind: FUNCTION category: permission
CREATE OR REPLACE FUNCTION dune.get_permission_for_player_actors(in_player_id bigint, in_min_rank smallint)
RETURNS SETOF dune.actorpermissioncombineddata
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
WITH player_owned_actors AS
(
SELECT array_agg(permission_actor_id) AS ids
FROM permission_actor_rank join actors on actors.id = permission_actor_rank.permission_actor_id
WHERE permission_actor_rank.player_id = in_player_id AND permission_actor_rank.rank <= in_min_rank and actors.owner_account_id is null
)
SELECT permissions.* FROM player_owned_actors, get_permission_for_actors(player_owned_actors.ids) AS permissions;
END
$function$
-- get_placeable_id(in_actor_id bigint, in_class text, in_building_type text) -> dune.placeablegetidcomposite
-- oid: 58330 kind: FUNCTION category: building_blueprint
CREATE OR REPLACE FUNCTION dune.get_placeable_id(in_actor_id bigint, in_class text, in_building_type text)
RETURNS dune.placeablegetidcomposite
LANGUAGE plpgsql
AS $function$
DECLARE
placeable_id BIGINT;
BEGIN
SELECT INTO placeable_id id FROM placeables WHERE "id" = in_actor_id;
IF placeable_id IS NULL THEN
SELECT assign_actor_id(in_class) id INTO placeable_id;
INSERT INTO placeables("id", "building_type") VALUES(placeable_id, in_building_type);
END IF;
return ROW(placeable_id)::PlaceableGetIdComposite;
END
$function$
-- get_player_access_codes(in_account_id bigint) -> TABLE(access_code integer, access_code_type integer)
-- oid: 58331 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_player_access_codes(in_account_id bigint)
RETURNS TABLE(access_code integer, access_code_type integer)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT p.access_code, p.access_code_type
FROM player_access_codes AS p
WHERE p.account_id = in_account_id;
END; $function$
-- get_player_current_faction_reputation(in_actor_id bigint, OUT out_faction_id smallint, OUT out_reputation_amount integer) -> record
-- oid: 58332 kind: FUNCTION category: faction
CREATE OR REPLACE FUNCTION dune.get_player_current_faction_reputation(in_actor_id bigint, OUT out_faction_id smallint, OUT out_reputation_amount integer)
RETURNS record
LANGUAGE plpgsql
AS $function$
BEGIN
SELECT
pf.faction_id,
COALESCE(pfr.reputation_amount, 0)
INTO out_faction_id, out_reputation_amount
FROM player_faction pf
LEFT JOIN player_faction_reputation pfr
ON pfr.actor_id = pf.actor_id AND pfr.faction_id = pf.faction_id
WHERE pf.actor_id = in_actor_id
limit 1;
END
$function$
-- get_player_faction(in_player_id bigint, in_neutral_faction_id smallint) -> smallint
-- oid: 58333 kind: FUNCTION category: faction
CREATE OR REPLACE FUNCTION dune.get_player_faction(in_player_id bigint, in_neutral_faction_id smallint)
RETURNS smallint
LANGUAGE plpgsql
AS $function$
DECLARE
player_faction_id SMALLINT;
BEGIN
SELECT player_faction.faction_id INTO player_faction_id
FROM player_faction
WHERE actor_id = in_player_id;
IF player_faction_id IS NULL THEN
player_faction_id := in_neutral_faction_id;
END IF;
RETURN player_faction_id;
END;
$function$
-- get_player_faction_name(in_actor_id bigint, OUT player_faction_name text, OUT utc_time_faction_change timestamp without time zone) -> record
-- oid: 58334 kind: FUNCTION category: faction
CREATE OR REPLACE FUNCTION dune.get_player_faction_name(in_actor_id bigint, OUT player_faction_name text, OUT utc_time_faction_change timestamp without time zone)
RETURNS record
LANGUAGE plpgsql
AS $function$
BEGIN
SELECT factions.name, player_faction.utc_time_faction_change AT TIME ZONE 'UTC' INTO player_faction_name, utc_time_faction_change
FROM factions INNER JOIN player_faction ON factions.id = player_faction.faction_id
WHERE player_faction.actor_id = in_actor_id
limit 1;
END; $function$
-- get_player_guild_invites(in_player_id bigint) -> TABLE(invite_id bigint, guild_id bigint, guild_name text, guild_description text, sender_player_id bigint, invite_sent_timespan bigint, character_name text, sender_character_name text)
-- oid: 58335 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_player_guild_invites(in_player_id bigint)
RETURNS TABLE(invite_id bigint, guild_id bigint, guild_name text, guild_description text, sender_player_id bigint, invite_sent_timespan bigint, character_name text, sender_character_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT guild_invites.invite_id, guilds.guild_id, guilds.guild_name, guilds.guild_description, guild_invites.sender_player_id, guild_invites.invite_sent_timespan, player_state.character_name, sender_player_state.character_name AS sender_character_name
FROM guild_invites
JOIN guilds ON guilds.guild_id = guild_invites.guild_id
JOIN player_state ON player_state.player_controller_id = guild_invites.player_id
JOIN player_state AS sender_player_state ON sender_player_state.player_controller_id = guild_invites.sender_player_id
WHERE player_id = in_player_id;
END
$function$
-- get_player_ids_online_state(in_player_ids bigint[]) -> SETOF dune.playeronlinestateentry
-- oid: 58336 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_player_ids_online_state(in_player_ids bigint[])
RETURNS SETOF dune.playeronlinestateentry
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT actors.id, player_state.character_name, (actors.map, actors.partition_id, actors.dimension_index)::ServerInfo, (player_state.last_avatar_activity AT TIME ZONE 'UTC')::TIMESTAMP, player_state.online_status
FROM actors
JOIN player_state ON player_state.player_controller_id = actors.id
WHERE actors.id = ANY(in_player_ids);
END
$function$
-- get_player_infos_for_actor_ids(in_actor_ids bigint[]) -> TABLE(player_id bigint, character_name text, fls_id text, funcom_id text, platform_id text, platform_name text)
-- oid: 58337 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_player_infos_for_actor_ids(in_actor_ids bigint[])
RETURNS TABLE(player_id bigint, character_name text, fls_id text, funcom_id text, platform_id text, platform_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT ps.player_controller_id, ps.character_name, acc.user, acc.funcom_id, acc.platform_id, acc.platform_name
FROM accounts acc LEFT JOIN player_state ps ON acc.id=ps.account_id
WHERE ps.player_controller_id = ANY(in_actor_ids);
END
$function$
-- get_player_infos_for_character_names(in_character_names text[]) -> TABLE(player_id bigint, character_name text, fls_id text, funcom_id text, platform_id text, platform_name text)
-- oid: 58338 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_player_infos_for_character_names(in_character_names text[])
RETURNS TABLE(player_id bigint, character_name text, fls_id text, funcom_id text, platform_id text, platform_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT ps.player_controller_id, ps.character_name, acc.user, acc.funcom_id, acc.platform_id, acc.platform_name
FROM accounts acc LEFT JOIN player_state ps ON acc.id=ps.account_id
WHERE ps.character_name = ANY(in_character_names);
END
$function$
-- get_player_infos_for_fls_ids(in_fls_ids text[]) -> TABLE(player_id bigint, character_name text, fls_id text, funcom_id text, platform_id text, platform_name text)
-- oid: 58339 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_player_infos_for_fls_ids(in_fls_ids text[])
RETURNS TABLE(player_id bigint, character_name text, fls_id text, funcom_id text, platform_id text, platform_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT ps.player_controller_id, ps.character_name, acc.user, acc.funcom_id, acc.platform_id, acc.platform_name
FROM accounts acc LEFT JOIN player_state ps ON acc.id=ps.account_id
WHERE acc.user = ANY(in_fls_ids);
END
$function$
-- get_player_infos_for_funcom_ids(in_funcom_ids text[]) -> TABLE(player_id bigint, character_name text, fls_id text, funcom_id text, platform_id text, platform_name text)
-- oid: 58340 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_player_infos_for_funcom_ids(in_funcom_ids text[])
RETURNS TABLE(player_id bigint, character_name text, fls_id text, funcom_id text, platform_id text, platform_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT ps.player_controller_id, ps.character_name, acc.user, acc.funcom_id, acc.platform_id, acc.platform_name
FROM accounts acc LEFT JOIN player_state ps ON acc.id=ps.account_id
WHERE acc.funcom_id = ANY(in_funcom_ids);
END
$function$
-- get_player_online_state_within_grace_period_for_each_server() -> TABLE(fls_id text, previous_partition_id bigint, current_server_id text, online_status dune.playerconnectionstatus, within_grace_period boolean, last_disconnect timestamp without time zone, demo_playtime_seconds integer, logoff_persistence_end_time timestamp without time zone, party_id bigint)
-- oid: 58341 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_player_online_state_within_grace_period_for_each_server()
RETURNS TABLE(fls_id text, previous_partition_id bigint, current_server_id text, online_status dune.playerconnectionstatus, within_grace_period boolean, last_disconnect timestamp without time zone, demo_playtime_seconds integer, logoff_persistence_end_time timestamp without time zone, party_id bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT accounts.user as fls_id, player_state.previous_server_partition_id as previous_partition_id, player_state.server_id as current_server_id, player_state.online_status as online_status, player_state.reconnect_grace_period_end > (now() AT TIME ZONE 'UTC')::TIMESTAMP as within_grace_period, (player_state.last_avatar_activity AT TIME ZONE 'UTC')::TIMESTAMP as last_disconnect, demo_users.demo_playtime_seconds, (player_state.logoff_persistence_end_time)::TIMESTAMP as logoff_persistence_end_time, party_members.party_id
FROM player_state
LEFT JOIN accounts ON accounts.id = player_state.account_id
LEFT JOIN demo_users ON accounts.user = demo_users.fls_id
LEFT JOIN party_members ON player_state.player_controller_id = party_members.player_id;
END
$function$
-- get_player_owned_vehicles_data(in_player_id bigint, in_account_id bigint) -> TABLE(out_actor_id bigint, out_name text, out_class text, out_map text, out_partition_id bigint, out_dimension integer, out_transform dune.transform, out_actor_state text)
-- oid: 58342 kind: FUNCTION category: vehicle
CREATE OR REPLACE FUNCTION dune.get_player_owned_vehicles_data(in_player_id bigint, in_account_id bigint)
RETURNS TABLE(out_actor_id bigint, out_name text, out_class text, out_map text, out_partition_id bigint, out_dimension integer, out_transform dune.transform, out_actor_state text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY WITH owned_actors AS(
SELECT (entry).actor_id, (entry).actor_name FROM get_permission_for_player_actors(in_player_id, 1::smallint) -- 1:owner
WHERE (entry).actor_type = 2 -- 2:vehicles
UNION SELECT vehicle_id, vehicle_name FROM recovered_vehicles -- note: recovered vehicles are removed from permission actors
WHERE account_id = in_account_id
) SELECT owned_actors.actor_id, actor_name, class, map, partition_id, dimension_index, transform, (actor_state).state::text
FROM owned_actors LEFT JOIN actors ON actors.id = owned_actors.actor_id LEFT JOIN actor_state ON actor_state.actor_id = owned_actors.actor_id;
END
$function$
-- get_player_partition_id(in_fls_id text) -> bigint
-- oid: 58343 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_player_partition_id(in_fls_id text)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
BEGIN
-- Only the player pawn actually travels and gets it's partition id updated
return (
select actors.partition_id
from accounts as acc
join player_state as ps on ps.account_id = acc.id
join actors on actors.id = ps.player_pawn_id
where acc.user = in_fls_id
);
END
$function$
-- get_player_pawn(in_account_id bigint) -> TABLE(description dune.actordescription, server_info dune.serverinfo, player_tags text[])
-- oid: 58344 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_player_pawn(in_account_id bigint)
RETURNS TABLE(description dune.actordescription, server_info dune.serverinfo, player_tags text[])
LANGUAGE plpgsql
AS $function$
BEGIN
return query
with id as (select player_pawn_id as id from player_state where account_id = in_account_id limit 1),
tags as (select array_agg(tag) from player_tags where account_id = in_account_id)
select load_full_actors(array[id]) as description, get_actor_server_info(id) as server_info, (select * from tags) as player_tags
from id limit 1;
END
$function$
-- get_player_virtual_currency_balances(in_controller_id bigint) -> TABLE(out_currency_id smallint, out_currency_balance bigint)
-- oid: 58345 kind: FUNCTION category: currency
CREATE OR REPLACE FUNCTION dune.get_player_virtual_currency_balances(in_controller_id bigint)
RETURNS TABLE(out_currency_id smallint, out_currency_balance bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
return query (
with currencies as (select currency_id, balance from player_virtual_currency_balances where player_controller_id = in_controller_id),
bad_currencies as (select * from currencies where balance < 0),
target_account_id as (select account_id from player_state where player_controller_id = in_controller_id limit 1),
report_cheaters as (select currency_id, flag_player_as_cheater(target_account_id.account_id, 'negative_solaris') from bad_currencies, target_account_id),
fix_bad_currencies as (update player_virtual_currency_balances set balance = 0 from report_cheaters where player_controller_id = in_controller_id and player_virtual_currency_balances.currency_id = report_cheaters.currency_id)
select currency_id, balance from currencies
);
END;
$function$
-- get_players_demo_data(in_controller_ids bigint[]) -> SETOF dune.playerdemostatedescription
-- oid: 58346 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_players_demo_data(in_controller_ids bigint[])
RETURNS SETOF dune.playerdemostatedescription
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT ps.player_controller_id, (ps.last_avatar_activity AT TIME ZONE 'UTC')::TIMESTAMP, demo_playtime_seconds, demo_state
FROM encrypted_accounts acc
JOIN player_state ps ON ps.account_id = acc.id
JOIN demo_users du ON acc.user = du.fls_id
WHERE ps.player_controller_id = ANY (in_controller_ids);
END
$function$
-- get_recipes_to_remove(recipes_to_remove text[]) -> text[]
-- oid: 58347 kind: FUNCTION category: items_purge
CREATE OR REPLACE FUNCTION dune.get_recipes_to_remove(recipes_to_remove text[])
RETURNS text[]
LANGUAGE plpgsql
AS $function$
declare
result text[];
begin
select array(
select unnest(recipes_to_remove)
except
select name
from removed_recipes
) into result;
return result;
end;
$function$
-- get_registered_spawned_actor(in_spawner_id bigint) -> SETOF bigint
-- oid: 58348 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.get_registered_spawned_actor(in_spawner_id bigint)
RETURNS SETOF bigint
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT actor_id FROM actor_spawner_actors WHERE spawner_id = in_spawner_id;
END; $function$
-- get_respawn_locations(in_account_id bigint) -> dune.respawnlocation[]
-- oid: 58349 kind: FUNCTION category: spawner
CREATE OR REPLACE FUNCTION dune.get_respawn_locations(in_account_id bigint)
RETURNS dune.respawnlocation[]
LANGUAGE plpgsql
AS $function$
DECLARE
result RespawnLocation[];
BEGIN
SELECT
array_agg(
(res.id,
(
CASE
WHEN res.locator_transform IS NOT NULL THEN 'Transform'
WHEN res.locator_actor_id IS NOT NULL THEN 'PersistentActor'
WHEN res.locator_name IS NOT NULL THEN 'StaticLocatorName'
END::SpawnLocatorType,
res.locator_transform,
res.locator_actor_id,
res.locator_name,
res.locator_name_index
)::SpawnLocatorDescriptor,
res.map,
res.dimension,
res.last_used_timestamp,
res.group
)::RespawnLocation
)
INTO result
FROM player_respawn_locations res
WHERE res.account_id = in_account_id;
RETURN result;
END
$function$
-- get_schema_version() -> integer
-- oid: 58350 kind: FUNCTION category: schema_meta
CREATE OR REPLACE FUNCTION dune.get_schema_version()
RETURNS integer
LANGUAGE plpgsql
AS $function$
BEGIN
return 999999;
END;
$function$
-- get_solaris_id() -> smallint
-- oid: 58351 kind: FUNCTION category: currency
CREATE OR REPLACE FUNCTION dune.get_solaris_id()
RETURNS smallint
LANGUAGE plpgsql
IMMUTABLE
AS $function$
DECLARE
solaris_id CONSTANT SMALLINT := 0;
BEGIN
return solaris_id;
END
$function$
-- get_spawner_id(in_map text, in_name text, in_dimension_index integer) -> bigint
-- oid: 58352 kind: FUNCTION category: spawner
CREATE OR REPLACE FUNCTION dune.get_spawner_id(in_map text, in_name text, in_dimension_index integer)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
spawner_id BIGINT;
BEGIN
SELECT INTO spawner_id "id" FROM actor_spawners WHERE map = in_map AND name = in_name AND dimension_index = in_dimension_index;
IF spawner_id IS NULL THEN
INSERT INTO actor_spawners("map", "name", "dimension_index") VALUES(in_map, in_name, in_dimension_index) ON CONFLICT DO NOTHING RETURNING "id" INTO spawner_id;
IF spawner_id IS NULL THEN
SELECT INTO spawner_id "id" FROM actor_spawners WHERE map = in_map AND name = in_name AND dimension_index = in_dimension_index;
END IF;
END IF;
RETURN spawner_id;
END $function$
-- get_stored_user_data_encryption_key_hash() -> bytea
-- oid: 58353 kind: FUNCTION category: encryption
CREATE OR REPLACE FUNCTION dune.get_stored_user_data_encryption_key_hash()
RETURNS bytea
LANGUAGE sql
IMMUTABLE
AS $function$select null::bytea;$function$
-- get_stored_user_data_encryption_status() -> dune.userdataencryptionstatus
-- oid: 58354 kind: FUNCTION category: encryption
CREATE OR REPLACE FUNCTION dune.get_stored_user_data_encryption_status()
RETURNS dune.userdataencryptionstatus
LANGUAGE sql
IMMUTABLE
AS $function$select 'Disabled'::UserDataEncryptionStatus$function$
-- get_stored_user_data_encryption_taint_xmax() -> bigint
-- oid: 58355 kind: FUNCTION category: encryption
CREATE OR REPLACE FUNCTION dune.get_stored_user_data_encryption_taint_xmax()
RETURNS bigint
LANGUAGE sql
IMMUTABLE
AS $function$select null::int8;$function$
-- get_sub_inventory_id(in_owner_item_id bigint) -> bigint
-- oid: 58356 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.get_sub_inventory_id(in_owner_item_id bigint)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
inv_id BIGINT;
BEGIN
SELECT INTO inv_id id FROM inventories WHERE item_id = in_owner_item_id;
IF inv_id IS NULL THEN
INSERT INTO inventories("id", "item_id") VALUES(DEFAULT, in_owner_item_id) RETURNING id INTO inv_id;
END IF;
RETURN inv_id;
END $function$
-- get_traveling_actor_id_and_types(in_actor_id bigint) -> TABLE(id bigint, is_instigator boolean, is_player boolean, level integer)
-- oid: 58357 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_traveling_actor_id_and_types(in_actor_id bigint)
RETURNS TABLE(id bigint, is_instigator boolean, is_player boolean, level integer)
LANGUAGE plpgsql
AS $function$
begin
return query
select t.id, t.is_instigator, (ps.player_pawn_id is not null) as is_player, t.level
from get_traveling_actor_ids(in_actor_id) as t
left join player_state as ps
on t.id = ps.player_pawn_id
order by t.level, t.id;
end
$function$
-- get_traveling_actor_ids(in_actor_id bigint, in_max_recursion_level integer) -> TABLE(id bigint, is_instigator boolean, level integer)
-- oid: 58358 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_traveling_actor_ids(in_actor_id bigint, in_max_recursion_level integer DEFAULT 5)
RETURNS TABLE(id bigint, is_instigator boolean, level integer)
LANGUAGE plpgsql
AS $function$
begin
return query
with recursive
-- go down the dependency tree and gather all the parents
p(id, lvl) AS (
select a.id, 0
from actors as a
where (in_actor_id = a.id)
union all
select ap.parent_id, (p.lvl - 1)
from p, travel_actor_parent as ap
where (p.id = ap.id) and (p.lvl > -in_max_recursion_level)
),
-- find the most distant parent (root)
r as (
select * from p order by p.lvl limit 1
),
-- go up the tree from the root and gather all the children
t(id, ins, lvl) as (
select r.id, true, 0 from r
union all
select ap.id, ap.is_instigator, (t.lvl + 1)
from t, travel_actor_parent as ap
where (t.id = ap.parent_id) and (t.lvl < 2 * in_max_recursion_level)
)
select * from t order by t.lvl, id;
end
$function$
-- get_traveling_actors_fls_ids(in_actor_id bigint) -> TABLE(out_id text)
-- oid: 58359 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_traveling_actors_fls_ids(in_actor_id bigint)
RETURNS TABLE(out_id text)
LANGUAGE plpgsql
AS $function$
begin
return query
select a.user
from get_traveling_actor_ids(in_actor_id) as t
inner join player_state as ps
on t.id = ps.player_pawn_id
inner join accounts as a
on a.id = ps.account_id;
end
$function$
-- get_traveling_non_player_actor_ids(in_actor_id bigint) -> TABLE(id bigint)
-- oid: 58360 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.get_traveling_non_player_actor_ids(in_actor_id bigint)
RETURNS TABLE(id bigint)
LANGUAGE plpgsql
AS $function$
begin
return query
select t.id
from get_traveling_actor_ids(in_actor_id) as t
left join player_state as ps
on t.id = ps.player_pawn_id
left join travel_actor_parent as ap
on ap.id = in_actor_id
where
ps.player_pawn_id is null
and ap.is_instigator is true
order by t.level, t.id;
end
$function$
-- get_unbacked_up_vehicle_ids_for_account(in_account_id bigint) -> TABLE(vehicle_id bigint)
-- oid: 58361 kind: FUNCTION category: vehicle
CREATE OR REPLACE FUNCTION dune.get_unbacked_up_vehicle_ids_for_account(in_account_id bigint)
RETURNS TABLE(vehicle_id bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT v.id
FROM vehicles v
JOIN permission_actor_rank par ON v.id = par.permission_actor_id
JOIN player_state ps ON par.player_id = ps.player_controller_id
left join backup_vehicles bv ON v.id = bv.vehicle_id
WHERE ps.account_id = in_account_id
AND bv.vehicle_id IS NULL;
END
$function$
-- get_universe_time() -> TABLE(universe_time_timestamp timestamp without time zone, down_time_accumulation bigint)
-- oid: 58362 kind: FUNCTION category: schema_meta
CREATE OR REPLACE FUNCTION dune.get_universe_time()
RETURNS TABLE(universe_time_timestamp timestamp without time zone, down_time_accumulation bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT farm_variables.universe_time_timestamp, farm_variables.down_time_accumulation from farm_variables;
RETURN;
END
$function$
-- get_unsaved_base_totem_ids_for_account(in_account_id bigint) -> TABLE(totem_id bigint)
-- oid: 58363 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune.get_unsaved_base_totem_ids_for_account(in_account_id bigint)
RETURNS TABLE(totem_id bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT t.id
FROM totems t
JOIN permission_actor_rank par ON t.id = par.permission_actor_id
JOIN player_state ps ON par.player_id = ps.player_controller_id
LEFT JOIN base_backup_linked_actors bbla ON t.id = bbla.actor_id
WHERE ps.account_id = in_account_id
AND bbla.actor_id IS NULL;
END
$function$
-- get_vehicle_id(in_actor_id bigint, in_class text) -> bigint
-- oid: 58364 kind: FUNCTION category: vehicle
CREATE OR REPLACE FUNCTION dune.get_vehicle_id(in_actor_id bigint, in_class text)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
vehicle_id BIGINT;
BEGIN
SELECT INTO vehicle_id id FROM vehicles WHERE "id" = in_actor_id;
IF vehicle_id IS NULL THEN
SELECT assign_actor_id(in_class) id INTO vehicle_id;
INSERT INTO vehicles("id") VALUES(vehicle_id);
END IF;
RETURN vehicle_id;
END
$function$
-- get_vehicle_module_inventory_id(in_vehicle_module_id bigint, in_vehicle_module_inventory_type integer) -> bigint
-- oid: 58365 kind: FUNCTION category: vehicle
CREATE OR REPLACE FUNCTION dune.get_vehicle_module_inventory_id(in_vehicle_module_id bigint, in_vehicle_module_inventory_type integer)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
inv_id BIGINT;
BEGIN
SELECT INTO inv_id inventory_id FROM vehicle_module_inventories vi JOIN inventories i ON (vi.inventory_id = i.id) WHERE i.vehicle_module_id = in_vehicle_module_id AND vi.vehicle_module_inventory_type = in_vehicle_module_inventory_type;
IF inv_id IS NULL THEN
INSERT INTO inventories("id", "vehicle_module_id") VALUES(DEFAULT, in_vehicle_module_id) RETURNING id INTO inv_id;
INSERT INTO vehicle_module_inventories("inventory_id", "vehicle_module_inventory_type") VALUES(inv_id, in_vehicle_module_inventory_type);
END IF;
RETURN inv_id;
END $function$
-- guild_handle_actor_delete(in_player_id bigint) -> void
-- oid: 58366 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.guild_handle_actor_delete(in_player_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
out_guild_id BIGINT;
out_new_leader_id BIGINT;
out_guild_count INT;
BEGIN
PERFORM guilds_get_exclusive_operation_lock();
-- remove invites
PERFORM reject_guild_invite(invite_id) FROM guild_invites
where player_id = in_player_id OR sender_player_id = in_player_id;
-- Get member guild id
SELECT guild_id INTO out_guild_id FROM guild_members WHERE player_id = in_player_id;
IF FOUND THEN
SELECT INTO out_guild_count COUNT(*) FROM guild_members WHERE guild_id = out_guild_id;
IF out_guild_count < 2 THEN
PERFORM disband_guild(out_guild_id);
ELSE
-- Promote new leder
IF is_player_guild_admin(in_player_id, out_guild_id) THEN
SELECT player_id into out_new_leader_id from guild_members
WHERE guild_id = out_guild_id AND player_id <> in_player_id
LIMIT 1;
IF out_new_leader_id IS NOT NULL THEN
PERFORM promote_guild_member(out_guild_id, out_new_leader_id, 100::smallint);
END IF;
END IF;
-- remove member
PERFORM remove_guild_members(ARRAY[in_player_id], out_guild_id, 0::smallint);
END IF;
END IF;
END
$function$
-- guilds_get_exclusive_operation_lock() -> void
-- oid: 58367 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.guilds_get_exclusive_operation_lock()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
PERFORM pg_advisory_xact_lock(601145); -- GUILDS in leet :/
END;
$function$
-- handle_player_faction_guild_effects(in_player_id bigint, in_faction_id smallint, neutral_faction_id smallint) -> void
-- oid: 58368 kind: FUNCTION category: faction
CREATE OR REPLACE FUNCTION dune.handle_player_faction_guild_effects(in_player_id bigint, in_faction_id smallint, neutral_faction_id smallint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
guild_member_record record;
BEGIN
PERFORM guilds_get_exclusive_operation_lock();
SELECT * INTO guild_member_record
FROM guild_members
JOIN guilds ON guilds.guild_id = guild_members.guild_id
WHERE player_id = in_player_id;
IF guild_member_record IS NOT NULL THEN
PERFORM pg_notify('guild_notify_channel', format('player_guild_data_changed#{"GuildId" : %s , "PlayerId" : %s, "FactionId" : %s}', guild_member_record.guild_id, in_player_id, in_faction_id));
IF guild_member_record.guild_faction != neutral_faction_id THEN
-- If guild leader changes faction and guild already has a non neutral faction, break the guild allegiance
IF is_player_guild_admin(in_player_id, guild_member_record.guild_id) THEN
PERFORM break_guild_allegiance(guild_member_record.guild_id, neutral_faction_id);
-- Neutral player changing to Faction A while Guild is Faction B must be kicked
ELSEIF guild_member_record.guild_faction != in_faction_id AND in_faction_id != neutral_faction_id THEN
PERFORM remove_guild_members(ARRAY[in_player_id], guild_member_record.guild_id, 2::smallint);
END IF;
END IF;
END IF;
PERFORM clean_guild_invites_with_incompatible_faction(in_player_id, in_faction_id, neutral_faction_id);
END
$function$
-- igwo_delete_world_partitions(in_partition_ids bigint[]) -> void
-- oid: 58369 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.igwo_delete_world_partitions(in_partition_ids bigint[])
RETURNS void
LANGUAGE sql
AS $function$
delete from world_partition where partition_id = any(in_partition_ids);
$function$
-- igwo_get_partition_id_seq_last_value() -> bigint
-- oid: 58370 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.igwo_get_partition_id_seq_last_value()
RETURNS bigint
LANGUAGE sql
AS $function$
select last_value from world_partition_partition_id_seq;
$function$
-- igwo_get_partition_ids() -> SETOF bigint
-- oid: 58371 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.igwo_get_partition_ids()
RETURNS SETOF bigint
LANGUAGE sql
AS $function$
select partition_id from world_partition order by partition_id asc;
$function$
-- igwo_get_partitions() -> TABLE(partition_id bigint, map text, dimension_index integer, label text, min_x double precision, min_y double precision, max_x double precision, max_y double precision)
-- oid: 58372 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.igwo_get_partitions()
RETURNS TABLE(partition_id bigint, map text, dimension_index integer, label text, min_x double precision, min_y double precision, max_x double precision, max_y double precision)
LANGUAGE sql
AS $function$
SELECT
partition_id,
map,
dimension_index,
label,
(partition_definition->'box'->>'min_x')::float8,
(partition_definition->'box'->>'min_y')::float8,
(partition_definition->'box'->>'max_x')::float8,
(partition_definition->'box'->>'max_y')::float8
FROM world_partition
ORDER BY partition_id ASC;
$function$
-- igwo_get_server_details() -> TABLE(address text, server_id text, ready boolean, partition_id bigint, map text, dimension_index integer, label text)
-- oid: 58373 kind: FUNCTION category: igwo
CREATE OR REPLACE FUNCTION dune.igwo_get_server_details()
RETURNS TABLE(address text, server_id text, ready boolean, partition_id bigint, map text, dimension_index integer, label text)
LANGUAGE sql
AS $function$
select
host(fs.igw_addr)||':'||fs.igw_port as address,
fs.server_id,
fs.ready,
wp.partition_id,
wp.map,
wp.dimension_index,
wp.label
from get_farm_state() fs
left join world_partition wp on wp.server_id = fs.server_id
where fs.server_id is not null;
$function$
-- igwo_insert_world_partition(in_partition_id bigint, in_map text, in_partition_definition jsonb, in_dimension_index integer, in_partition_label text) -> bigint
-- oid: 58374 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.igwo_insert_world_partition(in_partition_id bigint, in_map text, in_partition_definition jsonb, in_dimension_index integer DEFAULT 0, in_partition_label text DEFAULT NULL::text)
RETURNS bigint
LANGUAGE sql
AS $function$
insert into world_partition(partition_id, map, partition_definition, dimension_index, label)
values (
in_partition_id,
in_map,
in_partition_definition,
in_dimension_index,
coalesce(in_partition_label, determine_partition_label(in_map, in_dimension_index, null, false, in_partition_id)))
returning partition_id;
$function$
-- igwo_next_partition_id_seq() -> bigint
-- oid: 58375 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.igwo_next_partition_id_seq()
RETURNS bigint
LANGUAGE sql
AS $function$
select nextval('world_partition_partition_id_seq');
$function$
-- igwo_notify_world_partition_update() -> void
-- oid: 58376 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.igwo_notify_world_partition_update()
RETURNS void
LANGUAGE sql
AS $function$
notify world_partition_update;
$function$
-- igwo_restart_partition_id_seq(in_restart_with bigint) -> void
-- oid: 58377 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.igwo_restart_partition_id_seq(in_restart_with bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
-- Use execute to substitute the numeric parameter into the alter sequence command
execute format('alter sequence world_partition_partition_id_seq restart with %s', in_restart_with);
end;
$function$
-- igwo_update_world_partition(in_map text, in_partition_definition jsonb, in_partition_id bigint, in_dimension_index integer, in_label text) -> void
-- oid: 58378 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.igwo_update_world_partition(in_map text, in_partition_definition jsonb, in_partition_id bigint, in_dimension_index integer, in_label text DEFAULT NULL::text)
RETURNS void
LANGUAGE sql
AS $function$
update world_partition
set map = in_map,
partition_definition = in_partition_definition,
dimension_index = in_dimension_index,
label = coalesce(in_label, label)
where partition_id = in_partition_id;
$function$
-- init_event_log(in_partition_id bigint) -> void
-- oid: 58379 kind: FUNCTION category: event_log
CREATE OR REPLACE FUNCTION dune.init_event_log(in_partition_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
threshold_ts TIMESTAMPTZ;
last_ts TIMESTAMPTZ;
cleanup_threshold_days CONSTANT INTEGER := 14;
BEGIN
-- calculate threshold
threshold_ts := now() - (cleanup_threshold_days * interval '1 day');
-- lock table
LOCK TABLE event_log_maintanence IN EXCLUSIVE MODE;
SELECT last_cleanup
INTO last_ts
FROM event_log_maintanence;
IF last_ts < threshold_ts THEN
-- delete events older then the passed in threshold
DELETE FROM event_log
WHERE event_time < threshold_ts;
-- Update last_cleanup in event_log_maintanence
UPDATE event_log_maintanence
SET last_cleanup = now();
END IF;
-- update partition_id
PERFORM set_config('dune.partition_id', in_partition_id::TEXT, false);
END;
$function$
-- initialize_partitions_basic_battlegroup() -> void
-- oid: 58380 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.initialize_partitions_basic_battlegroup()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 1);
perform add_partition_unique('SH_HarkoVillage', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('SH_HarkoVillage', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 1);
perform add_partition_unique('SH_Arrakeen', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('SH_Arrakeen', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 1);
perform add_partition_unique('DeepDesert_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('DeepDesert_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 1);
perform add_partition_unique('DeepDesert_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 2);
perform add_partition_unique('Overmap', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform update_partition_labels();
END;
$function$
-- initialize_partitions_basic_survival_1() -> void
-- oid: 58381 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.initialize_partitions_basic_survival_1()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 1);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 2);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 3);
perform update_partition_labels();
END;
$function$
-- initialize_partitions_development_battlegroup() -> void
-- oid: 58382 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.initialize_partitions_development_battlegroup()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
-- Current battlegroup maps
perform initialize_partitions_full_battlegroup();
-- Core development maps
perform initialize_partitions_editor_default_1x1();
perform initialize_partitions_igw_test_small_2x2();
perform initialize_partitions_igw_training();
-- Additional Gyms
perform add_partition_unique('CombatGym_01', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0, 'CombatGym_01');
perform add_partition_unique('Audio_Gym', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0, 'Audio_Gym');
perform add_partition_unique('CombatGym_Camps', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0, 'CombatGym_Camps');
END;
$function$
-- initialize_partitions_editor_default_1x1() -> void
-- oid: 58383 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.initialize_partitions_editor_default_1x1()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
perform add_partition_unique('Editor_Default', '{"type": "box2d_array", "boxes": [{"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}]}', 0, 'Editor_Default');
END;
$function$
-- initialize_partitions_full_battlegroup() -> void
-- oid: 58384 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.initialize_partitions_full_battlegroup()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 1);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 2);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 3);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 4);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 5);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 6);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 7);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 8);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 9);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 10);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 11);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 12);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 13);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 14);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 15);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 16);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 17);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 18);
perform add_partition_unique('Survival_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 19);
perform add_partition_unique('SH_HarkoVillage', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('SH_HarkoVillage', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 1);
perform add_partition_unique('SH_Arrakeen', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('SH_Arrakeen', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 1);
perform add_partition_unique('SH_FallenLight', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('SH_FallenLight', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 1);
perform add_partition_unique('CB_Story_Hephaestus', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Story_Ecolab_Carthag', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Story_WaterFatManor', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('DeepDesert_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('DeepDesert_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 1);
perform add_partition_unique('DeepDesert_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 2);
perform add_partition_unique('Overmap', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('Story_ProcesVerbal', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('DLC_Story_LostHarvest', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('DLC_Story_LostHarvest_EcolabA', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('DLC_Story_LostHarvest_EcolabB', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('DLC_Story_LostHarvest_ForgottenLab', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('Story_ArtOfKanly', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('Story_HeighlinerDungeon', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Dungeon_Hephaestus', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Dungeon_OldCarthag', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Story_BanditFortress01', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Overland_S_05', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Overland_S_06', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Overland_S_04', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Overland_M_01', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Overland_S_07', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('Story_Faction_Outpost_Hark', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('Story_Faction_Outpost_Atre', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Ecolab_Bronze_Green_089', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Ecolab_Bronze_Green_152', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Ecolab_Bronze_Green_195', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Ecolab_Bronze_Green_024', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Ecolab_Bronze_Green_136', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('PolarCap_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('PolarCap_1', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 1);
perform add_partition_unique('CB_Story_DestroyedZanovar', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Story_OrbitalMonitor', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Dungeon_TheFacility', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Dungeon_ThePit', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform add_partition_unique('CB_Overland_S_08', '{"box": {"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0}, "type": "box2d_array"}', 0);
perform update_partition_labels();
END;
$function$
-- initialize_partitions_igw_test_small_2x1() -> void
-- oid: 58385 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.initialize_partitions_igw_test_small_2x1()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
perform add_partition_unique('IGW_Test_Small', '{"type": "box2d_array", "boxes": [{"max_x": 0.5, "max_y": 1, "min_x": 0, "min_y": 0}]}', 0, 'IGW_Test_Small_A1');
perform add_partition_unique('IGW_Test_Small', '{"type": "box2d_array", "boxes": [{"max_x": 1, "max_y": 1, "min_x": 0.5, "min_y": 0}]}', 0, 'IGW_Test_Small_A2');
END;
$function$
-- initialize_partitions_igw_test_small_2x2() -> void
-- oid: 58386 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.initialize_partitions_igw_test_small_2x2()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
perform add_partition_unique('IGW_Test_Small', '{"type": "box2d_array", "boxes": [{"max_x": 0.5, "max_y": 0.5, "min_x": 0, "min_y": 0}]}', 0, 'IGW_Test_Small_A1');
perform add_partition_unique('IGW_Test_Small', '{"type": "box2d_array", "boxes": [{"max_x": 0.5, "max_y": 1, "min_x": 0, "min_y": 0.5}]}', 0, 'IGW_Test_Small_A2');
perform add_partition_unique('IGW_Test_Small', '{"type": "box2d_array", "boxes": [{"max_x": 1, "max_y": 0.5, "min_x": 0.5, "min_y": 0}]}', 0, 'IGW_Test_Small_B1');
perform add_partition_unique('IGW_Test_Small', '{"type": "box2d_array", "boxes": [{"max_x": 1, "max_y": 1, "min_x": 0.5, "min_y": 0.5}]}', 0, 'IGW_Test_Small_B2');
END;
$function$
-- initialize_partitions_igw_training() -> void
-- oid: 58387 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.initialize_partitions_igw_training()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
perform add_partition_unique('IGW_Training', '{"type": "box2d_array", "boxes": [{"max_x": 1, "max_y": 0.33333, "min_x": 0, "min_y": 0}]}', 0, 'IGW_Training_A1');
perform add_partition_unique('IGW_Training', '{"type": "box2d_array", "boxes": [{"max_x": 1, "max_y": 0.66667, "min_x": 0, "min_y": 0.33333}]}', 0, 'IGW_Training_A2');
perform add_partition_unique('IGW_Training', '{"type": "box2d_array", "boxes": [{"max_x": 1, "max_y": 1, "min_x": 0, "min_y": 0.66667}]}', 0, 'IGW_Training_A3');
END;
$function$
-- initialize_specialization_keystones(in_keystones text[]) -> TABLE(keystone_id smallint, keystone_name text)
-- oid: 58388 kind: FUNCTION category: character_mod
CREATE OR REPLACE FUNCTION dune.initialize_specialization_keystones(in_keystones text[])
RETURNS TABLE(keystone_id smallint, keystone_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
LOCK TABLE specialization_keystones_map IN SHARE ROW EXCLUSIVE MODE;
-- Note: we filter the existing values before the insert, otherwise it bumps the generated id in specialization_keystones_map
INSERT INTO specialization_keystones_map (name)
SELECT in_keystone_name FROM UNNEST(in_keystones) in_keystone_name LEFT JOIN specialization_keystones_map k ON in_keystone_name = k.name
WHERE name IS NULL;
RETURN QUERY SELECT * from specialization_keystones_map;
END $function$
-- initialize_world_partition(in_map_name text, in_num_servers integer, in_dimension_index integer) -> SETOF bigint
-- oid: 58389 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.initialize_world_partition(in_map_name text, in_num_servers integer, in_dimension_index integer DEFAULT 0)
RETURNS SETOF bigint
LANGUAGE plpgsql
AS $function$
DECLARE
BEGIN
return query
with _cleanup as (
DELETE FROM world_partition WHERE map = in_map_name and dimension_index = in_dimension_index
)
INSERT INTO world_partition (map, partition_definition, dimension_index, label)
select in_map_name, format('{"type": "cell_index", "index": %s}', generate_series)::JSONB, in_dimension_index, in_map_name || '_' || in_dimension_index || '_' || generate_series
from generate_series(0, in_num_servers - 1)
returning partition_id;
END
$function$
-- interact_get_vendor_items_bought_from_player(in_vendor_id text, in_player_id bigint, in_current_cycle_start_timestamp bigint) -> TABLE(out_template_id text, out_amount_bought integer)
-- oid: 58390 kind: FUNCTION category: stock_vendor
CREATE OR REPLACE FUNCTION dune.interact_get_vendor_items_bought_from_player(in_vendor_id text, in_player_id bigint, in_current_cycle_start_timestamp bigint)
RETURNS TABLE(out_template_id text, out_amount_bought integer)
LANGUAGE plpgsql
AS $function$
DECLARE
player_timestamp BIGINT;
BEGIN
-- Clean items bought by player if the vendor's cycle was reset since the last time they interacted with it
IF EXISTS
(SELECT * FROM vendor_stock_cycle
WHERE vendor_id = in_vendor_id AND player_id = in_player_id AND last_interacted_timestamp < in_current_cycle_start_timestamp)
THEN
DELETE FROM vendor_stock_state WHERE vendor_id = in_vendor_id AND player_id = in_player_id;
END IF;
PERFORM update_vendor_timestamp_for_player(in_vendor_id, in_player_id, in_current_cycle_start_timestamp);
RETURN QUERY
SELECT template_id, amount_bought FROM vendor_stock_state WHERE vendor_id = in_vendor_id AND player_id = in_player_id;
END
$function$
-- internal_add_party_member(in_invite_id bigint, in_party_id bigint, in_player_id bigint, in_platform_session_id text, in_platform_name text, in_max_party_member_count integer) -> dune.partyacceptinviteresult
-- oid: 58391 kind: FUNCTION category: party
CREATE OR REPLACE FUNCTION dune.internal_add_party_member(in_invite_id bigint, in_party_id bigint, in_player_id bigint, in_platform_session_id text, in_platform_name text, in_max_party_member_count integer)
RETURNS dune.partyacceptinviteresult
LANGUAGE plpgsql
AS $function$
DECLARE
party_exists BOOLEAN;
member_count INTEGER;
out_platform_members_count INTEGER;
out_player_name TEXT;
out_accept_error PartyAcceptInviteResult DEFAULT 'Success'::PartyAcceptInviteResult;
BEGIN
-- check if party exists
SELECT INTO party_exists EXISTS (SELECT 1 FROM parties WHERE party_id = in_party_id);
IF NOT party_exists THEN
PERFORM remove_party_invite(in_invite_id, 2::smallint); -- PartyNoLongerExists = 2
RAISE NOTICE 'Trying to add player % to non existing party %.', in_player_id, in_party_id;
out_accept_error = 'NonExistingParty'::PartyAcceptInviteResult;
RETURN out_accept_error;
END IF;
-- check party member count
SELECT INTO member_count COUNT(*) FROM party_members WHERE party_id = in_party_id;
IF member_count >= in_max_party_member_count THEN
PERFORM remove_party_invite(in_invite_id, 1::smallint); -- PartyFull = 1
RAISE NOTICE 'Trying to add more members than the allowed % to party %.', in_max_party_member_count, in_party_id;
out_accept_error = 'PartyFull'::PartyAcceptInviteResult;
RETURN out_accept_error;
END IF;
-- insert member
INSERT INTO party_members("player_id", "party_id") VALUES(in_player_id, in_party_id);
-- track platform information
SELECT num_of_players INTO out_platform_members_count FROM platform_parties_mapping WHERE platform_name = in_platform_name AND dune_party_id = in_party_id;
IF out_platform_members_count IS NOT NULL THEN
-- there was a platform session for the player's party
UPDATE platform_parties_mapping SET num_of_players = out_platform_members_count+1 WHERE platform_name = in_platform_name AND dune_party_id = in_party_id;
ELSE
-- no mapping for this platform yet, add
INSERT INTO platform_parties_mapping ("platform_session_id", "platform_name", "dune_party_id", "num_of_players")
SELECT in_platform_session_id, in_platform_name, in_party_id, 1
WHERE in_platform_session_id <> '' AND in_platform_name <> '';
END IF;
-- Get player name
SELECT player_state.character_name INTO out_player_name
FROM player_state WHERE player_state.player_controller_id = in_player_id;
PERFORM remove_party_invite(in_invite_id, 0::smallint); -- Silent = 0
PERFORM pg_notify('party_notify_channel', format(
'add_party_member#{"PartyId" : %s, "PlayerId" : %s, "PlayerName" : "%s", "PlayerPlatformName" : "%s", "PlayerPlatformSessionId" : "%s"}',
in_party_id, in_player_id, out_player_name, in_platform_name, in_platform_session_id));
RETURN out_accept_error;
END
$function$
-- internal_create_party(in_invite_id bigint, in_leader_id bigint, in_leader_platform_session_id text, in_leader_platform_name text, in_member_id bigint, in_platform_session_id text, in_platform_name text) -> bigint
-- oid: 58392 kind: FUNCTION category: party
CREATE OR REPLACE FUNCTION dune.internal_create_party(in_invite_id bigint, in_leader_id bigint, in_leader_platform_session_id text, in_leader_platform_name text, in_member_id bigint, in_platform_session_id text, in_platform_name text)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
leader_registered BOOLEAN;
players_belong_to_party BOOLEAN;
out_party_id BIGINT;
out_player_name TEXT;
out_leader_name TEXT;
BEGIN
-- Check if the leader already exists in the parties table
SELECT INTO leader_registered EXISTS (SELECT 1 FROM parties WHERE party_leader_id = in_leader_id);
IF leader_registered THEN
RAISE EXCEPTION 'Leader already has a party.';
END IF;
-- Check if either the leader or member already exists in the party_members table
SELECT INTO players_belong_to_party EXISTS (SELECT 1 FROM party_members WHERE player_id = in_leader_id OR player_id = in_member_id);
IF players_belong_to_party THEN
RAISE EXCEPTION 'One of the players is already in a party.';
END IF;
-- If neither condition is met, insert the new party and members
INSERT INTO parties ("party_leader_id") VALUES (in_leader_id) RETURNING party_id INTO out_party_id;
INSERT INTO party_members (player_id, party_id) VALUES (in_leader_id, out_party_id), (in_member_id, out_party_id);
-- Update all of the leaders invites to have the new party as party id
UPDATE party_invites SET party_id = out_party_id WHERE sender_player_id = in_leader_id;
-- Get leader name
SELECT player_state.character_name INTO out_leader_name
FROM player_state WHERE player_state.player_controller_id = in_leader_id;
-- Get member name
SELECT player_state.character_name INTO out_player_name
FROM player_state WHERE player_state.player_controller_id = in_member_id;
-- Handle platform sessions mapping for new party
IF in_leader_platform_name = in_platform_name THEN
-- If players are from the same platform and leader has session id (console), we create mapping (if their platform_name and session_id are valid)
INSERT INTO platform_parties_mapping ("platform_session_id", "platform_name", "dune_party_id", "num_of_players")
SELECT in_leader_platform_session_id, in_leader_platform_name, out_party_id, 2
WHERE in_leader_platform_session_id <> '' AND in_leader_platform_name <> '';
ELSE
-- Create leader's platform session mapping if their platform_name and session_id are valid
INSERT INTO platform_parties_mapping ("platform_session_id", "platform_name", "dune_party_id", "num_of_players")
SELECT in_leader_platform_session_id, in_leader_platform_name, out_party_id, 1
WHERE in_leader_platform_session_id <> '' AND in_leader_platform_name <> '';
-- Create member's platform session mapping if their platform_name and session_id are valid
INSERT INTO platform_parties_mapping ("platform_session_id", "platform_name", "dune_party_id", "num_of_players")
SELECT in_platform_session_id, in_platform_name, out_party_id, 1
WHERE in_platform_session_id <> '' AND in_platform_name <> '';
END IF;
PERFORM remove_party_invite(in_invite_id, 0::smallint); -- Silent = 0
PERFORM pg_notify('party_notify_channel', format(
'create_party#{"PartyId" : %s, "LeaderId" : %s, "LeaderName" : "%s", "LeaderPlatformName" : "%s", "LeaderPlatformSessionId" : "%s", "MemberId" : %s, "MemberName" : "%s", "MemberPlatformName" : "%s", "MemberPlatformSessionId" : "%s"}',
out_party_id, in_leader_id, out_leader_name, in_leader_platform_name, in_leader_platform_session_id, in_member_id, out_player_name, in_platform_name, in_platform_session_id));
RETURN out_party_id;
END
$function$
-- is_player_guild_admin(in_player_id bigint, in_guild_id bigint) -> boolean
-- oid: 58393 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.is_player_guild_admin(in_player_id bigint, in_guild_id bigint)
RETURNS boolean
LANGUAGE plpgsql
AS $function$
DECLARE
found_role_id SMALLINT;
BEGIN
SELECT role_id FROM guild_members WHERE player_id = in_player_id AND guild_id = in_guild_id INTO found_role_id;
IF NOT FOUND THEN
RETURN FALSE;
END IF;
RETURN found_role_id = 100;
END
$function$
-- is_player_offline(in_fls_id text) -> boolean
-- oid: 58394 kind: FUNCTION category: lookup
-- comment: Return true if player is marked as offline, taking into account server crashing before players online state was updated in DB.
CREATE OR REPLACE FUNCTION dune.is_player_offline(in_fls_id text)
RETURNS boolean
LANGUAGE plpgsql
AS $function$
declare
has_state int;
is_offline boolean;
begin
-- If there's no player_state row for this account, treat the player as offline
select count(*) into has_state
from player_state ps
join accounts a on a.id = ps.account_id
where a.user = in_fls_id;
if has_state = 0 then
return true;
end if;
select exists(
select 1
from player_state ps
join accounts a on a.id = ps.account_id
where a.user = in_fls_id
and (
ps.online_status = 'Offline'
-- Player is treated as offline if last played server is offline/unavailable, or not set at all
or (ps.server_id is null or ps.server_id not in (select * from active_server_ids))
)
) into is_offline;
return is_offline;
end
$function$
-- is_player_party_leader(in_player_id bigint, in_party_id bigint) -> boolean
-- oid: 58395 kind: FUNCTION category: lookup
CREATE OR REPLACE FUNCTION dune.is_player_party_leader(in_player_id bigint, in_party_id bigint)
RETURNS boolean
LANGUAGE plpgsql
AS $function$
DECLARE
found_leader_id SMALLINT;
BEGIN
SELECT party_leader_id FROM parties WHERE party_id = in_party_id INTO found_leader_id;
IF NOT FOUND THEN
RETURN FALSE;
END IF;
RETURN found_leader_id = in_player_id;
END
$function$
-- join_platform_session_party(in_leader_platform_id text, in_player_platform_id text, in_platform_session_id text, in_platform_name text, in_max_party_member_count integer) -> dune.partyacceptinviteresult
-- oid: 58396 kind: FUNCTION category: party
CREATE OR REPLACE FUNCTION dune.join_platform_session_party(in_leader_platform_id text, in_player_platform_id text, in_platform_session_id text, in_platform_name text, in_max_party_member_count integer)
RETURNS dune.partyacceptinviteresult
LANGUAGE plpgsql
AS $function$
DECLARE
out_party_id BIGINT;
out_accept_error PartyAcceptInviteResult DEFAULT 'Success'::PartyAcceptInviteResult;
out_leader_id BIGINT;
out_player_id BIGINT;
BEGIN
IF in_platform_name IS NULL OR length(in_platform_name) = 0 THEN
RAISE EXCEPTION 'platform_name must not be empty';
END IF;
IF in_platform_session_id IS NULL OR length(in_platform_session_id) = 0 THEN
RAISE EXCEPTION 'platform_session_id must not be empty';
END IF;
-- Fetch party id, but do nothing with it
SELECT dune_party_id INTO out_party_id
FROM platform_parties_mapping
WHERE platform_session_id = in_platform_session_id
AND platform_name = in_platform_name
LIMIT 1;
out_leader_id = get_controller_id_from_platform_id(in_leader_platform_id);
out_player_id = get_controller_id_from_platform_id(in_player_platform_id);
IF out_party_id IS NULL THEN
-- Using same platform name and platform session id cause they are joining through system invite, they'll always be the same platform.
PERFORM internal_create_party(NULL, out_leader_id, in_platform_session_id, in_platform_name,
out_player_id, in_platform_session_id, in_platform_name);
ELSE
out_accept_error := internal_add_party_member(NULL, out_party_id, out_player_id, in_platform_session_id, in_platform_name, in_max_party_member_count);
END IF;
RETURN out_accept_error;
END
$function$
-- journey_story_node_cooldown_add(in_account_id bigint, in_story_node_id text, in_time_to_expire timestamp without time zone) -> void
-- oid: 58397 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.journey_story_node_cooldown_add(in_account_id bigint, in_story_node_id text, in_time_to_expire timestamp without time zone)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO journey_story_node_cooldown(account_id, story_node_id, time_to_expire)
VALUES(in_account_id, in_story_node_id, in_time_to_expire);
END $function$
-- journey_story_node_cooldown_delete_expired(in_time_to_check timestamp without time zone) -> void
-- oid: 58398 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.journey_story_node_cooldown_delete_expired(in_time_to_check timestamp without time zone)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM journey_story_node_cooldown WHERE time_to_expire < in_time_to_check;
END $function$
-- landsraad_cast_vote(in_term_id bigint, in_player_id bigint, in_decree_name text) -> void
-- oid: 58399 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_cast_vote(in_term_id bigint, in_player_id bigint, in_decree_name text)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
term_winning_faction_id SMALLINT = NULL;
elected_decree_id BIGINT = NULL;
player_guild_id BIGINT = NULL;
guild_faction_id SMALLINT = NULL;
voting_decree_id BIGINT = NULL;
voting_influence INTEGER = 0;
guild_ids_json JSON = NULL;
BEGIN
LOCK TABLE landsraad_decree_votes IN EXCLUSIVE MODE;
SELECT term.winning_faction_id, term.elected_decree_id FROM landsraad_decree_term AS term WHERE term.term_id = in_term_id INTO term_winning_faction_id, elected_decree_id;
IF term_winning_faction_id IS NULL THEN
RAISE EXCEPTION 'Cannot insert landsraad vote, term % has no winning faction yet', in_term_id;
END IF;
IF elected_decree_id IS NOT NULL THEN
RAISE EXCEPTION 'Cannot insert landsraad vote, term % already has an elected decree', in_term_id;
END IF;
SELECT guilds.guild_id, guilds.guild_faction FROM guild_members
JOIN guilds ON guild_members.guild_id = guilds.guild_id WHERE guild_members.player_id = in_player_id INTO player_guild_id, guild_faction_id;
IF player_guild_id IS NULL THEN
RAISE EXCEPTION 'Cannot insert landsraad vote, player % not in guild', in_player_id;
END IF;
IF guild_faction_id != term_winning_faction_id THEN
RAISE EXCEPTION 'Cannot insert landsraad vote, guild % not alligned to winning faction %', player_guild_id, term_winning_faction_id;
END IF;
IF is_player_guild_admin(in_player_id, player_guild_id) = FALSE THEN
RAISE EXCEPTION 'Cannot insert landsraad vote, player % is not guild admin of guild %', in_player_id, player_guild_id;
END IF;
IF EXISTS (SELECT FROM landsraad_decree_votes AS votes WHERE votes.guild_id = player_guild_id) THEN
RAISE WARNING 'Cannot insert landsraad vote, guild % has voted already', player_guild_id;
RETURN;
END IF;
SELECT decrees.id FROM landsraad_decree_rotation AS rotation
INNER JOIN landsraad_decrees AS decrees ON rotation.decree_id = decrees.id
WHERE decrees.decree_name = in_decree_name INTO voting_decree_id;
IF voting_decree_id IS NULL THEN
RAISE EXCEPTION 'Cannot insert landsraad vote, decree % not for election', in_decree_name;
END IF;
SELECT FLOOR(landsraad_load_guild_contribution(in_term_id, player_guild_id, term_winning_faction_id))::INTEGER INTO voting_influence;
IF voting_influence IS NULL THEN
RAISE WARNING 'Cannot insert landsraad vote, guild % has no contribution', player_guild_id;
RETURN;
END IF;
INSERT INTO landsraad_decree_votes VALUES(voting_decree_id, player_guild_id, in_player_id, voting_influence);
SELECT json_agg(player_guild_id) INTO guild_ids_json;
PERFORM pg_notify('landsraad_notify_channel', format('guild_vote_changed#{"GuildIds": %s}', guild_ids_json));
END $function$
-- landsraad_change_term_end_time(end_term_id bigint, new_end_time timestamp without time zone, in_test_term boolean) -> void
-- oid: 58400 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_change_term_end_time(end_term_id bigint, new_end_time timestamp without time zone, in_test_term boolean)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
LOCK TABLE landsraad_decree_term IN EXCLUSIVE MODE;
UPDATE landsraad_decree_term SET test_term = in_test_term WHERE term_id = end_term_id AND test_term = false;
UPDATE landsraad_decree_term SET end_time = new_end_time AT TIME ZONE 'UTC' WHERE term_id = end_term_id;
END $function$
-- landsraad_check_task_completion() -> trigger
-- oid: 58401 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_check_task_completion()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
task_completed BOOLEAN = FALSE;
BEGIN
-- if a faction reached the tasks's goal amount set the task as completed
WITH faction_progress AS (SELECT landsraad_task_faction_contributions.task_id, SUM(landsraad_task_faction_contributions.amount) AS amount FROM landsraad_task_faction_contributions
WHERE landsraad_task_faction_contributions.task_id = NEW.task_id AND faction_id = NEW.faction_id GROUP BY faction_id, landsraad_task_faction_contributions.task_id)
SELECT COALESCE (faction_progress.amount, 0) >= landsraad_tasks.goal_amount
FROM landsraad_tasks LEFT JOIN faction_progress ON landsraad_tasks.id = faction_progress.task_id
WHERE landsraad_tasks.id = NEW.task_id
INTO task_completed;
IF task_completed THEN
UPDATE landsraad_tasks SET completed = TRUE, winning_faction_id = NEW.faction_id, completion_time = NOW() WHERE id = NEW.task_id AND completed = FALSE;
PERFORM pg_notify('landsraad_notify_channel', 'state_changed');
END IF;
RETURN NULL;
END $function$
-- landsraad_check_term_won() -> trigger
-- oid: 58402 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_check_term_won()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
row_completed BIGINT[] = NULL;
column_completed BIGINT[] = NULL;
diagonal_1_completed BIGINT[] = NULL;
diagonal_2_completed BIGINT[] = NULL;
sysselraad_amount INTEGER = 0;
BEGIN
-- check for sysselrad rule (a row, a column or a diagonal completed by a faction)
WITH board AS (SELECT task.id as task_id, task.winning_faction_id, task.board_index / 5 AS row, task.board_index % 5 AS col, task.sysselraad FROM landsraad_tasks task WHERE task.term_id = NEW.term_id)
SELECT
(SELECT array_agg(board.task_id) FROM board WHERE board.row = ANY(SELECT board.row FROM board WHERE board.winning_faction_id = NEW.winning_faction_id GROUP BY (board.row) HAVING COUNT(board.col) = 5)),
(SELECT array_agg(board.task_id) FROM board WHERE board.col = ANY(SELECT board.col FROM board WHERE board.winning_faction_id = NEW.winning_faction_id GROUP BY (board.col) HAVING COUNT(board.row) = 5)),
(SELECT CASE WHEN COUNT(board.winning_faction_id) = 5 THEN array_agg(board.task_id) END FROM ( VALUES (0, 0), (1, 1), (2, 2), (3, 3), (4, 4) ) AS t(row, col) JOIN board ON board.row = t.row AND board.col = t.col WHERE board.winning_faction_id = NEW.winning_faction_id),
(SELECT CASE WHEN COUNT(board.winning_faction_id) = 5 THEN array_agg(board.task_id) END FROM ( VALUES (0, 4), (1, 3), (2, 2), (3, 1), (4, 0) ) AS t(row, col) JOIN board ON board.row = t.row AND board.col = t.col WHERE board.winning_faction_id = NEW.winning_faction_id),
(SELECT COUNT(*) FROM board WHERE board.sysselraad)
INTO row_completed, column_completed, diagonal_1_completed, diagonal_2_completed, sysselraad_amount;
IF sysselraad_amount = 0 AND (row_completed IS NOT NULL OR column_completed IS NOT NULL OR diagonal_1_completed IS NOT NULL OR diagonal_2_completed IS NOT NULL) THEN
UPDATE landsraad_tasks SET sysselraad = TRUE WHERE id = ANY(row_completed || column_completed || diagonal_1_completed || diagonal_2_completed);
UPDATE landsraad_decree_term SET winning_faction_id = NEW.winning_faction_id WHERE term_id = NEW.term_id AND winning_faction_id IS NULL;
PERFORM pg_notify('landsraad_notify_channel', 'state_changed');
END IF;
RETURN NULL;
END $function$
-- landsraad_collect_task_telemetry_for_faction(in_term_id bigint, in_faction_name text) -> TABLE(task_telemetry dune.landsraadtermtasktelemetry[])
-- oid: 58403 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_collect_task_telemetry_for_faction(in_term_id bigint, in_faction_name text)
RETURNS TABLE(task_telemetry dune.landsraadtermtasktelemetry[])
LANGUAGE plpgsql
AS $function$
DECLARE
current_faction_id SMALLINT = NULL;
BEGIN
SELECT id FROM factions WHERE factions.name = in_faction_name INTO current_faction_id;
RETURN query SELECT ARRAY_AGG((in_faction_name, landsraad_tasks.house_name, task_reveal.revealed, (CASE WHEN winning_faction_id = current_faction_id THEN TRUE ELSE FALSE END), task_progress.participant_count, CAST(landsraad_tasks.board_index AS INTEGER), landsraad_tasks.completion_time)::LandsraadTermTaskTelemetry)
FROM landsraad_tasks
LEFT JOIN landsraad_task_reveal_state task_reveal
ON task_reveal.task_id = landsraad_tasks.id AND task_reveal.faction_id = current_faction_id
LEFT JOIN LATERAL (
SELECT COUNT(DISTINCT ltpc.player_id) AS participant_count FROM landsraad_task_player_contributions ltpc WHERE ltpc.task_id = landsraad_tasks.id and ltpc.faction_id = current_faction_id GROUP BY task_id
) AS task_progress ON true
WHERE landsraad_tasks.term_id = in_term_id;
END $function$
-- landsraad_collect_term_telemetry(in_term_id bigint, in_faction_names text[]) -> TABLE(term_telemetry dune.landsraadtermtelemetry[], task_telemetry dune.landsraadtermtasktelemetry[])
-- oid: 58404 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_collect_term_telemetry(in_term_id bigint, in_faction_names text[])
RETURNS TABLE(term_telemetry dune.landsraadtermtelemetry[], task_telemetry dune.landsraadtermtasktelemetry[])
LANGUAGE plpgsql
AS $function$
DECLARE
faction_name TEXT = NULL;
term_telemetry LandsraadTermTelemetry[];
task_telemetry LandsraadTermTaskTelemetry[];
BEGIN
FOREACH faction_name IN ARRAY in_faction_names
LOOP
term_telemetry = ARRAY_APPEND(term_telemetry, landsraad_collect_term_telemetry_for_faction(in_term_id, faction_name));
task_telemetry = ARRAY_CAT(task_telemetry, landsraad_collect_task_telemetry_for_faction(in_term_id, faction_name));
END LOOP;
RETURN query SELECT term_telemetry, task_telemetry;
END $function$
-- landsraad_collect_term_telemetry_for_faction(in_term_id bigint, in_faction_name text) -> dune.landsraadtermtelemetry
-- oid: 58405 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_collect_term_telemetry_for_faction(in_term_id bigint, in_faction_name text)
RETURNS dune.landsraadtermtelemetry
LANGUAGE plpgsql
AS $function$
DECLARE
current_faction_id SMALLINT = NULL;
winning_faction_id SMALLINT = NULL;
start_time TIMESTAMPTZ = NULL;
end_time TIMESTAMPTZ = NULL;
sysselraad_count INTEGER = NULL;
term_result TEXT = NULL;
faction_won BOOLEAN = FALSE;
participants_num_faction INTEGER = NULL;
tasks_completed INTEGER = NULL;
tasks_revealed INTEGER = NULL;
BEGIN
SELECT id FROM factions WHERE factions.name = in_faction_name INTO current_faction_id;
SELECT term.winning_faction_id, term.start_time, term.end_time FROM landsraad_decree_term AS term WHERE term_id = in_term_id INTO winning_faction_id, start_time, end_time;
IF winning_faction_id IS NULL THEN
term_result = 'TIE';
ELSE
SELECT COUNT(id) FROM landsraad_tasks WHERE term_id = in_term_id AND sysselraad = true INTO sysselraad_count;
IF sysselraad_count = 5 THEN
term_result = 'SYSSELRAAD';
ELSE
term_result = 'TASK_COUNT';
END IF;
END IF;
IF winning_faction_id = current_faction_id THEN
faction_won = true;
END IF;
SELECT COUNT(DISTINCT player_id) FROM landsraad_task_player_contributions LEFT JOIN landsraad_tasks ON
landsraad_task_player_contributions.task_id = landsraad_tasks.id
WHERE landsraad_tasks.term_id = in_term_id AND landsraad_task_player_contributions.faction_id = current_faction_id
INTO participants_num_faction;
SELECT COUNT(id) FROM landsraad_tasks WHERE landsraad_tasks.term_id = in_term_id
AND landsraad_tasks.winning_faction_id = current_faction_id AND landsraad_tasks.completed = true
INTO tasks_completed;
SELECT COUNT(DISTINCT landsraad_tasks.id) FROM landsraad_task_reveal_state LEFT JOIN landsraad_tasks ON
landsraad_task_reveal_state.task_id = landsraad_tasks.id
WHERE landsraad_tasks.term_id = in_term_id AND landsraad_task_reveal_state.faction_id = current_faction_id AND landsraad_task_reveal_state.revealed = true
INTO tasks_revealed;
RETURN (in_faction_name, term_result, faction_won, participants_num_faction, tasks_completed, tasks_revealed, start_time, end_time)::LandsraadTermTelemetry;
END $function$
-- landsraad_collect_vote_telemetry(in_term_id bigint, in_winning_faction_id integer) -> TABLE(guild_id bigint, decree_name text, voting_influence integer)
-- oid: 58406 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_collect_vote_telemetry(in_term_id bigint, in_winning_faction_id integer)
RETURNS TABLE(guild_id bigint, decree_name text, voting_influence integer)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN query SELECT guild_contribution.guild_id, landsraad_decrees.decree_name, FLOOR(SUM(guild_contribution.amount))::INTEGER
FROM landsraad_tasks AS tasks
INNER JOIN landsraad_task_guild_contributions AS guild_contribution
ON guild_contribution.task_id = tasks.id AND guild_contribution.faction_id = tasks.winning_faction_id AND tasks.term_id = in_term_id AND guild_contribution.faction_id = in_winning_faction_id
LEFT JOIN landsraad_decree_votes
ON landsraad_decree_votes.guild_id = guild_contribution.guild_id
LEFT JOIN landsraad_decrees
ON landsraad_decree_votes.decree_id = landsraad_decrees.id
GROUP BY (guild_contribution.guild_id, landsraad_decrees.decree_name);
END $function$
-- landsraad_collect_votes(in_term_id bigint) -> TABLE(elected_decree text, winning_faction_name text, available_decrees text[], guild_votes dune.landsraadguildvotetelemetry[])
-- oid: 58407 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_collect_votes(in_term_id bigint)
RETURNS TABLE(elected_decree text, winning_faction_name text, available_decrees text[], guild_votes dune.landsraadguildvotetelemetry[])
LANGUAGE plpgsql
AS $function$
DECLARE
has_elected_decree BOOL = FALSE;
has_winning_faction BOOL = FALSE;
winning_decree_id BIGINT = NULL;
winning_decree_name TEXT = NULL;
winning_faction_name TEXT = NULL;
winning_faction_id INT = NULL;
available_decrees TEXT[];
vote_telemetry LandsraadGuildVoteTelemetry[];
BEGIN
LOCK TABLE landsraad_decree_term, landsraad_decree_rotation, landsraad_decree_votes IN EXCLUSIVE MODE;
SELECT CASE WHEN landsraad_decree_term.elected_decree_id IS NULL THEN FALSE ELSE TRUE END,
CASE WHEN landsraad_decree_term.winning_faction_id IS NULL THEN FALSE ELSE TRUE END
FROM landsraad_decree_term WHERE term_id = in_term_id INTO has_elected_decree, has_winning_faction;
IF has_winning_faction IS FALSE THEN
RETURN query SELECT NULL, NULL, available_decrees, vote_telemetry;
RETURN;
END IF;
SELECT factions.name, term.winning_faction_id FROM landsraad_decree_term AS term LEFT JOIN factions ON term.winning_faction_id = factions.id WHERE term.term_id = in_term_id INTO winning_faction_name, winning_faction_id;
SELECT ARRAY_AGG(landsraad_decrees.decree_name) FROM landsraad_decree_rotation INNER JOIN landsraad_decrees ON landsraad_decree_rotation.decree_id = landsraad_decrees.id INTO available_decrees;
SELECT ARRAY_AGG((guild_id, decree_name, voting_influence)::LandsraadGuildVoteTelemetry) FROM landsraad_collect_vote_telemetry(in_term_id, winning_faction_id) INTO vote_telemetry;
-- Only resolve votes if the latest term has no elected decree
IF has_elected_decree IS FALSE THEN
WITH
votes AS (SELECT decree_id, SUM(influence) AS amount FROM landsraad_decree_votes GROUP BY decree_id),
max_votes AS (SELECT MAX(amount) AS amount FROM votes)
UPDATE landsraad_decree_term
SET elected_decree_id =
CASE WHEN (SELECT amount FROM max_votes) IS NOT NULL THEN
(SELECT decree_id FROM votes WHERE amount = (SELECT amount FROM max_votes) ORDER BY RANDOM() LIMIT 1)
ELSE
(SELECT decree_id FROM landsraad_decree_rotation ORDER BY RANDOM() LIMIT 1)
END
WHERE term_id = in_term_id
returning elected_decree_id INTO winning_decree_id;
ELSE
SELECT term.elected_decree_id FROM landsraad_decree_term term ORDER BY term_id DESC LIMIT 1 INTO winning_decree_id;
END IF;
SELECT decree_name FROM landsraad_decrees WHERE id = winning_decree_id INTO winning_decree_name;
RETURN query SELECT winning_decree_name, winning_faction_name, available_decrees, vote_telemetry;
END $function$
-- landsraad_determine_winner(in_term_id bigint) -> text
-- oid: 58408 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_determine_winner(in_term_id bigint)
RETURNS text
LANGUAGE plpgsql
AS $function$
DECLARE
has_winning_faction BOOLEAN = FALSE;
winning_faction_name TEXT = NULL;
BEGIN
SELECT CASE WHEN winning_faction_id IS NULL THEN FALSE ELSE TRUE END FROM landsraad_decree_term WHERE term_id = in_term_id INTO has_winning_faction;
-- only set winning faction if not set already (sysselraad has been secured)
IF NOT has_winning_faction THEN
WITH tasks_completed_by_faction AS (SELECT winning_faction_id AS faction, COUNT(id) AS num_tasks FROM landsraad_tasks WHERE term_id = in_term_id AND winning_faction_id IS NOT NULL GROUP BY (winning_faction_id)),
winner_count AS (SELECT COUNT(faction) AS amount FROM tasks_completed_by_faction WHERE num_tasks = (SELECT MAX(num_tasks) FROM tasks_completed_by_faction) GROUP BY(num_tasks)),
winner AS (SELECT faction FROM tasks_completed_by_faction ORDER BY num_tasks DESC LIMIT 1)
UPDATE landsraad_decree_term SET winning_faction_id = CASE WHEN winner_count.amount = 1 THEN winner.faction ELSE NULL END FROM winner, winner_count;
PERFORM pg_notify('landsraad_notify_channel', 'state_changed');
END IF;
SELECT factions.name FROM landsraad_decree_term AS term LEFT JOIN factions ON term.winning_faction_id = factions.id WHERE term.term_id = in_term_id INTO winning_faction_name;
RETURN winning_faction_name;
END $function$
-- landsraad_force_end_term(end_term_id bigint) -> void
-- oid: 58409 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_force_end_term(end_term_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
PERFORM landsraad_change_term_end_time(end_term_id, now() AT TIME ZONE 'UTC', false);
END $function$
-- landsraad_has_term_of_task_ended(in_task_id bigint) -> boolean
-- oid: 58410 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_has_term_of_task_ended(in_task_id bigint)
RETURNS boolean
LANGUAGE plpgsql
AS $function$
DECLARE
term_ended BOOLEAN = FALSE;
BEGIN
SELECT NOW() > term.end_time FROM landsraad_tasks AS task LEFT JOIN landsraad_decree_term AS term ON task.term_id = term.term_id WHERE task.id = in_task_id INTO term_ended;
RETURN term_ended;
END $function$
-- landsraad_initialize_system(number_of_weeks_term_retention integer, number_of_nominated_decrees integer, in_end_time timestamp without time zone, in_test_term boolean, faction_names text[], decrees dune.landsraaddecree[], tasks dune.landsraadtask[], task_rewards dune.landsraadtaskreward[]) -> TABLE(term_id bigint, reigning_faction_name text, active_decree_name text, winning_faction_name text, elected_decree_name text, start_time timestamp without time zone, end_time timestamp without time zone)
-- oid: 58411 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_initialize_system(number_of_weeks_term_retention integer, number_of_nominated_decrees integer, in_end_time timestamp without time zone, in_test_term boolean, faction_names text[], decrees dune.landsraaddecree[], tasks dune.landsraadtask[], task_rewards dune.landsraadtaskreward[])
RETURNS TABLE(term_id bigint, reigning_faction_name text, active_decree_name text, winning_faction_name text, elected_decree_name text, start_time timestamp without time zone, end_time timestamp without time zone)
LANGUAGE plpgsql
AS $function$
DECLARE
current_term_id BIGINT = NULL;
BEGIN
LOCK TABLE landsraad_decree_term, landsraad_decree_rotation, landsraad_decrees, landsraad_decree_votes IN EXCLUSIVE MODE;
CALL landsraad_update_decrees(decrees);
CALL landsraad_update_factions(faction_names);
SELECT term.term_id FROM landsraad_decree_term term ORDER BY term.term_id DESC LIMIT 1 INTO current_term_id;
IF FOUND THEN
RETURN query SELECT term.term_id, reigning_faction.name, active_decree.decree_name, winning_faction.name, elected_decree.decree_name, (term.start_time AT TIME ZONE 'UTC')::TIMESTAMP, (term.end_time AT TIME ZONE 'UTC')::TIMESTAMP
FROM landsraad_decree_term term
LEFT JOIN factions reigning_faction ON term.reigning_faction_id = reigning_faction.id
LEFT JOIN landsraad_decrees active_decree ON term.active_decree_id = active_decree.id
LEFT JOIN factions winning_faction ON term.winning_faction_id = winning_faction.id
LEFT JOIN landsraad_decrees elected_decree ON term.elected_decree_id = elected_decree.id
WHERE term.term_id = current_term_id;
ELSE
RETURN query SELECT term.term_id, term.reigning_faction_name, term.active_decree_name, term.winning_faction_name, term.elected_decree_name, term.start_time, term.end_time
FROM landsraad_initialize_term(number_of_weeks_term_retention, number_of_nominated_decrees, in_end_time, in_test_term, tasks, task_rewards) AS term;
END IF;
END $function$
-- landsraad_initialize_term(number_of_weeks_term_retention integer, number_of_nominated_decrees integer, in_end_time timestamp without time zone, in_test_term boolean, tasks dune.landsraadtask[], task_rewards dune.landsraadtaskreward[]) -> TABLE(term_id bigint, reigning_faction_name text, active_decree_name text, winning_faction_name text, elected_decree_name text, start_time timestamp without time zone, end_time timestamp without time zone)
-- oid: 58412 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_initialize_term(number_of_weeks_term_retention integer, number_of_nominated_decrees integer, in_end_time timestamp without time zone, in_test_term boolean, tasks dune.landsraadtask[], task_rewards dune.landsraadtaskreward[])
RETURNS TABLE(term_id bigint, reigning_faction_name text, active_decree_name text, winning_faction_name text, elected_decree_name text, start_time timestamp without time zone, end_time timestamp without time zone)
LANGUAGE plpgsql
AS $function$
DECLARE
reigning_faction_id SMALLINT = NULL;
active_decree_id BIGINT = NULL;
last_active_decree_id BIGINT = NULL;
current_term_id BIGINT = NULL;
BEGIN
LOCK TABLE landsraad_decrees, landsraad_decree_term, landsraad_decree_rotation, landsraad_decree_votes IN EXCLUSIVE MODE;
-- read winning faction, elected and active decree from previous term
SELECT term.winning_faction_id, term.elected_decree_id, term.active_decree_id INTO reigning_faction_id, active_decree_id, last_active_decree_id FROM landsraad_decree_term term ORDER BY term.term_id DESC LIMIT 1;
-- insert new term
INSERT INTO landsraad_decree_term (reigning_faction_id, active_decree_id, start_time, end_time, test_term) VALUES(reigning_faction_id, active_decree_id, now(), in_end_time AT TIME ZONE 'UTC', in_test_term) RETURNING landsraad_decree_term.term_id INTO current_term_id;
-- cleanup old terms, except for previous one
DELETE FROM landsraad_decree_term term WHERE term.end_time < (now() - MAKE_INTERVAL(weeks => number_of_weeks_term_retention)) AND term.term_id < current_term_id - 1;
-- insert tasks for new term
CALL landsraad_insert_tasks(current_term_id, tasks, task_rewards);
-- insert decrees for voting
CALL landsraad_nominate_decrees_for_voting(last_active_decree_id, number_of_nominated_decrees);
-- clean expired landsraad contracts
PERFORM journey_story_node_cooldown_delete_expired(now() at time zone 'utc');
RETURN query SELECT term.term_id, reigning_faction.name, active_decree.decree_name, winning_faction.name, elected_decree.decree_name, (term.start_time AT TIME ZONE 'UTC')::TIMESTAMP, (term.end_time AT TIME ZONE 'UTC')::TIMESTAMP
FROM landsraad_decree_term term
LEFT JOIN factions reigning_faction ON term.reigning_faction_id = reigning_faction.id
LEFT JOIN landsraad_decrees active_decree ON term.active_decree_id = active_decree.id
LEFT JOIN factions winning_faction ON term.winning_faction_id = winning_faction.id
LEFT JOIN landsraad_decrees elected_decree ON term.elected_decree_id = elected_decree.id
WHERE term.term_id = current_term_id;
END $function$
-- landsraad_insert_task_progress(in_term_id bigint, in_player_id bigint, in_guild_id bigint, in_house_name text, in_faction_progress integer, in_guild_progress real, in_player_progress real, in_timestamp timestamp without time zone) -> void
-- oid: 58413 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_insert_task_progress(in_term_id bigint, in_player_id bigint, in_guild_id bigint, in_house_name text, in_faction_progress integer, in_guild_progress real, in_player_progress real, in_timestamp timestamp without time zone)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
guild_id BIGINT = NULL;
faction_id BIGINT = NULL;
faction_name TEXT = NULL;
progress_id BIGINT = NULL;
BEGIN
IF in_player_id IS NULL THEN
-- use guild_id
SELECT guilds.guild_id, factions.id, factions.name FROM guild_members
INNER JOIN guilds ON guild_members.guild_id = guilds.guild_id
LEFT JOIN factions ON guilds.guild_faction = factions.id
WHERE guild_members.guild_id = in_guild_id INTO guild_id, faction_id, faction_name;
ELSE
-- use player_id
SELECT guilds.guild_id, factions.id, factions.name FROM guild_members
INNER JOIN guilds ON guild_members.guild_id = guilds.guild_id
LEFT JOIN factions ON guilds.guild_faction = factions.id
WHERE guild_members.player_id = in_player_id INTO guild_id, faction_id, faction_name;
END IF;
IF guild_id IS NULL THEN
RAISE EXCEPTION 'Cannot insert landsraad task progress, player % not in guild', in_player_id;
END IF;
IF faction_id IS NULL OR faction_name = 'None' THEN
RAISE EXCEPTION 'Cannot insert landsraad task progress, guild % not aligned to faction', guild_id;
END IF;
INSERT INTO landsraad_task_progress (faction_id, task_id, faction_progress, guild_progress, player_progress, timestamp)
SELECT faction_id, tasks.id, in_faction_progress, in_guild_progress, in_player_progress, in_timestamp AT TIME ZONE 'UTC'
FROM landsraad_tasks AS tasks
WHERE tasks.term_id = in_term_id AND tasks.house_name = in_house_name
RETURNING id INTO progress_id;
IF in_player_id IS NOT NULL THEN
INSERT INTO landsraad_task_progress_player (progress_id, player_id) VALUES (progress_id, in_player_id);
END IF;
INSERT INTO landsraad_task_progress_guild (progress_id, guild_id) VALUES (progress_id, guild_id);
END $function$
-- landsraad_insert_task_progress_batched(in_term_id bigint, in_task_progress dune.landsraadtaskprogress[]) -> void
-- oid: 58414 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_insert_task_progress_batched(in_term_id bigint, in_task_progress dune.landsraadtaskprogress[])
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
task_progress record = NULL;
BEGIN
FOREACH task_progress IN ARRAY in_task_progress
LOOP
PERFORM landsraad_insert_task_progress(in_term_id, task_progress.player_id, task_progress.guild_id ,task_progress.house_name, task_progress.faction_progress, task_progress.guild_progress, task_progress.player_progress, task_progress.timestamp);
END LOOP;
END $function$
-- landsraad_insert_task_progress_faction(in_term_id bigint, in_faction_name text, in_house_name text, in_faction_progress integer, in_guild_progress real, in_player_progress real) -> void
-- oid: 58415 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_insert_task_progress_faction(in_term_id bigint, in_faction_name text, in_house_name text, in_faction_progress integer, in_guild_progress real, in_player_progress real)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
player_id BIGINT = NULL;
guild_id BIGINT = NULL;
BEGIN
SELECT guild_members.player_id, guilds.guild_id FROM guilds
LEFT JOIN factions ON guilds.guild_faction = factions.id
RIGHT JOIN guild_members ON guild_members.guild_id = guilds.guild_id
WHERE factions.name = in_faction_name
ORDER BY random() LIMIT 1 INTO player_id, guild_id;
IF player_id IS NULL THEN
RAISE EXCEPTION 'Cannot insert landsraad task progress for faction %, no guild member found', in_faction_name;
END IF;
PERFORM landsraad_insert_task_progress(in_term_id, player_id, guild_id, in_house_name, in_faction_progress, in_guild_progress, in_player_progress, now() AT TIME ZONE 'UTC');
END $function$
-- landsraad_insert_task_progress_random(in_term_id bigint, in_faction_names text[], in_num_rows integer) -> void
-- oid: 58416 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_insert_task_progress_random(in_term_id bigint, in_faction_names text[], in_num_rows integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
player_id BIGINT = NULL;
guild_id BIGINT = NULL;
house_name TEXT = NULL;
random_amount INTEGER = NULL;
BEGIN
FOR r IN 1..in_num_rows
LOOP
SELECT guild_members.player_id, guilds.guild_id FROM guilds
LEFT JOIN factions ON guilds.guild_faction = factions.id
RIGHT JOIN guild_members ON guild_members.guild_id = guilds.guild_id
WHERE factions.name = ANY(in_faction_names)
ORDER BY random() LIMIT 1 INTO player_id, guild_id;
SELECT tasks.house_name FROM landsraad_tasks tasks
WHERE tasks.term_id = in_term_id
ORDER BY random() LIMIT 1 INTO house_name;
SELECT (floor(random() * 5) + 1)::INTEGER * 10 INTO random_amount;
IF player_id IS NOT NULL AND house_name IS NOT NULL THEN
PERFORM landsraad_insert_task_progress(in_term_id, player_id, guild_id, house_name, random_amount * 10, (random_amount * 0.1)::REAL, (random_amount * 10)::REAL, now() AT TIME ZONE 'UTC');
END IF;
END LOOP;
END $function$
-- landsraad_insert_tasks(IN in_term_id bigint, IN in_tasks dune.landsraadtask[], IN in_task_rewards dune.landsraadtaskreward[]) -> void
-- oid: 58417 kind: PROCEDURE category: landsraad
CREATE OR REPLACE PROCEDURE dune.landsraad_insert_tasks(IN in_term_id bigint, IN in_tasks dune.landsraadtask[], IN in_task_rewards dune.landsraadtaskreward[])
LANGUAGE plpgsql
AS $procedure$
BEGIN
INSERT INTO landsraad_tasks (term_id, board_index, house_name, goal_amount)
SELECT in_term_id, tasks.board_index, tasks.house_name, tasks.goal_amount FROM UNNEST(in_tasks) AS tasks;
INSERT INTO landsraad_task_rewards (task_id, threshold, template_id, amount)
SELECT tasks.id, task_rewards.threshold, task_rewards.template_id, task_rewards.amount FROM UNNEST(in_task_rewards) AS task_rewards
LEFT JOIN landsraad_tasks AS tasks ON task_rewards.house_name = tasks.house_name WHERE tasks.term_id = in_term_id;
END $procedure$
-- landsraad_load_current_rotation(in_term_id bigint) -> TABLE(decree_name text, received_votes integer, open_votes integer)
-- oid: 58418 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_load_current_rotation(in_term_id bigint)
RETURNS TABLE(decree_name text, received_votes integer, open_votes integer)
LANGUAGE plpgsql
AS $function$
DECLARE
open_votes INTEGER = 0;
BEGIN
WITH open_guild_votes AS (
SELECT guild_contribution.guild_id, FLOOR(SUM(guild_contribution.amount))::INTEGER AS voting_influence
FROM landsraad_tasks AS tasks
INNER JOIN landsraad_task_guild_contributions AS guild_contribution
ON guild_contribution.task_id = tasks.id AND guild_contribution.faction_id = tasks.winning_faction_id
LEFT JOIN landsraad_decree_votes
ON guild_contribution.guild_id = landsraad_decree_votes.guild_id
WHERE tasks.term_id = in_term_id AND tasks.winning_faction_id = (SELECT winning_faction_id FROM landsraad_decree_term WHERE term_id = in_term_id)
AND landsraad_decree_votes.guild_id IS NULL
GROUP BY (guild_contribution.guild_id, guild_contribution.faction_id)
)
SELECT SUM(voting_influence)::INTEGER FROM open_guild_votes INTO open_votes;
RETURN query (
SELECT decrees.decree_name, SUM(decree_votes.influence)::INTEGER AS received_votes, open_votes
FROM landsraad_decree_rotation AS rotation
INNER JOIN landsraad_decrees AS decrees ON rotation.decree_id = decrees.id
LEFT JOIN landsraad_decree_votes AS decree_votes ON decree_votes.decree_id = rotation.decree_id
GROUP BY rotation.decree_id, decrees.decree_name
);
END $function$
-- landsraad_load_current_term() -> TABLE(term_id bigint, reigning_faction_name text, active_decree_name text, winning_faction_name text, elected_decree_name text, start_time timestamp without time zone, end_time timestamp without time zone, tasks dune.landsraadtask[], term_task_rewards dune.landsraadtaskreward[], winner_history text[], testterm boolean)
-- oid: 58419 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_load_current_term()
RETURNS TABLE(term_id bigint, reigning_faction_name text, active_decree_name text, winning_faction_name text, elected_decree_name text, start_time timestamp without time zone, end_time timestamp without time zone, tasks dune.landsraadtask[], term_task_rewards dune.landsraadtaskreward[], winner_history text[], testterm boolean)
LANGUAGE plpgsql
AS $function$
DECLARE
current_term_id BIGINT = NULL;
reigning_faction_name TEXT = NULL;
active_decree_name TEXT = NULL;
winning_faction_name TEXT = NULL;
elected_decree_name TEXT = NULL;
start_time TIMESTAMP = NULL;
end_time TIMESTAMP = NULL;
test_term BOOL = NULL;
term_tasks LandsraadTask[];
term_task_rewards LandsraadTaskReward[];
term_winner_history TEXT[];
BEGIN
SELECT term.term_id, reigning_faction.name, active_decree.decree_name, winning_faction.name, elected_decree.decree_name, (term.start_time AT TIME ZONE 'UTC')::TIMESTAMP, (term.end_time AT TIME ZONE 'UTC')::TIMESTAMP, term.test_term
INTO current_term_id, reigning_faction_name, active_decree_name, winning_faction_name, elected_decree_name, start_time, end_time, test_term
FROM landsraad_decree_term AS term
LEFT JOIN factions AS reigning_faction ON term.reigning_faction_id = reigning_faction.id
LEFT JOIN landsraad_decrees AS active_decree ON term.active_decree_id = active_decree.id
LEFT JOIN factions AS winning_faction ON term.winning_faction_id = winning_faction.id
LEFT JOIN landsraad_decrees AS elected_decree ON term.elected_decree_id = elected_decree.id
ORDER BY term.start_time DESC LIMIT 1;
SELECT ARRAY_AGG((tasks.board_index, tasks.house_name, tasks.completed, COALESCE(factions_winner.name, ''), tasks.sysselraad, tasks.goal_amount)::LandsraadTask)
INTO term_tasks
FROM landsraad_tasks AS tasks
LEFT JOIN factions AS factions_winner ON tasks.winning_faction_id = factions_winner.id
WHERE tasks.term_id = current_term_id;
WITH task_rewards AS (
SELECT tasks.house_name AS house_name, rewards.threshold AS threshold, rewards.template_id AS template_id, rewards.amount AS amount
FROM landsraad_task_rewards AS rewards
LEFT JOIN landsraad_tasks AS tasks ON rewards.task_id = tasks.id
WHERE tasks.term_id = current_term_id ORDER BY rewards.task_id ASC, rewards.threshold ASC
)
SELECT ARRAY_AGG((house_name, threshold, template_id, amount)::LandsraadTaskReward) FROM task_rewards INTO term_task_rewards;
SELECT ARRAY_AGG(winning_factions.name::TEXT)
INTO term_winner_history
FROM
(SELECT factions.name
FROM landsraad_decree_term
LEFT JOIN factions ON landsraad_decree_term.reigning_faction_id = factions.id
ORDER BY landsraad_decree_term.term_id DESC) AS winning_factions;
IF current_term_id IS NOT NULL THEN
RETURN query SELECT current_term_id, reigning_faction_name, active_decree_name, winning_faction_name, elected_decree_name, start_time, end_time, term_tasks, term_task_rewards, term_winner_history, test_term;
END IF;
END $function$
-- landsraad_load_guild_contribution(in_term_id bigint, in_guild_id bigint, in_faction_id bigint) -> TABLE(voting_influence real)
-- oid: 58420 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_load_guild_contribution(in_term_id bigint, in_guild_id bigint, in_faction_id bigint)
RETURNS TABLE(voting_influence real)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN query (
SELECT SUM(guild_contribution.amount)::REAL
FROM landsraad_tasks AS tasks
INNER JOIN landsraad_task_guild_contributions AS guild_contribution
ON guild_contribution.task_id = tasks.id
WHERE tasks.term_id = in_term_id AND guild_contribution.guild_id = in_guild_id AND guild_contribution.faction_id = in_faction_id
GROUP BY (guild_contribution.guild_id, guild_contribution.faction_id)
);
END $function$
-- landsraad_load_guild_contributions(in_term_id bigint, in_num_guilds integer, in_faction_names text[]) -> TABLE(faction_name text, guild_name text, voting_influence real)
-- oid: 58421 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_load_guild_contributions(in_term_id bigint, in_num_guilds integer, in_faction_names text[])
RETURNS TABLE(faction_name text, guild_name text, voting_influence real)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN query (
SELECT factions.name, top_guilds.guild_name, top_guilds.influence FROM factions
CROSS JOIN LATERAL (
SELECT guilds.guild_name as guild_name, SUM(guild_contribution.amount)::REAL AS influence
FROM landsraad_tasks AS tasks
INNER JOIN landsraad_task_guild_contributions AS guild_contribution
ON guild_contribution.task_id = tasks.id AND guild_contribution.faction_id = factions.id
JOIN guilds
ON guild_contribution.guild_id = guilds.guild_id
WHERE tasks.term_id = in_term_id
GROUP BY (guilds.guild_id, guilds.guild_name)
ORDER BY influence DESC LIMIT in_num_guilds
) AS top_guilds
WHERE factions.name = ANY(in_faction_names)
);
END $function$
-- landsraad_load_guild_vote(in_term_id bigint, in_player_id bigint) -> TABLE(decree_name text, voting_influence real)
-- oid: 58422 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_load_guild_vote(in_term_id bigint, in_player_id bigint)
RETURNS TABLE(decree_name text, voting_influence real)
LANGUAGE plpgsql
AS $function$
DECLARE
term_winning_faction_id SMALLINT = NULL;
player_guild_id BIGINT = NULL;
guild_faction_id SMALLINT = NULL;
BEGIN
SELECT guilds.guild_id, guilds.guild_faction FROM guild_members JOIN guilds ON guild_members.guild_id = guilds.guild_id WHERE guild_members.player_id = in_player_id INTO player_guild_id, guild_faction_id;
RETURN query (
SELECT
CASE WHEN player_guild_id IS NOT NULL AND guild_faction_id IS NOT NULL THEN
(SELECT COALESCE(decrees.decree_name, '') FROM landsraad_decree_votes AS votes LEFT JOIN landsraad_decrees AS decrees ON votes.decree_id = decrees.id WHERE votes.guild_id = player_guild_id)
ELSE
''
END,
CASE WHEN player_guild_id IS NOT NULL AND guild_faction_id IS NOT NULL THEN
(SELECT landsraad_load_guild_contribution(in_term_id, player_guild_id, guild_faction_id))
ELSE
0
END
);
END $function$
-- landsraad_load_house_rewards(in_player_id bigint) -> TABLE(house_name text, template_id text, amount integer, last_updated timestamp without time zone)
-- oid: 58423 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_load_house_rewards(in_player_id bigint)
RETURNS TABLE(house_name text, template_id text, amount integer, last_updated timestamp without time zone)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN query (SELECT rewards.house_name, rewards.template_id, rewards.amount, (rewards.last_updated AT TIME ZONE 'UTC')::TIMESTAMP FROM landsraad_house_rewards AS rewards WHERE player_id = in_player_id AND rewards.amount > 0);
END $function$
-- landsraad_load_player_contributions(in_term_id bigint, in_player_ids bigint[]) -> TABLE(player_id bigint, board_index smallint, amount integer)
-- oid: 58424 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_load_player_contributions(in_term_id bigint, in_player_ids bigint[])
RETURNS TABLE(player_id bigint, board_index smallint, amount integer)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN query (
SELECT contributions.player_id, tasks.board_index, FLOOR(SUM(contributions.amount))::INTEGER FROM landsraad_task_player_contributions AS contributions
INNER JOIN landsraad_tasks AS tasks ON contributions.task_id = tasks.id
WHERE tasks.term_id = in_term_id AND contributions.player_id = ANY(in_player_ids)
GROUP BY contributions.player_id, tasks.board_index
);
END $function$
-- landsraad_load_task_faction_progress(in_term_id bigint) -> TABLE(task_board_index integer, faction_name text, progress integer)
-- oid: 58425 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_load_task_faction_progress(in_term_id bigint)
RETURNS TABLE(task_board_index integer, faction_name text, progress integer)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN query SELECT CAST(faction_progress.board_index AS INTEGER), faction_progress.name, CAST(faction_progress.progress AS INTEGER) FROM
(SELECT tasks.id, tasks.board_index, factions.name, SUM(faction_contribution.amount) AS progress
FROM landsraad_tasks tasks
INNER JOIN landsraad_task_faction_contributions faction_contribution
ON faction_contribution.task_id = tasks.id
LEFT JOIN factions factions
ON factions.id = faction_contribution.faction_id
WHERE tasks.term_id = in_term_id
GROUP BY (tasks.id, tasks.board_index, factions.name)) AS faction_progress;
END $function$
-- landsraad_load_task_faction_reveal_state(in_term_id bigint) -> TABLE(task_board_index integer, faction_name text, reveal_state boolean, time_stamp timestamp without time zone)
-- oid: 58426 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_load_task_faction_reveal_state(in_term_id bigint)
RETURNS TABLE(task_board_index integer, faction_name text, reveal_state boolean, time_stamp timestamp without time zone)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN query SELECT CAST(tasks.board_index AS INTEGER), factions.name, reveal_state.revealed, (reveal_state.timestamp AT TIME ZONE 'UTC')::TIMESTAMP
FROM landsraad_tasks tasks
INNER JOIN landsraad_task_reveal_state reveal_state
ON reveal_state.task_id = tasks.id
INNER JOIN factions factions
ON factions.id = reveal_state.faction_id
WHERE tasks.term_id = in_term_id;
END $function$
-- landsraad_load_term_progress(in_term_id bigint, in_num_guilds integer, in_faction_names text[], in_player_ids bigint[]) -> TABLE(faction_progress dune.landsraadtaskfactionprogress[], faction_reveal_state dune.landsraadtaskfactionrevealstate[], guild_contributions dune.landsraadguildcontribution[], player_contributions dune.landsraadplayercontribution[])
-- oid: 58427 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_load_term_progress(in_term_id bigint, in_num_guilds integer, in_faction_names text[], in_player_ids bigint[])
RETURNS TABLE(faction_progress dune.landsraadtaskfactionprogress[], faction_reveal_state dune.landsraadtaskfactionrevealstate[], guild_contributions dune.landsraadguildcontribution[], player_contributions dune.landsraadplayercontribution[])
LANGUAGE plpgsql
AS $function$
DECLARE
term_faction_progress LandsraadTaskFactionProgress[];
term_faction_reveal_state LandsraadTaskFactionRevealState[];
term_guild_contributions LandsraadGuildContribution[];
term_player_contributions LandsraadPlayerContribution[];
BEGIN
SELECT ARRAY_AGG((task_board_index, faction_name, progress)::LandsraadTaskFactionProgress) FROM landsraad_load_task_faction_progress(in_term_id) INTO term_faction_progress;
SELECT ARRAY_AGG((task_board_index, faction_name, reveal_state, time_stamp)::LandsraadTaskFactionRevealState) FROM landsraad_load_task_faction_reveal_state(in_term_id) INTO term_faction_reveal_state;
SELECT ARRAY_AGG((faction_name, guild_name, voting_influence)::LandsraadGuildContribution) FROM landsraad_load_guild_contributions(in_term_id, in_num_guilds, in_faction_names) INTO term_guild_contributions;
SELECT ARRAY_AGG((player_id, board_index, amount)::LandsraadPlayerContribution) FROM landsraad_load_player_contributions(in_term_id, in_player_ids) INTO term_player_contributions;
RETURN query SELECT term_faction_progress, term_faction_reveal_state, term_guild_contributions, term_player_contributions;
END $function$
-- landsraad_nominate_decrees_for_voting(IN last_active_decree_id bigint, IN num_decrees integer) -> void
-- oid: 58428 kind: PROCEDURE category: landsraad
CREATE OR REPLACE PROCEDURE dune.landsraad_nominate_decrees_for_voting(IN last_active_decree_id bigint, IN num_decrees integer)
LANGUAGE plpgsql
AS $procedure$
BEGIN
LOCK TABLE landsraad_decrees, landsraad_decree_rotation, landsraad_decree_votes IN EXCLUSIVE MODE;
TRUNCATE TABLE landsraad_decree_votes;
TRUNCATE TABLE landsraad_decree_rotation;
INSERT INTO landsraad_decree_rotation
SELECT id FROM landsraad_decrees
WHERE (
CASE WHEN last_active_decree_id IS NULL THEN
True
ELSE
last_active_decree_id != id
END
) AND disabled = FALSE
ORDER BY RANDOM() * weight DESC
LIMIT num_decrees;
END $procedure$
-- landsraad_notify_house_rewards_changed() -> trigger
-- oid: 58429 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_notify_house_rewards_changed()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
PERFORM pg_notify('landsraad_notify_channel', format('house_rewards_changed#{"PlayerId" : %s}', NEW.player_id));
RETURN NULL;
END $function$
-- landsraad_perform_daily_task_reveal(in_term_id bigint, in_faction_names text[], in_house_names_to_reveal text[], in_reveal_day integer) -> TABLE(faction_name text, house_name text, board_index integer)
-- oid: 58430 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_perform_daily_task_reveal(in_term_id bigint, in_faction_names text[], in_house_names_to_reveal text[], in_reveal_day integer)
RETURNS TABLE(faction_name text, house_name text, board_index integer)
LANGUAGE plpgsql
AS $function$
DECLARE
last_processed_reveal_day INTEGER = NULL;
faction_ids BIGINT[];
newly_revealed_task_ids BIGINT[];
newly_revealed_house_names TEXT[];
newly_revealed_task_board_indices INTEGER[];
faction_of_newly_revealed_task BIGINT[];
BEGIN
LOCK TABLE landsraad_decree_term IN EXCLUSIVE MODE;
SELECT landsraad_decree_term.last_processed_reveal_day FROM landsraad_decree_term WHERE term_id = in_term_id INTO last_processed_reveal_day;
IF last_processed_reveal_day < in_reveal_day THEN
SELECT ARRAY_AGG(factions.id) FROM factions WHERE factions.name = ANY(in_faction_names) INTO faction_ids;
WITH revealed_task(id, faction_id) AS (
SELECT task.id, faction.id FROM landsraad_tasks AS task
CROSS JOIN UNNEST(faction_ids) AS faction(id)
WHERE task.house_name = ANY (in_house_names_to_reveal) AND task.term_id = in_term_id)
--filter out tasks already revealed from data to not stomp reveal date or send duplicate reveal event in telemetry
SELECT ARRAY_AGG(task.id), ARRAY_AGG(task.house_name), ARRAY_AGG(task.board_index), ARRAY_AGG(revealed_task.faction_id) FROM revealed_task
INNER JOIN landsraad_tasks AS task ON task.id = revealed_task.id
LEFT JOIN landsraad_task_reveal_state AS reveal_state ON task.id = reveal_state.task_id AND revealed_task.faction_id = reveal_state.faction_id
WHERE reveal_state.revealed IS NULL OR reveal_state.revealed IS FALSE
INTO newly_revealed_task_ids, newly_revealed_house_names, newly_revealed_task_board_indices, faction_of_newly_revealed_task;
INSERT INTO landsraad_task_reveal_state (task_id, faction_id, revealed, timestamp) SELECT UNNEST(newly_revealed_task_ids), UNNEST(faction_of_newly_revealed_task), TRUE, now()
ON CONFLICT(task_id, faction_id) DO UPDATE SET revealed = TRUE, timestamp = now();
UPDATE landsraad_decree_term SET last_processed_reveal_day = in_reveal_day WHERE term_id = in_term_id;
IF cardinality(newly_revealed_task_ids) > 0 THEN
PERFORM pg_notify('landsraad_notify_channel', 'progress_updated#{"changed": true}');
END IF;
END IF;
RETURN query
WITH newly_revealed_tasks (house_name, board_index, faction_id) AS (
SELECT UNNEST(newly_revealed_house_names), UNNEST(newly_revealed_task_board_indices), UNNEST(faction_of_newly_revealed_task))
SELECT factions.name, newly_revealed_tasks.house_name, newly_revealed_tasks.board_index FROM newly_revealed_tasks JOIN factions ON newly_revealed_tasks.faction_id = factions.id;
END $function$
-- landsraad_process_house_rewards() -> trigger
-- oid: 58431 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_process_house_rewards()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
WITH
task_player_contribution_threshold_passed (player_id, house_name, template_id, amount) AS (
SELECT NEW.player_id, tasks.house_name, task_rewards.template_id, task_rewards.amount
FROM landsraad_task_rewards as task_rewards
INNER JOIN landsraad_tasks AS tasks
ON task_rewards.task_id = tasks.id
LEFT JOIN landsraad_task_player_contributions AS player_contributions
ON player_contributions.task_id = tasks.id
WHERE task_rewards.task_id = NEW.task_id
AND tasks.id = NEW.task_id
AND player_contributions.player_id = NEW.player_id
AND COALESCE(OLD.amount, 0) < task_rewards.threshold
AND NEW.amount >= task_rewards.threshold)
INSERT INTO landsraad_house_rewards (player_id, house_name, template_id, amount, last_updated)
SELECT player_id, house_name, template_id, SUM(amount), CURRENT_TIMESTAMP FROM task_player_contribution_threshold_passed GROUP BY player_id, house_name, template_id
ON CONFLICT (player_id, house_name, template_id) DO UPDATE SET amount = landsraad_house_rewards.amount + excluded.amount, last_updated = CURRENT_TIMESTAMP;
RETURN NULL;
END $function$
-- landsraad_process_task_progress(max_rows integer) -> void
-- oid: 58432 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_process_task_progress(max_rows integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
task_progress RECORD = NULL;
current_term_id BIGINT = NULL;
last_progress_id BIGINT = NULL;
old_processed_id BIGINT = NULL;
new_processed_id BIGINT = NULL;
new_amount INTEGER = 0;
player BIGINT = NULL;
guild BIGINT = NULL;
notify_guild_ids BIGINT[];
guild_ids_json JSON = NULL;
BEGIN
LOCK TABLE landsraad_task_progress_processed IN EXCLUSIVE MODE;
SELECT term_id FROM landsraad_decree_term ORDER BY start_time DESC LIMIT 1 INTO current_term_id;
SELECT id FROM landsraad_task_progress ORDER BY id DESC LIMIT 1 INTO last_progress_id;
SELECT last_processed_id FROM landsraad_task_progress_processed INTO old_processed_id;
-- read batch of rows sorted by id, process ordered by timestamp
FOR task_progress IN
WITH progress_batch AS (
SELECT landsraad_task_progress.id, landsraad_task_progress.faction_id, landsraad_task_progress.task_id,
landsraad_task_progress.faction_progress, landsraad_task_progress.guild_progress, landsraad_task_progress.player_progress, landsraad_task_progress.timestamp,
task_progress_players.players, task_progress_guilds.guilds
FROM landsraad_task_progress,
LATERAL (SELECT ARRAY_AGG(player_id) AS players FROM landsraad_task_progress_player WHERE landsraad_task_progress_player.progress_id = landsraad_task_progress.id) AS task_progress_players,
LATERAL (SELECT ARRAY_AGG(guild_id) AS guilds FROM landsraad_task_progress_guild WHERE landsraad_task_progress_guild.progress_id = landsraad_task_progress.id) AS task_progress_guilds
WHERE CASE WHEN old_processed_id IS NOT NULL THEN id > old_processed_id ELSE TRUE END
ORDER BY id LIMIT MAX_ROWS
)
SELECT id, faction_id, task_id, players, guilds, faction_progress, guild_progress, player_progress FROM progress_batch ORDER BY timestamp
LOOP
IF NOT (SELECT landsraad_has_term_of_task_ended(task_progress.task_id)) THEN
-- player progress is allowed to happen even if the task was already completed
IF task_progress.players IS NOT NULL THEN
FOREACH player IN ARRAY task_progress.players
LOOP
INSERT INTO landsraad_task_player_contributions AS player_contribution (player_id, faction_id, task_id, amount)
VALUES (player, task_progress.faction_id, task_progress.task_id, task_progress.player_progress)
ON CONFLICT (player_id, faction_id, task_id)
DO UPDATE SET amount = player_contribution.amount + task_progress.player_progress;
END LOOP;
END IF;
IF NOT (SELECT landsraad_task_has_been_completed(task_progress.task_id)) THEN
IF task_progress.guilds IS NOT NULL THEN
FOREACH guild IN ARRAY task_progress.guilds
LOOP
-- only insert to guild contribution if no vote has been placed
IF (SELECT NOT EXISTS (SELECT 1 FROM landsraad_decree_votes WHERE landsraad_decree_votes.guild_id = guild)) THEN
INSERT INTO landsraad_task_guild_contributions AS guild_contribution (guild_id, faction_id, task_id, amount)
VALUES (guild, task_progress.faction_id, task_progress.task_id, task_progress.guild_progress)
ON CONFLICT (guild_id, faction_id, task_id)
DO UPDATE SET amount = guild_contribution.amount + task_progress.guild_progress;
notify_guild_ids = notify_guild_ids || guild;
END IF;
END LOOP;
END IF;
INSERT INTO landsraad_task_faction_contributions AS faction_contribution (faction_id, task_id, amount)
VALUES (task_progress.faction_id, task_progress.task_id, task_progress.faction_progress)
ON CONFLICT (faction_id, task_id)
DO UPDATE SET amount = faction_contribution.amount + task_progress.faction_progress;
END IF;
END IF;
new_processed_id = task_progress.id;
END LOOP;
IF new_processed_id IS NOT NULL THEN
IF old_processed_id IS NULL THEN
INSERT INTO landsraad_task_progress_processed (last_processed_id) VALUES (new_processed_id);
ELSE
UPDATE landsraad_task_progress_processed SET last_processed_id = new_processed_id;
END IF;
END IF;
IF last_progress_id > new_processed_id THEN
PERFORM pg_notify('landsraad_notify_channel', format('progress_pressure#{"UnprocessedCount": %s}', last_progress_id - new_processed_id));
END IF;
IF new_processed_id > old_processed_id THEN
PERFORM pg_notify('landsraad_notify_channel', 'progress_updated#{"changed": true}');
ELSE
PERFORM pg_notify('landsraad_notify_channel', 'progress_updated#{"changed": false}');
END IF;
IF cardinality(notify_guild_ids) > 0 THEN
SELECT json_agg(DISTINCT guild_id) FROM (SELECT unnest(notify_guild_ids) guild_id) guilds INTO guild_ids_json;
PERFORM pg_notify('landsraad_notify_channel', format('guild_vote_changed#{"GuildIds": %s}', guild_ids_json));
END IF;
END $function$
-- landsraad_task_has_been_completed(in_task_id bigint) -> boolean
-- oid: 58433 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_task_has_been_completed(in_task_id bigint)
RETURNS boolean
LANGUAGE plpgsql
AS $function$
DECLARE
task_completed BOOLEAN = FALSE;
BEGIN
SELECT task.completed FROM landsraad_tasks AS task WHERE task.id = in_task_id INTO task_completed;
RETURN task_completed;
END $function$
-- landsraad_update_decrees(IN in_decrees dune.landsraaddecree[]) -> void
-- oid: 58434 kind: PROCEDURE category: landsraad
CREATE OR REPLACE PROCEDURE dune.landsraad_update_decrees(IN in_decrees dune.landsraaddecree[])
LANGUAGE plpgsql
AS $procedure$
BEGIN
UPDATE landsraad_decrees SET disabled = TRUE WHERE decree_name NOT IN (
SELECT(UNNEST(in_decrees)).decree_name
);
INSERT INTO landsraad_decrees (decree_name, version, disabled, weight)
SELECT decrees.decree_name, decrees.version, decrees.disabled, decrees.weight FROM UNNEST(in_decrees) AS decrees
ON CONFLICT(decree_name) DO UPDATE SET version = excluded.version, disabled = excluded.disabled, weight = excluded.weight;
END $procedure$
-- landsraad_update_factions(IN in_faction_names text[]) -> void
-- oid: 58435 kind: PROCEDURE category: landsraad
CREATE OR REPLACE PROCEDURE dune.landsraad_update_factions(IN in_faction_names text[])
LANGUAGE plpgsql
AS $procedure$
BEGIN
WITH new_factions AS (
SELECT f FROM UNNEST(in_faction_names) f LEFT JOIN factions ON f = factions.name WHERE id IS NULL
)
INSERT INTO factions (name) SELECT * FROM new_factions;
END $procedure$
-- landsraad_update_task_faction_reveal_state(in_term_id bigint, in_task_board_index integer, faction_name text, reveal_state boolean) -> void
-- oid: 58436 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_update_task_faction_reveal_state(in_term_id bigint, in_task_board_index integer, faction_name text, reveal_state boolean)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
taskid BIGINT = NULL;
factionid BIGINT = NULL;
BEGIN
SELECT id FROM landsraad_tasks tasks WHERE tasks.board_index = in_task_board_index AND tasks.term_id = in_term_id INTO taskid;
IF taskid IS NULL THEN
RAISE EXCEPTION 'Cannot update landsraad task reveal state, no task id for index % term %', in_task_board_index, in_term_id;
END IF;
SELECT id FROM factions WHERE factions.name = faction_name INTO factionid;
IF factionid IS NULL OR faction_name = 'None' THEN
RAISE EXCEPTION 'Cannot update landsraad task reveal state, invalid faction (%)', faction_name;
END IF;
INSERT INTO landsraad_task_reveal_state (task_id, faction_id, revealed, timestamp) VALUES (taskid, factionid, reveal_state, now()) ON CONFLICT(task_id, faction_id) DO UPDATE
SET revealed = reveal_state, timestamp = now();
PERFORM pg_notify('landsraad_notify_channel', 'progress_updated#{"changed": true}');
END $function$
-- landsraad_withdraw_house_reward(in_player_id bigint, in_house_rewards dune.landsraadplayerhousereward[]) -> void
-- oid: 58437 kind: FUNCTION category: landsraad
CREATE OR REPLACE FUNCTION dune.landsraad_withdraw_house_reward(in_player_id bigint, in_house_rewards dune.landsraadplayerhousereward[])
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
current_amount INTEGER = NULL;
house_reward record = NULL;
grouped_house_rewards LandsraadPlayerHouseReward[];
BEGIN
LOCK TABLE landsraad_house_rewards IN EXCLUSIVE MODE;
--group rewards to make sure multiple entries of the same item do not slip by amount verfication below
WITH grouped_rewards AS (SELECT house_name, template_id, SUM(amount) as amount FROM UNNEST(in_house_rewards) GROUP BY house_name, template_id)
SELECT ARRAY_AGG((grouped_rewards.house_name, grouped_rewards.template_id, grouped_rewards.amount)::LandsraadPlayerHouseReward) INTO grouped_house_rewards FROM grouped_rewards;
FOREACH house_reward in ARRAY grouped_house_rewards
LOOP
SELECT lhr.amount INTO current_amount FROM landsraad_house_rewards AS lhr WHERE lhr.player_id = in_player_id AND lhr.house_name = house_reward.house_name AND lhr.template_id = house_reward.template_id;
IF current_amount IS NULL OR current_amount < house_reward.amount THEN
RAISE EXCEPTION 'Cannot withdraw house reward %s for player % and house %s', house_reward.template_id, in_player_id, house_reward.house_name;
RETURN;
END IF;
END LOOP;
-- finish full loop of checks first, all rewards need to be withdrawable before updating
FOREACH house_reward in ARRAY grouped_house_rewards
LOOP
UPDATE landsraad_house_rewards SET amount = amount - house_reward.amount, last_updated = CURRENT_TIMESTAMP WHERE player_id = in_player_id AND house_name = house_reward.house_name AND template_id = house_reward.template_id;
END LOOP;
END $function$
-- load_actors(in_actor_ids bigint[], in_actor_state dune.actorstate) -> TABLE(ord bigint, actor_id bigint, generic_data dune.actorgenericdata, serial bigint)
-- oid: 58438 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.load_actors(in_actor_ids bigint[], in_actor_state dune.actorstate DEFAULT 'Default'::dune.actorstate)
RETURNS TABLE(ord bigint, actor_id bigint, generic_data dune.actorgenericdata, serial bigint)
LANGUAGE plpgsql
AS $function$
begin
return query
with
ids as (
select * from unnest(in_actor_ids) with ordinality as t(id, ord)
),
entities as (
select
fgl_bridge.actor_id,
(fgl_bridge.entity_id, fgl_bridge.slot_name, entity_data.components)::FglEntity as data
from
ids
left join actor_fgl_entities as fgl_bridge on ids.id=fgl_bridge.actor_id
left join fgl_entities as entity_data on fgl_bridge.entity_id = entity_data.entity_id
)
select
ids.ord, actors.id,
(
coalesce(array_agg(entities.data) filter (where entities.data is not null), array[]::FglEntity[]),
actors.properties,
actors.gas_attributes,
case
when exists(select 1 from buildings where actors.id = buildings.id) then load_building(actors.id)
end
,
case
when exists(select 1 from placeables where actors.id = placeables.id) then load_placeable(actors.id)
end
,
case
when exists(select 1 from totems where actors.id = totems.id) then load_totem(actors.id)
end
)::ActorGenericData, actors.serial
from
ids
join actors using (id)
left join entities on actors.id = entities.actor_id
where
case when (in_actor_state = 'Default') then
not exists(select 1 from actor_state where actors.id = actor_state.actor_id)
else
exists(select 1 from actor_state where actors.id = actor_state.actor_id and actor_state.state = in_actor_state)
end
group by ids.ord, actors.id, actors.properties, actors.gas_attributes, actors.serial
order by ids.ord;
END
$function$
-- load_backup_vehicle(in_account_id bigint) -> TABLE(out_id bigint, out_class text, out_customization_id text)
-- oid: 58439 kind: FUNCTION category: vehicle
CREATE OR REPLACE FUNCTION dune.load_backup_vehicle(in_account_id bigint)
RETURNS TABLE(out_id bigint, out_class text, out_customization_id text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT a.id, a.class, bv.customization_id
FROM actors a
JOIN backup_vehicles bv on a.id = bv.vehicle_id
WHERE bv.account_id = in_account_id
LIMIT 1;
END
$function$
-- load_building(in_building_id bigint) -> dune.buildingsavedata
-- oid: 58449 kind: FUNCTION category: building_blueprint
CREATE OR REPLACE FUNCTION dune.load_building(in_building_id bigint)
RETURNS dune.buildingsavedata
LANGUAGE sql
BEGIN ATOMIC
RETURN ( SELECT ROW(array_agg(ROW(building_instances.instance_id, building_instances.building_type, building_instances.transform, building_instances.owner_entity_id, building_instances.building_flags, building_instances.health, building_instances.shelter, building_instances.stabilization_begin_timespan, building_instances.stabilization_end_timespan, building_instances.stabilization_state, building_instances.sand_buildup)::dune.buildinginstance), ARRAY[]::integer[], ARRAY[]::dune.buildinginstanceupdateowner[], ARRAY[]::dune.buildinginstanceupdatestabilization[], ARRAY[]::dune.buildinginstanceupdatehealth[], ARRAY[]::dune.buildinginstanceupdateshelter[], ARRAY[]::dune.buildinginstanceupdatesandbuildup[], ARRAY[]::dune.buildinginstanceupdatebuildingflags[], ARRAY[]::dune.buildinginstanceupdatetransform[])::dune.buildingsavedata AS "row"
FROM dune.building_instances
WHERE (building_instances.building_id = load_building.in_building_id));
END
-- load_communinet_player_data(in_account_id bigint) -> TABLE(is_active boolean, selected_channel_name text, channel_name text, is_tuned boolean)
-- oid: 58450 kind: FUNCTION category: communinet
CREATE OR REPLACE FUNCTION dune.load_communinet_player_data(in_account_id bigint)
RETURNS TABLE(is_active boolean, selected_channel_name text, channel_name text, is_tuned boolean)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT cp.is_active, cp.selected_channel_name, cpc.channel_name, cpc.is_tuned
FROM communinet_player AS cp JOIN communinet_player_channels as cpc
ON cp.account_id = cpc.account_id
WHERE cpc.account_id = in_account_id;
END; $function$
-- load_dialogue_data(in_player_controller_id bigint, OUT met_npcs text[], OUT taken_nodes integer[]) -> record
-- oid: 58451 kind: FUNCTION category: dialogue
CREATE OR REPLACE FUNCTION dune.load_dialogue_data(in_player_controller_id bigint, OUT met_npcs text[], OUT taken_nodes integer[])
RETURNS record
LANGUAGE plpgsql
AS $function$
BEGIN
SELECT ARRAY_AGG(npc_name)
INTO met_npcs
FROM dialogue_met_npcs
WHERE player_id = in_player_controller_id;
SELECT ARRAY_AGG(node_id)
INTO taken_nodes
FROM dialogue_taken_nodes
WHERE player_id = in_player_controller_id;
END
$function$
-- load_dimension_index(in_map text, in_partition_id bigint) -> integer
-- oid: 58452 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.load_dimension_index(in_map text, in_partition_id bigint)
RETURNS integer
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN (SELECT dimension_index from world_partition where map = in_map and partition_id = in_partition_id limit 1);
END; $function$
-- load_events_log_data_from_player(in_actor_id bigint, in_limit_entries_num integer) -> TABLE(game_event_owner bigint, universe_time timestamp without time zone, map_name text, partition_id bigint, event_type integer, x_location double precision, y_location double precision, z_location double precision, custom_data jsonb)
-- oid: 58453 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.load_events_log_data_from_player(in_actor_id bigint, in_limit_entries_num integer)
RETURNS TABLE(game_event_owner bigint, universe_time timestamp without time zone, map_name text, partition_id bigint, event_type integer, x_location double precision, y_location double precision, z_location double precision, custom_data jsonb)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT temp.actor_id, (temp.universe_time AT TIME ZONE 'UTC')::TIMESTAMP, temp.map, temp.partition_id, temp.event_type, temp.x, temp.y, temp.z, temp.custom_data
FROM (SELECT game_events.actor_id, game_events.universe_time, game_events.map, game_events.partition_id, game_events.event_type, game_events.x, game_events.y, game_events.z, game_events.custom_data FROM game_events WHERE game_events.actor_id = in_actor_id AND game_events.player_facing_event = true ORDER BY game_events.universe_time DESC LIMIT in_limit_entries_num) temp
ORDER BY temp.universe_time ASC;
END
$function$
-- load_full_actors(in_ids bigint[]) -> SETOF dune.actordescription
-- oid: 58454 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.load_full_actors(in_ids bigint[])
RETURNS SETOF dune.actordescription
LANGUAGE plpgsql
AS $function$
begin
return query
with
ids as (
select * from unnest(in_ids) with ordinality as t(id, ord)
),
entities as (
select
actor_id,
(fgl_bridge.entity_id, fgl_bridge.slot_name, entity_data.components)::FglEntity as data
from
ids
left join actor_fgl_entities as fgl_bridge on ids.id=fgl_bridge.actor_id
left join fgl_entities as entity_data using (entity_id)
)
select
id, "class", "transform", (
coalesce(array_agg(entities.data) filter (where entities.data is not null), array[]::FglEntity[]),
actors.properties,
actors.gas_attributes,
case
when exists(select 1 from buildings where ids.id = buildings.id) then load_building(id)
else null
end,
case
when exists(select 1 from placeables where ids.id = placeables.id) then load_placeable(id)
else null
end
,
case
when exists(select 1 from totems where id = totems.id) then load_totem(id)
end
)::ActorGenericData, actors.serial
from
ids
join actors using (id)
left join entities on id=entities.actor_id
group by ids.ord, id, class, "transform", actors.properties, actors.gas_attributes, actors.serial
order by ids.ord;
end
$function$
-- load_item(in_item_id bigint) -> TABLE(item_id bigint, stack_size bigint, quality_level bigint, volume_override real, position_index bigint, template_id text, inventory_id bigint, is_new boolean, acquisition_time bigint, stats jsonb, sub_inventory_id bigint)
-- oid: 58455 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.load_item(in_item_id bigint)
RETURNS TABLE(item_id bigint, stack_size bigint, quality_level bigint, volume_override real, position_index bigint, template_id text, inventory_id bigint, is_new boolean, acquisition_time bigint, stats jsonb, sub_inventory_id bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT item.id, item.stack_size, item.quality_level, item.volume_override, item.position_index, item.template_id, item.inventory_id, item.is_new, item.acquisition_time, item.stats, inventory.id
FROM items item
LEFT JOIN inventories inventory ON (inventory.item_id = item.id)
WHERE item.id = in_item_id;
END; $function$
-- load_items(in_inventory_id bigint) -> TABLE(item_id bigint, stack_size bigint, quality_level bigint, volume_override real, position_index bigint, template_id text, inventory_id bigint, is_new boolean, acquisition_time bigint, stats jsonb, sub_inventory_id bigint)
-- oid: 58456 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.load_items(in_inventory_id bigint)
RETURNS TABLE(item_id bigint, stack_size bigint, quality_level bigint, volume_override real, position_index bigint, template_id text, inventory_id bigint, is_new boolean, acquisition_time bigint, stats jsonb, sub_inventory_id bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
WITH RECURSIVE items_cte AS (
SELECT item.id, item.stack_size, item.quality_level, item.volume_override, item.position_index, item.template_id, item.inventory_id, item.is_new, item.acquisition_time, item.stats, inventory.id as sub_inventory_id
FROM items item
LEFT JOIN inventories inventory ON (inventory.item_id = item.id)
WHERE item.inventory_id = in_inventory_id
UNION ALL
SELECT item.id, item.stack_size, item.quality_level, item.volume_override, item.position_index, item.template_id, item.inventory_id, item.is_new, item.acquisition_time, item.stats, inventory.id as sub_inventory_id
FROM items item
LEFT JOIN inventories inventory ON (inventory.item_id = item.id)
JOIN items_cte ON item.inventory_id = items_cte.sub_inventory_id
WHERE items_cte.sub_inventory_id IS NOT NULL
)
SELECT * FROM items_cte order by id asc;
END; $function$
-- load_map_areas_entries(in_account_id bigint, in_map_name text) -> TABLE(account_id bigint, area_id smallint, time_discovered timestamp without time zone, time_first_entered timestamp without time zone, survey_point_marker_id bigint, items_surveyed_target jsonb, items_surveyed_progress jsonb, map_name text)
-- oid: 58457 kind: FUNCTION category: map_areas
CREATE OR REPLACE FUNCTION dune.load_map_areas_entries(in_account_id bigint, in_map_name text)
RETURNS TABLE(account_id bigint, area_id smallint, time_discovered timestamp without time zone, time_first_entered timestamp without time zone, survey_point_marker_id bigint, items_surveyed_target jsonb, items_surveyed_progress jsonb, map_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
map_areas.account_id,
map_areas.area_id,
map_areas.time_discovered AT TIME ZONE 'UTC',
map_areas.time_first_entered AT TIME ZONE 'UTC',
map_areas.survey_point_marker_id,
map_areas.items_surveyed_target,
map_areas.items_surveyed_progress,
map_areas.map_name
from map_areas WHERE map_areas.account_id = in_account_id AND map_areas.map_name = in_map_name;
END
$function$
-- load_markers(in_player_id bigint, in_dimension_id integer, in_map_name text) -> TABLE(out_marker_hash_id integer, out_marker_type text, out_x double precision, out_y double precision, out_z double precision, out_payload_type text, out_area_id smallint, out_area_radius real, out_long_range boolean, out_payload jsonb, out_discovery_level smallint, out_discovery_method smallint, out_player_payload jsonb)
-- oid: 58458 kind: FUNCTION category: markers
CREATE OR REPLACE FUNCTION dune.load_markers(in_player_id bigint, in_dimension_id integer, in_map_name text)
RETURNS TABLE(out_marker_hash_id integer, out_marker_type text, out_x double precision, out_y double precision, out_z double precision, out_payload_type text, out_area_id smallint, out_area_radius real, out_long_range boolean, out_payload jsonb, out_discovery_level smallint, out_discovery_method smallint, out_player_payload jsonb)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
player_markers.marker_hash_id,
(markers.marker).marker_type,
(markers.marker).x,
(markers.marker).y,
(markers.marker).z,
(markers.marker).payload_type,
area_id,
area_radius,
long_range,
markers.payload,
discovery_level,
discovery_method,
player_markers.payload
FROM map_names JOIN markers ON markers.map_name_id = map_names.map_name_id
JOIN player_markers ON markers.marker_hash_id = player_markers.marker_hash_id
AND markers.dimension_index = player_markers.dimension_index
AND markers.map_name_id = player_markers.map_name_id
WHERE player_markers.player_id = in_player_id
AND (player_markers.dimension_index = in_dimension_id OR player_markers.dimension_index = -1)
AND map_names.map_name = in_map_name;
END
$function$
-- load_partition_definition_map() -> TABLE(out_partition_id bigint, out_server_id text, out_partition_definition jsonb, out_dimension_index integer, out_blocked boolean, out_label text, out_map text)
-- oid: 58459 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.load_partition_definition_map()
RETURNS TABLE(out_partition_id bigint, out_server_id text, out_partition_definition jsonb, out_dimension_index integer, out_blocked boolean, out_label text, out_map text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT wp.partition_id, active_server_ids.server_id AS server_id, wp.partition_definition,
wp.dimension_index, wp.blocked, wp.label, wp.map
FROM world_partition as wp
LEFT JOIN active_server_ids
ON active_server_ids.server_id = wp.server_id;
END; $function$
-- load_placeable(in_placeable_id bigint) -> dune.placeablesavedata
-- oid: 58460 kind: FUNCTION category: building_blueprint
CREATE OR REPLACE FUNCTION dune.load_placeable(in_placeable_id bigint)
RETURNS dune.placeablesavedata
LANGUAGE plpgsql
AS $function$
DECLARE
result PlaceableSaveData;
BEGIN
SELECT
owner_entity_id as in_owner_entity_id,
health as in_health,
building_type as in_building_type,
has_hit_ground as in_has_hit_ground,
has_buildable_support as in_has_buildable_support,
is_hologram as in_is_hologram
INTO result
FROM placeables
WHERE id = in_placeable_id;
return result;
END
$function$
-- load_recovered_vehicles(in_account_id bigint, in_restore_time_limit integer) -> TABLE(out_vehicle_id bigint, out_class text, out_name text, out_time_stored timestamp without time zone, out_chassis_durability real, out_customization_id text, out_migrated boolean)
-- oid: 58461 kind: FUNCTION category: vehicle
CREATE OR REPLACE FUNCTION dune.load_recovered_vehicles(in_account_id bigint, in_restore_time_limit integer)
RETURNS TABLE(out_vehicle_id bigint, out_class text, out_name text, out_time_stored timestamp without time zone, out_chassis_durability real, out_customization_id text, out_migrated boolean)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT vehicle_id, class, vehicle_name, time_stored AT TIME ZONE 'UTC', chassis_durability, customization_id, migrated
FROM actors
JOIN recovered_vehicles on id = vehicle_id
WHERE account_id = in_account_id
AND (migrated = TRUE OR time_stored > NOW() - in_restore_time_limit * INTERVAL '1 second')
AND EXISTS (SELECT 1 FROM actor_state WHERE actor_state.actor_id = id AND actor_state.state = 'VehicleRecovery')
ORDER BY time_stored DESC;
END
$function$
-- load_static_encounter_name(in_map_name text, in_package_name text, in_actor_name text) -> TABLE(encounter_name text, waiting_for_reset boolean)
-- oid: 58462 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.load_static_encounter_name(in_map_name text, in_package_name text, in_actor_name text)
RETURNS TABLE(encounter_name text, waiting_for_reset boolean)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT t.encounter_name, t.waiting_for_reset
FROM encounters_static as t
WHERE t.map_name = in_map_name AND t.package_name = in_package_name AND t.actor_name = in_actor_name;
END; $function$
-- load_takeoverable_user_ids() -> SETOF dune.takeovercharacterdatacomposite
-- oid: 58463 kind: FUNCTION category: takeover
CREATE OR REPLACE FUNCTION dune.load_takeoverable_user_ids()
RETURNS SETOF dune.takeovercharacterdatacomposite
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT acc.user, ps.character_name
FROM accounts acc LEFT JOIN player_state ps ON acc.id=ps.account_id
WHERE acc.takeoverable=true;
END; $function$
-- load_totem(in_id bigint) -> dune.totemsavedata
-- oid: 58464 kind: FUNCTION category: building_blueprint
CREATE OR REPLACE FUNCTION dune.load_totem(in_id bigint)
RETURNS dune.totemsavedata
LANGUAGE plpgsql
AS $function$
DECLARE
result TotemSaveData;
BEGIN
SELECT
landclaim_vertical_level,
last_backup_timestamp,
landclaim_original_global_location,
landclaim_original_global_yaw_rotation
INTO result
FROM totems
WHERE id = in_id;
return result;
END
$function$
-- load_travel_return_info(in_player_controller_id bigint) -> TABLE(map text, transform dune.transform)
-- oid: 58465 kind: FUNCTION category: travel
CREATE OR REPLACE FUNCTION dune.load_travel_return_info(in_player_controller_id bigint)
RETURNS TABLE(map text, transform dune.transform)
LANGUAGE plpgsql
AS $function$
begin
return query
select travel_return_info.map, travel_return_info.transform
from travel_return_info
where player_controller_id = in_player_controller_id;
end
$function$
-- load_travel_to_player_info(in_player_controller_id bigint) -> TABLE(map text, transform dune.transform, partition_id bigint, dimension_index integer)
-- oid: 58466 kind: FUNCTION category: travel
CREATE OR REPLACE FUNCTION dune.load_travel_to_player_info(in_player_controller_id bigint)
RETURNS TABLE(map text, transform dune.transform, partition_id bigint, dimension_index integer)
LANGUAGE plpgsql
AS $function$
begin
return query
select actors.map, actors.transform, actors.partition_id, actors.dimension_index
from player_state
join actors on player_state.player_pawn_id = actors.id
where player_state.player_controller_id = in_player_controller_id
and player_state.online_status = 'Online';
end
$function$
-- load_vehicle_modules(in_vehicle_id bigint) -> TABLE(module_id bigint, template_id text, stats jsonb)
-- oid: 58467 kind: FUNCTION category: vehicle
CREATE OR REPLACE FUNCTION dune.load_vehicle_modules(in_vehicle_id bigint)
RETURNS TABLE(module_id bigint, template_id text, stats jsonb)
LANGUAGE plpgsql
AS $function$
begin
return query SELECT vm.id, vm.template_id, vm.stats FROM vehicle_modules vm WHERE vehicle_id = in_vehicle_id ORDER BY id;
end
$function$
-- load_world_partition(in_map_name text, in_server_id text, in_desired_dimension_index bigint, in_desired_partition_id bigint) -> TABLE(partition_id bigint, partition_definition jsonb, dimension_index integer, blocked boolean, label text)
-- oid: 58468 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.load_world_partition(in_map_name text, in_server_id text, in_desired_dimension_index bigint DEFAULT 0, in_desired_partition_id bigint DEFAULT NULL::bigint)
RETURNS TABLE(partition_id bigint, partition_definition jsonb, dimension_index integer, blocked boolean, label text)
LANGUAGE plpgsql
AS $function$
DECLARE
tmp_partition RECORD;
BEGIN
-- First check if the server already have a partition assigned
SELECT INTO tmp_partition wp.partition_id, wp.partition_definition, wp.dimension_index, wp.blocked, wp.label
FROM world_partition wp
WHERE server_id = in_server_id AND wp.map = in_map_name AND wp.dimension_index = in_desired_dimension_index;
IF tmp_partition.partition_id IS NOT NULL THEN
RETURN QUERY SELECT tmp_partition.partition_id, tmp_partition.partition_definition, tmp_partition.dimension_index, tmp_partition.blocked, tmp_partition.label;
RETURN;
END IF;
-- No partition assigned, so try to find an unassigned partition for this server
SELECT INTO tmp_partition wp.partition_id, wp.partition_definition, wp.dimension_index, wp.blocked, wp.label
FROM world_partition wp
WHERE (server_id IS NULL OR server_id NOT IN (SELECT * FROM active_server_ids)) AND wp.map = in_map_name AND wp.dimension_index = in_desired_dimension_index
ORDER BY (wp.partition_id = in_desired_partition_id) DESC, wp.partition_definition->'type', wp.partition_definition->'index', wp.partition_definition->'box'->'min_x', wp.partition_definition->'box'->'min_y'
LIMIT 1
FOR UPDATE SKIP LOCKED;
IF tmp_partition.partition_id IS NULL THEN
RETURN;
ELSE
-- Fake a server
INSERT INTO farm_state(server_id, farm_id, outgoing_s2s_connections, incoming_s2s_connections, connected_players, igw_addr, igw_port, game_addr, game_port, map, revision)
VALUES (in_server_id, '0', 0, 0, 0, '0.0.0.0', 0, '0.0.0.0', 0, '', 0) ON CONFLICT DO NOTHING;
UPDATE world_partition SET server_id = in_server_id WHERE world_partition.partition_id = tmp_partition.partition_id;
NOTIFY world_partition_update;
RETURN QUERY SELECT tmp_partition.partition_id, tmp_partition.partition_definition, tmp_partition.dimension_index, tmp_partition.blocked, tmp_partition.label;
RETURN;
END IF;
END
$function$
-- log_cheating(in_fls_id text, in_cheat_type dune.cheat_type_enum, in_event_time timestamp with time zone) -> void
-- oid: 58469 kind: FUNCTION category: anticheat
CREATE OR REPLACE FUNCTION dune.log_cheating(in_fls_id text, in_cheat_type dune.cheat_type_enum, in_event_time timestamp with time zone DEFAULT now())
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
-- Insert into suspicious_be
INSERT INTO cheater_tracking (
event_time,
fls_id,
cheat_type
) VALUES (
in_event_time,
in_fls_id,
in_cheat_type
);
END;
$function$
-- log_event_solaris(in_function_oid oid, in_message dune.logmessagetype, in_controller_id bigint, in_solaris_balance bigint, in_solaris_delta bigint) -> void
-- oid: 58470 kind: FUNCTION category: currency
CREATE OR REPLACE FUNCTION dune.log_event_solaris(in_function_oid oid, in_message dune.logmessagetype, in_controller_id bigint, in_solaris_balance bigint, in_solaris_delta bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
partition_id BIGINT = 0;
calling_function_name LogFunctionType;
fls_id TEXT;
fc_id BYTEA;
char_name BYTEA;
BEGIN
partition_id := coalesce(current_setting('dune.partition_id', true)::BIGINT, 0);
-- map calling function name to LogFunctionType (each calling function must be added to LogFunctionType)
SELECT proname::text::LogFunctionType
INTO calling_function_name
FROM pg_proc
WHERE oid = in_function_oid;
-- get the fls_id for the user performing the acction
SELECT acc."user"
INTO fls_id
FROM accounts acc
JOIN player_state ps on ps.account_id = acc.id
WHERE ps.player_controller_id = in_controller_id
LIMIT 1;
INSERT INTO event_log (
partition_id,
category,
function_name,
message,
event_time,
meta
) VALUES (
partition_id,
'solaris',
calling_function_name,
in_message,
now(),
json_build_object('fls_id', fls_id, 'event', calling_function_name::text, 'solaris_balance', in_solaris_balance, 'solaris_delta', in_solaris_delta)
);
END;
$function$
-- login_account(in_user_id text, in_funcom_id text, in_platform_id text, in_platform_name text, in_minimum_returning_player_time_seconds integer, in_character_name text, in_return_dimension_index integer, in_home_dimension_index integer) -> SETOF dune.playerdescription
-- oid: 58471 kind: FUNCTION category: character_mod
CREATE OR REPLACE FUNCTION dune.login_account(in_user_id text, in_funcom_id text, in_platform_id text, in_platform_name text, in_minimum_returning_player_time_seconds integer, in_character_name text, in_return_dimension_index integer, in_home_dimension_index integer)
RETURNS SETOF dune.playerdescription
LANGUAGE plpgsql
AS $function$
DECLARE
user_account_id BigInt;
BEGIN
PERFORM update_returning_player_status(in_user_id, in_minimum_returning_player_time_seconds);
return query with
acc as (
INSERT INTO encrypted_accounts("id", "user", "platform_id", "platform_name", "encrypted_funcom_id")
VALUES (default, in_user_id, in_platform_id, in_platform_name, encrypt_user_data(in_funcom_id))
ON CONFLICT ("user") DO UPDATE SET
encrypted_funcom_id = excluded.encrypted_funcom_id,
platform_id = excluded.platform_id,
platform_name = excluded.platform_name
RETURNING id, encrypted_accounts.user
),
actor_ids as (
-- TODO: unite this with accounts. One table to rule them all (until we want multiple chars per account)
SELECT
coalesce(player_controller_id, nextval('actors_id_seq')) as controller,
coalesce(player_state_id, nextval('actors_id_seq')) as state,
coalesce(player_pawn_id, nextval('actors_id_seq')) as pawn
from acc left join player_state on player_state.account_id = acc.id
),
actors_insert as (
INSERT INTO actors("id", "owner_account_id")
select unnest(array[controller, pawn, state]), acc.id from actor_ids, acc
ON CONFLICT DO NOTHING
returning id
),
insert_actor_audit_log as (
insert into actor_audit("id", "class")
select
unnest(array[controller, pawn, state]) as id,
unnest(array['Controller', 'Pawn', 'State']) as clas
from actor_ids
on conflict do nothing
),
demo as (
UPDATE demo_users
SET demo_state = CASE
WHEN demo_playtime_seconds IS NOT NULL THEN 'Demo'::DemoState
ELSE demo_state
END
WHERE fls_id = in_user_id
RETURNING fls_id, demo_playtime_seconds, demo_state
),
player_state_insert as (
INSERT INTO encrypted_player_state(
"account_id", "encrypted_character_name", "online_status",
"player_controller_id", "player_pawn_id", "player_state_id",
"return_dimension_index", "home_dimension_index", "last_login_time"
)
select
id, case
when in_character_name is not null then encrypt_user_data(in_character_name)
when encrypted_player_state.encrypted_character_name is null then encrypt_user_data('<TEMP>')
else encrypted_player_state.encrypted_character_name
end,
'Online', controller, pawn, state, in_return_dimension_index, in_home_dimension_index, now()
from acc left join encrypted_player_state on acc.id = encrypted_player_state.account_id, actor_ids
ON CONFLICT ("account_id")
DO UPDATE SET
online_status = 'Online',
"return_dimension_index" = coalesce(in_return_dimension_index, encrypted_player_state.return_dimension_index),
"home_dimension_index" = coalesce(in_home_dimension_index, encrypted_player_state.home_dimension_index),
"last_login_time" = now()
RETURNING
account_id,
"return_dimension_index",
"home_dimension_index"
),
inserted_count_dummy as (
select count(*) from actors_insert
),
player_actors as (
select array_agg(full_actors.*) as actors
from
-- We need to refer 'returning' from inserts to ensure order of with statements
inserted_count_dummy,
actor_ids,
load_full_actors(array[actor_ids.controller, actor_ids.state, actor_ids.pawn]) as full_actors
),
pawn_info as (
select id, (map, partition_id, dimension_index)::ServerInfo as server_info
from actor_ids join actors on actors.id=actor_ids.pawn
),
respawn_locations as (
SELECT acc.id as account_id, get_respawn_locations(acc.id) as locations
FROM acc
),
this_player_tags as (
select
acc.id as account_id,
array_agg(tag) as tags
from acc join player_tags as tgs on tgs.account_id=acc.id
group by acc.id
),
keystones as (
select player_id, array_agg(keystone_id) as purchased_keystones
from purchased_specialization_keystones
group by player_id
),
tracks as (
select player_id, array_agg(track_info) as progression_tracks
from (
select player_id, (track_type, xp_amount, level)::SpecializationTrackInfo as track_info
from specialization_tracks
)
group by player_id
),
journey_nodes as (
SELECT acc.id as account_id, get_login_journey_nodes(acc.id) as journey_nodes_data
FROM acc
),
journey_nodes_cooldown as (
SELECT acc.id as account_id, get_login_journey_nodes_cooldown(acc.id) as journey_nodes_cooldown_data
FROM acc
)
select
acc.id,
player_actors.actors[1], player_actors.actors[2], player_actors.actors[3],
coalesce(pawn_info.server_info, (null, null, null)::ServerInfo),
(
coalesce(respawn_locations.locations, array[]::RespawnLocation[]),
player_state.pending_respawn_location_id
)::RespawnInfo,
coalesce(player_state.life_state, 'Alive'),
coalesce(this_player_tags.tags, array[]::Text[]),
player_state_insert.return_dimension_index,
player_state.death_location,
player_state_insert.home_dimension_index,
demo.demo_state,
demo.demo_playtime_seconds,
(progression_tracks, purchased_keystones, refund_id)::SpecializationInfo,
(
coalesce(journey_nodes.journey_nodes_data, array[]::JourneyNodeInfo[]),
coalesce(journey_nodes_cooldown.journey_nodes_cooldown_data, array[]::JourneyNodeCooldownInfo[]),
coalesce(journey_tracked_cards.tracked_journey_card, ''),
coalesce(journey_tracked_cards.tracked_landsraad_card, '')
)::JourneyInfo,
(player_state.last_returning_player_event_time AT TIME ZONE 'UTC')::TIMESTAMP,
(player_state.last_returning_player_awarded_time AT TIME ZONE 'UTC')::TIMESTAMP
from
acc left join player_state on player_state.account_id = acc.id
left join respawn_locations on respawn_locations.account_id = acc.id
left join this_player_tags on this_player_tags.account_id = acc.id
left join player_state_insert on player_state_insert.account_id = acc.id
left join demo on demo.fls_id = acc.user
left join journey_nodes on journey_nodes.account_id = acc.id
left join journey_nodes_cooldown on journey_nodes_cooldown.account_id = acc.id
left join journey_tracked_cards on journey_tracked_cards.player_id = player_state.player_controller_id
left join keystones on keystones.player_id = player_state.player_controller_id
left join tracks on tracks.player_id = player_state.player_controller_id
left join specialization_refund_id on specialization_refund_id.player_id = player_state.player_controller_id,
player_actors left join pawn_info on (player_actors.actors[3].id = pawn_info.id)
limit 1;
END
$function$
-- mark_server_dead(in_server_id text) -> void
-- oid: 58473 kind: FUNCTION category: server
CREATE OR REPLACE FUNCTION dune.mark_server_dead(in_server_id text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE farm_state SET alive = false WHERE server_id = in_server_id;
END $function$
-- merge_inventory_items(in_item_id bigint, in_dst_inventory_id bigint, in_dst_index bigint, in_count bigint) -> bigint
-- oid: 58474 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.merge_inventory_items(in_item_id bigint, in_dst_inventory_id bigint, in_dst_index bigint, in_count bigint)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
remaining_stack_size BIGINT;
item_data items%ROWTYPE;
dst_item_data items%ROWTYPE;
BEGIN
SELECT INTO STRICT item_data * FROM items WHERE id = in_item_id;
SELECT INTO dst_item_data * FROM items WHERE inventory_id = in_dst_inventory_id AND position_index = in_dst_index;
IF dst_item_data.id IS NULL THEN
RETURN NULL;
END IF;
remaining_stack_size := item_data.stack_size - in_count;
IF remaining_stack_size < 0 THEN
RETURN NULL;
END IF;
IF item_data.template_id != dst_item_data.template_id THEN
RETURN NULL;
END IF;
-- log item tracking
PERFORM _add_item_trace_log('merge_inventory_items', in_item_id, in_dst_inventory_id, NULL, in_dst_index);
IF remaining_stack_size > 0 THEN
UPDATE items SET stack_size = remaining_stack_size WHERE id = in_item_id;
ELSE
PERFORM delete_item(in_item_id);
END IF;
UPDATE items SET stack_size = dst_item_data.stack_size + in_count WHERE id = dst_item_data.id;
RETURN dst_item_data.id;
END $function$
-- merge_or_move_inventory_item(in_item_id bigint, in_dst_inventory_id bigint, in_dst_index bigint, in_count bigint) -> bigint
-- oid: 58475 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.merge_or_move_inventory_item(in_item_id bigint, in_dst_inventory_id bigint, in_dst_index bigint, in_count bigint)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
remaining_stack_size BIGINT;
new_item_id BIGINT;
item_data items%ROWTYPE;
dst_item_data items%ROWTYPE;
BEGIN
SELECT INTO new_item_id merge_inventory_items(in_item_id, in_dst_inventory_id, in_dst_index, in_count);
IF new_item_id IS NULL THEN
SELECT INTO new_item_id move_inventory_item(in_item_id, in_dst_inventory_id, in_dst_index, in_count);
END IF;
RETURN new_item_id;
END $function$
-- migrate_character(in_account_id bigint, home_dimension integer, max_solaris_allowed bigint) -> void
-- oid: 58476 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune.migrate_character(in_account_id bigint, home_dimension integer, max_solaris_allowed bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
controller_id BIGINT;
pawn_id BIGINT;
BEGIN
SELECT player_controller_id, player_pawn_id into controller_id, pawn_id
FROM player_state ps
WHERE ps.account_id = in_account_id;
UPDATE encrypted_player_state SET home_dimension_index = home_dimension WHERE account_id = in_account_id;
UPDATE demo_users
SET demo_state = 'DbMigratedToRetail'::DemoState, demo_playtime_seconds = NULL
WHERE fls_id = (
SELECT acc.user FROM accounts AS acc WHERE acc.id = in_account_id
);
PERFORM migrate_clamp_max_allow_solaris(pawn_id, max_solaris_allowed);
END
$function$
-- migrate_clamp_max_allow_solaris(in_pawn_id bigint, max_solaris_allowed bigint) -> void
-- oid: 58477 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune.migrate_clamp_max_allow_solaris(in_pawn_id bigint, max_solaris_allowed bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
with all_solaris_items as (
SELECT row_number() over (), i.id, i.stack_size FROM inventories inv
JOIN items i on i.inventory_id = inv.id
WHERE inv.actor_id = in_pawn_id AND inv.inventory_type = 0 AND i.template_id = 'SolarisCoin' -- inventory_type 0 is backpack
), total_solaris as (
SELECT sum(all_solaris_items.stack_size) as total
from all_solaris_items
), items_to_delete AS (
SELECT array_agg(id) AS item_ids
FROM all_solaris_items
WHERE row_number > 1
), deleted_items AS (
SELECT delete_items(item_ids)
FROM items_to_delete
)
UPDATE items
SET stack_size = LEAST(max_solaris_allowed, total_solaris.total)
FROM all_solaris_items, total_solaris, deleted_items
WHERE items.id = all_solaris_items.id AND all_solaris_items.row_number = 1;
END
$function$
-- move_inventory_item(in_item_id bigint, in_dst_inventory_id bigint, in_dst_index bigint, in_count bigint) -> bigint
-- oid: 58478 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.move_inventory_item(in_item_id bigint, in_dst_inventory_id bigint, in_dst_index bigint, in_count bigint)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
remaining_stack_size BIGINT;
new_item_id BIGINT;
item_data items%ROWTYPE;
BEGIN
SELECT INTO STRICT item_data * FROM items WHERE id = in_item_id;
remaining_stack_size := item_data.stack_size - in_count;
IF remaining_stack_size < 0 THEN
RETURN NULL;
END IF;
-- log item tracking
PERFORM _add_item_trace_log('move_inventory_item', in_item_id, in_dst_inventory_id, NULL, in_dst_index);
IF remaining_stack_size > 0 THEN
item_data.stack_size := in_count;
item_data.position_index := in_dst_index;
item_data.inventory_id := in_dst_inventory_id;
SELECT INTO item_data.id nextval('items_id_seq');
INSERT INTO items VALUES(item_data.*) RETURNING id INTO new_item_id;
UPDATE items SET stack_size = remaining_stack_size WHERE id = in_item_id;
ELSE
UPDATE items SET inventory_id = in_dst_inventory_id, position_index = in_dst_index WHERE id = in_item_id;
new_item_id := in_item_id;
END IF;
RETURN new_item_id;
END $function$
-- overmap_delete_player_survival_data(in_player_id bigint) -> void
-- oid: 58479 kind: FUNCTION category: map_areas
CREATE OR REPLACE FUNCTION dune.overmap_delete_player_survival_data(in_player_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM overmap_players WHERE player_id = in_player_id;
END $function$
-- overmap_load_player_survival_data(in_player_id bigint) -> TABLE(out_vehicle_id bigint, out_has_polar_psu boolean, out_overmap_location dune.vector)
-- oid: 58480 kind: FUNCTION category: map_areas
CREATE OR REPLACE FUNCTION dune.overmap_load_player_survival_data(in_player_id bigint)
RETURNS TABLE(out_vehicle_id bigint, out_has_polar_psu boolean, out_overmap_location dune.vector)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT vehicle_id, has_polar_psu, overmap_location FROM overmap_players WHERE player_id = in_player_id;
END $function$
-- overmap_save_player_survival_data(in_player_id bigint, in_vehicle_id bigint, in_has_polar_psu boolean, in_overmap_location dune.vector) -> void
-- oid: 58481 kind: FUNCTION category: map_areas
CREATE OR REPLACE FUNCTION dune.overmap_save_player_survival_data(in_player_id bigint, in_vehicle_id bigint, in_has_polar_psu boolean, in_overmap_location dune.vector)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
real_vehicle_id BigInt;
BEGIN
real_vehicle_id := (select id from actors where id=in_vehicle_id);
INSERT INTO
overmap_players(player_id, vehicle_id, has_polar_psu, overmap_location) VALUES(in_player_id, real_vehicle_id, in_has_polar_psu, in_overmap_location)
ON CONFLICT(player_id)
DO UPDATE SET
vehicle_id = real_vehicle_id, has_polar_psu = in_has_polar_psu, overmap_location = in_overmap_location
WHERE
overmap_players.player_id = in_player_id;
END $function$
-- ownership_handle_actor_delete(in_player_id bigint) -> void
-- oid: 58482 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.ownership_handle_actor_delete(in_player_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
owned_totem_ids BIGINT[];
actors_with_permission BIGINT[];
BEGIN
-- Get owner entity ids (totems) where in_player_id is the owner
SELECT ARRAY_AGG(owner_entity_id) into owned_totem_ids
FROM permission_actor_rank
JOIN permission_actor ON actor_id = permission_actor_id
JOIN placeables on placeables.id = permission_actor_id
WHERE player_id = in_player_id AND rank = 1::smallint; -- 1:owner
-- Get actors where in_player_id is the owner
SELECT ARRAY_AGG(permission_actor_id) into actors_with_permission
FROM permission_actor_rank WHERE player_id = in_player_id AND rank = 1::smallint; -- 1:owner
-- Remove all permissions for those actors
IF cardinality(actors_with_permission) > 0 THEN
DELETE FROM permission_actor_rank WHERE permission_actor_id = ANY(actors_with_permission);
DELETE FROM MARKERS WHERE marker_hash_id = ANY(actors_with_permission);
PERFORM pg_notify('permission_notify_channel', format('owner_delete#{"PlayerId" : %s}', in_player_id));
END IF;
END
$function$
-- parties_get_exclusive_operation_lock() -> void
-- oid: 58483 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.parties_get_exclusive_operation_lock()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
PERFORM pg_advisory_xact_lock(9457135); -- Parties in leet :/
END;
$function$
-- perform_notify_on_character_delete(in_user_id text) -> void
-- oid: 58484 kind: FUNCTION category: player_persistence
CREATE OR REPLACE FUNCTION dune.perform_notify_on_character_delete(in_user_id text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
PERFORM pg_notify('player_info_notify_channel', format('character_deleted#%s', in_user_id));
END
$function$
-- permission_actor_create_or_update_base_marker(in_actor_id bigint, in_player_id bigint, in_rank smallint) -> void
-- oid: 58485 kind: FUNCTION category: permission
CREATE OR REPLACE FUNCTION dune.permission_actor_create_or_update_base_marker(in_actor_id bigint, in_player_id bigint, in_rank smallint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
out_marker_type TEXT := 'HomeBase';
out_owner_id BIGINT;
out_owner_name Text;
out_dimension_index INTEGER;
out_x REAL;
out_y REAL;
out_z REAL;
out_totem_name Text;
out_map_name Text;
out_map_name_id SMALLINT;
out_actor_type smallint;
BEGIN
-- Get owner data
SELECT player_id, character_name
INTO out_owner_id, out_owner_name
FROM permission_actor_rank
JOIN player_state on player_controller_id = player_id
WHERE rank = 1::smallint AND permission_actor_id = in_actor_id
LIMIT 1;
-- Get target and totem data
SELECT dimension_index, (transform).location.x, (transform).location.y, (transform).location.z, actor_name, map, actor_type
INTO out_dimension_index, out_x, out_y, out_z, out_totem_name, out_map_name, out_actor_type
FROM permission_actor_rank
JOIN permission_actor on actor_id = permission_actor_id
JOIN player_state on player_controller_id = player_id
JOIN actors ON permission_actor_id = actors.id
WHERE permission_actor_id = in_actor_id AND player_id = in_player_id
LIMIT 1;
SELECT map_name_id
INTO out_map_name_id
FROM map_names
WHERE map_name = out_map_name;
IF out_actor_type = 3 OR out_actor_type = 4 THEN -- Totem || TotemSmall
INSERT INTO markers ("dimension_index", "marker_hash_id", "map_name_id", "marker", "area_id", "area_radius", "long_range", "payload")
VALUES(out_dimension_index,
in_actor_id,
out_map_name_id,
ROW(
out_marker_type,
out_x, out_y, out_z,
'EMarkerPayloadType::Permissions'
)::MARKER,
0,
0,
FALSE,
jsonb_build_object(
'OwnerUID', out_owner_id,
'OwnerName', out_owner_name,
'TotemName', out_totem_name,
'TotemId', in_actor_id
)
)
ON CONFLICT (marker_hash_id, dimension_index, map_name_id)
DO UPDATE SET
marker = EXCLUDED.marker;
INSERT INTO player_markers ("dimension_index", "player_id", "marker_hash_id", "map_name_id", "discovery_level", "discovery_method", "payload")
VALUES(out_dimension_index,
in_player_id,
in_actor_id,
out_map_name_id,
3, -- EMarkerDiscoveryLevel::Discovered
10, -- EMarkerDiscoveryMethod::Permissions
'{}'::JSONB
)
ON CONFLICT (dimension_index, player_id, marker_hash_id, map_name_id)
DO NOTHING;
END IF;
END
$function$
-- permission_actor_destroy(in_actor_id bigint) -> void
-- oid: 58486 kind: FUNCTION category: permission
CREATE OR REPLACE FUNCTION dune.permission_actor_destroy(in_actor_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM permission_actor_rank WHERE permission_actor_id = in_actor_id;
DELETE FROM permission_actor WHERE actor_id = in_actor_id;
-- Destroy map markers related with this actor
DELETE FROM markers WHERE marker_hash_id = in_actor_id;
DELETE FROM player_markers WHERE marker_hash_id = in_actor_id;
PERFORM pg_notify('permission_notify_channel', format('destroy#{"ActorId" : %s}', in_actor_id));
END
$function$
-- permission_actor_register(in_entry dune.actorpermissionentry, in_owner_rank dune.actorpermissionrankdata) -> void
-- oid: 58487 kind: FUNCTION category: permission
CREATE OR REPLACE FUNCTION dune.permission_actor_register(in_entry dune.actorpermissionentry, in_owner_rank dune.actorpermissionrankdata)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO
permission_actor("actor_id", "actor_name", "actor_type", "access_level", "is_child")
VALUES(in_entry.actor_id, in_entry.actor_name, in_entry.actor_type, in_entry.access_level, in_entry.is_child);
IF NOT in_entry.is_child THEN
INSERT INTO permission_actor_rank("permission_actor_id", "player_id", "rank")
VALUES(in_entry.actor_id, in_owner_rank.player_id, in_owner_rank.rank);
END IF;
-- there is no pg_notify here as the use cases where it may be needed are very low and we do not want to pay that cost. If we find any scenario where we need it, it can be added
END
$function$
-- permission_actor_takeover(in_entry dune.actorpermissionentry, in_owner_rank dune.actorpermissionrankdata) -> void
-- oid: 58488 kind: FUNCTION category: permission
CREATE OR REPLACE FUNCTION dune.permission_actor_takeover(in_entry dune.actorpermissionentry, in_owner_rank dune.actorpermissionrankdata)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
found_actor_id BIGINT;
found_guild_id BIGINT;
found_previous_owner BIGINT;
BEGIN
-- Check if the actor is already owned to avoid exploits
SELECT player_id into found_previous_owner FROM permission_actor_rank WHERE permission_actor_id = in_entry.actor_id AND rank = 1::smallint;
IF found_previous_owner IS NOT NULL THEN
RAISE NOTICE 'Player % trying to claim ownership over actor % already owned by player %.',
in_owner_rank.player_id, in_entry.actor_id, found_previous_owner;
return;
END IF;
SELECT actor_id FROM permission_actor WHERE actor_id = in_entry.actor_id INTO found_actor_id;
IF NOT FOUND THEN
PERFORM permission_actor_register(in_entry, in_owner_rank);
RETURN;
END IF;
SELECT guild_id FROM guild_members WHERE player_id = in_entry.actor_id INTO found_guild_id;
IF NOT FOUND THEN
found_guild_id := 0;
END IF;
DELETE FROM permission_actor_rank WHERE permission_actor_id = in_entry.actor_id;
INSERT INTO permission_actor_rank("permission_actor_id", "player_id", "rank")
VALUES(in_entry.actor_id, in_owner_rank.player_id, in_owner_rank.rank);
PERFORM pg_notify('permission_notify_channel', format('takeover#{"ActorId" : %s , "PlayerId" : %s, "PlayerGuildId" : %s}', in_entry.actor_id, in_owner_rank.player_id, found_guild_id));
END
$function$
-- permission_actor_update_marker_location(in_actor_id bigint, in_location_x real, in_location_y real, in_location_z real) -> void
-- oid: 58489 kind: FUNCTION category: permission
CREATE OR REPLACE FUNCTION dune.permission_actor_update_marker_location(in_actor_id bigint, in_location_x real, in_location_y real, in_location_z real)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE markers SET marker =
(
(marker).marker_type,
in_location_x,
in_location_y,
in_location_z,
(marker).payload_type
)
WHERE marker_hash_id = in_actor_id;
END
$function$
-- permission_remove_player_rank(in_actor_id bigint, in_player_id bigint) -> void
-- oid: 58490 kind: FUNCTION category: permission
CREATE OR REPLACE FUNCTION dune.permission_remove_player_rank(in_actor_id bigint, in_player_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM permission_actor_rank WHERE permission_actor_id = in_actor_id AND player_id = in_player_id;
-- Remove from player_markers using player id and, actor id as hash id
DELETE FROM player_markers WHERE player_id = in_player_id AND marker_hash_id = in_actor_id;
PERFORM pg_notify('permission_notify_channel', format('remove_rank#{"ActorId" : %s , "PlayerId" : %s}', in_actor_id, in_player_id));
END
$function$
-- permission_set_access_level(in_actor_id bigint, in_access_level smallint) -> void
-- oid: 58491 kind: FUNCTION category: permission
CREATE OR REPLACE FUNCTION dune.permission_set_access_level(in_actor_id bigint, in_access_level smallint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE permission_actor SET access_level = in_access_level WHERE permission_actor.actor_id = in_actor_id;
PERFORM pg_notify('permission_notify_channel', format('set_access_level#{"ActorId" : %s , "AccessLevel" : %s}', in_actor_id, in_access_level));
END
$function$
-- permission_set_name(in_actor_id bigint, in_name text) -> void
-- oid: 58492 kind: FUNCTION category: permission
CREATE OR REPLACE FUNCTION dune.permission_set_name(in_actor_id bigint, in_name text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE permission_actor SET actor_name = in_name WHERE permission_actor.actor_id = in_actor_id;
UPDATE markers SET marker =
(
(marker).marker_type,
(marker).x,
(marker).y,
(marker).z,
(marker).payload_type
),
payload = jsonb_set(payload, '{TotemName}', to_jsonb(in_name) , false)
WHERE marker_hash_id = in_actor_id;
PERFORM pg_notify('permission_notify_channel', format('set_name#{"ActorId" : %s , "Name" : "%s"}', in_actor_id, in_name));
END
$function$
-- permission_set_player_rank(in_actor_id bigint, in_player_id bigint, in_rank smallint, in_map_id text) -> void
-- oid: 58493 kind: FUNCTION category: character_mod
CREATE OR REPLACE FUNCTION dune.permission_set_player_rank(in_actor_id bigint, in_player_id bigint, in_rank smallint, in_map_id text)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
found_actor_id BIGINT;
found_guild_id BIGINT;
BEGIN
SELECT permission_actor_id FROM permission_actor_rank WHERE permission_actor_id = in_actor_id AND player_id = in_player_id INTO found_actor_id;
IF NOT FOUND THEN
INSERT INTO permission_actor_rank("permission_actor_id", "player_id", "rank") VALUES(in_actor_id, in_player_id, in_rank);
ELSE
UPDATE permission_actor_rank SET rank = in_rank WHERE permission_actor_rank.permission_actor_id = in_actor_id AND player_id = in_player_id;
END IF;
SELECT guild_id FROM guild_members WHERE player_id = in_actor_id INTO found_guild_id;
IF NOT FOUND THEN
found_guild_id := 0;
END IF;
PERFORM permission_actor_create_or_update_base_marker(in_actor_id, in_player_id, in_rank);
PERFORM pg_notify('permission_notify_channel', format('set_rank#{"ActorId" : %s , "PlayerId" : %s, "PlayerGuildId" : %s, "Rank" : %s, "Map" : %s}', in_actor_id, in_player_id, found_guild_id, in_rank, in_map_id));
END
$function$
-- player_purchased_item_from_vendor(in_vendor_id text, in_player_id bigint, in_template_id text, in_amount_bought integer) -> void
-- oid: 58494 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.player_purchased_item_from_vendor(in_vendor_id text, in_player_id bigint, in_template_id text, in_amount_bought integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
-- Add/update stock
IF NOT EXISTS
( SELECT * FROM vendor_stock_state
WHERE vendor_id = in_vendor_id and player_id = in_player_id AND template_id = in_template_id)
THEN
INSERT INTO vendor_stock_state(vendor_id, player_id, template_id, amount_bought) VALUES(in_vendor_id, in_player_id, in_template_id, in_amount_bought);
ELSE
UPDATE vendor_stock_state SET amount_bought = amount_bought + in_amount_bought
WHERE vendor_id = in_vendor_id and player_id = in_player_id AND template_id = in_template_id;
END IF;
END
$function$
-- player_state_update(in_data dune.playerstateupdatedata[]) -> void
-- oid: 58495 kind: FUNCTION category: character_mod
CREATE OR REPLACE FUNCTION dune.player_state_update(in_data dune.playerstateupdatedata[])
RETURNS void
LANGUAGE sql
AS $function$
-- online -> offline
with
update_data as (select * from unnest(in_data))
update encrypted_player_state as ps
set online_status = update_data.online_status
from update_data
where ps.player_controller_id = update_data.player_controller_id
and ps.server_id = update_data.current_server_id -- make sure we don't update if the player is already online somewhere else
and update_data.online_status != 'Online'
and ps.online_status != update_data.online_status; -- avoid unnecessary data changes
-- offline -> online
with
update_data as (select * from unnest(in_data))
update encrypted_player_state as ps
set online_status = update_data.online_status,
server_id = update_data.current_server_id
from update_data
where ps.player_controller_id = update_data.player_controller_id
and update_data.online_status = 'Online'
and (ps.server_id is null or ps.server_id != update_data.current_server_id or ps.online_status != update_data.online_status); -- avoid unnecessary data changes
with
update_data as (select * from unnest(in_data))
update encrypted_player_state as ps
set reconnect_grace_period_end = update_data.reconnect_grace_period_end
from update_data
where ps.player_controller_id = update_data.player_controller_id
and not update_data.reconnect_grace_period_end is null;
with
update_data as (select * from unnest(in_data))
update encrypted_player_state as ps
set
last_avatar_activity = (update_data.on_disconnect).last_online_time AT TIME ZONE 'UTC',
previous_server_partition_id = (update_data.on_disconnect).previous_server_partition_id
from update_data
where ps.player_controller_id = update_data.player_controller_id
and not update_data.on_disconnect is null;
$function$
-- pledge_guild_allegiance(in_guild_id bigint, in_guild_leader_player_id bigint, in_neutral_faction_id smallint) -> void
-- oid: 58496 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.pledge_guild_allegiance(in_guild_id bigint, in_guild_leader_player_id bigint, in_neutral_faction_id smallint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
guilds_changed SMALLINT := 0;
guild_data_record record;
guild_leader_record record;
guild_leader_faction_id SMALLINT;
BEGIN
PERFORM guilds_get_exclusive_operation_lock();
SELECT * INTO guild_leader_record FROM guild_members
LEFT JOIN player_faction ON actor_id = in_guild_leader_player_id
WHERE player_id = in_guild_leader_player_id AND is_player_guild_admin(in_guild_leader_player_id, in_guild_id);
IF guild_leader_record IS NULL THEN
RAISE EXCEPTION 'Trying to change a faction for a player: % without a guild', in_guild_leader_player_id;
END IF;
SELECT * INTO guild_data_record FROM guilds WHERE guild_id = in_guild_id;
IF guild_data_record IS NULL THEN
RAISE EXCEPTION 'Trying to change a faction in non existing guild: %', in_guild_id;
END IF;
IF guild_leader_record.faction_id IS NULL THEN
guild_leader_faction_id := in_neutral_faction_id;
ELSE
guild_leader_faction_id := guild_leader_record.faction_id;
END IF;
if guild_leader_faction_id = in_neutral_faction_id THEN
RAISE EXCEPTION 'Guild leader has neutral faction, cannot change faction to neutral';
ELSEIF guild_data_record.guild_faction = guild_leader_faction_id THEN
RAISE EXCEPTION 'Guild already has the same allegiance: % as the guild leader %', in_guild_id, guild_data_record.guild_faction;
END IF;
UPDATE guilds SET guild_faction = guild_leader_faction_id WHERE guilds.guild_id = in_guild_id;
PERFORM pg_notify('guild_notify_channel', format('pledge_guild_allegiance#{"GuildId" : %s , "OldGuildFactionDbId" : %s, "NewGuildFactionDbId" : %s}', in_guild_id, guild_data_record.guild_faction, guild_leader_faction_id));
PERFORM remove_guild_members(ARRAY(
SELECT player_id FROM guild_members
JOIN player_faction ON guild_members.player_id = player_faction.actor_id
WHERE guild_leader_faction_id != player_faction.faction_id AND player_faction.faction_id != in_neutral_faction_id AND guild_members.guild_id = in_guild_id),
in_guild_id,
2::smallint
);
END
$function$
-- produce_spicefield_manifest(in_map_name text, in_dimension_index integer) -> TABLE(server text, type_id integer, inactive_fields integer, requested_fields integer)
-- oid: 58497 kind: FUNCTION category: spice_field
CREATE OR REPLACE FUNCTION dune.produce_spicefield_manifest(in_map_name text, in_dimension_index integer)
RETURNS TABLE(server text, type_id integer, inactive_fields integer, requested_fields integer)
LANGUAGE plpgsql
AS $function$
begin
return query
select sa.server_id, sa.spicefield_type_id, sa.inactive_fields_of_type, sa.requested_spawned_of_type
from spicefield_server_availability sa join spicefield_types st
on sa.spicefield_type_id = st.spicefield_type_id
where st.map_name = in_map_name and st.dimension_index = in_dimension_index
order by server_id;
end $function$
-- promote_guild_member(in_guild_id bigint, in_player_id bigint, in_new_role smallint) -> void
-- oid: 58498 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.promote_guild_member(in_guild_id bigint, in_player_id bigint, in_new_role smallint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
PERFORM guilds_get_exclusive_operation_lock();
-- check if new admin is actualy in guild
IF NOT EXISTS(SELECT FROM guild_members WHERE player_id = in_player_id AND guild_id = in_guild_id) THEN
RAISE EXCEPTION 'Trying to promte player not in guild %.', in_player_id;
END IF;
if in_new_role = 100 THEN
-- set admin to member
UPDATE guild_members SET role_id = 50 WHERE guild_id = in_guild_id AND role_id = 100;
END IF;
-- set new player to new role
UPDATE guild_members SET role_id = in_new_role WHERE player_id = in_player_id AND guild_id = in_guild_id;
PERFORM pg_notify('guild_notify_channel', format('promote_player#{"PlayerId" : %s , "GuildId" : %s, "NewRole" : %s}', in_player_id, in_guild_id, in_new_role));
END
$function$
-- promote_new_party_leader(in_party_id bigint) -> void
-- oid: 58499 kind: FUNCTION category: party
CREATE OR REPLACE FUNCTION dune.promote_new_party_leader(in_party_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
out_current_leader BIGINT;
out_new_leader BIGINT;
BEGIN
PERFORM parties_get_exclusive_operation_lock();
-- get current leader
SELECT party_leader_id FROM parties where party_id = in_party_id into out_current_leader;
IF out_current_leader IS NULL THEN
RAISE EXCEPTION 'Promoting a player to a non existing party %.', in_party_id;
END IF;
-- get first member in the party that is online and is not the party leader
SELECT party_members.player_id INTO out_new_leader FROM party_members
JOIN player_state ON player_state.player_controller_id = party_members.player_id
WHERE party_members.party_id = in_party_id
AND party_members.player_id <> out_current_leader
AND player_state.online_status = 'Online';
IF out_new_leader IS NOT NULL THEN
-- promote
UPDATE parties SET party_leader_id = out_new_leader WHERE party_id = in_party_id;
PERFORM pg_notify('party_notify_channel', format('promote_party_leader#{"PartyId" : %s, "PlayerId" : %s}', in_party_id, out_new_leader));
END IF;
END
$function$
-- promote_party_leader_to(in_party_id bigint, in_player_id bigint) -> void
-- oid: 58500 kind: FUNCTION category: party
CREATE OR REPLACE FUNCTION dune.promote_party_leader_to(in_party_id bigint, in_player_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
out_current_leader BIGINT;
out_new_leader BIGINT;
BEGIN
PERFORM parties_get_exclusive_operation_lock();
-- get current leader
SELECT party_leader_id FROM parties where party_id = in_party_id into out_current_leader;
IF out_current_leader IS NULL THEN
RAISE EXCEPTION 'Promoting a player to a non existing party %.', in_party_id;
END IF;
-- get new member if it is online
SELECT party_members.player_id INTO out_new_leader FROM party_members
JOIN player_state ON player_state.player_controller_id = party_members.player_id
WHERE party_members.party_id = in_party_id
AND party_members.player_id = in_player_id
AND player_state.online_status = 'Online';
IF out_new_leader IS NOT NULL THEN
-- promote
UPDATE parties SET party_leader_id = out_new_leader WHERE party_id = in_party_id;
PERFORM pg_notify('party_notify_channel', format('promote_party_leader#{"PartyId" : %s, "PlayerId" : %s}', in_party_id, out_new_leader));
ELSE
RAISE EXCEPTION 'Promopromoting player %, which is offline or does not belong to the party.', in_party_id;
-- pg_notify for feedback?
END IF;
END
$function$
-- purchase_specialization_keystone(in_player_id bigint, in_keystone text) -> boolean
-- oid: 58501 kind: FUNCTION category: character_mod
CREATE OR REPLACE FUNCTION dune.purchase_specialization_keystone(in_player_id bigint, in_keystone text)
RETURNS boolean
LANGUAGE plpgsql
AS $function$
DECLARE
found_id SMALLINT;
inserted_id SMALLINT;
BEGIN
SELECT id FROM specialization_keystones_map INTO found_id WHERE name = in_keystone;
IF found_id IS NULL THEN
RETURN FALSE;
END IF;
INSERT INTO purchased_specialization_keystones (player_id, keystone_id) VALUES (in_player_id, found_id)
ON CONFLICT DO NOTHING
RETURNING keystone_id INTO inserted_id;
IF inserted_id IS NULL THEN
RETURN FALSE;
END IF;
RETURN TRUE;
END $function$
-- record_deactivated_spice_field(in_server_id text, in_spicefield_type_id integer) -> void
-- oid: 58502 kind: FUNCTION category: spice_field
CREATE OR REPLACE FUNCTION dune.record_deactivated_spice_field(in_server_id text, in_spicefield_type_id integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE spicefield_server_availability
SET inactive_fields_of_type = inactive_fields_of_type + 1
WHERE server_id = in_server_id AND spicefield_type_id = in_spicefield_type_id;
UPDATE spicefield_types
SET current_globally_active = current_globally_active - 1
WHERE spicefield_type_id = in_spicefield_type_id;
END; $function$
-- record_dungeon_completion(in_dungeon_id text, in_difficulty integer, in_duration_ms integer, players_ids bigint[]) -> void
-- oid: 58503 kind: FUNCTION category: dungeon
CREATE OR REPLACE FUNCTION dune.record_dungeon_completion(in_dungeon_id text, in_difficulty integer, in_duration_ms integer, players_ids bigint[])
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
new_completion_id BIGINT;
begin
INSERT INTO dungeon_completion VALUES (DEFAULT, in_dungeon_id, in_difficulty, in_duration_ms, array_length(players_ids, 1))
RETURNING completion_id INTO new_completion_id;
INSERT INTO dungeon_completion_players SELECT t.player_id, new_completion_id FROM UNNEST(players_ids) AS t(player_id);
end
$function$
-- record_logoff_persistence_end_time(in_player_pawn_id bigint, in_logoff_persistence_end_time timestamp without time zone) -> void
-- oid: 58504 kind: FUNCTION category: player_persistence
CREATE OR REPLACE FUNCTION dune.record_logoff_persistence_end_time(in_player_pawn_id bigint, in_logoff_persistence_end_time timestamp without time zone)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE encrypted_player_state
SET logoff_persistence_end_time = in_logoff_persistence_end_time
WHERE player_pawn_id = in_player_pawn_id;
END
$function$
-- record_static_shifting_sand(in_id text, in_alpha double precision, in_x double precision, in_y double precision, in_last_modified_time bigint) -> void
-- oid: 58505 kind: FUNCTION category: shifting_sand
CREATE OR REPLACE FUNCTION dune.record_static_shifting_sand(in_id text, in_alpha double precision, in_x double precision, in_y double precision, in_last_modified_time bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO shiftingsands_data(id, alpha, x, y, last_modified_time) Values(in_id, in_alpha, in_x, in_y, to_timestamp(in_last_modified_time))
ON CONFLICT(id) DO UPDATE SET alpha = $2, last_modified_time = to_timestamp(in_last_modified_time);
END $function$
-- record_unreadied_spice_fields(in_server_id text, in_spicefield_type_id integer, in_num_unreadied integer) -> void
-- oid: 58506 kind: FUNCTION category: spice_field
CREATE OR REPLACE FUNCTION dune.record_unreadied_spice_fields(in_server_id text, in_spicefield_type_id integer, in_num_unreadied integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE spicefield_types
SET current_globally_primed = current_globally_primed - in_num_unreadied
WHERE spicefield_type_id = in_spicefield_type_id;
UPDATE spicefield_server_availability
SET inactive_fields_of_type = inactive_fields_of_type + in_num_unreadied
WHERE server_id = in_server_id AND spicefield_type_id = in_spicefield_type_id;
END; $function$
-- register_lore_pickup(in_lore_pickup_ids text[]) -> SETOF smallint
-- oid: 58507 kind: FUNCTION category: server
CREATE OR REPLACE FUNCTION dune.register_lore_pickup(in_lore_pickup_ids text[])
RETURNS SETOF smallint
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN query WITH
input AS (
SELECT UNNEST (in_lore_pickup_ids) as lore_pickup_id
),
existing AS (
SELECT incremental_id, lore_pickup_id FROM lore_pickups WHERE lore_pickup_id = ANY(SELECT lore_pickup_id FROM input)
),
inserted AS (
INSERT INTO lore_pickups("lore_pickup_id") SELECT lore_pickup_id FROM input WHERE NOT lore_pickup_id = ANY(SELECT lore_pickup_id FROM existing) returning incremental_id, lore_pickup_id
),
combined AS (
SELECT incremental_id, lore_pickup_id FROM existing UNION ALL SELECT incremental_id, lore_pickup_id FROM inserted
)
SELECT incremental_id FROM combined ORDER BY lore_pickup_id;
END
$function$
-- register_new_factions(factions text[]) -> TABLE(faction_id smallint, faction_name text)
-- oid: 58508 kind: FUNCTION category: faction
CREATE OR REPLACE FUNCTION dune.register_new_factions(factions text[])
RETURNS TABLE(faction_id smallint, faction_name text)
LANGUAGE plpgsql
AS $function$
DECLARE
found_role_id SMALLINT;
BEGIN
-- Lock the factions table to prevent concurrent modifications. This is only done once on server start up.
LOCK TABLE factions IN SHARE ROW EXCLUSIVE MODE;
WITH new_factions AS (
SELECT f FROM UNNEST(factions) f LEFT JOIN factions ON f = factions.name WHERE id IS NULL
)
INSERT INTO factions (name) SELECT * FROM new_factions ON CONFLICT DO NOTHING;
RETURN QUERY SELECT * from factions;
END
$function$
-- register_new_tutorials(tutorials text[]) -> TABLE(tutorial_id smallint, tutorial_name text)
-- oid: 58509 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.register_new_tutorials(tutorials text[])
RETURNS TABLE(tutorial_id smallint, tutorial_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
-- Lock the tutorials table to prevent concurrent modifications. This is only done once on server start up.
LOCK TABLE tutorials IN SHARE ROW EXCLUSIVE MODE;
WITH new_tutorials AS (
SELECT q FROM unnest(tutorials) q LEFT JOIN tutorials ON q = tutorials.name WHERE id is NULL
)
INSERT INTO tutorials (name) SELECT * FROM new_tutorials;
RETURN QUERY SELECT * from tutorials;
END
$function$
-- register_per_player_lore_pickup(in_lore_pickup_ids text[], in_use_temporary boolean) -> SETOF smallint
-- oid: 58510 kind: FUNCTION category: server
CREATE OR REPLACE FUNCTION dune.register_per_player_lore_pickup(in_lore_pickup_ids text[], in_use_temporary boolean)
RETURNS SETOF smallint
LANGUAGE plpgsql
AS $function$
BEGIN
IF in_use_temporary THEN
RETURN query select * from register_temporary_lore_pickup(in_lore_pickup_ids);
ELSE
RETURN query select * from register_lore_pickup(in_lore_pickup_ids);
END IF;
END
$function$
-- register_spawned_actor(in_spawner_id bigint, in_actor_id bigint) -> void
-- oid: 58511 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.register_spawned_actor(in_spawner_id bigint, in_actor_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO actor_spawner_actors(spawner_id, actor_id) VALUES(in_spawner_id, in_actor_id);
END $function$
-- register_spice_field_server_resources(in_server_id text, in_spicefield_type_ids integer[], in_inactive_fields_of_types integer[]) -> void
-- oid: 58512 kind: FUNCTION category: spice_field
CREATE OR REPLACE FUNCTION dune.register_spice_field_server_resources(in_server_id text, in_spicefield_type_ids integer[], in_inactive_fields_of_types integer[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO spicefield_server_availability(server_id, spicefield_type_id, inactive_fields_of_type)
SELECT in_server_id, unnest(in_spicefield_type_ids), unnest(in_inactive_fields_of_types)
ON CONFLICT(server_id, spicefield_type_id)
DO UPDATE SET server_id = excluded.server_id, spicefield_type_id = excluded.spicefield_type_id, inactive_fields_of_type = excluded.inactive_fields_of_type;
END; $function$
-- register_temporary_lore_pickup(in_lore_pickup_ids text[]) -> SETOF smallint
-- oid: 58513 kind: FUNCTION category: server
CREATE OR REPLACE FUNCTION dune.register_temporary_lore_pickup(in_lore_pickup_ids text[])
RETURNS SETOF smallint
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN query WITH
input AS (
SELECT UNNEST (in_lore_pickup_ids) as lore_pickup_id
),
existing AS (
SELECT incremental_id, lore_pickup_id FROM lore_pickups_temporary WHERE lore_pickup_id = ANY(SELECT lore_pickup_id FROM input)
),
inserted AS (
INSERT INTO lore_pickups_temporary("lore_pickup_id") SELECT lore_pickup_id FROM input WHERE NOT lore_pickup_id = ANY(SELECT lore_pickup_id FROM existing) returning incremental_id, lore_pickup_id
),
combined AS (
SELECT incremental_id, lore_pickup_id FROM existing UNION ALL SELECT incremental_id, lore_pickup_id FROM inserted
)
SELECT incremental_id FROM combined ORDER BY lore_pickup_id;
END
$function$
-- reject_guild_invite(in_invite_id bigint) -> void
-- oid: 58514 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.reject_guild_invite(in_invite_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
player_id BIGINT := 0;
guild_id BIGINT := 0;
BEGIN
PERFORM guilds_get_exclusive_operation_lock();
-- check if invite exists
SELECT guild_invites.player_id, guild_invites.guild_id FROM guild_invites WHERE invite_id = in_invite_id INTO player_id, guild_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Trying to remove non exiting invite %.', in_invite_id;
END IF;
DELETE FROM guild_invites WHERE invite_id = in_invite_id;
PERFORM pg_notify('guild_notify_channel', format('reject_invite#{"PlayerId" : %s , "GuildId" : %s}', player_id, guild_id));
END
$function$
-- remove_aborted_authority_transfer_actors(in_partition_id bigint) -> SETOF dune.actorspawninfo
-- oid: 58515 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.remove_aborted_authority_transfer_actors(in_partition_id bigint)
RETURNS SETOF dune.actorspawninfo
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
WITH removed_actors AS (
DELETE FROM actor_state WHERE actor_state.state = 'AbortedAuthorityTransfer'
RETURNING actor_state.actor_id
)
SELECT a.id, a.class AS class_name, a.transform, a.partition_id, a.dimension_index
FROM actors AS a
INNER JOIN removed_actors ON a.id = removed_actors.actor_id
WHERE a.partition_id = in_partition_id;
END
$function$
-- remove_character_transfer_state(in_fls_id text) -> void
-- oid: 58516 kind: FUNCTION category: transfer
CREATE OR REPLACE FUNCTION dune.remove_character_transfer_state(in_fls_id text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM character_transfer_imports WHERE fls_id = in_fls_id;
END;
$function$
-- remove_communinet_player_channel(in_account_id bigint, in_channel_name text) -> void
-- oid: 58517 kind: FUNCTION category: communinet
CREATE OR REPLACE FUNCTION dune.remove_communinet_player_channel(in_account_id bigint, in_channel_name text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM communinet_player_channels WHERE account_id = in_account_id AND channel_name = in_channel_name;
END
$function$
-- remove_guild_members(in_player_ids bigint[], in_guild_id bigint, in_remove_reason smallint) -> void
-- oid: 58518 kind: FUNCTION category: guild
CREATE OR REPLACE FUNCTION dune.remove_guild_members(in_player_ids bigint[], in_guild_id bigint, in_remove_reason smallint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
out_guild_name TEXT;
players_removed BIGINT[];
BEGIN
PERFORM guilds_get_exclusive_operation_lock();
-- check if guild exists
SELECT guild_name INTO out_guild_name FROM guilds WHERE guild_id = in_guild_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Trying to disband non existing guild %.', in_guild_id;
END IF;
WITH removed_members AS (
DELETE FROM guild_members
WHERE player_id = ANY(in_player_ids) AND NOT is_player_guild_admin(player_id, in_guild_id)
RETURNING *
) SELECT array_agg(player_id) from removed_members INTO players_removed;
PERFORM pg_notify('guild_notify_channel', format('remove_players#{"PlayerIds" : [%s] , "GuildId" : %s, "GuildName" : "%s", "GuildRemoveReason" : %s}', ARRAY_TO_STRING(players_removed, ','), in_guild_id, out_guild_name, in_remove_reason));
END
$function$
-- remove_items(items_to_remove text[]) -> void
-- oid: 58519 kind: FUNCTION category: items_purge
CREATE OR REPLACE FUNCTION dune.remove_items(items_to_remove text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
PERFORM delete_items(
(
SELECT array_agg(id)
FROM items
WHERE template_id = ANY (items_to_remove)
)
);
end;
$function$
-- remove_items_and_recipes(items_to_remove text[], recipes_to_remove text[]) -> void
-- oid: 58520 kind: FUNCTION category: items_purge
CREATE OR REPLACE FUNCTION dune.remove_items_and_recipes(items_to_remove text[], recipes_to_remove text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
declare
items_to_remove_filtered text[];
recipes_to_remove_filtered text[];
begin
lock table removed_items, removed_recipes, actors, fgl_entities, items in exclusive mode;
-- get the items in items_to_remove that are not in removed_items
items_to_remove_filtered := get_items_to_remove(items_to_remove);
-- remove items from fgl entities
perform remove_items_or_recipes_from_fgl_entities(items_to_remove_filtered);
-- get the recipes in recipes_to_remove that are not in removed_recipes
recipes_to_remove_filtered := get_recipes_to_remove(recipes_to_remove);
-- removes the requests that contain items or recipes from fgl_entities.
perform remove_items_or_recipes_from_fgl_entities(recipes_to_remove_filtered);
-- delete the items
perform remove_items(items_to_remove_filtered);
-- remove the recipes from actors known item recipes
perform remove_recipes_from_actor_properties(recipes_to_remove_filtered);
-- insert removed items and recipes in their respective tables
perform update_removed_items_and_recipes(items_to_remove_filtered, recipes_to_remove_filtered);
end
$function$
-- remove_items_or_recipes_from_fgl_entities(item_or_recipes text[]) -> void
-- oid: 58521 kind: FUNCTION category: items_purge
CREATE OR REPLACE FUNCTION dune.remove_items_or_recipes_from_fgl_entities(item_or_recipes text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
declare
item_or_recipe text;
ent_id bigint;
components_data jsonb;
components_data_updated jsonb;
request jsonb;
item_or_recipe_found boolean;
updated_requests jsonb;
ingredient_allocation jsonb;
item_alloc_node jsonb;
allocated_item jsonb;
allocated_ingredient_info record;
begin
drop table if exists allocated_ingredients_info_temp;
create temp table allocated_ingredients_info_temp (item_unique_id bigint, amount bigint);
if array_length(item_or_recipes, 1) is null then
return;
end if;
foreach item_or_recipe in array(item_or_recipes)
loop
for ent_id, components_data in select entity_id, components
from fgl_entities
where components->'FItemCraftingComponent' is not null
loop
components_data_updated := components_data;
if jsonb_array_length(components_data_updated->'FItemCraftingComponent'->1->'RequestsQueue') < 1 then
return;
end if;
loop
item_or_recipe_found := false;
for request in select value::jsonb
from jsonb_array_elements(components_data_updated->'FItemCraftingComponent'->1->'RequestsQueue')
loop
if (request->'RecipeId'->>'Name' = item_or_recipe or
exists (select 1 from jsonb_array_elements(request->'ResultItems') where value->'ItemTemplateId'->>'Name' = item_or_recipe) or
exists (select 1 from jsonb_array_elements(request->'IngredientAllocations') where value->'ItemAllocNodes'->0->'ItemTemplateId'->>'Name' = item_or_recipe)) then
insert into allocated_ingredients_info_temp (item_unique_id, amount)
select (item->>'ItemUniqueId')::BigInt, (item->>'ItemAmount')::BigInt from (
select jsonb_array_elements(node->'AllocatedItems') as item, node as outer_node from (
select jsonb_array_elements(allocation->'ItemAllocNodes') as node from (
select jsonb_array_elements(request->'IngredientAllocations') as allocation
) allocations
) nodes
) items;
updated_requests := (select jsonb_agg(value) from jsonb_array_elements(components_data_updated->'FItemCraftingComponent'->1->'RequestsQueue') where value::jsonb != request);
if updated_requests is null then
components_data_updated := jsonb_set(components_data_updated, '{FItemCraftingComponent,1,RequestsQueue}', '[]'::jsonb);
components_data_updated := jsonb_set(components_data_updated, '{FItemCraftingComponent,1,State}', '"Idle"'::jsonb);
components_data_updated := jsonb_set(components_data_updated, '{FItemCraftingComponent,1,TotalTimeToCraftInSec}', '0'::jsonb);
components_data_updated := jsonb_set(components_data_updated, '{FItemCraftingComponent,1,PreviouslyCompletedTimeToCraftInSec}', '0'::jsonb);
else
components_data_updated := jsonb_set(components_data_updated, '{FItemCraftingComponent,1,RequestsQueue}', updated_requests);
end if;
item_or_recipe_found := true;
exit;
end if;
end loop;
exit when not item_or_recipe_found;
end loop;
update fgl_entities
set components = components_data_updated
where entity_id = ent_id;
end loop;
end loop;
for allocated_ingredient_info in select * from allocated_ingredients_info_temp
loop
perform delete_inventory_item(allocated_ingredient_info.item_unique_id, allocated_ingredient_info.amount);
end loop;
drop table allocated_ingredients_info_temp;
end;
$function$
-- remove_members_offline_for(in_interval_seconds integer) -> void
-- oid: 58522 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.remove_members_offline_for(in_interval_seconds integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
PERFORM parties_get_exclusive_operation_lock();
PERFORM remove_party_member(party_members.player_id, 0::SMALLINT) FROM party_members
JOIN player_state ON player_state.player_controller_id = party_members.player_id
WHERE player_state.online_status = 'Offline'
AND CURRENT_TIMESTAMP > player_state.last_avatar_activity + INTERVAL '1 second' * in_interval_seconds;
END
$function$
-- remove_party_invite(in_invite_id bigint, in_remove_reason smallint) -> void
-- oid: 58523 kind: FUNCTION category: party
CREATE OR REPLACE FUNCTION dune.remove_party_invite(in_invite_id bigint, in_remove_reason smallint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
out_sender_player_id BIGINT;
out_player_id BIGINT;
BEGIN
-- delete invite
IF in_invite_id IS NOT NULL THEN
DELETE FROM party_invites WHERE invite_id = in_invite_id RETURNING player_id, sender_player_id INTO out_player_id, out_sender_player_id;
PERFORM pg_notify('party_notify_channel', format('remove_invite#{"InviteId" : %s, "Reason" : %s, "SenderId" : %s, "PlayerId" : %s}', in_invite_id, in_remove_reason, out_sender_player_id, out_player_id));
END IF;
END
$function$
-- remove_party_member(in_player_id bigint, in_remove_reason smallint) -> void
-- oid: 58524 kind: FUNCTION category: party
CREATE OR REPLACE FUNCTION dune.remove_party_member(in_player_id bigint, in_remove_reason smallint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
out_party_id BIGINT;
out_player_platform_name TEXT;
member_count BIGINT;
out_platform_members_count BIGINT;
removed_player_id BIGINT;
removed_is_leader BOOLEAN;
BEGIN
PERFORM parties_get_exclusive_operation_lock();
DELETE FROM party_members WHERE player_id = in_player_id RETURNING player_id, party_id INTO removed_player_id, out_party_id;
IF removed_player_id IS NOT NULL THEN
-- check if there are more than 1 player in the party
SELECT INTO member_count COUNT(*) FROM party_members WHERE party_id = out_party_id;
IF member_count > 1 THEN
-- check if removed player is leader
SELECT INTO removed_is_leader EXISTS (SELECT 1 FROM parties WHERE party_leader_id = removed_player_id);
IF removed_is_leader THEN
-- TODO promote player other player
PERFORM promote_new_party_leader(out_party_id);
END IF;
END IF;
SELECT accounts.platform_name INTO out_player_platform_name FROM accounts
JOIN actors ON actors.id = in_player_id
WHERE accounts.id = actors.owner_account_id;
SELECT num_of_players INTO out_platform_members_count FROM platform_parties_mapping WHERE platform_name = out_player_platform_name AND dune_party_id = out_party_id;
IF out_platform_members_count IS NOT NULL THEN
-- there was a platform session for the player's party
IF out_platform_members_count <= 1 THEN
-- if player leaving causes no players to be in that platform session anymore, remove entry
DELETE FROM platform_parties_mapping WHERE platform_name = out_player_platform_name AND dune_party_id = out_party_id;
ELSE
-- still players, decrease platform player count
UPDATE platform_parties_mapping SET num_of_players = out_platform_members_count-1 WHERE platform_name = out_player_platform_name AND dune_party_id = out_party_id;
END IF;
END IF;
PERFORM pg_notify('party_notify_channel', format(
'remove_party_member#{"PlayerId" : %s, "PartyId" : %s, "PlayerPlatformName" : "%s", "PartyRemoveReason" : %s}',
removed_player_id, out_party_id, out_player_platform_name, in_remove_reason));
If member_count <= 1 THEN
PERFORM disband_party(out_party_id);
END IF;
END IF;
END
$function$
-- remove_recipes_from_actor_properties(recipes_to_remove text[]) -> void
-- oid: 58525 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.remove_recipes_from_actor_properties(recipes_to_remove text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
with actors_properties as (
select id, properties->'CraftingRecipesLibraryActorComponent'->'m_KnownItemRecipes' as recipes
from actors
),
modified_actors_properties as (
select id, (
select jsonb_agg(recipe)
from jsonb_array_elements(actors_properties.recipes) as recipe
where not (recipe->'BaseRecipeId'->>'Name' = any(recipes_to_remove))
) as filtered_recipes
from actors_properties
)
update actors
set properties = jsonb_set(
actors.properties,
'{CraftingRecipesLibraryActorComponent,m_KnownItemRecipes}',
coalesce(modified_actors_properties.filtered_recipes, '[]'::jsonb)
)
from modified_actors_properties
where actors.id = modified_actors_properties.id;
end;
$function$
-- remove_resourcefield_states(in_map text, in_dimension_index integer, in_field_ids bigint[]) -> void
-- oid: 58526 kind: FUNCTION category: items_purge
CREATE OR REPLACE FUNCTION dune.remove_resourcefield_states(in_map text, in_dimension_index integer, in_field_ids bigint[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE
FROM resourcefield_state
WHERE field_id = ANY(in_field_ids) AND map = in_map AND dimension_index = in_dimension_index;
END
$function$
-- request_spawn_spice_field(in_server_id text, in_spicefield_type_id integer) -> void
-- oid: 58527 kind: FUNCTION category: spice_field
CREATE OR REPLACE FUNCTION dune.request_spawn_spice_field(in_server_id text, in_spicefield_type_id integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
PERFORM spicefield_type_id FROM spicefield_types AS t
WHERE t.spicefield_type_id = in_spicefield_type_id AND t.is_spawning_active IS TRUE AND t.current_globally_primed < t.max_globally_primed AND t.current_globally_active < t.max_globally_active;
IF NOT FOUND THEN
RETURN;
END IF;
UPDATE spicefield_server_availability
SET requested_spawned_of_type = requested_spawned_of_type + 1
WHERE server_id = in_server_id AND spicefield_type_id = in_spicefield_type_id;
END; $function$
-- reset_all_players_from_server_ids_grace_period_and_logoff_timer(in_server_id text, in_reset_time timestamp without time zone) -> void
-- oid: 58528 kind: FUNCTION category: cleanup
CREATE OR REPLACE FUNCTION dune.reset_all_players_from_server_ids_grace_period_and_logoff_timer(in_server_id text, in_reset_time timestamp without time zone)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE encrypted_player_state SET reconnect_grace_period_end = in_reset_time WHERE server_id = in_server_id AND reconnect_grace_period_end > in_reset_time;
UPDATE encrypted_player_state SET logoff_persistence_end_time = in_reset_time WHERE server_id = in_server_id AND logoff_persistence_end_time > in_reset_time;
END
$function$
-- reset_global_spice_field_state(in_map_name text, in_dimension_index integer) -> void
-- oid: 58529 kind: FUNCTION category: spice_field
CREATE OR REPLACE FUNCTION dune.reset_global_spice_field_state(in_map_name text, in_dimension_index integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE spicefield_server_availability sa
SET requested_spawned_of_type = 0
FROM spicefield_types st
WHERE sa.spicefield_type_id = st.spicefield_type_id AND st.map_name = in_map_name AND st.dimension_index = in_dimension_index;
UPDATE spicefield_types
SET current_globally_primed = 0, current_globally_active = 0
WHERE map_name = in_map_name AND dimension_index = in_dimension_index;
END; $function$
-- reset_journey_story_nodes_for_player(in_player_id text, in_story_node_ids text[]) -> void
-- oid: 58530 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.reset_journey_story_nodes_for_player(in_player_id text, in_story_node_ids text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
IF NOT is_player_offline(in_player_id) THEN
RAISE EXCEPTION 'Cannot execute query because the player is online - they must be offline in order for the journey data to be updated correctly without risking it being overwritten by player actions.';
END IF;
UPDATE journey_story_node
SET complete_condition_state = jsonb_object(ARRAY[]::text[])
WHERE story_node_id = ANY(in_story_node_ids)
AND account_id IN (
SELECT id
FROM accounts a
WHERE a.user = in_player_id
);
DELETE FROM journey_story_node_cooldown
WHERE story_node_id = ANY(in_story_node_ids)
AND account_id IN (
SELECT id
FROM accounts a
WHERE a.user = in_player_id
);
END $function$
-- reset_server_all_player_access_codes(in_account_id bigint) -> void
-- oid: 58531 kind: FUNCTION category: cleanup
CREATE OR REPLACE FUNCTION dune.reset_server_all_player_access_codes(in_account_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM player_access_codes
WHERE account_id = in_account_id
AND is_resettable = true;
END
$function$
-- reset_specialization_keystones(in_player_id bigint) -> void
-- oid: 58532 kind: FUNCTION category: character_mod
CREATE OR REPLACE FUNCTION dune.reset_specialization_keystones(in_player_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM purchased_specialization_keystones WHERE player_id = in_player_id;
END $function$
-- reset_specialization_tracks(in_player_id bigint) -> void
-- oid: 58533 kind: FUNCTION category: character_mod
CREATE OR REPLACE FUNCTION dune.reset_specialization_tracks(in_player_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM specialization_tracks WHERE player_id = in_player_id;
END $function$
-- restore_backup_vehicle(in_account_id bigint, in_server_info dune.serverinfo, in_transform dune.transform) -> bigint
-- oid: 58534 kind: FUNCTION category: vehicle
CREATE OR REPLACE FUNCTION dune.restore_backup_vehicle(in_account_id bigint, in_server_info dune.serverinfo, in_transform dune.transform)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
found_vehicle_id BIGINT;
BEGIN
-- Retrieve a backup vehicle for the provided account.
SELECT vehicle_id INTO found_vehicle_id
FROM backup_vehicles
WHERE account_id = in_account_id
LIMIT 1; -- In case more than one is stored.
IF NOT FOUND THEN
RAISE EXCEPTION 'No backup vehicle found for account_id: %', in_account_id;
END IF;
-- Only restore vehicle if the actor state is vehicle backup
PERFORM 1 FROM actor_state WHERE actor_id = found_vehicle_id AND state = 'VehicleBackup';
IF NOT FOUND THEN
RAISE EXCEPTION 'Trying to restore vehicle % that does not belong to vehicle backup feature.', found_vehicle_id;
END IF;
-- Update the actors record to restore the vehicle.
UPDATE actors
SET map = in_server_info.map,
partition_id = in_server_info.partition_id,
dimension_index = in_server_info.dimension_index,
transform = in_transform
WHERE id = found_vehicle_id;
IF NOT FOUND THEN
RAISE WARNING 'No actor record found with id % during restore.', found_vehicle_id;
END IF;
-- Remove the restored vehicle from backup_vehicles.
DELETE FROM backup_vehicles WHERE vehicle_id = found_vehicle_id;
RAISE INFO 'Deleted backup record for vehicle %.', found_vehicle_id;
-- There is no need to keep the actor state in the table anymore since the vehicle backup was successful data wise
DELETE FROM actor_state WHERE actor_id = found_vehicle_id;
RAISE INFO 'Deleted actor state for vehicle %.', found_vehicle_id;
PERFORM verify_item_dup_backup_tool(in_account_id, found_vehicle_id, 'item_dup_on_restore_vbt');
RETURN found_vehicle_id;
END;
$function$
-- restore_recovered_vehicle(in_account_id bigint, in_vehicle_id bigint, in_server_info dune.serverinfo, in_transform dune.transform, in_restore_time_limit integer) -> void
-- oid: 58535 kind: FUNCTION category: vehicle
CREATE OR REPLACE FUNCTION dune.restore_recovered_vehicle(in_account_id bigint, in_vehicle_id bigint, in_server_info dune.serverinfo, in_transform dune.transform, in_restore_time_limit integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
found_vehicle_id BIGINT;
found_vehicle_name TEXT;
found_player_id BIGINT;
BEGIN
-- Retrieve a recovered vehicle for the provided account.
SELECT vehicle_id, vehicle_name INTO found_vehicle_id, found_vehicle_name
FROM recovered_vehicles
WHERE account_id = in_account_id AND vehicle_id = in_vehicle_id
-- 60s leeway in favor of player in case they try to restore last second
AND (migrated = TRUE OR time_stored > NOW() - (in_restore_time_limit + 60) * INTERVAL '1 second')
LIMIT 1; -- In case more than one is stored.
IF NOT FOUND THEN
RAISE EXCEPTION 'No recovered vehicle found for account_id: % and vehicle_id: %', in_account_id, in_vehicle_id;
END IF;
-- Only restore vehicle if the actor state is VehicleRecovery
PERFORM 1 FROM actor_state WHERE actor_id = found_vehicle_id AND state = 'VehicleRecovery';
IF NOT FOUND THEN
RAISE EXCEPTION 'Trying to restore vehicle % that does not belong to vehicle recovery feature.', found_vehicle_id;
END IF;
-- Update the actors record to restore the vehicle.
UPDATE actors
SET map = in_server_info.map,
partition_id = in_server_info.partition_id,
dimension_index = in_server_info.dimension_index,
transform = in_transform
WHERE id = found_vehicle_id;
IF NOT FOUND THEN
RAISE WARNING 'No actor record found with id % during restore.', found_vehicle_id;
END IF;
-- permissions use the player controllers id, so we need to get that from the account id
SELECT player_controller_id INTO found_player_id FROM player_state WHERE account_id = in_account_id LIMIT 1;
IF NOT FOUND THEN
RAISE EXCEPTION 'No player_controller_id found for account: %', in_account_id;
END IF;
-- restore default permissions
INSERT INTO permission_actor("actor_id", "actor_name", "actor_type", "access_level", "is_child")
VALUES(found_vehicle_id, found_vehicle_name, 2, 3, false);
INSERT INTO permission_actor_rank("permission_actor_id", "player_id", "rank")
VALUES(found_vehicle_id, found_player_id, 1);
-- Remove the restored vehicle from recovered_vehicles.
DELETE FROM recovered_vehicles WHERE vehicle_id = found_vehicle_id;
RAISE INFO 'Deleted recovery record for vehicle %.', found_vehicle_id;
-- There is no need to keep the actor state in the table anymore since the vehicle recovery was successful data wise
DELETE FROM actor_state WHERE actor_id = found_vehicle_id;
RAISE INFO 'Deleted actor state for vehicle %.', found_vehicle_id;
--PERFORM verify_item_dup_backup_tool(in_account_id, found_vehicle_id, 'item_dup_on_restore_vbt');
END;
$function$
-- retrieve_all_static_shifting_sand() -> TABLE(out_id text, out_alpha double precision, out_x double precision, out_y double precision, out_last_modified_time bigint)
-- oid: 58536 kind: FUNCTION category: shifting_sand
CREATE OR REPLACE FUNCTION dune.retrieve_all_static_shifting_sand()
RETURNS TABLE(out_id text, out_alpha double precision, out_x double precision, out_y double precision, out_last_modified_time bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT id, alpha, x, y, EXTRACT(EPOCH FROM last_modified_time)::BIGINT AS last_modified_time FROM shiftingsands_data;
END; $function$
-- returning_player_award_given(in_account_id bigint) -> void
-- oid: 58537 kind: FUNCTION category: character_mod
CREATE OR REPLACE FUNCTION dune.returning_player_award_given(in_account_id bigint)
RETURNS void
LANGUAGE sql
AS $function$
UPDATE player_state SET last_returning_player_awarded_time=now(), last_returning_player_event_time=NULL WHERE account_id=in_account_id;
$function$
-- reveal_journey_story_nodes_for_player(in_player_id text, in_story_node_ids text[]) -> void
-- oid: 58538 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.reveal_journey_story_nodes_for_player(in_player_id text, in_story_node_ids text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
IF NOT is_player_offline(in_player_id) THEN
RAISE EXCEPTION 'Cannot execute query because the player is online - they must be offline in order for the journey data to be updated correctly without risking it being overwritten by player actions.';
END IF;
WITH player_account_id AS (
SELECT id
FROM accounts a
WHERE a.user = in_player_id
)
INSERT INTO journey_story_node(account_id, story_node_id, override_reward_block, has_pending_reward, complete_condition_state, reveal_condition_state, fail_condition_state, metadata_state, reset_group)
SELECT player_account_id.id, completed_node.story_node_id, completed_node.override_reward_block, completed_node.has_pending_reward, completed_node.complete_condition_state, completed_node.reveal_condition_state, completed_node.fail_condition_state, completed_node.metadata_state, completed_node.reset_group
FROM player_account_id
CROSS JOIN (
SELECT story_node_id, false, false, jsonb_object(ARRAY[]::text[]), to_jsonb(true), jsonb_object(ARRAY[]::text[]), jsonb_object(ARRAY[]::text[]), 'Default'::JourneyStoryResetGroup
FROM UNNEST(in_story_node_ids) AS story_node_id
) completed_node(story_node_id, override_reward_block, has_pending_reward, complete_condition_state, reveal_condition_state, fail_condition_state, metadata_state, reset_group)
ON CONFLICT ON CONSTRAINT journey_story_node_pkey
DO UPDATE SET
reveal_condition_state = EXCLUDED.reveal_condition_state,
metadata_state = EXCLUDED.metadata_state;
END $function$
-- save_aborted_authority_transfer_actors(in_actor_ids bigint[], in_partition_id bigint) -> void
-- oid: 58539 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.save_aborted_authority_transfer_actors(in_actor_ids bigint[], in_partition_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO actor_state(actor_id, state)
SELECT a.id, 'AbortedAuthorityTransfer'
FROM actors AS a
WHERE a.id = ANY(in_actor_ids) AND a.partition_id = in_partition_id
ON CONFLICT DO NOTHING;
END
$function$
-- save_actor_dislocation(in_actor_id bigint, in_current_server_info dune.serverinfo, in_target_location dune.vector, in_target_dimension_index integer) -> void
-- oid: 58540 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.save_actor_dislocation(in_actor_id bigint, in_current_server_info dune.serverinfo, in_target_location dune.vector, in_target_dimension_index integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
update actors
set
transform = (in_target_location, (transform).rotation),
dimension_index = in_target_dimension_index,
partition_id = null
where id = in_actor_id
and map = (in_current_server_info).map
and dimension_index = (in_current_server_info).dimension_index
and (
partition_id is null
or (in_current_server_info).partition_id is null
or partition_id = (in_current_server_info).partition_id
);
end
$function$
-- save_actors(in_server_info dune.serverinfo, in_actors dune.actordescription[], in_actor_state dune.actorstate) -> TABLE(actor_id bigint, current_saved_serial bigint, saved boolean)
-- oid: 58541 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.save_actors(in_server_info dune.serverinfo, in_actors dune.actordescription[], in_actor_state dune.actorstate DEFAULT 'Default'::dune.actorstate)
RETURNS TABLE(actor_id bigint, current_saved_serial bigint, saved boolean)
LANGUAGE plpgsql
AS $function$
BEGIN
return query with
input_actors as (
select * from unnest(in_actors)
),
valid_input_actors as (
select input_actors.id from input_actors
left join actor_state on input_actors.id = actor_state.actor_id
where actor_state.state = in_actor_state or (in_actor_state = 'Default' and actor_state.actor_id is null)
),
serial_checks as (
select
input.id,
input.serial as input_serial,
coalesce(actors.serial, 0) as saved_serial,
input.serial >= coalesce(actors.serial, 0) and input.id in (select id from valid_input_actors) as should_save
from
input_actors as input left join actors using (id)
),
actors_to_save as (
select i.* from input_actors as i join serial_checks as c using (id) where c.should_save
),
upsert_actors as (
insert into actors(
"id", "class", "transform",
"gas_attributes",
"properties",
"map", "partition_id", "dimension_index",
"serial"
)
select
i.id, i.class_name, i.transform,
(i.generic_data).gas_attribute_sets_json, (i.generic_data).properties_json,
in_server_info.map, in_server_info.partition_id, coalesce(in_server_info.dimension_index, 0),
i.serial
from actors_to_save as i
on conflict (id) do update
set
"class" = EXCLUDED.class, "transform" = case when EXCLUDED.transform is null or (EXCLUDED.transform).location = (zero_transform()).location then actors.transform else EXCLUDED.transform end,
"gas_attributes" = EXCLUDED.gas_attributes, "properties" = EXCLUDED.properties,
"map" = EXCLUDED.map, "partition_id" = EXCLUDED.partition_id, "dimension_index" = EXCLUDED.dimension_index,
"serial" = EXCLUDED.serial
returning id
),
fgl_entity_data as (
select id as actor_id, (u).entity_id, (u).slot_name, (u).components_json as components
from (select id, unnest((generic_data).entities) as u from actors_to_save) q
),
missing_entities as (
select entity_id
from actors_to_save join actor_fgl_entities as existing on (actors_to_save.id = existing.actor_id)
where not exists(select 1 from fgl_entity_data as updated where updated.entity_id=existing.entity_id)
),
delete_missing_entity_links as (
delete from actor_fgl_entities as existing using missing_entities
where existing.entity_id=missing_entities.entity_id
returning existing.entity_id as deleted_entity_id
),
delete_missing_entities as (
delete from fgl_entities as existing using missing_entities
where existing.entity_id=missing_entities.entity_id
),
upsert_entities as (
insert into fgl_entities("entity_id", "components")
select entity_id, components from fgl_entity_data
on conflict (entity_id) do update
set components=EXCLUDED.components
returning entity_id
),
upsert_entity_links as (
insert into actor_fgl_entities("actor_id", "entity_id", "slot_name")
select fgl_entity_data.actor_id, fgl_entity_data.entity_id, fgl_entity_data.slot_name
from fgl_entity_data left join upsert_entities using (entity_id)
-- HACK: this is a temporary fix until we do TECH-23063
where not entity_id in (select deleted_entity_id from delete_missing_entity_links)
on conflict (entity_id) do update
set
actor_id=EXCLUDED.actor_id,
slot_name=EXCLUDED.slot_name
returning actor_fgl_entities.actor_id, actor_fgl_entities.entity_id
),
all_actor_entities as (
select fgl_entity_data.actor_id as id, array_agg(entity_id) as entity_ids
from fgl_entity_data
left join upsert_entities using (entity_id)
left join upsert_entity_links using (entity_id)
group by fgl_entity_data.actor_id
),
extra_data as (
select
input_actors.id,
serial_checks.saved_serial,
(serial_checks.should_save) as saved,
(input_actors.generic_data).building_actor_data as building_data,
(input_actors.generic_data).placeable_actor_data as placeable_data,
(input_actors.generic_data).totem_actor_data as totem_data,
coalesce(all_actor_entities.entity_ids, array[]::int[]) as entity_ids
from
serial_checks
left join input_actors using(id)
left join upsert_actors using(id) -- this is needed for dependency
left join all_actor_entities using(id) -- this is needed for dependency only
),
save_extras as (
select
extra_data.id,
extra_data.saved_serial,
extra_data.saved,
case when extra_data.saved and building_data is not null then
save_building(id, building_data)
end,
case when extra_data.saved and placeable_data is not null then
save_placeable(id, placeable_data)
end,
case when extra_data.saved and totem_data is not null then
save_totem(id, totem_data)
end,
entity_ids
from extra_data
)
select id, saved_serial, save_extras.saved from save_extras;
END
$function$
-- save_building(in_building_id bigint, in_data dune.buildingsavedata) -> void
-- oid: 58543 kind: FUNCTION category: building_blueprint
CREATE OR REPLACE FUNCTION dune.save_building(in_building_id bigint, in_data dune.buildingsavedata)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
instance BUILDINGINSTANCE;
instance_to_remove INTEGER;
instance_owner BuildingInstanceUpdateOwner;
BEGIN
-- ADD
IF array_length(in_data.in_add_building_data, 1) > 0 THEN
INSERT INTO building_instances(
"building_id",
"instance_id",
"building_type",
"transform",
"owner_entity_id",
"building_flags",
"health",
"shelter",
"stabilization_begin_timespan",
"stabilization_end_timespan",
"stabilization_state",
"sand_buildup"
)
SELECT
in_building_id,
add_data.instance_id,
add_data.building_type,
add_data.transform,
_building_validate_totem_owner_id(add_data.owner_entity_id),
add_data.building_flags,
add_data.health,
add_data.shelter,
add_data.stabilization_begin_timespan,
add_data.stabilization_end_timespan,
add_data.stabilization_state,
add_data.sand_buildup
FROM unnest(in_data.in_add_building_data) as add_data
ON CONFLICT ("building_id", "instance_id")
DO UPDATE SET
"building_type" = (instance).building_type,
"transform" = (instance).transform,
"owner_entity_id" = _building_validate_totem_owner_id((instance).owner_entity_id),
"building_flags" = (instance).building_flags,
"health" = (instance).health,
"shelter" = (instance).shelter,
"stabilization_begin_timespan" = (instance).stabilization_begin_timespan,
"stabilization_end_timespan" = (instance).stabilization_end_timespan,
"stabilization_state" = (instance).stabilization_state,
"sand_buildup" = (instance).sand_buildup;
END IF;
-- REMOVE
IF array_length(in_data.in_remove_building_data, 1) > 0 THEN
DELETE FROM building_instances
WHERE building_instances."building_id" = in_building_id AND building_instances."instance_id" = ANY(in_data.in_remove_building_data);
END IF;
-- OWNER. 99.99% of the time, this will only have 1 Owner Array.
IF array_length(in_data.in_building_owner_data, 1) > 0 THEN
FOREACH instance_owner IN ARRAY in_data.in_building_owner_data LOOP
WITH owner_changes_table AS
(
SELECT unnest(instance_owner.instances) AS instance_id, instance_owner.owner_entity_id AS owner_entity_id
)
UPDATE building_instances
SET owner_entity_id = _building_validate_totem_owner_id(owner_changes_table.owner_entity_id)
FROM owner_changes_table
WHERE building_instances.building_id = in_building_id AND building_instances.instance_id = owner_changes_table.instance_id;
END LOOP;
END IF;
-- STABILIZATION
IF array_length(in_data.in_building_stabilization_data, 1) > 0 THEN
WITH stabilization_changes_table AS
(
select * FROM unnest(in_data.in_building_stabilization_data)
)
UPDATE building_instances SET stabilization_begin_timespan = stabilization_changes_table.stabilization_begin_timespan, stabilization_end_timespan = stabilization_changes_table.stabilization_end_timespan, stabilization_state = stabilization_changes_table.stabilization_state
FROM stabilization_changes_table
WHERE building_instances.building_id = in_building_id AND building_instances.instance_id = stabilization_changes_table.instance_id;
END IF;
-- HEALTH
IF array_length(in_data.in_building_health_data, 1) > 0 THEN
WITH health_changes_table AS
(
select * FROM unnest(in_data.in_building_health_data)
)
UPDATE building_instances SET health = health_changes_table.health
FROM health_changes_table
WHERE building_instances.building_id = in_building_id AND building_instances.instance_id = health_changes_table.instance_id;
END IF;
-- SHELTER
IF array_length(in_data.in_building_shelter_data, 1) > 0 THEN
WITH shelter_changes_table AS
(
select * FROM unnest(in_data.in_building_shelter_data)
)
UPDATE building_instances SET shelter = shelter_changes_table.shelter
FROM shelter_changes_table
WHERE building_instances.building_id = in_building_id AND building_instances.instance_id = shelter_changes_table.instance_id;
END IF;
-- SAND BUILDUP
IF array_length(in_data.in_building_sand_buildup_data, 1) > 0 THEN
WITH sand_buildup_changes_table AS
(
select * FROM unnest(in_data.in_building_sand_buildup_data)
)
UPDATE building_instances SET sand_buildup = sand_buildup_changes_table.sand_buildup
FROM sand_buildup_changes_table
WHERE building_instances.building_id = in_building_id AND building_instances.instance_id = sand_buildup_changes_table.instance_id;
END IF;
-- BUILDING FLAGS
IF array_length(in_data.in_building_building_flags_data, 1) > 0 THEN
WITH building_flags_changes_table AS
(
select * FROM unnest(in_data.in_building_building_flags_data)
)
UPDATE building_instances SET building_flags = building_flags_changes_table.building_flags
FROM building_flags_changes_table
WHERE building_instances.building_id = in_building_id AND building_instances.instance_id = building_flags_changes_table.instance_id;
END IF;
-- BUILDING TRANSFORM
IF array_length(in_data.in_building_building_transform_data, 1) > 0 THEN
WITH building_flags_transform_table AS
(
select * FROM unnest(in_data.in_building_building_transform_data)
)
UPDATE building_instances SET transform = building_flags_transform_table.transform
FROM building_flags_transform_table
WHERE building_instances.building_id = in_building_id AND building_instances.instance_id = building_flags_transform_table.instance_id;
END IF;
END
$function$
-- save_building_blueprint_copy(in_building_item_id bigint, in_building_blueprint_id bigint, in_building_blueprint_building_data dune.buildingblueprintpiecesaveitemcontainer[], in_building_blueprint_placeable_data dune.buildingblueprintplaceablesaveitemcontainer[], in_building_blueprint_pentashield_data dune.buildingblueprintpentashielditem[]) -> bigint
-- oid: 58544 kind: FUNCTION category: building_blueprint
CREATE OR REPLACE FUNCTION dune.save_building_blueprint_copy(in_building_item_id bigint, in_building_blueprint_id bigint, in_building_blueprint_building_data dune.buildingblueprintpiecesaveitemcontainer[], in_building_blueprint_placeable_data dune.buildingblueprintplaceablesaveitemcontainer[], in_building_blueprint_pentashield_data dune.buildingblueprintpentashielditem[])
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
return_id BIGINT;
BEGIN
IF in_building_blueprint_id != 0 THEN
DELETE FROM building_blueprints WHERE id = in_building_blueprint_id;
END IF;
INSERT INTO building_blueprints(id, item_id, player_id, building_blueprint_map)
VALUES(DEFAULT, in_building_item_id, NULL, '') RETURNING id INTO return_id;
-- All Building Pieces
INSERT INTO building_blueprint_instances(building_blueprint_id, instance_id, building_type, transform, provides_stability, health, hologram)
SELECT
return_id,
piece_data.instance_id,
container_data.building_type,
piece_data.transform,
piece_data.provides_stability,
piece_data.health,
True
FROM
unnest(in_building_blueprint_building_data) AS container_data
CROSS JOIN LATERAL unnest(container_data.building_pieces) AS piece_data;
-- All Placeables
INSERT INTO building_blueprint_placeables(building_blueprint_id, placeable_id, building_type, transform, hologram)
SELECT
return_id,
placeable_data.placeable_id,
container_data.building_type,
placeable_data.transform,
True
FROM
unnest(in_building_blueprint_placeable_data) AS container_data
CROSS JOIN LATERAL unnest(container_data.placeables) AS placeable_data;
-- Pentashields
INSERT INTO building_blueprint_pentashields("building_blueprint_id", "placeable_id", "scale")
SELECT return_id as building_blueprint_id, placeable_id, scale FROM unnest(in_building_blueprint_pentashield_data);
RETURN return_id;
END
$function$
-- save_demo_account_time(in_fls_id text, in_demo_playtime_seconds integer) -> void
-- oid: 58545 kind: FUNCTION category: schema_meta
CREATE OR REPLACE FUNCTION dune.save_demo_account_time(in_fls_id text, in_demo_playtime_seconds integer)
RETURNS void
LANGUAGE sql
AS $function$
INSERT INTO demo_users("fls_id", "demo_playtime_seconds", "demo_state")
VALUES (in_fls_id, in_demo_playtime_seconds, 'Demo'::DemoState)
ON CONFLICT ("fls_id") DO UPDATE
SET demo_playtime_seconds = EXCLUDED.demo_playtime_seconds;
$function$
-- save_dialogue_data(in_player_controller_id bigint, in_met_npcs text[], in_taken_nodes integer[]) -> void
-- oid: 58546 kind: FUNCTION category: dialogue
CREATE OR REPLACE FUNCTION dune.save_dialogue_data(in_player_controller_id bigint, in_met_npcs text[], in_taken_nodes integer[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO dialogue_met_npcs(player_id, npc_name) SELECT in_player_controller_id, unnest(in_met_npcs)
ON CONFLICT DO NOTHING;
INSERT INTO dialogue_taken_nodes(player_id, node_id) SELECT in_player_controller_id, unnest(in_taken_nodes)
ON CONFLICT DO NOTHING;
END
$function$
-- save_item(in_item dune.inventoryitem) -> void
-- oid: 58547 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.save_item(in_item dune.inventoryitem)
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
INSERT INTO
items (id, inventory_id, stack_size, quality_level, volume_override, position_index, template_id, stats, is_new, acquisition_time)
VALUES (
(in_item).item_id,
(in_item).inventory_id,
(in_item).stack_size,
(in_item).quality_level,
(in_item).volume_override,
(in_item).position_index,
(in_item).template_id,
(in_item).stats,
(in_item).is_new,
(in_item).acquisition_time
)
ON CONFLICT (id)
DO UPDATE SET
inventory_id = (in_item).inventory_id,
stack_size = (in_item).stack_size,
quality_level = (in_item).quality_level,
volume_override = (in_item).volume_override,
position_index = (in_item).position_index,
template_id = (in_item).template_id,
stats = items.stats || (in_item).stats,
is_new = (in_item).is_new,
acquisition_time = (in_item).acquisition_time;
-- log item tracking
PERFORM _add_item_trace_log('save_item', (in_item).item_id, (in_item).inventory_id, (in_item).template_id, (in_item).position_index);
end
$function$
-- save_journey_story_node(in_account_id bigint, in_story_node_id text, in_override_reward_block boolean, in_has_pending_reward boolean, in_complete_condition_state jsonb, in_reveal_condition_state jsonb, in_fail_condition_state jsonb, in_metadata_state jsonb, in_reset_group dune.journeystoryresetgroup) -> void
-- oid: 58548 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.save_journey_story_node(in_account_id bigint, in_story_node_id text, in_override_reward_block boolean, in_has_pending_reward boolean, in_complete_condition_state jsonb, in_reveal_condition_state jsonb, in_fail_condition_state jsonb, in_metadata_state jsonb, in_reset_group dune.journeystoryresetgroup)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO journey_story_node(account_id, story_node_id, override_reward_block, has_pending_reward, complete_condition_state, reveal_condition_state, fail_condition_state, metadata_state, reset_group)
VALUES(in_account_id, in_story_node_id, in_override_reward_block, in_has_pending_reward, in_complete_condition_state, in_reveal_condition_state, in_fail_condition_state, in_metadata_state, in_reset_group)
ON CONFLICT (account_id, story_node_id)
DO UPDATE SET
override_reward_block = in_override_reward_block,
has_pending_reward = in_has_pending_reward,
complete_condition_state = in_complete_condition_state,
reveal_condition_state = in_reveal_condition_state,
fail_condition_state = in_fail_condition_state,
metadata_state = in_metadata_state,
reset_group = in_reset_group;
END $function$
-- save_journey_story_nodes(in_account_id bigint, in_journey_data dune.savejourneydata[]) -> void
-- oid: 58549 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.save_journey_story_nodes(in_account_id bigint, in_journey_data dune.savejourneydata[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO journey_story_node(account_id, story_node_id, override_reward_block, has_pending_reward, complete_condition_state, reveal_condition_state, fail_condition_state, metadata_state, reset_group)
SELECT in_account_id, story_node_id, override_reward_block, has_pending_reward, to_jsonb(completion_state_string), to_jsonb(reveal_state_string), to_jsonb(fail_state_string), to_jsonb(metadata_state_string), reset_group
FROM UNNEST(in_journey_data)
ON CONFLICT ON CONSTRAINT journey_story_node_pkey
DO UPDATE SET
override_reward_block = EXCLUDED.override_reward_block,
has_pending_reward = EXCLUDED.has_pending_reward,
complete_condition_state = EXCLUDED.complete_condition_state,
reveal_condition_state = EXCLUDED.reveal_condition_state,
fail_condition_state = EXCLUDED.fail_condition_state,
metadata_state = EXCLUDED.metadata_state,
reset_group = EXCLUDED.reset_group;
END
$function$
-- save_login_target_dimension(in_fls_id text, in_login_target_dimension_index integer) -> void
-- oid: 58550 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.save_login_target_dimension(in_fls_id text, in_login_target_dimension_index integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO player_travel_state (fls_id, login_target_dimension_index)
VALUES (in_fls_id, in_login_target_dimension_index)
ON CONFLICT (fls_id) DO UPDATE
SET login_target_dimension_index = EXCLUDED.login_target_dimension_index;
END;
$function$
-- save_markers(in_player_marker_data dune.saveplayermarkerdata[], in_marker_data dune.savemarkerdata[]) -> void
-- oid: 58551 kind: FUNCTION category: markers
CREATE OR REPLACE FUNCTION dune.save_markers(in_player_marker_data dune.saveplayermarkerdata[], in_marker_data dune.savemarkerdata[])
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
null_player_marker_map_id_count INTEGER;
null_marker_map_id_count INTEGER;
BEGIN
SELECT COUNT(*)
INTO null_player_marker_map_id_count
FROM UNNEST(in_player_marker_data) AS umd
LEFT JOIN map_names AS mn ON umd.map_name = mn.map_name
WHERE mn.map_name_id IS NULL; -- Find map name that doesn't exist
IF null_player_marker_map_id_count > 0 THEN
RAISE EXCEPTION 'Found records with NULL map name id for player markers!';
END IF;
SELECT COUNT(*)
INTO null_marker_map_id_count
FROM UNNEST(in_marker_data) AS umd
LEFT JOIN map_names AS mn ON umd.map_name = mn.map_name
WHERE mn.map_name_id IS NULL; -- Find map name that doesn't exist
IF null_marker_map_id_count > 0 THEN
RAISE EXCEPTION 'Found records with NULL map name id for markers!';
END IF;
INSERT INTO markers("marker_hash_id", "dimension_index", "map_name_id", "marker", "area_id", "area_radius", "long_range", "payload")
SELECT
umd.marker_hash_id,
umd.dimension_index,
mn.map_name_id,
umd.marker,
umd.area_id,
umd.area_radius,
umd.long_range,
umd.payload
FROM UNNEST(in_marker_data) umd JOIN map_names mn USING(map_name)
ORDER BY marker_hash_id, dimension_index, map_name_id
ON CONFLICT ON CONSTRAINT markers_pkey
DO UPDATE SET
"area_id" = EXCLUDED.area_id,
"area_radius" = EXCLUDED.area_radius,
"long_range" = EXCLUDED.long_range,
"payload" = EXCLUDED.payload;
WITH marker_data_for_existing_players AS (
SELECT
player_data.player_id,
player_data.marker_hash_id,
player_data.dimension_index,
mn.map_name_id,
player_data.discovery_level,
player_data.discovery_method,
player_data.player_payload
FROM UNNEST(in_player_marker_data) AS player_data
JOIN actors ON actors.id = player_data.player_id
JOIN map_names mn USING(map_name)
)
INSERT INTO player_markers("player_id", "marker_hash_id", "dimension_index", "map_name_id", "discovery_level", "discovery_method", "payload")
SELECT
in_data.player_id,
in_data.marker_hash_id,
in_data.dimension_index,
in_data.map_name_id,
in_data.discovery_level,
in_data.discovery_method,
in_data.player_payload
FROM marker_data_for_existing_players AS in_data, markers
WHERE markers.marker_hash_id = in_data.marker_hash_id AND markers.dimension_index = in_data.dimension_index AND markers.map_name_id = in_data.map_name_id
ORDER BY player_id, in_data.marker_hash_id, in_data.dimension_index, in_data.map_name_id -- Ordering for deadlock avoidance
ON CONFLICT ON CONSTRAINT player_markers_pkey
DO UPDATE SET
"discovery_level" = EXCLUDED.discovery_level,
"discovery_method" = EXCLUDED.discovery_method,
"payload" = EXCLUDED.payload;
END
$function$
-- save_mnemonic_recall_lesson(in_account_id bigint, in_lesson_id text, in_lesson_state bigint, in_lesson_progress integer, in_is_new boolean) -> void
-- oid: 58552 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.save_mnemonic_recall_lesson(in_account_id bigint, in_lesson_id text, in_lesson_state bigint, in_lesson_progress integer, in_is_new boolean)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO mnemonic_recall(account_id, lesson_id, lesson_state, lesson_progress, is_new)
VALUES(in_account_id, in_lesson_id, in_lesson_state, in_lesson_progress, in_is_new)
ON CONFLICT (account_id, lesson_id)
DO UPDATE SET
lesson_state = in_lesson_state,
lesson_progress = in_lesson_progress,
is_new = in_is_new;
END; $function$
-- save_placeable(in_placeable_id bigint, in_data dune.placeablesavedata) -> void
-- oid: 58561 kind: FUNCTION category: building_blueprint
CREATE OR REPLACE FUNCTION dune.save_placeable(in_placeable_id bigint, in_data dune.placeablesavedata)
RETURNS void
LANGUAGE sql
BEGIN ATOMIC
INSERT INTO dune.placeables (id, owner_entity_id, health, building_type, has_hit_ground, has_buildable_support, is_hologram)
VALUES (save_placeable.in_placeable_id, dune._placeable_validate_totem_owner_id((save_placeable.in_data).in_owner_entity_id), (save_placeable.in_data).in_health, (save_placeable.in_data).in_building_type, (save_placeable.in_data).in_has_hit_ground, (save_placeable.in_data).in_has_buildable_support, (save_placeable.in_data).in_is_hologram) ON CONFLICT(id) DO UPDATE SET owner_entity_id = excluded.owner_entity_id, health = excluded.health, building_type = excluded.building_type, has_hit_ground = excluded.has_hit_ground, has_buildable_support = excluded.has_buildable_support, is_hologram = excluded.is_hologram;
END
-- save_player(in_player dune.playerdescription) -> boolean
-- oid: 58562 kind: FUNCTION category: player_persistence
CREATE OR REPLACE FUNCTION dune.save_player(in_player dune.playerdescription)
RETURNS boolean
LANGUAGE plpgsql
AS $function$
DECLARE
fgl_entity FglEntity;
player_actors ActorDescription[];
all_serials_are_same Boolean;
should_save_all_actors Boolean;
BEGIN
player_actors := array[in_player.state, in_player.controller];
IF NOT in_player.pawn IS NULL THEN
foreach fgl_entity in array (in_player).pawn.generic_data.entities loop
if fgl_entity.slot_name in ('DuneCharacter', 'PersonalCrafting') and fgl_entity.components_json = '{}'::jsonb then
raise exception 'invalid player save with empty fgl state for pawn';
end if;
end loop;
player_actors := player_actors || in_player.pawn;
END IF;
with
ids_and_serials as (
select
input.serial = (in_player.controller).serial as is_matching_serial,
input.serial >= existing.serial as should_save
from unnest(player_actors) as input join actors as existing using (id)
)
select bool_and(is_matching_serial), bool_and(should_save) from ids_and_serials
into all_serials_are_same, should_save_all_actors;
IF NOT all_serials_are_same THEN
raise exception 'serial: serial mismatch between the player actors';
end if;
-- Demo time remaining
IF in_player.demo_playtime_seconds IS NOT NULL THEN
UPDATE demo_users
SET demo_playtime_seconds = in_player.demo_playtime_seconds
WHERE fls_id = (
SELECT acc.user FROM accounts AS acc
WHERE acc.id = in_player.id
);
END IF;
-- Demo state
IF in_player.demo_state IS NOT NULL THEN
UPDATE demo_users
SET demo_state = in_player.demo_state
WHERE fls_id = (
SELECT acc.user FROM accounts AS acc
WHERE acc.id = in_player.id
);
END IF;
IF NOT should_save_all_actors THEN
return false;
end if;
-- Allow saving of the player without having a player character (pawn)
PERFORM save_actors(in_player.pawn_server_info, player_actors);
PERFORM update_respawn_locations(in_player.id, (in_player.respawn_info).locations);
PERFORM update_death_location(in_player.pawn, in_player.pawn_server_info, in_player.life_state);
UPDATE player_state
SET pending_respawn_location_id=(in_player.respawn_info).pending_location_id, life_state=in_player.life_state
WHERE account_id=in_player.id;
return true;
END;
$function$
-- save_player_pawn(in_pawn dune.actordescription, in_server_info dune.serverinfo, in_life_state dune.playerlifestate) -> boolean
-- oid: 58563 kind: FUNCTION category: player_persistence
CREATE OR REPLACE FUNCTION dune.save_player_pawn(in_pawn dune.actordescription, in_server_info dune.serverinfo, in_life_state dune.playerlifestate)
RETURNS boolean
LANGUAGE plpgsql
AS $function$
DECLARE
should_save_pawn BOOLEAN;
BEGIN
WITH
ids_and_serials AS (
SELECT actors.serial >= (in_pawn).serial AS should_save
FROM actors
WHERE actors.id = in_pawn.id
)
SELECT should_save FROM ids_and_serials
INTO should_save_pawn;
IF NOT should_save_pawn THEN
RETURN false;
END IF;
PERFORM save_actors(in_server_info, ARRAY[in_pawn]);
PERFORM update_death_location(in_pawn, in_server_info, in_life_state);
RETURN true;
END;
$function$
-- save_static_encounter_name(in_map_name text, in_package_name text, in_actor_name text, in_encounter_name text) -> void
-- oid: 58564 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.save_static_encounter_name(in_map_name text, in_package_name text, in_actor_name text, in_encounter_name text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO encounters_static(map_name, package_name, actor_name, encounter_name, waiting_for_reset)
VALUES(in_map_name, in_package_name, in_actor_name, in_encounter_name, false)
ON CONFLICT(map_name, package_name, actor_name)
DO UPDATE SET encounter_name = in_encounter_name, waiting_for_reset = false
WHERE encounters_static.map_name = in_map_name AND encounters_static.package_name = in_package_name AND encounters_static.actor_name = in_actor_name;
END; $function$
-- save_static_encounter_waiting_for_reset(in_map_name text, in_package_name text, in_actor_name text, in_waiting_for_reset boolean) -> void
-- oid: 58565 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.save_static_encounter_waiting_for_reset(in_map_name text, in_package_name text, in_actor_name text, in_waiting_for_reset boolean)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE encounters_static
SET waiting_for_reset = in_waiting_for_reset
WHERE encounters_static.map_name = in_map_name AND encounters_static.package_name = in_package_name AND encounters_static.actor_name = in_actor_name;
END; $function$
-- save_totem(in_id bigint, in_data dune.totemsavedata) -> void
-- oid: 58571 kind: FUNCTION category: building_blueprint
CREATE OR REPLACE FUNCTION dune.save_totem(in_id bigint, in_data dune.totemsavedata)
RETURNS void
LANGUAGE sql
BEGIN ATOMIC
INSERT INTO dune.totems (id, landclaim_vertical_level, last_backup_timestamp, landclaim_original_global_location, landclaim_original_global_yaw_rotation)
VALUES (save_totem.in_id, (save_totem.in_data).landclaim_vertical_level, (save_totem.in_data).last_backup_timestamp, (save_totem.in_data).landclaim_original_global_location, (save_totem.in_data).landclaim_original_global_yaw_rotation) ON CONFLICT(id) DO UPDATE SET landclaim_vertical_level = excluded.landclaim_vertical_level, last_backup_timestamp = excluded.last_backup_timestamp, landclaim_original_global_location = excluded.landclaim_original_global_location, landclaim_original_global_yaw_rotation = excluded.landclaim_original_global_yaw_rotation;
END
-- save_tracked_journey_cards(in_player_id bigint, in_tracked_journey_card text, in_tracked_landsraad_card text) -> void
-- oid: 58572 kind: FUNCTION category: player_persistence
CREATE OR REPLACE FUNCTION dune.save_tracked_journey_cards(in_player_id bigint, in_tracked_journey_card text, in_tracked_landsraad_card text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO journey_tracked_cards (player_id, tracked_journey_card, tracked_landsraad_card) Values(in_player_id, in_tracked_journey_card, in_tracked_landsraad_card)
ON CONFLICT(player_id) DO
UPDATE SET tracked_journey_card = in_tracked_journey_card, tracked_landsraad_card = in_tracked_landsraad_card;
END $function$
-- save_travel_return_info(in_player_controller_id bigint, in_map text, in_transform dune.transform) -> void
-- oid: 58573 kind: FUNCTION category: travel
CREATE OR REPLACE FUNCTION dune.save_travel_return_info(in_player_controller_id bigint, in_map text, in_transform dune.transform)
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
insert into travel_return_info(
"player_controller_id", "map", "transform"
)
values(
in_player_controller_id, in_map, in_transform
)
on conflict (player_controller_id) do update
set
"map" = EXCLUDED.map,
"transform" = EXCLUDED.transform;
end
$function$
-- save_vehicle_modules(in_add_list dune.vehiclemodule[], in_delete_list bigint[], in_stat_update dune.itemstatupdate[]) -> SETOF bigint
-- oid: 58574 kind: FUNCTION category: vehicle
CREATE OR REPLACE FUNCTION dune.save_vehicle_modules(in_add_list dune.vehiclemodule[], in_delete_list bigint[], in_stat_update dune.itemstatupdate[])
RETURNS SETOF bigint
LANGUAGE plpgsql
AS $function$
DECLARE
module VEHICLEMODULE;
stat ItemStatUpdate;
new_module_id BIGINT;
currentstat RECORD;
BEGIN
--RAISE NOTICE 'Add vehicle modules';
-- add vehicle modules
FOREACH module IN ARRAY in_add_list LOOP
INSERT INTO vehicle_modules(
"vehicle_id", "template_id", "stats"
) VALUES(
(module).vehicle_id, (module).template_id, (module).stats
) RETURNING id INTO new_module_id;
RETURN NEXT new_module_id;
END LOOP;
--RAISE NOTICE 'Delete vehicle modules';
-- delete modules
DELETE FROM vehicle_modules WHERE id = ANY(in_delete_list);
--RAISE NOTICE 'Add vehicle module stats';
-- add vehicle module stats
FOREACH stat IN ARRAY in_stat_update LOOP
UPDATE vehicle_modules SET "stats" = vehicle_modules.stats || (stat).value WHERE "id" = (stat).item_id;
END LOOP;
RETURN;
END
$function$
-- save_world_partition(in_map_name text, in_server_id text, in_dimension_index bigint, in_partition_definition jsonb, in_blocked boolean, in_label text) -> bigint
-- oid: 58575 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.save_world_partition(in_map_name text, in_server_id text, in_dimension_index bigint, in_partition_definition jsonb, in_blocked boolean DEFAULT false, in_label text DEFAULT NULL::text)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
partition_id BIGINT;
BEGIN
LOCK TABLE world_partition; -- only one at a time, please
INSERT INTO world_partition(partition_id, server_id, map, partition_definition, dimension_index, blocked, label) VALUES(DEFAULT, in_server_id, in_map_name, in_partition_definition, in_dimension_index, in_blocked, in_label)
ON CONFLICT ("server_id", "map") DO UPDATE set partition_definition = in_partition_definition, blocked = in_blocked, label = in_label WHERE world_partition.server_id = in_server_id
RETURNING world_partition.partition_id INTO partition_id;
RETURN partition_id;
END
$function$
-- server_info_match(in_actor dune.actors, in_server_info dune.serverinfo) -> boolean
-- oid: 58586 kind: FUNCTION category: server
CREATE OR REPLACE FUNCTION dune.server_info_match(in_actor dune.actors, in_server_info dune.serverinfo)
RETURNS boolean
LANGUAGE plpgsql
STABLE STRICT
AS $function$
BEGIN
return in_actor.map = in_server_info.map
AND in_actor.dimension_index = in_server_info.dimension_index
AND (
in_actor.partition_id IS NULL
OR
in_server_info.partition_id IS NULL
OR
in_actor.partition_id = in_server_info.partition_id
);
END
$function$
-- set_account_as_takeoverable(in_user_id text, in_new_user_id text) -> void
-- oid: 58587 kind: FUNCTION category: takeover
CREATE OR REPLACE FUNCTION dune.set_account_as_takeoverable(in_user_id text, in_new_user_id text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE accounts SET "user"=in_new_user_id, takeoverable=TRUE WHERE "user"=in_user_id;
END; $function$
-- set_all_inactive_players_in_farm_offline() -> void
-- oid: 58588 kind: FUNCTION category: farm
CREATE OR REPLACE FUNCTION dune.set_all_inactive_players_in_farm_offline()
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE player_state SET online_status = 'Offline', last_avatar_activity = current_timestamp WHERE online_status <> 'Offline' AND server_id NOT IN (SELECT * FROM active_server_ids);
END
$function$
-- set_battlegroup_close_date(in_close_date timestamp without time zone) -> timestamp without time zone
-- oid: 58589 kind: FUNCTION category: battlegroup
CREATE OR REPLACE FUNCTION dune.set_battlegroup_close_date(in_close_date timestamp without time zone)
RETURNS timestamp without time zone
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO farm_variables (one_row, battlegroup_close_date) VALUES (true,in_close_date)
ON CONFLICT (one_row) DO UPDATE SET battlegroup_close_date = in_close_date;
RETURN (select * from get_battlegroup_close_date());
END
$function$
-- set_character_import_state(in_fls_id text, in_state dune.transferimportstate) -> void
-- oid: 58590 kind: FUNCTION category: character_mod
CREATE OR REPLACE FUNCTION dune.set_character_import_state(in_fls_id text, in_state dune.transferimportstate)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO character_transfer_imports (fls_id, last_update, transfer_state)
VALUES (in_fls_id, now(), in_state)
ON CONFLICT (fls_id) DO UPDATE
SET last_update = now(),
transfer_state = EXCLUDED.transfer_state;
END;
$function$
-- set_character_name(in_account_id bigint, in_name text) -> void
-- oid: 58591 kind: FUNCTION category: character_mod
CREATE OR REPLACE FUNCTION dune.set_character_name(in_account_id bigint, in_name text)
RETURNS void
LANGUAGE sql
AS $function$
update encrypted_player_state set encrypted_character_name=encrypt_user_data(in_name) where account_id=in_account_id;
$function$
-- set_demo_state(in_user_id text, in_demo_state dune.demostate) -> void
-- oid: 58592 kind: FUNCTION category: schema_meta
CREATE OR REPLACE FUNCTION dune.set_demo_state(in_user_id text, in_demo_state dune.demostate)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE demo_users
SET demo_state = in_demo_state
WHERE fls_id = in_user_id;
END
$function$
-- set_item_tracking_enabled(in_enabled boolean) -> void
-- oid: 58593 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.set_item_tracking_enabled(in_enabled boolean)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
PERFORM set_config('dune.item_tracking_enabled', in_enabled::TEXT, false);
END;
$function$
-- set_player_faction_reputation(in_actor_id bigint, in_faction_id smallint, in_reputation_amount integer) -> void
-- oid: 58594 kind: FUNCTION category: faction
CREATE OR REPLACE FUNCTION dune.set_player_faction_reputation(in_actor_id bigint, in_faction_id smallint, in_reputation_amount integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO player_faction_reputation (actor_id, faction_id, reputation_amount)
VALUES (in_actor_id, in_faction_id, in_reputation_amount)
ON CONFLICT (actor_id, faction_id)
DO UPDATE
SET reputation_amount = EXCLUDED.reputation_amount;
END
$function$
-- set_players_from_server_ids_offline(in_server_ids text[]) -> void
-- oid: 58595 kind: FUNCTION category: character_mod
CREATE OR REPLACE FUNCTION dune.set_players_from_server_ids_offline(in_server_ids text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE player_state SET online_status = 'Offline', last_avatar_activity = current_timestamp WHERE online_status <> 'Offline' AND server_id = ANY(in_server_ids);
END
$function$
-- set_specialization_xp_and_level(in_player_id bigint, in_track_type dune.specializationtracktype, in_xp_amount integer, in_level real) -> void
-- oid: 58596 kind: FUNCTION category: character_mod
CREATE OR REPLACE FUNCTION dune.set_specialization_xp_and_level(in_player_id bigint, in_track_type dune.specializationtracktype, in_xp_amount integer, in_level real)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO specialization_tracks (player_id, track_type, xp_amount, level) VALUES (in_player_id, in_track_type, in_xp_amount, in_level)
ON CONFLICT(player_id, track_type) DO UPDATE SET xp_amount = in_xp_amount, level = in_level;
END $function$
-- setup_user_data_encryption(IN in_enable boolean) -> void
-- oid: 58597 kind: PROCEDURE category: encryption
CREATE OR REPLACE PROCEDURE dune.setup_user_data_encryption(IN in_enable boolean)
LANGUAGE plpgsql
AS $procedure$
declare
encryption_key Text;
encryption_key_hash bytea;
stored_encryption_key_hash bytea;
stored_encryption_status UserDataEncryptionStatus;
begin
select current_setting('funcom.user_data_encryption_key', true) into encryption_key;
-- might be null if the encryption_key is null
select ext.digest(encryption_key, 'md5') into encryption_key_hash;
-- might be null if the stored data is not encrypted
select get_stored_user_data_encryption_key_hash() into stored_encryption_key_hash;
-- should never be null
select get_stored_user_data_encryption_status() into stored_encryption_status;
drop index if exists encrypted_player_state_character_name_gin;
if stored_encryption_status = 'Disabled' then
if in_enable then
perform _user_data_encryption_setup_enabled(encryption_key_hash);
perform _user_data_encryption_initially_encrypt_existing_data();
else
-- technically we shouldn't do anything but let's just validate a few things
-- we not replacing get_stored_user_data_encryption_key_hash()
if (select get_stored_user_data_encryption_key_hash()) is not null then
-- should have been filtered by the main setup function
raise exception 'The data is encrypted, should use the taint version';
end if;
if (select get_stored_user_data_encryption_taint_xmax()) is not null then
-- should have been filtered by the main setup function
raise exception 'The data is tainted, should use the taint version';
end if;
end if;
elseif stored_encryption_status = 'Tainted' then
-- doesn't matter what we want, we get the tainted version
perform _user_data_encryption_setup_tainted();
else -- Enabled
if in_enable is null or in_enable then
if encryption_key_hash = stored_encryption_key_hash then -- and our key is the same
perform _user_data_encryption_setup_enabled(encryption_key_hash);
else
raise warning 'User-data encryption requested but the data is already encrypted with a different key';
perform _user_data_encryption_setup_tainted();
end if;
else
raise warning 'User-data encryption not requested but the data is already encrypted';
perform _user_data_encryption_setup_tainted();
end if;
end if;
commit;
CREATE INDEX encrypted_player_state_character_name_gin ON encrypted_player_state USING GIN((decrypt_user_data(encrypted_character_name)) ext.gin_trgm_ops);
end
$procedure$
-- store_backup_vehicle(in_vehicle_id bigint, in_account_id bigint, in_customization_id text) -> void
-- oid: 58598 kind: FUNCTION category: vehicle
CREATE OR REPLACE FUNCTION dune.store_backup_vehicle(in_vehicle_id bigint, in_account_id bigint, in_customization_id text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
-- Lock the backup_vehicles table exclusively to prevent race conditions.
LOCK TABLE backup_vehicles IN EXCLUSIVE MODE;
-- Check if the vehicle already belongs to an actor state
PERFORM 1 FROM actor_state WHERE actor_id = in_vehicle_id;
IF FOUND THEN
RAISE EXCEPTION 'Trying to backup vehicle % that already has an actor state.', in_vehicle_id;
END IF;
-- Check if the vehicle is already in backup_vehicles.
PERFORM 1 FROM backup_vehicles WHERE vehicle_id = in_vehicle_id;
IF FOUND THEN
RAISE EXCEPTION 'Vehicle % is already stored as a backup.', in_vehicle_id;
END IF;
-- Check if the vehicle exists in the actors table.
PERFORM 1 FROM actors WHERE id = in_vehicle_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Vehicle % does not exist in actors.', in_vehicle_id;
END IF;
-- Insert the vehicle into backup_vehicles.
INSERT INTO backup_vehicles (account_id, vehicle_id, customization_id)
VALUES (in_account_id, in_vehicle_id, in_customization_id);
RAISE INFO 'Inserted vehicle % into backup_vehicles for account %.', in_vehicle_id, in_account_id;
-- Mark the actor as belonging to the vehicle backup tool
INSERT INTO actor_state("actor_id", "state") VALUES(in_vehicle_id, 'VehicleBackup');
RAISE INFO 'Inserted vehicle % into actor_state with VehicleBackup state', in_vehicle_id;
PERFORM verify_item_dup_backup_tool(in_account_id, in_vehicle_id, 'item_dup_on_store_vbt');
END;
$function$
-- store_recovered_vehicle(in_vehicle_id bigint, in_chassis_durability real, in_customization_id text, in_is_migration boolean) -> void
-- oid: 58599 kind: FUNCTION category: vehicle
CREATE OR REPLACE FUNCTION dune.store_recovered_vehicle(in_vehicle_id bigint, in_chassis_durability real, in_customization_id text, in_is_migration boolean DEFAULT false)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
owner_id BIGINT;
player_id BIGINT;
vehicle_class TEXT;
vehicle_name TEXT;
BEGIN
-- Lock the recovered_vehicles table exclusively to prevent race conditions.
LOCK TABLE recovered_vehicles IN EXCLUSIVE MODE;
-- Check if the vehicle already belongs to an actor state
PERFORM 1 FROM actor_state WHERE actor_id = in_vehicle_id;
IF FOUND THEN
RAISE EXCEPTION 'Trying to store recovered vehicle % that already has an actor state.', in_vehicle_id;
END IF;
-- Check if the vehicle is already in recovered_vehicles.
PERFORM 1 FROM recovered_vehicles WHERE vehicle_id = in_vehicle_id;
IF FOUND THEN
RAISE EXCEPTION 'Vehicle % is already recovered.', in_vehicle_id;
END IF;
-- Check if the vehicle exists in the actors table.
SELECT class INTO vehicle_class
FROM actors
WHERE id = in_vehicle_id
LIMIT 1;
IF NOT FOUND THEN
RAISE EXCEPTION 'Vehicle % does not exist in actors.', in_vehicle_id;
END IF;
-- get the vehicles owner account id
SELECT a.owner_account_id, r.player_id, p.actor_name INTO owner_id, player_id, vehicle_name
FROM actors a
INNER JOIN permission_actor_rank r ON r.player_id = a.id
INNER JOIN permission_actor p ON p.actor_id = r.permission_actor_id
WHERE r.permission_actor_id = in_vehicle_id
AND r.rank = 1::smallint
LIMIT 1;
IF NOT FOUND THEN
RAISE EXCEPTION 'No account_id found for vehicle: %', in_vehicle_id;
END IF;
-- Insert the vehicle into recovered_vehicles.
INSERT INTO recovered_vehicles(account_id, vehicle_id, chassis_durability, vehicle_name, customization_id, migrated)
VALUES (owner_id, in_vehicle_id, in_chassis_durability, vehicle_name, in_customization_id, in_is_migration);
RAISE INFO 'Inserted vehicle % into recovered_vehicles for account %.', in_vehicle_id, owner_id;
-- Mark the actor as belonging to the vehicle recovery tool
INSERT INTO actor_state("actor_id", "state") VALUES(in_vehicle_id, 'VehicleRecovery');
RAISE INFO 'Inserted vehicle % into actor_state with VehicleRecovery state', in_vehicle_id;
PERFORM pg_notify('vehicle_recovery_notify_channel', FORMAT('stored#{"PlayerId":%s, "VehicleId":%s, "VehicleClass":"%s", "VehicleName":"%s", "VehicleCustomizationId":"%s", "TimeStored":"%s", "ChassisDurability":%s, "bIsMigrated":%s}', player_id, in_vehicle_id, vehicle_class, vehicle_name, in_customization_id, NOW() AT TIME ZONE 'UTC', in_chassis_durability, CASE WHEN in_is_migration THEN 'true' ELSE 'false' END) );
END;
$function$
-- store_recovered_vehicles_wiped_before_spawn(in_vehicle_ids bigint[], in_delete_items boolean) -> void
-- oid: 58600 kind: FUNCTION category: vehicle
CREATE OR REPLACE FUNCTION dune.store_recovered_vehicles_wiped_before_spawn(in_vehicle_ids bigint[], in_delete_items boolean DEFAULT true)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
to_store RECORD;
BEGIN
FOR to_store IN
SELECT v.id AS vehicle_id, a.properties -> (regexp_replace("class", '^.*\.', '')) ->> 'm_CustomizationId' AS customization_id
FROM actors a
INNER JOIN vehicles v ON a.id = v.id
INNER JOIN permission_actor p ON a.id = p.actor_id
INNER JOIN permission_actor_rank r ON p.actor_id = r.permission_actor_id
WHERE v.id = ANY(in_vehicle_ids)
AND r.rank = 1::smallint
AND NOT EXISTS
(
SELECT 1 FROM actor_state
WHERE actor_state.actor_id = v.id
AND actor_state.state IS DISTINCT FROM 'Default'
)
LOOP
-- remove all items in all inventories of that vehicle
if in_delete_items then
PERFORM delete_items_from_actor(to_store.vehicle_id);
end if;
-- note: storing hardcoded chassis durability because its not available from pure database :(
-- this will only be shown incorrectly in UI though, the spawned vehicle will get the correct value
PERFORM store_recovered_vehicle(to_store.vehicle_id, 0.25, to_store.customization_id);
-- remove permissions so the stored vehicles don't count towards the vehicle limit (the owner is preserved in the store function above)
PERFORM permission_actor_destroy(to_store.vehicle_id);
END LOOP;
END
$function$
-- takeover_account(in_user_to_takeover text, in_current_user text) -> void
-- oid: 58601 kind: FUNCTION category: takeover
CREATE OR REPLACE FUNCTION dune.takeover_account(in_user_to_takeover text, in_current_user text)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
current_funcom_id ByteA;
current_account_id BigInt;
takeover_funcom_id ByteA;
takeover_account_id BigInt;
BEGIN
select "encrypted_funcom_id", "id" into current_funcom_id, current_account_id
from encrypted_accounts WHERE "user"=in_current_user;
select "encrypted_funcom_id", "id" into takeover_funcom_id, takeover_account_id
from encrypted_accounts WHERE "user"=in_user_to_takeover;
-- Account swap
UPDATE encrypted_accounts SET "user"=in_current_user || 'TempTakeover' WHERE "id"=current_account_id;
UPDATE encrypted_accounts SET "user"=in_current_user, "encrypted_funcom_id"=current_funcom_id WHERE "id"=takeover_account_id;
UPDATE encrypted_accounts SET "user"=in_user_to_takeover, "encrypted_funcom_id"=takeover_funcom_id WHERE "id"=current_account_id;
END
$function$
-- taxation_emit_invoices(new_tax_invoices dune.taxinvoicedata[]) -> void
-- oid: 58602 kind: FUNCTION category: taxation
CREATE OR REPLACE FUNCTION dune.taxation_emit_invoices(new_tax_invoices dune.taxinvoicedata[])
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
invoice_ids BIGINT[];
new_invoice_id BIGINT;
new_tax_invoice TaxInvoiceData;
BEGIN
IF array_length(new_tax_invoices, 1) > 0 THEN
FOREACH new_tax_invoice IN ARRAY new_tax_invoices LOOP
INSERT INTO tax_invoice("totem_id", "reference_timespan", "invoice_status", "amount")
VALUES(new_tax_invoice.totem_id, new_tax_invoice.reference_timespan, new_tax_invoice.invoice_status, new_tax_invoice.amount) RETURNING id INTO new_invoice_id;
invoice_ids := array_append(invoice_ids, new_invoice_id);
END LOOP;
PERFORM pg_notify('taxation_notify_channel', format('emit_invoices|{"InvoiceIds" : %s, "InvoiceData" : %s}', to_json(invoice_ids)::text, to_json(new_tax_invoices)::text));
END IF;
END
$function$
-- taxation_get_all_invoices_for_player(in_player_id bigint) -> TABLE(id bigint, totem_id bigint, reference_timestamp bigint, invoice_status smallint, amount integer, actor_name text)
-- oid: 58603 kind: FUNCTION category: taxation
CREATE OR REPLACE FUNCTION dune.taxation_get_all_invoices_for_player(in_player_id bigint)
RETURNS TABLE(id bigint, totem_id bigint, reference_timestamp bigint, invoice_status smallint, amount integer, actor_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT tax_invoice.id, tax_invoice.totem_id, tax_invoice.reference_timespan, tax_invoice.invoice_status, tax_invoice.amount, permission_actor.actor_name
FROM tax_invoice
JOIN permission_actor ON permission_actor.actor_id = tax_invoice.totem_id
JOIN permission_actor_rank ON permission_actor.actor_id = permission_actor_rank.permission_actor_id
WHERE permission_actor_rank.player_id = in_player_id
ORDER BY tax_invoice.reference_timespan;
END
$function$
-- taxation_get_all_invoices_for_server(map_name text, in_dimension_index integer, in_partition_id bigint) -> TABLE(id bigint, totem_id bigint, reference_timestamp bigint, invoice_status smallint, amount integer, actor_name text)
-- oid: 58604 kind: FUNCTION category: taxation
CREATE OR REPLACE FUNCTION dune.taxation_get_all_invoices_for_server(map_name text, in_dimension_index integer, in_partition_id bigint)
RETURNS TABLE(id bigint, totem_id bigint, reference_timestamp bigint, invoice_status smallint, amount integer, actor_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT tax_invoice.id, actors.id, tax_invoice.reference_timespan, tax_invoice.invoice_status, tax_invoice.amount, permission_actor.actor_name
FROM tax_invoice
JOIN permission_actor ON permission_actor.actor_id = tax_invoice.totem_id
JOIN actors ON actors.id = tax_invoice.totem_id
WHERE actors.map = map_name
AND actors.dimension_index = in_dimension_index
AND ((in_partition_id = 0 AND actors.partition_id IS NULL) OR actors.partition_id = in_partition_id)
ORDER BY tax_invoice.reference_timespan;
END
$function$
-- taxation_get_all_invoices_for_totem(in_totem_id bigint) -> TABLE(id bigint, totem_id bigint, reference_timestamp bigint, invoice_status smallint, amount integer, actor_name text)
-- oid: 58605 kind: FUNCTION category: taxation
CREATE OR REPLACE FUNCTION dune.taxation_get_all_invoices_for_totem(in_totem_id bigint)
RETURNS TABLE(id bigint, totem_id bigint, reference_timestamp bigint, invoice_status smallint, amount integer, actor_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY SELECT tax_invoice.id, actors.id, tax_invoice.reference_timespan, tax_invoice.invoice_status, tax_invoice.amount, permission_actor.actor_name
FROM tax_invoice
JOIN permission_actor ON permission_actor.actor_id = tax_invoice.totem_id
JOIN actors ON actors.id = tax_invoice.totem_id
WHERE actors.id = in_totem_id
ORDER BY tax_invoice.reference_timespan;
END
$function$
-- taxation_pay_invoice(invoice_id bigint, paid_invoice_status smallint) -> bigint
-- oid: 58606 kind: FUNCTION category: taxation
CREATE OR REPLACE FUNCTION dune.taxation_pay_invoice(invoice_id bigint, paid_invoice_status smallint)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
found_invoice_id BIGINT;
BEGIN
UPDATE tax_invoice SET invoice_status = paid_invoice_status WHERE tax_invoice.id = invoice_id AND tax_invoice.invoice_status != paid_invoice_status RETURNING id INTO found_invoice_id;
if found_invoice_id IS NOT NULL THEN
PERFORM pg_notify('taxation_notify_channel', format('pay_invoice|{"InvoiceId" : %s }', invoice_id));
END IF;
RETURN found_invoice_id;
END
$function$
-- taxation_remove_invoices(invoices_to_remove bigint[]) -> void
-- oid: 58607 kind: FUNCTION category: taxation
CREATE OR REPLACE FUNCTION dune.taxation_remove_invoices(invoices_to_remove bigint[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM tax_invoice WHERE id = ANY(invoices_to_remove);
PERFORM pg_notify('taxation_notify_channel', format('remove_invoice|{"InvoiceIds" : %s}', to_json(invoices_to_remove)::text));
END
$function$
-- taxation_remove_invoices_from_totem(totem_actor_id bigint) -> void
-- oid: 58608 kind: FUNCTION category: taxation
CREATE OR REPLACE FUNCTION dune.taxation_remove_invoices_from_totem(totem_actor_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
invoice_ids BIGINT[];
BEGIN
SELECT array_agg(id) from tax_invoice into invoice_ids WHERE totem_id = totem_actor_id;
DELETE FROM tax_invoice WHERE totem_id = totem_actor_id;
PERFORM pg_notify('taxation_notify_channel', format('remove_invoice|{"InvoiceIds" : %s}', to_json(invoice_ids)::text));
END
$function$
-- taxation_update_invoice_status(invoices_to_overdue bigint[], invoices_to_defaulted bigint[], overdue_invoice_status smallint, defaulted_invoice_status smallint) -> void
-- oid: 58609 kind: FUNCTION category: taxation
CREATE OR REPLACE FUNCTION dune.taxation_update_invoice_status(invoices_to_overdue bigint[], invoices_to_defaulted bigint[], overdue_invoice_status smallint, defaulted_invoice_status smallint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
IF array_length(invoices_to_overdue, 1) > 0 THEN
UPDATE tax_invoice SET invoice_status = overdue_invoice_status WHERE id = ANY(invoices_to_overdue);
PERFORM pg_notify('taxation_notify_channel', format('update_invoice_status|{"InvoiceStatus" : %s, "InvoiceIds" : %s}', overdue_invoice_status, to_json(invoices_to_overdue)::text));
END IF;
IF array_length(invoices_to_defaulted, 1) > 0 THEN
UPDATE tax_invoice SET invoice_status = defaulted_invoice_status WHERE id = ANY(invoices_to_defaulted);
PERFORM pg_notify('taxation_notify_channel', format('update_invoice_status|{"InvoiceStatus" : %s, "InvoiceIds" : %s}', defaulted_invoice_status, to_json(invoices_to_defaulted)::text));
END IF;
END
$function$
-- try_prime_spicefield(in_source_server_id text, in_spicefield_id integer) -> boolean
-- oid: 58610 kind: FUNCTION category: spice_field
CREATE OR REPLACE FUNCTION dune.try_prime_spicefield(in_source_server_id text, in_spicefield_id integer)
RETURNS boolean
LANGUAGE plpgsql
AS $function$
begin
perform spicefield_type_id from spicefield_types as t
where t.spicefield_type_id = in_spicefield_id and t.is_spawning_active is true and t.current_globally_primed < t.max_globally_primed and t.current_globally_active < t.max_globally_active;
if not found then
return false;
end if;
update spicefield_server_availability
set inactive_fields_of_type = inactive_fields_of_type - 1
where server_id = in_source_server_id and spicefield_type_id = in_spicefield_id;
update spicefield_types
set current_globally_primed = current_globally_primed + 1
where spicefield_type_id = in_spicefield_id;
return true;
commit;
end $function$
-- try_restart_spicefield(in_server_id text, in_spicefield_type_id integer) -> boolean
-- oid: 58611 kind: FUNCTION category: spice_field
CREATE OR REPLACE FUNCTION dune.try_restart_spicefield(in_server_id text, in_spicefield_type_id integer)
RETURNS boolean
LANGUAGE plpgsql
AS $function$
BEGIN
PERFORM spicefield_type_id
FROM spicefield_types AS t
WHERE t.spicefield_type_id = in_spicefield_type_id AND t.is_spawning_active IS TRUE;
IF NOT FOUND THEN
RETURN FALSE;
END IF;
UPDATE spicefield_server_availability
SET inactive_fields_of_type = inactive_fields_of_type - 1
WHERE server_id = in_server_id AND spicefield_type_id = in_spicefield_type_id;
UPDATE spicefield_types
SET current_globally_active = current_globally_active + 1
WHERE spicefield_type_id = in_spicefield_type_id;
RETURN TRUE;
END
$function$
-- try_spawn_spicefield(in_source_server_id text, in_spicefield_id integer) -> boolean
-- oid: 58612 kind: FUNCTION category: spice_field
CREATE OR REPLACE FUNCTION dune.try_spawn_spicefield(in_source_server_id text, in_spicefield_id integer)
RETURNS boolean
LANGUAGE plpgsql
AS $function$
begin
perform spicefield_type_id from spicefield_types as t
where t.spicefield_type_id = in_spicefield_id and t.is_spawning_active is true and t.current_globally_active < t.max_globally_active;
if not found then
return false;
end if;
update spicefield_server_availability
set requested_spawned_of_type = requested_spawned_of_type - 1
where server_id = in_source_server_id and spicefield_type_id = in_spicefield_id;
update spicefield_types
set current_globally_active = current_globally_active + 1, current_globally_primed = current_globally_primed -1
where spicefield_type_id = in_spicefield_id;
return true;
commit;
end $function$
-- try_update_exchange_categories_hash(in_new_hash integer) -> TABLE(item_template_id text, mask integer, depth smallint)
-- oid: 58613 kind: FUNCTION category: exchange
CREATE OR REPLACE FUNCTION dune.try_update_exchange_categories_hash(in_new_hash integer)
RETURNS TABLE(item_template_id text, mask integer, depth smallint)
LANGUAGE plpgsql
AS $function$
DECLARE
BEGIN
IF NOT EXISTS (SELECT * FROM dune_exchange_categories_hash) FOR UPDATE THEN
INSERT INTO dune_exchange_categories_hash(id, hash) VALUES(1, in_new_hash);
RETURN;
END IF;
IF NOT in_new_hash IN (SELECT hash FROM dune_exchange_categories_hash FOR SHARE) THEN
UPDATE dune_exchange_categories_hash SET hash = in_new_hash WHERE dune_exchange_categories_hash.id = 1;
RETURN QUERY SELECT DISTINCT template_id, category_mask, category_depth FROM dune_exchange_orders;
END IF;
RETURN;
END
$function$
-- unassign_partition(in_server_id text) -> boolean
-- oid: 58614 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.unassign_partition(in_server_id text)
RETURNS boolean
LANGUAGE plpgsql
AS $function$
DECLARE
v_affected_rows BigInt;
BEGIN
UPDATE world_partition SET server_id = null WHERE server_id = in_server_id;
get diagnostics v_affected_rows = ROW_COUNT;
-- If we didn't actually unassign anything, we have no need to trigger the notification (as nothing changed)
if v_affected_rows > 0 then
NOTIFY world_partition_update;
return true;
end if;
return false;
END $function$
-- update_communinet_player_channel(in_account_id bigint, in_channel_name text, in_is_tuned boolean) -> void
-- oid: 58615 kind: FUNCTION category: communinet
CREATE OR REPLACE FUNCTION dune.update_communinet_player_channel(in_account_id bigint, in_channel_name text, in_is_tuned boolean)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO communinet_player_channels(account_id, channel_name, is_tuned) VALUES (in_account_id, in_channel_name, in_is_tuned)
ON CONFLICT(account_id, channel_name) DO UPDATE SET is_tuned = in_is_tuned WHERE communinet_player_channels.account_id = in_account_id AND communinet_player_channels.channel_name = in_channel_name;
END $function$
-- update_communinet_player_data(in_account_id bigint, in_is_active boolean, in_selected_channel_name text) -> void
-- oid: 58616 kind: FUNCTION category: communinet
CREATE OR REPLACE FUNCTION dune.update_communinet_player_data(in_account_id bigint, in_is_active boolean, in_selected_channel_name text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO communinet_player(account_id, is_active, selected_channel_name) VALUES (in_account_id, in_is_active, in_selected_channel_name)
ON CONFLICT(account_id) DO UPDATE SET is_active = in_is_active, selected_channel_name = in_selected_channel_name WHERE communinet_player.account_id = in_account_id;
END $function$
-- update_consumed_per_player_lore(in_actor_id bigint, in_consumed_bit_array bit, in_use_temporary boolean) -> void
-- oid: 58617 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.update_consumed_per_player_lore(in_actor_id bigint, in_consumed_bit_array bit, in_use_temporary boolean)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
IF in_use_temporary THEN
INSERT INTO consumed_temporary_per_player_lore(actor_id, consumed_bit_array)
VALUES(in_actor_id, in_consumed_bit_array)
ON CONFLICT (actor_id)
DO UPDATE SET
consumed_bit_array = in_consumed_bit_array;
ELSE
INSERT INTO consumed_per_player_lore(actor_id, consumed_bit_array)
VALUES(in_actor_id, in_consumed_bit_array)
ON CONFLICT (actor_id)
DO UPDATE SET
consumed_bit_array = in_consumed_bit_array;
END IF;
END; $function$
-- update_coriolis_for_player(in_controller_id bigint, OUT out_was_coriolis_processed boolean) -> boolean
-- oid: 58618 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.update_coriolis_for_player(in_controller_id bigint, OUT out_was_coriolis_processed boolean)
RETURNS boolean
LANGUAGE plpgsql
AS $function$
begin
SELECT is_coriolis_processed INTO out_was_coriolis_processed
FROM player_state WHERE player_controller_id = in_controller_id;
UPDATE player_state SET is_coriolis_processed = TRUE WHERE player_controller_id = in_controller_id;
end
$function$
-- update_death_location(in_pawn dune.actordescription, in_server_info dune.serverinfo, in_life_state dune.playerlifestate) -> void
-- oid: 58619 kind: FUNCTION category: player_persistence
CREATE OR REPLACE FUNCTION dune.update_death_location(in_pawn dune.actordescription, in_server_info dune.serverinfo, in_life_state dune.playerlifestate)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
IF in_life_state IS NOT NULL THEN
UPDATE encrypted_player_state
SET
life_state = in_life_state,
death_location = CASE
WHEN in_life_state != 'Alive'::PlayerLifeState THEN ((in_pawn).transform.location, (in_server_info).map, (in_server_info).dimension_index)::DeathLocation
ELSE null
END
WHERE player_pawn_id = in_pawn.id;
END IF;
END;
$function$
-- update_farm_state(in_server_id text, in_outgoing_s2s_connections integer, in_incoming_s2s_connections integer, in_connected_players integer, in_farm_id text, in_igw_addr inet, in_igw_port integer, in_ready boolean, in_alive boolean, in_game_addr inet, in_game_port integer, in_map text, in_revision integer) -> void
-- oid: 58620 kind: FUNCTION category: farm
CREATE OR REPLACE FUNCTION dune.update_farm_state(in_server_id text, in_outgoing_s2s_connections integer, in_incoming_s2s_connections integer, in_connected_players integer, in_farm_id text, in_igw_addr inet, in_igw_port integer, in_ready boolean, in_alive boolean, in_game_addr inet, in_game_port integer, in_map text, in_revision integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO farm_state
(server_id, outgoing_s2s_connections, incoming_s2s_connections, connected_players, farm_id, igw_addr, igw_port, ready, alive, game_addr, game_port, map, revision)
VALUES
(in_server_id, in_outgoing_s2s_connections, in_incoming_s2s_connections, in_connected_players, in_farm_id, in_igw_addr, in_igw_port, in_ready, in_alive, in_game_addr, in_game_port, in_map, in_revision)
ON CONFLICT(server_id) DO UPDATE SET
outgoing_s2s_connections=in_outgoing_s2s_connections, incoming_s2s_connections=in_incoming_s2s_connections, connected_players=in_connected_players, farm_id=in_farm_id, igw_addr=in_igw_addr, igw_port=in_igw_port, ready=in_ready, alive=in_alive, game_addr=in_game_addr, game_port=in_game_port, map=in_map, revision=in_revision
WHERE
farm_state.server_id = in_server_id;
END
$function$
-- update_global_spice_field_rules(in_max_globally_primed integer, in_max_globally_active integer, in_spicefield_type_id integer) -> void
-- oid: 58621 kind: FUNCTION category: spice_field
CREATE OR REPLACE FUNCTION dune.update_global_spice_field_rules(in_max_globally_primed integer, in_max_globally_active integer, in_spicefield_type_id integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE spicefield_types SET max_globally_primed = in_max_globally_primed, max_globally_active = in_max_globally_active WHERE spicefield_type_id = in_spicefield_type_id;
END; $function$
-- update_inventories_data(in_inventory_data_list dune.inventorydata[]) -> void
-- oid: 58622 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.update_inventories_data(in_inventory_data_list dune.inventorydata[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE inventories
SET inventory_type = (u).inventory_type, max_item_count = (u).max_item_count, max_item_volume = (u).max_item_volume
FROM (SELECT inventory_id, inventory_type, max_item_count, max_item_volume FROM UNNEST(in_inventory_data_list)) u
WHERE id = (u).inventory_id;
END
$function$
-- update_inventory(in_delete_list bigint[], in_stack_update dune.itemstackupdate[], in_quality_update dune.itemqualityupdate[], in_stat_update dune.itemstatupdate[], in_item_locations dune.inventoryitemlocation[]) -> void
-- oid: 58623 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.update_inventory(in_delete_list bigint[], in_stack_update dune.itemstackupdate[], in_quality_update dune.itemqualityupdate[], in_stat_update dune.itemstatupdate[], in_item_locations dune.inventoryitemlocation[])
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
delete_item_id BIGINT;
BEGIN
-- log item movement
PERFORM _add_item_trace_log('update_inventory_locations', in_item_locations);
-- delete items
PERFORM delete_items(in_delete_list);
-- update item stacks
UPDATE items SET "stack_size" = (u).stack_size FROM (SELECT stack_size, item_id FROM UNNEST(in_stack_update)) u WHERE "id" = (u).item_id;
-- update item quality
UPDATE items SET "quality_level" = (u).quality_level FROM (SELECT quality_level, item_id FROM UNNEST(in_quality_update)) u WHERE "id" = (u).item_id;
-- update stats
UPDATE items SET "stats" = "stats" || (u).value FROM (SELECT value, item_id FROM UNNEST(in_stat_update)) u WHERE "id" = (u).item_id;
UPDATE items
SET inventory_id = (item).inventory_id, position_index = (item).position_index
FROM (SELECT item_id, inventory_id, position_index FROM UNNEST(in_item_locations)) item
WHERE id = (item).item_id;
END
$function$
-- update_item_locations(in_item_locations dune.inventoryitemlocation[]) -> void
-- oid: 58624 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.update_item_locations(in_item_locations dune.inventoryitemlocation[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE items
SET inventory_id = (item).inventory_id, position_index = (item).position_index
FROM (SELECT item_id, inventory_id, position_index FROM UNNEST(in_item_locations)) item
WHERE id = (item).item_id;
-- log item tracking
PERFORM _add_item_trace_log('update_item_locations', in_item_locations);
END
$function$
-- update_journey_story_ids(old_story_ids text[], new_story_ids text[]) -> void
-- oid: 58625 kind: FUNCTION category: journey_progression
CREATE OR REPLACE FUNCTION dune.update_journey_story_ids(old_story_ids text[], new_story_ids text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
index INT;
BEGIN
IF array_length(old_story_ids, 1) != array_length(new_story_ids, 1) THEN
RAISE EXCEPTION 'The length of the array of old IDs does not match the length of the array of new IDs - they must both have the same length';
END IF;
FOR index in 1 .. array_length(old_story_ids, 1) LOOP
UPDATE journey_story_node
SET story_node_id = new_story_ids[index]
WHERE story_node_id = old_story_ids[index];
UPDATE journey_story_node_cooldown
SET story_node_id = new_story_ids[index]
WHERE story_node_id = old_story_ids[index];
END LOOP;
END;
$function$
-- update_marker_ids(in_old_ids integer[], in_new_ids integer[]) -> void
-- oid: 58626 kind: FUNCTION category: markers
CREATE OR REPLACE FUNCTION dune.update_marker_ids(in_old_ids integer[], in_new_ids integer[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE markers SET marker_hash_id = data_table.new_id, marker.z = 0 from (select unnest(in_old_ids) as old_id, unnest(in_new_ids) as new_id) as data_table WHERE markers.marker_hash_id = data_table.old_id;
END; $function$
-- update_partition_labels(in_allow_overwrite boolean) -> void
-- oid: 58627 kind: FUNCTION category: partition
CREATE OR REPLACE FUNCTION dune.update_partition_labels(in_allow_overwrite boolean DEFAULT true)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
-- Compute candidate labels per-partition using the helper and apply them where appropriate
UPDATE world_partition wp
SET label = sub.new_label
FROM (
SELECT partition_id, determine_partition_label(map, dimension_index, label, in_allow_overwrite, partition_id) AS new_label
FROM world_partition
) AS sub
WHERE wp.partition_id = sub.partition_id
AND (wp.label IS NULL OR in_allow_overwrite = true)
AND sub.new_label IS NOT NULL;
-- The default is `MAP_DIMENSION`
-- Only set it for map, dimension combos that have a single partition (label must be unique), and for labels not already touched
UPDATE world_partition SET label = map || '_' || dimension_index
from (
select grouping.partition_ids[1] as partition_id
from (
select count(*) as count, array_agg(partition_id) as partition_ids
from world_partition
group by map, dimension_index
) as grouping
where grouping.count = 1
) as partition
where
world_partition.partition_id = partition.partition_id
and label is null;
END;
$function$
-- update_party_platform_session(in_party_id bigint, in_platform_session_id text, in_platform_name text) -> void
-- oid: 58628 kind: FUNCTION category: party
CREATE OR REPLACE FUNCTION dune.update_party_platform_session(in_party_id bigint, in_platform_session_id text, in_platform_name text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
IF in_platform_session_id IS NULL OR length(in_platform_session_id) = 0 THEN
RAISE EXCEPTION 'in_platform_session_id must not be empty';
END IF;
IF in_platform_name IS NULL OR length(in_platform_name) = 0 THEN
RAISE EXCEPTION 'platform_name must not be empty';
END IF;
INSERT INTO platform_parties_mapping (platform_session_id, platform_name, dune_party_id)
VALUES (in_platform_session_id, in_platform_name, in_party_id)
ON CONFLICT (platform_name, dune_party_id)
DO UPDATE SET platform_session_id = EXCLUDED.platform_session_id;
PERFORM pg_notify('party_notify_channel', format('update_party_platform_id#{"PartyId" : %s, "PlatformSessionId" : "%s", "PlatformName" : "%s"}',
in_party_id, in_platform_session_id, in_platform_name));
END
$function$
-- update_player_tags(in_account_id bigint, tags_to_add text[], tags_to_remove text[]) -> void
-- oid: 58629 kind: FUNCTION category: character_mod
CREATE OR REPLACE FUNCTION dune.update_player_tags(in_account_id bigint, tags_to_add text[], tags_to_remove text[])
RETURNS void
LANGUAGE sql
AS $function$
insert into player_tags("account_id", "tag") select in_account_id, unnest(tags_to_add) on conflict do nothing;
delete from player_tags where account_id = in_account_id and tag = ANY(tags_to_remove);
$function$
-- update_removed_items_and_recipes(items_removed text[], recipes_removed text[]) -> void
-- oid: 58630 kind: FUNCTION category: items_purge
CREATE OR REPLACE FUNCTION dune.update_removed_items_and_recipes(items_removed text[], recipes_removed text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
insert into removed_items (name) select unnest(items_removed);
insert into removed_recipes (name) select unnest(recipes_removed);
end;
$function$
-- update_resourcefield_states(in_map text, in_dimension_index integer, in_field_kind_id smallint, in_field_states dune.resourcefieldstateentry[]) -> void
-- oid: 58631 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.update_resourcefield_states(in_map text, in_dimension_index integer, in_field_kind_id smallint, in_field_states dune.resourcefieldstateentry[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO resourcefield_state(map, dimension_index, field_kind_id, field_id, spawn_time, value_remaining)
SELECT in_map, in_dimension_index, in_field_kind_id, * FROM UNNEST(in_field_states)
ON CONFLICT("field_id", "map", "dimension_index") DO
UPDATE
SET value_remaining = EXCLUDED.value_remaining
WHERE resourcefield_state.field_id = EXCLUDED.field_id AND resourcefield_state.map = in_map AND resourcefield_state.dimension_index = in_dimension_index;
END
$function$
-- update_respawn_locations(player_id bigint, respawn_locations dune.respawnlocation[]) -> void
-- oid: 58632 kind: FUNCTION category: spawner
CREATE OR REPLACE FUNCTION dune.update_respawn_locations(player_id bigint, respawn_locations dune.respawnlocation[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
PERFORM 1
FROM unnest(respawn_locations) AS loc
WHERE
(loc.locator).type != 'Invalid' AND NOT (
(loc.locator).type = 'Transform' AND (loc.locator).transform IS NOT NULL AND (loc.locator).actor_id IS NULL AND (loc.locator).name IS NULL
OR
(loc.locator).type = 'PersistentActor' AND (loc.locator).actor_id IS NOT NULL AND (loc.locator).transform IS NULL AND (loc.locator).name IS NULL
OR
(loc.locator).type = 'StaticLocatorName' AND (loc.locator).name IS NOT NULL AND (loc.locator).transform IS NULL AND (loc.locator).actor_id IS NULL
);
IF FOUND THEN
RAISE EXCEPTION 'Invalid respawn location in input array. Check locator type and associated fields.';
END IF;
WITH
updated_respawn_locations AS (
SELECT
id,
"group",
(locator).transform AS locator_transform,
(locator).actor_id AS locator_actor_id,
(locator).name AS locator_name,
(locator).name_index AS locator_name_index,
map,
dimension,
last_used_timestamp
FROM unnest(respawn_locations) AS updated
),
delete_missing_respawn_locations AS (
DELETE FROM player_respawn_locations AS existing
WHERE NOT EXISTS (
SELECT 1 FROM updated_respawn_locations AS updated WHERE updated.id = existing.id
)
AND existing.account_id = player_id
)
INSERT INTO player_respawn_locations(
"id", "account_id", "group", "locator_transform", "locator_actor_id", "locator_name", "locator_name_index", "map", "dimension", "last_used_timestamp"
)
SELECT
up.id, player_id, up.group, up.locator_transform, up.locator_actor_id, up.locator_name, up.locator_name_index, up.map, up.dimension, up.last_used_timestamp
FROM updated_respawn_locations AS up
WHERE up.locator_actor_id IS NULL OR EXISTS (
SELECT 1 FROM actors WHERE id = up.locator_actor_id
)
ON CONFLICT ("id", "account_id")
DO UPDATE SET
"account_id" = EXCLUDED.account_id,
"group" = EXCLUDED.group,
"locator_transform" = EXCLUDED.locator_transform,
"locator_actor_id" = EXCLUDED.locator_actor_id,
"locator_name" = EXCLUDED.locator_name,
"locator_name_index" = EXCLUDED.locator_name_index,
"map" = EXCLUDED.map,
"dimension" = EXCLUDED.dimension,
"last_used_timestamp" = EXCLUDED.last_used_timestamp;
END;
$function$
-- update_returning_player_status(in_user_id text, in_minimum_returning_player_time_seconds integer) -> void
-- oid: 58633 kind: FUNCTION category: character_mod
CREATE OR REPLACE FUNCTION dune.update_returning_player_status(in_user_id text, in_minimum_returning_player_time_seconds integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
user_account_id BigInt;
last_login_time TIMESTAMPTZ;
last_award_time TIMESTAMPTZ;
BEGIN
SELECT INTO user_account_id, last_login_time, last_award_time id, ps.last_login_time, ps.last_returning_player_awarded_time
FROM accounts acc
JOIN player_state ps ON ps.account_id = acc.id
WHERE acc.user=in_user_id;
IF user_account_id IS NOT NULL THEN
IF last_award_time + INTERVAL '1 second' * in_minimum_returning_player_time_seconds > CURRENT_TIMESTAMP THEN
UPDATE player_state SET last_returning_player_event_time=NULL WHERE account_id=user_account_id;
ELSIF last_login_time + INTERVAL '1 second' * in_minimum_returning_player_time_seconds < CURRENT_TIMESTAMP THEN
UPDATE player_state SET last_returning_player_event_time=now() WHERE account_id=user_account_id;
END IF;
END IF;
END
$function$
-- update_sell_orders_categories(category_update_data dune.exchangecategoryupdatedata[]) -> void
-- oid: 58634 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.update_sell_orders_categories(category_update_data dune.exchangecategoryupdatedata[])
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
BEGIN
UPDATE dune_exchange_orders SET category_mask = update_data.mask, category_depth = update_data.depth
FROM UNNEST(category_update_data) update_data
WHERE update_data.item_template_id = dune_exchange_orders.template_id;
END
$function$
-- update_server_building_favorites(in_account_id bigint, in_building_types text[]) -> void
-- oid: 58635 kind: FUNCTION category: building_blueprint
CREATE OR REPLACE FUNCTION dune.update_server_building_favorites(in_account_id bigint, in_building_types text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO building_favorites(account_id, building_types)
VALUES(in_account_id, in_building_types)
ON CONFLICT(account_id) DO UPDATE SET building_types = in_building_types WHERE building_favorites.account_id = in_account_id;
END; $function$
-- update_server_learned_building_sets(in_account_id bigint, in_learned_building_sets text[]) -> void
-- oid: 58636 kind: FUNCTION category: building_blueprint
CREATE OR REPLACE FUNCTION dune.update_server_learned_building_sets(in_account_id bigint, in_learned_building_sets text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO building_progression(account_id, learned_building_sets)
VALUES(in_account_id, in_learned_building_sets)
ON CONFLICT(account_id) DO UPDATE SET learned_building_sets = in_learned_building_sets WHERE building_progression.account_id = in_account_id;
END; $function$
-- update_server_learned_new_buildable_pieces(in_account_id bigint, in_new_buildable_pieces text[]) -> void
-- oid: 58637 kind: FUNCTION category: server
CREATE OR REPLACE FUNCTION dune.update_server_learned_new_buildable_pieces(in_account_id bigint, in_new_buildable_pieces text[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO building_progression(account_id, new_buildable_pieces)
VALUES(in_account_id, in_new_buildable_pieces)
ON CONFLICT(account_id) DO UPDATE SET new_buildable_pieces = in_new_buildable_pieces WHERE building_progression.account_id = in_account_id;
END; $function$
-- update_specialization_refund_id(in_player_id bigint, in_refund_id smallint, in_removed_keystones smallint[]) -> void
-- oid: 58638 kind: FUNCTION category: character_mod
CREATE OR REPLACE FUNCTION dune.update_specialization_refund_id(in_player_id bigint, in_refund_id smallint, in_removed_keystones smallint[])
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM purchased_specialization_keystones WHERE player_id = in_player_id AND keystone_id = ANY(in_removed_keystones);
INSERT INTO specialization_refund_id (player_id, refund_id) VALUES(in_player_id, in_refund_id)
ON conflict (player_id) DO UPDATE SET refund_id = in_refund_id;
END $function$
-- update_spice_field_spawn_state(in_is_spawning_active boolean, in_spicefield_type_id integer) -> void
-- oid: 58639 kind: FUNCTION category: spice_field
CREATE OR REPLACE FUNCTION dune.update_spice_field_spawn_state(in_is_spawning_active boolean, in_spicefield_type_id integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE spicefield_types SET is_spawning_active = in_is_spawning_active WHERE spicefield_type_id = in_spicefield_type_id;
UPDATE spicefield_server_availability SET requested_spawned_of_type = 0 WHERE spicefield_type_id = in_spicefield_type_id;
END; $function$
-- update_traveling_actor_dependencies(in_dep dune.traveldependency[]) -> void
-- oid: 58640 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.update_traveling_actor_dependencies(in_dep dune.traveldependency[])
RETURNS void
LANGUAGE plpgsql
AS $function$
begin
with valid_ids as (
with d as (select * from unnest(in_dep))
select d.id from d where d.id is not null
union
select d.parent_id from d where d.parent_id is not null
),
valid_ids_plus_dep as (
select * from valid_ids
union
select tap.id from travel_actor_parent as tap where tap.parent_id in (select * from valid_ids)
),
-- remove valid ids and connected dependencies from actor_state
delete_actor_state_ids AS (
delete from actor_state as acs
where acs.actor_id in (select * from valid_ids_plus_dep) and acs.state = 'Travel'
)
-- remove valid ids and connected dependencies from any other dependency tree
delete from travel_actor_parent
where id in (select * from valid_ids_plus_dep);
-- add/update dependencies with valid parent and child ids
with valid_dep as (
select a1.id as id, a2.id as parent_id, d.is_instigator
from unnest(in_dep) d
left join actors as a1
on d.id = a1.id
left join actors as a2
on d.parent_id = a2.id
where
a1.id is not null
and a2.id is not null
)
insert into travel_actor_parent (id, parent_id, is_instigator)
select * from valid_dep
on conflict (id) do update set
"parent_id" = excluded.parent_id,
"is_instigator" = excluded.is_instigator;
end
$function$
-- update_traveling_actor_tree(in_actor_id bigint, in_target_transform dune.transform, in_target_map text, in_target_dimension_index integer, in_target_partition_id bigint) -> TABLE(out_id bigint, out_actor_state text)
-- oid: 58641 kind: FUNCTION category: actors
CREATE OR REPLACE FUNCTION dune.update_traveling_actor_tree(in_actor_id bigint, in_target_transform dune.transform, in_target_map text, in_target_dimension_index integer, in_target_partition_id bigint)
RETURNS TABLE(out_id bigint, out_actor_state text)
LANGUAGE plpgsql
AS $function$
begin
RETURN query WITH
traveling_actor_ids AS (
SELECT t.id FROM get_traveling_actor_ids(in_actor_id) AS t
),
invalid_traveling_actor_ids AS (
SELECT id, actor_state.state::TEXT FROM traveling_actor_ids
INNER JOIN actor_state ON actor_state.actor_id = traveling_actor_ids.id
WHERE actor_state.state != 'Travel'
),
valid_traveling_actor_ids AS (
SELECT id FROM traveling_actor_ids
WHERE NOT EXISTS (SELECT 1 FROM invalid_traveling_actor_ids)
),
insert_actor_state AS (
INSERT INTO actor_state(actor_id, state)
SELECT id, 'Travel' FROM valid_traveling_actor_ids
ON CONFLICT DO NOTHING
),
update_actors AS (
UPDATE actors
SET
transform = in_target_transform,
dimension_index = in_target_dimension_index,
map = in_target_map,
partition_id = in_target_partition_id
FROM valid_traveling_actor_ids
WHERE actors.id = valid_traveling_actor_ids.id
)
SELECT * FROM invalid_traveling_actor_ids;
end
$function$
-- update_universe_time(in_farm_id text) -> TABLE(universe_time_timestamp timestamp without time zone, down_time_accumulation bigint)
-- oid: 58642 kind: FUNCTION category: schema_meta
CREATE OR REPLACE FUNCTION dune.update_universe_time(in_farm_id text DEFAULT NULL::text)
RETURNS TABLE(universe_time_timestamp timestamp without time zone, down_time_accumulation bigint)
LANGUAGE plpgsql
AS $function$
DECLARE
BEGIN
INSERT INTO farm_variables(farm_id, universe_time_timestamp, universe_lastactive_timestamp, down_time_accumulation, one_row)
VALUES (in_farm_id, (CURRENT_TIMESTAMP AT TIME ZONE 'UTC')::TIMESTAMP, (CURRENT_TIMESTAMP AT TIME ZONE 'UTC')::TIMESTAMP, 0, true)
ON CONFLICT(one_row) DO UPDATE
SET
down_time_accumulation = CASE
WHEN farm_variables.farm_id != EXCLUDED.farm_id AND farm_variables.farm_id IS NOT NULL AND EXCLUDED.farm_id IS NOT NULL THEN farm_variables.down_time_accumulation + (EXTRACT(EPOCH FROM ((CURRENT_TIMESTAMP AT TIME ZONE 'UTC')::TIMESTAMP - farm_variables.universe_lastactive_timestamp)) * 1000000)::BIGINT
ELSE farm_variables.down_time_accumulation
END,
universe_lastactive_timestamp = (CURRENT_TIMESTAMP AT TIME ZONE 'UTC')::TIMESTAMP,
farm_id = CASE
WHEN EXCLUDED.farm_id IS NULL AND farm_variables.farm_id IS NOT NULL THEN farm_variables.farm_id
ELSE EXCLUDED.farm_id
END;
RETURN QUERY select * from get_universe_time();
RETURN;
END
$function$
-- update_vendor_timestamp_for_player(in_vendor_id text, in_player_id bigint, in_timestamp bigint) -> void
-- oid: 58643 kind: FUNCTION category: stock_vendor
CREATE OR REPLACE FUNCTION dune.update_vendor_timestamp_for_player(in_vendor_id text, in_player_id bigint, in_timestamp bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
IF NOT EXISTS
( SELECT * FROM vendor_stock_cycle
WHERE vendor_id = in_vendor_id AND player_id = in_player_id)
THEN
INSERT INTO vendor_stock_cycle(vendor_id, player_id, last_interacted_timestamp) VALUES(in_vendor_id, in_player_id, in_timestamp);
ELSE
UPDATE vendor_stock_cycle
SET last_interacted_timestamp = in_timestamp
WHERE vendor_id = in_vendor_id AND player_id = in_player_id;
END IF;
END
$function$
-- upgrade_location_data_list(in_location_data_list jsonb, in_map_field_name text) -> jsonb
-- oid: 58644 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.upgrade_location_data_list(in_location_data_list jsonb, in_map_field_name text)
RETURNS jsonb
LANGUAGE plpgsql
AS $function$
BEGIN
return COALESCE(
(
SELECT jsonb_agg(
CASE
WHEN elem ? in_map_field_name
THEN
jsonb_set(
elem,
('{' || in_map_field_name ||'}')::Text[],
upgrade_map_value(elem->in_map_field_name)
)
ELSE
elem
END
)
FROM jsonb_array_elements(in_location_data_list) elem
),
'[]'::jsonb
);
END
$function$
-- upgrade_map_name(in_map_name text) -> text
-- oid: 58645 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.upgrade_map_name(in_map_name text)
RETURNS text
LANGUAGE plpgsql
AS $function$
BEGIN
return
CASE in_map_name
WHEN 'Survival_1' THEN 'HaggaBasin'
WHEN 'SH_HarkoVillage' THEN 'HarkoVillage'
WHEN 'DeepDesert_1' THEN 'DeepDesert'
WHEN 'Overmap' THEN 'Overland'
WHEN 'SH_Arrakeen' THEN 'Arrakeen'
WHEN 'CB_Story_Hephaestus' THEN 'WreckOfHephaestus'
WHEN 'CB_Story_Ecolab_Carthag' THEN 'BeneathCarthag'
WHEN 'CB_SurvivalChallenge_Station_15' THEN 'Station15'
WHEN 'CB_Story_WaterFatManor' THEN 'WaterFat'
WHEN 'SH_FallenLight' THEN 'FallenLight'
WHEN 'Story_ProcesVerbal' THEN 'ProcesVerbal'
WHEN 'DLC_Story_LostHarvest' THEN 'LostHarvest'
WHEN 'DLC_Story_LostHarvest_EcolabA' THEN 'LostHarvest_EcolabA'
WHEN 'DLC_Story_LostHarvest_EcolabB' THEN 'LostHarvest_EcolabB'
WHEN 'DLC_Story_LostHarvest_ForgottenLab' THEN 'LostHarvest_ForgottenLab'
WHEN 'Story_ArtOfKanly' THEN 'ArtOfKanly'
WHEN 'Story_HeighlinerDungeon' THEN 'HeighlinerDungeon'
WHEN 'CB_Dungeon_Hephaestus' THEN 'WreckOfHephaestusDungeon'
WHEN 'CB_Dungeon_OldCarthag' THEN 'OldCarthagDungeon'
WHEN 'CB_Story_BanditFortress01' THEN 'SandfliesFortress'
WHEN 'CB_Overland_S_05' THEN 'ClosedOffTestingStationIsland'
WHEN 'CB_Overland_S_06' THEN 'GroundVehicleTimeTrialIsland'
WHEN 'CB_Overland_S_04' THEN 'ErythriteCaveIsland'
WHEN 'CB_Overland_M_01' THEN 'RadioactiveShipwreck'
WHEN 'CB_Overland_S_07' THEN 'TheRuinsOfTsimpo'
WHEN 'Story_Faction_Outpost_Hark' THEN 'Story_Faction_Outpost_Hark'
WHEN 'Story_Faction_Outpost_Atre' THEN 'Story_Faction_Outpost_Atre'
WHEN 'CB_Ecolab_Bronze_Green_089' THEN 'RadiationDungeon'
WHEN 'CB_Ecolab_Bronze_Green_152' THEN 'ElectricityDungeon'
WHEN 'CB_Ecolab_Bronze_Green_195' THEN 'PoisonDungeon'
WHEN 'CB_Ecolab_Bronze_Green_024' THEN 'DarknessDungeon'
WHEN 'CB_Ecolab_Bronze_Green_136' THEN 'FireDungeon'
WHEN 'CB_Story_DestroyedZanovar' THEN 'DestroyedZanovar'
WHEN 'CB_Story_OrbitalMonitor' THEN 'OrbitalMonitor'
WHEN 'CB_Dungeon_TheFacility' THEN 'FacilityDungeon'
WHEN 'CB_Dungeon_ThePit' THEN 'PitDungeon'
WHEN 'CB_Overland_S_08' THEN 'WindPass'
ELSE in_map_name
END;
END;
$function$
-- upgrade_map_value(in_value jsonb) -> jsonb
-- oid: 58646 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.upgrade_map_value(in_value jsonb)
RETURNS jsonb
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN
CASE
WHEN jsonb_typeof(in_value) = 'string'
THEN
jsonb_build_object('Name', upgrade_map_name(in_value->>0))
ELSE
in_value
END;
END;
$function$
-- upsert_spicefield_types(in_max_globally_active integer[], in_max_globally_primed integer[], in_field_types text[], in_map_name text, in_dimension_index integer) -> TABLE(type_id integer, max_global integer, max_global_primed integer, spawning_active boolean, out_field_type text)
-- oid: 58647 kind: FUNCTION category: spice_field
CREATE OR REPLACE FUNCTION dune.upsert_spicefield_types(in_max_globally_active integer[], in_max_globally_primed integer[], in_field_types text[], in_map_name text, in_dimension_index integer)
RETURNS TABLE(type_id integer, max_global integer, max_global_primed integer, spawning_active boolean, out_field_type text)
LANGUAGE plpgsql
AS $function$
begin
insert into spicefield_types (max_globally_active, max_globally_primed, field_type, map_name, dimension_index)
select unnest(in_max_globally_active), unnest(in_max_globally_primed), unnest(in_field_types), in_map_name, in_dimension_index
on conflict do nothing;
return query select spicefield_type_id, max_globally_active, max_globally_primed, is_spawning_active, field_type::text from spicefield_types where map_name = in_map_name and dimension_index = in_dimension_index;
end $function$
-- use_sinkchart(in_player_id bigint, in_account_id bigint, in_area_id smallint, in_item_id bigint, in_sinkchart_map_name text, in_player_map_name text, in_player_current_dimension integer) -> dune.usesinkchartreturndata
-- oid: 58648 kind: FUNCTION category: map_areas
CREATE OR REPLACE FUNCTION dune.use_sinkchart(in_player_id bigint, in_account_id bigint, in_area_id smallint, in_item_id bigint, in_sinkchart_map_name text, in_player_map_name text, in_player_current_dimension integer)
RETURNS dune.usesinkchartreturndata
LANGUAGE plpgsql
AS $function$
DECLARE
sinkchart_markers SinkchartMarkerData[];
survey_target JSONB;
BEGIN
-- Update map areas.
INSERT INTO map_areas (account_id, time_first_entered, time_discovered, area_id, items_surveyed_target, map_name)
SELECT in_account_id, NULL, NOW(), in_area_id, map_areas.items_surveyed_target, in_sinkchart_map_name
FROM map_areas
WHERE map_areas.area_id = in_area_id
AND map_areas.map_name = in_sinkchart_map_name
AND map_areas.items_surveyed_target IS NOT NULL
LIMIT 1
ON CONFLICT ON CONSTRAINT map_areas_pkey DO UPDATE
SET items_surveyed_target = EXCLUDED.items_surveyed_target,
time_discovered = CASE WHEN map_areas.time_discovered IS NULL THEN EXCLUDED.time_discovered ELSE map_areas.time_discovered END
RETURNING items_surveyed_target INTO survey_target;
-- Perform the INSERT operation explicitly.
INSERT INTO player_markers (player_id, marker_hash_id, dimension_index, map_name_id, discovery_level, discovery_method, payload)
SELECT DISTINCT ON (m.marker_hash_id, m.dimension_index)
in_player_id, m.marker_hash_id, m.dimension_index, m.map_name_id, /*discovery_level*/ 2, /*discovery_method*/ 12, /*payload*/ '{}'::JSONB
FROM (
SELECT UNNEST(sc.marker_hash_ids) AS marker_hash_id
FROM sinkcharts sc
WHERE sc.item_id = in_item_id
) expanded_ids
JOIN map_names mn
ON mn.map_name = in_sinkchart_map_name
INNER JOIN markers m
ON expanded_ids.marker_hash_id = m.marker_hash_id AND m.map_name_id = mn.map_name_id
ORDER BY m.marker_hash_id, m.dimension_index
ON CONFLICT ON CONSTRAINT player_markers_pkey DO UPDATE
SET discovery_level = GREATEST(player_markers.discovery_level, EXCLUDED.discovery_level);
-- Get markers that should be shown to the player immediately (marker on same map and dimension).
SELECT ARRAY_AGG(ROW(m.marker_hash_id, m.marker, m.area_id, m.area_radius, m.long_range, pm.payload, m.payload)::SinkchartMarkerData)
INTO sinkchart_markers
FROM (
SELECT UNNEST(sc.marker_hash_ids) AS marker_hash_id
FROM sinkcharts sc
WHERE sc.item_id = in_item_id
) expanded_ids
JOIN map_names smn
ON smn.map_name = in_sinkchart_map_name
INNER JOIN markers m
ON expanded_ids.marker_hash_id = m.marker_hash_id AND m.map_name_id = smn.map_name_id
INNER JOIN player_markers pm
ON m.marker_hash_id = pm.marker_hash_id
AND m.dimension_index = pm.dimension_index
AND m.map_name_id = pm.map_name_id
AND pm.player_id = in_player_id
JOIN map_names pmn
ON pmn.map_name = in_player_map_name
WHERE m.map_name_id = pmn.map_name_id
AND (m.dimension_index = -1 OR m.dimension_index = in_player_current_dimension);
RETURN (sinkchart_markers, survey_target);
END;
$function$
-- verify_item_dup_backup_tool(in_account_id bigint, in_vehicle_id bigint, in_cheat_type dune.cheat_type_enum) -> void
-- oid: 58649 kind: FUNCTION category: inventory
CREATE OR REPLACE FUNCTION dune.verify_item_dup_backup_tool(in_account_id bigint, in_vehicle_id bigint, in_cheat_type dune.cheat_type_enum)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
v_FLS_id TEXT;
BEGIN
PERFORM 1 FROM inventories inv JOIN items it ON it.inventory_id = inv.id WHERE inv.actor_id = in_vehicle_id;
IF FOUND THEN
-- Delete all items in this vehicle's inventories (excluding module inventories)
PERFORM delete_items(
(
SELECT array_agg(i.id)
FROM items i
JOIN inventories inv ON inv.id = i.inventory_id
WHERE inv.actor_id = in_vehicle_id
AND inv.inventory_type = 0 -- 0 = vehicle backpack
)
);
-- Debug logging
SELECT acc."user"
INTO v_FLS_id
FROM accounts acc
WHERE acc.id = in_account_id
LIMIT 1;
RAISE WARNING 'Trying to vbt vehicle that has not an empty inventory. Behavior: % Vehicle: %, Account: %, FLS: %', in_cheat_type, in_vehicle_id, in_account_id, v_FLS_id;
-- DB tracking
PERFORM log_cheating(v_FLS_id, in_cheat_type);
END IF;
END;
$function$
-- wipe_old_events_log(in_days_limit integer) -> void
-- oid: 58650 kind: FUNCTION category: cleanup
CREATE OR REPLACE FUNCTION dune.wipe_old_events_log(in_days_limit integer)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
DELETE FROM game_events WHERE universe_time < to_timestamp(in_days_limit);
END
$function$
-- zero_transform() -> dune.transform
-- oid: 58651 kind: FUNCTION category: misc
CREATE OR REPLACE FUNCTION dune.zero_transform()
RETURNS dune.transform
LANGUAGE plpgsql
IMMUTABLE
AS $function$
BEGIN
RETURN (
ROW(
ROW(0.0, 0.0, 0.0)::Vector,
ROW(0.0, 0.0, 0.0, 1.0)::Quaternion
)::Transform
);
END;
$function$