您的位置:首页 > 房产 > 家装 > Rust入门实战 编写Minecraft启动器#5启动游戏

Rust入门实战 编写Minecraft启动器#5启动游戏

2024/12/23 12:13:28 来源:https://blog.csdn.net/Enaium/article/details/140242914  浏览:    关键词:Rust入门实战 编写Minecraft启动器#5启动游戏

首发于Enaium的个人博客


好了,我们已经完成了所有的准备工作,现在我们可以开始编写启动游戏的代码了。

首先我们需要添加几个依赖。

model = { path = "../model" }
parse = { path = "../parse" }
download = { path = "../download" }
clap = { version = "4.5" }
zip = "2.1"

clap用于解析命令行参数,zip用于解压文件。

首先创建一个cli函数用于构建我们的命令行。

fn cli() -> Command {Command::new("rmcl").about("A Minecraft launcher written in Rust").version("0.1.0").author("Enaium").subcommand_required(true).arg_required_else_help(true).allow_external_subcommands(true).subcommand(Command::new("search").about("Search Game").arg(arg!([VERSION] "Game version")).arg(arg!(-t --type <TYPE> "Game type").value_parser(["release", "snapshot", "old_beta", "old_alpha"]).require_equals(true).default_value("release").default_missing_value("release"),),).subcommand(Command::new("download").about("Download Game").arg(arg!(<VERSION> "Game Version")).arg_required_else_help(true),).subcommand(Command::new("launch").about("Launch Game").arg(arg!(<VERSION> "Game Version")).arg_required_else_help(true),)
}

接着创建一个get_version_manifest函数用于获取游戏的所有版本清单。

fn get_version_manifest() -> model::version_manifest::VersionManifest {return get("https://launchermeta.mojang.com/mc/game/version_manifest.json").unwrap().json::<version_manifest::VersionManifest>().unwrap();
}

之后编写每个子命令的处理函数。

fn search(sub_matches: &clap::ArgMatches) {let version = sub_matches.get_one::<String>("VERSION");let type_ = sub_matches.get_one::<String>("type").unwrap();let versions = get_version_manifest().versions;let versions = versions.iter().filter(|v| {(if version.is_some() {v.id.contains(version.unwrap())} else {true}) && v.type_.eq(type_)});for version in versions {println!("Version:{}", version.id);}
}
fn download(sub_matches: &clap::ArgMatches) {let game_dir = std::env::current_dir().unwrap().join(".minecraft");let version = sub_matches.get_one::<String>("VERSION").unwrap();let versions = get_version_manifest().versions;if let Some(version) = versions.iter().find(|v| v.id.eq(version)) {version.download(&game_dir).unwrap_or_else(|err| {panic!("Version download error:{:?}", err);});} else {println!("Version:{} not found", version);}
}
fn launch(sub_matches: &clap::ArgMatches) {let game_dir = std::env::current_dir().unwrap().join(".minecraft");let libraries_dir = game_dir.join("libraries");let assets_dir = game_dir.join("assets");let version = sub_matches.get_one::<String>("VERSION").unwrap();let version_dir = game_dir.join("versions").join(version);let natives_dir = version_dir.join("natives");let config_path = version_dir.join(format!("{}.json", version));let version_path = version_dir.join(format!("{}.jar", version));if !version_path.exists() || !config_path.exists() {println!("Version:{} not found", version_path.display());return;}let version = &Version::parse(&std::fs::read_to_string(&config_path).unwrap()).unwrap();for library in &version.libraries {if library.allowed() && library.name.contains("natives") {extract_jar(&libraries_dir.join(&library.downloads.artifact.path),&natives_dir,);}}let classpath = format!("{}{}",&version.libraries.iter().map(|library| {format!("{}{}",libraries_dir.join(&library.downloads.artifact.path).display(),if cfg!(windows) { ";" } else { ":" })}).collect::<String>(),version_path.display());std::process::Command::new("java").current_dir(&game_dir).arg(format!("-Djava.library.path={}", natives_dir.display())).arg("-Dminecraft.launcher.brand=rmcl").arg("-cp").arg(classpath).arg(&version.main_class).arg("--username").arg("Enaium").arg("--version").arg(&version.id).arg("--gameDir").arg(game_dir).arg("--assetsDir").arg(assets_dir).arg("--assetIndex").arg(&version.asset_index.id).arg("--accessToken").arg("0").arg("--versionType").arg("RMCL 0.1.0").status().unwrap();
}

解压natives文件。

fn extract_jar(jar: &Path, dir: &Path) {if !dir.exists() {std::fs::create_dir_all(dir).unwrap();}let mut archive = zip::ZipArchive::new(std::fs::File::open(jar).unwrap()).unwrap();for i in 0..archive.len() {let mut entry = archive.by_index(i).unwrap();if entry.is_file() && !entry.name().contains("META-INF") {let mut name = entry.name();if name.contains("/") {name = &name[entry.name().rfind('/').unwrap() + 1..];}let path = dir.join(name);if path.exists() {std::fs::remove_file(&path).unwrap();}let mut file = std::fs::File::create(&path).unwrap();std::io::copy(&mut entry, &mut file).unwrap();}}
}

最后在main函数中调用cli函数。

fn main() {let matches = cli().get_matches();match matches.subcommand() {Some(("search", sub_matches)) => {search(sub_matches);}Some(("download", sub_matches)) => {download(sub_matches);}Some(("launch", sub_matches)) => {launch(sub_matches);}_ => unreachable!(),}
}

现在我们可以使用rmcl命令来搜索、下载、启动游戏了。

20240705202955

项目地址

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com