咕咕咕~~~
问Fabric服务端创建的原理


Fabric-Installer源码地址:https://github.com/FabricMC/fabric-installer
可惜我只学过C++和Python,看不懂
求大佬帮忙看一下代码里是怎么创建的Fabric服务端

PercyDan
本帖最后由 PercyDan 于 2021-3-7 11:23 编辑

https://github.com/FabricMC/fabr ... erverInstaller.java

  1. public class ServerInstaller {
  2.         private static final String servicesDir = "META-INF/services/";

  3.         public static void install(File dir, String loaderVersion, String gameVersion, InstallerProgress progress) throws IOException {
  4.                 progress.updateProgress(new MessageFormat(Utils.BUNDLE.getString("progress.installing.server")).format(new Object[]{String.format("%s(%s)", loaderVersion, gameVersion)}));
  5.                 File libsDir = new File(Utils.findDefaultUserDir(), ".cache" + File.separator + "fabric-installer" + File.separator + "libraries"); // 检查依赖文件夹
  6.                 if (!libsDir.exists()) { //没有则创建
  7.                         if (!libsDir.mkdirs()) {
  8.                                 throw new IOException("Could not create " + libsDir.getAbsolutePath() + "!");
  9.                         }
  10.                 }
  11.                 if(!dir.exists()){
  12.                         if (!dir.mkdirs()) {
  13.                                 throw new IOException("Could not create " + dir.getAbsolutePath() + "!");
  14.                         }
  15.                 }

  16.                 progress.updateProgress(Utils.BUNDLE.getString("progress.download.libraries"));

  17.                 URL profileUrl = new URL(Reference.getMetaServerEndpoint(String.format("v2/versions/loader/%s/%s/server/json", gameVersion, loaderVersion))); //获取依赖信息
  18.                 Json json = Json.read(Utils.readTextFile(profileUrl));

  19.                 List<File> libraryFiles = new ArrayList<>();

  20.                 for (Json libraryJson : json.at("libraries").asJsonList()) {
  21.                         Library library = new Library(libraryJson); //遍历并下载依赖

  22.                         progress.updateProgress(new MessageFormat(Utils.BUNDLE.getString("progress.download.library.entry")).format(new Object[]{library.name}));
  23.                         File libraryFile = new File(libsDir, library.getFileName());
  24.                         Utils.downloadFile(new URL(library.getURL()), libraryFile.toPath());
  25.                         libraryFiles.add(libraryFile);
  26.                 }

  27.                 progress.updateProgress(Utils.BUNDLE.getString("progress.generating.launch.jar"));
  28.                
  29.                 //创建Jar
  30.                 File launchJar = new File(dir, "fabric-server-launch.jar");
  31.                 String mainClass = json.at("mainClass").asString();
  32.                 makeLaunchJar(launchJar, mainClass, libraryFiles, progress);

  33.                 progress.updateProgress(new MessageFormat(Utils.BUNDLE.getString("progress.done.start.server")).format(new Object[]{launchJar.getName()}));
  34.         }

  35.         private static void makeLaunchJar(File file, String mainclass, List<File> libraryFiles, InstallerProgress progress) throws IOException {
  36.                 if (file.exists()) {
  37.                         if (!file.delete()) {
  38.                                 throw new IOException("Could not delete file: " + file.getAbsolutePath());
  39.                         }
  40.                 }

  41.                 FileOutputStream outputStream = new FileOutputStream(file);
  42.                 ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);

  43.                 Set<String> addedEntries = new HashSet<>();

  44.                 {
  45.                         //写Manifest
  46.                         addedEntries.add("META-INF/MANIFEST.MF");
  47.                         zipOutputStream.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF"));

  48.                         Manifest manifest = new Manifest();
  49.                         manifest.getMainAttributes().put(new Attributes.Name("Manifest-Version"), "1.0");
  50.                         manifest.getMainAttributes().put(new Attributes.Name("Main-Class"), "net.fabricmc.loader.launch.server.FabricServerLauncher");
  51.                         manifest.write(zipOutputStream);

  52.                         zipOutputStream.closeEntry();

  53.                         addedEntries.add("fabric-server-launch.properties");
  54.                         zipOutputStream.putNextEntry(new ZipEntry("fabric-server-launch.properties"));
  55.                         zipOutputStream.write(("launch.mainClass=" + mainclass + "\n").getBytes(StandardCharsets.UTF_8));
  56.                         zipOutputStream.closeEntry();

  57.                         Map<String, Set<String>> services = new HashMap<>();
  58.                         byte[] buffer = new byte[32768];
  59.                         
  60.                         //遍历并打包依赖
  61.                         for (File f : libraryFiles) {
  62.                                 progress.updateProgress(new MessageFormat(Utils.BUNDLE.getString("progress.generating.launch.jar.library")).format(new Object[]{f.getName()}));

  63.                                 // read service definitions (merging them), copy other files
  64.                                 try (
  65.                                                 FileInputStream is = new FileInputStream(f);
  66.                                                 JarInputStream jis = new JarInputStream(is)
  67.                                 ) {
  68.                                         JarEntry entry;
  69.                                         while ((entry = jis.getNextJarEntry()) != null) {
  70.                                                 if (entry.isDirectory()) continue;

  71.                                                 String name = entry.getName();

  72.                                                 if (name.startsWith(servicesDir) && name.indexOf('/', servicesDir.length()) < 0) { // service definition file
  73.                                                         parseServiceDefinition(name, jis, services);
  74.                                                 } else if (!addedEntries.add(name)) {
  75.                                                         System.out.printf("duplicate file: %s%n", name);
  76.                                                 } else {
  77.                                                         JarEntry newEntry = new JarEntry(name);
  78.                                                         zipOutputStream.putNextEntry(newEntry);

  79.                                                         int r;
  80.                                                         while ((r = jis.read(buffer, 0, buffer.length)) >= 0) {
  81.                                                                 zipOutputStream.write(buffer, 0, r);
  82.                                                         }

  83.                                                         zipOutputStream.closeEntry();
  84.                                                 }
  85.                                         }
  86.                                 }
  87.                         }

  88.                         // write service definitions
  89.                         for (Map.Entry<String, Set<String>> entry : services.entrySet()) {
  90.                                 JarEntry newEntry = new JarEntry(entry.getKey());
  91.                                 zipOutputStream.putNextEntry(newEntry);

  92.                                 writeServiceDefinition(entry.getValue(), zipOutputStream);

  93.                                 zipOutputStream.closeEntry();
  94.                         }
  95.                 }

  96.                 zipOutputStream.close();
  97.                 outputStream.close();
  98.         }

  99.         private static void parseServiceDefinition(String name, InputStream rawIs, Map<String, Set<String>> services) throws IOException {
  100.                 Collection<String> out = null;
  101.                 BufferedReader reader = new BufferedReader(new InputStreamReader(rawIs, StandardCharsets.UTF_8));
  102.                 String line;

  103.                 while ((line = reader.readLine()) != null) {
  104.                         int pos = line.indexOf('#');
  105.                         if (pos >= 0) line = line.substring(0, pos);
  106.                         line = line.trim();

  107.                         if (!line.isEmpty()) {
  108.                                 if (out == null) out = services.computeIfAbsent(name, ignore -> new LinkedHashSet<>());

  109.                                 out.add(line);
  110.                         }
  111.                 }
  112.         }

  113.         private static void writeServiceDefinition(Collection<String> defs, OutputStream os) throws IOException {
  114.                 BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8));

  115.                 for (String def : defs) {
  116.                         writer.write(def);
  117.                         writer.write('\n');
  118.                 }

  119.                 writer.flush();
  120.         }
  121. }
复制代码

xmdhs
如果你只是要安装的话,可以看看这里用命令行的方式来安装
https://fabricmc.net/wiki/install#cli_installation

如果说原理的话,无非就是下载依赖,和打包 jar 而已。