mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-11-03 07:57:05 +00:00
458 lines
13 KiB
Elixir
458 lines
13 KiB
Elixir
defmodule WandererApp.Map.Server.CharactersImpl do
|
|
@moduledoc false
|
|
|
|
require Logger
|
|
|
|
alias WandererApp.Map.Server.{Impl, ConnectionsImpl, SystemsImpl}
|
|
|
|
def get_characters(%{map_id: map_id} = _state),
|
|
do: {:ok, map_id |> WandererApp.Map.list_characters()}
|
|
|
|
def add_character(%{map_id: map_id} = state, %{id: character_id} = character, track_character) do
|
|
Task.start_link(fn ->
|
|
with :ok <- map_id |> WandererApp.Map.add_character(character),
|
|
{:ok, _} <-
|
|
WandererApp.MapCharacterSettingsRepo.create(%{
|
|
character_id: character_id,
|
|
map_id: map_id,
|
|
tracked: track_character,
|
|
followed: false
|
|
}),
|
|
{:ok, character} <- WandererApp.Character.get_character(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)
|
|
Impl.broadcast!(map_id, :character_added, character)
|
|
:ok
|
|
end
|
|
end)
|
|
|
|
state
|
|
end
|
|
|
|
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
|
|
Impl.broadcast!(map_id, :character_removed, character)
|
|
|
|
:telemetry.execute([:wanderer_app, :map, :character, :removed], %{count: 1})
|
|
|
|
:ok
|
|
else
|
|
{:error, _error} ->
|
|
:ok
|
|
end
|
|
end)
|
|
end
|
|
|
|
def update_tracked_characters(map_id) do
|
|
Task.start_link(fn ->
|
|
{:ok, map_tracked_character_ids} =
|
|
map_id
|
|
|> WandererApp.MapCharacterSettingsRepo.get_tracked_by_map_all()
|
|
|> case do
|
|
{:ok, settings} -> {:ok, settings |> Enum.map(&Map.get(&1, :character_id))}
|
|
_ -> {:ok, []}
|
|
end
|
|
|
|
{:ok, tracked_characters} = WandererApp.Cache.lookup("tracked_characters", [])
|
|
|
|
map_active_tracked_characters =
|
|
map_tracked_character_ids
|
|
|> Enum.filter(fn character -> character in tracked_characters end)
|
|
|
|
WandererApp.Cache.insert("maps:#{map_id}:tracked_characters", map_active_tracked_characters)
|
|
|
|
:ok
|
|
end)
|
|
end
|
|
|
|
def untrack_characters(map_id, character_ids),
|
|
do:
|
|
character_ids
|
|
|> Enum.each(fn character_id ->
|
|
WandererApp.Character.TrackerManager.update_track_settings(character_id, %{
|
|
map_id: map_id,
|
|
track: false
|
|
})
|
|
end)
|
|
|
|
def cleanup_characters(map_id, owner_id) do
|
|
{:ok, invalidate_character_ids} =
|
|
WandererApp.Cache.lookup(
|
|
"map_#{map_id}:invalidate_character_ids",
|
|
[]
|
|
)
|
|
|
|
acls =
|
|
map_id
|
|
|> WandererApp.Map.get_map!()
|
|
|> Map.get(:acls, [])
|
|
|
|
invalidate_character_ids
|
|
|> Task.async_stream(
|
|
fn character_id ->
|
|
character_id
|
|
|> WandererApp.Character.get_character()
|
|
|> case do
|
|
{:ok, character} ->
|
|
[character_permissions] =
|
|
WandererApp.Permissions.check_characters_access([character], acls)
|
|
|
|
map_permissions =
|
|
WandererApp.Permissions.get_map_permissions(
|
|
character_permissions,
|
|
owner_id,
|
|
[character_id]
|
|
)
|
|
|
|
case map_permissions do
|
|
%{view_system: false} ->
|
|
{:remove_character, character_id}
|
|
|
|
%{track_character: false} ->
|
|
{:remove_character, character_id}
|
|
|
|
_ ->
|
|
:ok
|
|
end
|
|
|
|
_ ->
|
|
:ok
|
|
end
|
|
end,
|
|
timeout: :timer.seconds(60),
|
|
max_concurrency: System.schedulers_online(),
|
|
on_timeout: :kill_task
|
|
)
|
|
|> Enum.each(fn
|
|
{:ok, {:remove_character, character_id}} ->
|
|
remove_and_untrack_characters(map_id, [character_id])
|
|
:ok
|
|
|
|
{:ok, _result} ->
|
|
:ok
|
|
|
|
{:error, reason} ->
|
|
Logger.error("Error in cleanup_characters: #{inspect(reason)}")
|
|
end)
|
|
|
|
WandererApp.Cache.insert(
|
|
"map_#{map_id}:invalidate_character_ids",
|
|
[]
|
|
)
|
|
end
|
|
|
|
defp remove_and_untrack_characters(map_id, character_ids) do
|
|
Logger.debug(fn ->
|
|
"Map #{map_id} - remove and untrack characters #{inspect(character_ids)}"
|
|
end)
|
|
|
|
map_id
|
|
|> untrack_characters(character_ids)
|
|
|
|
map_id
|
|
|> WandererApp.MapCharacterSettingsRepo.get_by_map_filtered(character_ids)
|
|
|> case do
|
|
{:ok, settings} ->
|
|
settings
|
|
|> Enum.each(fn s ->
|
|
WandererApp.MapCharacterSettingsRepo.destroy!(s)
|
|
remove_character(map_id, s.character_id)
|
|
end)
|
|
|
|
_ ->
|
|
:ok
|
|
end
|
|
end
|
|
|
|
def track_characters(_map_id, []), do: :ok
|
|
|
|
def track_characters(map_id, [character_id | rest]) do
|
|
track_character(map_id, character_id)
|
|
track_characters(map_id, rest)
|
|
end
|
|
|
|
def update_characters(%{map_id: map_id} = state) do
|
|
WandererApp.Cache.lookup!("maps:#{map_id}:tracked_characters", [])
|
|
|> Enum.map(fn character_id ->
|
|
Task.start_link(fn ->
|
|
character_updates =
|
|
maybe_update_online(map_id, character_id) ++
|
|
maybe_update_location(map_id, character_id) ++
|
|
maybe_update_ship(map_id, character_id) ++
|
|
maybe_update_alliance(map_id, character_id) ++
|
|
maybe_update_corporation(map_id, character_id)
|
|
|
|
character_updates
|
|
|> Enum.filter(fn update -> update != :skip end)
|
|
|> Enum.map(fn update ->
|
|
update
|
|
|> case do
|
|
{:character_location, location_info, old_location_info} ->
|
|
update_location(
|
|
character_id,
|
|
location_info,
|
|
old_location_info,
|
|
state
|
|
)
|
|
|
|
:broadcast
|
|
|
|
{:character_ship, _info} ->
|
|
:broadcast
|
|
|
|
{:character_online, _info} ->
|
|
:broadcast
|
|
|
|
{:character_alliance, _info} ->
|
|
WandererApp.Cache.insert_or_update(
|
|
"map_#{map_id}:invalidate_character_ids",
|
|
[character_id],
|
|
fn ids ->
|
|
[character_id | ids]
|
|
end
|
|
)
|
|
|
|
:broadcast
|
|
|
|
{:character_corporation, _info} ->
|
|
WandererApp.Cache.insert_or_update(
|
|
"map_#{map_id}:invalidate_character_ids",
|
|
[character_id],
|
|
fn ids ->
|
|
[character_id | ids]
|
|
end
|
|
)
|
|
|
|
:broadcast
|
|
|
|
_ ->
|
|
:skip
|
|
end
|
|
end)
|
|
|> Enum.filter(fn update -> update != :skip end)
|
|
|> Enum.uniq()
|
|
|> Enum.each(fn update ->
|
|
case update do
|
|
:broadcast ->
|
|
update_character(map_id, character_id)
|
|
|
|
_ ->
|
|
:ok
|
|
end
|
|
end)
|
|
|
|
:ok
|
|
end)
|
|
end)
|
|
end
|
|
|
|
defp update_character(map_id, character_id) do
|
|
{:ok, character} = WandererApp.Character.get_character(character_id)
|
|
Impl.broadcast!(map_id, :character_updated, character)
|
|
end
|
|
|
|
defp update_location(
|
|
character_id,
|
|
location,
|
|
old_location,
|
|
%{map: map, map_id: map_id, rtree_name: rtree_name, map_opts: map_opts} = _state
|
|
) do
|
|
case is_nil(old_location.solar_system_id) and
|
|
ConnectionsImpl.can_add_location(map.scope, location.solar_system_id) do
|
|
true ->
|
|
:ok = SystemsImpl.maybe_add_system(map_id, location, nil, rtree_name, map_opts)
|
|
|
|
_ ->
|
|
ConnectionsImpl.is_connection_valid(
|
|
map.scope,
|
|
old_location.solar_system_id,
|
|
location.solar_system_id
|
|
)
|
|
|> case do
|
|
true ->
|
|
:ok =
|
|
SystemsImpl.maybe_add_system(map_id, location, old_location, rtree_name, map_opts)
|
|
|
|
:ok =
|
|
SystemsImpl.maybe_add_system(map_id, old_location, location, rtree_name, map_opts)
|
|
|
|
:ok =
|
|
ConnectionsImpl.maybe_add_connection(map_id, location, old_location, character_id)
|
|
|
|
_ ->
|
|
:ok
|
|
end
|
|
end
|
|
end
|
|
|
|
defp track_character(map_id, character_id),
|
|
do:
|
|
WandererApp.Character.TrackerManager.update_track_settings(character_id, %{
|
|
map_id: map_id,
|
|
track: true,
|
|
track_online: true,
|
|
track_location: true,
|
|
track_ship: true
|
|
})
|
|
|
|
defp maybe_update_online(map_id, character_id) do
|
|
with {:ok, old_online} <-
|
|
WandererApp.Cache.lookup("map:#{map_id}:character:#{character_id}:online"),
|
|
{:ok, %{online: online}} <-
|
|
WandererApp.Character.get_character(character_id) do
|
|
case old_online != online do
|
|
true ->
|
|
WandererApp.Cache.insert(
|
|
"map:#{map_id}:character:#{character_id}:online",
|
|
online
|
|
)
|
|
|
|
[{:character_online, %{online: online}}]
|
|
|
|
_ ->
|
|
[:skip]
|
|
end
|
|
else
|
|
error ->
|
|
Logger.error("Failed to update online: #{inspect(error, pretty: true)}")
|
|
[:skip]
|
|
end
|
|
end
|
|
|
|
defp maybe_update_ship(map_id, character_id) do
|
|
with {:ok, old_ship_type_id} <-
|
|
WandererApp.Cache.lookup("map:#{map_id}:character:#{character_id}:ship_type_id"),
|
|
{:ok, old_ship_name} <-
|
|
WandererApp.Cache.lookup("map:#{map_id}:character:#{character_id}:ship_name"),
|
|
{:ok, %{ship: ship_type_id, ship_name: ship_name}} <-
|
|
WandererApp.Character.get_character(character_id) do
|
|
case old_ship_type_id != ship_type_id or
|
|
old_ship_name != ship_name do
|
|
true ->
|
|
WandererApp.Cache.insert(
|
|
"map:#{map_id}:character:#{character_id}:ship_type_id",
|
|
ship_type_id
|
|
)
|
|
|
|
WandererApp.Cache.insert(
|
|
"map:#{map_id}:character:#{character_id}:ship_name",
|
|
ship_name
|
|
)
|
|
|
|
[{:character_ship, %{ship: ship_type_id, ship_name: ship_name}}]
|
|
|
|
_ ->
|
|
[:skip]
|
|
end
|
|
else
|
|
error ->
|
|
Logger.error("Failed to update ship: #{inspect(error, pretty: true)}")
|
|
[:skip]
|
|
end
|
|
end
|
|
|
|
defp maybe_update_location(map_id, character_id) do
|
|
WandererApp.Cache.lookup!(
|
|
"character:#{character_id}:location_started",
|
|
false
|
|
)
|
|
|> case do
|
|
true ->
|
|
{:ok, old_solar_system_id} =
|
|
WandererApp.Cache.lookup("map:#{map_id}:character:#{character_id}:solar_system_id")
|
|
|
|
{:ok, %{solar_system_id: solar_system_id}} =
|
|
WandererApp.Character.get_character(character_id)
|
|
|
|
WandererApp.Cache.insert(
|
|
"map:#{map_id}:character:#{character_id}:solar_system_id",
|
|
solar_system_id
|
|
)
|
|
|
|
case solar_system_id != old_solar_system_id do
|
|
true ->
|
|
[
|
|
{:character_location, %{solar_system_id: solar_system_id},
|
|
%{solar_system_id: old_solar_system_id}}
|
|
]
|
|
_ ->
|
|
[:skip]
|
|
end
|
|
|
|
false ->
|
|
{:ok, old_solar_system_id} =
|
|
WandererApp.Cache.lookup("map:#{map_id}:character:#{character_id}:solar_system_id")
|
|
|
|
{:ok, %{solar_system_id: solar_system_id} = _character} =
|
|
WandererApp.Character.get_character(character_id)
|
|
|
|
WandererApp.Cache.insert(
|
|
"map:#{map_id}:character:#{character_id}:solar_system_id",
|
|
solar_system_id
|
|
)
|
|
|
|
if is_nil(old_solar_system_id) or solar_system_id != old_solar_system_id do
|
|
[
|
|
{:character_location, %{solar_system_id: solar_system_id}, %{solar_system_id: nil}}
|
|
]
|
|
else
|
|
[:skip]
|
|
end
|
|
end
|
|
end
|
|
|
|
defp maybe_update_alliance(map_id, character_id) do
|
|
with {:ok, old_alliance_id} <-
|
|
WandererApp.Cache.lookup("map:#{map_id}:character:#{character_id}:alliance_id"),
|
|
{:ok, %{alliance_id: alliance_id}} <-
|
|
WandererApp.Character.get_character(character_id) do
|
|
case old_alliance_id != alliance_id do
|
|
true ->
|
|
WandererApp.Cache.insert(
|
|
"map:#{map_id}:character:#{character_id}:alliance_id",
|
|
alliance_id
|
|
)
|
|
|
|
[{:character_alliance, %{alliance_id: alliance_id}}]
|
|
|
|
_ ->
|
|
[:skip]
|
|
end
|
|
else
|
|
error ->
|
|
Logger.error("Failed to update alliance: #{inspect(error, pretty: true)}")
|
|
[:skip]
|
|
end
|
|
end
|
|
|
|
defp maybe_update_corporation(map_id, character_id) do
|
|
with {:ok, old_corporation_id} <-
|
|
WandererApp.Cache.lookup("map:#{map_id}:character:#{character_id}:corporation_id"),
|
|
{:ok, %{corporation_id: corporation_id}} <-
|
|
WandererApp.Character.get_character(character_id) do
|
|
case old_corporation_id != corporation_id do
|
|
true ->
|
|
WandererApp.Cache.insert(
|
|
"map:#{map_id}:character:#{character_id}:corporation_id",
|
|
corporation_id
|
|
)
|
|
|
|
[{:character_corporation, %{corporation_id: corporation_id}}]
|
|
|
|
_ ->
|
|
[:skip]
|
|
end
|
|
else
|
|
error ->
|
|
Logger.error("Failed to update corporation: #{inspect(error, pretty: true)}")
|
|
[:skip]
|
|
end
|
|
end
|
|
end
|