Compare commits

..

10 Commits

Author SHA1 Message Date
CI
c1ecd3690e chore: release version v1.21.0
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-11-24 15:55:38 +00:00
Aleksei Chichenkov
3250fe1ec6 Merge pull request #74 from wanderer-industries/dessign-issues-2
feat(Map): add new gate design, change EOL placement
2024-11-24 18:55:12 +03:00
achichenkov
48e8cd93b9 feat(Map): add new gate design, change EOL placement 2024-11-24 18:30:40 +03:00
CI
afacbb16b6 chore: release version v1.20.1
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-11-22 12:42:02 +00:00
Dmitry Popov
dfad127f32 Merge branch 'main' of github.com:wanderer-industries/wanderer 2024-11-22 13:41:35 +01:00
Dmitry Popov
300c1b5a18 chore: release version v1.19.3 2024-11-22 13:41:30 +01:00
CI
bb38e1710b chore: release version v1.20.0
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / 🛠 Build (1.17, 18.x, 27) (push) Waiting to run
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
2024-11-22 12:27:53 +00:00
Dmitry Popov
0857a82de5 feat(Core): Add connection type for Gates, add new Update logic
fixes #73
2024-11-22 13:27:17 +01:00
CI
da5afcc91c chore: release version v1.19.3
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2024-11-20 14:09:27 +00:00
Dmitry Popov
0002979fda fix(Core): Fix adding systems on splash (#71)
* fix(Core): Fix adding systems on splash
2024-11-20 18:08:59 +04:00
31 changed files with 1711 additions and 685 deletions

View File

@@ -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)

View File

@@ -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 */

View File

@@ -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`,

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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(

View File

@@ -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 = {

View File

@@ -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);
}, []);

View File

@@ -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;

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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',

View File

@@ -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();
},

View File

@@ -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"

View File

@@ -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==

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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>
"""

View File

@@ -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>
"""

View 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

View File

@@ -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

View File

@@ -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}",

View File

@@ -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
[

View 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

View 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"
}