|
|
|
|
@@ -26,7 +26,7 @@ public final class MineGeoPlugin extends JavaPlugin {
|
|
|
|
|
private final Gson gson=new GsonBuilder().setPrettyPrinting().create();
|
|
|
|
|
private volatile Path latest;
|
|
|
|
|
@Override public void onEnable(){
|
|
|
|
|
saveDefaultConfig();Objects.requireNonNull(getCommand("minegeo")).setExecutor(this);
|
|
|
|
|
saveDefaultConfig();if(getConfig().isSet("website")){getConfig().set("website",null);saveConfig();}Objects.requireNonNull(getCommand("minegeo")).setExecutor(this);
|
|
|
|
|
try {
|
|
|
|
|
Files.createDirectories(getDataFolder().toPath().resolve("exports"));
|
|
|
|
|
Path root=getDataFolder().toPath().toAbsolutePath().normalize(),pointer=root.resolve("latest-export.txt");
|
|
|
|
|
@@ -42,7 +42,7 @@ public final class MineGeoPlugin extends JavaPlugin {
|
|
|
|
|
// every rejected command receives a visible explanation.
|
|
|
|
|
if(!(sender instanceof ConsoleCommandSender||sender instanceof RemoteConsoleCommandSender||sender instanceof Player&&sender.hasPermission("minegeo.admin"))){sender.sendMessage("MineGeo: administrator permission required.");return true;}
|
|
|
|
|
try {
|
|
|
|
|
if(args.length==0||args[0].equalsIgnoreCase("help")) {sender.sendMessage(new String[]{"MineGeo commands:","/minegeo website <http(s)://address:port>","/minegeo server <hostname[:port]> — capture server address","/minegeo link <dashboard token>","/minegeo area <world> <minX> <minZ> <maxX> <maxZ>","/minegeo export <map name> — saves a local export","/minegeo upload — uploads the latest completed export","/minegeo contribute <map ID> — add panoramas (24 owner, 12 guests)","/minegeo status | cancel | reload"});return true;}
|
|
|
|
|
if(args.length==0||args[0].equalsIgnoreCase("help")) {sender.sendMessage(new String[]{"MineGeo commands:","/minegeo website — show the built-in MineGeo service","/minegeo server <hostname[:port]> — capture server address","/minegeo link <dashboard token>","/minegeo area <world> <minX> <minZ> <maxX> <maxZ>","/minegeo export <map name> — saves a local export","/minegeo upload — uploads the latest completed export","/minegeo contribute <map ID> — add panoramas (24 owner, 12 guests)","/minegeo status | cancel | reload"});return true;}
|
|
|
|
|
switch(args[0].toLowerCase(Locale.ROOT)) {
|
|
|
|
|
case "area" -> {
|
|
|
|
|
requireIdle();if(args.length!=6)throw new IllegalArgumentException("Usage: /minegeo area <world> <minX> <minZ> <maxX> <maxZ>");
|
|
|
|
|
@@ -54,7 +54,7 @@ public final class MineGeoPlugin extends JavaPlugin {
|
|
|
|
|
case "status" -> {sender.sendMessage("MineGeo: "+status);if(latest!=null)sender.sendMessage("Latest complete export: "+latest.toAbsolutePath());}
|
|
|
|
|
case "cancel" -> {cancelled.set(true);sender.sendMessage(busy?"MineGeo cancellation requested. Completed exports are kept.":"MineGeo: no operation is running.");}
|
|
|
|
|
case "reload" -> {requireIdle();reloadConfig();sender.sendMessage("MineGeo configuration reloaded.");}
|
|
|
|
|
case "website" -> {requireIdle();if(args.length!=2)throw new IllegalArgumentException("Usage: /minegeo website <http(s)://address:port>");new UploadClient(args[1],"");getConfig().set("website",args[1]);saveConfig();sender.sendMessage("MineGeo website saved: "+args[1]);}
|
|
|
|
|
case "website" -> {if(args.length!=1)throw new IllegalArgumentException("The MineGeo website is fixed to "+UploadClient.WEBSITE+". No URL configuration is needed.");sender.sendMessage("MineGeo website: "+UploadClient.WEBSITE);}
|
|
|
|
|
case "server" -> {requireIdle();if(args.length!=2)throw new IllegalArgumentException("Usage: /minegeo server <hostname[:port]>");String address=ServerAddress.normalize(args[1]);getConfig().set("server-address",address);saveConfig();sender.sendMessage("Capture server address saved: "+address+". Export again to record it.");}
|
|
|
|
|
case "link" -> {requireIdle();if(args.length!=2||args[1].isBlank())throw new IllegalArgumentException("Usage: /minegeo link <dashboard token>");getConfig().set("token",args[1]);saveConfig();sender.sendMessage("MineGeo contributor token saved.");}
|
|
|
|
|
case "export" -> export(sender,args.length>1?String.join(" ",Arrays.copyOfRange(args,1,args.length)):"Overworld Explorer");
|
|
|
|
|
@@ -69,8 +69,8 @@ public final class MineGeoPlugin extends JavaPlugin {
|
|
|
|
|
private void fail(CommandSender sender,String phase,Exception ex){report(sender,cancelled.get()?"Cancelled. Completed exports are kept.":phase+" failed: "+ex.getMessage());if(!cancelled.get())getLogger().log(Level.WARNING,phase+" failure",ex);}
|
|
|
|
|
private void upload(CommandSender sender,String targetMap)throws Exception {
|
|
|
|
|
requireIdle();if(latest==null||!Files.isRegularFile(latest.resolve("manifest.json")))throw new IOException("No completed export. Run /minegeo export My World, wait for 'Export complete', then /minegeo upload. Files: "+getDataFolder().toPath().resolve("exports").toAbsolutePath());
|
|
|
|
|
String website=getConfig().getString("website"),token=getConfig().getString("token");if(token==null||token.isBlank())throw new IOException("Create a token in the website dashboard, then /minegeo link <token>.");
|
|
|
|
|
UploadClient client=new UploadClient(website,token);Path directory=latest;busy=true;cancelled.set(false);status="Uploading "+directory.getFileName();
|
|
|
|
|
String website=UploadClient.WEBSITE,token=getConfig().getString("token");if(token==null||token.isBlank())throw new IOException("Create a token in the website dashboard, then /minegeo link <token>.");
|
|
|
|
|
UploadClient client=new UploadClient(token);Path directory=latest;busy=true;cancelled.set(false);status="Uploading "+directory.getFileName();
|
|
|
|
|
Bukkit.getScheduler().runTaskAsynchronously(this,()->{try{client.upload(directory,targetMap,message->status=message,cancelled::get);report(sender,"Upload complete. Open "+website+"/dashboard to preview and submit "+(targetMap==null?"the private map for moderator review.":"the panoramas to the map owner."));}catch(Exception ex){fail(sender,"Upload",ex);}finally{busy=false;}});
|
|
|
|
|
sender.sendMessage("MineGeo upload started. /minegeo status shows progress; retry /minegeo upload after a connection failure.");
|
|
|
|
|
}
|
|
|
|
|
@@ -133,7 +133,7 @@ public final class MineGeoPlugin extends JavaPlugin {
|
|
|
|
|
List<int[]> chosen=candidates.subList(0,Math.min(desired,candidates.size()));
|
|
|
|
|
for(int i=0;i<chosen.size();i++){int[] p=chosen.get(i);JsonObject point=new JsonObject();point.addProperty("id","p"+i);point.addProperty("x",p[0]+.5);point.addProperty("y",p[1]+1.62);point.addProperty("z",p[2]+.5);points.add(point);}
|
|
|
|
|
if(Files.size(terrain)>50L*1024*1024)throw new IOException("Compressed export exceeds 50 MiB. Select a smaller area.");
|
|
|
|
|
JsonObject manifest=new JsonObject();manifest.addProperty("format","minegeo-dataset/v1");manifest.addProperty("minecraftVersion","1.21.11");manifest.addProperty("dimension","minecraft:overworld");manifest.addProperty("source","plugin");manifest.addProperty("name",name);manifest.addProperty("description","Vanilla surface terrain exported from "+serverName);manifest.addProperty("serverName",serverName);if(serverAddress!=null)manifest.addProperty("serverAddress",serverAddress);manifest.addProperty("north","-z");manifest.add("bounds",bounds);manifest.add("viewpoints",points);
|
|
|
|
|
JsonObject manifest=new JsonObject();manifest.addProperty("format","minegeo-dataset/v1");manifest.addProperty("minecraftVersion",Bukkit.getBukkitVersion().split("[-+]",2)[0].replaceFirst("[.]build[.].*$",""));manifest.addProperty("dimension","minecraft:overworld");manifest.addProperty("source","plugin");manifest.addProperty("name",name);manifest.addProperty("description","Vanilla surface terrain exported from "+serverName);manifest.addProperty("serverName",serverName);if(serverAddress!=null)manifest.addProperty("serverAddress",serverAddress);manifest.addProperty("north","-z");manifest.add("bounds",bounds);manifest.add("viewpoints",points);
|
|
|
|
|
JsonArray assets=new JsonArray();JsonObject asset=new JsonObject();asset.addProperty("path","terrain.json.gz");asset.addProperty("bytes",Files.size(terrain));MessageDigest digest=MessageDigest.getInstance("SHA-256");try(InputStream input=Files.newInputStream(terrain)){byte[] buffer=new byte[65536];int read;while((read=input.read(buffer))!=-1)digest.update(buffer,0,read);}asset.addProperty("sha256",HexFormat.of().formatHex(digest.digest()));assets.add(asset);manifest.add("assets",assets);
|
|
|
|
|
// The manifest is the completion marker. Partial folders never become upload candidates.
|
|
|
|
|
if(cancelled.get())throw new IOException("Cancelled");Files.writeString(out.resolve("manifest.json.tmp"),gson.toJson(manifest));Files.move(out.resolve("manifest.json.tmp"),out.resolve("manifest.json"),StandardCopyOption.REPLACE_EXISTING);
|
|
|
|
|
|