XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEServerManager

【Java】我的世界XFE服务器管理器

公开
关注 0 Fork 0 Star 0
UTF-8
export interface RichTextVariableDefinition {
  /** Variable expression without the surrounding braces. */
  key: string;
  nameZh: string;
  nameEn: string;
  descriptionZh: string;
  descriptionEn: string;
  category: string;
  /** Human-readable scope tags such as global, player-event, or schedule. */
  scopes: readonly string[];
  /** Complete placeholder ready for insertion, for example {server.tps}. */
  example?: string;
  /** Example resolved value, for example 20.0. */
  sampleValue?: string;
  formatHint?: string;
  type?: string;
  events?: readonly string[];
  templateAllowed?: boolean;
  conditionAllowed?: boolean;
  sensitive?: boolean;
}

export const RICH_TEXT_VARIABLE_CATEGORY_LABELS: Readonly<Record<string, { zh: string; en: string }>> = {
  legacy: { zh: '兼容变量', en: 'Compatibility' },
  event: { zh: '触发事件', en: 'Trigger event' },
  time: { zh: '日期与时间', en: 'Date & time' },
  server: { zh: '服务器', en: 'Server' },
  world: { zh: '世界', en: 'World' },
  player: { zh: '玩家', en: 'Player' },
  entity: { zh: '实体', en: 'Entity' },
  command: { zh: '指令与参数', en: 'Command & arguments' },
  schedule: { zh: '计划任务', en: 'Schedule' },
  economy: { zh: '经济系统', en: 'Economy' },
  trigger: { zh: '触发器运行', en: 'Trigger runtime' },
};

function eventPatternMatches(pattern: string, eventType: string): boolean {
  const normalizedPattern = pattern.trim().toLowerCase();
  const normalizedEvent = eventType.trim().toLowerCase();
  if (!normalizedPattern || !normalizedEvent) return false;

  // Split on wildcard runs first, then quote every regular-expression character
  // in the literal fragments. This keeps catalog-provided event patterns data,
  // rather than allowing them to become regular expressions themselves.
  const expression = normalizedPattern.split(/\*+/).map((fragment) =>
    fragment.replace(/[|\\{}()[\]^$+?.]/g, '\\$&')).join('.*');
  return new RegExp(`^${expression}$`).test(normalizedEvent);
}

/** Returns whether a catalog variable is available for a concrete trigger event. */
export function variableAppliesToEvent(
  variable: RichTextVariableDefinition,
  eventType: string,
): boolean {
  // `events` is authoritative on current servers. Falling back to `scopes`
  // preserves compatibility with the legacy client-side catalog and previews.
  const patterns = variable.events?.length ? variable.events : variable.scopes;
  return patterns.some((pattern) => eventPatternMatches(pattern, eventType));
}

/**
 * Conservative fallback used only when connected to an older server that does
 * not expose `catalog.variables`. Every entry is supported by the legacy
 * message renderer. New servers provide the authoritative 100+ item catalog.
 */
export const RICH_TEXT_VARIABLE_CATALOG: readonly RichTextVariableDefinition[] = Object.freeze([
  {
    key: 'player', nameZh: '玩家名(兼容)', nameEn: 'Player name (legacy)',
    descriptionZh: '等同于 player.name;需要玩家事件上下文。', descriptionEn: 'Alias of player.name; requires player event context.',
    category: 'legacy', scopes: ['player.*'], example: '{player}', type: 'string', templateAllowed: true,
  },
  {
    key: 'online', nameZh: '在线人数(兼容)', nameEn: 'Online players (legacy)',
    descriptionZh: '等同于 server.online。', descriptionEn: 'Alias of server.online.',
    category: 'legacy', scopes: ['*'], example: '{online}', type: 'number', templateAllowed: true,
  },
  {
    key: 'maxPlayers', nameZh: '人数上限(兼容)', nameEn: 'Player limit (legacy)',
    descriptionZh: '等同于 server.maxPlayers。', descriptionEn: 'Alias of server.maxPlayers.',
    category: 'legacy', scopes: ['*'], example: '{maxPlayers}', type: 'number', templateAllowed: true,
  },
  {
    key: 'date', nameZh: '事件日期(兼容)', nameEn: 'Event date (legacy)',
    descriptionZh: '计划任务使用配置时区,其他事件使用服务器时区。',
    descriptionEn: 'Uses the configured schedule zone for schedules and the server zone otherwise.',
    category: 'legacy', scopes: ['*'], example: '{date}', type: 'string', templateAllowed: true,
  },
]);