Improve settings display

This commit is contained in:
Corey 2024-04-15 12:05:27 -04:00
parent ba172f599c
commit fb5e67924d
Signed by: coco
GPG Key ID: 051138DA6AEE3E60
25 changed files with 1532 additions and 851 deletions

View File

@ -17,6 +17,7 @@ services:
volumes: volumes:
- ixion-dev-node_modules:/workspace/client/node_modules - ixion-dev-node_modules:/workspace/client/node_modules
- ../client:/workspace/client:cached - ../client:/workspace/client:cached
- ../api:/workspace/api:cached
ports: ports:
- 3000:3000 - 3000:3000
depends_on: depends_on:
@ -32,6 +33,7 @@ services:
volumes: volumes:
- ixion-dev-static:/workspace/api/static - ixion-dev-static:/workspace/api/static
- ../api:/workspace/api:cached - ../api:/workspace/api:cached
- ../client:/workspace/client:cached
ports: ports:
- 8000:8000 - 8000:8000
depends_on: depends_on:

View File

@ -18,12 +18,13 @@ Including another URLconf
from django.contrib import admin from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from rest_framework import routers from rest_framework import routers
from v1.views import server from v1.views.server import ServerViewSet, ServerDetailsViewSet
router = routers.DefaultRouter() router = routers.DefaultRouter()
router.register(r"servers", server.ServerViewSet, "v1") router.register(r"servers", ServerViewSet, basename="servers")
router.register(r"server", ServerDetailsViewSet, basename="server")
urlpatterns = [ urlpatterns = [
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
path("v1/", include(router.urls)), path("", include(router.urls)),
] ]

View File

@ -5,8 +5,9 @@ services:
restart: always restart: always
volumes: volumes:
- .:/app - .:/app
ports: - static:/app/static
- 8000:8000 expose:
- 8000
depends_on: depends_on:
- redis - redis
celery: celery:
@ -28,3 +29,14 @@ services:
redis: redis:
image: redis:alpine image: redis:alpine
restart: always restart: always
nginx:
image: nginx
ports:
- 9969:80
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./static:/app/static
depends_on:
- api
volumes:
static:

20
api/nginx.conf Normal file
View File

@ -0,0 +1,20 @@
upstream ixion_api {
server api:8000;
}
server {
listen 80;
location / {
proxy_pass http://ixion_api;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /static/ {
alias /app/static/;
}
}

View File

@ -0,0 +1,44 @@
# Generated by Django 5.0.4 on 2024-04-15 16:13
import v1.models.server
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="Server",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(editable=False, max_length=255, null=True)),
(
"url",
models.CharField(
max_length=255,
validators=[v1.models.server.OptionalSchemeURLValidator()],
),
),
("max_level", models.IntegerField(editable=False, null=True)),
("settings", models.JSONField(editable=False, null=True)),
("customizations", models.JSONField(editable=False, null=True)),
("login_limit", models.IntegerField(editable=False, null=True)),
("active_sessions", models.IntegerField(editable=False, null=True)),
("created", models.DateTimeField(auto_now_add=True)),
("updated", models.DateTimeField(auto_now=True)),
("inactivity_counter", models.IntegerField(editable=False, null=True)),
],
),
]

View File

@ -1,4 +1,6 @@
import json
import os import os
import socket
import requests import requests
from django.db import models from django.db import models
from django.core.validators import URLValidator from django.core.validators import URLValidator
@ -6,13 +8,23 @@ from django.core.exceptions import ValidationError
from urllib.parse import urlparse from urllib.parse import urlparse
from v1.common.logging import logger from v1.common.logging import logger
# Required and recommended settings used to build the initial client server card
required_settings = [ required_settings = [
"MAIN.SERVER_NAME", "MAIN.SERVER_NAME",
"MAIN.MAX_LEVEL", "MAIN.MAX_LEVEL",
"LOGIN.LOGIN_LIMIT", "LOGIN.LOGIN_LIMIT",
] ]
recommended_settings = [
"MAIN.ENABLE_TRUST_CASTING",
"MAP.LEVEL_SYNC_ENABLE",
"LOGIN.RISE_OF_ZILART",
"LOGIN.CHAINS_OF_PROMATHIA",
"LOGIN.TREASURES_OF_AHT_URGHAN",
"LOGIN.WINGS_OF_THE_GODDESS",
"LOGIN.SEEKERS_OF_ADOULIN",
]
class OptionalSchemeURLValidator(URLValidator): class OptionalSchemeURLValidator(URLValidator):
def __call__(self, value): def __call__(self, value):
@ -23,12 +35,16 @@ class OptionalSchemeURLValidator(URLValidator):
class Server(models.Model): class Server(models.Model):
name = models.CharField(max_length=255, null=True, editable=False)
url = models.CharField( url = models.CharField(
max_length=400, max_length=255,
null=False, null=False,
validators=[OptionalSchemeURLValidator()], validators=[OptionalSchemeURLValidator()],
) )
max_level = models.IntegerField(null=True, editable=False)
settings = models.JSONField(null=True, editable=False) settings = models.JSONField(null=True, editable=False)
customizations = models.JSONField(null=True, editable=False)
login_limit = models.IntegerField(null=True, editable=False)
active_sessions = models.IntegerField(null=True, editable=False) active_sessions = models.IntegerField(null=True, editable=False)
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True) updated = models.DateTimeField(auto_now=True)
@ -73,26 +89,59 @@ class Server(models.Model):
def parse_server_api(self): def parse_server_api(self):
try: try:
# Request server settings from API # Request server settings from API
response = requests.get(f"http://{self.url}/api/settings") response = requests.get(f"http://{self.url}/api/settings", timeout=5)
response.raise_for_status() # Raise an exception for HTTP errors (4xx and 5xx) response.raise_for_status()
json_data = response.json() server_settings = response.json()
if not isinstance(server_settings, dict):
# Validate the JSON structure
if not isinstance(json_data, dict):
return False return False
# Check for required settings
for setting in required_settings: for setting in required_settings:
if setting not in json_data: if setting not in server_settings:
return False return False
self.name = server_settings["MAIN.SERVER_NAME"]
self.max_level = server_settings["MAIN.MAX_LEVEL"]
self.login_limit = server_settings["LOGIN.LOGIN_LIMIT"]
self.settings = json_data # Check customizations
with open("defaultLsbSettings.json", "r") as default_settings_file:
default_settings = json.load(default_settings_file)
customizations = {}
customizations["LOGIN.CLIENT_VER"] = server_settings["LOGIN.CLIENT_VER"]
for key, value in server_settings.items():
if key not in required_settings and (
key in recommended_settings
or key not in default_settings
or default_settings[key] != value
):
customizations[key] = value
self.customizations = customizations
self.settings = server_settings
# Request the active session count from API # Request the active session count from API
response = requests.get(f"http://{self.url}/api/sessions") response = requests.get(f"http://{self.url}/api/sessions")
response.raise_for_status() # Raise an exception for HTTP errors (4xx and 5xx) response.raise_for_status()
session_count = response.text session_count = response.text
if session_count.isdigit(): if session_count.isdigit():
self.active_sessions = int(session_count) self.active_sessions = int(session_count)
# Test other server ports (you can actually change all of these?)
# ports_to_check = [
# 54230, # NETWORK.LOGIN_DATA_PORT, NETWORK.MAP_PORT
# 54231, # NETWORK.LOGIN_AUTH_PORT
# 54001, # NETWORK.LOGIN_VIEW_PORT
# ]
# for port in ports_to_check:
# try:
# with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
# s.settimeout(5)
# s.connect((self.url, port))
# except socket.error:
# return False
return True return True
except requests.RequestException: except requests.RequestException:

View File

@ -6,10 +6,26 @@ from django.core.exceptions import ValidationError
class ServerSerializer(serializers.ModelSerializer): class ServerSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Server model = Server
fields = "__all__" fields = [
"id",
"name",
"url",
"max_level",
"customizations",
"login_limit",
"active_sessions",
"updated",
"inactivity_counter",
]
def create(self, validated_data): def create(self, validated_data):
try: try:
return super().create(validated_data) return super().create(validated_data)
except ValidationError as e: except ValidationError as e:
raise serializers.ValidationError({"url": e.messages}) raise serializers.ValidationError({"url": e.messages})
class ServerDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = Server
fields = "__all__"

View File

@ -1,19 +1,9 @@
from rest_framework import viewsets, mixins from rest_framework import viewsets, mixins
from django.db.models.fields.json import KT from django.db.models.fields.json import KT
from v1.serializers.server import ServerSerializer from v1.serializers.server import ServerDetailsSerializer, ServerSerializer
from v1.models.server import Server from v1.models.server import Server
class ServerView(
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet,
):
serializer_class = ServerSerializer
queryset = Server.objects.all()
class ServerViewSet( class ServerViewSet(
mixins.CreateModelMixin, mixins.CreateModelMixin,
mixins.RetrieveModelMixin, mixins.RetrieveModelMixin,
@ -28,14 +18,6 @@ class ServerViewSet(
"annotate_field": "settings__MAIN.SERVER_NAME", "annotate_field": "settings__MAIN.SERVER_NAME",
"filter_type": "startswith", "filter_type": "startswith",
}, },
"level_cap_min": {
"annotate_field": "settings__MAIN.MAX_LEVEL",
"filter_type": "gte",
},
"level_cap_max": {
"annotate_field": "settings__MAIN.MAX_LEVEL",
"filter_type": "lte",
},
} }
def get_queryset(self): def get_queryset(self):
@ -58,3 +40,11 @@ class ServerViewSet(
queryset._result_cache = None queryset._result_cache = None
return queryset return queryset
class ServerDetailsViewSet(
mixins.RetrieveModelMixin,
viewsets.GenericViewSet,
):
queryset = Server.objects.all()
serializer_class = ServerDetailsSerializer

View File

@ -16,7 +16,12 @@
href="https://fonts.googleapis.com/icon?family=Material+Icons" href="https://fonts.googleapis.com/icon?family=Material+Icons"
/> />
<title>IXION - FFXI Private Server Directory</title> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
<title>IXION - Private Servers</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

BIN
client/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

View File

@ -23,7 +23,7 @@ export const fetchDemo = async () => {
export const fetchData = async (queryParams = {}) => { export const fetchData = async (queryParams = {}) => {
const queryString = new URLSearchParams(queryParams).toString(); const queryString = new URLSearchParams(queryParams).toString();
const url = `${import.meta.env.PROD ? 'https://api.ixion.dev' : 'http://localhost:8000'}/v1/servers/${queryString ? `?${queryString}` : ''}`; const url = `${import.meta.env.PROD ? 'https://api.ixion.dev' : 'http://localhost:8000'}/servers/${queryString ? `?${queryString}` : ''}`;
try { try {
const response = await fetch(url, { const response = await fetch(url, {
signal: AbortSignal.timeout(5000), signal: AbortSignal.timeout(5000),
@ -47,7 +47,7 @@ export const fetchData = async (queryParams = {}) => {
export const postData = async (inputText: string) => { export const postData = async (inputText: string) => {
try { try {
const response = await fetch( const response = await fetch(
`${import.meta.env.PROD ? 'https://api.ixion.dev' : 'http://localhost:8000'}/v1/servers/`, `${import.meta.env.PROD ? 'https://api.ixion.dev' : 'http://localhost:8000'}/servers/`,
{ {
method: 'POST', method: 'POST',
headers: { headers: {

View File

@ -30,7 +30,7 @@ export const AccordionSummary = styled((props: AccordionSummaryProps) => (
'& .MuiAccordionSummary-content': { '& .MuiAccordionSummary-content': {
margin: theme.spacing(0), margin: theme.spacing(0),
}, },
boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)', // Adjust the size and opacity as needed boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)',
minHeight: '32px', minHeight: '32px',
})); }));

View File

@ -75,7 +75,6 @@ export default function Header({
sx={{ sx={{
color: 'inherit', color: 'inherit',
textDecoration: 'none', textDecoration: 'none',
userSelect: 'none',
flexGrow: 1, flexGrow: 1,
}} }}
> >

View File

@ -1,4 +1,6 @@
import { import {
Check,
Close,
ContentCopy, ContentCopy,
ExpandMore, ExpandMore,
Launch, Launch,
@ -10,13 +12,9 @@ import {
Box, Box,
Card, Card,
CardContent, CardContent,
Chip,
Divider, Divider,
IconButton, IconButton,
Table,
TableBody,
TableCell,
TableContainer,
TableRow,
ToggleButton, ToggleButton,
ToggleButtonGroup, ToggleButtonGroup,
Tooltip, Tooltip,
@ -25,7 +23,11 @@ import {
} from '@mui/material'; } from '@mui/material';
import { useState } from 'react'; import { useState } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import ServerData, { ServerSettingsInfo } from '../data/ServerData'; import ServerData, {
ServerSetting,
ServerSettingsInfo,
} from '../data/ServerData';
import CopyImageIcon from '../images/copy-image.png';
import { Accordion, AccordionDetails, AccordionSummary } from './Accordion'; import { Accordion, AccordionDetails, AccordionSummary } from './Accordion';
export default function ServerCard({ server }: { server: ServerData }) { export default function ServerCard({ server }: { server: ServerData }) {
@ -63,15 +65,75 @@ export default function ServerCard({ server }: { server: ServerData }) {
}; };
function formatExternalUrl(url: string): string { function formatExternalUrl(url: string): string {
// Check if the URL starts with a valid protocol // prepend 'https://' to the URL if it's not already there
if (!/^https?:\/\//i.test(url)) { if (!/^https?:\/\//i.test(url)) {
// If not, prepend 'https://' to the URL
return `https://${url}`; return `https://${url}`;
} }
// Otherwise, return the original URL
return url; return url;
} }
const renderSettingsChip = ([key, value]: [
string,
boolean | string | number,
]) => {
if (!ServerSettingsInfo[key]) return null;
const transformValue = (
v: boolean | string | number,
setting: ServerSetting
): string | number | boolean => {
return setting.transform?.(v) ?? v;
};
let chipValue: string | number | boolean | JSX.Element = transformValue(
value,
ServerSettingsInfo[key]
);
if (typeof chipValue === 'boolean') {
chipValue = chipValue ? (
<Check color="success" />
) : (
<Close color="error" />
);
}
return (
<Tooltip
arrow
disableInteractive
title={ServerSettingsInfo[key].description}
>
<Chip
key={key}
label={`${ServerSettingsInfo[key].name === '' ? key : ServerSettingsInfo[key].name}`}
avatar={
typeof chipValue === 'object' ? undefined : (
<Chip label={chipValue} size="small" />
)
}
icon={typeof chipValue === 'object' ? chipValue : undefined}
size="small"
className="m-1 pr-1"
sx={{
'& .MuiChip-avatar': {
width: 'auto',
marginX: 0,
order: 2,
},
'& .MuiChip-icon': {
marginX: 0,
order: 2,
},
'&> .MuiChip-label': {
paddingRight: 0.5,
},
boxShadow: '0px 3px 3px rgba(0, 0, 0, .25)',
}}
/>
</Tooltip>
);
};
const expansions = [ const expansions = [
<ToggleButton <ToggleButton
key="rotz" key="rotz"
@ -125,7 +187,7 @@ export default function ServerCard({ server }: { server: ServerData }) {
<Accordion className="my-0"> <Accordion className="my-0">
<AccordionSummary expandIcon={<ExpandMore />}> <AccordionSummary expandIcon={<ExpandMore />}>
<Box className="flex items-center justify-center"> <Box className="flex items-center justify-center">
{server.settings['LOGIN.MAINT_MODE'] === 1 ? ( {server.customizations['LOGIN.MAINT_MODE'] === 1 ? (
<Tooltip <Tooltip
arrow arrow
disableInteractive disableInteractive
@ -158,14 +220,16 @@ export default function ServerCard({ server }: { server: ServerData }) {
color={(theme) => alpha(theme.palette.text.primary, 0.87)} color={(theme) => alpha(theme.palette.text.primary, 0.87)}
sx={{ lineHeight: 1.0 }} sx={{ lineHeight: 1.0 }}
> >
{server.settings['MAIN.SERVER_NAME']} {server.name}
</Typography> </Typography>
{typeof server.settings['API.WEBSITE'] === 'string' && {typeof server.customizations['API.WEBSITE'] === 'string' &&
server.settings['API.WEBSITE'] !== '' && ( server.customizations['API.WEBSITE'] !== '' && (
<Tooltip arrow disableInteractive title="Visit website."> <Tooltip arrow disableInteractive title="Visit website.">
<IconButton <IconButton
component={Link} component={Link}
to={formatExternalUrl(server.settings['API.WEBSITE'])} to={formatExternalUrl(
server.customizations['API.WEBSITE']
)}
target="_blank" target="_blank"
rel="noopener" rel="noopener"
size="small" size="small"
@ -225,11 +289,12 @@ export default function ServerCard({ server }: { server: ServerData }) {
<ToggleButtonGroup <ToggleButtonGroup
size="small" size="small"
value={[ value={[
server.settings['LOGIN.RISE_OF_ZILART'] && 'rotz', server.customizations['LOGIN.RISE_OF_ZILART'] && 'rotz',
server.settings['LOGIN.CHAINS_OF_PROMATHIA'] && 'cop', server.customizations['LOGIN.CHAINS_OF_PROMATHIA'] && 'cop',
server.settings['LOGIN.TREASURES_OF_AHT_URGHAN'] && 'toau', server.customizations['LOGIN.TREASURES_OF_AHT_URGHAN'] &&
server.settings['LOGIN.WINGS_OF_THE_GODDESS'] && 'wotg', 'toau',
server.settings['LOGIN.SEEKERS_OF_ADOULIN'] && 'soa', server.customizations['LOGIN.WINGS_OF_THE_GODDESS'] && 'wotg',
server.customizations['LOGIN.SEEKERS_OF_ADOULIN'] && 'soa',
]} ]}
sx={{ '& button': { lineHeight: 1.0 } }} sx={{ '& button': { lineHeight: 1.0 } }}
> >
@ -241,95 +306,57 @@ export default function ServerCard({ server }: { server: ServerData }) {
variant="h5" variant="h5"
color={(theme) => theme.palette.text.secondary} color={(theme) => theme.palette.text.secondary}
> >
{`Lv.${server.settings['MAIN.MAX_LEVEL']}`} {`Lv.${server.max_level}`}
</Typography> </Typography>
</Box> </Box>
</AccordionSummary> </AccordionSummary>
<AccordionDetails className="p-2"> <AccordionDetails className="p-2">
<TableContainer> {server.customizations['MAIN.SERVER_MESSAGE'] && (
<Table size="small"> <>
<TableBody> <Box className="flex justify-center">
<TableRow> <Typography variant="body2">
<TableCell> {server.customizations['MAIN.SERVER_MESSAGE']}
<Tooltip </Typography>
title={ </Box>
ServerSettingsInfo['LOGIN.LOGIN_LIMIT'].description <Divider sx={{ marginY: 1 }} />
} </>
arrow )}
disableInteractive <Box
> sx={{
<Typography variant="caption" sx={{ userSelect: 'none' }}> display: 'flex',
{`${ServerSettingsInfo['LOGIN.LOGIN_LIMIT'].name}: `} flexWrap: 'wrap',
{server.settings['LOGIN.LOGIN_LIMIT'] === 0 justifyContent: 'center',
? 'unlimited' maxWidth: '100%',
: server.settings['LOGIN.LOGIN_LIMIT']} }}
</Typography> >
</Tooltip> {Object.entries(server.customizations).map(renderSettingsChip)}
</TableCell> </Box>
<TableCell>
<Tooltip
title={
ServerSettingsInfo['MAIN.ENABLE_TRUST_CASTING']
.description
}
arrow
disableInteractive
>
<Typography variant="caption" sx={{ userSelect: 'none' }}>
{`${ServerSettingsInfo['MAIN.ENABLE_TRUST_CASTING'].name}: `}
{server.settings['MAIN.ENABLE_TRUST_CASTING'] === 1
? 'Enabled'
: 'Disabled'}
</Typography>
</Tooltip>
</TableCell>
<TableCell>
<Tooltip
title={
ServerSettingsInfo['MAP.LEVEL_SYNC_ENABLE'].description
}
arrow
disableInteractive
>
<Typography variant="caption" sx={{ userSelect: 'none' }}>
{`${ServerSettingsInfo['MAP.LEVEL_SYNC_ENABLE'].name}: `}
{server.settings['MAP.LEVEL_SYNC_ENABLE']
? 'Enabled'
: 'Disabled'}
</Typography>
</Tooltip>
</TableCell>
</TableRow>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<TableCell>
<Tooltip
title={ServerSettingsInfo['MAP.SPEED_MOD'].description}
arrow
disableInteractive
>
<Typography variant="caption" sx={{ userSelect: 'none' }}>
{`${ServerSettingsInfo['MAP.SPEED_MOD'].name}: `}
{typeof server.settings['MAP.SPEED_MOD'] === 'number'
? (server.settings['MAP.SPEED_MOD'] < 0 ? '' : '+') +
((50 + server.settings['MAP.SPEED_MOD']) / 50 - 1) *
100
: '???'}
%
</Typography>
</Tooltip>
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
</AccordionDetails> </AccordionDetails>
</Accordion> </Accordion>
<Divider /> <Divider />
<CardContent className="flex justify-between py-1"> <CardContent className="flex justify-between py-1">
<Typography variant="caption" sx={{ userSelect: 'none' }}> <Typography variant="caption">
{server.active_sessions} active sessions {server.active_sessions} active sessions
{server.login_limit !== 1 && (
<Tooltip
title={`Server allows ${server.login_limit === 0 ? 'unlimited' : server.login_limit} simultaneous game sessions per IP.`}
arrow
disableInteractive
>
<img
src={CopyImageIcon}
alt=""
style={{
maxHeight: '1.5em',
marginLeft: '0.5em',
verticalAlign: 'middle',
}}
onContextMenu={(event) => event.preventDefault()}
/>
</Tooltip>
)}
</Typography> </Typography>
<Typography variant="caption" sx={{ userSelect: 'none' }}> <Typography variant="caption">
Updated: {new Date(server.updated).toLocaleString()} Updated: {new Date(server.updated).toLocaleString()}
</Typography> </Typography>
</CardContent> </CardContent>

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -5,4 +5,5 @@
* { * {
-webkit-user-drag: none; /* Safari */ -webkit-user-drag: none; /* Safari */
user-drag: none; user-drag: none;
user-select: none;
} }

View File

@ -45,30 +45,26 @@ export default function Home({
}, [setAlertInfo, setServers]); }, [setAlertInfo, setServers]);
const filterServers = (server: ServerData): boolean => { const filterServers = (server: ServerData): boolean => {
if ( if (searchState.name.value.length > 0) {
searchState.name.value.length > 0 && const serverName = server.name.toLowerCase();
typeof server.settings['MAIN.SERVER_NAME'] === 'string'
) {
const serverName = server.settings['MAIN.SERVER_NAME'].toLowerCase();
if (!serverName.includes(searchState.name.value.toLowerCase())) { if (!serverName.includes(searchState.name.value.toLowerCase())) {
return false; return false;
} }
} }
if (typeof server.settings['MAIN.MAX_LEVEL'] === 'number') { if (server.max_level < searchState.maxLevel.value[0]) {
if (server.settings['MAIN.MAX_LEVEL'] < searchState.maxLevel.value[0]) { return false;
return false; }
} if (server.max_level > searchState.maxLevel.value[1]) {
if (server.settings['MAIN.MAX_LEVEL'] > searchState.maxLevel.value[1]) { return false;
return false;
}
} }
if ( if (
searchState.trusts.value && searchState.trusts.value &&
typeof server.settings['MAIN.ENABLE_TRUST_CASTING'] === 'number' typeof server.customizations['MAIN.ENABLE_TRUST_CASTING'] === 'number'
) { ) {
const serverTrusts = server.settings['MAIN.ENABLE_TRUST_CASTING'] === 1; const serverTrusts =
server.customizations['MAIN.ENABLE_TRUST_CASTING'] === 1;
const searchEnabled = searchState.trusts.value.includes('enabled'); const searchEnabled = searchState.trusts.value.includes('enabled');
const searchDisabled = searchState.trusts.value.includes('disabled'); const searchDisabled = searchState.trusts.value.includes('disabled');
if (serverTrusts && searchDisabled && !searchEnabled) { if (serverTrusts && searchDisabled && !searchEnabled) {
@ -81,9 +77,9 @@ export default function Home({
if ( if (
searchState.levelSync.value && searchState.levelSync.value &&
typeof server.settings['MAP.LEVEL_SYNC_ENABLE'] === 'boolean' typeof server.customizations['MAP.LEVEL_SYNC_ENABLE'] === 'boolean'
) { ) {
const serverLevelSync = server.settings['MAP.LEVEL_SYNC_ENABLE']; const serverLevelSync = server.customizations['MAP.LEVEL_SYNC_ENABLE'];
const searchEnabled = searchState.levelSync.value.includes('enabled'); const searchEnabled = searchState.levelSync.value.includes('enabled');
const searchDisabled = searchState.levelSync.value.includes('disabled'); const searchDisabled = searchState.levelSync.value.includes('disabled');
if (serverLevelSync && searchDisabled && !searchEnabled) { if (serverLevelSync && searchDisabled && !searchEnabled) {
@ -98,19 +94,19 @@ export default function Home({
const searchNoneEnabled = searchState.expansions.value.includes('none'); const searchNoneEnabled = searchState.expansions.value.includes('none');
const searchRotzEnabled = searchState.expansions.value.includes('rotz'); const searchRotzEnabled = searchState.expansions.value.includes('rotz');
const serverRotzEnabled = const serverRotzEnabled =
server.settings['LOGIN.RISE_OF_ZILART'] === true; server.customizations['LOGIN.RISE_OF_ZILART'] === true;
const searchCopEnabled = searchState.expansions.value.includes('cop'); const searchCopEnabled = searchState.expansions.value.includes('cop');
const serverCopEnabled = const serverCopEnabled =
server.settings['LOGIN.CHAINS_OF_PROMATHIA'] === true; server.customizations['LOGIN.CHAINS_OF_PROMATHIA'] === true;
const searchToauEnabled = searchState.expansions.value.includes('toau'); const searchToauEnabled = searchState.expansions.value.includes('toau');
const serverToauEnabled = const serverToauEnabled =
server.settings['LOGIN.TREASURES_OF_AHT_URGHAN'] === true; server.customizations['LOGIN.TREASURES_OF_AHT_URGHAN'] === true;
const searchWotgEnabled = searchState.expansions.value.includes('wotg'); const searchWotgEnabled = searchState.expansions.value.includes('wotg');
const serverWotgEnabled = const serverWotgEnabled =
server.settings['LOGIN.WINGS_OF_THE_GODDESS'] === true; server.customizations['LOGIN.WINGS_OF_THE_GODDESS'] === true;
const searchSoaEnabled = searchState.expansions.value.includes('soa'); const searchSoaEnabled = searchState.expansions.value.includes('soa');
const serverSoaEnabled = const serverSoaEnabled =
server.settings['LOGIN.SEEKERS_OF_ADOULIN'] === true; server.customizations['LOGIN.SEEKERS_OF_ADOULIN'] === true;
if ( if (
searchNoneEnabled && searchNoneEnabled &&
(serverRotzEnabled || (serverRotzEnabled ||
@ -139,27 +135,22 @@ export default function Home({
} }
if (searchState.multibox.value) { if (searchState.multibox.value) {
const serverMultibox = server.settings['LOGIN.LOGIN_LIMIT']; const serverMultibox = server.login_limit;
if (typeof serverMultibox === 'number') { if (searchState.multibox.value.includes('none') && serverMultibox !== 1) {
if ( return false;
searchState.multibox.value.includes('none') && }
serverMultibox !== 1 if (
) { searchState.multibox.value.includes('unlimited') &&
return false; serverMultibox !== 0
} ) {
if ( return false;
searchState.multibox.value.includes('unlimited') && }
serverMultibox !== 0 if (
) { searchState.multibox.value.includes('limited') &&
return false; !searchState.multibox.value.includes('unlimited') &&
} serverMultibox < 2
if ( ) {
searchState.multibox.value.includes('limited') && return false;
!searchState.multibox.value.includes('unlimited') &&
serverMultibox < 2
) {
return false;
}
} }
} }