本章教会大家如何解压Natives文件
ps:本章仅仅适用于1.19-pre1版本及以下。
首先哈,在部分的版本json文件中,我们总能看到有这么一种影子:libraries->downloads->classifiers键,这个键所指的是什么意思呢?其实,这个关系到一个很重要的概念:Natives本地库的解压。
在上一期,我已经说过了,需要大家在别的启动器中不仅下载过一次游戏,而且需要启动过一次后,才可以正常在本启动器中启动。
那么这到底是为什么呢?本章就要来为大家探讨这个问题。
首先,我们众所周知,MC在启动的时候,会在versions/
然后嘛,用过官启的小伙伴们,肯定都应该知道,官启是将Natives本地库解压到【C:\Users<用户名>\AppData\Local\Temp】路径下的,也就意味着电脑重启一次,该本地库将会被自动删除。
但我们这么想,几乎所有的第三方启动器,都是将Natives本地库解压到【versions<versionName><版本名>-natives】库下。
而我们的启动器已经设置过了,自动检测versionname下面出现的第一个有natives关键字的程序。因此,直接解压就好了捏!
请看代码:【依旧存放在Launcher类下,由UnzipNative方法名断定。】
function Launcher.UnzipNatives(json: TJsonObject; path, relpath: String): Boolean;
begin //其实,乍一看这个方法,居然与前面的GetCPLibraries函数近乎一致,甚至连参数都是一样的。只是这个返回值变成了Boolean值罢了。
result := false; //初始返回值为false
var sb := TStringBuilder.Create; //定义好几个初始变量,与GetCPLibraries一致。
var Yuan := TStringList.Create;
var LibNo := TStringList.Create;
var NoRe := TStringList.Create;
var ReTemp := TStringList.Create;
var vername := ExtractFileName(relpath); //获取版本文件夹下的版本名字。
if path.LastIndexOf('\\') <> path.Length - 1 then path := Concat(path, '\\');
try //解析Json
var Jr := json.GetValue('libraries') as TJsonArray; //获取libraries中的内容
for var I in Jr do //添加元素,并将name转换成path加入进yuan数组
begin
try //找不同游戏,本次找不同你的对手是:Mojang!【又来一次找不同啊!】
var Jr1 := I as TJsonObject;
var pdd := true;
try
var rl := Jr1.GetValue('rules') as TJsonArray; //获取某一个元素的rulWe值。
for var J in rl do begin //下面开始判断rule值里面的action的os是否支持windows
var r1 := J as TJsonObject;
var an := r1.GetValue('action').Value;
if an = 'allow' then begin
var r2 := r1.GetValue('os') as TJsonObject;
var r3 := r2.GetValue('name').Value;
if r3 <> 'windows' then begin pdd := false; end; //如果支持windows,则pdd为true,反之则为false
end else if an = 'disallow' then begin
var r2 := r1.GetValue('os') as TJsonObject;
var r3 := r2.GetValue('name').Value;
if r3 = 'windows' then begin pdd := false; end;
end;
end;
except end;
var arch := IfThen(IsX64, '64', '32'); //直接开始查询natives,如果有x64之类的替换,则替换即可。这里是用于适配1.7.10中的json文件。
var Jr2 := Jr1.GetValue('name').Value; //查询name值。
var Jr3 := Jr1.GetValue('natives') as TJsonObject; //查询natives值。
var Jr4 := Jr3.GetValue('windows').Value.Replace('${arch}', arch); //如果遇到了有arch需要替换的键,则替换即可。
if pdd then Yuan.Add(Concat(Jr2, ':', Jr4)); //直接开始加入Yuan列表中。
except
continue; //如果出现了报错,则跳过。
end;
end;//去除重复
for var N in Yuan do
if LibNo.IndexOf(N) = -1 then // 去除重复
LibNo.Add(N);
for var G in libNo do begin //去除版本较低的那个,以下为去除不必要的重复
var KN := G.Replace('.', '').Replace(':', '').Replace('-', '').Replace('/', '');
var KW := ExtractNumber(KN, false); //摘取字符
var KM := ExtractNumber(KN, true).Substring(0, 9); //摘取数字
strtoint(KM);
if ReTemp.IndexOf(KW) = -1 then begin //判断是否
ReTemp.Add(KW);
NoRe.Add(G);
end else if strtoint(ExtractNumber(NoRe[ReTemp.IndexOf(KW)], true).Substring(0, 9)) <= strtoint(KM) then begin
NoRe.Delete(ReTemp.IndexOf(KW));
NoRe.Insert(ReTemp.IndexOf(KW), G); // 添加新元素
end;
end;
if not DirectoryExists(getMCRealDir(relpath, 'natives')) then //如果不存在natives文件夹的话,以下用了双右划号的同上几章一样处理。
begin //此处才开始认真起来,前面大部分都是仿照GetCPLibraries的。
ForceDirectories(Concat(relpath, '\\', vername, '-natives')); //创建一个新的文件夹。
if NoRe.Count = 0 then //如果查询出的文件为0,则创建一个新文件且不解压。
begin //直接返回true,并退出该函数。
result := true;
exit;
end;
for var C in Nore do //如果不为空,则开始循环判断并且解压,如果解压不成功,则返回空。
begin
if not UnzipMethod(Concat(path, 'libraries\\', ConvertNameToPath(C).Replace('/', '\\')), Concat(relpath, '\\', vername, '-natives')) then begin
continue;
end;
end; //如果以上不仅检测出来,还解压全部成功,则判断version文件夹中是否有natives文件,如果有,则删除除了dll文件以外的所有文件。
DeleteRetain(getMCRealDir(relpath, 'natives'), '.dll');
if not DirectoryExists(getMCRealDir(relpath, 'natives')) then begin
ForceDirectories(Concat(relpath, '\\', vername, '-LLL-natives'));
result := true;
abort;
end;//如果任何一项不完成,则返回值设为false。
end;
result := true;
finally
sb.Free; //给所有free掉
Yuan.Free;
libNo.Free;
NoRe.Free;
ReTemp.Free;
end;
end;
好了,以上就是解压Natives了!自己一看,还真的与GetCPLibraries差不多一致呢! 这个函数是在Launcher类下的,因为我们只需要用其解压Natives参数,别的时候都用不着。
上面我们使用了一个三元运算符的Delphi内置函数,叫做IfThen,这个函数可以引用下列单元文件可以使用:
uses
StrUtils;
然后,还有,此处我们需要判断如果使用的Windows电脑到底是32位的,还是64位的。我们需要做的是新建一个IsX64函数。 函数如下:
function isX64: Boolean;
var
si: SYSTEM_INFO;
begin //将上方变量获取系统本地信息。
GetNativeSystemInfo(&si); // = 9 表示的是 AMD64
if(si.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64 {9}) or
(si.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64) then //如果是AMD64或者是IA64,则返回True,否则返回false。
Result := True
else
Result := False;
end;
然后捏,这个函数我们由于与GetOutsideDocument差不多一致,因此,我们需要在implementation上方写上这串代码的声明哦!我们就无需在Launcher类里面声明了。
再者,我们还需要写两个函数,第一个函数是:UnzipMethod,这个函数用于传入两个参数,第一个是需要解压的zip或者别的后缀的文件,第二个参数是解压的路径【第一个参数是具体的文件,第二个参数是一个文件夹路径】
实现如下:
function UnzipMethod(zippath, forderpath: String): Boolean;
begin
result := false;
if DirectoryExists(forderpath) then ForceDirectories(forderpath);
if not FileExists(zippath) then exit;
var zp := TZipFile.Create;
try
zp.Open(zippath, zmRead); //打开压缩包
zp.ExtractAll(forderpath); //解压压缩包
result := true;
finally
zp.Free;
end;
end;
在使用这个函数解压的时候,我们需要引用一个单元文件:
uses
Zip;
这个函数写好之后,下一个函数就是DeleteRetain函数,这个函数也是填入两个参数【说实话,这个函数其实没有必要的,但是又其实有点小必要吧。】【第一个参数:要删掉的总文件夹路径,第二个参数:要保留的文件后缀。】
其实,说实话,这个函数很危险,稍有不慎【例如删掉的总文件夹路径填为空】,可能会引发格式化磁盘的风险。
但我还是得和玩家说说啦:
function DeleteRetain(N, suffix: String): Boolean;
var
F: TSearchRec;
begin
result := false;
if N = '' then exit;
if (suffix = '') or (suffix = '.') then exit;
if N.IndexOf('\\') = -1 then exit;
if FindFirst(Concat(N, '\*.*'), faAnyFile, F) = 0 then begin //查找文件并赋值
try
repeat //此处调用了API函数。
var S: String := F.Name;
if (F.Attr and faDirectory) > 0 then //查找是否为文件夹,如果是则执行
begin
if (S <> '.') and (S <> '..') then //删除首次寻找文件时出现的【.】和【..】字符。
DeleteRetain(Concat(N, '\\', S), suffix) //重复调用本函数,并且加上文件名。
end
else
if S.Substring(S.LastIndexOf('.', S.Length)) <> suffix then DeleteFile(N + '\\' + F.Name); //如果没发现后缀为suffix的话,则执行。
until FindNext(F) <> 0; //查询下一个。
finally
FindClose(F); //关闭文件查询。
end;
RemoveDir(N);
result := true;
end;
end;
这个函数,我们就不写在Launcher类下了,与上面两个函数一致,我们在将来程序运用中将经常运用到这个函数。我们只需要在implementation上写这个就好了。
好了,在敲完上述代码之后,我们只需要应用就好了!,我们只需要在Button1Click执行一次这个即可啦!
请看代码:
var res := lch.SelectParam(root); //成功获取到拼接完成后的json文件。
//在这两行代码的中间,新建一个这样的代码块
if not UnzipNatives(root, MinecraftPath, spath) then begin
messagebox(Handle, '解压Natives文件失败,请从别的启动器启动一次,再尝试启动吧!', '解压Natives失败', MB_ICONERROR);
exit;
end;
res := Concat(res, ' --width ', wt, ' --height ', ht); //新增最后两个参数,分别代表了长宽高。这里我一般会输入1000和800。
好了,非常简单的代码实现,就写完了!现在,大家终于可以在别的启动器直接下载好,然后直接在本启动器就可以直接启动了!大家可以先行一步,去versions\versionName\目录下,删掉含有natives的任何子目录,然后再尝试启动游戏哦!
这里墙裂建议使用1.12.2或者1.16.5进行测试,当然你用别的MC版本启动也是可以哒!