Skip to content
This repository was archived by the owner on May 2, 2026. It is now read-only.
This repository was archived by the owner on May 2, 2026. It is now read-only.

另一种实现思路 #1

Description

@HJWqa

不知道作者的 mods_data.json 是怎么获取的
不过看着好累的样子Image

不知道您有没有看过这样一个思路:
检查jar包里的配置文件

希望能帮到您
如果您愿意的话,可以拿这个方案做原方案的兜底

我之前简单实现了以下
以下代码由AI生成

const fs = require('fs/promises');
const path = require('path');
const AdmZip = require('adm-zip');
const toml = require('@iarna/toml');
const readline = require('readline');

// ===================== 配置 =====================
const MODS_DIR = path.resolve('./mods');
const BACKUP_DIR = path.resolve('./backup_deleted_mods');
// =================================================

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

function confirm(msg) {
  return new Promise(resolve => {
    rl.question(msg, ans => {
      resolve(ans.trim().toLowerCase() === 'y');
    });
  });
}

// 正确判断:是否真·客户端MOD
async function isClientOnlyMod(jarPath) {
  try {
    const zip = new AdmZip(jarPath);

    const neoforgeToml = zip.getEntry('META-INF/neoforge.mods.toml');
    const forgeToml = zip.getEntry('META-INF/mods.toml');
    const tomlEntry = neoforgeToml || forgeToml;

    if (tomlEntry) {
      const content = tomlEntry.getData().toString('utf8');
      const parsed = toml.parse(content);

      // 1. 看mod自身side
      if (Array.isArray(parsed.mods)) {
        for (const mod of parsed.mods) {
          if (mod.side === 'CLIENT') {
            return true;
          }
        }
      }

      // 2. 看依赖:必须是 required 且 side=CLIENT 才判定
      if (parsed.dependencies) {
        for (const depKey of Object.keys(parsed.dependencies)) {
          const deps = parsed.dependencies[depKey];
          if (!Array.isArray(deps)) continue;
          for (const d of deps) {
            if (d.type === 'required' && d.side === 'CLIENT') {
              return true;
            }
          }
        }
      }
    }

    // Fabric / Quilt
    const fabricEntry = zip.getEntry('fabric.mod.json');
    const quiltEntry = zip.getEntry('quilt.mod.json');
    const jsonEntry = fabricEntry || quiltEntry;

    if (jsonEntry) {
      const json = JSON.parse(jsonEntry.getData().toString('utf8'));
      if (json.environment === 'client') {
        return true;
      }
    }

    return false;
  } catch (e) {
    console.log(`[⚠️ 解析失败] ${path.basename(jarPath)}`);
    return false;
  }
}

async function ensureBackupDir() {
  try {
    await fs.access(BACKUP_DIR);
  } catch (e) {
    await fs.mkdir(BACKUP_DIR, { recursive: true });
  }
}

async function backupFile(filePath) {
  const fileName = path.basename(filePath);
  const dest = path.join(BACKUP_DIR, fileName);
  await fs.copyFile(filePath, dest);
}

async function scanAndClean() {
  console.log('📁 扫描 MOD 目录:', MODS_DIR);
  console.log('🔍 识别 服务端无效 MOD(仅客户端)...\n');

  let files;
  try {
    files = await fs.readdir(MODS_DIR);
  } catch (e) {
    console.log('❌ mods 目录不存在!');
    rl.close();
    return;
  }

  const jarFiles = files
    .filter(f => f.toLowerCase().endsWith('.jar'))
    .map(f => path.join(MODS_DIR, f));

  const clientOnlyList = [];

  for (const jar of jarFiles) {
    const name = path.basename(jar);
    const clientOnly = await isClientOnlyMod(jar);
    console.log(`${clientOnly ? '❌ 客户端' : '✅ 可服务端'} | ${name}`);
    if (clientOnly) clientOnlyList.push(jar);
  }

  if (clientOnlyList.length === 0) {
    console.log('\n🎉 没有需要删除的服务端无效 MOD');
    rl.close();
    return;
  }

  console.log(`\n⚠️  共找到 ${clientOnlyList.length} 个服务端无效 MOD:`);
  clientOnlyList.forEach(jar => console.log('  -', path.basename(jar)));

  const yes = await confirm('\n🗑️  确定删除并备份吗?(y/N) ');
  if (!yes) {
    console.log('🚫 已取消');
    rl.close();
    return;
  }

  await ensureBackupDir();

  for (const jar of clientOnlyList) {
    await backupFile(jar);
    await fs.unlink(jar);
    console.log('✅ 已删除并备份:', path.basename(jar));
  }

  console.log('\n🎉 清理完成!已备份到 backup_deleted_mods');
  rl.close();
}

scanAndClean().catch(err => {
  console.error('💥 错误:', err);
  rl.close();
});

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions