package org.cfpa.i18nupdatemod.command; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import net.minecraft.client.Minecraft; import net.minecraft.command.CommandBase; import net.minecraft.command.ICommandSender; import net.minecraft.server.MinecraftServer; import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.TextComponentTranslation; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.cfpa.i18nupdatemod.I18nUtils; import javax.annotation.Nullable; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public class CmdUpload extends CommandBase { private boolean haveResponse; @Override public String getName() { return "lang_upload"; } @Override public String getUsage(ICommandSender sender) { return "上传汉化文件到 weblate"; } @Override public int getRequiredPermissionLevel() { return 0; } @Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) { List<String> modidList = getLangpackName(); String key = I18nUtils.readToken(); // 参数为空,警告 if (args.length == 0) { Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_upload.empty")); return; } // 参数存在,进行下一步判定 if (modidList.contains(args[0])) { if (key == null) { Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_upload.no_token", args[0])); return; } new Thread(() -> { try { postFile(args[0], key); } catch (Exception e) { Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_upload.upload_error")); e.printStackTrace(); } }, "I18n-Thread-File-Upload").start(); } // 参数不存在,警告 else { Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_upload.not_found", args[0])); } } @Override public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) { List<String> modidList = getLangpackName(); // 如果输入参数为空,返回整个列表 if (args.length == 0) { return modidList; } // 如果输入不为空,从头字符串检索,进行输出 List<String> availableArgs = new ArrayList<>(); for (String modid : modidList) { if (modid.indexOf(args[0]) == 0) { availableArgs.add(modid); } } return availableArgs; } /** * 检索资源包列表,获取语言文件地址 * * @return 获取的语言文件列表 */ private List<String> getLangpackName() { List<String> outputModid = new ArrayList<>(); File resourcepacksDir = new File(Minecraft.getMinecraft().getResourcePackRepository().getDirResourcepacks().toString()); if (resourcepacksDir.exists() && resourcepacksDir.isDirectory()) { for (File i : resourcepacksDir.listFiles()) { Matcher matcher = Pattern.compile("resourcepacks" + Matcher.quoteReplacement(File.separator) + "(.*?)_tmp_resource_pack").matcher(i.toString()); while (matcher.find()) { File transFile = new File(i.toString() + File.separator + "assets" + File.separator + matcher.group(1) + File.separator + "lang" + File.separator + "zh_cn.lang"); if (transFile.exists() && transFile.isFile()) { outputModid.add(matcher.group(1)); } } } } return outputModid; } /** * POST 表单提交,将文件上传到 weblate 对应仓库 * * @param modid 上传的模组资源 domain * @param key weblate 的上传密匙 */ private void postFile(String modid, String key) { // API 地址 String apiURL = "https://weblate.exz.me/api/translations/langpack/" + modid + "/zh_cn/file/"; // 将要上传的文件预处理 File tmpFile; try { tmpFile = handleFile(modid); if (tmpFile == null) { Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_upload.handle_error")); return; } } catch (IOException ioe) { Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_upload.handle_error")); return; } // 建立连接 CloseableHttpClient closeableHttpClient = HttpClients.createDefault(); // Post 请求,同时设置验证和接收数据格式 HttpPost httpPost = new HttpPost(apiURL); httpPost.addHeader("Authorization", "Token " + key); httpPost.addHeader("Accept", "application/json"); // 装填表单本体 httpPost.setEntity( MultipartEntityBuilder.create() .addTextBody("overwrite", "true") .addBinaryBody("file", tmpFile) .build() ); // 执行,并在执行完毕后,关闭连接 CloseableHttpResponse response; try { // 先来一句提醒 Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_upload.excute_ready")); // 开始拨动线程检查 haveResponse = false; new Thread(() -> { // 计数器 int count = 0; while (!haveResponse) { try { // 阻塞 Thread.sleep(5 * 1000); // 计数 count = count + 5; // 二次检查显示信息 if (!haveResponse) { Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_upload.uploading", count)); } } catch (InterruptedException e) { e.printStackTrace(); } } }, "I18n-Upload-Check-Thread").start(); // 执行上传! response = closeableHttpClient.execute(httpPost); // 依据返回结果进行不同显示 switch (response.getStatusLine().getStatusCode()) { case 200: Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_token.200")); try { Gson gson = new Gson(); POJOResult result = gson.fromJson(IOUtils.readLines(response.getEntity().getContent(), "UTF-8").get(0), new TypeToken<POJOResult>() { }.getType()); Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_upload.result_title")); Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_upload.result_count", result.getCount())); Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_upload.result_total", result.getTotal())); Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_upload.result_accepted", result.getAccepted())); Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_upload.result_not_found", result.getNot_found())); Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_upload.result_skipped", result.getSkipped())); } catch (IOException ioe) { ioe.printStackTrace(); } break; case 400: Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_token.400")); break; case 401: Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_token.401")); break; case 403: Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_token.403")); break; case 404: Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_token.404")); break; case 429: Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_token.429")); break; case 524: Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_token.524")); break; default: Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_token.other", response.getStatusLine().getStatusCode())); break; } // 关闭提醒功能 haveResponse = true; // 关闭网络连接 closeableHttpClient.close(); } catch (IOException ioe) { Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_upload.upload_error")); } } /** * 将准备上传的语言文件进行处理 * * @param modid 上传的模组资源 domain * @return 处理后的文件对象 * @throws IOException 读取文件可能发生的 IO 错误 */ @Nullable private File handleFile(String modid) throws IOException { // 英文,中文,临时文件 File rawChineseFile = new File(String.format(Minecraft.getMinecraft().mcDataDir.toString() + File.separator + "resourcepacks" + File.separator + "%s_tmp_resource_pack" + File.separator + "assets" + File.separator + "%s" + File.separator + "lang" + File.separator + "zh_cn.lang", modid, modid)); File rawEnglishFile = new File(String.format(Minecraft.getMinecraft().mcDataDir.toString() + File.separator + "resourcepacks" + File.separator + "%s_tmp_resource_pack" + File.separator + "assets" + File.separator + "%s" + File.separator + "lang" + File.separator + "en_us.lang", modid, modid)); File handleChineseFile = new File(String.format(Minecraft.getMinecraft().mcDataDir.toString() + File.separator + "resourcepacks" + File.separator + "%s_tmp_resource_pack" + File.separator + "assets" + File.separator + "%s" + File.separator + "lang" + File.separator + "zh_cn_tmp.lang", modid, modid)); // 文件存在,才进行处理 if (rawEnglishFile.exists() && rawChineseFile.exists()) { // 读取处理成 HashMap Map<String, String> zh_cn = I18nUtils.listToMap(FileUtils.readLines(rawChineseFile, StandardCharsets.UTF_8)); Map<String, String> en_us = I18nUtils.listToMap(FileUtils.readLines(rawEnglishFile, StandardCharsets.UTF_8)); // 未翻译处进行剔除 List<String> tmpFile = new ArrayList<>(); for (String key : zh_cn.keySet()) { if (en_us.get(key) != null && !en_us.get(key).equals(zh_cn.get(key))) { tmpFile.add(key + '=' + zh_cn.get(key)); } } // 文件写入 FileUtils.writeLines(handleChineseFile, "UTF-8", tmpFile, "\n", false); Minecraft.getMinecraft().player.sendMessage(new TextComponentTranslation("message.i18nmod.cmd_upload.handle_success")); return handleChineseFile; } else { return null; // 不存在返回 null } } }