Release v0.1.4 with terrain rescanning and owner-published map updates
All checks were successful
Deploy MineGeo to Dokploy / deploy (push) Successful in 6m26s
All checks were successful
Deploy MineGeo to Dokploy / deploy (push) Successful in 6m26s
This commit is contained in:
@@ -25,9 +25,9 @@ public final class ServerAddress {
|
||||
return host+(port==25565?"":":"+port);
|
||||
}
|
||||
public static void requireMatch(String expected,String captured) throws IOException {
|
||||
String server=normalize(expected);
|
||||
if(server==null)return;
|
||||
if(expected==null||expected.isBlank())return;
|
||||
String actual=normalize(captured);
|
||||
if(!server.equals(actual))throw new IOException("Wrong capture server: this map requires "+server+", but the export records "+(actual==null?"no server address":actual)+". Join the correct server and capture again. Plugin admins: configure server-address before exporting. The map owner can correct or clear its server address in settings.");
|
||||
for(String address:expected.split(","))if(java.util.Objects.equals(normalize(address),actual)&&actual!=null)return;
|
||||
throw new IOException("Wrong capture server: this map requires one of "+expected+", but the export records "+(actual==null?"no server address":actual)+". Join the correct server and capture again.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package dev.minegeo.plugin;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.zip.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** Persists bounded, client-visible surface samples in the plugin terrain format. */
|
||||
public final class SurfaceTerrain {
|
||||
private static final Pattern BLOCK_STATE=Pattern.compile("minecraft:[a-z0-9_]+(\\[[a-z0-9_=,]+\\])?");
|
||||
private final Path directory;
|
||||
private final Gson gson=new Gson();
|
||||
private JsonArray current;
|
||||
public SurfaceTerrain(Path directory){this.directory=directory;}
|
||||
private Path chunk(String key){return directory.resolve("terrain-chunks").resolve(key.replace(',','_')+".json.gz");}
|
||||
public boolean has(String key){return Files.isRegularFile(chunk(key));}
|
||||
public void begin(){current=new JsonArray();}
|
||||
public void add(int x,int y,int z,String state){
|
||||
if(current==null)return;
|
||||
state=state.replace("Block{","").replace("}","");
|
||||
if(!BLOCK_STATE.matcher(state).matches())return;
|
||||
JsonArray block=new JsonArray();block.add(x);block.add(y);block.add(z);block.add(state);current.add(block);
|
||||
}
|
||||
public void finish(String key) throws IOException {
|
||||
Path file=chunk(key);Files.createDirectories(file.getParent());Path tmp=file.resolveSibling(file.getFileName()+".tmp");
|
||||
try(Writer out=new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(tmp)),StandardCharsets.UTF_8)){gson.toJson(current,out);}
|
||||
Files.move(tmp,file,StandardCopyOption.REPLACE_EXISTING);current=null;
|
||||
}
|
||||
public boolean export(Set<String> coverage,int minX,int minZ) throws IOException {
|
||||
List<String> keys=coverage.stream().filter(this::has).sorted().toList();if(keys.isEmpty())return false;
|
||||
Path target=directory.resolve("terrain.json.gz"),tmp=directory.resolve("terrain.json.gz.tmp");int blocks=0;
|
||||
try(JsonWriter writer=new JsonWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(tmp)),StandardCharsets.UTF_8))){
|
||||
writer.beginObject().name("bounds").beginObject().name("minX").value(minX).name("minZ").value(minZ).name("maxX").value(minX+512).name("maxZ").value(minZ+512).endObject();
|
||||
writer.name("blocks").beginArray();
|
||||
for(String key:keys)try(Reader in=new InputStreamReader(new GZIPInputStream(Files.newInputStream(chunk(key))),StandardCharsets.UTF_8)){
|
||||
for(JsonElement block:JsonParser.parseReader(in).getAsJsonArray()){gson.toJson(block,writer);blocks++;}
|
||||
}
|
||||
writer.endArray().name("coverage").beginArray();for(String key:keys){String[] xy=key.split(",");writer.beginArray().value(Integer.parseInt(xy[0])).value(Integer.parseInt(xy[1])).endArray();}writer.endArray().endObject();
|
||||
}
|
||||
if(blocks==0){Files.deleteIfExists(tmp);return false;}
|
||||
if(Files.size(tmp)>50L*1024*1024){Files.deleteIfExists(tmp);throw new IOException("Terrain snapshot exceeds 50 MiB");}
|
||||
Files.move(tmp,target,StandardCopyOption.REPLACE_EXISTING);return true;
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,8 @@ public final class UploadClient {
|
||||
JsonObject manifest=JsonParser.parseString(Files.readString(manifestFile)).getAsJsonObject();
|
||||
if(targetMap!=null){
|
||||
JsonObject serverInfo=api("/contributions/"+targetMap+"/server",null);
|
||||
ServerAddress.requireMatch(serverInfo.has("serverAddress")&&!serverInfo.get("serverAddress").isJsonNull()?serverInfo.get("serverAddress").getAsString():null,manifest.has("serverAddress")?manifest.get("serverAddress").getAsString():null);
|
||||
String expected=serverInfo.has("serverAddresses")?java.util.stream.StreamSupport.stream(serverInfo.getAsJsonArray("serverAddresses").spliterator(),false).map(e->e.getAsString()).collect(java.util.stream.Collectors.joining(",")):serverInfo.has("serverAddress")&&!serverInfo.get("serverAddress").isJsonNull()?serverInfo.get("serverAddress").getAsString():null;
|
||||
ServerAddress.requireMatch(expected,manifest.has("serverAddress")?manifest.get("serverAddress").getAsString():null);
|
||||
String originalHash=hash((website+"\n"+hash(token.getBytes(StandardCharsets.UTF_8))+"\n"+gson.toJson(manifest)).getBytes(StandardCharsets.UTF_8));
|
||||
Path selectionFile=directory.resolve(".contribution-"+targetMap+".json");
|
||||
JsonObject cached=Files.exists(selectionFile)?JsonParser.parseString(Files.readString(selectionFile)).getAsJsonObject():new JsonObject();
|
||||
@@ -64,7 +65,7 @@ public final class UploadClient {
|
||||
manifest.add("viewpoints",subset);
|
||||
if(manifest.get("source").getAsString().equals("fabric")){
|
||||
Set<String> ids=new HashSet<>();for(JsonElement p:subset)ids.add(p.getAsJsonObject().get("id").getAsString());
|
||||
JsonArray assets=new JsonArray();for(JsonElement a:manifest.getAsJsonArray("assets")){String path=a.getAsJsonObject().get("path").getAsString();if(path.equals("map.png")||ids.contains(path.split("/")[0]))assets.add(a);}manifest.add("assets",assets);
|
||||
JsonArray assets=new JsonArray();for(JsonElement a:manifest.getAsJsonArray("assets")){String path=a.getAsJsonObject().get("path").getAsString();if(path.equals("map.png")||path.equals("terrain.json.gz")||ids.contains(path.split("/")[0]))assets.add(a);}manifest.add("assets",assets);
|
||||
}
|
||||
cached=new JsonObject();cached.addProperty("hash",originalHash);cached.add("manifest",manifest);save(selectionFile,cached);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: MineGeo
|
||||
version: 0.1.3
|
||||
version: 0.1.4
|
||||
main: dev.minegeo.plugin.MineGeoPlugin
|
||||
api-version: '1.21'
|
||||
description: Bounded terrain export for MineGeo panorama guessing
|
||||
|
||||
Reference in New Issue
Block a user