Wildstash commited on
Commit
2d5a495
·
verified ·
1 Parent(s): 38537c3

Import live AfterBlock packets in Paper plugin

Browse files
.gitignore CHANGED
@@ -4,3 +4,4 @@ __pycache__/
4
  .venv/
5
  dist/
6
  build/
 
 
4
  .venv/
5
  dist/
6
  build/
7
+ paper-plugin/target/
CODEX_BUILD_LOG.md CHANGED
@@ -27,3 +27,6 @@ This file records how Codex was used to build the hackathon entry.
27
  - Added per-tile Minecraft origins/bounds and documented the Paper bridge path for placing Living Canvas tiles and representing motion through particles, map updates, or block-frame updates.
28
  - Reframed the project as DreamWall: AfterBlock Museum with `dreamwall.museum.v1`, deterministic hall placement, curation scores, artifact spirits, passport cards, seeded relic demos, museum-terminal UI, and a GitHub Actions Paper plugin build workflow.
29
  - Expanded AfterBlock assets into a resource-pack pipeline: 1,200 generated item textures, 1,200 3D model JSONs, custom model data overrides, contact-sheet screenshots, a browser gallery, and a `/dreamwall demo` Paper command for an in-game pedestal proof.
 
 
 
 
27
  - Added per-tile Minecraft origins/bounds and documented the Paper bridge path for placing Living Canvas tiles and representing motion through particles, map updates, or block-frame updates.
28
  - Reframed the project as DreamWall: AfterBlock Museum with `dreamwall.museum.v1`, deterministic hall placement, curation scores, artifact spirits, passport cards, seeded relic demos, museum-terminal UI, and a GitHub Actions Paper plugin build workflow.
29
  - Expanded AfterBlock assets into a resource-pack pipeline: 1,200 generated item textures, 1,200 3D model JSONs, custom model data overrides, contact-sheet screenshots, a browser gallery, and a `/dreamwall demo` Paper command for an in-game pedestal proof.
30
+ - Added `/dreamwall import` and `/dreamwall import here` to the Paper bridge. The plugin now calls the live Gradio `curate_artifact` endpoint, parses `dreamwall.museum.v1`, and places a packet-derived pedestal/sign/item in Minecraft.
31
+ - Added Gson shading plus explicit Maven Central resolution so the Paper jar compiles into a self-contained PebbleHost-friendly plugin.
32
+ - Verified the shaded jar locally with temporary Maven 3.9.9 and prepared an ignored deploy bundle at `dist/pebblehost/` with the plugin jar, `AfterBlockMuseum.zip`, and hashes.
docs/PEBBLEHOST_DEPLOY.md ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PebbleHost Deployment Checklist
2
+
3
+ This is the shortest path to make the AfterBlock Museum demo visible in a live Minecraft server.
4
+
5
+ ## Local Build Artifacts
6
+
7
+ Build the Paper plugin:
8
+
9
+ ```bash
10
+ /tmp/apache-maven-3.9.9/bin/mvn -q -f paper-plugin/pom.xml package
11
+ ```
12
+
13
+ Prepared local bundle:
14
+
15
+ ```text
16
+ dist/pebblehost/dreamwall-paper-bridge-0.1.0.jar
17
+ dist/pebblehost/AfterBlockMuseum.zip
18
+ dist/pebblehost/SHA256SUMS
19
+ ```
20
+
21
+ Current hashes from the verified local bundle:
22
+
23
+ ```text
24
+ 7ace99dec709e01c3f118bb48cde406b9711d6683d74e848707015692a78087c AfterBlockMuseum.zip
25
+ 0bc7e6b3bb2e80b01943cea42bb3efdd7432eac6db39e394b3564dbfc584829e dreamwall-paper-bridge-0.1.0.jar
26
+ ```
27
+
28
+ ## Upload Targets
29
+
30
+ Upload with SFTP or the PebbleHost file manager:
31
+
32
+ ```text
33
+ plugins/dreamwall-paper-bridge-0.1.0.jar
34
+ AfterBlockMuseum.zip
35
+ ```
36
+
37
+ The known PebbleHost SFTP endpoint from the account screen was:
38
+
39
+ ```text
40
+ sftp://uk144.pebblehost.net:2222
41
+ ```
42
+
43
+ Use the PebbleHost panel username/password. Do not commit credentials.
44
+
45
+ ## Server Resource Pack
46
+
47
+ In the PebbleHost panel or `server.properties`, set the server resource pack URL to a downloadable URL for:
48
+
49
+ ```text
50
+ AfterBlockMuseum.zip
51
+ ```
52
+
53
+ The pack contains:
54
+
55
+ ```text
56
+ 1,200 PNG item textures
57
+ 1,200 item model JSON files
58
+ 1,200 CustomModelData overrides on minecraft:paper
59
+ ```
60
+
61
+ ## In-Game Smoke Test
62
+
63
+ After restarting the server, run these as an op player:
64
+
65
+ ```text
66
+ /dreamwall
67
+ /dreamwall fetch
68
+ /dreamwall demo
69
+ /dreamwall import here
70
+ ```
71
+
72
+ Expected result:
73
+
74
+ - `/dreamwall fetch` reports the Hugging Face Space is reachable.
75
+ - `/dreamwall demo` places a small pedestal and gives a Paper item using `CustomModelData 730002`.
76
+ - `/dreamwall import here` calls the live `curate_artifact` endpoint, parses `dreamwall.museum.v1`, places a packet-derived pedestal/sign beside the player, and gives a Paper item using the packet's `custom_model_data`.
77
+
78
+ If the Paper item looks like ordinary paper, the plugin is working but the resource pack is not loaded.
paper-plugin/README.md CHANGED
@@ -9,6 +9,8 @@ The hackathon Space is the official submission surface. This plugin scaffold is
9
  - `/dreamwall` shows the configured Space endpoint.
10
  - `/dreamwall fetch` calls the Space app domain and confirms the bridge can reach Hugging Face.
11
  - `/dreamwall demo` places a safe local pedestal/sign proof and gives the player a Paper item with `CustomModelData 730002`.
 
 
12
  - A scheduled task can be extended to poll approved museum artifacts.
13
  - The Space now emits `dreamwall.museum.v1`, the main AfterBlock packet for the hackathon demo.
14
  - The Space also emits `living_canvas.mc.v1` for the secondary wall/canvas mode.
@@ -28,7 +30,7 @@ The main Space endpoint is `curate_artifact`, which emits `dreamwall.museum.v1`
28
  - curation scores
29
  - resonance links
30
 
31
- Expected `/dreamwall fetch` V1 behavior:
32
 
33
  1. Fetch latest artifact packet.
34
  2. Place pedestal/sign/painting marker at coordinates.
@@ -125,3 +127,4 @@ The jar will be in `target/dreamwall-paper-bridge-0.1.0.jar`.
125
  5. Edit `plugins/DreamWall/config.yml` if needed.
126
  6. Run `/dreamwall fetch` in game or console.
127
  7. Run `/dreamwall demo` in game to place a pedestal proof and receive the AirPods demo item.
 
 
9
  - `/dreamwall` shows the configured Space endpoint.
10
  - `/dreamwall fetch` calls the Space app domain and confirms the bridge can reach Hugging Face.
11
  - `/dreamwall demo` places a safe local pedestal/sign proof and gives the player a Paper item with `CustomModelData 730002`.
12
+ - `/dreamwall import` calls the live `curate_artifact` Gradio endpoint, parses the returned `dreamwall.museum.v1` packet, and places a packet-derived pedestal/sign/item at the artifact coordinates.
13
+ - `/dreamwall import here` uses the same live packet but places it two blocks in front of the player for fast video proof.
14
  - A scheduled task can be extended to poll approved museum artifacts.
15
  - The Space now emits `dreamwall.museum.v1`, the main AfterBlock packet for the hackathon demo.
16
  - The Space also emits `living_canvas.mc.v1` for the secondary wall/canvas mode.
 
30
  - curation scores
31
  - resonance links
32
 
33
+ Implemented `/dreamwall import` V1 behavior:
34
 
35
  1. Fetch latest artifact packet.
36
  2. Place pedestal/sign/painting marker at coordinates.
 
127
  5. Edit `plugins/DreamWall/config.yml` if needed.
128
  6. Run `/dreamwall fetch` in game or console.
129
  7. Run `/dreamwall demo` in game to place a pedestal proof and receive the AirPods demo item.
130
+ 8. Run `/dreamwall import here` in game to place a live Hugging Face artifact packet beside you.
paper-plugin/pom.xml CHANGED
@@ -15,6 +15,10 @@
15
  </properties>
16
 
17
  <repositories>
 
 
 
 
18
  <repository>
19
  <id>papermc</id>
20
  <url>https://repo.papermc.io/repository/maven-public/</url>
@@ -28,6 +32,11 @@
28
  <version>1.21.4-R0.1-SNAPSHOT</version>
29
  <scope>provided</scope>
30
  </dependency>
 
 
 
 
 
31
  </dependencies>
32
 
33
  <build>
@@ -37,6 +46,22 @@
37
  <artifactId>maven-compiler-plugin</artifactId>
38
  <version>3.13.0</version>
39
  </plugin>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  </plugins>
41
  </build>
42
  </project>
 
15
  </properties>
16
 
17
  <repositories>
18
+ <repository>
19
+ <id>central</id>
20
+ <url>https://repo.maven.apache.org/maven2/</url>
21
+ </repository>
22
  <repository>
23
  <id>papermc</id>
24
  <url>https://repo.papermc.io/repository/maven-public/</url>
 
32
  <version>1.21.4-R0.1-SNAPSHOT</version>
33
  <scope>provided</scope>
34
  </dependency>
35
+ <dependency>
36
+ <groupId>com.google.code.gson</groupId>
37
+ <artifactId>gson</artifactId>
38
+ <version>2.11.0</version>
39
+ </dependency>
40
  </dependencies>
41
 
42
  <build>
 
46
  <artifactId>maven-compiler-plugin</artifactId>
47
  <version>3.13.0</version>
48
  </plugin>
49
+ <plugin>
50
+ <groupId>org.apache.maven.plugins</groupId>
51
+ <artifactId>maven-shade-plugin</artifactId>
52
+ <version>3.6.0</version>
53
+ <executions>
54
+ <execution>
55
+ <phase>package</phase>
56
+ <goals>
57
+ <goal>shade</goal>
58
+ </goals>
59
+ <configuration>
60
+ <createDependencyReducedPom>false</createDependencyReducedPom>
61
+ </configuration>
62
+ </execution>
63
+ </executions>
64
+ </plugin>
65
  </plugins>
66
  </build>
67
  </project>
paper-plugin/src/main/java/com/dreamwall/DreamWallPlugin.java CHANGED
@@ -6,8 +6,14 @@ import java.net.http.HttpClient;
6
  import java.net.http.HttpRequest;
7
  import java.net.http.HttpResponse;
8
  import java.time.Duration;
 
9
  import java.util.logging.Level;
10
 
 
 
 
 
 
11
  import org.bukkit.Location;
12
  import org.bukkit.Material;
13
  import org.bukkit.Particle;
@@ -16,7 +22,6 @@ import org.bukkit.block.Block;
16
  import org.bukkit.block.Sign;
17
  import org.bukkit.command.Command;
18
  import org.bukkit.command.CommandSender;
19
- import org.bukkit.entity.ItemFrame;
20
  import org.bukkit.entity.Player;
21
  import org.bukkit.inventory.ItemStack;
22
  import org.bukkit.inventory.meta.ItemMeta;
@@ -58,12 +63,18 @@ public final class DreamWallPlugin extends JavaPlugin {
58
  placeDemoArtifact(sender);
59
  return true;
60
  }
 
 
 
 
 
61
 
62
  sender.sendMessage("DreamWall bridge is configured for: " + spaceUrl());
63
  sender.sendMessage("Canvas: " + getConfig().getInt("canvas-size", 12) + "x" + getConfig().getInt("canvas-size", 12)
64
  + " plots, plot size=" + getConfig().getInt("plot-size", 32));
65
  sender.sendMessage("Use /dreamwall fetch to test Hugging Face reachability.");
66
  sender.sendMessage("Use /dreamwall demo in-game to place a safe AfterBlock pedestal proof.");
 
67
  return true;
68
  }
69
 
@@ -94,6 +105,92 @@ public final class DreamWallPlugin extends JavaPlugin {
94
  });
95
  }
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  private void placeDemoArtifact(CommandSender sender) {
98
  if (!(sender instanceof Player player)) {
99
  sender.sendMessage("/dreamwall demo must be run by an in-game player.");
@@ -147,6 +244,58 @@ public final class DreamWallPlugin extends JavaPlugin {
147
  return response.body();
148
  }
149
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  private String spaceUrl() {
151
  return getConfig().getString("space-url", "https://build-small-hackathon-dreamwall-mc.hf.space").replaceAll("/+$", "");
152
  }
 
6
  import java.net.http.HttpRequest;
7
  import java.net.http.HttpResponse;
8
  import java.time.Duration;
9
+ import java.util.Locale;
10
  import java.util.logging.Level;
11
 
12
+ import com.google.gson.JsonArray;
13
+ import com.google.gson.JsonElement;
14
+ import com.google.gson.JsonObject;
15
+ import com.google.gson.JsonParser;
16
+
17
  import org.bukkit.Location;
18
  import org.bukkit.Material;
19
  import org.bukkit.Particle;
 
22
  import org.bukkit.block.Sign;
23
  import org.bukkit.command.Command;
24
  import org.bukkit.command.CommandSender;
 
25
  import org.bukkit.entity.Player;
26
  import org.bukkit.inventory.ItemStack;
27
  import org.bukkit.inventory.meta.ItemMeta;
 
63
  placeDemoArtifact(sender);
64
  return true;
65
  }
66
+ if (args.length > 0 && args[0].equalsIgnoreCase("import")) {
67
+ boolean placeHere = args.length > 1 && args[1].equalsIgnoreCase("here");
68
+ importArtifact(sender, placeHere);
69
+ return true;
70
+ }
71
 
72
  sender.sendMessage("DreamWall bridge is configured for: " + spaceUrl());
73
  sender.sendMessage("Canvas: " + getConfig().getInt("canvas-size", 12) + "x" + getConfig().getInt("canvas-size", 12)
74
  + " plots, plot size=" + getConfig().getInt("plot-size", 32));
75
  sender.sendMessage("Use /dreamwall fetch to test Hugging Face reachability.");
76
  sender.sendMessage("Use /dreamwall demo in-game to place a safe AfterBlock pedestal proof.");
77
+ sender.sendMessage("Use /dreamwall import or /dreamwall import here to place a live Space artifact packet.");
78
  return true;
79
  }
80
 
 
105
  });
106
  }
107
 
108
+ private void importArtifact(CommandSender sender, boolean placeHere) {
109
+ if (!(sender instanceof Player player)) {
110
+ sender.sendMessage("/dreamwall import must be run by an in-game player.");
111
+ return;
112
+ }
113
+ sender.sendMessage("Importing one live AfterBlock artifact from " + spaceUrl() + " ...");
114
+ getServer().getScheduler().runTaskAsynchronously(this, () -> {
115
+ try {
116
+ JsonObject packet = fetchMuseumPacket();
117
+ getServer().getScheduler().runTask(this, () -> placePacketArtifact(player, packet, placeHere));
118
+ } catch (IOException | InterruptedException e) {
119
+ sender.sendMessage("DreamWall import failed: " + e.getMessage());
120
+ Thread.currentThread().interrupt();
121
+ } catch (RuntimeException e) {
122
+ sender.sendMessage("DreamWall import parse failed: " + e.getMessage());
123
+ }
124
+ });
125
+ }
126
+
127
+ private JsonObject fetchMuseumPacket() throws IOException, InterruptedException {
128
+ String payload = "{\"data\":[\"Minecraft\",\"@afterblock\",\"object_photo\",\"a white AirPods case from a first year desk\",\"A small object that carried private worlds through public noise.\",null]}";
129
+ String callBody = post(spaceUrl() + "/gradio_api/call/curate_artifact", payload);
130
+ JsonObject call = JsonParser.parseString(callBody).getAsJsonObject();
131
+ String eventId = call.get("event_id").getAsString();
132
+ String stream = get(spaceUrl() + "/gradio_api/call/curate_artifact/" + eventId);
133
+ for (String line : stream.split("\\R")) {
134
+ if (line.startsWith("data: ")) {
135
+ JsonArray outputs = JsonParser.parseString(line.substring(6)).getAsJsonArray();
136
+ JsonObject packet = JsonParser.parseString(outputs.get(5).getAsString()).getAsJsonObject();
137
+ String type = text(packet, "type", "");
138
+ if (!"dreamwall.museum.v1".equals(type)) {
139
+ throw new IOException("unexpected packet type " + type);
140
+ }
141
+ return packet;
142
+ }
143
+ }
144
+ throw new IOException("no data event returned by Gradio");
145
+ }
146
+
147
+ private void placePacketArtifact(Player player, JsonObject packet, boolean placeHere) {
148
+ JsonObject minecraft = packet.getAsJsonObject("minecraft");
149
+ JsonObject artifact = packet.getAsJsonObject("artifact");
150
+ JsonObject coordinates = minecraft.getAsJsonObject("coordinates");
151
+ World world = player.getWorld();
152
+ Location base = placeHere
153
+ ? player.getLocation().getBlock().getLocation().add(0, 0, 2)
154
+ : new Location(world, number(coordinates, "x", player.getLocation().getBlockX()),
155
+ number(coordinates, "y", player.getLocation().getBlockY()),
156
+ number(coordinates, "z", player.getLocation().getBlockZ()));
157
+
158
+ Material pedestalMaterial = materialFromPacket(minecraft);
159
+ base.getBlock().setType(pedestalMaterial);
160
+ base.clone().add(0, 1, 0).getBlock().setType(Material.AMETHYST_BLOCK);
161
+
162
+ Block signBlock = base.clone().add(0, 1, -1).getBlock();
163
+ signBlock.setType(Material.OAK_SIGN);
164
+ String title = text(minecraft, "title", text(artifact, "title", "AfterBlock Relic"));
165
+ String owner = text(minecraft, "owner_handle", text(artifact, "owner_handle", "@unknown"));
166
+ String hall = text(minecraft, "hall", text(artifact, "hall", "Museum"));
167
+ String plaque = text(minecraft, "plaque_text", text(artifact, "plaque_line", "Preserved in AfterBlock"));
168
+ if (signBlock.getState() instanceof Sign sign) {
169
+ sign.setLine(0, trimLine(title));
170
+ sign.setLine(1, trimLine(owner));
171
+ sign.setLine(2, trimLine(hall));
172
+ sign.setLine(3, trimLine(plaque));
173
+ sign.update();
174
+ }
175
+
176
+ ItemStack item = new ItemStack(Material.PAPER);
177
+ ItemMeta meta = item.getItemMeta();
178
+ if (meta != null) {
179
+ meta.setDisplayName(trimLine(title) + " Passport");
180
+ int customModelData = integer(minecraft, "custom_model_data", 730001);
181
+ meta.setCustomModelData(customModelData);
182
+ item.setItemMeta(meta);
183
+ }
184
+ player.getInventory().addItem(item);
185
+ world.spawnParticle(Particle.ENCHANT, base.clone().add(0.5, 1.4, 0.5), 38, 0.45, 0.65, 0.45, 0.015);
186
+ player.sendMessage("Imported " + title + " by " + owner + " into " + hall + ".");
187
+ player.sendMessage("Gave Paper item with CustomModelData " + integer(minecraft, "custom_model_data", 730001) + ".");
188
+ if (!placeHere) {
189
+ player.sendMessage("Placed at packet coordinates " + base.getBlockX() + " " + base.getBlockY() + " " + base.getBlockZ()
190
+ + ". Use /dreamwall import here for a nearby proof.");
191
+ }
192
+ }
193
+
194
  private void placeDemoArtifact(CommandSender sender) {
195
  if (!(sender instanceof Player player)) {
196
  sender.sendMessage("/dreamwall demo must be run by an in-game player.");
 
244
  return response.body();
245
  }
246
 
247
+ private String post(String url, String body) throws IOException, InterruptedException {
248
+ HttpRequest request = HttpRequest.newBuilder()
249
+ .uri(URI.create(url))
250
+ .timeout(Duration.ofSeconds(18))
251
+ .header("User-Agent", "DreamWall-Paper-Bridge/0.1")
252
+ .header("Content-Type", "application/json")
253
+ .POST(HttpRequest.BodyPublishers.ofString(body))
254
+ .build();
255
+ HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
256
+ if (response.statusCode() < 200 || response.statusCode() >= 300) {
257
+ throw new IOException("HTTP " + response.statusCode() + " from " + url);
258
+ }
259
+ return response.body();
260
+ }
261
+
262
+ private Material materialFromPacket(JsonObject minecraft) {
263
+ JsonArray materials = minecraft.has("materials") && minecraft.get("materials").isJsonArray()
264
+ ? minecraft.getAsJsonArray("materials")
265
+ : new JsonArray();
266
+ for (JsonElement element : materials) {
267
+ String name = element.getAsString().toUpperCase(Locale.ROOT).replace("MINECRAFT:", "");
268
+ Material material = Material.matchMaterial(name);
269
+ if (material != null && material.isBlock()) {
270
+ return material;
271
+ }
272
+ }
273
+ return Material.POLISHED_DEEPSLATE;
274
+ }
275
+
276
+ private String text(JsonObject object, String key, String fallback) {
277
+ if (object == null || !object.has(key) || object.get(key).isJsonNull()) {
278
+ return fallback;
279
+ }
280
+ return object.get(key).getAsString();
281
+ }
282
+
283
+ private int integer(JsonObject object, String key, int fallback) {
284
+ if (object == null || !object.has(key) || object.get(key).isJsonNull()) {
285
+ return fallback;
286
+ }
287
+ return object.get(key).getAsInt();
288
+ }
289
+
290
+ private int number(JsonObject object, String key, int fallback) {
291
+ return integer(object, key, fallback);
292
+ }
293
+
294
+ private String trimLine(String value) {
295
+ String cleaned = value == null ? "" : value.replaceAll("\\s+", " ").trim();
296
+ return cleaned.length() <= 15 ? cleaned : cleaned.substring(0, 15);
297
+ }
298
+
299
  private String spaceUrl() {
300
  return getConfig().getString("space-url", "https://build-small-hackathon-dreamwall-mc.hf.space").replaceAll("/+$", "");
301
  }
paper-plugin/src/main/resources/plugin.yml CHANGED
@@ -7,7 +7,7 @@ description: Bridge a Paper server to the DreamWall MC Hugging Face Space.
7
  commands:
8
  dreamwall:
9
  description: Show or test the DreamWall Space bridge.
10
- usage: /dreamwall [fetch|demo]
11
  permission: dreamwall.use
12
  permissions:
13
  dreamwall.use:
 
7
  commands:
8
  dreamwall:
9
  description: Show or test the DreamWall Space bridge.
10
+ usage: /dreamwall [fetch|demo|import]
11
  permission: dreamwall.use
12
  permissions:
13
  dreamwall.use: