mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-07 16:25:37 +00:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d00b4843a7 | ||
|
|
6068de2c71 | ||
|
|
73da427c6b | ||
|
|
9b7ec0ddfe | ||
|
|
c2f5f14c44 | ||
|
|
0b7c3588d5 | ||
|
|
a51fac5736 | ||
|
|
726c3d0704 | ||
|
|
8dd564dbd0 | ||
|
|
e33c65cddc | ||
|
|
f2fbd2ead0 | ||
|
|
123a2e45eb | ||
|
|
f8d2d9c680 | ||
|
|
9dcbef9a79 | ||
|
|
0b14857a12 | ||
|
|
bd3d516f60 | ||
|
|
40d0bd8cea | ||
|
|
968deeb254 | ||
|
|
959041be52 | ||
|
|
3319520179 | ||
|
|
580fcf3657 | ||
|
|
53dae7c520 | ||
|
|
6d59d709f1 | ||
|
|
4343e9070c | ||
|
|
b62373fb5f | ||
|
|
3da98f8e56 | ||
|
|
494d24952e | ||
|
|
8a6b17bd7b | ||
|
|
d2e859a74e | ||
|
|
4a78d55d22 | ||
|
|
dc252b8c4b | ||
|
|
c433205e89 | ||
|
|
d6bc5b57b1 |
81
CHANGELOG.md
81
CHANGELOG.md
@@ -2,6 +2,87 @@
|
||||
|
||||
<!-- changelog -->
|
||||
|
||||
## [v1.77.15](https://github.com/wanderer-industries/wanderer/compare/v1.77.14...v1.77.15) (2025-09-10)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Map: Fix problem with unnecessary rerenders and loads routes if move/positioning widgets.
|
||||
|
||||
## [v1.77.14](https://github.com/wanderer-industries/wanderer/compare/v1.77.13...v1.77.14) (2025-09-08)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Fixed issue with loading connection info
|
||||
|
||||
## [v1.77.13](https://github.com/wanderer-industries/wanderer/compare/v1.77.12...v1.77.13) (2025-09-07)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Updated character tracking, added an extra check for offline characters to reduce errors
|
||||
|
||||
## [v1.77.12](https://github.com/wanderer-industries/wanderer/compare/v1.77.11...v1.77.12) (2025-09-07)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Decreased character tracking grace period
|
||||
|
||||
## [v1.77.11](https://github.com/wanderer-industries/wanderer/compare/v1.77.10...v1.77.11) (2025-09-07)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Fixed CSP errors
|
||||
|
||||
## [v1.77.10](https://github.com/wanderer-industries/wanderer/compare/v1.77.9...v1.77.10) (2025-09-04)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Removed invalid invite options
|
||||
|
||||
## [v1.77.9](https://github.com/wanderer-industries/wanderer/compare/v1.77.8...v1.77.9) (2025-09-04)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Auto select following char system on start
|
||||
|
||||
## [v1.77.8](https://github.com/wanderer-industries/wanderer/compare/v1.77.7...v1.77.8) (2025-09-03)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Updated character tracking
|
||||
|
||||
## [v1.77.7](https://github.com/wanderer-industries/wanderer/compare/v1.77.6...v1.77.7) (2025-09-03)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Updated character tracking
|
||||
|
||||
## [v1.77.6](https://github.com/wanderer-industries/wanderer/compare/v1.77.5...v1.77.6) (2025-09-02)
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts';
|
||||
import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
|
||||
import { PingData, SolarSystemConnection, SolarSystemRawType } from '@/hooks/Mapper/types';
|
||||
import { MapHandlers, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||
import type { PanelPosition } from '@reactflow/core';
|
||||
import clsx from 'clsx';
|
||||
import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect, useMemo } from 'react';
|
||||
import ReactFlow, {
|
||||
Background,
|
||||
@@ -16,8 +23,6 @@ import ReactFlow, {
|
||||
import 'reactflow/dist/style.css';
|
||||
import classes from './Map.module.scss';
|
||||
import { MapProvider, useMapState } from './MapProvider';
|
||||
import { useEdgesState, useMapHandlers, useNodesState, useUpdateNodes } from './hooks';
|
||||
import { MapHandlers, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import {
|
||||
ContextMenuConnection,
|
||||
ContextMenuRoot,
|
||||
@@ -26,14 +31,9 @@ import {
|
||||
useContextMenuRootHandlers,
|
||||
} from './components';
|
||||
import { getBehaviorForTheme } from './helpers/getThemeBehavior';
|
||||
import { OnMapAddSystemCallback, OnMapSelectionChange } from './map.types';
|
||||
import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
|
||||
import { PingData, SolarSystemConnection, SolarSystemRawType } from '@/hooks/Mapper/types';
|
||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||
import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts';
|
||||
import clsx from 'clsx';
|
||||
import { useEdgesState, useMapHandlers, useNodesState, useUpdateNodes } from './hooks';
|
||||
import { useBackgroundVars } from './hooks/useBackgroundVars';
|
||||
import type { PanelPosition } from '@reactflow/core';
|
||||
import { OnMapAddSystemCallback, OnMapSelectionChange } from './map.types';
|
||||
|
||||
const DEFAULT_VIEW_PORT = { zoom: 1, x: 0, y: 0 };
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { MapData, useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
|
||||
import { useEventBuffer } from '@/hooks/Mapper/hooks';
|
||||
import { SolarSystemConnection, SolarSystemRawType } from '@/hooks/Mapper/types';
|
||||
import { CommandInit } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { useReactFlow } from 'reactflow';
|
||||
@@ -11,6 +13,20 @@ export const useMapInit = () => {
|
||||
const ref = useRef({ rf, data, update });
|
||||
ref.current = { update, data, rf };
|
||||
|
||||
const updateSystems = useCallback((systems: SolarSystemRawType[]) => {
|
||||
const { rf } = ref.current;
|
||||
rf.setNodes(systems.map(convertSystem2Node));
|
||||
}, []);
|
||||
|
||||
const { handleEvent: handleUpdateSystems } = useEventBuffer<any>(updateSystems);
|
||||
|
||||
const updateEdges = useCallback((connections: SolarSystemConnection[]) => {
|
||||
const { rf } = ref.current;
|
||||
rf.setEdges(connections.map(convertConnection2Edge));
|
||||
}, []);
|
||||
|
||||
const { handleEvent: handleUpdateConnections } = useEventBuffer<any>(updateEdges);
|
||||
|
||||
return useCallback(
|
||||
({
|
||||
systems,
|
||||
@@ -24,7 +40,6 @@ export const useMapInit = () => {
|
||||
hubs,
|
||||
}: CommandInit) => {
|
||||
const { update } = ref.current;
|
||||
const { rf } = ref.current;
|
||||
|
||||
const updateData: Partial<MapData> = {};
|
||||
|
||||
@@ -63,11 +78,13 @@ export const useMapInit = () => {
|
||||
update(updateData);
|
||||
|
||||
if (systems) {
|
||||
rf.setNodes(systems.map(convertSystem2Node));
|
||||
handleUpdateSystems(systems);
|
||||
// rf.setNodes(systems.map(convertSystem2Node));
|
||||
}
|
||||
|
||||
if (connections) {
|
||||
rf.setEdges(connections.map(convertConnection2Edge));
|
||||
handleUpdateConnections(connections);
|
||||
// rf.setEdges(connections.map(convertConnection2Edge));
|
||||
}
|
||||
},
|
||||
[],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useReactFlow } from 'reactflow';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { CommandSelectSystems } from '@/hooks/Mapper/types';
|
||||
import { OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts';
|
||||
import { CommandSelectSystems } from '@/hooks/Mapper/types';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { useReactFlow } from 'reactflow';
|
||||
|
||||
export const useSelectSystems = (onSelectionChange: OnMapSelectionChange) => {
|
||||
const rf = useReactFlow();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ForwardedRef, useImperativeHandle, useRef } from 'react';
|
||||
import {
|
||||
CommandAddConnections,
|
||||
CommandAddSystems,
|
||||
@@ -19,8 +18,11 @@ import {
|
||||
CommandUpdateSystems,
|
||||
MapHandlers,
|
||||
} from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { ForwardedRef, useImperativeHandle, useRef } from 'react';
|
||||
|
||||
import { OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts';
|
||||
import {
|
||||
useCenterSystem,
|
||||
useCommandsCharacters,
|
||||
useCommandsConnections,
|
||||
useMapAddSystems,
|
||||
@@ -28,10 +30,8 @@ import {
|
||||
useMapInit,
|
||||
useMapRemoveSystems,
|
||||
useMapUpdateSystems,
|
||||
useCenterSystem,
|
||||
useSelectSystems,
|
||||
} from './api';
|
||||
import { OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts';
|
||||
|
||||
export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange: OnMapSelectionChange) => {
|
||||
const mapInit = useMapInit();
|
||||
@@ -49,91 +49,87 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
|
||||
const { charactersUpdated, presentCharacters, characterAdded, characterRemoved, characterUpdated } =
|
||||
useCommandsCharacters();
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => {
|
||||
return {
|
||||
command(type, data) {
|
||||
switch (type) {
|
||||
case Commands.init:
|
||||
mapInit(data as CommandInit);
|
||||
break;
|
||||
case Commands.addSystems:
|
||||
setTimeout(() => mapAddSystems(data as CommandAddSystems), 100);
|
||||
break;
|
||||
case Commands.updateSystems:
|
||||
mapUpdateSystems(data as CommandUpdateSystems);
|
||||
break;
|
||||
case Commands.removeSystems:
|
||||
setTimeout(() => removeSystems(data as CommandRemoveSystems), 100);
|
||||
break;
|
||||
case Commands.addConnections:
|
||||
setTimeout(() => addConnections(data as CommandAddConnections), 100);
|
||||
break;
|
||||
case Commands.removeConnections:
|
||||
setTimeout(() => removeConnections(data as CommandRemoveConnections), 100);
|
||||
break;
|
||||
case Commands.charactersUpdated:
|
||||
charactersUpdated(data as CommandCharactersUpdated);
|
||||
break;
|
||||
case Commands.characterAdded:
|
||||
characterAdded(data as CommandCharacterAdded);
|
||||
break;
|
||||
case Commands.characterRemoved:
|
||||
characterRemoved(data as CommandCharacterRemoved);
|
||||
break;
|
||||
case Commands.characterUpdated:
|
||||
characterUpdated(data as CommandCharacterUpdated);
|
||||
break;
|
||||
case Commands.presentCharacters:
|
||||
presentCharacters(data as CommandPresentCharacters);
|
||||
break;
|
||||
case Commands.updateConnection:
|
||||
updateConnection(data as CommandUpdateConnection);
|
||||
break;
|
||||
case Commands.mapUpdated:
|
||||
mapUpdated(data as CommandMapUpdated);
|
||||
break;
|
||||
case Commands.killsUpdated:
|
||||
killsUpdated(data as CommandKillsUpdated);
|
||||
break;
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
command(type, data) {
|
||||
switch (type) {
|
||||
case Commands.init:
|
||||
mapInit(data as CommandInit);
|
||||
break;
|
||||
case Commands.addSystems:
|
||||
setTimeout(() => mapAddSystems(data as CommandAddSystems), 100);
|
||||
break;
|
||||
case Commands.updateSystems:
|
||||
mapUpdateSystems(data as CommandUpdateSystems);
|
||||
break;
|
||||
case Commands.removeSystems:
|
||||
setTimeout(() => removeSystems(data as CommandRemoveSystems), 100);
|
||||
break;
|
||||
case Commands.addConnections:
|
||||
setTimeout(() => addConnections(data as CommandAddConnections), 100);
|
||||
break;
|
||||
case Commands.removeConnections:
|
||||
setTimeout(() => removeConnections(data as CommandRemoveConnections), 100);
|
||||
break;
|
||||
case Commands.charactersUpdated:
|
||||
charactersUpdated(data as CommandCharactersUpdated);
|
||||
break;
|
||||
case Commands.characterAdded:
|
||||
characterAdded(data as CommandCharacterAdded);
|
||||
break;
|
||||
case Commands.characterRemoved:
|
||||
characterRemoved(data as CommandCharacterRemoved);
|
||||
break;
|
||||
case Commands.characterUpdated:
|
||||
characterUpdated(data as CommandCharacterUpdated);
|
||||
break;
|
||||
case Commands.presentCharacters:
|
||||
presentCharacters(data as CommandPresentCharacters);
|
||||
break;
|
||||
case Commands.updateConnection:
|
||||
updateConnection(data as CommandUpdateConnection);
|
||||
break;
|
||||
case Commands.mapUpdated:
|
||||
mapUpdated(data as CommandMapUpdated);
|
||||
break;
|
||||
case Commands.killsUpdated:
|
||||
killsUpdated(data as CommandKillsUpdated);
|
||||
break;
|
||||
|
||||
case Commands.centerSystem:
|
||||
setTimeout(() => {
|
||||
const systemId = `${data}`;
|
||||
centerSystem(systemId as CommandSelectSystem);
|
||||
}, 100);
|
||||
break;
|
||||
case Commands.centerSystem:
|
||||
setTimeout(() => {
|
||||
const systemId = `${data}`;
|
||||
centerSystem(systemId as CommandSelectSystem);
|
||||
}, 100);
|
||||
break;
|
||||
|
||||
case Commands.selectSystem:
|
||||
selectSystems({ systems: [data as string], delay: 500 });
|
||||
break;
|
||||
case Commands.selectSystem:
|
||||
selectSystems({ systems: [data as string], delay: 500 });
|
||||
break;
|
||||
|
||||
case Commands.selectSystems:
|
||||
selectSystems(data as CommandSelectSystems);
|
||||
break;
|
||||
case Commands.selectSystems:
|
||||
selectSystems(data as CommandSelectSystems);
|
||||
break;
|
||||
|
||||
case Commands.pingAdded:
|
||||
case Commands.pingCancelled:
|
||||
case Commands.routes:
|
||||
case Commands.signaturesUpdated:
|
||||
case Commands.linkSignatureToSystem:
|
||||
case Commands.detailedKillsUpdated:
|
||||
case Commands.characterActivityData:
|
||||
case Commands.trackingCharactersData:
|
||||
case Commands.updateActivity:
|
||||
case Commands.updateTracking:
|
||||
case Commands.userSettingsUpdated:
|
||||
// do nothing
|
||||
break;
|
||||
case Commands.pingAdded:
|
||||
case Commands.pingCancelled:
|
||||
case Commands.routes:
|
||||
case Commands.signaturesUpdated:
|
||||
case Commands.linkSignatureToSystem:
|
||||
case Commands.detailedKillsUpdated:
|
||||
case Commands.characterActivityData:
|
||||
case Commands.trackingCharactersData:
|
||||
case Commands.updateActivity:
|
||||
case Commands.updateTracking:
|
||||
case Commands.userSettingsUpdated:
|
||||
// do nothing
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn(`Map handlers: Unknown command: ${type}`, data);
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
[],
|
||||
);
|
||||
default:
|
||||
console.warn(`Map handlers: Unknown command: ${type}`, data);
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { Node, useOnViewportChange, useReactFlow } from 'reactflow';
|
||||
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
|
||||
import { SolarSystemRawType } from '@/hooks/Mapper/types';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { Node, useOnViewportChange, useReactFlow } from 'reactflow';
|
||||
|
||||
const useThrottle = () => {
|
||||
const throttleSeed = useRef<number | null>(null);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { RoutesType } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
||||
import { LoadRoutesCommand } from '@/hooks/Mapper/components/mapInterface/widgets/RoutesWidget/types.ts';
|
||||
import { RoutesList } from '@/hooks/Mapper/types/routes.ts';
|
||||
import { flattenValues } from '@/hooks/Mapper/utils/flattenValues.ts';
|
||||
|
||||
function usePrevious<T>(value: T): T | undefined {
|
||||
const ref = useRef<T>();
|
||||
@@ -64,12 +65,8 @@ export const useLoadRoutes = ({
|
||||
systems?.length,
|
||||
connections,
|
||||
hubs,
|
||||
routesSettings,
|
||||
...Object.keys(routesSettings)
|
||||
.sort()
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
.map(x => routesSettings[x]),
|
||||
// we need make it flat recursively
|
||||
...flattenValues(routesSettings),
|
||||
...deps,
|
||||
]);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './useClipboard';
|
||||
export * from './useConfirmPopup';
|
||||
export * from './useEventBuffer';
|
||||
export * from './useHotkey';
|
||||
export * from './usePageVisibility';
|
||||
export * from './useSkipContextMenu';
|
||||
export * from './useThrottle';
|
||||
export * from './useConfirmPopup';
|
||||
|
||||
41
assets/js/hooks/Mapper/hooks/useEventBuffer.ts
Normal file
41
assets/js/hooks/Mapper/hooks/useEventBuffer.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import debounce from 'lodash.debounce';
|
||||
import { useCallback, useRef } from 'react';
|
||||
export type UseEventBufferHandler<T> = (event: T) => void;
|
||||
|
||||
export const useEventBuffer = <T>(handler: UseEventBufferHandler<T>) => {
|
||||
// @ts-ignore
|
||||
const eventsBufferRef = useRef<T[]>([]);
|
||||
|
||||
const eventTick = useCallback(
|
||||
debounce(() => {
|
||||
if (eventsBufferRef.current.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const event = eventsBufferRef.current.shift()!;
|
||||
handler(event);
|
||||
|
||||
// TODO - do not delete THIS code it needs for debug
|
||||
// console.log('JOipP', `Tick Buff`, eventsBufferRef.current.length);
|
||||
|
||||
if (eventsBufferRef.current.length > 0) {
|
||||
eventTick();
|
||||
}
|
||||
}, 10),
|
||||
[],
|
||||
);
|
||||
const eventTickRef = useRef(eventTick);
|
||||
eventTickRef.current = eventTick;
|
||||
|
||||
// @ts-ignore
|
||||
const handleEvent = useCallback(event => {
|
||||
if (!eventTickRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
eventsBufferRef.current.push(event);
|
||||
eventTickRef.current();
|
||||
}, []);
|
||||
|
||||
return { handleEvent };
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback } from 'react';
|
||||
import { CommandInit } from '@/hooks/Mapper/types';
|
||||
import { MapRootData, useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
|
||||
import { CommandInit } from '@/hooks/Mapper/types';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useMapInit = () => {
|
||||
const { update } = useMapRootState();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ForwardedRef, useImperativeHandle } from 'react';
|
||||
import {
|
||||
CommandAddConnections,
|
||||
CommandAddSystems,
|
||||
@@ -8,24 +7,25 @@ import {
|
||||
CommandCharactersUpdated,
|
||||
CommandCharacterUpdated,
|
||||
CommandCommentAdd,
|
||||
CommandCommentRemoved,
|
||||
CommandInit,
|
||||
CommandLinkSignatureToSystem,
|
||||
CommandMapUpdated,
|
||||
CommandPingAdded,
|
||||
CommandPingCancelled,
|
||||
CommandPresentCharacters,
|
||||
CommandRemoveConnections,
|
||||
CommandRemoveSystems,
|
||||
CommandRoutes,
|
||||
Commands,
|
||||
CommandSignaturesUpdated,
|
||||
CommandTrackingCharactersData,
|
||||
CommandUpdateConnection,
|
||||
CommandUpdateSystems,
|
||||
CommandUserSettingsUpdated,
|
||||
Commands,
|
||||
MapHandlers,
|
||||
CommandCommentRemoved,
|
||||
CommandPingAdded,
|
||||
CommandPingCancelled,
|
||||
} from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { ForwardedRef, useImperativeHandle } from 'react';
|
||||
|
||||
import {
|
||||
useCommandComments,
|
||||
@@ -39,9 +39,9 @@ import {
|
||||
useUserRoutes,
|
||||
} from './api';
|
||||
|
||||
import { useCommandsActivity } from './api/useCommandsActivity';
|
||||
import { emitMapEvent } from '@/hooks/Mapper/events';
|
||||
import { DetailedKill } from '../../types/kills';
|
||||
import { useCommandsActivity } from './api/useCommandsActivity';
|
||||
|
||||
export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => {
|
||||
const mapInit = useMapInit();
|
||||
@@ -63,127 +63,123 @@ export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => {
|
||||
const { pingAdded, pingCancelled } = useCommandPings();
|
||||
const { characterActivityData, trackingCharactersData, userSettingsUpdated } = useCommandsActivity();
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => {
|
||||
return {
|
||||
command(type, data) {
|
||||
switch (type) {
|
||||
case Commands.init: // USED
|
||||
mapInit(data as CommandInit);
|
||||
break;
|
||||
case Commands.addSystems: // USED
|
||||
addSystems(data as CommandAddSystems);
|
||||
break;
|
||||
case Commands.updateSystems: // USED
|
||||
updateSystems(data as CommandUpdateSystems);
|
||||
break;
|
||||
case Commands.removeSystems: // USED
|
||||
removeSystems(data as CommandRemoveSystems);
|
||||
break;
|
||||
case Commands.addConnections: // USED
|
||||
addConnections(data as CommandAddConnections);
|
||||
break;
|
||||
case Commands.removeConnections: // USED
|
||||
removeConnections(data as CommandRemoveConnections);
|
||||
break;
|
||||
case Commands.updateConnection: // USED
|
||||
updateConnection(data as CommandUpdateConnection);
|
||||
break;
|
||||
case Commands.charactersUpdated: // USED
|
||||
charactersUpdated(data as CommandCharactersUpdated);
|
||||
break;
|
||||
case Commands.characterAdded: // USED
|
||||
characterAdded(data as CommandCharacterAdded);
|
||||
break;
|
||||
case Commands.characterRemoved: // USED
|
||||
characterRemoved(data as CommandCharacterRemoved);
|
||||
break;
|
||||
case Commands.characterUpdated: // USED
|
||||
characterUpdated(data as CommandCharacterUpdated);
|
||||
break;
|
||||
case Commands.presentCharacters: // USED
|
||||
presentCharacters(data as CommandPresentCharacters);
|
||||
break;
|
||||
case Commands.mapUpdated: // USED
|
||||
mapUpdated(data as CommandMapUpdated);
|
||||
break;
|
||||
case Commands.routes:
|
||||
mapRoutes(data as CommandRoutes);
|
||||
break;
|
||||
case Commands.userRoutes:
|
||||
mapUserRoutes(data as CommandRoutes);
|
||||
break;
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
command(type, data) {
|
||||
switch (type) {
|
||||
case Commands.init: // USED
|
||||
mapInit(data as CommandInit);
|
||||
break;
|
||||
case Commands.addSystems: // USED
|
||||
addSystems(data as CommandAddSystems);
|
||||
break;
|
||||
case Commands.updateSystems: // USED
|
||||
updateSystems(data as CommandUpdateSystems);
|
||||
break;
|
||||
case Commands.removeSystems: // USED
|
||||
removeSystems(data as CommandRemoveSystems);
|
||||
break;
|
||||
case Commands.addConnections: // USED
|
||||
addConnections(data as CommandAddConnections);
|
||||
break;
|
||||
case Commands.removeConnections: // USED
|
||||
removeConnections(data as CommandRemoveConnections);
|
||||
break;
|
||||
case Commands.updateConnection: // USED
|
||||
updateConnection(data as CommandUpdateConnection);
|
||||
break;
|
||||
case Commands.charactersUpdated: // USED
|
||||
charactersUpdated(data as CommandCharactersUpdated);
|
||||
break;
|
||||
case Commands.characterAdded: // USED
|
||||
characterAdded(data as CommandCharacterAdded);
|
||||
break;
|
||||
case Commands.characterRemoved: // USED
|
||||
characterRemoved(data as CommandCharacterRemoved);
|
||||
break;
|
||||
case Commands.characterUpdated: // USED
|
||||
characterUpdated(data as CommandCharacterUpdated);
|
||||
break;
|
||||
case Commands.presentCharacters: // USED
|
||||
presentCharacters(data as CommandPresentCharacters);
|
||||
break;
|
||||
case Commands.mapUpdated: // USED
|
||||
mapUpdated(data as CommandMapUpdated);
|
||||
break;
|
||||
case Commands.routes:
|
||||
mapRoutes(data as CommandRoutes);
|
||||
break;
|
||||
case Commands.userRoutes:
|
||||
mapUserRoutes(data as CommandRoutes);
|
||||
break;
|
||||
|
||||
case Commands.signaturesUpdated: // USED
|
||||
updateSystemSignatures(data as CommandSignaturesUpdated);
|
||||
break;
|
||||
case Commands.signaturesUpdated: // USED
|
||||
updateSystemSignatures(data as CommandSignaturesUpdated);
|
||||
break;
|
||||
|
||||
case Commands.linkSignatureToSystem: // USED
|
||||
setTimeout(() => {
|
||||
updateLinkSignatureToSystem(data as CommandLinkSignatureToSystem);
|
||||
}, 200);
|
||||
break;
|
||||
case Commands.linkSignatureToSystem: // USED
|
||||
setTimeout(() => {
|
||||
updateLinkSignatureToSystem(data as CommandLinkSignatureToSystem);
|
||||
}, 200);
|
||||
break;
|
||||
|
||||
case Commands.centerSystem: // USED
|
||||
// do nothing here
|
||||
break;
|
||||
case Commands.centerSystem: // USED
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
case Commands.selectSystem: // USED
|
||||
// do nothing here
|
||||
break;
|
||||
case Commands.selectSystem: // USED
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
case Commands.killsUpdated:
|
||||
// do nothing here
|
||||
break;
|
||||
case Commands.killsUpdated:
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
case Commands.detailedKillsUpdated:
|
||||
updateDetailedKills(data as Record<string, DetailedKill[]>);
|
||||
break;
|
||||
case Commands.detailedKillsUpdated:
|
||||
updateDetailedKills(data as Record<string, DetailedKill[]>);
|
||||
break;
|
||||
|
||||
case Commands.characterActivityData:
|
||||
characterActivityData(data as CommandCharacterActivityData);
|
||||
break;
|
||||
case Commands.characterActivityData:
|
||||
characterActivityData(data as CommandCharacterActivityData);
|
||||
break;
|
||||
|
||||
case Commands.trackingCharactersData:
|
||||
trackingCharactersData(data as CommandTrackingCharactersData);
|
||||
break;
|
||||
case Commands.trackingCharactersData:
|
||||
trackingCharactersData(data as CommandTrackingCharactersData);
|
||||
break;
|
||||
|
||||
case Commands.updateActivity:
|
||||
break;
|
||||
case Commands.updateActivity:
|
||||
break;
|
||||
|
||||
case Commands.updateTracking:
|
||||
break;
|
||||
case Commands.updateTracking:
|
||||
break;
|
||||
|
||||
case Commands.userSettingsUpdated:
|
||||
userSettingsUpdated(data as CommandUserSettingsUpdated);
|
||||
break;
|
||||
case Commands.userSettingsUpdated:
|
||||
userSettingsUpdated(data as CommandUserSettingsUpdated);
|
||||
break;
|
||||
|
||||
case Commands.systemCommentAdded:
|
||||
addComment(data as CommandCommentAdd);
|
||||
break;
|
||||
case Commands.systemCommentAdded:
|
||||
addComment(data as CommandCommentAdd);
|
||||
break;
|
||||
|
||||
case Commands.systemCommentRemoved:
|
||||
removeComment(data as CommandCommentRemoved);
|
||||
break;
|
||||
case Commands.systemCommentRemoved:
|
||||
removeComment(data as CommandCommentRemoved);
|
||||
break;
|
||||
|
||||
case Commands.pingAdded:
|
||||
pingAdded(data as CommandPingAdded);
|
||||
break;
|
||||
case Commands.pingAdded:
|
||||
pingAdded(data as CommandPingAdded);
|
||||
break;
|
||||
|
||||
case Commands.pingCancelled:
|
||||
pingCancelled(data as CommandPingCancelled);
|
||||
break;
|
||||
case Commands.pingCancelled:
|
||||
pingCancelled(data as CommandPingCancelled);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn(`JOipP Interface handlers: Unknown command: ${type}`, data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.warn(`JOipP Interface handlers: Unknown command: ${type}`, data);
|
||||
break;
|
||||
}
|
||||
|
||||
emitMapEvent({ name: type, data });
|
||||
},
|
||||
};
|
||||
},
|
||||
[],
|
||||
);
|
||||
emitMapEvent({ name: type, data });
|
||||
},
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useEventBuffer } from '@/hooks/Mapper/hooks';
|
||||
import usePageVisibility from '@/hooks/Mapper/hooks/usePageVisibility.ts';
|
||||
|
||||
import { MapHandlers } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { RefObject, useCallback, useEffect, useRef } from 'react';
|
||||
import debounce from 'lodash.debounce';
|
||||
import usePageVisibility from '@/hooks/Mapper/hooks/usePageVisibility.ts';
|
||||
|
||||
// const inIndex = 0;
|
||||
// const prevEventTime = +new Date();
|
||||
@@ -10,10 +11,28 @@ const LAST_VERSION_KEY = 'wandererLastVersion';
|
||||
// @ts-ignore
|
||||
export const useMapperHandlers = (handlerRefs: RefObject<MapHandlers>[], hooksRef: RefObject<any>) => {
|
||||
const visible = usePageVisibility();
|
||||
|
||||
const wasHiddenOnce = useRef(false);
|
||||
const visibleRef = useRef(visible);
|
||||
visibleRef.current = visible;
|
||||
|
||||
// @ts-ignore
|
||||
const handleBufferedEvent = useCallback(({ type, body }) => {
|
||||
if (!visibleRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
handlerRefs.forEach(ref => {
|
||||
if (!ref.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
ref.current?.command(type, body);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const { handleEvent: handleMapEvent } = useEventBuffer<any>(handleBufferedEvent);
|
||||
|
||||
// TODO - do not delete THIS code it needs for debug
|
||||
// const [record, setRecord] = useLocalStorageState<boolean>('record', {
|
||||
// defaultValue: false,
|
||||
@@ -54,52 +73,6 @@ export const useMapperHandlers = (handlerRefs: RefObject<MapHandlers>[], hooksRe
|
||||
[hooksRef.current],
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
const eventsBufferRef = useRef<{ type; body }[]>([]);
|
||||
|
||||
const eventTick = useCallback(
|
||||
debounce(() => {
|
||||
if (eventsBufferRef.current.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { type, body } = eventsBufferRef.current.shift()!;
|
||||
handlerRefs.forEach(ref => {
|
||||
if (!ref.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
ref.current?.command(type, body);
|
||||
});
|
||||
|
||||
// TODO - do not delete THIS code it needs for debug
|
||||
// console.log('JOipP', `Tick Buff`, eventsBufferRef.current.length);
|
||||
|
||||
if (eventsBufferRef.current.length > 0) {
|
||||
eventTick();
|
||||
}
|
||||
}, 10),
|
||||
[],
|
||||
);
|
||||
const eventTickRef = useRef(eventTick);
|
||||
eventTickRef.current = eventTick;
|
||||
|
||||
// @ts-ignore
|
||||
const handleMapEvent = useCallback(({ type, body }) => {
|
||||
// TODO - do not delete THIS code it needs for debug
|
||||
// const currentTime = +new Date();
|
||||
// const timeDiff = currentTime - prevEventTime;
|
||||
// prevEventTime = currentTime;
|
||||
// console.log('JOipP', `IN [${inIndex++}] [${timeDiff}] ${getFormattedTime()}`, { type, body });
|
||||
|
||||
if (!eventTickRef.current || !visibleRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
eventsBufferRef.current.push({ type, body });
|
||||
eventTickRef.current();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!visible && !wasHiddenOnce.current) {
|
||||
wasHiddenOnce.current = true;
|
||||
|
||||
132
assets/js/hooks/Mapper/utils/flattenValues.ts
Normal file
132
assets/js/hooks/Mapper/utils/flattenValues.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
const TYPE_ORDER = [
|
||||
'undefined',
|
||||
'null',
|
||||
'boolean',
|
||||
'number',
|
||||
'bigint',
|
||||
'string',
|
||||
'symbol',
|
||||
'function',
|
||||
'date',
|
||||
'regexp',
|
||||
'other',
|
||||
] as const;
|
||||
type TypeTag = (typeof TYPE_ORDER)[number];
|
||||
|
||||
const getTypeTag = (v: unknown): TypeTag => {
|
||||
if (v === undefined) return 'undefined';
|
||||
if (v === null) return 'null';
|
||||
const t = typeof v;
|
||||
if (t === 'boolean' || t === 'number' || t === 'bigint' || t === 'string' || t === 'symbol' || t === 'function')
|
||||
return t as TypeTag;
|
||||
const tag = Object.prototype.toString.call(v);
|
||||
if (tag === '[object Date]') return 'date';
|
||||
if (tag === '[object RegExp]') return 'regexp';
|
||||
return 'other';
|
||||
};
|
||||
|
||||
const cmp = (a: unknown, b: unknown): number => {
|
||||
const ta = getTypeTag(a);
|
||||
const tb = getTypeTag(b);
|
||||
if (ta !== tb) return TYPE_ORDER.indexOf(ta) - TYPE_ORDER.indexOf(tb);
|
||||
|
||||
switch (ta) {
|
||||
case 'undefined':
|
||||
case 'null':
|
||||
return 0;
|
||||
case 'boolean':
|
||||
return (a as boolean) === (b as boolean) ? 0 : a ? 1 : -1;
|
||||
case 'number': {
|
||||
const na = a as number,
|
||||
nb = b as number;
|
||||
const aIsNaN = Number.isNaN(na),
|
||||
bIsNaN = Number.isNaN(nb);
|
||||
if (aIsNaN || bIsNaN) return aIsNaN && bIsNaN ? 0 : aIsNaN ? 1 : -1; // NaN в конец чисел
|
||||
return na === nb ? 0 : na < nb ? -1 : 1;
|
||||
}
|
||||
case 'bigint': {
|
||||
const ba = a as bigint,
|
||||
bb = b as bigint;
|
||||
return ba === bb ? 0 : ba < bb ? -1 : 1;
|
||||
}
|
||||
case 'string':
|
||||
return (a as string).localeCompare(b as string);
|
||||
case 'symbol': {
|
||||
const da = (a as symbol).description ?? '';
|
||||
const db = (b as symbol).description ?? '';
|
||||
return da.localeCompare(db);
|
||||
}
|
||||
case 'function':
|
||||
// @ts-ignore
|
||||
return ((a as Function).name || '').localeCompare((b as Function).name || '');
|
||||
case 'date':
|
||||
return (a as Date).getTime() - (b as Date).getTime();
|
||||
case 'regexp':
|
||||
return a!.toString().localeCompare(b!.toString());
|
||||
default:
|
||||
return String(a).localeCompare(String(b));
|
||||
}
|
||||
};
|
||||
|
||||
const isIterable = (v: unknown): v is Iterable<unknown> =>
|
||||
v != null && typeof (v as any)[Symbol.iterator] === 'function';
|
||||
|
||||
const pushTypedArrayValues = (v: unknown, out: unknown[]) => {
|
||||
if (ArrayBuffer.isView(v) && !(v instanceof DataView)) {
|
||||
// @ts-ignore
|
||||
out.push(...(v as ArrayLike<number> as any));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate this func with ChatGPT 5. Cause it pure func and looks like what i need
|
||||
* May be in net we can find smtng like that
|
||||
* @param input
|
||||
*/
|
||||
export const flattenValues = (input: unknown): unknown[] => {
|
||||
const out: unknown[] = [];
|
||||
const seen = new WeakSet<object>();
|
||||
|
||||
const visit = (v: unknown): void => {
|
||||
const tag = getTypeTag(v);
|
||||
if (tag !== 'other') {
|
||||
out.push(v);
|
||||
return;
|
||||
}
|
||||
|
||||
if (v && typeof v === 'object') {
|
||||
if (seen.has(v)) return;
|
||||
seen.add(v);
|
||||
|
||||
if (pushTypedArrayValues(v, out)) return;
|
||||
|
||||
if (v instanceof Map) {
|
||||
for (const val of v.values()) visit(val);
|
||||
return;
|
||||
}
|
||||
|
||||
if (v instanceof Set) {
|
||||
for (const val of v.values()) visit(val);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(v) || isIterable(v)) {
|
||||
for (const item of v as Iterable<unknown>) visit(item);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const key of Object.keys(v)) {
|
||||
// @ts-ignore
|
||||
visit((v as never)[key]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
out.push(v);
|
||||
};
|
||||
|
||||
visit(input);
|
||||
return out.sort(cmp);
|
||||
};
|
||||
@@ -37,13 +37,14 @@ defmodule WandererApp.Character.Tracker do
|
||||
}
|
||||
|
||||
@pause_tracking_timeout :timer.minutes(60 * 10)
|
||||
@offline_timeout :timer.minutes(10)
|
||||
@offline_timeout :timer.minutes(5)
|
||||
@online_error_timeout :timer.minutes(10)
|
||||
@ship_error_timeout :timer.minutes(10)
|
||||
@location_error_timeout :timer.minutes(10)
|
||||
@online_forbidden_ttl :timer.seconds(7)
|
||||
@offline_check_delay_ttl :timer.seconds(15)
|
||||
@online_limit_ttl :timer.seconds(7)
|
||||
@forbidden_ttl :timer.seconds(5)
|
||||
@forbidden_ttl :timer.seconds(10)
|
||||
@limit_ttl :timer.seconds(5)
|
||||
@location_limit_ttl :timer.seconds(1)
|
||||
@pubsub_client Application.compile_env(:wanderer_app, :pubsub_client)
|
||||
@@ -71,18 +72,19 @@ defmodule WandererApp.Character.Tracker do
|
||||
WandererApp.Cache.lookup!("character:#{character_id}:last_online_time")
|
||||
|> case do
|
||||
nil ->
|
||||
WandererApp.Cache.insert(
|
||||
"character:#{character_id}:last_online_time",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
|
||||
:ok
|
||||
|
||||
last_online_time ->
|
||||
duration = DateTime.diff(DateTime.utc_now(), last_online_time, :millisecond)
|
||||
|
||||
if duration >= @offline_timeout do
|
||||
pause_tracking(character_id)
|
||||
WandererApp.Character.update_character(character_id, %{online: false})
|
||||
|
||||
WandererApp.Character.update_character_state(character_id, %{
|
||||
is_online: false
|
||||
})
|
||||
|
||||
WandererApp.Cache.delete("character:#{character_id}:last_online_time")
|
||||
|
||||
:ok
|
||||
else
|
||||
@@ -186,7 +188,9 @@ defmodule WandererApp.Character.Tracker do
|
||||
|> WandererApp.Character.get_character_state!()
|
||||
|> update_online()
|
||||
|
||||
def update_online(%{track_online: true, character_id: character_id} = character_state) do
|
||||
def update_online(
|
||||
%{track_online: true, character_id: character_id, is_online: is_online} = character_state
|
||||
) do
|
||||
case WandererApp.Character.get_character(character_id) do
|
||||
{:ok, %{eve_id: eve_id, access_token: access_token, tracking_pool: tracking_pool}}
|
||||
when not is_nil(access_token) ->
|
||||
@@ -197,8 +201,6 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :skipped}
|
||||
|
||||
_ ->
|
||||
# Monitor cache for potential evictions before ESI call
|
||||
|
||||
case WandererApp.Esi.get_character_online(eve_id,
|
||||
access_token: access_token,
|
||||
character_id: character_id
|
||||
@@ -211,70 +213,67 @@ defmodule WandererApp.Character.Tracker do
|
||||
"character:#{character_id}:last_online_time",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
|
||||
WandererApp.Cache.delete("character:#{character_id}:online_forbidden")
|
||||
else
|
||||
# Delay next online updates for offline characters
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:online_forbidden",
|
||||
true,
|
||||
ttl: @offline_check_delay_ttl
|
||||
)
|
||||
end
|
||||
|
||||
if online.online == true && online.online != is_online do
|
||||
WandererApp.Cache.delete("character:#{character_id}:ship_error_time")
|
||||
WandererApp.Cache.delete("character:#{character_id}:location_error_time")
|
||||
WandererApp.Cache.delete("character:#{character_id}:info_forbidden")
|
||||
WandererApp.Cache.delete("character:#{character_id}:ship_forbidden")
|
||||
WandererApp.Cache.delete("character:#{character_id}:location_forbidden")
|
||||
WandererApp.Cache.delete("character:#{character_id}:wallet_forbidden")
|
||||
WandererApp.Cache.delete("character:#{character_id}:corporation_info_forbidden")
|
||||
end
|
||||
|
||||
WandererApp.Cache.delete("character:#{character_id}:online_forbidden")
|
||||
WandererApp.Cache.delete("character:#{character_id}:online_error_time")
|
||||
WandererApp.Cache.delete("character:#{character_id}:ship_error_time")
|
||||
WandererApp.Cache.delete("character:#{character_id}:location_error_time")
|
||||
WandererApp.Cache.delete("character:#{character_id}:info_forbidden")
|
||||
WandererApp.Cache.delete("character:#{character_id}:ship_forbidden")
|
||||
WandererApp.Cache.delete("character:#{character_id}:location_forbidden")
|
||||
WandererApp.Cache.delete("character:#{character_id}:wallet_forbidden")
|
||||
|
||||
try do
|
||||
WandererApp.Character.update_character(character_id, online)
|
||||
rescue
|
||||
error ->
|
||||
Logger.error("DB_ERROR: Failed to update character in database",
|
||||
character_id: character_id,
|
||||
error: inspect(error),
|
||||
operation: "update_character_online"
|
||||
)
|
||||
if online.online != is_online do
|
||||
try do
|
||||
WandererApp.Character.update_character(character_id, online)
|
||||
rescue
|
||||
error ->
|
||||
Logger.error("DB_ERROR: Failed to update character in database",
|
||||
character_id: character_id,
|
||||
error: inspect(error),
|
||||
operation: "update_character_online"
|
||||
)
|
||||
|
||||
# Re-raise to maintain existing error handling
|
||||
reraise error, __STACKTRACE__
|
||||
end
|
||||
# Re-raise to maintain existing error handling
|
||||
reraise error, __STACKTRACE__
|
||||
end
|
||||
|
||||
update = %{
|
||||
character_state
|
||||
| is_online: online.online,
|
||||
track_ship: online.online,
|
||||
track_location: online.online
|
||||
}
|
||||
try do
|
||||
WandererApp.Character.update_character_state(character_id, %{
|
||||
character_state
|
||||
| is_online: online.online,
|
||||
track_ship: online.online,
|
||||
track_location: online.online
|
||||
})
|
||||
rescue
|
||||
error ->
|
||||
Logger.error("DB_ERROR: Failed to update character state in database",
|
||||
character_id: character_id,
|
||||
error: inspect(error),
|
||||
operation: "update_character_state"
|
||||
)
|
||||
|
||||
try do
|
||||
WandererApp.Character.update_character_state(character_id, update)
|
||||
rescue
|
||||
error ->
|
||||
Logger.error("DB_ERROR: Failed to update character state in database",
|
||||
character_id: character_id,
|
||||
error: inspect(error),
|
||||
operation: "update_character_state"
|
||||
)
|
||||
|
||||
# Re-raise to maintain existing error handling
|
||||
reraise error, __STACKTRACE__
|
||||
# Re-raise to maintain existing error handling
|
||||
reraise error, __STACKTRACE__
|
||||
end
|
||||
end
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} when error in [:forbidden, :not_found, :timeout] ->
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute([:wanderer_app, :esi, :error], %{count: 1}, %{
|
||||
endpoint: "character_online",
|
||||
error_type: error,
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
})
|
||||
|
||||
Logger.warning("ESI_ERROR: Character online tracking failed #{inspect(error)}",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
error_type: error,
|
||||
endpoint: "character_online"
|
||||
)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:online_forbidden",
|
||||
true,
|
||||
@@ -301,28 +300,6 @@ defmodule WandererApp.Character.Tracker do
|
||||
remaining =
|
||||
Map.get(headers, "x-esi-error-limit-remain", ["unknown"]) |> List.first()
|
||||
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute(
|
||||
[:wanderer_app, :esi, :rate_limited],
|
||||
%{
|
||||
reset_duration: reset_timeout,
|
||||
count: 1
|
||||
},
|
||||
%{
|
||||
endpoint: "character_online",
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
}
|
||||
)
|
||||
|
||||
Logger.warning("ESI_RATE_LIMITED: Character online tracking rate limited",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
endpoint: "character_online",
|
||||
reset_seconds: reset_seconds,
|
||||
remaining_requests: remaining
|
||||
)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:online_forbidden",
|
||||
true,
|
||||
@@ -332,15 +309,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :skipped}
|
||||
|
||||
{:error, error} ->
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute([:wanderer_app, :esi, :error], %{count: 1}, %{
|
||||
endpoint: "character_online",
|
||||
error_type: error,
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
})
|
||||
|
||||
Logger.error("ESI_ERROR: Character online tracking failed",
|
||||
Logger.error("ESI_ERROR: Character online tracking failed: #{inspect(error)}",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
error_type: error,
|
||||
@@ -417,21 +386,6 @@ defmodule WandererApp.Character.Tracker do
|
||||
:ok
|
||||
|
||||
{:error, error} when error in [:forbidden, :not_found, :timeout] ->
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute([:wanderer_app, :esi, :error], %{count: 1}, %{
|
||||
endpoint: "character_info",
|
||||
error_type: error,
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
})
|
||||
|
||||
Logger.warning("ESI_ERROR: Character info tracking failed",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
error_type: error,
|
||||
endpoint: "character_info"
|
||||
)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:info_forbidden",
|
||||
true,
|
||||
@@ -443,33 +397,6 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :error_limited, headers} ->
|
||||
reset_timeout = get_reset_timeout(headers)
|
||||
|
||||
reset_seconds =
|
||||
Map.get(headers, "x-esi-error-limit-reset", ["unknown"]) |> List.first()
|
||||
|
||||
remaining = Map.get(headers, "x-esi-error-limit-remain", ["unknown"]) |> List.first()
|
||||
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute(
|
||||
[:wanderer_app, :esi, :rate_limited],
|
||||
%{
|
||||
reset_duration: reset_timeout,
|
||||
count: 1
|
||||
},
|
||||
%{
|
||||
endpoint: "character_info",
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
}
|
||||
)
|
||||
|
||||
Logger.warning("ESI_RATE_LIMITED: Character info tracking rate limited",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
endpoint: "character_info",
|
||||
reset_seconds: reset_seconds,
|
||||
remaining_requests: remaining
|
||||
)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:info_forbidden",
|
||||
true,
|
||||
@@ -479,21 +406,13 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :error_limited}
|
||||
|
||||
{:error, error} ->
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute([:wanderer_app, :esi, :error], %{count: 1}, %{
|
||||
endpoint: "character_info",
|
||||
error_type: error,
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
})
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:info_forbidden",
|
||||
true,
|
||||
ttl: @forbidden_ttl
|
||||
)
|
||||
|
||||
Logger.error("ESI_ERROR: Character info tracking failed",
|
||||
Logger.error("ESI_ERROR: Character info tracking failed: #{inspect(error)}",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
error_type: error,
|
||||
@@ -540,21 +459,6 @@ defmodule WandererApp.Character.Tracker do
|
||||
:ok
|
||||
|
||||
{:error, error} when error in [:forbidden, :not_found, :timeout] ->
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute([:wanderer_app, :esi, :error], %{count: 1}, %{
|
||||
endpoint: "character_ship",
|
||||
error_type: error,
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
})
|
||||
|
||||
Logger.warning("ESI_ERROR: Character ship tracking failed",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
error_type: error,
|
||||
endpoint: "character_ship"
|
||||
)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:ship_forbidden",
|
||||
true,
|
||||
@@ -573,34 +477,6 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :error_limited, headers} ->
|
||||
reset_timeout = get_reset_timeout(headers)
|
||||
|
||||
reset_seconds =
|
||||
Map.get(headers, "x-esi-error-limit-reset", ["unknown"]) |> List.first()
|
||||
|
||||
remaining =
|
||||
Map.get(headers, "x-esi-error-limit-remain", ["unknown"]) |> List.first()
|
||||
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute(
|
||||
[:wanderer_app, :esi, :rate_limited],
|
||||
%{
|
||||
reset_duration: reset_timeout,
|
||||
count: 1
|
||||
},
|
||||
%{
|
||||
endpoint: "character_ship",
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
}
|
||||
)
|
||||
|
||||
Logger.warning("ESI_RATE_LIMITED: Character ship tracking rate limited",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
endpoint: "character_ship",
|
||||
reset_seconds: reset_seconds,
|
||||
remaining_requests: remaining
|
||||
)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:ship_forbidden",
|
||||
true,
|
||||
@@ -610,15 +486,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :error_limited}
|
||||
|
||||
{:error, error} ->
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute([:wanderer_app, :esi, :error], %{count: 1}, %{
|
||||
endpoint: "character_ship",
|
||||
error_type: error,
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
})
|
||||
|
||||
Logger.error("ESI_ERROR: Character ship tracking failed",
|
||||
Logger.error("ESI_ERROR: Character ship tracking failed: #{inspect(error)}",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
error_type: error,
|
||||
@@ -641,14 +509,6 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, error}
|
||||
|
||||
_ ->
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute([:wanderer_app, :esi, :error], %{count: 1}, %{
|
||||
endpoint: "character_ship",
|
||||
error_type: "wrong_response",
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
})
|
||||
|
||||
Logger.error("ESI_ERROR: Character ship tracking failed - wrong response",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
@@ -711,14 +571,6 @@ defmodule WandererApp.Character.Tracker do
|
||||
:ok
|
||||
|
||||
{:error, error} when error in [:forbidden, :not_found, :timeout] ->
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute([:wanderer_app, :esi, :error], %{count: 1}, %{
|
||||
endpoint: "character_location",
|
||||
error_type: error,
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
})
|
||||
|
||||
Logger.warning("ESI_ERROR: Character location tracking failed",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
@@ -740,34 +592,6 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :error_limited, headers} ->
|
||||
reset_timeout = get_reset_timeout(headers, @location_limit_ttl)
|
||||
|
||||
reset_seconds =
|
||||
Map.get(headers, "x-esi-error-limit-reset", ["unknown"]) |> List.first()
|
||||
|
||||
remaining =
|
||||
Map.get(headers, "x-esi-error-limit-remain", ["unknown"]) |> List.first()
|
||||
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute(
|
||||
[:wanderer_app, :esi, :rate_limited],
|
||||
%{
|
||||
reset_duration: reset_timeout,
|
||||
count: 1
|
||||
},
|
||||
%{
|
||||
endpoint: "character_location",
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
}
|
||||
)
|
||||
|
||||
Logger.warning("ESI_RATE_LIMITED: Character location tracking rate limited",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
endpoint: "character_location",
|
||||
reset_seconds: reset_seconds,
|
||||
remaining_requests: remaining
|
||||
)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:location_forbidden",
|
||||
true,
|
||||
@@ -777,15 +601,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :error_limited}
|
||||
|
||||
{:error, error} ->
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute([:wanderer_app, :esi, :error], %{count: 1}, %{
|
||||
endpoint: "character_location",
|
||||
error_type: error,
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
})
|
||||
|
||||
Logger.error("ESI_ERROR: Character location tracking failed",
|
||||
Logger.error("ESI_ERROR: Character location tracking failed: #{inspect(error)}",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
error_type: error,
|
||||
@@ -804,14 +620,6 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :skipped}
|
||||
|
||||
_ ->
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute([:wanderer_app, :esi, :error], %{count: 1}, %{
|
||||
endpoint: "character_location",
|
||||
error_type: "wrong_response",
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
})
|
||||
|
||||
Logger.error("ESI_ERROR: Character location tracking failed - wrong response",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
@@ -873,14 +681,6 @@ defmodule WandererApp.Character.Tracker do
|
||||
:ok
|
||||
|
||||
{:error, error} when error in [:forbidden, :not_found, :timeout] ->
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute([:wanderer_app, :esi, :error], %{count: 1}, %{
|
||||
endpoint: "character_wallet",
|
||||
error_type: error,
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
})
|
||||
|
||||
Logger.warning("ESI_ERROR: Character wallet tracking failed",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
@@ -899,34 +699,6 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :error_limited, headers} ->
|
||||
reset_timeout = get_reset_timeout(headers)
|
||||
|
||||
reset_seconds =
|
||||
Map.get(headers, "x-esi-error-limit-reset", ["unknown"]) |> List.first()
|
||||
|
||||
remaining =
|
||||
Map.get(headers, "x-esi-error-limit-remain", ["unknown"]) |> List.first()
|
||||
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute(
|
||||
[:wanderer_app, :esi, :rate_limited],
|
||||
%{
|
||||
reset_duration: reset_timeout,
|
||||
count: 1
|
||||
},
|
||||
%{
|
||||
endpoint: "character_wallet",
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
}
|
||||
)
|
||||
|
||||
Logger.warning("ESI_RATE_LIMITED: Character wallet tracking rate limited",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
endpoint: "character_wallet",
|
||||
reset_seconds: reset_seconds,
|
||||
remaining_requests: remaining
|
||||
)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:wallet_forbidden",
|
||||
true,
|
||||
@@ -936,15 +708,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :skipped}
|
||||
|
||||
{:error, error} ->
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute([:wanderer_app, :esi, :error], %{count: 1}, %{
|
||||
endpoint: "character_wallet",
|
||||
error_type: error,
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
})
|
||||
|
||||
Logger.error("ESI_ERROR: Character wallet tracking failed",
|
||||
Logger.error("ESI_ERROR: Character wallet tracking failed: #{inspect(error)}",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
error_type: error,
|
||||
@@ -960,15 +724,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :skipped}
|
||||
|
||||
error ->
|
||||
# Emit telemetry for tracking
|
||||
:telemetry.execute([:wanderer_app, :esi, :error], %{count: 1}, %{
|
||||
endpoint: "character_wallet",
|
||||
error_type: error,
|
||||
tracking_pool: tracking_pool,
|
||||
character_id: character_id
|
||||
})
|
||||
|
||||
Logger.error("ESI_ERROR: Character wallet tracking failed",
|
||||
Logger.error("ESI_ERROR: Character wallet tracking failed: #{inspect(error)}",
|
||||
character_id: character_id,
|
||||
tracking_pool: tracking_pool,
|
||||
error_type: error,
|
||||
@@ -1073,6 +829,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
)
|
||||
when old_corporation_id != corporation_id do
|
||||
(WandererApp.Cache.has_key?("character:#{character_id}:online_forbidden") ||
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:corporation_info_forbidden") ||
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:tracking_paused"))
|
||||
|> case do
|
||||
true ->
|
||||
@@ -1112,6 +869,17 @@ defmodule WandererApp.Character.Tracker do
|
||||
state
|
||||
|> Map.merge(%{corporation_id: corporation_id})
|
||||
|
||||
{:error, :error_limited, headers} ->
|
||||
reset_timeout = get_reset_timeout(headers)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:corporation_info_forbidden",
|
||||
true,
|
||||
ttl: reset_timeout
|
||||
)
|
||||
|
||||
state
|
||||
|
||||
error ->
|
||||
Logger.warning(
|
||||
"Failed to get corporation info for character #{character_id}: #{inspect(error)}",
|
||||
|
||||
@@ -13,10 +13,9 @@ defmodule WandererApp.Character.TrackerManager.Impl do
|
||||
}
|
||||
|
||||
@check_start_queue_interval :timer.seconds(1)
|
||||
@garbage_collection_interval :timer.minutes(15)
|
||||
@garbage_collection_interval :timer.minutes(5)
|
||||
@untrack_characters_interval :timer.minutes(5)
|
||||
@inactive_character_timeout :timer.minutes(10)
|
||||
@untrack_character_timeout :timer.minutes(10)
|
||||
@inactive_character_timeout :timer.minutes(5)
|
||||
|
||||
@logger Application.compile_env(:wanderer_app, :logger)
|
||||
|
||||
@@ -116,13 +115,6 @@ defmodule WandererApp.Character.TrackerManager.Impl do
|
||||
end
|
||||
|
||||
def add_to_untrack_queue(map_id, character_id) do
|
||||
if not WandererApp.Cache.has_key?("#{map_id}:#{character_id}:untrack_requested") do
|
||||
WandererApp.Cache.insert(
|
||||
"#{map_id}:#{character_id}:untrack_requested",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
end
|
||||
|
||||
WandererApp.Cache.insert_or_update(
|
||||
"character_untrack_queue",
|
||||
[{map_id, character_id}],
|
||||
@@ -134,8 +126,6 @@ defmodule WandererApp.Character.TrackerManager.Impl do
|
||||
end
|
||||
|
||||
def remove_from_untrack_queue(map_id, character_id) do
|
||||
WandererApp.Cache.delete("#{map_id}:#{character_id}:untrack_requested")
|
||||
|
||||
WandererApp.Cache.insert_or_update(
|
||||
"character_untrack_queue",
|
||||
[],
|
||||
@@ -239,50 +229,32 @@ defmodule WandererApp.Character.TrackerManager.Impl do
|
||||
WandererApp.Cache.lookup!("character_untrack_queue", [])
|
||||
|> Task.async_stream(
|
||||
fn {map_id, character_id} ->
|
||||
untrack_timeout_reached =
|
||||
if WandererApp.Cache.has_key?("#{map_id}:#{character_id}:untrack_requested") do
|
||||
untrack_requested =
|
||||
WandererApp.Cache.lookup!(
|
||||
"#{map_id}:#{character_id}:untrack_requested",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
remove_from_untrack_queue(map_id, character_id)
|
||||
|
||||
duration = DateTime.diff(DateTime.utc_now(), untrack_requested, :millisecond)
|
||||
duration >= @untrack_character_timeout
|
||||
else
|
||||
false
|
||||
end
|
||||
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")
|
||||
|
||||
Logger.debug(fn -> "Untrack timeout reached: #{inspect(untrack_timeout_reached)}" end)
|
||||
{:ok, character_state} =
|
||||
WandererApp.Character.Tracker.update_settings(character_id, %{
|
||||
map_id: map_id,
|
||||
track: false
|
||||
})
|
||||
|
||||
if untrack_timeout_reached do
|
||||
remove_from_untrack_queue(map_id, character_id)
|
||||
{:ok, character} = WandererApp.Character.get_character(character_id)
|
||||
|
||||
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, _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
|
||||
})
|
||||
|
||||
{:ok, character_state} =
|
||||
WandererApp.Character.Tracker.update_settings(character_id, %{
|
||||
map_id: map_id,
|
||||
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)
|
||||
WandererApp.Map.Server.Impl.broadcast!(map_id, :untrack_character, character_id)
|
||||
end
|
||||
WandererApp.Character.update_character_state(character_id, character_state)
|
||||
WandererApp.Map.Server.Impl.broadcast!(map_id, :untrack_character, character_id)
|
||||
end,
|
||||
max_concurrency: System.schedulers_online(),
|
||||
on_timeout: :kill_task,
|
||||
|
||||
@@ -18,7 +18,7 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
|
||||
@update_location_interval :timer.seconds(1)
|
||||
@update_online_interval :timer.seconds(5)
|
||||
@check_offline_characters_interval :timer.minutes(2)
|
||||
@check_offline_characters_interval :timer.minutes(5)
|
||||
@check_online_errors_interval :timer.minutes(1)
|
||||
@check_ship_errors_interval :timer.minutes(1)
|
||||
@check_location_errors_interval :timer.minutes(1)
|
||||
@@ -124,7 +124,7 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
Process.send_after(self(), :check_online_errors, :timer.seconds(60))
|
||||
Process.send_after(self(), :check_ship_errors, :timer.seconds(90))
|
||||
Process.send_after(self(), :check_location_errors, :timer.seconds(120))
|
||||
# Process.send_after(self(), :check_offline_characters, @check_offline_characters_interval)
|
||||
Process.send_after(self(), :check_offline_characters, @check_offline_characters_interval)
|
||||
Process.send_after(self(), :update_location, 300)
|
||||
Process.send_after(self(), :update_ship, 500)
|
||||
Process.send_after(self(), :update_info, 1500)
|
||||
@@ -176,11 +176,15 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
|
||||
try do
|
||||
characters
|
||||
|> Enum.each(fn character_id ->
|
||||
WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_online, [
|
||||
character_id
|
||||
])
|
||||
end)
|
||||
|> Task.async_stream(
|
||||
fn character_id ->
|
||||
WandererApp.Character.Tracker.update_online(character_id)
|
||||
end,
|
||||
max_concurrency: System.schedulers_online(),
|
||||
on_timeout: :kill_task,
|
||||
timeout: :timer.seconds(5)
|
||||
)
|
||||
|> Enum.each(fn _result -> :ok end)
|
||||
rescue
|
||||
e ->
|
||||
Logger.error("""
|
||||
@@ -234,17 +238,7 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
characters
|
||||
|> Task.async_stream(
|
||||
fn character_id ->
|
||||
if WandererApp.Character.can_pause_tracking?(character_id) do
|
||||
WandererApp.TaskWrapper.start_link(
|
||||
WandererApp.Character.Tracker,
|
||||
:check_offline,
|
||||
[
|
||||
character_id
|
||||
]
|
||||
)
|
||||
else
|
||||
:ok
|
||||
end
|
||||
WandererApp.Character.Tracker.check_offline(character_id)
|
||||
end,
|
||||
timeout: :timer.seconds(15),
|
||||
max_concurrency: System.schedulers_online(),
|
||||
@@ -397,11 +391,15 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
|
||||
try do
|
||||
characters
|
||||
|> Enum.each(fn character_id ->
|
||||
WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_location, [
|
||||
character_id
|
||||
])
|
||||
end)
|
||||
|> Task.async_stream(
|
||||
fn character_id ->
|
||||
WandererApp.Character.Tracker.update_location(character_id)
|
||||
end,
|
||||
max_concurrency: System.schedulers_online(),
|
||||
on_timeout: :kill_task,
|
||||
timeout: :timer.seconds(5)
|
||||
)
|
||||
|> Enum.each(fn _result -> :ok end)
|
||||
rescue
|
||||
e ->
|
||||
Logger.error("""
|
||||
@@ -434,11 +432,15 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
|
||||
try do
|
||||
characters
|
||||
|> Enum.each(fn character_id ->
|
||||
WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_ship, [
|
||||
character_id
|
||||
])
|
||||
end)
|
||||
|> Task.async_stream(
|
||||
fn character_id ->
|
||||
WandererApp.Character.Tracker.update_ship(character_id)
|
||||
end,
|
||||
max_concurrency: System.schedulers_online(),
|
||||
on_timeout: :kill_task,
|
||||
timeout: :timer.seconds(5)
|
||||
)
|
||||
|> Enum.each(fn _result -> :ok end)
|
||||
rescue
|
||||
e ->
|
||||
Logger.error("""
|
||||
@@ -473,9 +475,7 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
characters
|
||||
|> Task.async_stream(
|
||||
fn character_id ->
|
||||
WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_info, [
|
||||
character_id
|
||||
])
|
||||
WandererApp.Character.Tracker.update_info(character_id)
|
||||
end,
|
||||
timeout: :timer.seconds(15),
|
||||
max_concurrency: System.schedulers_online(),
|
||||
@@ -519,9 +519,7 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
characters
|
||||
|> Task.async_stream(
|
||||
fn character_id ->
|
||||
WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_wallet, [
|
||||
character_id
|
||||
])
|
||||
WandererApp.Character.Tracker.update_wallet(character_id)
|
||||
end,
|
||||
timeout: :timer.seconds(15),
|
||||
max_concurrency: System.schedulers_online(),
|
||||
|
||||
@@ -213,86 +213,100 @@ 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", [])
|
||||
try 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 =
|
||||
maybe_update_online(map_id, character_id) ++
|
||||
maybe_update_tracking_status(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)
|
||||
presence_character_ids
|
||||
|> Task.async_stream(
|
||||
fn character_id ->
|
||||
character_updates =
|
||||
maybe_update_online(map_id, character_id) ++
|
||||
maybe_update_tracking_status(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
|
||||
)
|
||||
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
|
||||
:broadcast
|
||||
|
||||
{:character_ship, _info} ->
|
||||
:broadcast
|
||||
{:character_ship, _info} ->
|
||||
:broadcast
|
||||
|
||||
{:character_online, _info} ->
|
||||
:broadcast
|
||||
{:character_online, _info} ->
|
||||
:broadcast
|
||||
|
||||
{:character_tracking, _info} ->
|
||||
:broadcast
|
||||
{:character_tracking, _info} ->
|
||||
:broadcast
|
||||
|
||||
{:character_alliance, _info} ->
|
||||
WandererApp.Cache.insert_or_update(
|
||||
"map_#{map_id}:invalidate_character_ids",
|
||||
[character_id],
|
||||
fn ids ->
|
||||
[character_id | ids]
|
||||
end
|
||||
)
|
||||
{:character_alliance, _info} ->
|
||||
WandererApp.Cache.insert_or_update(
|
||||
"map_#{map_id}:invalidate_character_ids",
|
||||
[character_id],
|
||||
fn ids ->
|
||||
[character_id | ids] |> Enum.uniq()
|
||||
end
|
||||
)
|
||||
|
||||
:broadcast
|
||||
:broadcast
|
||||
|
||||
{:character_corporation, _info} ->
|
||||
WandererApp.Cache.insert_or_update(
|
||||
"map_#{map_id}:invalidate_character_ids",
|
||||
[character_id],
|
||||
fn ids ->
|
||||
[character_id | ids]
|
||||
end
|
||||
)
|
||||
{:character_corporation, _info} ->
|
||||
WandererApp.Cache.insert_or_update(
|
||||
"map_#{map_id}:invalidate_character_ids",
|
||||
[character_id],
|
||||
fn ids ->
|
||||
[character_id | ids] |> Enum.uniq()
|
||||
end
|
||||
)
|
||||
|
||||
:broadcast
|
||||
: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)
|
||||
_ ->
|
||||
: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)
|
||||
|
||||
:ok
|
||||
:ok
|
||||
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_characters: #{inspect(reason)}")
|
||||
end)
|
||||
end)
|
||||
rescue
|
||||
e ->
|
||||
Logger.error("""
|
||||
[Map Server] update_characters => exception: #{Exception.message(e)}
|
||||
#{Exception.format_stacktrace(__STACKTRACE__)}
|
||||
""")
|
||||
end
|
||||
end
|
||||
|
||||
defp update_character(map_id, character_id) do
|
||||
|
||||
@@ -32,7 +32,7 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
@backup_state_timeout :timer.minutes(1)
|
||||
@update_presence_timeout :timer.seconds(5)
|
||||
@update_characters_timeout :timer.seconds(1)
|
||||
@update_tracked_characters_timeout :timer.seconds(1)
|
||||
@update_tracked_characters_timeout :timer.minutes(1)
|
||||
|
||||
def new(), do: __struct__()
|
||||
def new(args), do: __struct__(args)
|
||||
@@ -96,7 +96,13 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
)
|
||||
|
||||
Process.send_after(self(), :update_characters, @update_characters_timeout)
|
||||
Process.send_after(self(), :update_tracked_characters, 100)
|
||||
|
||||
Process.send_after(
|
||||
self(),
|
||||
:update_tracked_characters,
|
||||
@update_tracked_characters_timeout
|
||||
)
|
||||
|
||||
Process.send_after(self(), :update_presence, @update_presence_timeout)
|
||||
Process.send_after(self(), :cleanup_connections, 5_000)
|
||||
Process.send_after(self(), :cleanup_systems, 10_000)
|
||||
|
||||
@@ -94,7 +94,7 @@ defmodule WandererApp.Maps do
|
||||
end
|
||||
end
|
||||
|
||||
def load_characters(map, user_id) do
|
||||
def load_characters(map, user_id) when not is_nil(map) do
|
||||
{:ok, user_characters} =
|
||||
WandererApp.Api.Character.active_by_user(%{user_id: user_id})
|
||||
|
||||
@@ -117,6 +117,8 @@ defmodule WandererApp.Maps do
|
||||
{:ok, %{characters: characters}}
|
||||
end
|
||||
|
||||
def load_characters(_map, _user_id), do: {:ok, %{characters: []}}
|
||||
|
||||
def map_character(
|
||||
%{
|
||||
name: name,
|
||||
|
||||
@@ -8,13 +8,13 @@ defmodule WandererApp.MapChainPassagesRepo do
|
||||
to
|
||||
)
|
||||
|> case do
|
||||
{:ok, connection} ->
|
||||
{:ok, %{inserted_at: inserted_at} = _connection} when not is_nil(inserted_at) ->
|
||||
{:ok, from_passages} =
|
||||
WandererApp.Api.MapChainPassages.by_connection(%{
|
||||
map_id: map_id,
|
||||
from: from,
|
||||
to: to,
|
||||
after: connection.inserted_at
|
||||
after: inserted_at
|
||||
})
|
||||
|
||||
{:ok, to_passages} =
|
||||
@@ -22,7 +22,7 @@ defmodule WandererApp.MapChainPassagesRepo do
|
||||
map_id: map_id,
|
||||
from: to,
|
||||
to: from,
|
||||
after: connection.inserted_at
|
||||
after: inserted_at
|
||||
})
|
||||
|
||||
from_passages =
|
||||
@@ -39,7 +39,7 @@ defmodule WandererApp.MapChainPassagesRepo do
|
||||
|
||||
{:ok, passages}
|
||||
|
||||
{:error, _error} ->
|
||||
_error ->
|
||||
{:ok, []}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -318,11 +318,18 @@ defmodule WandererAppWeb.AdminLive do
|
||||
end
|
||||
|
||||
defp apply_action(socket, :add_invite_link, _params, uri) do
|
||||
invite_types =
|
||||
if socket.assigns.map_subscriptions_enabled? do
|
||||
[%{label: "User", id: :user}, %{label: "Admin", id: :admin}]
|
||||
else
|
||||
[%{label: "User", id: :user}]
|
||||
end
|
||||
|
||||
socket
|
||||
|> assign(:active_page, :admin)
|
||||
|> assign(:uri, URI.parse(uri))
|
||||
|> assign(:page_title, "Add Invite Link")
|
||||
|> assign(:invite_types, [%{label: "User", id: :user}, %{label: "Admin", id: :admin}])
|
||||
|> assign(:invite_types, invite_types)
|
||||
|> assign(:valid_types, [
|
||||
%{label: "1D", id: 1},
|
||||
%{label: "1W", id: 7},
|
||||
|
||||
@@ -703,6 +703,18 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
|
||||
Process.send_after(self(), %{event: :load_map_pings}, 200)
|
||||
|
||||
Process.send_after(
|
||||
self(),
|
||||
%{
|
||||
event: :maybe_select_system,
|
||||
payload: %{
|
||||
character_id: nil,
|
||||
solar_system_id: nil
|
||||
}
|
||||
},
|
||||
200
|
||||
)
|
||||
|
||||
if needs_tracking_setup do
|
||||
Process.send_after(self(), %{event: :show_tracking}, 10)
|
||||
|
||||
|
||||
@@ -44,16 +44,24 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
current_user: current_user,
|
||||
tracked_characters: tracked_characters,
|
||||
map_id: map_id,
|
||||
map_user_settings: map_user_settings
|
||||
map_user_settings: map_user_settings,
|
||||
main_character_eve_id: main_character_eve_id,
|
||||
following_character_eve_id: following_character_eve_id
|
||||
}
|
||||
} = socket
|
||||
) do
|
||||
character =
|
||||
tracked_characters
|
||||
|> Enum.find(fn tracked_character -> tracked_character.id == character_id end)
|
||||
if is_nil(character_id) do
|
||||
tracked_characters
|
||||
|> Enum.find(fn tracked_character ->
|
||||
tracked_character.eve_id == (following_character_eve_id || main_character_eve_id)
|
||||
end)
|
||||
else
|
||||
tracked_characters
|
||||
|> Enum.find(fn tracked_character -> tracked_character.id == character_id end)
|
||||
end
|
||||
|
||||
is_user_character =
|
||||
not is_nil(character)
|
||||
is_user_character = not is_nil(character)
|
||||
|
||||
is_select_on_spash =
|
||||
map_user_settings
|
||||
@@ -61,10 +69,9 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
|> WandererApp.MapUserSettingsRepo.get_boolean_setting("select_on_spash")
|
||||
|
||||
is_following =
|
||||
case WandererApp.MapUserSettingsRepo.get(map_id, current_user.id) do
|
||||
{:ok, %{following_character_eve_id: following_character_eve_id}}
|
||||
when not is_nil(following_character_eve_id) ->
|
||||
is_user_character && following_character_eve_id == character.eve_id
|
||||
case is_user_character && not is_nil(following_character_eve_id) do
|
||||
true ->
|
||||
following_character_eve_id == character.eve_id
|
||||
|
||||
_ ->
|
||||
false
|
||||
@@ -77,8 +84,17 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
else
|
||||
# Always select the system when auto-select is enabled (following or select_on_spash).
|
||||
# The frontend will handle deselecting other systems
|
||||
#
|
||||
select_solar_system_id =
|
||||
if not is_nil(solar_system_id) do
|
||||
"#{solar_system_id}"
|
||||
else
|
||||
{:ok, character} = WandererApp.Character.get_map_character(map_id, character.id)
|
||||
"#{character.solar_system_id}"
|
||||
end
|
||||
|
||||
socket
|
||||
|> MapEventHandler.push_map_event("select_system", solar_system_id)
|
||||
|> MapEventHandler.push_map_event("select_system", select_solar_system_id)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ defmodule WandererAppWeb.PresenceGracePeriodManager do
|
||||
require Logger
|
||||
|
||||
# 30 minutes
|
||||
@grace_period_ms :timer.minutes(30)
|
||||
@grace_period_ms :timer.minutes(10)
|
||||
@check_remove_queue_interval :timer.seconds(30)
|
||||
|
||||
defstruct pending_removals: %{}, timers: %{}, to_remove: []
|
||||
|
||||
@@ -70,7 +70,8 @@ defmodule WandererAppWeb.Router do
|
||||
"'self'",
|
||||
"https://api.appzi.io",
|
||||
"https://www.googletagmanager.com",
|
||||
"https://www.google-analytics.com"
|
||||
"https://www.google-analytics.com",
|
||||
"https://*.google-analytics.com"
|
||||
]
|
||||
|
||||
# Define sandbox values individually to ensure proper spacing
|
||||
|
||||
Reference in New Issue
Block a user