mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-11-30 13:03:53 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1ecd3690e | ||
|
|
3250fe1ec6 | ||
|
|
48e8cd93b9 | ||
|
|
afacbb16b6 | ||
|
|
dfad127f32 | ||
|
|
300c1b5a18 | ||
|
|
bb38e1710b | ||
|
|
0857a82de5 | ||
|
|
da5afcc91c | ||
|
|
0002979fda |
34
CHANGELOG.md
34
CHANGELOG.md
@@ -2,6 +2,40 @@
|
||||
|
||||
<!-- changelog -->
|
||||
|
||||
## [v1.21.0](https://github.com/wanderer-industries/wanderer/compare/v1.20.1...v1.21.0) (2024-11-24)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Map: add new gate design, change EOL placement
|
||||
|
||||
## [v1.20.1](https://github.com/wanderer-industries/wanderer/compare/v1.20.0...v1.20.1) (2024-11-22)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.20.0](https://github.com/wanderer-industries/wanderer/compare/v1.19.3...v1.20.0) (2024-11-22)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Core: Add connection type for Gates, add new Update logic
|
||||
|
||||
## [v1.19.3](https://github.com/wanderer-industries/wanderer/compare/v1.19.2...v1.19.3) (2024-11-20)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Fix adding systems on splash (#71)
|
||||
|
||||
* Core: Fix adding systems on splash
|
||||
|
||||
## [v1.19.2](https://github.com/wanderer-industries/wanderer/compare/v1.19.1...v1.19.2) (2024-11-19)
|
||||
|
||||
|
||||
|
||||
@@ -466,3 +466,407 @@ body {
|
||||
transform: rotate(-360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Map refresh */
|
||||
.socket {
|
||||
scale: 0.5;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
left: 50%;
|
||||
/* margin-left: -75px; */
|
||||
top: 50%;
|
||||
/* margin-top: -50px; */
|
||||
}
|
||||
|
||||
.hex-brick {
|
||||
background: #000;
|
||||
width: 30px;
|
||||
height: 17px;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
animation-name: fade;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
-webkit-animation-name: fade;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.hex-brick--active {
|
||||
animation-name: fade-active;
|
||||
-webkit-animation-name: fade-active;
|
||||
}
|
||||
|
||||
.h2 {
|
||||
transform: rotate(60deg);
|
||||
-webkit-transform: rotate(60deg);
|
||||
}
|
||||
|
||||
.h3 {
|
||||
transform: rotate(-60deg);
|
||||
-webkit-transform: rotate(-60deg);
|
||||
}
|
||||
|
||||
.gel {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
transition: all 0.3s;
|
||||
-webkit-transition: all 0.3s;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
.center-gel {
|
||||
margin-left: -15px;
|
||||
margin-top: -15px;
|
||||
|
||||
animation-name: pulse-version;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
-webkit-animation-name: pulse-version;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
margin-left: -47px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
|
||||
.c2 {
|
||||
margin-left: -31px;
|
||||
margin-top: -43px;
|
||||
}
|
||||
|
||||
.c3 {
|
||||
margin-left: 1px;
|
||||
margin-top: -43px;
|
||||
}
|
||||
|
||||
.c4 {
|
||||
margin-left: 17px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
.c5 {
|
||||
margin-left: -31px;
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
.c6 {
|
||||
margin-left: 1px;
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
.c7 {
|
||||
margin-left: -63px;
|
||||
margin-top: -43px;
|
||||
}
|
||||
|
||||
.c8 {
|
||||
margin-left: 33px;
|
||||
margin-top: -43px;
|
||||
}
|
||||
|
||||
.c9 {
|
||||
margin-left: -15px;
|
||||
margin-top: 41px;
|
||||
}
|
||||
|
||||
.c10 {
|
||||
margin-left: -63px;
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
.c11 {
|
||||
margin-left: 33px;
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
.c12 {
|
||||
margin-left: -15px;
|
||||
margin-top: -71px;
|
||||
}
|
||||
|
||||
.c13 {
|
||||
margin-left: -47px;
|
||||
margin-top: -71px;
|
||||
}
|
||||
|
||||
.c14 {
|
||||
margin-left: 17px;
|
||||
margin-top: -71px;
|
||||
}
|
||||
|
||||
.c15 {
|
||||
margin-left: -47px;
|
||||
margin-top: 41px;
|
||||
}
|
||||
|
||||
.c16 {
|
||||
margin-left: 17px;
|
||||
margin-top: 41px;
|
||||
}
|
||||
|
||||
.c17 {
|
||||
margin-left: -79px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
|
||||
.c18 {
|
||||
margin-left: 49px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
|
||||
.c19 {
|
||||
margin-left: -63px;
|
||||
margin-top: -99px;
|
||||
}
|
||||
|
||||
.c20 {
|
||||
margin-left: 33px;
|
||||
margin-top: -99px;
|
||||
}
|
||||
|
||||
.c21 {
|
||||
margin-left: 1px;
|
||||
margin-top: -99px;
|
||||
}
|
||||
|
||||
.c22 {
|
||||
margin-left: -31px;
|
||||
margin-top: -99px;
|
||||
}
|
||||
|
||||
.c23 {
|
||||
margin-left: -63px;
|
||||
margin-top: 69px;
|
||||
}
|
||||
|
||||
.c24 {
|
||||
margin-left: 33px;
|
||||
margin-top: 69px;
|
||||
}
|
||||
|
||||
.c25 {
|
||||
margin-left: 1px;
|
||||
margin-top: 69px;
|
||||
}
|
||||
|
||||
.c26 {
|
||||
margin-left: -31px;
|
||||
margin-top: 69px;
|
||||
}
|
||||
|
||||
.c27 {
|
||||
margin-left: -79px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
|
||||
.c28 {
|
||||
margin-left: -95px;
|
||||
margin-top: -43px;
|
||||
}
|
||||
|
||||
.c29 {
|
||||
margin-left: -95px;
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
.c30 {
|
||||
margin-left: 49px;
|
||||
margin-top: 41px;
|
||||
}
|
||||
|
||||
.c31 {
|
||||
margin-left: -79px;
|
||||
margin-top: -71px;
|
||||
}
|
||||
|
||||
.c32 {
|
||||
margin-left: -111px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
|
||||
.c33 {
|
||||
margin-left: 65px;
|
||||
margin-top: -43px;
|
||||
}
|
||||
|
||||
.c34 {
|
||||
margin-left: 65px;
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
.c35 {
|
||||
margin-left: -79px;
|
||||
margin-top: 41px;
|
||||
}
|
||||
|
||||
.c36 {
|
||||
margin-left: 49px;
|
||||
margin-top: -71px;
|
||||
}
|
||||
|
||||
.c37 {
|
||||
margin-left: 81px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
|
||||
.r1 {
|
||||
animation-name: pulse-version;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-delay: 0.2s;
|
||||
-webkit-animation-name: pulse-version;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.r2 {
|
||||
animation-name: pulse-version;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-delay: 0.4s;
|
||||
-webkit-animation-name: pulse-version;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.r3 {
|
||||
animation-name: pulse-version;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-delay: 0.6s;
|
||||
-webkit-animation-name: pulse-version;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
.r1 > .hex-brick {
|
||||
animation-name: fade;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-delay: 0.2s;
|
||||
-webkit-animation-name: fade;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.r1 > .hex-brick--active {
|
||||
animation-name: fade-active;
|
||||
-webkit-animation-name: fade-active;
|
||||
}
|
||||
|
||||
.r2 > .hex-brick {
|
||||
animation-name: fade;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-delay: 0.4s;
|
||||
-webkit-animation-name: fade;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.r2 > .hex-brick--active {
|
||||
animation-name: fade-active;
|
||||
-webkit-animation-name: fade-active;
|
||||
}
|
||||
|
||||
.r3 > .hex-brick {
|
||||
animation-name: fade;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-delay: 0.6s;
|
||||
-webkit-animation-name: fade;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
.r3 > .hex-brick--active {
|
||||
animation-name: fade-active;
|
||||
-webkit-animation-name: fade-active;
|
||||
}
|
||||
|
||||
@keyframes pulse-version {
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform: scale(0.01);
|
||||
transform: scale(0.01);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
0% {
|
||||
background: #09d0e2;
|
||||
}
|
||||
|
||||
50% {
|
||||
background: #8ae6ee;
|
||||
}
|
||||
|
||||
100% {
|
||||
background: #09d0e2;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-active {
|
||||
0% {
|
||||
background: #ff52d9;
|
||||
}
|
||||
|
||||
50% {
|
||||
background: #ff52d9;
|
||||
}
|
||||
|
||||
100% {
|
||||
background: #ff52d9;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes pulse {
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform: scale(0.01);
|
||||
transform: scale(0.01);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes fade {
|
||||
0% {
|
||||
background: #abf8ff;
|
||||
}
|
||||
|
||||
50% {
|
||||
background: #389ca6;
|
||||
}
|
||||
|
||||
100% {
|
||||
background: #abf8ff;
|
||||
}
|
||||
}
|
||||
/* Map refresh END */
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ContextMenu } from 'primereact/contextmenu';
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
import { MenuItem } from 'primereact/menuitem';
|
||||
import { Edge } from '@reactflow/core/dist/esm/types/edges';
|
||||
import { MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import clsx from 'clsx';
|
||||
import classes from './ContextMenuConnection.module.scss';
|
||||
import { MASS_STATE_NAMES, MASS_STATE_NAMES_ORDER } from '@/hooks/Mapper/components/map/constants.ts';
|
||||
@@ -35,36 +35,49 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
|
||||
}
|
||||
|
||||
const isFrigateSize = edge.data?.ship_size_type === ShipSizeStatus.small;
|
||||
const isWormhole = edge.data?.type !== ConnectionType.gate;
|
||||
|
||||
return [
|
||||
{
|
||||
label: `EOL`,
|
||||
className: clsx({
|
||||
[classes.ConnectionTimeEOL]: edge.data?.time_status === TimeStatus.eol,
|
||||
}),
|
||||
icon: PrimeIcons.CLOCK,
|
||||
command: onChangeTimeState,
|
||||
},
|
||||
{
|
||||
label: `Frigate`,
|
||||
className: clsx({
|
||||
[classes.ConnectionFrigate]: isFrigateSize,
|
||||
}),
|
||||
icon: PrimeIcons.CLOUD,
|
||||
command: () =>
|
||||
onChangeShipSizeStatus(
|
||||
edge.data?.ship_size_type === ShipSizeStatus.small ? ShipSizeStatus.normal : ShipSizeStatus.small,
|
||||
),
|
||||
},
|
||||
{
|
||||
label: `Save mass`,
|
||||
className: clsx({
|
||||
[classes.ConnectionSave]: edge.data?.locked,
|
||||
}),
|
||||
icon: PrimeIcons.LOCK,
|
||||
command: () => onToggleMassSave(!edge.data?.locked),
|
||||
},
|
||||
...(!isFrigateSize
|
||||
...(isWormhole
|
||||
? [
|
||||
{
|
||||
label: `EOL`,
|
||||
className: clsx({
|
||||
[classes.ConnectionTimeEOL]: edge.data?.time_status === TimeStatus.eol,
|
||||
}),
|
||||
icon: PrimeIcons.CLOCK,
|
||||
command: onChangeTimeState,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(isWormhole
|
||||
? [
|
||||
{
|
||||
label: `Frigate`,
|
||||
className: clsx({
|
||||
[classes.ConnectionFrigate]: isFrigateSize,
|
||||
}),
|
||||
icon: PrimeIcons.CLOUD,
|
||||
command: () =>
|
||||
onChangeShipSizeStatus(
|
||||
edge.data?.ship_size_type === ShipSizeStatus.small ? ShipSizeStatus.normal : ShipSizeStatus.small,
|
||||
),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(isWormhole
|
||||
? [
|
||||
{
|
||||
label: `Save mass`,
|
||||
className: clsx({
|
||||
[classes.ConnectionSave]: edge.data?.locked,
|
||||
}),
|
||||
icon: PrimeIcons.LOCK,
|
||||
command: () => onToggleMassSave(!edge.data?.locked),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(isWormhole && !isFrigateSize
|
||||
? [
|
||||
{
|
||||
label: `Mass status`,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ContextMenu } from 'primereact/contextmenu';
|
||||
import { useMapState } from '../../MapProvider.tsx';
|
||||
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { Edge } from '@reactflow/core/dist/esm/types/edges';
|
||||
import { MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||
|
||||
export const useContextMenuConnectionHandlers = () => {
|
||||
@@ -47,6 +47,23 @@ export const useContextMenuConnectionHandlers = () => {
|
||||
setEdge(undefined);
|
||||
};
|
||||
|
||||
const onChangeType = useCallback((type: ConnectionType) => {
|
||||
const { edge, outCommand } = ref.current;
|
||||
|
||||
if (!edge) {
|
||||
return;
|
||||
}
|
||||
|
||||
outCommand({
|
||||
type: OutCommand.updateConnectionType,
|
||||
data: {
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
value: type,
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onChangeMassState = useCallback((status: MassState) => {
|
||||
const { edge, outCommand } = ref.current;
|
||||
|
||||
@@ -118,6 +135,7 @@ export const useContextMenuConnectionHandlers = () => {
|
||||
contextMenuRef,
|
||||
onDeleteConnection,
|
||||
onChangeTimeState,
|
||||
onChangeType,
|
||||
onChangeMassState,
|
||||
onChangeShipSizeStatus,
|
||||
onToggleMassSave,
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
stroke: #d4f0ff;
|
||||
}
|
||||
|
||||
&.Gate {
|
||||
stroke: #1c1e15;
|
||||
}
|
||||
|
||||
&.Hovered {
|
||||
stroke: #4e5d6c;
|
||||
stroke-width: 2px;
|
||||
@@ -76,6 +80,11 @@
|
||||
stroke-width: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&.Gate {
|
||||
stroke: #9aff40;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.ClickPath {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import classes from './SolarSystemEdge.module.scss';
|
||||
import { EdgeLabelRenderer, EdgeProps, getBezierPath, Position, useStore } from 'reactflow';
|
||||
import { EdgeLabelRenderer, EdgeProps, getBezierPath, getSmoothStepPath, Position, useStore } from 'reactflow';
|
||||
import { getEdgeParams } from '@/hooks/Mapper/components/map/utils.ts';
|
||||
import clsx from 'clsx';
|
||||
import { MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
|
||||
@@ -33,6 +33,7 @@ const MAP_OFFSETS: Record<string, { x: number; y: number }> = {
|
||||
export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }: EdgeProps<SolarSystemConnection>) => {
|
||||
const sourceNode = useStore(useCallback(store => store.nodeInternals.get(source), [source]));
|
||||
const targetNode = useStore(useCallback(store => store.nodeInternals.get(target), [target]));
|
||||
const isWormhole = data?.type !== ConnectionType.gate;
|
||||
|
||||
const {
|
||||
data: { isThickConnections },
|
||||
@@ -45,7 +46,9 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
||||
|
||||
const offset = isThickConnections ? MAP_OFFSETS_TICK[targetPos] : MAP_OFFSETS[targetPos];
|
||||
|
||||
const [edgePath, labelX, labelY] = getBezierPath({
|
||||
const method = isWormhole ? getBezierPath : getSmoothStepPath;
|
||||
|
||||
const [edgePath, labelX, labelY] = method({
|
||||
sourceX: sx - offset.x,
|
||||
sourceY: sy - offset.y,
|
||||
sourcePosition: sourcePos,
|
||||
@@ -53,8 +56,9 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
||||
targetX: tx + offset.x,
|
||||
targetY: ty + offset.y,
|
||||
});
|
||||
|
||||
return [edgePath, labelX, labelY, sx, sy, tx, ty, sourcePos, targetPos];
|
||||
}, [isThickConnections, sourceNode, targetNode]);
|
||||
}, [isThickConnections, sourceNode, targetNode, isWormhole]);
|
||||
|
||||
if (!sourceNode || !targetNode || !data) {
|
||||
return null;
|
||||
@@ -66,8 +70,9 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
||||
id={`back_${id}`}
|
||||
className={clsx(classes.EdgePathBack, {
|
||||
[classes.Tick]: isThickConnections,
|
||||
[classes.TimeCrit]: data.time_status === TimeStatus.eol,
|
||||
[classes.TimeCrit]: isWormhole && data.time_status === TimeStatus.eol,
|
||||
[classes.Hovered]: hovered,
|
||||
[classes.Gate]: !isWormhole,
|
||||
})}
|
||||
d={path}
|
||||
markerEnd={markerEnd}
|
||||
@@ -78,9 +83,10 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
||||
className={clsx(classes.EdgePathFront, {
|
||||
[classes.Tick]: isThickConnections,
|
||||
[classes.Hovered]: hovered,
|
||||
[classes.MassVerge]: data.mass_status === MassState.verge,
|
||||
[classes.MassHalf]: data.mass_status === MassState.half,
|
||||
[classes.Frigate]: data.ship_size_type === ShipSizeStatus.small,
|
||||
[classes.MassVerge]: isWormhole && data.mass_status === MassState.verge,
|
||||
[classes.MassHalf]: isWormhole && data.mass_status === MassState.half,
|
||||
[classes.Frigate]: isWormhole && data.ship_size_type === ShipSizeStatus.small,
|
||||
[classes.Gate]: !isWormhole,
|
||||
})}
|
||||
d={path}
|
||||
markerEnd={markerEnd}
|
||||
@@ -120,7 +126,7 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
||||
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
|
||||
}}
|
||||
>
|
||||
{data.locked && (
|
||||
{isWormhole && data.locked && (
|
||||
<WdTooltipWrapper
|
||||
content="Save mass"
|
||||
className={clsx(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MassState } from '@/hooks/Mapper/types';
|
||||
import { ConnectionType, MassState } from '@/hooks/Mapper/types';
|
||||
|
||||
export enum SOLAR_SYSTEM_CLASS_IDS {
|
||||
ccp1 = -1,
|
||||
@@ -712,6 +712,13 @@ export const STATUS_CLASSES: Record<number, string> = {
|
||||
[STATUSES.dangerous]: 'eve-system-status-dangerous',
|
||||
};
|
||||
|
||||
export const TYPE_NAMES_ORDER = [ConnectionType.wormhole, ConnectionType.gate];
|
||||
|
||||
export const TYPE_NAMES = {
|
||||
[ConnectionType.wormhole]: 'Wormhole',
|
||||
[ConnectionType.gate]: 'Gate',
|
||||
};
|
||||
|
||||
export const MASS_STATE_NAMES_ORDER = [MassState.verge, MassState.half, MassState.normal];
|
||||
|
||||
export const MASS_STATE_NAMES = {
|
||||
|
||||
@@ -12,6 +12,7 @@ export const useMapAddSystems = () => {
|
||||
return useCallback((systems: CommandAddSystems) => {
|
||||
const { rf } = ref.current;
|
||||
const nodes = rf.getNodes();
|
||||
|
||||
const prepared: Node[] = systems.filter(x => !nodes.some(y => x.id === y.id)).map(convertSystem2Node);
|
||||
rf.addNodes(prepared);
|
||||
}, []);
|
||||
|
||||
@@ -112,13 +112,17 @@ export const useMapHandlers = (ref: ForwardedRef<MapHandlers>, onSelectionChange
|
||||
connections: [],
|
||||
});
|
||||
selectSystem(systemId as CommandSelectSystem);
|
||||
}, 100);
|
||||
}, 500);
|
||||
break;
|
||||
|
||||
case Commands.routes:
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
case Commands.signaturesUpdated:
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
case Commands.linkSignatureToSystem:
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
@@ -48,6 +48,7 @@ const restoreWindowsFromLS = (): WidgetGridItem[] => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const raw = localStorage.getItem(SESSION_KEY.windows);
|
||||
if (!raw) {
|
||||
console.warn('No windows found in local storage!!');
|
||||
return DEFAULT_WINDOWS;
|
||||
}
|
||||
|
||||
@@ -63,7 +64,7 @@ const restoreWindowsFromLS = (): WidgetGridItem[] => {
|
||||
};
|
||||
|
||||
export const MapInterface = () => {
|
||||
const [items, setItems] = useState<WidgetGridItem[]>(restoreWindowsFromLS());
|
||||
const [items, setItems] = useState<WidgetGridItem[]>(restoreWindowsFromLS);
|
||||
|
||||
return (
|
||||
<WidgetsGrid
|
||||
|
||||
@@ -5,12 +5,14 @@ import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { VirtualScroller, VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
ConnectionType,
|
||||
ConnectionOutput,
|
||||
ConnectionInfoOutput,
|
||||
OutCommand,
|
||||
Passage,
|
||||
SolarSystemConnection,
|
||||
} from '@/hooks/Mapper/types';
|
||||
|
||||
import { PassageCard } from './PassageCard';
|
||||
import { InfoDrawer, SystemView } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { kgToTons } from '@/hooks/Mapper/utils/kgToTons.ts';
|
||||
@@ -75,8 +77,12 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
|
||||
return connections.find(x => x.source === selectedConnection.source && x.target === selectedConnection.target);
|
||||
}, [connections, selectedConnection]);
|
||||
|
||||
const isWormhole = useMemo(() => {
|
||||
return cnInfo?.type !== ConnectionType.gate;
|
||||
}, [cnInfo]);
|
||||
|
||||
const [passages, setPassages] = useState<Passage[]>([]);
|
||||
const [info, setInfo] = useState<ConnectionInfoOutput>(null);
|
||||
const [info, setInfo] = useState<ConnectionInfoOutput | null>(null);
|
||||
|
||||
const loadInfo = useCallback(
|
||||
async (connection: SolarSystemConnection) => {
|
||||
@@ -135,7 +141,7 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
|
||||
>
|
||||
<div className={clsx(classes.SidebarContent, '')}>
|
||||
{/* Connection Info */}
|
||||
<div className="px-2 pb-3 flex flex-col gap-2">
|
||||
<div className="px-2 flex flex-col gap-2">
|
||||
{/* Connection Info Row */}
|
||||
<InfoDrawer title="Connection" rightSide>
|
||||
<div className="flex justify-end gap-2 items-center">
|
||||
@@ -153,14 +159,25 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
|
||||
</div>
|
||||
</InfoDrawer>
|
||||
|
||||
{/* Connection Info Row */}
|
||||
<InfoDrawer title="Approximate mass of passages" rightSide>
|
||||
{kgToTons(approximateMass)}
|
||||
</InfoDrawer>
|
||||
<div className="flex justify-between gap-2">
|
||||
{/*Left column*/}
|
||||
<div>
|
||||
{isWormhole && info?.marl_eol_time && (
|
||||
<InfoDrawer title="Mark EOL Time">
|
||||
<TimeAgo timestamp={info.marl_eol_time} />
|
||||
</InfoDrawer>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<InfoDrawer title="Mark EOL Time" rightSide>
|
||||
{info?.marl_eol_time ? <TimeAgo timestamp={info.marl_eol_time} /> : ' unknown '}
|
||||
</InfoDrawer>
|
||||
{/*Right column*/}
|
||||
<div>
|
||||
{isWormhole && (
|
||||
<InfoDrawer title="Approximate mass of passages" rightSide>
|
||||
{kgToTons(approximateMass)}
|
||||
</InfoDrawer>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2"></div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
// import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { OutCommand, SignatureGroup, SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { Controller, FormProvider, useForm } from 'react-hook-form';
|
||||
import {
|
||||
@@ -25,102 +24,106 @@ export const SignatureSettings = ({ systemId, show, onHide, signatureData }: Map
|
||||
const { outCommand } = useMapRootState();
|
||||
|
||||
const handleShow = async () => {};
|
||||
const form = useForm<Partial<SystemSignaturePrepared>>({});
|
||||
const signatureForm = useForm<Partial<SystemSignaturePrepared>>({});
|
||||
|
||||
const handleSave = useCallback(async () => {
|
||||
if (!signatureData) {
|
||||
return;
|
||||
}
|
||||
const handleSave = useCallback(
|
||||
async (e: any) => {
|
||||
e?.preventDefault();
|
||||
if (!signatureData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { group, ...values } = form.getValues();
|
||||
let out = { ...signatureData };
|
||||
const { group, ...values } = signatureForm.getValues();
|
||||
let out = { ...signatureData };
|
||||
|
||||
switch (group) {
|
||||
case SignatureGroup.Wormhole:
|
||||
if (values.linked_system) {
|
||||
await outCommand({
|
||||
type: OutCommand.linkSignatureToSystem,
|
||||
data: {
|
||||
signature_eve_id: signatureData.eve_id,
|
||||
solar_system_source: systemId,
|
||||
solar_system_target: values.linked_system,
|
||||
},
|
||||
});
|
||||
}
|
||||
switch (group) {
|
||||
case SignatureGroup.Wormhole:
|
||||
if (values.linked_system) {
|
||||
await outCommand({
|
||||
type: OutCommand.linkSignatureToSystem,
|
||||
data: {
|
||||
signature_eve_id: signatureData.eve_id,
|
||||
solar_system_source: systemId,
|
||||
solar_system_target: values.linked_system,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (values.type != null) {
|
||||
out = { ...out, type: values.type };
|
||||
}
|
||||
if (values.type != null) {
|
||||
out = { ...out, type: values.type };
|
||||
}
|
||||
|
||||
if (signatureData.group !== SignatureGroup.Wormhole) {
|
||||
out = { ...out, name: '' };
|
||||
}
|
||||
if (signatureData.group !== SignatureGroup.Wormhole) {
|
||||
out = { ...out, name: '' };
|
||||
}
|
||||
|
||||
break;
|
||||
case SignatureGroup.CosmicSignature:
|
||||
out = { ...out, type: '', name: '' };
|
||||
break;
|
||||
default:
|
||||
if (values.name != null) {
|
||||
out = { ...out, name: values.name ?? '' };
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SignatureGroup.CosmicSignature:
|
||||
out = { ...out, type: '', name: '' };
|
||||
break;
|
||||
default:
|
||||
if (values.name != null) {
|
||||
out = { ...out, name: values.name ?? '' };
|
||||
}
|
||||
}
|
||||
|
||||
if (values.description != null) {
|
||||
out = { ...out, description: values.description };
|
||||
}
|
||||
if (values.description != null) {
|
||||
out = { ...out, description: values.description };
|
||||
}
|
||||
|
||||
// Note: when type of signature changed from WH to other type - we should drop name
|
||||
if (
|
||||
group !== SignatureGroup.Wormhole && // new
|
||||
signatureData.group === SignatureGroup.Wormhole && // prev
|
||||
signatureData.linked_system
|
||||
) {
|
||||
await outCommand({
|
||||
type: OutCommand.unlinkSignature,
|
||||
data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId },
|
||||
});
|
||||
|
||||
out = { ...out, type: '' };
|
||||
}
|
||||
|
||||
if (group === SignatureGroup.Wormhole && signatureData.linked_system != null && values.linked_system === null) {
|
||||
await outCommand({
|
||||
type: OutCommand.unlinkSignature,
|
||||
data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId },
|
||||
});
|
||||
}
|
||||
|
||||
// Note: despite groups have optional type - this will always set
|
||||
out = { ...out, group: group! };
|
||||
|
||||
// Note: when type of signature changed from WH to other type - we should drop name
|
||||
if (
|
||||
group !== SignatureGroup.Wormhole && // new
|
||||
signatureData.group === SignatureGroup.Wormhole && // prev
|
||||
signatureData.linked_system
|
||||
) {
|
||||
await outCommand({
|
||||
type: OutCommand.unlinkSignature,
|
||||
data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId },
|
||||
type: OutCommand.updateSignatures,
|
||||
data: {
|
||||
system_id: systemId,
|
||||
added: [],
|
||||
updated: [out],
|
||||
removed: [],
|
||||
},
|
||||
});
|
||||
|
||||
out = { ...out, type: '' };
|
||||
}
|
||||
|
||||
if (group === SignatureGroup.Wormhole && signatureData.linked_system != null && values.linked_system === null) {
|
||||
await outCommand({
|
||||
type: OutCommand.unlinkSignature,
|
||||
data: { signature_eve_id: signatureData.eve_id, solar_system_source: systemId },
|
||||
});
|
||||
}
|
||||
|
||||
// Note: despite groups have optional type - this will always set
|
||||
out = { ...out, group: group! };
|
||||
|
||||
await outCommand({
|
||||
type: OutCommand.updateSignatures,
|
||||
data: {
|
||||
system_id: systemId,
|
||||
added: [],
|
||||
updated: [out],
|
||||
removed: [],
|
||||
},
|
||||
});
|
||||
|
||||
form.reset();
|
||||
onHide();
|
||||
}, [form, onHide, outCommand, signatureData, systemId]);
|
||||
signatureForm.reset();
|
||||
onHide();
|
||||
},
|
||||
[signatureForm, onHide, outCommand, signatureData, systemId],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!signatureData) {
|
||||
form.reset();
|
||||
signatureForm.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
const { linked_system, ...rest } = signatureData;
|
||||
|
||||
form.reset({
|
||||
signatureForm.reset({
|
||||
linked_system: linked_system?.solar_system_id.toString() ?? undefined,
|
||||
...rest,
|
||||
});
|
||||
}, [form, signatureData]);
|
||||
}, [signatureForm, signatureData]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
@@ -138,32 +141,34 @@ export const SignatureSettings = ({ systemId, show, onHide, signatureData }: Map
|
||||
}}
|
||||
>
|
||||
<SystemsSettingsProvider initialValue={{ systemId }}>
|
||||
<FormProvider {...form}>
|
||||
<div className="flex flex-col gap-2 justify-between">
|
||||
<div className="w-full flex flex-col gap-1 p-1 min-h-[150px]">
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>Group:</span>
|
||||
<SignatureGroupSelect name="group" />
|
||||
</label>
|
||||
<FormProvider {...signatureForm}>
|
||||
<form onSubmit={handleSave}>
|
||||
<div className="flex flex-col gap-2 justify-between">
|
||||
<div className="w-full flex flex-col gap-1 p-1 min-h-[150px]">
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>Group:</span>
|
||||
<SignatureGroupSelect name="group" />
|
||||
</label>
|
||||
|
||||
<SignatureGroupContent />
|
||||
<SignatureGroupContent />
|
||||
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>Description:</span>
|
||||
<Controller
|
||||
name="description"
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<InputText placeholder="Type description" value={field.value} onChange={field.onChange} />
|
||||
)}
|
||||
/>
|
||||
</label>
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>Description:</span>
|
||||
<Controller
|
||||
name="description"
|
||||
control={signatureForm.control}
|
||||
render={({ field }) => (
|
||||
<InputText placeholder="Type description" value={field.value} onChange={field.onChange} />
|
||||
)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button onClick={handleSave} outlined size="small" label="Save"></Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button onClick={handleSave} outlined size="small" label="Save"></Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</FormProvider>
|
||||
</SystemsSettingsProvider>
|
||||
</Dialog>
|
||||
|
||||
@@ -87,6 +87,14 @@ export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => {
|
||||
mapRoutes(data as CommandRoutes);
|
||||
break;
|
||||
|
||||
case Commands.signaturesUpdated: // USED
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
case Commands.linkSignatureToSystem: // USED
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
case Commands.centerSystem: // USED
|
||||
// do nothing here
|
||||
break;
|
||||
@@ -95,22 +103,10 @@ export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => {
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
// case Commands.linkSignatureToSystem:
|
||||
// // TODO command data type lost
|
||||
// // @ts-ignore
|
||||
// emitMapEvent({ name: Commands.linkSignatureToSystem, data });
|
||||
// break;
|
||||
|
||||
case Commands.killsUpdated:
|
||||
// do nothing here
|
||||
break;
|
||||
|
||||
// case Commands.signaturesUpdated:
|
||||
// // TODO command data type lost
|
||||
// // @ts-ignore
|
||||
// emitMapEvent({ name: Commands.signaturesUpdated, data });
|
||||
// break;
|
||||
|
||||
default:
|
||||
console.warn(`JOipP Interface handlers: Unknown command: ${type}`, data);
|
||||
break;
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
export enum ConnectionType {
|
||||
wormhole,
|
||||
gate,
|
||||
}
|
||||
|
||||
export enum MassState {
|
||||
normal,
|
||||
half,
|
||||
@@ -32,4 +37,6 @@ export type SolarSystemConnection = {
|
||||
|
||||
source: string;
|
||||
target: string;
|
||||
|
||||
type?: ConnectionType;
|
||||
};
|
||||
|
||||
@@ -120,6 +120,7 @@ export enum OutCommand {
|
||||
getSystemStaticInfos = 'get_system_static_infos',
|
||||
getConnectionInfo = 'get_connection_info',
|
||||
updateConnectionTimeStatus = 'update_connection_time_status',
|
||||
updateConnectionType = 'update_connection_type',
|
||||
updateConnectionMassStatus = 'update_connection_mass_status',
|
||||
updateConnectionShipSizeType = 'update_connection_ship_size_type',
|
||||
updateConnectionLocked = 'update_connection_locked',
|
||||
|
||||
@@ -4,13 +4,26 @@ export default {
|
||||
mounted() {
|
||||
const hook = this;
|
||||
|
||||
const button = hook.el.querySelector('.update-button');
|
||||
const refreshZone = hook.el.querySelector('#refresh-area');
|
||||
|
||||
button.addEventListener('click', function () {
|
||||
const lastVersion = hook.el.dataset.version;
|
||||
localStorage.setItem(LAST_VERSION_KEY, lastVersion);
|
||||
window.location.reload();
|
||||
});
|
||||
const handleUpdate = function (e: Event) {
|
||||
const hexBricks = hook.el.querySelectorAll('.hex-brick');
|
||||
|
||||
// Add a new class to each element
|
||||
hexBricks.forEach(el => {
|
||||
el.classList.add('hex-brick--active');
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
const lastVersion = hook.el.dataset.version;
|
||||
localStorage.setItem(LAST_VERSION_KEY, lastVersion);
|
||||
|
||||
window.location.reload();
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
refreshZone.addEventListener('click', handleUpdate);
|
||||
refreshZone.addEventListener('mouseover', handleUpdate);
|
||||
|
||||
this.updated();
|
||||
},
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "0.7.0",
|
||||
"@react-rxjs/core": "^0.10.7",
|
||||
"@react-rxjs/utils": "^0.9.7",
|
||||
"@shopify/draggable": "^1.1.3",
|
||||
"clsx": "^2.1.1",
|
||||
"daisyui": "^4.11.1",
|
||||
@@ -34,7 +32,6 @@
|
||||
"react-hook-form": "^7.53.1",
|
||||
"react-usestateref": "^1.0.9",
|
||||
"reactflow": "^11.11.4",
|
||||
"rxjs": "^7.8.1",
|
||||
"tailwindcss": "^3.3.6",
|
||||
"topbar": "^3.0.0",
|
||||
"use-local-storage-state": "^19.3.1"
|
||||
|
||||
@@ -469,19 +469,6 @@
|
||||
resolved "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz"
|
||||
integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==
|
||||
|
||||
"@react-rxjs/core@^0.10.7":
|
||||
version "0.10.7"
|
||||
resolved "https://registry.npmjs.org/@react-rxjs/core/-/core-0.10.7.tgz"
|
||||
integrity sha512-dornp8pUs9OcdqFKKRh9+I2FVe21gWufNun6RYU1ddts7kUy9i4Thvl0iqcPFbGY61cJQMAJF7dxixWMSD/A/A==
|
||||
dependencies:
|
||||
"@rx-state/core" "0.1.4"
|
||||
use-sync-external-store "^1.0.0"
|
||||
|
||||
"@react-rxjs/utils@^0.9.7":
|
||||
version "0.9.7"
|
||||
resolved "https://registry.npmjs.org/@react-rxjs/utils/-/utils-0.9.7.tgz"
|
||||
integrity sha512-m9CUTdRsglObvUAlYfB24QvN+QH4XqCGEKnCdSILIeOx7mMqSi9TTFp2zrj5XqtMiLnj4ReAdDxrXegLPB73bQ==
|
||||
|
||||
"@reactflow/background@11.3.14":
|
||||
version "11.3.14"
|
||||
resolved "https://registry.npmjs.org/@reactflow/background/-/background-11.3.14.tgz"
|
||||
@@ -650,11 +637,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz#5a2d08b81e8064b34242d5cc9973ef8dd1e60503"
|
||||
integrity sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==
|
||||
|
||||
"@rx-state/core@0.1.4":
|
||||
version "0.1.4"
|
||||
resolved "https://registry.npmjs.org/@rx-state/core/-/core-0.1.4.tgz"
|
||||
integrity sha512-Z+3hjU2xh1HisLxt+W5hlYX/eGSDaXXP+ns82gq/PLZpkXLu0uwcNUh9RLY3Clq4zT+hSsA3vcpIGt6+UAb8rQ==
|
||||
|
||||
"@shopify/draggable@^1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmjs.org/@shopify/draggable/-/draggable-1.1.3.tgz"
|
||||
@@ -3421,13 +3403,6 @@ run-parallel@^1.1.9:
|
||||
dependencies:
|
||||
queue-microtask "^1.2.2"
|
||||
|
||||
rxjs@^7.8.1:
|
||||
version "7.8.1"
|
||||
resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz"
|
||||
integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==
|
||||
dependencies:
|
||||
tslib "^2.1.0"
|
||||
|
||||
safe-array-concat@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz"
|
||||
@@ -3732,7 +3707,7 @@ ts-interface-checker@^0.1.9:
|
||||
resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz"
|
||||
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
|
||||
|
||||
tslib@^2.1.0, tslib@^2.6.2:
|
||||
tslib@^2.6.2:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz"
|
||||
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
|
||||
@@ -3838,7 +3813,7 @@ use-local-storage-state@^19.3.1:
|
||||
resolved "https://registry.npmjs.org/use-local-storage-state/-/use-local-storage-state-19.3.1.tgz"
|
||||
integrity sha512-y3Z1dODXvZXZB4qtLDNN8iuXbsYD6TAxz61K58GWB9/yKwrNG9ynI0GzCTHi/Je1rMiyOwMimz0oyFsZn+Kj7Q==
|
||||
|
||||
use-sync-external-store@1.2.0, use-sync-external-store@^1.0.0:
|
||||
use-sync-external-store@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz"
|
||||
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
||||
|
||||
@@ -29,13 +29,16 @@ defmodule WandererApp.Api.MapConnection do
|
||||
define(:update_ship_size_type, action: :update_ship_size_type)
|
||||
define(:update_locked, action: :update_locked)
|
||||
define(:update_custom_info, action: :update_custom_info)
|
||||
define(:update_type, action: :update_type)
|
||||
define(:update_wormhole_type, action: :update_wormhole_type)
|
||||
end
|
||||
|
||||
actions do
|
||||
default_accept [
|
||||
:map_id,
|
||||
:solar_system_source,
|
||||
:solar_system_target
|
||||
:solar_system_target,
|
||||
:type
|
||||
]
|
||||
|
||||
defaults [:create, :read, :update, :destroy]
|
||||
@@ -92,6 +95,14 @@ defmodule WandererApp.Api.MapConnection do
|
||||
update :update_custom_info do
|
||||
accept [:custom_info]
|
||||
end
|
||||
|
||||
update :update_type do
|
||||
accept [:type]
|
||||
end
|
||||
|
||||
update :update_wormhole_type do
|
||||
accept [:wormhole_type]
|
||||
end
|
||||
end
|
||||
|
||||
attributes do
|
||||
@@ -126,6 +137,14 @@ defmodule WandererApp.Api.MapConnection do
|
||||
allow_nil?(true)
|
||||
end
|
||||
|
||||
# where 0 - Wormhole
|
||||
# where 1 - Gate
|
||||
attribute :type, :integer do
|
||||
default(0)
|
||||
|
||||
allow_nil?(true)
|
||||
end
|
||||
|
||||
attribute :wormhole_type, :string
|
||||
|
||||
attribute :count_of_passage, :integer do
|
||||
|
||||
@@ -195,6 +195,12 @@ defmodule WandererApp.Map.Server do
|
||||
|> map_pid!
|
||||
|> GenServer.cast({&Impl.update_connection_time_status/2, [connection_info]})
|
||||
|
||||
def update_connection_type(map_id, connection_info) when is_binary(map_id),
|
||||
do:
|
||||
map_id
|
||||
|> map_pid!
|
||||
|> GenServer.cast({&Impl.update_connection_type/2, [connection_info]})
|
||||
|
||||
def update_connection_mass_status(map_id, connection_info) when is_binary(map_id),
|
||||
do:
|
||||
map_id
|
||||
|
||||
@@ -4,6 +4,8 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
"""
|
||||
require Logger
|
||||
|
||||
alias WandererApp.Map.Server.ConnectionsImpl
|
||||
|
||||
@enforce_keys [
|
||||
:map_id
|
||||
]
|
||||
@@ -15,74 +17,10 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
map_opts: []
|
||||
]
|
||||
|
||||
# @ccp1 -1
|
||||
@c1 1
|
||||
@c2 2
|
||||
@c3 3
|
||||
@c4 4
|
||||
@c5 5
|
||||
@c6 6
|
||||
@hs 7
|
||||
@ls 8
|
||||
@ns 9
|
||||
# @ccp2 10
|
||||
# @ccp3 11
|
||||
@thera 12
|
||||
@c13 13
|
||||
@sentinel 14
|
||||
@baribican 15
|
||||
@vidette 16
|
||||
@conflux 17
|
||||
@redoubt 18
|
||||
@a1 19
|
||||
@a2 20
|
||||
@a3 21
|
||||
@a4 22
|
||||
@a5 23
|
||||
@ccp4 24
|
||||
# @pochven 25
|
||||
# @zarzakh 10100
|
||||
|
||||
@jita 30_000_142
|
||||
|
||||
@wh_space [
|
||||
@c1,
|
||||
@c2,
|
||||
@c3,
|
||||
@c4,
|
||||
@c5,
|
||||
@c6,
|
||||
@c13,
|
||||
@thera,
|
||||
@sentinel,
|
||||
@baribican,
|
||||
@vidette,
|
||||
@conflux,
|
||||
@redoubt
|
||||
]
|
||||
|
||||
@known_space [@hs, @ls, @ns]
|
||||
|
||||
@prohibited_systems [@jita]
|
||||
@prohibited_system_classes [
|
||||
@a1,
|
||||
@a2,
|
||||
@a3,
|
||||
@a4,
|
||||
@a5,
|
||||
@ccp4
|
||||
]
|
||||
|
||||
# this class of systems will guaranty that no one real class will take that place
|
||||
# @unknown 100_100
|
||||
|
||||
@systems_cleanup_timeout :timer.minutes(30)
|
||||
@characters_cleanup_timeout :timer.minutes(1)
|
||||
@connections_cleanup_timeout :timer.minutes(2)
|
||||
|
||||
@connection_time_status_eol 1
|
||||
@connection_auto_eol_hours 21
|
||||
@connection_auto_expire_hours 24
|
||||
@system_auto_expire_minutes 15
|
||||
|
||||
@ddrt Application.compile_env(:wanderer_app, :ddrt)
|
||||
@@ -90,7 +28,6 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
@pubsub_client Application.compile_env(:wanderer_app, :pubsub_client)
|
||||
@backup_state_timeout :timer.minutes(1)
|
||||
@system_inactive_timeout :timer.minutes(15)
|
||||
@connection_eol_expire_timeout :timer.hours(3) + :timer.minutes(30)
|
||||
@update_presence_timeout :timer.seconds(1)
|
||||
@update_characters_timeout :timer.seconds(1)
|
||||
@update_tracked_characters_timeout :timer.seconds(1)
|
||||
@@ -410,106 +347,23 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
state
|
||||
end
|
||||
|
||||
def add_connection(
|
||||
%{map_id: map_id} = state,
|
||||
%{
|
||||
solar_system_source_id: solar_system_source_id,
|
||||
solar_system_target_id: solar_system_target_id
|
||||
} = _connection_info
|
||||
) do
|
||||
:ok =
|
||||
maybe_add_connection(
|
||||
map_id,
|
||||
%{solar_system_id: solar_system_target_id},
|
||||
%{
|
||||
solar_system_id: solar_system_source_id
|
||||
},
|
||||
nil
|
||||
)
|
||||
defdelegate add_connection(state, connection_info), to: ConnectionsImpl
|
||||
|
||||
state
|
||||
end
|
||||
defdelegate delete_connection(state, connection_info), to: ConnectionsImpl
|
||||
|
||||
def delete_connection(
|
||||
%{map_id: map_id} = state,
|
||||
%{
|
||||
solar_system_source_id: solar_system_source_id,
|
||||
solar_system_target_id: solar_system_target_id
|
||||
} = _connection_info
|
||||
) do
|
||||
:ok =
|
||||
maybe_remove_connection(map_id, %{solar_system_id: solar_system_target_id}, %{
|
||||
solar_system_id: solar_system_source_id
|
||||
})
|
||||
defdelegate get_connection_info(state, connection_info), to: ConnectionsImpl
|
||||
|
||||
state
|
||||
end
|
||||
defdelegate update_connection_time_status(state, connection_update), to: ConnectionsImpl
|
||||
|
||||
def get_connection_info(
|
||||
%{map_id: map_id} = _state,
|
||||
%{
|
||||
solar_system_source_id: solar_system_source_id,
|
||||
solar_system_target_id: solar_system_target_id
|
||||
} = _connection_info
|
||||
) do
|
||||
WandererApp.Map.find_connection(
|
||||
map_id,
|
||||
solar_system_source_id,
|
||||
solar_system_target_id
|
||||
)
|
||||
|> case do
|
||||
{:ok, %{id: connection_id}} ->
|
||||
connection_mark_eol_time = get_connection_mark_eol_time(map_id, connection_id, nil)
|
||||
{:ok, %{marl_eol_time: connection_mark_eol_time}}
|
||||
defdelegate update_connection_type(state, connection_update), to: ConnectionsImpl
|
||||
|
||||
_ ->
|
||||
{:error, :not_found}
|
||||
end
|
||||
end
|
||||
defdelegate update_connection_mass_status(state, connection_update), to: ConnectionsImpl
|
||||
|
||||
def update_connection_time_status(
|
||||
%{map_id: map_id} = state,
|
||||
connection_update
|
||||
),
|
||||
do:
|
||||
update_connection(state, :update_time_status, [:time_status], connection_update, fn
|
||||
%{id: connection_id, time_status: time_status} ->
|
||||
case time_status == @connection_time_status_eol do
|
||||
true ->
|
||||
WandererApp.Cache.put(
|
||||
"map_#{map_id}:conn_#{connection_id}:mark_eol_time",
|
||||
DateTime.utc_now(),
|
||||
ttl: @connection_eol_expire_timeout
|
||||
)
|
||||
defdelegate update_connection_ship_size_type(state, connection_update), to: ConnectionsImpl
|
||||
|
||||
_ ->
|
||||
WandererApp.Cache.delete("map_#{map_id}:conn_#{connection_id}:mark_eol_time")
|
||||
end
|
||||
end)
|
||||
defdelegate update_connection_locked(state, connection_update), to: ConnectionsImpl
|
||||
|
||||
def update_connection_mass_status(
|
||||
state,
|
||||
connection_update
|
||||
),
|
||||
do: update_connection(state, :update_mass_status, [:mass_status], connection_update)
|
||||
|
||||
def update_connection_ship_size_type(
|
||||
state,
|
||||
connection_update
|
||||
),
|
||||
do: update_connection(state, :update_ship_size_type, [:ship_size_type], connection_update)
|
||||
|
||||
def update_connection_locked(
|
||||
state,
|
||||
connection_update
|
||||
),
|
||||
do: update_connection(state, :update_locked, [:locked], connection_update)
|
||||
|
||||
def update_connection_custom_info(
|
||||
state,
|
||||
connection_update
|
||||
),
|
||||
do: update_connection(state, :update_custom_info, [:custom_info], connection_update)
|
||||
defdelegate update_connection_custom_info(state, connection_update), to: ConnectionsImpl
|
||||
|
||||
def import_settings(%{map_id: map_id} = state, settings, user_id) do
|
||||
WandererApp.Cache.put(
|
||||
@@ -742,90 +596,10 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event(:cleanup_connections, %{map_id: map_id} = state) do
|
||||
def handle_event(:cleanup_connections, state) do
|
||||
Process.send_after(self(), :cleanup_connections, @connections_cleanup_timeout)
|
||||
|
||||
state =
|
||||
map_id
|
||||
|> WandererApp.Map.list_connections!()
|
||||
|> Enum.filter(fn %{
|
||||
inserted_at: inserted_at,
|
||||
solar_system_source: solar_system_source_id,
|
||||
solar_system_target: solar_system_target_id
|
||||
} ->
|
||||
DateTime.diff(DateTime.utc_now(), inserted_at, :hour) >=
|
||||
@connection_auto_eol_hours and
|
||||
is_connection_valid(
|
||||
:wormholes,
|
||||
solar_system_source_id,
|
||||
solar_system_target_id
|
||||
)
|
||||
end)
|
||||
|> Enum.reduce(state, fn %{
|
||||
solar_system_source: solar_system_source_id,
|
||||
solar_system_target: solar_system_target_id
|
||||
},
|
||||
state ->
|
||||
state
|
||||
|> update_connection_time_status(%{
|
||||
solar_system_source_id: solar_system_source_id,
|
||||
solar_system_target_id: solar_system_target_id,
|
||||
time_status: @connection_time_status_eol
|
||||
})
|
||||
end)
|
||||
|
||||
state =
|
||||
map_id
|
||||
|> WandererApp.Map.list_connections!()
|
||||
|> Enum.filter(fn %{
|
||||
id: connection_id,
|
||||
inserted_at: inserted_at,
|
||||
solar_system_source: solar_system_source_id,
|
||||
solar_system_target: solar_system_target_id
|
||||
} ->
|
||||
connection_mark_eol_time = get_connection_mark_eol_time(map_id, connection_id)
|
||||
|
||||
reverse_connection =
|
||||
WandererApp.Map.get_connection(
|
||||
map_id,
|
||||
solar_system_target_id,
|
||||
solar_system_source_id
|
||||
)
|
||||
|
||||
is_connection_exist =
|
||||
is_connection_exist(
|
||||
map_id,
|
||||
solar_system_source_id,
|
||||
solar_system_target_id
|
||||
) || not is_nil(reverse_connection)
|
||||
|
||||
is_connection_valid =
|
||||
is_connection_valid(
|
||||
:wormholes,
|
||||
solar_system_source_id,
|
||||
solar_system_target_id
|
||||
)
|
||||
|
||||
not is_connection_exist ||
|
||||
(is_connection_valid &&
|
||||
(DateTime.diff(DateTime.utc_now(), inserted_at, :hour) >=
|
||||
@connection_auto_expire_hours ||
|
||||
DateTime.diff(DateTime.utc_now(), connection_mark_eol_time, :hour) >=
|
||||
@connection_auto_expire_hours - @connection_auto_eol_hours))
|
||||
end)
|
||||
|> Enum.reduce(state, fn %{
|
||||
solar_system_source: solar_system_source_id,
|
||||
solar_system_target: solar_system_target_id
|
||||
},
|
||||
state ->
|
||||
state
|
||||
|> delete_connection(%{
|
||||
solar_system_source_id: solar_system_source_id,
|
||||
solar_system_target_id: solar_system_target_id
|
||||
})
|
||||
end)
|
||||
|
||||
state
|
||||
state |> ConnectionsImpl.cleanup_connections()
|
||||
end
|
||||
|
||||
def handle_event(:cleanup_characters, %{map_id: map_id, map: %{owner_id: owner_id}} = state) do
|
||||
@@ -1006,17 +780,6 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
end
|
||||
end
|
||||
|
||||
defp get_connection_mark_eol_time(map_id, connection_id, default \\ DateTime.utc_now()) do
|
||||
WandererApp.Cache.get("map_#{map_id}:conn_#{connection_id}:mark_eol_time")
|
||||
|> case do
|
||||
nil ->
|
||||
default
|
||||
|
||||
value ->
|
||||
value
|
||||
end
|
||||
end
|
||||
|
||||
defp can_broadcast?(map_id),
|
||||
do:
|
||||
not WandererApp.Cache.lookup!("map_#{map_id}:importing", false) and
|
||||
@@ -1029,20 +792,23 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
%{map: map, map_id: map_id, rtree_name: rtree_name, map_opts: map_opts} = _state
|
||||
) do
|
||||
case is_nil(old_location.solar_system_id) and
|
||||
can_add_location(map.scope, location.solar_system_id) do
|
||||
ConnectionsImpl.can_add_location(map.scope, location.solar_system_id) do
|
||||
true ->
|
||||
:ok = maybe_add_system(map_id, location, nil, rtree_name, map_opts)
|
||||
|
||||
_ ->
|
||||
case is_connection_valid(
|
||||
map.scope,
|
||||
old_location.solar_system_id,
|
||||
location.solar_system_id
|
||||
) do
|
||||
ConnectionsImpl.is_connection_valid(
|
||||
map.scope,
|
||||
old_location.solar_system_id,
|
||||
location.solar_system_id
|
||||
)
|
||||
|> case do
|
||||
true ->
|
||||
:ok = maybe_add_system(map_id, location, old_location, rtree_name, map_opts)
|
||||
:ok = maybe_add_system(map_id, old_location, location, rtree_name, map_opts)
|
||||
:ok = maybe_add_connection(map_id, location, old_location, character_id)
|
||||
|
||||
:ok =
|
||||
ConnectionsImpl.maybe_add_connection(map_id, location, old_location, character_id)
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
@@ -1205,48 +971,6 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
end
|
||||
end
|
||||
|
||||
defp update_connection(
|
||||
%{map_id: map_id} = state,
|
||||
update_method,
|
||||
attributes,
|
||||
%{
|
||||
solar_system_source_id: solar_system_source_id,
|
||||
solar_system_target_id: solar_system_target_id
|
||||
} = update,
|
||||
callback_fn \\ nil
|
||||
) do
|
||||
with {:ok, connection} <-
|
||||
WandererApp.Map.find_connection(
|
||||
map_id,
|
||||
solar_system_source_id,
|
||||
solar_system_target_id
|
||||
),
|
||||
{:ok, update_map} <- get_update_map(update, attributes),
|
||||
:ok <-
|
||||
WandererApp.Map.update_connection(
|
||||
map_id,
|
||||
connection |> Map.merge(update_map)
|
||||
),
|
||||
{:ok, updated_connection} <-
|
||||
apply(WandererApp.MapConnectionRepo, update_method, [
|
||||
connection,
|
||||
update_map
|
||||
]) do
|
||||
if not is_nil(callback_fn) do
|
||||
callback_fn.(updated_connection)
|
||||
end
|
||||
|
||||
broadcast!(map_id, :update_connection, updated_connection)
|
||||
|
||||
state
|
||||
else
|
||||
{:error, error} ->
|
||||
@logger.error("Failed to update connection: #{inspect(error, pretty: true)}")
|
||||
|
||||
state
|
||||
end
|
||||
end
|
||||
|
||||
defp update_system(
|
||||
%{map_id: map_id} = state,
|
||||
update_method,
|
||||
@@ -1281,7 +1005,7 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
end
|
||||
end
|
||||
|
||||
defp get_update_map(update, attributes),
|
||||
def get_update_map(update, attributes),
|
||||
do:
|
||||
{:ok,
|
||||
Enum.reduce(attributes, Map.new(), fn attribute, map ->
|
||||
@@ -1449,14 +1173,7 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
)
|
||||
end)
|
||||
|
||||
connections_eol_time
|
||||
|> Enum.each(fn {connection_id, connection_eol_time} ->
|
||||
WandererApp.Cache.put(
|
||||
"map_#{map_id}:conn_#{connection_id}:mark_eol_time",
|
||||
connection_eol_time,
|
||||
ttl: @connection_eol_expire_timeout
|
||||
)
|
||||
end)
|
||||
ConnectionsImpl.init_eol_cache(map_id, connections_eol_time)
|
||||
|
||||
state
|
||||
|
||||
@@ -1621,87 +1338,6 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
broadcast!(map_id, :update_system, updated_system)
|
||||
end
|
||||
|
||||
defp can_add_location(_scope, nil), do: false
|
||||
|
||||
defp can_add_location(:all, _solar_system_id), do: true
|
||||
|
||||
defp can_add_location(:none, _solar_system_id), do: false
|
||||
|
||||
defp can_add_location(scope, solar_system_id) do
|
||||
system_static_info =
|
||||
case WandererApp.CachedInfo.get_system_static_info(solar_system_id) do
|
||||
{:ok, system_static_info} when not is_nil(system_static_info) ->
|
||||
system_static_info
|
||||
|
||||
_ ->
|
||||
%{system_class: nil}
|
||||
end
|
||||
|
||||
case scope do
|
||||
:wormholes ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not (@prohibited_systems |> Enum.member?(solar_system_id)) and
|
||||
@wh_space |> Enum.member?(system_static_info.system_class)
|
||||
|
||||
:stargates ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
@known_space |> Enum.member?(system_static_info.system_class)
|
||||
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
defp is_connection_exist(map_id, from_solar_system_id, to_solar_system_id),
|
||||
do:
|
||||
not is_nil(
|
||||
WandererApp.Map.find_system_by_location(
|
||||
map_id,
|
||||
%{solar_system_id: from_solar_system_id}
|
||||
)
|
||||
) &&
|
||||
not is_nil(
|
||||
WandererApp.Map.find_system_by_location(
|
||||
map_id,
|
||||
%{solar_system_id: to_solar_system_id}
|
||||
)
|
||||
)
|
||||
|
||||
defp is_connection_valid(_scope, nil, _to_solar_system_id), do: false
|
||||
|
||||
defp is_connection_valid(:all, _from_solar_system_id, _to_solar_system_id), do: true
|
||||
|
||||
defp is_connection_valid(:none, _from_solar_system_id, _to_solar_system_id), do: false
|
||||
|
||||
defp is_connection_valid(scope, from_solar_system_id, to_solar_system_id) do
|
||||
{:ok, known_jumps} =
|
||||
WandererApp.Api.MapSolarSystemJumps.find(%{
|
||||
before_system_id: from_solar_system_id,
|
||||
current_system_id: to_solar_system_id
|
||||
})
|
||||
|
||||
system_static_info =
|
||||
case WandererApp.CachedInfo.get_system_static_info(to_solar_system_id) do
|
||||
{:ok, system_static_info} when not is_nil(system_static_info) ->
|
||||
system_static_info
|
||||
|
||||
_ ->
|
||||
%{system_class: nil}
|
||||
end
|
||||
|
||||
case scope do
|
||||
:wormholes ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not (@prohibited_systems |> Enum.member?(to_solar_system_id)) and
|
||||
known_jumps |> Enum.empty?() and to_solar_system_id != @jita and
|
||||
from_solar_system_id != @jita
|
||||
|
||||
:stargates ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not (known_jumps |> Enum.empty?())
|
||||
end
|
||||
end
|
||||
|
||||
defp update_presence(map_id) do
|
||||
case WandererApp.Cache.lookup!("map_#{map_id}:started", false) and
|
||||
WandererApp.Cache.get_and_remove!("map_#{map_id}:presence_updated", false) do
|
||||
@@ -1781,85 +1417,6 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
end)
|
||||
end
|
||||
|
||||
defp maybe_remove_connection(map_id, location, old_location)
|
||||
when not is_nil(location) and not is_nil(old_location) and
|
||||
location.solar_system_id != old_location.solar_system_id do
|
||||
case WandererApp.Map.find_connection(
|
||||
map_id,
|
||||
location.solar_system_id,
|
||||
old_location.solar_system_id
|
||||
) do
|
||||
{:ok, connection} when not is_nil(connection) ->
|
||||
:ok = WandererApp.MapConnectionRepo.destroy(map_id, connection)
|
||||
|
||||
broadcast!(map_id, :remove_connections, [connection])
|
||||
map_id |> WandererApp.Map.remove_connection(connection)
|
||||
|
||||
_error ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_remove_connection(_map_id, _location, _old_location), do: :ok
|
||||
|
||||
defp maybe_add_connection(map_id, location, old_location, character_id)
|
||||
when not is_nil(location) and not is_nil(old_location) and
|
||||
not is_nil(old_location.solar_system_id) and
|
||||
location.solar_system_id != old_location.solar_system_id do
|
||||
character_id
|
||||
|> WandererApp.Character.get_character!()
|
||||
|> case do
|
||||
nil ->
|
||||
:ok
|
||||
|
||||
character ->
|
||||
:telemetry.execute([:wanderer_app, :map, :character, :jump], %{count: 1}, %{})
|
||||
|
||||
{:ok, _} =
|
||||
WandererApp.Api.MapChainPassages.new(%{
|
||||
map_id: map_id,
|
||||
character_id: character_id,
|
||||
ship_type_id: character.ship,
|
||||
ship_name: character.ship_name,
|
||||
solar_system_source_id: old_location.solar_system_id,
|
||||
solar_system_target_id: location.solar_system_id
|
||||
})
|
||||
|
||||
broadcast!(map_id, :maybe_select_system, %{
|
||||
character_id: character_id,
|
||||
solar_system_id: location.solar_system_id
|
||||
})
|
||||
end
|
||||
|
||||
case WandererApp.Map.check_connection(map_id, location, old_location) do
|
||||
:ok ->
|
||||
connection =
|
||||
WandererApp.MapConnectionRepo.create!(%{
|
||||
map_id: map_id,
|
||||
solar_system_source: old_location.solar_system_id,
|
||||
solar_system_target: location.solar_system_id
|
||||
})
|
||||
|
||||
WandererApp.Map.add_connection(map_id, connection)
|
||||
|
||||
broadcast!(map_id, :add_connection, connection)
|
||||
|
||||
broadcast!(map_id, :maybe_link_signature, %{
|
||||
character_id: character_id,
|
||||
solar_system_source: old_location.solar_system_id,
|
||||
solar_system_target: location.solar_system_id
|
||||
})
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} ->
|
||||
@logger.debug(fn -> "Failed to add connection: #{inspect(error, pretty: true)}" end)
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_add_connection(_map_id, _location, _old_location, _character_id), do: :ok
|
||||
|
||||
defp maybe_add_system(map_id, location, old_location, rtree_name, map_opts)
|
||||
when not is_nil(location) do
|
||||
case WandererApp.Map.check_location(map_id, location) do
|
||||
@@ -1897,6 +1454,7 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
)
|
||||
|
||||
WandererApp.Map.add_system(map_id, updated_system)
|
||||
|
||||
broadcast!(map_id, :add_system, updated_system)
|
||||
:ok
|
||||
|
||||
|
||||
512
lib/wanderer_app/map/server/map_server_connections_impl.ex
Normal file
512
lib/wanderer_app/map/server/map_server_connections_impl.ex
Normal file
@@ -0,0 +1,512 @@
|
||||
defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
@moduledoc """
|
||||
Holds state for a map and exposes an interface to managing the map instance
|
||||
"""
|
||||
require Logger
|
||||
|
||||
alias WandererApp.Map.Server.Impl
|
||||
|
||||
# @ccp1 -1
|
||||
@c1 1
|
||||
@c2 2
|
||||
@c3 3
|
||||
@c4 4
|
||||
@c5 5
|
||||
@c6 6
|
||||
@hs 7
|
||||
@ls 8
|
||||
@ns 9
|
||||
# @ccp2 10
|
||||
# @ccp3 11
|
||||
@thera 12
|
||||
@c13 13
|
||||
@sentinel 14
|
||||
@baribican 15
|
||||
@vidette 16
|
||||
@conflux 17
|
||||
@redoubt 18
|
||||
@a1 19
|
||||
@a2 20
|
||||
@a3 21
|
||||
@a4 22
|
||||
@a5 23
|
||||
@ccp4 24
|
||||
# @pochven 25
|
||||
# @zarzakh 10100
|
||||
|
||||
@jita 30_000_142
|
||||
|
||||
@wh_space [
|
||||
@c1,
|
||||
@c2,
|
||||
@c3,
|
||||
@c4,
|
||||
@c5,
|
||||
@c6,
|
||||
@c13,
|
||||
@thera,
|
||||
@sentinel,
|
||||
@baribican,
|
||||
@vidette,
|
||||
@conflux,
|
||||
@redoubt
|
||||
]
|
||||
|
||||
@known_space [@hs, @ls, @ns]
|
||||
|
||||
@prohibited_systems [@jita]
|
||||
@prohibited_system_classes [
|
||||
@a1,
|
||||
@a2,
|
||||
@a3,
|
||||
@a4,
|
||||
@a5,
|
||||
@ccp4
|
||||
]
|
||||
|
||||
# this class of systems will guaranty that no one real class will take that place
|
||||
# @unknown 100_100
|
||||
#
|
||||
@connection_time_status_eol 1
|
||||
@connection_auto_eol_hours 21
|
||||
@connection_auto_expire_hours 24
|
||||
@connection_eol_expire_timeout :timer.hours(3) + :timer.minutes(30)
|
||||
|
||||
@connection_type_wormhole 0
|
||||
@connection_type_stargate 1
|
||||
|
||||
def init_eol_cache(map_id, connections_eol_time) do
|
||||
connections_eol_time
|
||||
|> Enum.each(fn {connection_id, connection_eol_time} ->
|
||||
WandererApp.Cache.put(
|
||||
"map_#{map_id}:conn_#{connection_id}:mark_eol_time",
|
||||
connection_eol_time,
|
||||
ttl: @connection_eol_expire_timeout
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
||||
def add_connection(
|
||||
%{map_id: map_id} = state,
|
||||
%{
|
||||
solar_system_source_id: solar_system_source_id,
|
||||
solar_system_target_id: solar_system_target_id,
|
||||
character_id: character_id
|
||||
} = _connection_info
|
||||
) do
|
||||
:ok =
|
||||
maybe_add_connection(
|
||||
map_id,
|
||||
%{solar_system_id: solar_system_target_id},
|
||||
%{
|
||||
solar_system_id: solar_system_source_id
|
||||
},
|
||||
character_id
|
||||
)
|
||||
|
||||
state
|
||||
end
|
||||
|
||||
def delete_connection(
|
||||
%{map_id: map_id} = state,
|
||||
%{
|
||||
solar_system_source_id: solar_system_source_id,
|
||||
solar_system_target_id: solar_system_target_id
|
||||
} = _connection_info
|
||||
) do
|
||||
:ok =
|
||||
maybe_remove_connection(map_id, %{solar_system_id: solar_system_target_id}, %{
|
||||
solar_system_id: solar_system_source_id
|
||||
})
|
||||
|
||||
state
|
||||
end
|
||||
|
||||
def get_connection_info(
|
||||
%{map_id: map_id} = _state,
|
||||
%{
|
||||
solar_system_source_id: solar_system_source_id,
|
||||
solar_system_target_id: solar_system_target_id
|
||||
} = _connection_info
|
||||
) do
|
||||
WandererApp.Map.find_connection(
|
||||
map_id,
|
||||
solar_system_source_id,
|
||||
solar_system_target_id
|
||||
)
|
||||
|> case do
|
||||
{:ok, %{id: connection_id}} ->
|
||||
connection_mark_eol_time = get_connection_mark_eol_time(map_id, connection_id, nil)
|
||||
{:ok, %{marl_eol_time: connection_mark_eol_time}}
|
||||
|
||||
_ ->
|
||||
{:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
def update_connection_time_status(
|
||||
%{map_id: map_id} = state,
|
||||
connection_update
|
||||
),
|
||||
do:
|
||||
update_connection(state, :update_time_status, [:time_status], connection_update, fn
|
||||
%{id: connection_id, time_status: time_status} ->
|
||||
case time_status == @connection_time_status_eol do
|
||||
true ->
|
||||
WandererApp.Cache.put(
|
||||
"map_#{map_id}:conn_#{connection_id}:mark_eol_time",
|
||||
DateTime.utc_now(),
|
||||
ttl: @connection_eol_expire_timeout
|
||||
)
|
||||
|
||||
_ ->
|
||||
WandererApp.Cache.delete("map_#{map_id}:conn_#{connection_id}:mark_eol_time")
|
||||
end
|
||||
end)
|
||||
|
||||
def update_connection_type(
|
||||
state,
|
||||
connection_update
|
||||
),
|
||||
do: update_connection(state, :update_type, [:type], connection_update)
|
||||
|
||||
def update_connection_mass_status(
|
||||
state,
|
||||
connection_update
|
||||
),
|
||||
do: update_connection(state, :update_mass_status, [:mass_status], connection_update)
|
||||
|
||||
def update_connection_ship_size_type(
|
||||
state,
|
||||
connection_update
|
||||
),
|
||||
do: update_connection(state, :update_ship_size_type, [:ship_size_type], connection_update)
|
||||
|
||||
def update_connection_locked(
|
||||
state,
|
||||
connection_update
|
||||
),
|
||||
do: update_connection(state, :update_locked, [:locked], connection_update)
|
||||
|
||||
def update_connection_custom_info(
|
||||
state,
|
||||
connection_update
|
||||
),
|
||||
do: update_connection(state, :update_custom_info, [:custom_info], connection_update)
|
||||
|
||||
def cleanup_connections(%{map_id: map_id} = state) do
|
||||
state =
|
||||
map_id
|
||||
|> WandererApp.Map.list_connections!()
|
||||
|> Enum.filter(fn %{
|
||||
inserted_at: inserted_at,
|
||||
solar_system_source: solar_system_source_id,
|
||||
solar_system_target: solar_system_target_id,
|
||||
type: type
|
||||
} ->
|
||||
type != @connection_type_stargate &&
|
||||
DateTime.diff(DateTime.utc_now(), inserted_at, :hour) >=
|
||||
@connection_auto_eol_hours &&
|
||||
is_connection_valid(
|
||||
:wormholes,
|
||||
solar_system_source_id,
|
||||
solar_system_target_id
|
||||
)
|
||||
end)
|
||||
|> Enum.reduce(state, fn %{
|
||||
solar_system_source: solar_system_source_id,
|
||||
solar_system_target: solar_system_target_id
|
||||
},
|
||||
state ->
|
||||
state
|
||||
|> update_connection_time_status(%{
|
||||
solar_system_source_id: solar_system_source_id,
|
||||
solar_system_target_id: solar_system_target_id,
|
||||
time_status: @connection_time_status_eol
|
||||
})
|
||||
end)
|
||||
|
||||
state =
|
||||
map_id
|
||||
|> WandererApp.Map.list_connections!()
|
||||
|> Enum.filter(fn %{
|
||||
id: connection_id,
|
||||
inserted_at: inserted_at,
|
||||
solar_system_source: solar_system_source_id,
|
||||
solar_system_target: solar_system_target_id,
|
||||
type: type
|
||||
} ->
|
||||
connection_mark_eol_time =
|
||||
get_connection_mark_eol_time(map_id, connection_id)
|
||||
|
||||
reverse_connection =
|
||||
WandererApp.Map.get_connection(
|
||||
map_id,
|
||||
solar_system_target_id,
|
||||
solar_system_source_id
|
||||
)
|
||||
|
||||
is_connection_exist =
|
||||
is_connection_exist(
|
||||
map_id,
|
||||
solar_system_source_id,
|
||||
solar_system_target_id
|
||||
) || not is_nil(reverse_connection)
|
||||
|
||||
is_connection_valid =
|
||||
is_connection_valid(
|
||||
:wormholes,
|
||||
solar_system_source_id,
|
||||
solar_system_target_id
|
||||
)
|
||||
|
||||
not is_connection_exist ||
|
||||
(type != @connection_type_stargate && is_connection_valid &&
|
||||
(DateTime.diff(DateTime.utc_now(), inserted_at, :hour) >=
|
||||
@connection_auto_expire_hours ||
|
||||
DateTime.diff(DateTime.utc_now(), connection_mark_eol_time, :hour) >=
|
||||
@connection_auto_expire_hours - @connection_auto_eol_hours))
|
||||
end)
|
||||
|> Enum.reduce(state, fn %{
|
||||
solar_system_source: solar_system_source_id,
|
||||
solar_system_target: solar_system_target_id
|
||||
},
|
||||
state ->
|
||||
state
|
||||
|> delete_connection(%{
|
||||
solar_system_source_id: solar_system_source_id,
|
||||
solar_system_target_id: solar_system_target_id
|
||||
})
|
||||
end)
|
||||
|
||||
state
|
||||
end
|
||||
|
||||
def maybe_add_connection(map_id, location, old_location, character_id)
|
||||
when not is_nil(location) and not is_nil(old_location) and
|
||||
not is_nil(old_location.solar_system_id) and
|
||||
location.solar_system_id != old_location.solar_system_id do
|
||||
character_id
|
||||
|> WandererApp.Character.get_character!()
|
||||
|> case do
|
||||
nil ->
|
||||
:ok
|
||||
|
||||
character ->
|
||||
:telemetry.execute([:wanderer_app, :map, :character, :jump], %{count: 1}, %{})
|
||||
|
||||
{:ok, _} =
|
||||
WandererApp.Api.MapChainPassages.new(%{
|
||||
map_id: map_id,
|
||||
character_id: character_id,
|
||||
ship_type_id: character.ship,
|
||||
ship_name: character.ship_name,
|
||||
solar_system_source_id: old_location.solar_system_id,
|
||||
solar_system_target_id: location.solar_system_id
|
||||
})
|
||||
end
|
||||
|
||||
case WandererApp.Map.check_connection(map_id, location, old_location) do
|
||||
:ok ->
|
||||
connection_type =
|
||||
is_connection_valid(
|
||||
:stargates,
|
||||
old_location.solar_system_id,
|
||||
location.solar_system_id
|
||||
)
|
||||
|> case do
|
||||
true ->
|
||||
@connection_type_stargate
|
||||
|
||||
_ ->
|
||||
@connection_type_wormhole
|
||||
end
|
||||
|
||||
{:ok, connection} =
|
||||
WandererApp.MapConnectionRepo.create(%{
|
||||
map_id: map_id,
|
||||
solar_system_source: old_location.solar_system_id,
|
||||
solar_system_target: location.solar_system_id,
|
||||
type: connection_type
|
||||
})
|
||||
|
||||
WandererApp.Map.add_connection(map_id, connection)
|
||||
|
||||
Impl.broadcast!(map_id, :maybe_select_system, %{
|
||||
character_id: character_id,
|
||||
solar_system_id: location.solar_system_id
|
||||
})
|
||||
|
||||
Impl.broadcast!(map_id, :add_connection, connection)
|
||||
|
||||
Impl.broadcast!(map_id, :maybe_link_signature, %{
|
||||
character_id: character_id,
|
||||
solar_system_source: old_location.solar_system_id,
|
||||
solar_system_target: location.solar_system_id
|
||||
})
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} ->
|
||||
Logger.debug(fn -> "Failed to add connection: #{inspect(error, pretty: true)}" end)
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
def maybe_add_connection(_map_id, _location, _old_location, _character_id), do: :ok
|
||||
|
||||
def can_add_location(_scope, nil), do: false
|
||||
|
||||
def can_add_location(:all, _solar_system_id), do: true
|
||||
|
||||
def can_add_location(:none, _solar_system_id), do: false
|
||||
|
||||
def can_add_location(scope, solar_system_id) do
|
||||
system_static_info =
|
||||
case WandererApp.CachedInfo.get_system_static_info(solar_system_id) do
|
||||
{:ok, system_static_info} when not is_nil(system_static_info) ->
|
||||
system_static_info
|
||||
|
||||
_ ->
|
||||
%{system_class: nil}
|
||||
end
|
||||
|
||||
case scope do
|
||||
:wormholes ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not (@prohibited_systems |> Enum.member?(solar_system_id)) and
|
||||
@wh_space |> Enum.member?(system_static_info.system_class)
|
||||
|
||||
:stargates ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
@known_space |> Enum.member?(system_static_info.system_class)
|
||||
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def is_connection_exist(map_id, from_solar_system_id, to_solar_system_id),
|
||||
do:
|
||||
not is_nil(
|
||||
WandererApp.Map.find_system_by_location(
|
||||
map_id,
|
||||
%{solar_system_id: from_solar_system_id}
|
||||
)
|
||||
) &&
|
||||
not is_nil(
|
||||
WandererApp.Map.find_system_by_location(
|
||||
map_id,
|
||||
%{solar_system_id: to_solar_system_id}
|
||||
)
|
||||
)
|
||||
|
||||
def is_connection_valid(_scope, nil, _to_solar_system_id), do: false
|
||||
|
||||
def is_connection_valid(:all, _from_solar_system_id, _to_solar_system_id), do: true
|
||||
|
||||
def is_connection_valid(:none, _from_solar_system_id, _to_solar_system_id), do: false
|
||||
|
||||
def is_connection_valid(scope, from_solar_system_id, to_solar_system_id) do
|
||||
{:ok, known_jumps} =
|
||||
WandererApp.Api.MapSolarSystemJumps.find(%{
|
||||
before_system_id: from_solar_system_id,
|
||||
current_system_id: to_solar_system_id
|
||||
})
|
||||
|
||||
system_static_info =
|
||||
case WandererApp.CachedInfo.get_system_static_info(to_solar_system_id) do
|
||||
{:ok, system_static_info} when not is_nil(system_static_info) ->
|
||||
system_static_info
|
||||
|
||||
_ ->
|
||||
%{system_class: nil}
|
||||
end
|
||||
|
||||
case scope do
|
||||
:wormholes ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not (@prohibited_systems |> Enum.member?(to_solar_system_id)) and
|
||||
known_jumps |> Enum.empty?() and to_solar_system_id != @jita and
|
||||
from_solar_system_id != @jita
|
||||
|
||||
:stargates ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not (known_jumps |> Enum.empty?())
|
||||
end
|
||||
end
|
||||
|
||||
def get_connection_mark_eol_time(map_id, connection_id, default \\ DateTime.utc_now()) do
|
||||
WandererApp.Cache.get("map_#{map_id}:conn_#{connection_id}:mark_eol_time")
|
||||
|> case do
|
||||
nil ->
|
||||
default
|
||||
|
||||
value ->
|
||||
value
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_remove_connection(map_id, location, old_location)
|
||||
when not is_nil(location) and not is_nil(old_location) and
|
||||
location.solar_system_id != old_location.solar_system_id do
|
||||
case WandererApp.Map.find_connection(
|
||||
map_id,
|
||||
location.solar_system_id,
|
||||
old_location.solar_system_id
|
||||
) do
|
||||
{:ok, connection} when not is_nil(connection) ->
|
||||
:ok = WandererApp.MapConnectionRepo.destroy(map_id, connection)
|
||||
|
||||
Impl.broadcast!(map_id, :remove_connections, [connection])
|
||||
map_id |> WandererApp.Map.remove_connection(connection)
|
||||
|
||||
_error ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_remove_connection(_map_id, _location, _old_location), do: :ok
|
||||
|
||||
defp update_connection(
|
||||
%{map_id: map_id} = state,
|
||||
update_method,
|
||||
attributes,
|
||||
%{
|
||||
solar_system_source_id: solar_system_source_id,
|
||||
solar_system_target_id: solar_system_target_id
|
||||
} = update,
|
||||
callback_fn \\ nil
|
||||
) do
|
||||
with {:ok, connection} <-
|
||||
WandererApp.Map.find_connection(
|
||||
map_id,
|
||||
solar_system_source_id,
|
||||
solar_system_target_id
|
||||
),
|
||||
{:ok, update_map} <- Impl.get_update_map(update, attributes),
|
||||
{:ok, updated_connection} <-
|
||||
apply(WandererApp.MapConnectionRepo, update_method, [
|
||||
connection,
|
||||
update_map
|
||||
]),
|
||||
:ok <-
|
||||
WandererApp.Map.update_connection(
|
||||
map_id,
|
||||
connection |> Map.merge(update_map)
|
||||
) do
|
||||
if not is_nil(callback_fn) do
|
||||
callback_fn.(updated_connection)
|
||||
end
|
||||
|
||||
Impl.broadcast!(map_id, :update_connection, updated_connection)
|
||||
|
||||
state
|
||||
else
|
||||
{:error, error} ->
|
||||
Logger.error("Failed to update connection: #{inspect(error, pretty: true)}")
|
||||
|
||||
state
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -27,6 +27,7 @@ defmodule WandererApp.MapConnectionRepo do
|
||||
end
|
||||
end
|
||||
|
||||
def create(connection), do: connection |> WandererApp.Api.MapConnection.create()
|
||||
def create!(connection), do: connection |> WandererApp.Api.MapConnection.create!()
|
||||
|
||||
def destroy(map_id, connection) when not is_nil(connection) do
|
||||
@@ -70,6 +71,11 @@ defmodule WandererApp.MapConnectionRepo do
|
||||
connection
|
||||
|> WandererApp.Api.MapConnection.update_time_status(update)
|
||||
|
||||
def update_type(connection, update),
|
||||
do:
|
||||
connection
|
||||
|> WandererApp.Api.MapConnection.update_type(update)
|
||||
|
||||
def update_mass_status(connection, update),
|
||||
do:
|
||||
connection
|
||||
|
||||
@@ -146,7 +146,12 @@ defmodule WandererAppWeb.CoreComponents do
|
||||
class="flex flex-col p-4 items-center absolute bottom-16 left-1 gap-2 tooltip tooltip-right"
|
||||
data-tip="server: Tranquility"
|
||||
>
|
||||
<div class={"block w-4 h-4 rounded-full shadow-inner #{if @online, do: " bg-green-500 animate-pulse", else: "bg-red-500"}"}>
|
||||
<div
|
||||
:if={@online}
|
||||
class="absolute block w-4 h-4 rounded-full shadow-inner bg-green-500 animate-ping"
|
||||
>
|
||||
</div>
|
||||
<div class={"block w-4 h-4 rounded-full shadow-inner #{if @online, do: " bg-green-500", else: "bg-red-500"}"}>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
@@ -30,25 +30,35 @@ defmodule WandererAppWeb.Layouts do
|
||||
phx-hook="NewVersionUpdate"
|
||||
phx-update="ignore"
|
||||
data-version={@app_version}
|
||||
class="!z-100 hidden group alert items-center fixed bottom-52 left-2 fade-in-scale text-white !bg-opacity-70 w-10 h-10 hover:w-[250px] hover:h-[70px] rounded p-px overflow-hidden"
|
||||
class="!z-1000 hidden absolute top-0 left-0 w-full h-full group items-center fade-in-scale text-white !bg-opacity-70 rounded p-px overflow-hidden flex items-center"
|
||||
>
|
||||
<div class="group animate-rotate absolute inset-0 h-full w-full rounded-full bg-[conic-gradient(#0ea5e9_20deg,transparent_120deg)] group-hover:bg-[#0ea5e9]" />
|
||||
|
||||
<div class="!bg-black rounded w-9 h-9 hover:m-0 group-hover:w-[246px] group-hover:h-[66px] flex items-center justify-center p-2 relative z-20">
|
||||
<.icon name="hero-bell-alert" class="animate-pulse group-hover:hidden absolute top-2 h-5 w-5" />
|
||||
<div class="opacity-0 group-hover:opacity-100 flex flex-col items-center justify-center w-[250px] h-full">
|
||||
<div class="text-white text-nowrap text-sm">
|
||||
New Version Available
|
||||
</div>
|
||||
<a href="/changelog" target="_blank" class="text-sm link-secondary">What's new?</a>
|
||||
<div class="hs-overlay-backdrop transition duration absolute left-0 top-0 w-full h-full bg-gray-900 bg-opacity-50 dark:bg-opacity-80 dark:bg-neutral-900">
|
||||
</div>
|
||||
<div class="absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] flex items-center">
|
||||
<div class="rounded w-9 h-9 w-[80px] h-[66px] flex items-center justify-center relative z-20">
|
||||
<.icon name="hero-chevron-double-right" class="w-9 h-9 mr-[-40px]" />
|
||||
</div>
|
||||
<div id="refresh-area">
|
||||
<.live_component module={WandererAppWeb.MapRefresh} id="map-refresh" />
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="invisible group-hover:visible update-button p-button p-component p-button-outlined p-button-sm p-0 px-1 w-[76px]"
|
||||
>
|
||||
Update
|
||||
</button>
|
||||
<div class="rounded h-[66px] flex items-center justify-center relative z-20">
|
||||
<div class=" flex items-center w-[200px] h-full">
|
||||
<.icon name="hero-chevron-double-left" class="w-9 h-9 mr-[20px]" />
|
||||
<div class=" flex flex-col items-center justify-center h-full">
|
||||
<div class="text-white text-nowrap text-sm [text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]">
|
||||
Update Required
|
||||
</div>
|
||||
<a
|
||||
href="/changelog"
|
||||
target="_blank"
|
||||
class="text-sm link-secondary [text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]"
|
||||
>
|
||||
What's new?
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
197
lib/wanderer_app_web/components/map/map_refresh.ex
Normal file
197
lib/wanderer_app_web/components/map/map_refresh.ex
Normal file
@@ -0,0 +1,197 @@
|
||||
defmodule WandererAppWeb.MapRefresh do
|
||||
use WandererAppWeb, :live_component
|
||||
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div id="map-refresh" class="socket">
|
||||
<div class="gel center-gel">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c1 r1">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c2 r1">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c3 r1">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c4 r1">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c5 r1">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c6 r1">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
|
||||
<div class="gel c7 r2">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
|
||||
<div class="gel c8 r2">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c9 r2">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c10 r2">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c11 r2">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c12 r2">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c13 r2">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c14 r2">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c15 r2">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c16 r2">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c17 r2">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c18 r2">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c19 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c20 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c21 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c22 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c23 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c24 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c25 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c26 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c28 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c29 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c30 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c31 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c32 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c33 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c34 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c35 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c36 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
<div class="gel c37 r3">
|
||||
<div class="hex-brick h1"></div>
|
||||
<div class="hex-brick h2"></div>
|
||||
<div class="hex-brick h3"></div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
end
|
||||
@@ -54,7 +54,8 @@ defmodule WandererAppWeb.MapConnectionsEventHandler do
|
||||
map_id
|
||||
|> WandererApp.Map.Server.add_connection(%{
|
||||
solar_system_source_id: solar_system_source_id |> String.to_integer(),
|
||||
solar_system_target_id: solar_system_target_id |> String.to_integer()
|
||||
solar_system_target_id: solar_system_target_id |> String.to_integer(),
|
||||
character_id: tracked_character_ids |> List.first()
|
||||
})
|
||||
|
||||
{:ok, _} =
|
||||
@@ -122,6 +123,7 @@ defmodule WandererAppWeb.MapConnectionsEventHandler do
|
||||
method_atom =
|
||||
case param do
|
||||
"time_status" -> :update_connection_time_status
|
||||
"type" -> :update_connection_type
|
||||
"mass_status" -> :update_connection_mass_status
|
||||
"ship_size_type" -> :update_connection_ship_size_type
|
||||
"locked" -> :update_connection_locked
|
||||
@@ -132,6 +134,7 @@ defmodule WandererAppWeb.MapConnectionsEventHandler do
|
||||
key_atom =
|
||||
case param do
|
||||
"time_status" -> :time_status
|
||||
"type" -> :type
|
||||
"mass_status" -> :mass_status
|
||||
"ship_size_type" -> :ship_size_type
|
||||
"locked" -> :locked
|
||||
|
||||
@@ -64,6 +64,7 @@ defmodule WandererAppWeb.MapEventHandler do
|
||||
"get_connection_info",
|
||||
"get_passages",
|
||||
"update_connection_time_status",
|
||||
"update_connection_type",
|
||||
"update_connection_mass_status",
|
||||
"update_connection_ship_size_type",
|
||||
"update_connection_locked",
|
||||
@@ -215,6 +216,7 @@ defmodule WandererAppWeb.MapEventHandler do
|
||||
solar_system_target: solar_system_target,
|
||||
mass_status: mass_status,
|
||||
time_status: time_status,
|
||||
type: type,
|
||||
ship_size_type: ship_size_type,
|
||||
locked: locked
|
||||
} = _connection
|
||||
@@ -223,6 +225,7 @@ defmodule WandererAppWeb.MapEventHandler do
|
||||
id: "#{solar_system_source}_#{solar_system_target}",
|
||||
mass_status: mass_status,
|
||||
time_status: time_status,
|
||||
type: type,
|
||||
ship_size_type: ship_size_type,
|
||||
locked: locked,
|
||||
source: "#{solar_system_source}",
|
||||
|
||||
2
mix.exs
2
mix.exs
@@ -2,7 +2,7 @@ defmodule WandererApp.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
@source_url "https://github.com/wanderer-industries/wanderer"
|
||||
@version "1.19.2"
|
||||
@version "1.21.0"
|
||||
|
||||
def project do
|
||||
[
|
||||
|
||||
21
priv/repo/migrations/20241122091348_add_connection_type.exs
Normal file
21
priv/repo/migrations/20241122091348_add_connection_type.exs
Normal file
@@ -0,0 +1,21 @@
|
||||
defmodule WandererApp.Repo.Migrations.AddConnectionType do
|
||||
@moduledoc """
|
||||
Updates resources based on their most recent snapshots.
|
||||
|
||||
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
||||
"""
|
||||
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
alter table(:map_chain_v1) do
|
||||
add :type, :bigint, default: 0
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:map_chain_v1) do
|
||||
remove :type
|
||||
end
|
||||
end
|
||||
end
|
||||
178
priv/resource_snapshots/repo/map_chain_v1/20241122091349.json
Normal file
178
priv/resource_snapshots/repo/map_chain_v1/20241122091349.json
Normal file
@@ -0,0 +1,178 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_source",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_target",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "mass_status",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "time_status",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "1",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship_size_type",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "type",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "wormhole_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "count_of_passage",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "locked",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "custom_info",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "map_chain_v1_map_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "maps_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "map_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "D9587518CE16355CA4FB501321848465A3E1DDD7800563069242249DA0B4C389",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "map_chain_v1"
|
||||
}
|
||||
Reference in New Issue
Block a user