package pptest; import eu.mhsl.minecraft.pixelpics.Main; import eu.mhsl.minecraft.pixelpics.render.render.DefaultScreenRenderer; import eu.mhsl.minecraft.pixelpics.render.render.RenderJob; import eu.mhsl.minecraft.pixelpics.render.render.Resolution; import java.awt.image.BufferedImage; import java.io.File; import java.util.*; import java.util.function.Consumer; import javax.imageio.ImageIO; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.type.Bed; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.*; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.util.Transformation; import org.bukkit.util.Vector; import org.joml.AxisAngle4f; import org.joml.Quaternionf; import org.joml.Vector3f; /** Test-only plugin: builds edge-case scenes and renders them through PixelPics into PNGs. */ public class PPTest extends JavaPlugin { record Scene(String name, Consumer build, double camX, double camY, double camZ, double lookX, double lookY, double lookZ) {} private final Map scenes = new LinkedHashMap<>(); private World w; @Override public void onEnable() { // Each scene sits on its own stone platform; the camera is placed relative to the platform origin. scenes.put("held", new Scene("held", this::held, 4.4, 2.6, -8.5, 4.4, 1.0, 1)); scenes.put("equip", new Scene("equip", this::equip, 4, 3, -8.5, 4, 1.0, 1.5)); scenes.put("riding", new Scene("riding", this::riding, 5, 3, -7.5, 5, 1.2, 1)); scenes.put("leash", new Scene("leash", this::leash, 5, 4, -9, 5, 2.5, 2)); scenes.put("displays", new Scene("displays", this::displays, 5, 3, -9, 5, 1.0, 1.5)); scenes.put("pixelblock", new Scene("pixelblock", this::pixelblock, 1.5, 1.9, -1.3, 0.5, 0.5, 0.5)); scenes.put("misc", new Scene("misc", this::misc, 5, 3, -7, 5, 0.8, 1.5)); scenes.put("dbg", new Scene("dbg", this::dbg, 1, 1.6, -3, 1, 0.8, 0.5)); scenes.put("shelf", new Scene("shelf", this::shelf, 3.5, 3.2, -2.5, 0.5, 1.5, 0.5)); scenes.put("shelftop", new Scene("shelftop", this::shelf, 0.52, 6, 0.5, 0.5, 0, 0.5)); scenes.put("rotated", new Scene("rotated", this::rotated, 3, 4.5, -4, 3, 0.5, 2)); scenes.put("rotated2", new Scene("rotated2", this::rotated, -0.5, 3.5, 9.5, -0.5, 0.5, 5)); scenes.put("redstone", new Scene("redstone", this::redstone, 3, 3, -3, 3, 0, 2)); scenes.put("piston", new Scene("piston", this::piston, 4.5, 3.2, -3.5, 5, 1, 1)); scenes.put("pistonback", new Scene("pistonback", this::piston, 5.5, 3.2, 5.5, 5, 1, 1)); scenes.put("pistondown", new Scene("pistondown", this::piston, -1.6, 2.6, -1.6, 0.5, 1.4, 0.5)); scenes.put("pistonup", new Scene("pistonup", this::piston, 0.4, 2.6, -1.6, 2.5, 1.4, 0.5)); scenes.put("vehicles", new Scene("vehicles", this::vehicles, 5, 3, -7, 5, 0.6, 1.5)); } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { w = Bukkit.getWorlds().getFirst(); w.setTime(6000); w.setStorm(false); List todo = args.length == 0 || args[0].equals("all") ? new ArrayList<>(scenes.keySet()) : List.of(args); int i = 0; for (String n : todo) { Scene sc = scenes.get(n); if (sc == null) { getLogger().warning("unknown scene " + n); continue; } int idx = new ArrayList<>(scenes.keySet()).indexOf(n); Location origin = new Location(w, 5000 + idx * 64, 200, 5000); Bukkit.getScheduler().runTaskLater(this, () -> run(sc, origin), 1 + i * 60L); i++; } return true; } private void run(Scene sc, Location o) { try { prepareArea(o); sc.build.accept(o.clone()); } catch (Throwable t) { getLogger().severe("build " + sc.name + " failed: " + t); t.printStackTrace(); } // Let one tick pass (passengers/leashes settle), then render. Bukkit.getScheduler().runTaskLater(this, () -> render(sc, o), 10); } private void render(Scene sc, Location o) { DefaultScreenRenderer r = Main.getInstance().getScreenRenderer(); Location eye = o.clone().add(sc.camX, sc.camY, sc.camZ); Vector look = o.clone().add(sc.lookX, sc.lookY, sc.lookZ).toVector().subtract(eye.toVector()); eye.setDirection(look); RenderJob job = r.prepare(eye, new Resolution(480, 320), null); if (sc.name.equals("dbg")) { try { java.lang.reflect.Field f = DefaultScreenRenderer.class.getDeclaredField("objectBaker"); f.setAccessible(true); var ob = (eu.mhsl.minecraft.pixelpics.render.entity.ObjectBaker) f.get(r); for (var st : job.objects()) { double[] pos = st.toWorld().t; if (Math.abs(pos[0] - o.getX()) > 5 || Math.abs(pos[2] - o.getZ()) > 5) continue; try { var re = ob.bake(st); getLogger().info("dbg obj " + st.kind() + " item=" + st.item() + " ctx=" + st.context() + " -> " + (re == null ? "null" : re.cubes.size() + " cubes " + java.util.Arrays.toString(re.aabbMin))); } catch (Throwable t) { getLogger().warning("dbg bake failed: " + t); t.printStackTrace(); } } } catch (Throwable t) { getLogger().warning("dbg: " + t); } } getLogger().info("scene " + sc.name + ": " + job.entities().size() + " entities, " + job.objects().size() + " objects, " + job.blockEntities().size() + " block entities"); Bukkit.getScheduler().runTaskAsynchronously(this, () -> { try { BufferedImage img = r.execute(job); File f = new File(getDataFolder(), sc.name + ".png"); f.getParentFile().mkdirs(); ImageIO.write(img, "png", f); getLogger().info("WROTE " + f); } catch (Throwable t) { getLogger().severe("render " + sc.name + " failed: " + t); t.printStackTrace(); } }); } /** Clears the area (entities + blocks) and lays a 14x14 stone floor at the origin's y-1. */ private void prepareArea(Location o) { for (int cx = -1; cx <= 2; cx++) for (int cz = -2; cz <= 2; cz++) { Chunk c = w.getChunkAt((o.getBlockX() >> 4) + cx, (o.getBlockZ() >> 4) + cz); c.load(true); c.addPluginChunkTicket(this); } for (Entity e : w.getNearbyEntities(o.clone().add(6, 5, 2), 20, 20, 20)) if (!(e instanceof Player)) e.remove(); for (int x = -6; x < 18; x++) for (int z = -12; z < 12; z++) for (int y = -1; y < 12; y++) w.getBlockAt(o.getBlockX() + x, o.getBlockY() + y, o.getBlockZ() + z) .setType(y == -1 ? Material.SMOOTH_STONE : Material.AIR, false); } // --- helpers --------------------------------------------------------------------------------- private T spawn(Location o, double x, double y, double z, float yaw, Class type) { Location l = o.clone().add(x, y, z); l.setYaw(yaw); T e = w.spawn(l, type, en -> { if (en instanceof Mob m) m.setAI(false); en.setPersistent(false); }); if (e instanceof LivingEntity le) le.setRemoveWhenFarAway(false); return e; } private Entity spawn(Location o, double x, double y, double z, float yaw, EntityType type) { Location l = o.clone().add(x, y, z); l.setYaw(yaw); Entity e = w.spawnEntity(l, type); if (e instanceof Mob m) m.setAI(false); return e; } private static ItemStack it(Material m) { return new ItemStack(m); } private static void hands(Entity e, Material main, Material off) { if (!(e instanceof LivingEntity le) || le.getEquipment() == null) return; if (main != null) le.getEquipment().setItemInMainHand(it(main)); if (off != null) le.getEquipment().setItemInOffHand(it(off)); } // --- scenes ---------------------------------------------------------------------------------- private void held(Location o) { hands(spawn(o, 0, 0, 0, 180, Zombie.class), Material.IRON_SWORD, Material.SHIELD); hands(spawn(o, 1.6, 0, 0, 180, Skeleton.class), Material.BOW, null); hands(spawn(o, 3.2, 0, 0, 180, Piglin.class), Material.GOLDEN_SWORD, null); hands(spawn(o, 4.8, 0, 0, 180, EntityType.MANNEQUIN), Material.DIAMOND_PICKAXE, Material.TORCH); ArmorStand as = spawn(o, 6.4, 0, 0, 180, ArmorStand.class); as.setArms(true); hands(as, Material.TRIDENT, Material.GRASS_BLOCK); hands(spawn(o, 8.0, 0, 0, 180, Drowned.class), Material.TRIDENT, null); hands(spawn(o, 0.8, 0, 2, 180, Pillager.class), Material.CROSSBOW, null); hands(spawn(o, 2.4, 0, 2, 180, Vindicator.class), Material.IRON_AXE, null); hands(spawn(o, 4.0, 0, 2, 180, Fox.class), Material.SWEET_BERRIES, null); hands(spawn(o, 5.6, 0.5, 2, 180, Allay.class), Material.DIAMOND, null); hands(spawn(o, 7.2, 0, 2, 180, Witch.class), Material.POTION, null); hands(spawn(o, 8.8, 0, 2, 180, Villager.class), Material.EMERALD, null); } private void equip(Location o) { LivingEntity m = (LivingEntity) spawn(o, 0, 0, 0, 180, EntityType.MANNEQUIN); m.getEquipment().setChestplate(it(Material.ELYTRA)); m.getEquipment().setHelmet(it(Material.IRON_HELMET)); LivingEntity m2 = (LivingEntity) spawn(o, 1.6, 0, 0, 0, EntityType.MANNEQUIN); // back view: elytra m2.getEquipment().setChestplate(it(Material.ELYTRA)); ArmorStand pumpkin = spawn(o, 3.2, 0, 0, 180, ArmorStand.class); pumpkin.getEquipment().setHelmet(it(Material.CARVED_PUMPKIN)); pumpkin.getEquipment().setChestplate(it(Material.CHAINMAIL_CHESTPLATE)); ArmorStand head = spawn(o, 4.8, 0, 0, 180, ArmorStand.class); head.getEquipment().setHelmet(it(Material.CREEPER_HEAD)); head.setInvisible(true); Zombie z = spawn(o, 6.4, 0, 0, 180, Zombie.class); z.getEquipment().setHelmet(it(Material.GOLDEN_HELMET)); z.getEquipment().setChestplate(it(Material.COPPER_CHESTPLATE)); z.getEquipment().setLeggings(it(Material.NETHERITE_LEGGINGS)); z.getEquipment().setBoots(it(Material.LEATHER_BOOTS)); spawn(o, 8.0, 0, 0, 180, Snowman.class); Wolf wolf = spawn(o, 1, 0, 2.5, 180, Wolf.class); wolf.getEquipment().setItem(EquipmentSlot.BODY, it(Material.WOLF_ARMOR)); Horse h = spawn(o, 4, 0, 3, 90, Horse.class); h.getInventory().setSaddle(it(Material.SADDLE)); h.getInventory().setArmor(it(Material.GOLDEN_HORSE_ARMOR)); Zombie baby = spawn(o, 7.5, 0, 2.5, 180, Zombie.class); baby.setBaby(); baby.getEquipment().setHelmet(it(Material.DIAMOND_HELMET)); hands(baby, Material.STONE_SWORD, null); } private void riding(Location o) { Spider sp = spawn(o, 0, 0, 0, 180, Spider.class); sp.addPassenger(spawn(o, 0, 1, 0, 180, Skeleton.class)); Chicken ch = spawn(o, 2, 0, 0, 180, Chicken.class); Zombie bz = spawn(o, 2, 1, 0, 180, Zombie.class); bz.setBaby(); ch.addPassenger(bz); Horse h = spawn(o, 4.5, 0, 0.5, 180, Horse.class); h.getInventory().setSaddle(it(Material.SADDLE)); h.addPassenger(spawn(o, 4.5, 2, 0.5, 180, EntityType.MANNEQUIN)); Minecart mc = spawn(o, 7, 0, 0, 0, Minecart.class); mc.addPassenger(spawn(o, 7, 1, 0, 180, Villager.class)); Boat b = (Boat) spawn(o, 9.5, 0, 0, 180, EntityType.OAK_BOAT); b.addPassenger(spawn(o, 9.5, 1, 0, 180, Zombie.class)); b.addPassenger(spawn(o, 9.5, 1, 0, 180, Pig.class)); Camel c = spawn(o, 2, 0, 3.5, 90, Camel.class); c.getInventory().setSaddle(it(Material.SADDLE)); c.addPassenger(spawn(o, 2, 2, 3.5, 90, EntityType.MANNEQUIN)); c.addPassenger(spawn(o, 2, 2, 3.5, 90, Villager.class)); Strider st = spawn(o, 6, 0, 3.5, 180, Strider.class); st.setSaddle(true); st.addPassenger(spawn(o, 6, 2, 3.5, 180, Piglin.class)); Boat cb = (Boat) spawn(o, 9, 0, 3.5, 180, EntityType.BIRCH_CHEST_BOAT); cb.addPassenger(spawn(o, 9, 1, 3.5, 180, EntityType.MANNEQUIN)); } private void leash(Location o) { Block fence = o.clone().add(0, 0, 0).getBlock(); fence.setType(Material.OAK_FENCE); LeashHitch knot = w.spawn(fence.getLocation(), LeashHitch.class); Cow cow = spawn(o, 2.5, 0, 0.5, 180, Cow.class); cow.setLeashHolder(knot); LivingEntity holder = (LivingEntity) spawn(o, 5, 0, 0, 180, EntityType.MANNEQUIN); Wolf wolf = spawn(o, 7, 0, 0.5, 180, Wolf.class); wolf.setLeashHolder(holder); HappyGhast g = spawn(o, 4, 4, 5, 180, HappyGhast.class); g.getEquipment().setItem(EquipmentSlot.BODY, it(Material.BLUE_HARNESS)); Boat boat = (Boat) spawn(o, 4, 0, 4, 180, EntityType.OAK_BOAT); try { boat.getClass().getMethod("setLeashHolder", Entity.class).invoke(boat, g); } catch (Throwable t) { getLogger().warning("boat not leashable: " + t); } Llama l1 = spawn(o, 9, 0, 2, 180, Llama.class); Llama l2 = spawn(o, 10.5, 0, 3.5, 180, Llama.class); l2.setLeashHolder(l1); l1.getInventory().setDecor(it(Material.RED_CARPET)); l1.setCarryingChest(true); } private void displays(Location o) { // Text displays: default background, coloured + shadowed + billboard center, multi-line wrapped. TextDisplay t1 = spawn(o, 0.5, 1.5, 0, 0, TextDisplay.class); t1.text(Component.text("Default Text")); TextDisplay t2 = spawn(o, 2.5, 1.5, 0, 0, TextDisplay.class); t2.text(Component.text("Billboard CENTER", NamedTextColor.GOLD)); t2.setBillboard(Display.Billboard.CENTER); t2.setShadowed(true); t2.setBackgroundColor(Color.fromARGB(255, 30, 30, 90)); TextDisplay t3 = spawn(o, 5, 1.5, 0, 180, TextDisplay.class); t3.text(Component.text("Dies ist ein langer Text, der umgebrochen werden sollte weil er breit ist")); t3.setLineWidth(80); // Item displays. ItemDisplay i1 = spawn(o, 7, 1, 0, 0, ItemDisplay.class); i1.setItemStack(it(Material.DIAMOND_SWORD)); i1.setTransformation(new Transformation(new Vector3f(), new AxisAngle4f((float) Math.toRadians(45), 0, 0, 1), new Vector3f(1, 1, 1), new AxisAngle4f())); ItemDisplay i2 = spawn(o, 8.5, 0.5, 0, 30, ItemDisplay.class); i2.setItemStack(it(Material.CHEST)); ItemDisplay i3 = spawn(o, 0.5, 0.5, 2.5, 0, ItemDisplay.class); i3.setItemStack(it(Material.PLAYER_HEAD)); ItemDisplay i4 = spawn(o, 2, 0.5, 2.5, 0, ItemDisplay.class); i4.setItemStack(it(Material.SHIELD)); // Block displays: rotated stairs, scaled chest (block entity!), banner, bed, glass. BlockDisplay b1 = spawn(o, 3.5, 0, 2.5, 0, BlockDisplay.class); b1.setBlock(Bukkit.createBlockData("minecraft:oak_stairs[facing=east]")); b1.setTransformation(new Transformation(new Vector3f(), new Quaternionf().rotateY(0.6f), new Vector3f(0.8f), new Quaternionf())); BlockDisplay b2 = spawn(o, 5, 0, 2.5, 0, BlockDisplay.class); b2.setBlock(Bukkit.createBlockData("minecraft:chest[facing=north]")); BlockDisplay b3 = spawn(o, 6.5, 0, 2.5, 0, BlockDisplay.class); b3.setBlock(Bukkit.createBlockData("minecraft:red_banner")); BlockDisplay b4 = spawn(o, 8, 0, 2.5, 0, BlockDisplay.class); b4.setBlock(Bukkit.createBlockData("minecraft:light_blue_stained_glass")); BlockDisplay b5 = spawn(o, 9.5, 0, 2.5, 0, BlockDisplay.class); b5.setBlock(Bukkit.createBlockData("minecraft:oak_sign")); } /** Mimics PixelBlocks: 1/16-scaled block displays forming a mini build + the placeholder displays. */ private void pixelblock(Location o) { float s = 1 / 16f; String[] palette = {"minecraft:oak_planks", "minecraft:stone_bricks", "minecraft:glass", "minecraft:red_wool", "minecraft:chest[facing=north]", "minecraft:red_bed[part=head,facing=north]", "minecraft:oak_stairs[facing=south]", "minecraft:torch", "minecraft:grass_block"}; Random rnd = new Random(1); for (int x = 0; x < 16; x++) for (int z = 0; z < 16; z++) { int h = 1 + (int) (3 * Math.abs(Math.sin(x * 0.4) * Math.cos(z * 0.3))); for (int y = 0; y < h; y++) { String b = y == h - 1 ? palette[rnd.nextInt(palette.length)] : "minecraft:stone_bricks"; pixel(o, x, y, z, s, Bukkit.createBlockData(b)); } } // Placeholder look (small pixel blocks): end-crystal item cores + glass container, next to it. Location p = o.clone().add(-1.5, 0.5, 0.5); for (int i = 0; i <= 90; i += 90) { ItemDisplay core = w.spawn(p, ItemDisplay.class); core.setRotation(i, 0); core.setItemStack(it(Material.END_CRYSTAL)); Transformation t = core.getTransformation(); t.getScale().set(0.5); core.setTransformation(t); } ItemDisplay glass = w.spawn(p, ItemDisplay.class); glass.setItemStack(it(Material.WHITE_STAINED_GLASS)); Interaction hit = w.spawn(o.clone().add(0.5, 0, 0.5), Interaction.class); hit.setInteractionWidth(1); hit.setInteractionHeight(1); } private void pixel(Location o, int x, int y, int z, float s, BlockData data) { Location l = o.clone().add(x * s + s / 2, y * s + s / 2, z * s + s / 2); BlockDisplay d = w.spawn(l, BlockDisplay.class); d.setBlock(data); Transformation t = d.getTransformation(); t.getScale().set(s); t.getTranslation().set(-s / 2, -s / 2, -s / 2); d.setTransformation(t); } private void misc(Location o) { w.dropItem(o.clone().add(0, 0.2, 0), it(Material.DIAMOND_SWORD), i -> i.setVelocity(new Vector())); w.dropItem(o.clone().add(1, 0.2, 0), it(Material.OAK_LOG), i -> i.setVelocity(new Vector())); w.dropItem(o.clone().add(2, 0.2, 0), new ItemStack(Material.COBBLESTONE, 32), i -> i.setVelocity(new Vector())); w.dropItem(o.clone().add(3, 0.2, 0), it(Material.APPLE), i -> i.setVelocity(new Vector())); FallingBlock fb = w.spawn(o.clone().add(4.5, 0.5, 0.5), FallingBlock.class, f -> f.setGravity(false)); fb.setBlockData(Bukkit.createBlockData("minecraft:sand")); TNTPrimed tnt = w.spawn(o.clone().add(6, 0, 0.5), TNTPrimed.class); tnt.setFuseTicks(10000); tnt.setGravity(false); w.spawn(o.clone().add(8, 0, 0.5), EnderCrystal.class); Sheep sh = spawn(o, 0.5, 0, 3, 180, Sheep.class); sh.setSheared(true); Sheep sh2 = spawn(o, 2.5, 0, 3, 180, Sheep.class); sh2.setColor(DyeColor.LIME); ArmorStand small = spawn(o, 4.5, 0, 3, 180, ArmorStand.class); small.setSmall(true); small.getEquipment().setHelmet(it(Material.LEATHER_HELMET)); spawn(o, 6, 0, 3, 180, EntityType.EXPERIENCE_ORB); Arrow a = w.spawn(o.clone().add(7, 0.5, 3), Arrow.class, ar -> ar.setGravity(false)); a.setVelocity(new Vector(1, -0.4, 0)); Trident tr = w.spawn(o.clone().add(9, 0.5, 3), Trident.class, ar -> ar.setGravity(false)); tr.setVelocity(new Vector(0, -1, 0.2)); spawn(o, 10, 0, 0, 180, TropicalFish.class); } private void dbg(Location o) { TextDisplay t1 = spawn(o, 0, 1.2, 0, 0, TextDisplay.class); t1.text(Component.text("Default Text")); ItemDisplay i4 = spawn(o, 2, 0.5, 0.5, 0, ItemDisplay.class); i4.setItemStack(it(Material.SHIELD)); getLogger().info("dbg text=" + t1.isValid() + " opacity=" + t1.getTextOpacity() + " shield=" + i4.getItemStack()); } /** World blocks: a log column with shelf mushrooms (26.x) on all four sides, both growth stages. */ private void shelf(Location o) { for (int y = 0; y < 3; y++) o.clone().add(0, y, 0).getBlock().setType(Material.OAK_LOG, false); o.clone().add(0, 0, -3).getBlock().setType(Material.DIAMOND_BLOCK, false); // north marker o.clone().add(3, 0, 0).getBlock().setType(Material.GOLD_BLOCK, false); // east marker String[] f = {"north", "south", "east", "west"}; int[][] off = {{0, -1}, {0, 1}, {1, 0}, {-1, 0}}; for (int i = 0; i < 4; i++) { for (int age = 0; age < 2; age++) { o.clone().add(off[i][0], 1 + age, off[i][1]).getBlock().setBlockData( Bukkit.createBlockData("minecraft:shelf_mushroom[age=" + age + ",facing=" + f[i] + "]"), false); } } } /** Blockstate y-rotations: ladders, stairs, wall torches, furnaces, logs on all sides of stone pillars. */ private void rotated(Location o) { String[] f = {"north", "south", "east", "west"}; int[][] off = {{0, -1}, {0, 1}, {1, 0}, {-1, 0}}; String[] kinds = {"ladder[facing=%s]", "wall_torch[facing=%s]", "oak_stairs[facing=%s]", "furnace[facing=%s]"}; for (int k = 0; k < kinds.length; k++) { Location c = o.clone().add(k * 3 - 1, 0, 2); c.getBlock().setType(Material.STONE, false); c.clone().add(0, 1, 0).getBlock().setType(Material.STONE, false); for (int i = 0; i < 4; i++) { c.clone().add(off[i][0], k == 2 || k == 3 ? 0 : 1, off[i][1]).getBlock().setBlockData( Bukkit.createBlockData("minecraft:" + String.format(kinds[k], f[i])), false); } } // Fence with a single connection to the east (stone post at +x), door facing east, chest-free check. o.clone().add(-3, 0, 5).getBlock().setBlockData(Bukkit.createBlockData("minecraft:oak_fence[east=true]"), false); o.clone().add(-2, 0, 5).getBlock().setType(Material.STONE, false); o.clone().add(1, 0, 5).getBlock().setBlockData( Bukkit.createBlockData("minecraft:oak_door[facing=east,half=lower,hinge=left,open=false]"), false); o.clone().add(1, 1, 5).getBlock().setBlockData( Bukkit.createBlockData("minecraft:oak_door[facing=east,half=upper,hinge=left,open=false]"), false); o.clone().add(9, 0, 2).getBlock().setBlockData(Bukkit.createBlockData("minecraft:oak_log[axis=x]"), false); o.clone().add(9, 0, 4).getBlock().setBlockData(Bukkit.createBlockData("minecraft:oak_log[axis=z]"), false); } /** Redstone wire (dot, line, corner, climbing a wall, powered/unpowered) next to glass and panes. */ private void redstone(Location o) { java.util.function.BiConsumer put = (p, data) -> o.clone().add(p[0], p[1], p[2]).getBlock() .setBlockData(Bukkit.createBlockData("minecraft:" + data), false); put.accept(new int[] {0, 0, 0}, "redstone_wire[power=0]"); for (int x = 1; x <= 4; x++) put.accept(new int[] {x, 0, 0}, "redstone_wire[east=side,west=side,power=" + (x * 3) + "]"); put.accept(new int[] {5, 0, 0}, "redstone_wire[west=side,north=side,power=15]"); put.accept(new int[] {5, 0, -1}, "redstone_wire[south=side,north=up,power=15]"); put.accept(new int[] {5, 0, -2}, "stone"); put.accept(new int[] {5, 1, -2}, "redstone_wire[south=side,power=15]"); put.accept(new int[] {0, 0, 3}, "glass"); put.accept(new int[] {1, 0, 3}, "red_stained_glass"); put.accept(new int[] {2, 0, 3}, "glass_pane[east=true,west=false]"); put.accept(new int[] {3, 0, 3}, "blue_stained_glass_pane[east=true,west=true]"); put.accept(new int[] {4, 0, 3}, "blue_stained_glass_pane[west=true,north=true]"); } /** x/y blockstate rotations without uvlock: pistons in all six directions, observers, dispensers, logs. */ private void piston(Location o) { String[] f = {"down", "up", "north", "south", "east", "west"}; for (int i = 0; i < f.length; i++) { o.clone().add(i * 2, 1, 0).getBlock().setBlockData( Bukkit.createBlockData("minecraft:piston[facing=" + f[i] + ",extended=false]"), false); o.clone().add(i * 2, 1, 2).getBlock().setBlockData( Bukkit.createBlockData("minecraft:observer[facing=" + f[i] + "]"), false); o.clone().add(i * 2, 0, 2).getBlock().setBlockData( Bukkit.createBlockData("minecraft:dispenser[facing=" + f[i] + "]"), false); } o.clone().add(0, 0, -2).getBlock().setBlockData(Bukkit.createBlockData("minecraft:oak_log[axis=x]"), false); o.clone().add(2, 0, -2).getBlock().setBlockData(Bukkit.createBlockData("minecraft:oak_log[axis=z]"), false); o.clone().add(4, 0, -2).getBlock().setBlockData(Bukkit.createBlockData("minecraft:furnace[facing=north]"), false); } private void vehicles(Location o) { spawn(o, 0, 0, 0, 0, EntityType.CHEST_MINECART); spawn(o, 1.8, 0, 0, 90, EntityType.FURNACE_MINECART); spawn(o, 3.6, 0, 0, 0, EntityType.HOPPER_MINECART); spawn(o, 5.4, 0, 0, 0, EntityType.TNT_MINECART); spawn(o, 7.2, 0, 0, 0, EntityType.SPAWNER_MINECART); spawn(o, 9, 0, 0, 0, EntityType.COMMAND_BLOCK_MINECART); spawn(o, 0.5, 0, 3, 30, EntityType.PALE_OAK_BOAT); spawn(o, 3, 0, 3, 60, EntityType.BAMBOO_CHEST_RAFT); spawn(o, 6, 0, 3, 90, EntityType.CHERRY_CHEST_BOAT); spawn(o, 9, 0, 3, 120, EntityType.MANGROVE_BOAT); } }