nsisogf
本篇前言:
在深度优化和扩展KMCCC启动核心前,请您确保已提前阅读:http://www.mcbbs.net/thread-728419-1-3.html
这能保证您对Minecraft的启动流程概念有一定的认知。

在魔改KMCCC前,您必须了解:
KMCCC遵循 GNU Lesser General Public License v3.0 协议
简体中文协议原文:(若协议出现更新或冲突,请以官方最新版本为基础)

根据协议原文第五条:
5 .发布修改过的源码版本
您可以在第4 节的条款下以源码形式发布一个基于本程序的软件,或者从本程序中制作该软件需要进行的修改,只要您同时满足所有以下条件:
* a )制作的软件必须包含明确的通告说明您修改了它,并给出相应的修改日期。
* b )制作的软件必须包含明确的通告,陈述它在本授权下发布并指出任何按照第7 节加入的条件。这条要求修改了第4 节的“保持所有通知完整”的要求。
* c )您必须把整个软件作为一个整体向任何获取副本的人按照本授权协议授权。本授权因此会和任何按照第7 节加入的条款一起,对整个软件及其所有部分,无论是以什么形式打包的,起法律效力。本授权不允许以其他任何形式授权该软件,但如果您个别地收到这样的许可,本授权并不否定该许可。
* d )如果您制作的软件包含交互的用户接口,每个用户接口都必须显示适当的法律通告;但是,如果本程序包含没有显示适当的法律通告的交互接口,您的软件没有必要修改他们让他们显示。
如 果一个覆盖程序和其他本身不是该程序的扩展的程序的联合体,这样的联合的目的不是为了在某个存储或发布媒体上生成更大的程序,且联合体程序和相应产生的版 权没有用来限制程序的使用或限制单个程序赋予的联合程序的用户的合法权利的时候,这样的联合体就被称为“聚集体”。在聚集体中包含覆盖程序并不会使本授权 应用于该聚集体的其他部分。
重点:您,包括我们在内,需要在修改后的KMCCC核心中明确注明您曾进行修改,以及修改日期。尊重他人劳动
在本帖中,由于发布的是零散需要修改区域的代码,就不再进行声明
KMCCC著作权归原作者(们)所有(原帖地址http://www.mcbbs.net/thread-492606-1-1.html

下面我们将开始一步步进行优化

nsisogf
环境声明:
本贴的KMCCC使用的为2017年11月15日最新更新的版本
并不保证KMCCC之后的更新会不会与本优化教程进行冲突。如果没有特殊情况,每次KMCCC的更新我都会同步进行说明和更新
本贴使用的IDE为VisualStudio 2017
请确保您已下载KMCCC最新版本并保存(GITHUB链接:https://github.com/MineStudio/KMCCC

nsisogf
本帖最后由 nsisogf 于 2017-11-24 20:10 编辑

第一章:优化KMCCC的JAVA策略I-
在KMCCC中,获取JAVA的唯一路径只能从注册表中获取一组JAVA路径的列表。并不能区分,标识各个java版本的不同(版本?位数?)
在查询和操作时,也导致了非常大的不便。由此我们第一件事是优化KMCCC的JAVA路径

打开原装KMCCC工程,可以见到有以下项目:

KMCCC.Basic:调用核心示例文件
KMCCC.Pro:添加了更多支持(MOJANG API)的核心调用示例文件
KMCCC.Shared:KMCCC核心本体
KMCCC.Simple:使用KMCCC的简单命令台示例

打开KMCCC.Shared,我们开始对KMCCC的JAVA策略进行优化
打开Tools文件夹,新建一个Java.cs(Java Class),并将JAVA类


将SystemTools.cs中的有关JAVA的静态方法移动至JAVA类中
将方法名FindJava()改为FindJavaPath()
FindJavaInternal(RegistryKey registry)改为FindJavaPathInternal(RegistryKey registry)

完成后如下(截图时未改名,请自行改名):


我们知道,JAVA环境的三要素有:版本,位数,路径。在开发启动器时,我们都会经常使用到。(例如提供给用户选择java版本,根据用户环境判断java位数等...)

那么现在我们为这三要素添加至JAVA类中(非静态,我们需要每一个JAVA的实例)
再JAVA类中添加:
  1. /// <summary>
  2.         /// JAVA路径
  3.         /// </summary>
  4.         public string Path { get; private set; }

  5.         /// <summary>
  6.         /// JAVA版本
  7.         /// </summary>
  8.         public string Version { get; private set; }

  9.         /// <summary>
  10.         /// java位数
  11.         /// </summary>
  12.         public ArchEnum Arch { get; private set; }

  13.         /// <summary>
  14.         /// 位数ENUM
  15.         /// </summary>
  16.         public enum ArchEnum
  17.         {
  18.             x32,
  19.             x64
  20.         }
复制代码

接下来,在这个类中创建构建函数,保证在创建实例时能为每个属性赋值。
  1. public Java(string path, string version, ArchEnum arch)
  2.         {
  3.             this.Path = path;
  4.             this.Version = version;
  5.             this.Arch = arch;
  6.         }
复制代码

因为KMCCC已经提供获取java路径列表的方法,我们就不多进行更改。现在我们需要根据java路径获取java具体信息(单个)
下面代码的实现原理,请详情参考:http://www.mcbbs.net/thread-704797-1-1.html(也可以参考这贴中其他人提供的方法)
  1. /// <summary>
  2.         /// 根据路径获取单个JAVA详细信息
  3.         /// </summary>
  4.         /// <param name="javaPath"></param>
  5.         /// <returns></returns>
  6.         public static Java GetJavaInfo(string javaPath)
  7.         {
  8.             Process p = new Process();
  9.             p.StartInfo.FileName = javaPath;
  10.             p.StartInfo.Arguments = "-version";
  11.             p.StartInfo.RedirectStandardError = true;
  12.             p.StartInfo.UseShellExecute = false;
  13.             p.StartInfo.CreateNoWindow = true;
  14.             p.Start();
  15.             string result = p.StandardError.ReadToEnd();
  16.             string version = result.Replace("java version "", "");
  17.             version = version.Remove(version.IndexOf("""));
  18.             bool is64 = result.Contains("64-Bit");
  19.             Java info;
  20.             if (is64) { info = new Java(javaPath, version, ArchEnum.x64); } else { info = new Java(javaPath, version, ArchEnum.x32); }
  21.             p.Dispose();
  22.             return info;
  23.         }
复制代码




接下来,我们可以创建一个公共方法,对外部提供JAVA实例列表:
  1. /// <summary>
  2.         /// 获取本机所有JAVA列表
  3.         /// </summary>
  4.         /// <returns></returns>
  5.         public static List<Java> GetJavaList()
  6.         {
  7.             var javaPaths = FindJavaPath();
  8.             List<Java> javas = new List<Java>();
  9.             foreach (string item in javaPaths)
  10.             {
  11.                 //判断文件是否存在
  12.                 if (File.Exists(item))
  13.                 {
  14.                     javas.Add(GetJavaInfo(item));
  15.                 }
  16.             }
  17.             return javas;
  18.         }
复制代码


为了方便接下来的外部使用,创建一个公共方法,获取本机最佳JAVA:
  1. /// <summary>
  2.         /// 从本机JAVA中获取最符合此电脑的JAVA版本
  3.         /// </summary>
  4.         /// <param name="javalist">JAVA详细信息集合</param>
  5.         /// <returns>最佳JAVA详细信息</returns>
  6.         public static Java GetSuitableJava()
  7.         {
  8.             List<Java> javalist = GetJavaList();
  9.             List<Java> goodjava = new List<Java>();
  10.             if (SystemTools.GetArch() == "64")
  11.             {
  12.                 foreach (var item in javalist)
  13.                 {
  14.                     if (item.Arch == ArchEnum.x64)
  15.                     {
  16.                         goodjava.Add(item);
  17.                     }
  18.                 }

  19.                 if (goodjava.Count == 0)
  20.                 {
  21.                     goodjava.AddRange(javalist);
  22.                 }
  23.             }
  24.             else
  25.             {
  26.                 goodjava = javalist;
  27.             }
  28.             goodjava = goodjava.OrderByDescending(a => a.Version).ToList();
  29.             return goodjava.FirstOrDefault();
  30.         }
复制代码


下一章我们将修改KMCCC中全部跟JAVA有关的东西



nsisogf
第一章:优化KMCCC的JAVA策略II
上次我们已经成功创建了Java类。但是我们光创建是没用的,我们要将其应用在KMCCC中。
在LaucherCore.cs中,找到LauncherCoreCreationOption这个class
将其中的属性
  1. /// <summary>
  2.                 ///     JAVA地址
  3.                 /// </summary>
  4.                 public string JavaPath { get; internal set; }
复制代码
修改为下面的
  1. /// <summary>
  2.                 /// 使用的JAVA
  3.                 /// </summary>
  4.                 public Java Java { get; internal set; }
复制代码
这样,我们在创建核心的时候,就可以传递一个JAVA实例参数了。核心也有更高的拓展性了

修改完后,追踪程序的报错(红色波浪线),就可以方便的找到另一个修改的位置
第一个报错的在这个方法力LauncherCoreCreationOption的构造函数中(背景色黄色):
  1. /// <summary>
  2.                 ///     核心选项
  3.                 /// </summary>
  4.                 /// <param name="gameRootPath">游戏根目录,默认为 ./.minecraft </param>
  5.                 /// <param name="javaPath">JAVA地址,默认为自动搜寻所的第一个</param>
  6.                 /// <param name="versionLocator">Version定位器,默认为 JVersionLoacator</param>
  7.                 public LauncherCoreCreationOption(string gameRootPath = null, string javaPath = null, IVersionLocator versionLocator = null)
  8.                 {
  9.                         GameRootPath = new DirectoryInfo(gameRootPath ?? ".minecraft").FullName;
  10. <span style="background-color: yellow;">                        JavaPath</span> = <span style="background-color: yellow;">javaPath</span> ?? <span style="background-color: yellow;">SystemTools.FindJava().FirstOrDefault();</span>
  11.                         VersionLocator = versionLocator ?? new JVersionLocator();
  12.                         if (!Directory.Exists(GameRootPath))
  13.                         {
  14.                                 Directory.CreateDirectory(GameRootPath);
  15.                         }
  16.                 }
复制代码
第一个报错是因为这个类中不再存在JavaPath这个属性,我们已经将属性JavaPath改名为Java,同理,这里的JavaPath改为Java
第二个报错是因为传递过来的javaPath参数为字符串。将参数中的string javaPath改为Java java即可
第三个报错是因为systemTools中不再存在关于获取Java的方法。我们已经将其转移到Java这个Class中
如果这时候你观察原版的代码,你会发现:原版KMCCC真的简单粗暴,如果你没有指定java路径的话,他直接选择List<string>Java路径中的第一位
这时候我们前面做的GetSuitableJava()方法就起到作用了。他能自动选择这台电脑最匹配的Java实例:
  1. Java = java ?? Java.GetSuitableJava();
复制代码

到最后全部修改完的效果是这样的:
  1. /// <summary>
  2.                 ///     核心选项
  3.                 /// </summary>
  4.                 /// <param name="gameRootPath">游戏根目录,默认为 ./.minecraft </param>
  5.                 /// <param name="java">JAVA实例,自动搜索最佳匹配的java</param>
  6.                 /// <param name="versionLocator">Version定位器,默认为 JVersionLoacator</param>
  7.                 public LauncherCoreCreationOption(string gameRootPath = null, Java java = null, IVersionLocator versionLocator = null)
  8.                 {
  9.                         GameRootPath = new DirectoryInfo(gameRootPath ?? ".minecraft").FullName;
  10. <span style="background-color: yellowgreen;">                        Java = java ?? Java.GetSuitableJava()</span>;
  11.                         VersionLocator = versionLocator ?? new JVersionLocator();
  12.                         if (!Directory.Exists(GameRootPath))
  13.                         {
  14.                                 Directory.CreateDirectory(GameRootPath);
  15.                         }
  16.                 }
复制代码


追踪到下一个错误点,是这里:
  1. [Obsolete]
  2.                 public static LauncherCoreCreationOption Create(string gameRootPath = null, string javaPath = null, IVersionLocator versionLocator = null)
  3.                 {
  4.                         return new LauncherCoreCreationOption(gameRootPath, <span style="background-color: yellow;">javaPath</span>, versionLocator);
  5.                 }
复制代码
老样子,javaPath参数为字符串,不匹配这个方法。修改为Java类型的即可
  1. [Obsolete]
  2.                 public static LauncherCoreCreationOption Create(string gameRootPath = null, <span style="background-color: yellowgreen;">Java java = null</span>, IVersionLocator versionLocator = null)
  3.                 {
  4.                         return new LauncherCoreCreationOption(gameRootPath, java, versionLocator);
  5.                 }
复制代码


继续追踪下一个错误点:
  1. /// <summary>
  2.                 ///     从CreationOption创建启动器核心
  3.                 /// </summary>
  4.                 /// <param name="option">启动器创建选项</param>
  5.                 /// <returns>创建的启动器核心</returns>
  6.                 public static LauncherCore Create(LauncherCoreCreationOption option)
  7.                 {
  8.                         var launcherCore = new LauncherCore
  9.                         {
  10.                                 GameRootPath = option.GameRootPath,
  11.                                 JavaPath = <span style="background-color: yellow;">option.JavaPath</span>,
  12.                                 VersionLocator = option.VersionLocator
  13.                         };
  14.                         return launcherCore;
  15.                 }
复制代码
这个错误是因为:1.这个类的JavaPath为字符串类型,而option.Java是Java类型。2.option.JavaPath已经被我们改名了。
先找到LauncherCore中的JavaPath属性
  1. /// <summary>
  2.                 ///     JAVA目录
  3.                 /// </summary>
  4.                 public string JavaPath { get; set; }
复制代码
修改为Java类型:
  1. /// <summary>
  2.                 ///     JAVA
  3.                 /// </summary>
  4.                 public Java Java { get; set; }
复制代码


接下来回到Create(LauncherCoreCreationOption option)方法,修改:
  1. /// <summary>
  2.                 ///     从CreationOption创建启动器核心
  3.                 /// </summary>
  4.                 /// <param name="option">启动器创建选项</param>
  5.                 /// <returns>创建的启动器核心</returns>
  6.                 public static LauncherCore Create(LauncherCoreCreationOption option)
  7.                 {
  8.                         var launcherCore = new LauncherCore
  9.                         {
  10.                                 GameRootPath = option.GameRootPath,
  11. <span style="background-color: yellowgreen;">                                Java = option.Java</span>,
  12.                                 VersionLocator = option.VersionLocator
  13.                         };
  14.                         return launcherCore;
  15.                 }
复制代码


此时,当前的LauncherCore.cs中就没有显示其他错误了。但是当你尝试编译的时候,会发现另一个类:LauncherCoreInternal.cs也会开始报错
追踪到第一个错误点:
  1. internal LaunchResult LaunchInternal(LaunchOptions options, params Action<MinecraftLaunchArguments>[] argumentsOperators)
  2.                 {
  3.                         lock (Locker)
  4.                         {
  5.                                 if (!File.Exists(<span style="background-color: yellow;">JavaPath</span>))
  6.                                 {
  7.                                         return new LaunchResult {Success = false, ErrorType = ErrorType.NoJAVA, ErrorMessage = "指定的JAVA位置不存在"};
  8.                                 }
  9.                                 CurrentCode = Random.Next();
  10.                                 var args = new MinecraftLaunchArguments();
  11.                                 var result = GenerateArguments(options, ref args);
  12.                                 if (result != null)
  13.                                 {
  14.                                         return result;
  15.                                 }
  16.                                 if (argumentsOperators == null) return LaunchGame(args);
  17.                                 foreach (var opt in argumentsOperators)
  18.                                 {
  19.                                         try
  20.                                         {
  21.                                                 if (opt != null)
  22.                                                 {
  23.                                                         opt(args);
  24.                                                 }
  25.                                         }
  26.                                         catch (Exception exp)
  27.                                         {
  28.                                                 return new LaunchResult {Success = false, ErrorType = ErrorType.OperatorException, ErrorMessage = "指定的操作器引发了异常", Exception = exp};
  29.                                         }
  30.                                 }
  31.                                 return LaunchGame(args);
  32.                         }
  33.                 }
复制代码
原因是我们改过名字。改为Java.Path即可:
  1. internal LaunchResult LaunchInternal(LaunchOptions options, params Action<MinecraftLaunchArguments>[] argumentsOperators)
  2.                 {
  3.                         lock (Locker)
  4.                         {
  5.                                 if (!File.Exists(<span style="background-color: yellowgreen;">Java.Path</span>))
  6.                                 {
  7.                                         return new LaunchResult {Success = false, ErrorType = ErrorType.NoJAVA, ErrorMessage = "指定的JAVA位置不存在"};
  8.                                 }
  9.                                 CurrentCode = Random.Next();
  10.                                 var args = new MinecraftLaunchArguments();
  11.                                 var result = GenerateArguments(options, ref args);
  12.                                 if (result != null)
  13.                                 {
  14.                                         return result;
  15.                                 }
  16.                                 if (argumentsOperators == null) return LaunchGame(args);
  17.                                 foreach (var opt in argumentsOperators)
  18.                                 {
  19.                                         try
  20.                                         {
  21.                                                 if (opt != null)
  22.                                                 {
  23.                                                         opt(args);
  24.                                                 }
  25.                                         }
  26.                                         catch (Exception exp)
  27.                                         {
  28.                                                 return new LaunchResult {Success = false, ErrorType = ErrorType.OperatorException, ErrorMessage = "指定的操作器引发了异常", Exception = exp};
  29.                                         }
  30.                                 }
  31.                                 return LaunchGame(args);
  32.                         }
  33.                 }
复制代码


继续追踪下一个。。。:
  1. private LaunchResult LaunchGame(MinecraftLaunchArguments args)
  2.                 {
  3.                         try
  4.                         {
  5.                                 var handle = new LaunchHandle(args.Authentication)
  6.                                 {
  7.                                         Code = CurrentCode,
  8.                                         Core = this,
  9.                                         Arguments = args,
  10.                                         Process = Process.Start(new ProcessStartInfo(<span style="background-color: yellow;">JavaPath</span>)
  11.                                         {
  12.                                                 Arguments = args.ToArguments(),
  13.                                                 UseShellExecute = false,
  14.                                                 WorkingDirectory = GameRootPath,
  15.                                                 RedirectStandardError = true,
  16.                                                 RedirectStandardOutput = true
  17.                                         })
  18.                                 };
  19.                                 handle.Work();
  20.                                 Task.Factory.StartNew(handle.Process.WaitForExit).ContinueWith(t => Exit(handle, handle.Process.ExitCode));
  21.                                 return new LaunchResult {Success = true, Handle = handle};
  22.                         }
  23.                         catch (Exception exp)
  24.                         {
  25.                                 return new LaunchResult {Success = false, ErrorType = ErrorType.Unknown, ErrorMessage = "启动时出现了异常", Exception = exp};
  26.                         }
  27.                 }
复制代码
也是直接改为Java.Path,我就不再发修改后的了

最后,在Reporter.cs中,最后一个错误在等待着我们
  1. public class BasicLaunchReport : MinLaunchReport
  2.                 {
  3.                         #region 目录/位置

  4.                         public string JavaPath;

  5.                         #endregion

  6.                         public BasicLaunchReport(LauncherCore core, LaunchResult result, LaunchOptions options) : base(result, options)
  7.                         {
  8.                                 #region 系统信息

  9.                                 VideoCardInfo = GetVideoCardInfo();
  10.                                 ProcessorInfo = GetProcessorInfo();

  11.                                 #endregion

  12.                                 #region 启动信息

  13.                                 JavaPath = <span style="background-color: yellow;">core.JavaPath</span>;

  14.                                 #endregion
  15.                         }

  16.                         #region 系统信息

  17.                         public string ProcessorInfo;
  18.                         public string VideoCardInfo;

  19.                         #endregion
  20.                 }
复制代码
也是改为Java.Path。不再赘述

这时候,我们就全部修改成功了。
下一章我们将给KMCCC添加获取最佳内存大小方法。

nsisogf
拓展阅读:我们获得JAVA详细信息的原理
并不像其他人那样,选择使用注册表来获取JAVA的版本名称和JAVA位数
相反,我们采用了使用-version参数来启动JAVA(在已有路径的前提下),截取信息流关键字眼的方式来获取JAVA的详细信息
在介绍具体方法前,我们先简单概括两者特点,由用户自主选择最佳的方法

注册表键法:
通过读取表键的名称,以最快的方式获取JAVA的路径以及版本。至于位数,则要深入检查Wow6432
这种方法在某种意义上来说,是最方便的。因为JAVA安装后的确会在注册表中留下详细信息。但在位数检测上,就显得比较繁琐。但要搜索路径的话,这种方法是不可避免的

使用-version启动JAVA:
在获取到路径的前提下,这个方法是最具有扩展性,获取JAVA信息最全,可靠的方式。在写出的数据流中,会包含大量有用的信息。但确定就是效率太低,但完全可以用它的弹性优势抵消。


以下是我计算机上的JAVA Version信息
如果您想体验,请在已装载官方JAVA的Windows计算机中使j用win+R,输入cmd,后输入java -version。输出的信息即JAVA详细信息
  1. java version "1.8.0_144"
  2. Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
  3. Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
复制代码
第一行java version,既JAVA版本,不再解释
第二行中的SE代表所装JAVA为三个系列(SE/EE/ME)中的SE(详情https://www.cnblogs.com/EasonJim/p/6181981.html
Runtime Environment代表运行时环境,还有一个Development Kit(既JRE和JDK,详情:https://zhidao.baidu.com/question/55791862.html
扩号内build代表构建版本号
第三号:Hotspot为JVM名称详情(http://xiaomogui.iteye.com/blog/857821
64-Bit为64位独有标志,用这个可以判断JAVA位数
其他的对我们没什么用处,就不分析了


nsisogf
第一章:KMCCC获取启动游戏最佳内存大小IIIKMCCC systemTools.cs中已经自带获取系统剩余内存以及全部内存的方法
GetTotalMemory() GetRunmemory()
但是在启动时我们需要获取最佳内存大小,但又受限于JAVA位数已经系统剩余内存大小。所以我们创建一个获取最佳内存大小的方法:
  1. /// <summary>
  2.         /// 获取启动游戏的最佳内存大小
  3.         /// </summary>
  4.         /// <param name="java">JAVA</param>
  5.         /// <returns>最佳启动内存</returns>
  6.         public static int GetSuitableLaunchMemory(Java java)
  7.         {
  8.             int rm = Convert.ToInt32(Math.Floor(GetRunmemory() * 0.6));
  9.             switch (java.Arch)
  10.             {
  11.                 case Java.ArchEnum.x32:
  12.                     if (rm > 1024) { return 1024; }
  13.                     else { return rm; }

  14.                 case Java.ArchEnum.x64:
  15.                     if (rm > 4096) { return 4096; }
  16.                     else { return rm; }

  17.                 default:
  18.                     return rm;
  19.             }
  20.         }
复制代码
我们先通过判断JAVA位数,来确定最大内存限制
在这里我需要声明下,32位JAVA我把上限调为1024MB是因为32位JAVA太奇葩了。。不同系统上的上限内存都不同。。1024一般是最安全的
确定上限后,这个方**返回系统剩余内存的0.6倍的Int32整数(保证有其他空闲内存)

nsisogf
第二章:扩展KMCCC的NET(网络操作)库
我们知道,实际的优秀启动器会包含版本下载等需要网络操作的功能
虽然KMCCC是启动类库,但个人喜爱和出于方便的原因,我硬生生的给她写了了NET库

先在KMCCC中添加一个Net文件夹,作为Net类的namespace


先从最基本网络操作类开始。就是GET/POST
在Net文件夹中添加一个类,命名为HttpDownload(当然这个名字你自己可以换其他的)

在类中写入方法
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;

namespace KMCCC.Net
{
    public static class HttpDownload
    {
        /// <summary>
        /// Http发送Get请求方法
        /// </summary>
        /// <param name="Url">GET网址</param>
        /// <param name="postDataStr">GET传递数据</param>
        /// <returns>网站响应</returns>
        public static string FromHttpGet(string Url, string postDataStr)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url + (postDataStr == "" ? "" : "?") + postDataStr);
            return WebGet(request);
        }

        /// <summary>  
        /// 指定Post地址使用Get 方式获取全部字符串  
        /// </summary>  
        /// <param name="url">请求后台地址</param>  
        /// <param name="content">Post提交数据内容(utf-8编码的)</param>  
        /// <returns></returns>  
        public static string FromHttpPost(string url, string content)
        {
            string result = string.Empty;
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
            req.Method = "POST";
            req.ContentType = "application/x-www-form-urlencoded";

            #region 添加Post 参数  
            byte[] data = Encoding.UTF8.GetBytes(content);
            req.ContentLength = data.Length;
            using (Stream reqStream = req.GetRequestStream())
            {
                reqStream.Write(data, 0, data.Length);
                reqStream.Close();
            }
            #endregion

            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
            Stream stream = resp.GetResponseStream();
            //获取响应内容  
            using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
            {
                result = reader.ReadToEnd();
            }
            return result;
        }

        /// <summary>
        /// Http发送Get请求方法
        /// </summary>
        /// <param name="Url">GET网址</param>
        /// <returns>网站响应</returns>
        public static string FromHttpGet(string Url)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
            return WebGet(request);
        }

        private static string WebGet(HttpWebRequest request)
        {
            request.Method = "GET";
            request.ContentType = "application/x-www-form-urlencoded";

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Stream myResponseStream = response.GetResponseStream();
            StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.UTF8);
            string retString = myStreamReader.ReadToEnd();
            myStreamReader.Close();
            myResponseStream.Close();
            return retString;
        }
    }
}


这时候,KMCCC已经能做到基本的POST/Get数据操作

Oleg
nsisogf 发表于 2018-2-21 16:55
第二章:扩展KMCCC的NET(网络操作)库
我们知道,实际的优秀启动器会包含版本下载等需要网络操作的功能
虽 ...

如果你打算扩展这个库的功能,为什么不把它放到 GitHub 上面?
不过网络请求辅助这种功能不算 Kmccc 的核心功能。我不知道原先类库的作者会不会同意。

nsisogf
Oleg 发表于 2018-2-21 17:15
如果你打算扩展这个库的功能,为什么不把它放到 GitHub 上面?
不过网络请求辅助这种功能不算 Kmccc 的核 ...

我的启动器发布的同时我会把我修改的KMCCC放到Github上的(根据协议是这样要求的)
新版本的KMCCC.Pro中已经自带了MOJANG API功能,而且她们也明确说了会发展Net部分

nidb
表示KMCCC貌似无法启动1.12.2

nsisogf
nidb 发表于 2018-2-23 10:16
表示KMCCC貌似无法启动1.12.2

我这里正常。
有问题的话请去kmccc官方贴回复 = =这里是非官方的扩展贴

Oleg
nsisogf 发表于 2018-2-21 16:55
第二章:扩展KMCCC的NET(网络操作)库
我们知道,实际的优秀启动器会包含版本下载等需要网络操作的功能
虽 ...

你注意到没有,有个流 double free 了。

nsisogf
Oleg 发表于 2018-2-28 16:44
你注意到没有,有个流 double free 了。

神tm double free
首先这里是C#,指针操作对于C#不安全。
第二这里的方法都是我自己经过多次测试,在之前的启动器发布版本中用的都是这个方法,都没有出现异常。

Oleg
本帖最后由 Oleg 于 2018-3-1 13:52 编辑
nsisogf 发表于 2018-2-28 23:29
神tm double free
首先这里是C#,指针操作对于C#不安全。
第二这里的方法都是我自己经过多次测试,在之前 ...

using 会调用 Dispose。你写那个 Close 在 Win32 已经是对 Socket 调用过 CloseHandle 了,在 Dispose 里面发现句柄已经关闭就不会进行有价值的操作。
那个方法返回的流是 ConnectStream。ConnectStream 嵌套了 NetworkStream。你看看 NetworkSteam 源码:
NetworkStream.cs
是不是会在 Dispose 里面对套接字调用 Close 达到关闭流的效果 ?
所以,

  1.             using (Stream reqStream = req.GetRequestStream())
  2.             {
  3.                 reqStream.Write(data, 0, data.Length);
  4.                 reqStream.Close();
  5.             }
复制代码
应该改成
  1. using (Stream reqStream = req.GetRequestStream())
  2. {
  3. reqStream.Write(data, 0, data.Length);
  4. }
复制代码




nsisogf
Oleg 发表于 2018-3-1 13:36
using 会调用 Dispose。你写那个 Close 在 Win32 已经是对 Socket 调用过 CloseHandle 了,在 Dispose 里 ...

不要乱说话。
Dispose和Close完全不一样。
没错,Dispose的确会执行Close的大部分方法
但是一个是基于对象销毁,一个是关闭资源。这个就是关于CLR托管,非托管的资源操作
我用Close是方便将来对此方法的升级,我并不想他销毁(避免资源占用)
另外,这并不会占用更多资源。你所谓的进行无价值操作,并不费时。详情参考Dispose过程。我先前的Close并不会影响整体效率。你也可以自己测试。
所以说,Close在此没有任何问题,不会出现任何BUG,也不会额外占用资源耗时
--这不叫double free

nsisogf
提示:
开启“只看该作者”选项方便浏览