mirror of
https://github.com/wanderer-industries/wanderer
synced 2026-08-25 07:16:31 +00:00
fix(Core): Fixed map character tracking issues
This commit is contained in:
@@ -3,7 +3,8 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
|
||||
use Ash.Resource,
|
||||
domain: WandererApp.Api,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
extensions: [AshCloak]
|
||||
|
||||
@derive {Jason.Encoder,
|
||||
only: [
|
||||
@@ -24,8 +25,10 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
code_interface do
|
||||
define(:create, action: :create)
|
||||
define(:destroy, action: :destroy)
|
||||
define(:update, action: :update)
|
||||
|
||||
define(:read_by_map, action: :read_by_map)
|
||||
define(:read_by_map_and_character, action: :read_by_map_and_character)
|
||||
define(:by_map_filtered, action: :by_map_filtered)
|
||||
define(:tracked_by_map_filtered, action: :tracked_by_map_filtered)
|
||||
define(:tracked_by_character, action: :tracked_by_character)
|
||||
@@ -45,7 +48,7 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
:tracked
|
||||
]
|
||||
|
||||
defaults [:read, :update, :destroy]
|
||||
defaults [:read, :destroy]
|
||||
|
||||
create :create do
|
||||
primary? true
|
||||
@@ -92,6 +95,15 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
filter(expr(map_id == ^arg(:map_id)))
|
||||
end
|
||||
|
||||
read :read_by_map_and_character do
|
||||
get? true
|
||||
|
||||
argument(:map_id, :string, allow_nil?: false)
|
||||
argument(:character_id, :uuid, allow_nil?: false)
|
||||
|
||||
filter(expr(map_id == ^arg(:map_id) and character_id == ^arg(:character_id)))
|
||||
end
|
||||
|
||||
read :tracked_by_map_all do
|
||||
argument(:map_id, :string, allow_nil?: false)
|
||||
filter(expr(map_id == ^arg(:map_id) and tracked == true))
|
||||
@@ -102,6 +114,20 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
filter(expr(character_id == ^arg(:character_id) and tracked == true))
|
||||
end
|
||||
|
||||
update :update do
|
||||
primary? true
|
||||
require_atomic? false
|
||||
|
||||
accept([
|
||||
:ship,
|
||||
:ship_name,
|
||||
:ship_item_id,
|
||||
:solar_system_id,
|
||||
:structure_id,
|
||||
:station_id
|
||||
])
|
||||
end
|
||||
|
||||
update :track do
|
||||
accept [:map_id, :character_id]
|
||||
argument :map_id, :string, allow_nil?: false
|
||||
@@ -159,6 +185,28 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
end
|
||||
end
|
||||
|
||||
cloak do
|
||||
vault(WandererApp.Vault)
|
||||
|
||||
attributes([
|
||||
:ship,
|
||||
:ship_name,
|
||||
:ship_item_id,
|
||||
:solar_system_id,
|
||||
:structure_id,
|
||||
:station_id
|
||||
])
|
||||
|
||||
decrypt_by_default([
|
||||
:ship,
|
||||
:ship_name,
|
||||
:ship_item_id,
|
||||
:solar_system_id,
|
||||
:structure_id,
|
||||
:station_id
|
||||
])
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key :id
|
||||
|
||||
@@ -172,6 +220,13 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
attribute :solar_system_id, :integer
|
||||
attribute :structure_id, :integer
|
||||
attribute :station_id, :integer
|
||||
attribute :ship, :integer
|
||||
attribute :ship_name, :string
|
||||
attribute :ship_item_id, :integer
|
||||
|
||||
create_timestamp(:inserted_at)
|
||||
update_timestamp(:updated_at)
|
||||
end
|
||||
|
||||
@@ -7,6 +7,15 @@ defmodule WandererApp.Character do
|
||||
@read_character_wallet_scope "esi-wallet.read_character_wallet.v1"
|
||||
@read_corp_wallet_scope "esi-wallet.read_corporation_wallets.v1"
|
||||
|
||||
@default_character_tracking_data %{
|
||||
solar_system_id: nil,
|
||||
structure_id: nil,
|
||||
station_id: nil,
|
||||
ship: nil,
|
||||
ship_name: nil,
|
||||
ship_item_id: nil
|
||||
}
|
||||
|
||||
@decorate cacheable(
|
||||
cache: WandererApp.Cache,
|
||||
key: "characters-#{character_eve_id}"
|
||||
@@ -45,6 +54,32 @@ defmodule WandererApp.Character do
|
||||
end
|
||||
end
|
||||
|
||||
def get_map_character(map_id, character_id) do
|
||||
case get_character(character_id) do
|
||||
{:ok, character} ->
|
||||
{:ok,
|
||||
character
|
||||
|> maybe_merge_map_character_settings(
|
||||
map_id,
|
||||
WandererApp.Character.TrackerManager.Impl.character_is_present(map_id, character_id)
|
||||
)}
|
||||
|
||||
_ ->
|
||||
{:ok, nil}
|
||||
end
|
||||
end
|
||||
|
||||
def get_map_character!(map_id, character_id) do
|
||||
case get_map_character(map_id, character_id) do
|
||||
{:ok, character} ->
|
||||
character
|
||||
|
||||
_ ->
|
||||
Logger.error("Failed to get map character #{map_id} #{character_id}")
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def get_character_eve_ids!(character_ids),
|
||||
do:
|
||||
character_ids
|
||||
@@ -146,7 +181,7 @@ defmodule WandererApp.Character do
|
||||
params: opts[:params]
|
||||
) do
|
||||
{:ok, result} ->
|
||||
{:ok, result |> _prepare_search_results()}
|
||||
{:ok, result |> prepare_search_results()}
|
||||
|
||||
{:error, error} ->
|
||||
Logger.warning("#{__MODULE__} failed search: #{inspect(error)}")
|
||||
@@ -208,7 +243,28 @@ defmodule WandererApp.Character do
|
||||
end
|
||||
end
|
||||
|
||||
defp _prepare_search_results(result) do
|
||||
defp maybe_merge_map_character_settings(character, map_id, true), do: character
|
||||
|
||||
defp maybe_merge_map_character_settings(
|
||||
%{id: character_id} = character,
|
||||
map_id,
|
||||
_character_is_present
|
||||
) do
|
||||
WandererApp.MapCharacterSettingsRepo.get(map_id, character_id)
|
||||
|> case do
|
||||
{:ok, settings} when not is_nil(settings) ->
|
||||
character
|
||||
|> Map.put(:online, false)
|
||||
|> Map.merge(settings)
|
||||
|
||||
_ ->
|
||||
character
|
||||
|> Map.put(:online, false)
|
||||
|> Map.merge(@default_character_tracking_data)
|
||||
end
|
||||
end
|
||||
|
||||
defp prepare_search_results(result) do
|
||||
{:ok, characters} =
|
||||
_load_eve_info(Map.get(result, "character"), :get_character_info, &_map_character_info/1)
|
||||
|
||||
|
||||
@@ -220,6 +220,18 @@ defmodule WandererApp.Character.TrackerManager.Impl do
|
||||
track: false
|
||||
})
|
||||
|
||||
{:ok, character} = WandererApp.Character.get_character(character_id)
|
||||
|
||||
{:ok, _updated} =
|
||||
WandererApp.MapCharacterSettingsRepo.update(map_id, character_id, %{
|
||||
ship: character.ship,
|
||||
ship_name: character.ship_name,
|
||||
ship_item_id: character.ship_item_id,
|
||||
solar_system_id: character.solar_system_id,
|
||||
structure_id: character.structure_id,
|
||||
station_id: character.station_id
|
||||
})
|
||||
|
||||
WandererApp.Character.update_character_state(character_id, character_state)
|
||||
end
|
||||
end,
|
||||
@@ -246,7 +258,7 @@ defmodule WandererApp.Character.TrackerManager.Impl do
|
||||
def handle_info(_event, state),
|
||||
do: state
|
||||
|
||||
defp character_is_present(map_id, character_id) do
|
||||
def character_is_present(map_id, character_id) do
|
||||
{:ok, presence_character_ids} =
|
||||
WandererApp.Cache.lookup("map_#{map_id}:presence_character_ids", [])
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ defmodule WandererApp.Map do
|
||||
map_id
|
||||
|> get_map!()
|
||||
|> Map.get(:characters, [])
|
||||
|> Enum.map(&WandererApp.Character.get_character!(&1))
|
||||
|> Enum.map(fn character_id -> WandererApp.Character.get_map_character!(map_id, character_id) end)
|
||||
|
||||
def list_systems(map_id),
|
||||
do: {:ok, map_id |> get_map!() |> Map.get(:systems, Map.new()) |> Map.values()}
|
||||
|
||||
@@ -17,13 +17,13 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
map_id: map_id,
|
||||
tracked: track_character
|
||||
}),
|
||||
{:ok, character} <- WandererApp.Character.get_character(character_id) do
|
||||
{:ok, character} <- WandererApp.Character.get_map_character(map_id, character_id) do
|
||||
Impl.broadcast!(map_id, :character_added, character)
|
||||
:telemetry.execute([:wanderer_app, :map, :character, :added], %{count: 1})
|
||||
:ok
|
||||
else
|
||||
_error ->
|
||||
{:ok, character} = WandererApp.Character.get_character(character_id)
|
||||
{:ok, character} = WandererApp.Character.get_map_character(map_id, character_id)
|
||||
Impl.broadcast!(map_id, :character_added, character)
|
||||
:ok
|
||||
end
|
||||
@@ -35,7 +35,7 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
def remove_character(map_id, character_id) do
|
||||
Task.start_link(fn ->
|
||||
with :ok <- WandererApp.Map.remove_character(map_id, character_id),
|
||||
{:ok, character} <- WandererApp.Character.get_character(character_id) do
|
||||
{:ok, character} <- WandererApp.Character.get_map_character(map_id, character_id) do
|
||||
Impl.broadcast!(map_id, :character_removed, character)
|
||||
|
||||
:telemetry.execute([:wanderer_app, :map, :character, :removed], %{count: 1})
|
||||
@@ -90,17 +90,26 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
do:
|
||||
character_ids
|
||||
|> Enum.each(fn character_id ->
|
||||
# TODO consider storing character data in MapCharacterSettings
|
||||
# remove_character(map_id, character_id)
|
||||
if is_character_map_active?(map_id, character_id) do
|
||||
WandererApp.Character.TrackerManager.update_track_settings(character_id, %{
|
||||
map_id: map_id,
|
||||
track: false
|
||||
})
|
||||
|
||||
WandererApp.Character.TrackerManager.update_track_settings(character_id, %{
|
||||
map_id: map_id,
|
||||
track: false
|
||||
})
|
||||
|
||||
Impl.broadcast!(map_id, :untrack_character, character_id)
|
||||
Impl.broadcast!(map_id, :untrack_character, character_id)
|
||||
end
|
||||
end)
|
||||
|
||||
def is_character_map_active?(map_id, character_id) do
|
||||
case WandererApp.Character.get_character_state(character_id) do
|
||||
{:ok, %{active_maps: active_maps}} ->
|
||||
map_id in active_maps
|
||||
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def cleanup_characters(map_id, owner_id) do
|
||||
{:ok, invalidate_character_ids} =
|
||||
WandererApp.Cache.lookup(
|
||||
@@ -276,7 +285,7 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
end
|
||||
|
||||
defp update_character(map_id, character_id) do
|
||||
{:ok, character} = WandererApp.Character.get_character(character_id)
|
||||
{:ok, character} = WandererApp.Character.get_map_character(map_id, character_id)
|
||||
Impl.broadcast!(map_id, :character_updated, character)
|
||||
end
|
||||
|
||||
|
||||
@@ -451,6 +451,14 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
|> WandererApp.Map.update_subscription_settings!(subscription_settings)
|
||||
|> WandererApp.Map.add_systems!(systems)
|
||||
|> WandererApp.Map.add_connections!(connections)
|
||||
|> WandererApp.Map.add_characters!(characters)
|
||||
|
||||
character_ids =
|
||||
map_id
|
||||
|> WandererApp.Map.get_map!()
|
||||
|> Map.get(:characters, [])
|
||||
|
||||
WandererApp.Cache.insert("map_#{map_id}:invalidate_character_ids", character_ids)
|
||||
|
||||
%{state | map: map, map_opts: map_options(options)}
|
||||
end
|
||||
|
||||
@@ -1,10 +1,34 @@
|
||||
defmodule WandererApp.MapCharacterSettingsRepo do
|
||||
use WandererApp, :repository
|
||||
|
||||
def get(map_id, character_id) do
|
||||
case WandererApp.Api.MapCharacterSettings.read_by_map_and_character(%{
|
||||
map_id: map_id,
|
||||
character_id: character_id
|
||||
}) do
|
||||
{:ok, settings} ->
|
||||
{:ok, settings}
|
||||
|
||||
{:error, reason} ->
|
||||
{:ok, nil}
|
||||
end
|
||||
end
|
||||
|
||||
def create(settings) do
|
||||
WandererApp.Api.MapCharacterSettings.create(settings)
|
||||
end
|
||||
|
||||
def update(map_id, character_id, updated_settings) do
|
||||
case get(map_id, character_id) do
|
||||
{:ok, settings} when not is_nil(settings) ->
|
||||
settings
|
||||
|> WandererApp.Api.MapCharacterSettings.update(updated_settings)
|
||||
|
||||
{:error, reason} ->
|
||||
{:ok, nil}
|
||||
end
|
||||
end
|
||||
|
||||
def get_tracked_by_map_filtered(map_id, character_ids),
|
||||
do:
|
||||
WandererApp.Api.MapCharacterSettings.tracked_by_map_filtered(%{
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
defmodule WandererApp.Repo.Migrations.AddMapCharacterTrackingInfo do
|
||||
@moduledoc """
|
||||
Updates resources based on their most recent snapshots.
|
||||
|
||||
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
||||
"""
|
||||
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
alter table(:map_character_settings_v1) do
|
||||
add :encrypted_ship, :binary
|
||||
add :encrypted_ship_name, :binary
|
||||
add :encrypted_ship_item_id, :binary
|
||||
add :encrypted_solar_system_id, :binary
|
||||
add :encrypted_structure_id, :binary
|
||||
add :encrypted_station_id, :binary
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:map_character_settings_v1) do
|
||||
remove :encrypted_station_id
|
||||
remove :encrypted_structure_id
|
||||
remove :encrypted_solar_system_id
|
||||
remove :encrypted_ship_item_id
|
||||
remove :encrypted_ship_name
|
||||
remove :encrypted_ship
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,206 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "tracked",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "followed",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "map_character_settings_v1_map_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "maps_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "map_id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "map_character_settings_v1_character_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "character_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "character_id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_ship",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_ship_name",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_ship_item_id",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_solar_system_id",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_structure_id",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_station_id",
|
||||
"type": "binary"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "3F585D1C545A5264AEFA05502C0F625F9B27B15CA36699DCF37E4F834E6339AE",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "map_character_settings_v1_uniq_map_character_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "map_id"
|
||||
},
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "character_id"
|
||||
}
|
||||
],
|
||||
"name": "uniq_map_character",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "map_character_settings_v1"
|
||||
}
|
||||
Reference in New Issue
Block a user