feat:移除了弹窗,服务器添加sls
This commit is contained in:
15
packages/server/src/config/registry.ts
Normal file
15
packages/server/src/config/registry.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import type { ConfigFileDefinition } from './types.ts'
|
||||
|
||||
const registry = new Map<string, ConfigFileDefinition>()
|
||||
|
||||
export function registerConfigFile(definition: ConfigFileDefinition): void {
|
||||
registry.set(definition.filename, definition)
|
||||
}
|
||||
|
||||
export function getConfigFile(filename: string): ConfigFileDefinition | undefined {
|
||||
return registry.get(filename)
|
||||
}
|
||||
|
||||
export function listConfigFiles(): ConfigFileDefinition[] {
|
||||
return [...registry.values()]
|
||||
}
|
||||
37
packages/server/src/config/server-properties.test.ts
Normal file
37
packages/server/src/config/server-properties.test.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { test } from 'node:test'
|
||||
|
||||
import { serverPropertiesDefinition } from './server-properties.ts'
|
||||
import { configFieldLabel, resolveConfigField } from './types.ts'
|
||||
|
||||
test('declared keys keep their configured kind', () => {
|
||||
const difficulty = resolveConfigField(serverPropertiesDefinition, 'difficulty', 'normal')
|
||||
assert.equal(difficulty.kind, 'enum')
|
||||
assert.deepEqual(difficulty.options, ['peaceful', 'easy', 'normal', 'hard'])
|
||||
assert.equal(difficulty.inferred, false)
|
||||
|
||||
const port = resolveConfigField(serverPropertiesDefinition, 'server-port', '25565')
|
||||
assert.equal(port.kind, 'integer')
|
||||
assert.equal(port.min, 1)
|
||||
assert.equal(port.max, 65535)
|
||||
|
||||
const online = resolveConfigField(serverPropertiesDefinition, 'online-mode', 'true')
|
||||
assert.equal(online.kind, 'boolean')
|
||||
})
|
||||
|
||||
test('unknown keys are inferred from their value', () => {
|
||||
const booleanField = resolveConfigField(serverPropertiesDefinition, 'some-flag', 'true')
|
||||
assert.equal(booleanField.kind, 'boolean')
|
||||
assert.equal(booleanField.inferred, true)
|
||||
|
||||
const numericField = resolveConfigField(serverPropertiesDefinition, 'some-count', '12')
|
||||
assert.equal(numericField.kind, 'integer')
|
||||
|
||||
const textField = resolveConfigField(serverPropertiesDefinition, 'some-name', 'hello')
|
||||
assert.equal(textField.kind, 'string')
|
||||
})
|
||||
|
||||
test('labels are humanized from the key', () => {
|
||||
assert.equal(configFieldLabel('server-port'), 'Server port')
|
||||
assert.equal(configFieldLabel('rcon.password'), 'Rcon password')
|
||||
})
|
||||
92
packages/server/src/config/server-properties.ts
Normal file
92
packages/server/src/config/server-properties.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import { inferFieldKindFromValue, type ConfigFileDefinition } from './types.ts'
|
||||
import { registerConfigFile } from './registry.ts'
|
||||
|
||||
const boolean = (key: string) => ({ key, kind: 'boolean' as const })
|
||||
const integer = (key: string, min?: number, max?: number) => ({
|
||||
key,
|
||||
kind: 'integer' as const,
|
||||
min,
|
||||
max,
|
||||
})
|
||||
const string = (key: string) => ({ key, kind: 'string' as const })
|
||||
const enumeration = (key: string, options: string[]) => ({ key, kind: 'enum' as const, options })
|
||||
|
||||
/**
|
||||
* Known server.properties fields. Keys not listed here fall back to automatic
|
||||
* type inference from the current value.
|
||||
*/
|
||||
export const serverPropertiesDefinition: ConfigFileDefinition = {
|
||||
id: 'server-properties',
|
||||
filename: 'server.properties',
|
||||
inferFieldKind: (_key, value) => inferFieldKindFromValue(value),
|
||||
fields: [
|
||||
integer('server-port', 1, 65535),
|
||||
enumeration('difficulty', ['peaceful', 'easy', 'normal', 'hard']),
|
||||
enumeration('gamemode', ['survival', 'creative', 'adventure', 'spectator']),
|
||||
enumeration('level-type', [
|
||||
'minecraft:normal',
|
||||
'minecraft:flat',
|
||||
'minecraft:large_biomes',
|
||||
'minecraft:amplified',
|
||||
'minecraft:single_biome',
|
||||
'minecraft:debug',
|
||||
'normal',
|
||||
'flat',
|
||||
'largeBiomes',
|
||||
'amplified',
|
||||
'default',
|
||||
]),
|
||||
integer('max-players', 0, 1000),
|
||||
integer('view-distance', 2, 32),
|
||||
integer('simulation-distance', 2, 32),
|
||||
integer('max-tick-time', -1),
|
||||
integer('max-world-size', 1, 29999984),
|
||||
integer('op-permission-level', 0, 4),
|
||||
integer('function-permission-level', 0, 4),
|
||||
integer('spawn-protection', 0, 256),
|
||||
integer('player-idle-timeout', 0),
|
||||
integer('network-compression-threshold', -1, 1024),
|
||||
integer('rate-limit', 0),
|
||||
integer('query.port', 1, 65535),
|
||||
integer('rcon.port', 1, 65535),
|
||||
string('level-name'),
|
||||
string('level-seed'),
|
||||
string('motd'),
|
||||
string('resource-pack'),
|
||||
string('resource-pack-sha1'),
|
||||
string('resource-pack-prompt'),
|
||||
string('rcon.password'),
|
||||
string('server-ip'),
|
||||
string('text-filtering-config'),
|
||||
string('initial-enabled-packs'),
|
||||
boolean('online-mode'),
|
||||
boolean('white-list'),
|
||||
boolean('enforce-whitelist'),
|
||||
boolean('enforce-secure-profile'),
|
||||
boolean('prevent-proxy-connections'),
|
||||
boolean('allow-flight'),
|
||||
boolean('allow-nether'),
|
||||
boolean('spawn-animals'),
|
||||
boolean('spawn-monsters'),
|
||||
boolean('spawn-npcs'),
|
||||
boolean('pvp'),
|
||||
boolean('enable-command-block'),
|
||||
boolean('enable-status'),
|
||||
boolean('enable-query'),
|
||||
boolean('enable-rcon'),
|
||||
boolean('enable-jmx-monitoring'),
|
||||
boolean('force-gamemode'),
|
||||
boolean('hardcore'),
|
||||
boolean('announce-player-achievements'),
|
||||
boolean('log-ips'),
|
||||
boolean('hide-online-players'),
|
||||
boolean('require-resource-pack'),
|
||||
boolean('sync-chunk-writes'),
|
||||
boolean('use-native-transport'),
|
||||
boolean('allow-end'),
|
||||
boolean('generate-structures'),
|
||||
boolean('enable-lan'),
|
||||
],
|
||||
}
|
||||
|
||||
registerConfigFile(serverPropertiesDefinition)
|
||||
85
packages/server/src/config/types.ts
Normal file
85
packages/server/src/config/types.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import type { PropertiesEntry } from '../properties.ts'
|
||||
|
||||
export type ConfigFieldKind = 'boolean' | 'integer' | 'number' | 'string' | 'enum'
|
||||
|
||||
export interface ConfigFieldDefinition {
|
||||
key: string
|
||||
kind: ConfigFieldKind
|
||||
/** Allowed values for `enum` fields. */
|
||||
options?: string[]
|
||||
min?: number
|
||||
max?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes an editable configuration file. Formats plug in through the
|
||||
* `entries` accessors so new files (bukkit.yml, whitelist.json, ...) only need
|
||||
* a schema definition and, when needed, their own parse/serialize functions.
|
||||
*/
|
||||
export interface ConfigFileDefinition {
|
||||
id: string
|
||||
filename: string
|
||||
fields: ConfigFieldDefinition[]
|
||||
/** Fallback inference for keys not present in `fields`. */
|
||||
inferFieldKind: (key: string, value: string) => ConfigFieldKind
|
||||
}
|
||||
|
||||
export interface ResolvedConfigField extends ConfigFieldDefinition {
|
||||
/** True when the kind was inferred from the value rather than declared. */
|
||||
inferred: boolean
|
||||
}
|
||||
|
||||
const TRUE_VALUES = new Set(['true', 'false'])
|
||||
const INTEGER_RE = /^-?\d+$/
|
||||
const NUMBER_RE = /^-?\d+(\.\d+)?$/
|
||||
|
||||
export function inferFieldKindFromValue(value: string): ConfigFieldKind {
|
||||
if (TRUE_VALUES.has(value.toLowerCase())) return 'boolean'
|
||||
if (INTEGER_RE.test(value)) return 'integer'
|
||||
if (NUMBER_RE.test(value)) return 'number'
|
||||
return 'string'
|
||||
}
|
||||
|
||||
export function resolveConfigField(
|
||||
definition: ConfigFileDefinition,
|
||||
key: string,
|
||||
value: string,
|
||||
): ResolvedConfigField {
|
||||
const declared = definition.fields.find((field) => field.key === key)
|
||||
if (declared) return { ...declared, inferred: false }
|
||||
return { key, kind: definition.inferFieldKind(key, value), inferred: true }
|
||||
}
|
||||
|
||||
/** Humanizes a config key (`server-port` -> `Server port`) as a fallback label. */
|
||||
export function configFieldLabel(key: string): string {
|
||||
const parts = key.split(/[-_.]/)
|
||||
if (parts.length === 0) return key
|
||||
const first = parts[0]
|
||||
return (
|
||||
(first.length > 0 ? first[0].toUpperCase() + first.slice(1) : first) +
|
||||
' ' +
|
||||
parts.slice(1).join(' ')
|
||||
).trimEnd()
|
||||
}
|
||||
|
||||
export interface ConfigFileDocument {
|
||||
definition: ConfigFileDefinition
|
||||
entries: PropertiesEntry[]
|
||||
}
|
||||
|
||||
export function getRawValue(document: ConfigFileDocument, key: string): string | undefined {
|
||||
const entry = document.entries.find(
|
||||
(entry): entry is Extract<PropertiesEntry, { type: 'pair' }> =>
|
||||
entry.type === 'pair' && entry.key === key,
|
||||
)
|
||||
return entry?.value
|
||||
}
|
||||
|
||||
export function setRawValue(document: ConfigFileDocument, key: string, value: string): void {
|
||||
const index = document.entries.findIndex((entry) => entry.type === 'pair' && entry.key === key)
|
||||
if (index === -1) {
|
||||
document.entries.push({ type: 'pair', key, value, separator: '=' })
|
||||
return
|
||||
}
|
||||
document.entries[index] = { type: 'pair', key, value, separator: '=' }
|
||||
}
|
||||
Reference in New Issue
Block a user