Compare commits

...

31 Commits

Author SHA1 Message Date
CI
9e31542c5f chore: release version v1.65.24
Some checks failed
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-06-04 18:57:47 +00:00
Dmitry Popov
aae6ed0cb3 fix(Core): Fixed errors duration check to reduce requests amount to ESI 2025-06-04 20:57:17 +02:00
CI
bb0e06589f chore: release version v1.65.23 2025-06-04 15:53:50 +00:00
Dmitry Popov
d65b72c4dd fix(Core): Added back arm docker image build 2025-06-04 17:53:21 +02:00
CI
24a3d5b3de chore: release version v1.65.22 2025-06-04 15:44:34 +00:00
Dmitry Popov
2814b46941 fix(Core): Fix character tracking issues 2025-06-04 17:43:53 +02:00
CI
d9a82f7c9f chore: release version v1.65.21
Some checks failed
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-06-01 19:00:52 +00:00
Dmitry Popov
9a8106947e Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-06-01 21:00:23 +02:00
Dmitry Popov
e21ca79ea1 fix(Core): Fix connection pool errors 2025-06-01 21:00:14 +02:00
CI
dd579caeac chore: release version v1.65.20 2025-06-01 17:56:46 +00:00
Dmitry Popov
ddf8a4b9fb Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-06-01 19:56:18 +02:00
Dmitry Popov
3c04f4194e fix(Core): fix waypoint set timeout errors 2025-06-01 19:56:14 +02:00
CI
1e0de841eb chore: release version v1.65.19
Some checks failed
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-06-01 08:15:49 +00:00
Dmitry Popov
c9c88cf0a6 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-06-01 10:15:21 +02:00
Dmitry Popov
fd1e124166 fix(Core): fix updating character online 2025-06-01 10:15:18 +02:00
CI
aa2bee258a chore: release version v1.65.18
Some checks failed
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-05-30 21:38:38 +00:00
Dmitry Popov
12d696dc07 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-30 23:38:10 +02:00
Dmitry Popov
b33772a1d6 fix(Core): fix updating systems and connections 2025-05-30 23:38:07 +02:00
CI
b41f89d7de chore: release version v1.65.17
Some checks failed
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-05-29 15:38:24 +00:00
Dmitry Popov
e8ca213b74 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-29 17:37:51 +02:00
Dmitry Popov
7620b8d493 fix(Core): fix updating systems and connections 2025-05-29 17:37:47 +02:00
Dmitry Popov
23306fd9e3 fix(Comments): fix error loading comments 2025-05-29 17:36:45 +02:00
CI
a68ccdca50 chore: release version v1.65.16
Some checks failed
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-05-29 09:10:07 +00:00
Dmitry Popov
771e432546 Merge pull request #415 from wanderer-industries/wnd-414-allow-lock
fix(Map): Allow lock systems for members
2025-05-29 13:09:35 +04:00
DanSylvest
20bdbfb81a fix(Map): Allow lock systems for members
wnd-414
2025-05-29 09:05:43 +03:00
CI
90729b436c chore: release version v1.65.15
Some checks failed
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-05-28 10:25:50 +00:00
Dmitry Popov
8ea4e97bbf Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-28 12:25:23 +02:00
Dmitry Popov
17e12e9263 chore: got rid of timestamp checking on server 2025-05-28 12:25:19 +02:00
CI
4cb3d021f4 chore: release version v1.65.14 2025-05-28 09:29:39 +00:00
Dmitry Popov
97cec2e127 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-05-28 11:28:55 +02:00
Dmitry Popov
9c4294659c chore: got rid of timestamp checking on UI 2025-05-28 11:28:50 +02:00
20 changed files with 538 additions and 283 deletions

View File

@@ -2,6 +2,99 @@
<!-- changelog -->
## [v1.65.24](https://github.com/wanderer-industries/wanderer/compare/v1.65.23...v1.65.24) (2025-06-04)
### Bug Fixes:
* Core: Fixed errors duration check to reduce requests amount to ESI
## [v1.65.23](https://github.com/wanderer-industries/wanderer/compare/v1.65.22...v1.65.23) (2025-06-04)
### Bug Fixes:
* Core: Added back arm docker image build
## [v1.65.22](https://github.com/wanderer-industries/wanderer/compare/v1.65.21...v1.65.22) (2025-06-04)
### Bug Fixes:
* Core: Fix character tracking issues
## [v1.65.21](https://github.com/wanderer-industries/wanderer/compare/v1.65.20...v1.65.21) (2025-06-01)
### Bug Fixes:
* Core: Fix connection pool errors
## [v1.65.20](https://github.com/wanderer-industries/wanderer/compare/v1.65.19...v1.65.20) (2025-06-01)
### Bug Fixes:
* Core: fix waypoint set timeout errors
## [v1.65.19](https://github.com/wanderer-industries/wanderer/compare/v1.65.18...v1.65.19) (2025-06-01)
### Bug Fixes:
* Core: fix updating character online
## [v1.65.18](https://github.com/wanderer-industries/wanderer/compare/v1.65.17...v1.65.18) (2025-05-30)
### Bug Fixes:
* Core: fix updating systems and connections
## [v1.65.17](https://github.com/wanderer-industries/wanderer/compare/v1.65.16...v1.65.17) (2025-05-29)
### Bug Fixes:
* Core: fix updating systems and connections
* Comments: fix error loading comments
## [v1.65.16](https://github.com/wanderer-industries/wanderer/compare/v1.65.15...v1.65.16) (2025-05-29)
### Bug Fixes:
* Map: Allow lock systems for members
## [v1.65.15](https://github.com/wanderer-industries/wanderer/compare/v1.65.14...v1.65.15) (2025-05-28)
## [v1.65.14](https://github.com/wanderer-industries/wanderer/compare/v1.65.13...v1.65.14) (2025-05-28)
## [v1.65.13](https://github.com/wanderer-industries/wanderer/compare/v1.65.12...v1.65.13) (2025-05-28)

View File

@@ -44,6 +44,7 @@ export const useContextMenuSystemItems = ({
const getLabels = useLabelsMenu(systems, systemId, onSystemLabels, onCustomLabelDialog);
const getWaypointMenu = useWaypointMenu(onWaypointSet);
const canLockSystem = useMapCheckPermissions([UserPermission.LOCK_SYSTEM]);
const canManageSystem = useMapCheckPermissions([UserPermission.UPDATE_SYSTEM]);
const canDeleteSystem = useMapCheckPermissions([UserPermission.DELETE_SYSTEM]);
const getUserRoutes = useUserRoute({ userHubs, systemId, onUserHubToggle });
@@ -132,11 +133,20 @@ export const useContextMenuSystemItems = ({
);
},
},
...(canLockSystem
...(system.locked && canLockSystem
? [
{
label: system.locked ? 'Unlock' : 'Lock',
icon: system.locked ? PrimeIcons.LOCK_OPEN : PrimeIcons.LOCK,
label: 'Unlock',
icon: PrimeIcons.LOCK_OPEN,
command: onLockToggle,
},
]
: []),
...(!system.locked && canManageSystem
? [
{
label: 'Lock',
icon: PrimeIcons.LOCK,
command: onLockToggle,
},
]

View File

@@ -1,7 +1,8 @@
import { MutableRefObject, useCallback, useEffect, useRef } from 'react';
import { Command, Commands, MapHandlers } from '@/hooks/Mapper/types';
import { MapEvent } from '@/hooks/Mapper/events';
// import { useThrottle } from '@/hooks/Mapper/hooks';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { Command, Commands, MapHandlers } from '@/hooks/Mapper/types';
import { MutableRefObject, useCallback, useEffect, useRef } from 'react';
export const useCommonMapEventProcessor = () => {
const mapRef = useRef<MapHandlers>() as MutableRefObject<MapHandlers>;
@@ -11,13 +12,14 @@ export const useCommonMapEventProcessor = () => {
const refQueue = useRef<MapEvent<Command>[]>([]);
// const ref = useRef({})
const runCommand = useCallback(({ name, data }: MapEvent<Command>) => {
switch (name) {
case Commands.addSystems:
case Commands.removeSystems:
// case Commands.updateSystems:
// case Commands.addConnections:
// case Commands.removeConnections:
// case Commands.updateConnection:
refQueue.current.push({ name, data });
return;
}
@@ -26,9 +28,17 @@ export const useCommonMapEventProcessor = () => {
mapRef.current?.command(name, data);
}, []);
useEffect(() => {
refQueue.current.forEach(x => mapRef.current?.command(x.name, x.data));
const processQueue = useCallback(() => {
const commands = [...refQueue.current];
refQueue.current = [];
commands.forEach(x => mapRef.current?.command(x.name, x.data));
}, []);
// const throttledProcessQueue = useThrottle(processQueue, 200);
useEffect(() => {
// throttledProcessQueue();
processQueue();
}, [systems]);
return {

View File

@@ -1,5 +1,6 @@
export * from './usePageVisibility';
export * from './useActualizeSettings';
export * from './useClipboard';
export * from './useHotkey';
export * from './usePageVisibility';
export * from './useSkipContextMenu';
export * from './useActualizeSettings';
export * from './useThrottle';

View File

@@ -0,0 +1,13 @@
import { useCallback, useRef } from 'react';
export const useThrottle = (callback: any, limit: number) => {
const lastCallRef = useRef(0);
const throttledCallback = useCallback(() => {
const now = Date.now();
if (now - lastCallRef.current >= limit) {
lastCallRef.current = now;
callback();
}
}, [callback, limit]);
return throttledCallback;
};

View File

@@ -1,9 +1,6 @@
import { MapHandlers } from '@/hooks/Mapper/types/mapHandlers.ts';
import { RefObject, useCallback } from 'react';
// Force reload the page after 5 minutes of inactivity
const FORCE_PAGE_RELOAD_TIMEOUT = 1000 * 60 * 5;
export const useMapperHandlers = (handlerRefs: RefObject<MapHandlers>[], hooksRef: RefObject<any>) => {
const handleCommand = useCallback(
async ({ type, data }) => {
@@ -16,13 +13,7 @@ export const useMapperHandlers = (handlerRefs: RefObject<MapHandlers>[], hooksRe
[hooksRef.current],
);
const handleMapEvent = useCallback(({ type, body, timestamp }) => {
const timeDiff = Date.now() - Date.parse(timestamp);
// If the event is older than the timeout, force reload the page
if (timeDiff > FORCE_PAGE_RELOAD_TIMEOUT) {
window.location.reload();
return;
}
const handleMapEvent = useCallback(({ type, body }) => {
handlerRefs.forEach(ref => {
if (!ref.current) {
return;

View File

@@ -20,9 +20,9 @@ defmodule WandererApp.Application do
pools: %{
default: [
# number of connections per pool
size: 25,
size: 50,
# number of pools (so total 50 connections)
count: 2
count: 4
]
}
},

View File

@@ -54,18 +54,26 @@ defmodule WandererApp.Character do
end
end
def get_map_character(map_id, character_id) do
def get_map_character(map_id, character_id, opts \\ []) do
case get_character(character_id) do
{:ok, character} ->
# If we are forcing the character to not be present, we merge the character state with map settings
character_is_present =
if opts |> Keyword.get(:not_present, false) do
false
else
WandererApp.Character.TrackerManager.Impl.character_is_present(map_id, character_id)
end
{:ok,
character
|> maybe_merge_map_character_settings(
map_id,
WandererApp.Character.TrackerManager.Impl.character_is_present(map_id, character_id)
character_is_present
)}
_ ->
{:ok, nil}
error ->
error
end
end

View File

@@ -79,8 +79,8 @@ defmodule WandererApp.Character.Tracker do
:ok
{:error, :forbidden} ->
Logger.warning("#{__MODULE__} failed to get_character_info: forbidden")
{:error, error} when error in [:forbidden, :error_limited, :not_found, :timeout] ->
Logger.warning("#{__MODULE__} failed to get_character_info: #{inspect(error)}")
WandererApp.Cache.put(
"character:#{character_id}:info_forbidden",
@@ -88,7 +88,7 @@ defmodule WandererApp.Character.Tracker do
ttl: @forbidden_ttl
)
{:error, :forbidden}
{:error, error}
{:error, error} ->
Logger.error("#{__MODULE__} failed to get_character_info: #{inspect(error)}")
@@ -121,13 +121,13 @@ defmodule WandererApp.Character.Tracker do
character_id: character_id,
refresh_token?: true
) do
{:ok, ship} ->
{:ok, ship} when is_non_struct_map(ship) ->
character_state |> maybe_update_ship(ship)
:ok
{:error, :forbidden} ->
Logger.warning("#{__MODULE__} failed to update_ship: forbidden")
{:error, error} when error in [:forbidden, :error_limited, :not_found, :timeout] ->
Logger.warning("#{__MODULE__} failed to update_ship: #{inspect(error)}")
WandererApp.Cache.put(
"character:#{character_id}:ship_forbidden",
@@ -135,7 +135,7 @@ defmodule WandererApp.Character.Tracker do
ttl: @forbidden_ttl
)
{:error, :forbidden}
{:error, error}
{:error, error} ->
Logger.error("#{__MODULE__} failed to update_ship: #{inspect(error)}")
@@ -147,6 +147,11 @@ defmodule WandererApp.Character.Tracker do
)
{:error, error}
_ ->
Logger.error("#{__MODULE__} failed to update_ship: wrong response")
{:error, :skipped}
end
end
@@ -179,14 +184,14 @@ defmodule WandererApp.Character.Tracker do
character_id: character_id,
refresh_token?: true
) do
{:ok, location} ->
{:ok, location} when is_non_struct_map(location) ->
character_state
|> maybe_update_location(location)
:ok
{:error, :forbidden} ->
Logger.warning("#{__MODULE__} failed to update_location: forbidden")
{:error, error} when error in [:forbidden, :error_limited, :not_found, :timeout] ->
Logger.warning("#{__MODULE__} failed to update_location: #{inspect(error)}")
WandererApp.Cache.put(
"character:#{character_id}:location_forbidden",
@@ -194,7 +199,7 @@ defmodule WandererApp.Character.Tracker do
ttl: @forbidden_ttl
)
{:error, :forbidden}
{:error, error}
{:error, error} ->
Logger.error("#{__MODULE__} failed to update_location: #{inspect(error)}")
@@ -206,6 +211,11 @@ defmodule WandererApp.Character.Tracker do
)
{:error, error}
_ ->
Logger.error("#{__MODULE__} failed to update_location: wrong response")
{:error, :skipped}
end
_ ->
@@ -247,11 +257,6 @@ defmodule WandererApp.Character.Tracker do
WandererApp.Cache.delete("character:#{character_id}:online_error_time")
WandererApp.Character.update_character(character_id, online)
if not online.online do
WandererApp.Cache.delete("character:#{character_id}:location_started")
WandererApp.Cache.delete("character:#{character_id}:start_solar_system_id")
end
update = %{
character_state
| is_online: online.online,
@@ -263,8 +268,8 @@ defmodule WandererApp.Character.Tracker do
:ok
{:error, :forbidden} ->
Logger.warning("#{__MODULE__} failed to update_online: forbidden")
{:error, error} when error in [:forbidden, :error_limited, :not_found, :timeout] ->
Logger.warning("#{__MODULE__} failed to update_online: #{inspect(error)}")
if not WandererApp.Cache.lookup!(
"character:#{character_id}:online_forbidden",
@@ -316,7 +321,7 @@ defmodule WandererApp.Character.Tracker do
:skip
error_time ->
duration = DateTime.diff(DateTime.utc_now(), error_time, :second)
duration = DateTime.diff(DateTime.utc_now(), error_time, :millisecond)
if duration >= @online_error_timeout do
WandererApp.Cache.delete("character:#{character_id}:online_forbidden")
@@ -362,8 +367,9 @@ defmodule WandererApp.Character.Tracker do
:ok
{:error, :forbidden} ->
Logger.warning("#{__MODULE__} failed to _update_wallet: forbidden")
{:error, error}
when error in [:forbidden, :error_limited, :not_found, :timeout] ->
Logger.warning("#{__MODULE__} failed to update_wallet: #{inspect(error)}")
WandererApp.Cache.put(
"character:#{character_id}:wallet_forbidden",
@@ -371,7 +377,7 @@ defmodule WandererApp.Character.Tracker do
ttl: @forbidden_ttl
)
{:error, :forbidden}
{:error, error}
{:error, error} ->
Logger.error("#{__MODULE__} failed to _update_wallet: #{inspect(error)}")
@@ -475,7 +481,8 @@ defmodule WandererApp.Character.Tracker do
} =
state,
ship
) do
)
when is_non_struct_map(ship) do
ship_type_id = Map.get(ship, "ship_type_id")
ship_name = Map.get(ship, "ship_name")
@@ -499,6 +506,12 @@ defmodule WandererApp.Character.Tracker do
state
end
defp maybe_update_ship(
state,
_ship
),
do: state
defp maybe_update_location(
%{
character_id: character_id
@@ -508,32 +521,12 @@ defmodule WandererApp.Character.Tracker do
) do
location = get_location(location)
if not is_location_started?(character_id) do
WandererApp.Cache.lookup!("character:#{character_id}:start_solar_system_id", nil)
|> case do
nil ->
WandererApp.Cache.put(
"character:#{character_id}:start_solar_system_id",
location.solar_system_id
)
start_solar_system_id ->
if location.solar_system_id != start_solar_system_id do
WandererApp.Cache.put(
"character:#{character_id}:location_started",
true
)
end
end
end
{:ok,
%{solar_system_id: solar_system_id, structure_id: structure_id, station_id: station_id} =
character} =
WandererApp.Character.get_character(character_id)
(not is_location_started?(character_id) ||
is_location_updated?(location, solar_system_id, structure_id, station_id))
is_location_updated?(location, solar_system_id, structure_id, station_id)
|> case do
true ->
{:ok, _character} = WandererApp.Api.Character.update_location(character, location)
@@ -548,12 +541,12 @@ defmodule WandererApp.Character.Tracker do
state
end
defp is_location_started?(character_id),
do:
WandererApp.Cache.lookup!(
"character:#{character_id}:location_started",
false
)
# defp is_location_started?(character_id),
# do:
# WandererApp.Cache.lookup!(
# "character:#{character_id}:location_started",
# false
# )
defp is_location_updated?(
%{
@@ -692,16 +685,31 @@ defmodule WandererApp.Character.Tracker do
defp maybe_update_active_maps(
%{character_id: character_id, active_maps: active_maps} =
state,
%{map_id: map_id, track: true} = _track_settings
%{map_id: map_id, track: true} = track_settings
) do
WandererApp.Cache.put(
"character:#{character_id}:map:#{map_id}:tracking_start_time",
DateTime.utc_now()
)
if not Enum.member?(active_maps, map_id) do
WandererApp.Cache.put(
"character:#{character_id}:map:#{map_id}:tracking_start_time",
DateTime.utc_now()
)
WandererApp.Cache.take("character:#{character_id}:last_active_time")
WandererApp.Cache.put(
"map:#{map_id}:character:#{character_id}:start_solar_system_id",
track_settings |> Map.get(:solar_system_id)
)
%{state | active_maps: [map_id | active_maps] |> Enum.uniq()}
WandererApp.Cache.delete("map:#{map_id}:character:#{character_id}:solar_system_id")
WandererApp.Cache.delete("map:#{map_id}:character:#{character_id}:station_id")
WandererApp.Cache.delete("map:#{map_id}:character:#{character_id}:structure_id")
WandererApp.Cache.take("character:#{character_id}:last_active_time")
%{state | active_maps: [map_id | active_maps]}
else
WandererApp.Cache.take("character:#{character_id}:last_active_time")
state
end
end
defp maybe_update_active_maps(

View File

@@ -77,8 +77,6 @@ defmodule WandererApp.Character.TrackerManager.Impl do
Logger.debug(fn -> "Shutting down character tracker: #{inspect(character_id)}" end)
WandererApp.Cache.delete("character:#{character_id}:last_active_time")
WandererApp.Cache.delete("character:#{character_id}:location_started")
WandererApp.Cache.delete("character:#{character_id}:start_solar_system_id")
WandererApp.Character.delete_character_state(character_id)
tracked_characters =
@@ -189,7 +187,7 @@ defmodule WandererApp.Character.TrackerManager.Impl do
end,
max_concurrency: System.schedulers_online(),
on_timeout: :kill_task,
timeout: :timer.seconds(15)
timeout: :timer.seconds(60)
)
|> Enum.map(fn result ->
case result do
@@ -214,6 +212,10 @@ defmodule WandererApp.Character.TrackerManager.Impl do
|> Task.async_stream(
fn {map_id, character_id} ->
if not character_is_present(map_id, character_id) do
WandererApp.Cache.delete("map:#{map_id}:character:#{character_id}:solar_system_id")
WandererApp.Cache.delete("map:#{map_id}:character:#{character_id}:station_id")
WandererApp.Cache.delete("map:#{map_id}:character:#{character_id}:structure_id")
{:ok, character_state} =
WandererApp.Character.Tracker.update_settings(character_id, %{
map_id: map_id,

View File

@@ -49,7 +49,9 @@ defmodule WandererApp.Character.TrackerPool do
# end)
tracked_ids
|> Enum.each(fn id -> Cachex.put(@cache, id, uuid) end)
|> Enum.each(fn id ->
Cachex.put(@cache, id, uuid)
end)
state =
%{
@@ -78,7 +80,6 @@ defmodule WandererApp.Character.TrackerPool do
# Cachex.get_and_update(@cache, :tracked_characters, fn ids ->
# {:commit, ids ++ [tracked_id]}
# end)
Cachex.put(@cache, tracked_id, uuid)
{:noreply, %{state | characters: [tracked_id | characters]}}
@@ -96,7 +97,7 @@ defmodule WandererApp.Character.TrackerPool do
# Cachex.get_and_update(@cache, :tracked_characters, fn ids ->
# {:commit, ids |> Enum.reject(fn id -> id == tracked_id end)}
# end)
#
Cachex.del(@cache, tracked_id)
{:noreply, %{state | characters: characters |> Enum.reject(fn id -> id == tracked_id end)}}
@@ -155,12 +156,20 @@ defmodule WandererApp.Character.TrackerPool do
) do
Process.send_after(self(), :update_online, @update_online_interval)
characters
|> Enum.map(fn character_id ->
WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_online, [
character_id
])
end)
try do
characters
|> Enum.map(fn character_id ->
WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_online, [
character_id
])
end)
rescue
e ->
Logger.error("""
[Tracker Pool] update_online => exception: #{Exception.message(e)}
#{Exception.format_stacktrace(__STACKTRACE__)}
""")
end
{:noreply, state}
end
@@ -174,14 +183,22 @@ defmodule WandererApp.Character.TrackerPool do
) do
Process.send_after(self(), :update_online, @update_online_interval)
characters
|> Enum.each(fn character_id ->
WandererApp.Character.update_character(character_id, %{online: false})
try do
characters
|> Enum.each(fn character_id ->
WandererApp.Character.update_character(character_id, %{online: false})
WandererApp.Character.update_character_state(character_id, %{
is_online: false
})
end)
WandererApp.Character.update_character_state(character_id, %{
is_online: false
})
end)
rescue
e ->
Logger.error("""
[Tracker Pool] update_online => exception: #{Exception.message(e)}
#{Exception.format_stacktrace(__STACKTRACE__)}
""")
end
{:noreply, state}
end
@@ -195,21 +212,33 @@ defmodule WandererApp.Character.TrackerPool do
) do
Process.send_after(self(), :check_online_errors, @check_online_errors_interval)
characters
|> Task.async_stream(
fn character_id ->
WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :check_online_errors, [
character_id
])
end,
timeout: :timer.seconds(15),
max_concurrency: System.schedulers_online(),
on_timeout: :kill_task
)
|> Enum.each(fn
{:ok, _result} -> :ok
{:error, reason} -> @logger.error("Error in check_online_errors: #{inspect(reason)}")
end)
try do
characters
|> Task.async_stream(
fn character_id ->
WandererApp.TaskWrapper.start_link(
WandererApp.Character.Tracker,
:check_online_errors,
[
character_id
]
)
end,
timeout: :timer.seconds(15),
max_concurrency: System.schedulers_online(),
on_timeout: :kill_task
)
|> Enum.each(fn
{:ok, _result} -> :ok
{:error, reason} -> @logger.error("Error in check_online_errors: #{inspect(reason)}")
end)
rescue
e ->
Logger.error("""
[Tracker Pool] check_online_errors => exception: #{Exception.message(e)}
#{Exception.format_stacktrace(__STACKTRACE__)}
""")
end
{:noreply, state}
end
@@ -224,12 +253,20 @@ defmodule WandererApp.Character.TrackerPool do
) do
Process.send_after(self(), :update_location, @update_location_interval)
characters
|> Enum.map(fn character_id ->
WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_location, [
character_id
])
end)
try do
characters
|> Enum.map(fn character_id ->
WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_location, [
character_id
])
end)
rescue
e ->
Logger.error("""
[Tracker Pool] update_location => exception: #{Exception.message(e)}
#{Exception.format_stacktrace(__STACKTRACE__)}
""")
end
{:noreply, state}
end
@@ -253,12 +290,20 @@ defmodule WandererApp.Character.TrackerPool do
) do
Process.send_after(self(), :update_ship, @update_ship_interval)
characters
|> Enum.map(fn character_id ->
WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_ship, [
character_id
])
end)
try do
characters
|> Enum.map(fn character_id ->
WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_ship, [
character_id
])
end)
rescue
e ->
Logger.error("""
[Tracker Pool] update_ship => exception: #{Exception.message(e)}
#{Exception.format_stacktrace(__STACKTRACE__)}
""")
end
{:noreply, state}
end
@@ -282,21 +327,29 @@ defmodule WandererApp.Character.TrackerPool do
) do
Process.send_after(self(), :update_info, @update_info_interval)
characters
|> Task.async_stream(
fn character_id ->
WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_info, [
character_id
])
end,
timeout: :timer.seconds(15),
max_concurrency: System.schedulers_online(),
on_timeout: :kill_task
)
|> Enum.each(fn
{:ok, _result} -> :ok
{:error, reason} -> @logger.error("Error in update_info: #{inspect(reason)}")
end)
try do
characters
|> Task.async_stream(
fn character_id ->
WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_info, [
character_id
])
end,
timeout: :timer.seconds(15),
max_concurrency: System.schedulers_online(),
on_timeout: :kill_task
)
|> Enum.each(fn
{:ok, _result} -> :ok
{:error, reason} -> Logger.error("Error in update_info: #{inspect(reason)}")
end)
rescue
e ->
Logger.error("""
[Tracker Pool] update_info => exception: #{Exception.message(e)}
#{Exception.format_stacktrace(__STACKTRACE__)}
""")
end
{:noreply, state}
end
@@ -320,21 +373,29 @@ defmodule WandererApp.Character.TrackerPool do
) do
Process.send_after(self(), :update_wallet, @update_wallet_interval)
characters
|> Task.async_stream(
fn character_id ->
WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_wallet, [
character_id
])
end,
timeout: :timer.seconds(15),
max_concurrency: System.schedulers_online(),
on_timeout: :kill_task
)
|> Enum.each(fn
{:ok, _result} -> :ok
{:error, reason} -> @logger.error("Error in update_wallet: #{inspect(reason)}")
end)
try do
characters
|> Task.async_stream(
fn character_id ->
WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_wallet, [
character_id
])
end,
timeout: :timer.seconds(15),
max_concurrency: System.schedulers_online(),
on_timeout: :kill_task
)
|> Enum.each(fn
{:ok, _result} -> :ok
{:error, reason} -> Logger.error("Error in update_wallet: #{inspect(reason)}")
end)
rescue
e ->
Logger.error("""
[Tracker Pool] update_wallet => exception: #{Exception.message(e)}
#{Exception.format_stacktrace(__STACKTRACE__)}
""")
end
{:noreply, state}
end

View File

@@ -11,6 +11,8 @@ defmodule WandererApp.Esi.ApiClient do
@base_url "https://esi.evetech.net/latest"
@wanderrer_user_agent "(wanderer-industries@proton.me; +https://github.com/wanderer-industries/wanderer)"
@req_esi Req.new(base_url: @base_url, finch: WandererApp.Finch)
@get_link_pairs_advanced_params [
:include_mass_crit,
:include_eol,
@@ -36,7 +38,7 @@ defmodule WandererApp.Esi.ApiClient do
@cache_opts [cache: true]
@retry_opts [max_retries: 0, retry_log_level: :warning]
@timeout_opts [pool_timeout: 15_000, receive_timeout: :timer.seconds(30)]
@timeout_opts [pool_timeout: 15_000, receive_timeout: :timer.minutes(1)]
@api_retry_count 1
@logger Application.compile_env(:wanderer_app, :logger)
@@ -47,7 +49,7 @@ defmodule WandererApp.Esi.ApiClient do
do:
post_esi(
"/ui/autopilot/waypoint",
opts
get_auth_opts(opts)
|> Keyword.merge(
params: %{
add_to_beginning: add_to_beginning,
@@ -60,8 +62,8 @@ defmodule WandererApp.Esi.ApiClient do
def post_characters_affiliation(character_eve_ids, _opts)
when is_list(character_eve_ids),
do:
post(
"#{@base_url}/characters/affiliation/",
post_esi(
"/characters/affiliation/",
json: character_eve_ids,
params: %{
datasource: "tranquility"
@@ -218,8 +220,6 @@ defmodule WandererApp.Esi.ApiClient do
{:ok, result}
{:error, _error} ->
@logger.error("Error getting custom routes for #{inspect(origin)}: #{inspect(hubs)}")
@logger.error(
"Error getting custom routes for #{inspect(origin)}: #{inspect(params)}"
)
@@ -499,13 +499,6 @@ defmodule WandererApp.Esi.ApiClient do
opts |> Keyword.merge(@cache_opts) |> Keyword.merge(cache_dir: System.tmp_dir!())
end
defp post_esi(path, opts),
do:
post(
"#{@base_url}#{path}",
[params: opts[:params] || []] ++ (opts |> get_auth_opts())
)
defp get(path, api_opts \\ [], opts \\ []) do
case Cachex.get(:api_cache, path) do
{:ok, cached_data} when not is_nil(cached_data) ->
@@ -519,8 +512,9 @@ defmodule WandererApp.Esi.ApiClient do
defp do_get_request(path, api_opts \\ [], opts \\ []) do
try do
case Req.get(
"#{@base_url}#{path}",
@req_esi,
api_opts
|> Keyword.merge(url: path)
|> with_user_agent_opts()
|> with_cache_opts()
|> Keyword.merge(@retry_opts)
@@ -537,11 +531,11 @@ defmodule WandererApp.Esi.ApiClient do
{:ok, %{status: 404}} ->
{:error, :not_found}
{:ok, %{status: 403} = _error} ->
{:ok, %{status: status} = _error} when status in [401, 403] ->
get_retry(path, api_opts, opts)
{:ok, %{status: 420} = _error} ->
get_retry(path, api_opts, opts, :error_limited)
{:error, :error_limited}
{:ok, %{status: status}} ->
{:error, "Unexpected status: #{status}"}
@@ -551,7 +545,7 @@ defmodule WandererApp.Esi.ApiClient do
end
rescue
e ->
@logger.error(Exception.message(e))
Logger.error(Exception.message(e))
{:error, "Request failed"}
end
@@ -610,6 +604,45 @@ defmodule WandererApp.Esi.ApiClient do
end
end
defp post_esi(url, opts) do
try do
req_opts =
(opts
|> with_user_agent_opts()) ++
[params: opts[:params] || []]
Req.new(
[base_url: @base_url, finch: WandererApp.Finch] ++
req_opts
)
|> Req.post(url: url)
|> case do
{:ok, %{status: status, body: body}} when status in [200, 201] ->
{:ok, body}
{:ok, %{status: 504}} ->
{:error, :timeout}
{:ok, %{status: 403}} ->
{:error, :forbidden}
{:ok, %{status: 420}} ->
{:error, :error_limited}
{:ok, %{status: status}} ->
{:error, "Unexpected status: #{status}"}
{:error, reason} ->
{:error, reason}
end
rescue
e ->
@logger.error(Exception.message(e))
{:error, "Request failed"}
end
end
defp get_retry(path, api_opts, opts, status \\ :forbidden) do
refresh_token? = opts |> Keyword.get(:refresh_token?, false)
retry_count = opts |> Keyword.get(:retry_count, 0)

View File

@@ -96,7 +96,9 @@ defmodule WandererApp.Map do
map_id
|> get_map!()
|> Map.get(:characters, [])
|> Enum.map(fn character_id -> WandererApp.Character.get_map_character!(map_id, character_id) end)
|> 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()}
@@ -155,56 +157,61 @@ defmodule WandererApp.Map do
case not (characters |> Enum.member?(character_id)) do
true ->
{:ok,
%{
alliance_id: alliance_id,
corporation_id: corporation_id,
solar_system_id: solar_system_id,
structure_id: structure_id,
station_id: station_id,
ship: ship_type_id,
ship_name: ship_name
}} = WandererApp.Character.get_character(character_id)
WandererApp.Character.get_map_character(map_id, character_id)
|> case do
{:ok,
%{
alliance_id: alliance_id,
corporation_id: corporation_id,
solar_system_id: solar_system_id,
structure_id: structure_id,
station_id: station_id,
ship: ship_type_id,
ship_name: ship_name
}} ->
map_id
|> update_map(%{characters: [character_id | characters]})
map_id
|> update_map(%{characters: [character_id | characters]})
WandererApp.Cache.insert(
"map:#{map_id}:character:#{character_id}:alliance_id",
alliance_id
)
WandererApp.Cache.insert(
"map:#{map_id}:character:#{character_id}:alliance_id",
alliance_id
)
WandererApp.Cache.insert(
"map:#{map_id}:character:#{character_id}:corporation_id",
corporation_id
)
WandererApp.Cache.insert(
"map:#{map_id}:character:#{character_id}:corporation_id",
corporation_id
)
# WandererApp.Cache.insert(
# "map:#{map_id}:character:#{character_id}:solar_system_id",
# solar_system_id
# )
WandererApp.Cache.insert(
"map:#{map_id}:character:#{character_id}:solar_system_id",
solar_system_id
)
# WandererApp.Cache.insert(
# "map:#{map_id}:character:#{character_id}:structure_id",
# structure_id
# )
WandererApp.Cache.insert(
"map:#{map_id}:character:#{character_id}:structure_id",
structure_id
)
# WandererApp.Cache.insert(
# "map:#{map_id}:character:#{character_id}:station_id",
# station_id
# )
WandererApp.Cache.insert(
"map:#{map_id}:character:#{character_id}:station_id",
station_id
)
# 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_type_id",
ship_type_id
)
# WandererApp.Cache.insert(
# "map:#{map_id}:character:#{character_id}:ship_name",
# ship_name
# )
WandererApp.Cache.insert(
"map:#{map_id}:character:#{character_id}:ship_name",
ship_name
)
:ok
:ok
error ->
error
end
_ ->
{:error, :already_exists}

View File

@@ -14,13 +14,16 @@ defmodule WandererApp.Map.Server.CharactersImpl do
map_id: map_id,
tracked: track_character
}),
{:ok, character} <- WandererApp.Character.get_map_character(map_id, character_id) do
{: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, :not_found} ->
:ok
_error ->
{:ok, character} = WandererApp.Character.get_map_character(map_id, character_id)
{:ok, character} = WandererApp.Character.get_character(character_id)
Impl.broadcast!(map_id, :character_added, character)
:ok
end
@@ -207,7 +210,11 @@ defmodule WandererApp.Map.Server.CharactersImpl do
end
def update_characters(%{map_id: map_id} = state) do
{:ok, presence_character_ids} =
WandererApp.Cache.lookup("map_#{map_id}:presence_character_ids", [])
WandererApp.Cache.lookup!("maps:#{map_id}:tracked_characters", [])
|> Enum.filter(fn character_id -> character_id in presence_character_ids end)
|> Enum.map(fn character_id ->
Task.start_link(fn ->
character_updates =
@@ -292,38 +299,47 @@ defmodule WandererApp.Map.Server.CharactersImpl do
old_location,
%{map: map, map_id: map_id, rtree_name: rtree_name, map_opts: map_opts} = _state
) do
start_solar_system_id =
WandererApp.Cache.take("map:#{map_id}:character:#{character_id}:start_solar_system_id")
case is_nil(old_location.solar_system_id) and
is_nil(start_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)
if is_character_in_space?(location) do
if is_nil(start_solar_system_id) || start_solar_system_id == old_location.solar_system_id do
ConnectionsImpl.is_connection_valid(
map.scope,
old_location.solar_system_id,
location.solar_system_id
)
|> case do
true ->
:ok =
ConnectionsImpl.maybe_add_connection(
map_id,
location,
old_location,
character_id,
false
)
end
SystemsImpl.maybe_add_system(map_id, location, old_location, rtree_name, map_opts)
_ ->
:ok
:ok =
SystemsImpl.maybe_add_system(map_id, old_location, location, rtree_name, map_opts)
if is_character_in_space?(location) do
:ok =
ConnectionsImpl.maybe_add_connection(
map_id,
location,
old_location,
character_id,
false
)
end
_ ->
:ok
end
else
# skip adding connection or system if character just started tracking on the map
:ok
end
end
end
@@ -333,7 +349,9 @@ defmodule WandererApp.Map.Server.CharactersImpl do
end
defp track_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, not_present: true)
add_character(%{map_id: map_id}, character, true)
WandererApp.Character.TrackerManager.update_track_settings(character_id, %{
@@ -341,7 +359,8 @@ defmodule WandererApp.Map.Server.CharactersImpl do
track: true,
track_online: true,
track_location: true,
track_ship: true
track_ship: true,
solar_system_id: character.solar_system_id
})
end

View File

@@ -326,8 +326,7 @@ defmodule WandererApp.Map.Server.Impl do
if can_broadcast?(map_id) do
@pubsub_client.broadcast!(WandererApp.PubSub, map_id, %{
event: event,
payload: payload,
timestamp: DateTime.utc_now()
payload: payload
})
end

View File

@@ -329,15 +329,20 @@ defmodule WandererAppWeb.MapRoutesEventHandler do
%{assigns: %{current_user: current_user, has_tracked_characters?: true}} = socket
) do
character_eve_ids
|> Task.async_stream(fn character_eve_id ->
set_autopilot_waypoint(
current_user,
character_eve_id,
add_to_beginning,
clear_other_waypoints,
destination_id
)
end)
|> Task.async_stream(
fn character_eve_id ->
set_autopilot_waypoint(
current_user,
character_eve_id,
add_to_beginning,
clear_other_waypoints,
destination_id
)
end,
max_concurrency: System.schedulers_online(),
on_timeout: :kill_task,
timeout: :timer.minutes(1)
)
|> Enum.map(fn _result -> :skip end)
{:noreply, socket}

View File

@@ -105,19 +105,23 @@ defmodule WandererAppWeb.MapSystemCommentsEventHandler do
} =
socket
) do
system =
WandererApp.Map.find_system_by_location(map_id, %{
solar_system_id: solar_system_id |> String.to_integer()
})
WandererApp.Map.find_system_by_location(map_id, %{
solar_system_id: solar_system_id |> String.to_integer()
})
|> case do
%{id: system_id} when not is_nil(system_id) ->
{:ok, comments} = WandererApp.MapSystemCommentRepo.get_by_system(system_id)
{:ok, comments} = WandererApp.MapSystemCommentRepo.get_by_system(system.id)
comments =
comments
|> Enum.map(fn c -> c |> Ash.load!([:character, :system]) end)
|> Enum.map(&map_system_comment/1)
comments =
comments
|> Enum.map(fn c -> c |> Ash.load!([:character, :system]) end)
|> Enum.map(&map_system_comment/1)
{:reply, %{comments: comments}, socket}
{:reply, %{comments: comments}, socket}
_ ->
{:reply, %{comments: []}, socket}
end
end
def handle_ui_event(

View File

@@ -306,8 +306,7 @@ defmodule WandererAppWeb.MapEventHandler do
socket
|> Phoenix.LiveView.Utils.push_event("map_event", %{
type: type,
body: body,
timestamp: DateTime.utc_now()
body: body
})
end

View File

@@ -4,8 +4,6 @@ defmodule WandererAppWeb.MapLive do
require Logger
@server_event_unsync_timeout :timer.minutes(2)
@impl true
def mount(%{"slug" => map_slug} = _params, _session, socket) when is_connected?(socket) do
Process.send_after(self(), %{event: :load_map}, Enum.random(10..800))
@@ -96,16 +94,10 @@ defmodule WandererAppWeb.MapLive do
|> push_navigate(to: ~p"/tracking/#{map_slug}")}
@impl true
def handle_info(%{timestamp: timestamp} = info, %{assigns: %{map_slug: map_slug}} = socket) do
duration = DateTime.diff(DateTime.utc_now(), timestamp, :millisecond)
if duration > @server_event_unsync_timeout do
{:noreply, socket |> push_navigate(to: ~p"/#{map_slug}")}
else
{:noreply,
socket
|> WandererAppWeb.MapEventHandler.handle_event(info)}
end
def handle_info(info, %{assigns: %{map_slug: map_slug}} = socket) do
{:noreply,
socket
|> WandererAppWeb.MapEventHandler.handle_event(info)}
end
@impl true

View File

@@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do
@source_url "https://github.com/wanderer-industries/wanderer"
@version "1.65.13"
@version "1.65.24"
def project do
[