Show MineGeo console output and release plugin 0.1.2

This commit is contained in:
2026-10-05 18:37:32 +05:00
parent 891de77a10
commit b33db48b63
7 changed files with 60 additions and 9 deletions

View File

@@ -1,6 +1,8 @@
{ {
"passed": true, "passed": true,
"checks": [ "checks": [
"local console help, status and errors produce visible output",
"remote console help produces visible output",
"missing export guidance", "missing export guidance",
"empty area failure", "empty area failure",
"real Paper export without a player", "real Paper export without a player",
@@ -16,5 +18,5 @@
"blocks": 10112, "blocks": 10112,
"chunks": 16, "chunks": 16,
"viewpoints": 24, "viewpoints": 24,
"directory": ".cache/plugin-regression-1791186051478" "directory": ".cache/plugin-regression-1791207258927"
} }

View File

@@ -1,8 +1,8 @@
# Server plugin 0.1.1 # Server plugin 0.1.2
## Update and connect ## Update and connect
1. Stop the Minecraft server, replace its old MineGeo JAR with `public/tools/plugin/minegeo-plugin-1.21.11.jar`, and restart. Run `/version MineGeo` to verify 0.1.1. 1. Stop the Minecraft server, replace its old MineGeo JAR with `public/tools/plugin/minegeo-plugin-1.21.11.jar`, and restart. Run `version MineGeo` in the console to verify 0.1.2.
2. Open your MineGeo website dashboard, sign in, and generate a plugin token. 2. Open your MineGeo website dashboard, sign in, and generate a plugin token.
3. In the Minecraft server console, configure the website and token. Console commands do not need the leading slash: 3. In the Minecraft server console, configure the website and token. Console commands do not need the leading slash:

View File

@@ -1 +1 @@
allprojects { group = 'dev.minegeo'; version = '0.1.1'; repositories { mavenCentral() } } allprojects { group = 'dev.minegeo'; version = '0.1.2'; repositories { mavenCentral() } }

View File

@@ -37,7 +37,10 @@ public final class MineGeoPlugin extends JavaPlugin {
} }
@Override public void onDisable(){cancelled.set(true);} @Override public void onDisable(){cancelled.set(true);}
@Override public boolean onCommand(CommandSender sender,Command command,String label,String[] args){ @Override public boolean onCommand(CommandSender sender,Command command,String label,String[] args){
if(!sender.hasPermission("minegeo.admin")){sender.sendMessage("MineGeo: administrator permission required.");return true;} // Paper may dispatch hosting-panel commands through a remote console sender
// whose permissions differ from the local console. Check permissions here so
// 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 { 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> — send up to 12 panoramas to its owner","/minegeo status | cancel | reload"});return true;} 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> — send up to 12 panoramas to its owner","/minegeo status | cancel | reload"});return true;}
switch(args[0].toLowerCase(Locale.ROOT)) { switch(args[0].toLowerCase(Locale.ROOT)) {
@@ -62,7 +65,7 @@ public final class MineGeoPlugin extends JavaPlugin {
}catch(Exception ex){sender.sendMessage("MineGeo: "+ex.getMessage());}return true; }catch(Exception ex){sender.sendMessage("MineGeo: "+ex.getMessage());}return true;
} }
private void requireIdle(){if(busy)throw new IllegalArgumentException("An operation is running. Use /minegeo status or /minegeo cancel.");} private void requireIdle(){if(busy)throw new IllegalArgumentException("An operation is running. Use /minegeo status or /minegeo cancel.");}
private void report(CommandSender sender,String message){status=message;getLogger().info(message);if(sender instanceof Player&&isEnabled())Bukkit.getScheduler().runTask(this,()->sender.sendMessage("MineGeo: "+message));} private void report(CommandSender sender,String message){status=message;getLogger().info(message);if(!(sender instanceof ConsoleCommandSender)&&isEnabled())Bukkit.getScheduler().runTask(this,()->sender.sendMessage("MineGeo: "+message));}
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 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 { 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()); 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());

View File

@@ -1,12 +1,11 @@
name: MineGeo name: MineGeo
version: 0.1.1 version: 0.1.2
main: dev.minegeo.plugin.MineGeoPlugin main: dev.minegeo.plugin.MineGeoPlugin
api-version: '1.21' api-version: '1.21'
description: Bounded terrain export for MineGeo panorama guessing description: Bounded terrain export for MineGeo panorama guessing
commands: commands:
minegeo: minegeo:
description: Link, select, export, upload, status or cancel MineGeo maps description: Link, select, export, upload, status or cancel MineGeo maps
permission: minegeo.admin
permissions: permissions:
minegeo.admin: minegeo.admin:
default: op default: op

View File

@@ -1,4 +1,5 @@
import { createServer } from "node:http"; import { createServer } from "node:http";
import { connect } from "node:net";
import { spawn } from "node:child_process"; import { spawn } from "node:child_process";
import { mkdir, copyFile, writeFile, readFile } from "node:fs/promises"; import { mkdir, copyFile, writeFile, readFile } from "node:fs/promises";
import { createHash } from "node:crypto"; import { createHash } from "node:crypto";
@@ -17,7 +18,7 @@ await copyFile(
await writeFile(`${directory}/eula.txt`, "eula=true\n"); await writeFile(`${directory}/eula.txt`, "eula=true\n");
await writeFile( await writeFile(
`${directory}/server.properties`, `${directory}/server.properties`,
'server-ip=127.0.0.1\nserver-port=25580\nonline-mode=true\nlevel-type=minecraft:flat\ngenerator-settings={"layers":[{"block":"minecraft:bedrock","height":1},{"block":"minecraft:dirt","height":2},{"block":"minecraft:grass_block","height":1}],"biome":"minecraft:plains"}\ngenerate-structures=false\nview-distance=2\nsimulation-distance=2\nspawn-protection=0\nmax-players=1\n', 'server-ip=127.0.0.1\nserver-port=25580\nenable-rcon=true\nrcon.port=25581\nrcon.password=minegeo-fixture\nonline-mode=true\nlevel-type=minecraft:flat\ngenerator-settings={"layers":[{"block":"minecraft:bedrock","height":1},{"block":"minecraft:dirt","height":2},{"block":"minecraft:grass_block","height":1}],"biome":"minecraft:plains"}\ngenerate-structures=false\nview-distance=2\nsimulation-distance=2\nspawn-protection=0\nmax-players=1\n',
); );
let manifest: any, let manifest: any,
received = Buffer.alloc(0), received = Buffer.alloc(0),
@@ -164,8 +165,52 @@ const command = async (text: string, pattern: RegExp) => {
child.stdin.write(text + "\n"); child.stdin.write(text + "\n");
await pending; await pending;
}; };
const rconCommand = (text: string, expected: RegExp) => new Promise<void>((resolve, reject) => {
const socket = connect(25581, "127.0.0.1");
let buffer = Buffer.alloc(0);
let authenticated = false;
const timeout = setTimeout(() => finish(new Error(`RCON timed out waiting for ${expected}`)), 10000);
const finish = (error?: Error) => {
clearTimeout(timeout);
socket.destroy();
if (error) reject(error);
else resolve();
};
const packet = (id: number, type: number, body: string) => {
const payload = Buffer.from(body);
const frame = Buffer.alloc(payload.length + 14);
frame.writeInt32LE(payload.length + 10, 0);
frame.writeInt32LE(id, 4);
frame.writeInt32LE(type, 8);
payload.copy(frame, 12);
return frame;
};
socket.on("connect", () => socket.write(packet(1, 3, "minegeo-fixture")));
socket.on("error", finish);
socket.on("data", (chunk) => {
buffer = Buffer.concat([buffer, chunk]);
while (buffer.length >= 4 && buffer.length >= buffer.readInt32LE(0) + 4) {
const length = buffer.readInt32LE(0);
const frame = buffer.subarray(0, length + 4);
buffer = buffer.subarray(length + 4);
const id = frame.readInt32LE(4);
const type = frame.readInt32LE(8);
if (id === -1) return finish(new Error("RCON authentication failed"));
if (id === 1 && type === 2 && !authenticated) {
authenticated = true;
socket.write(packet(2, 2, text));
} else if (id === 2 && expected.test(frame.toString("utf8", 12, length + 2))) {
return finish();
}
}
});
});
try { try {
await wait(/Done \(/, 0, 180000); await wait(/Done \(/, 0, 180000);
await command("minegeo help", /MineGeo commands:/);
await command("minegeo status", /MineGeo: Idle/);
await command("minegeo unknown", /Unknown command\. Use \/minegeo help/);
await rconCommand("minegeo help", /MineGeo commands:/);
await command("minegeo upload", /No completed export/); await command("minegeo upload", /No completed export/);
await command( await command(
"minegeo area world 100000 100000 100032 100032", "minegeo area world 100000 100000 100032 100032",
@@ -266,6 +311,8 @@ try {
{ {
passed: true, passed: true,
checks: [ checks: [
"local console help, status and errors produce visible output",
"remote console help produces visible output",
"missing export guidance", "missing export guidance",
"empty area failure", "empty area failure",
"real Paper export without a player", "real Paper export without a player",