feat:移除了弹窗,服务器添加sls

This commit is contained in:
2026-09-08 22:39:45 +08:00
commit 6a295f9a7a
4082 changed files with 1322534 additions and 0 deletions

View 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()]
}

View 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')
})

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

View 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: '=' }
}