mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-08-27 04:37:47 +00:00
Object fixes / PS
This commit is contained in:
@@ -1222,8 +1222,9 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
|||||||
return generatorResolver.resolveDefaultBiomeProvider(worldName, id, () -> super.getDefaultBiomeProvider(worldName, id));
|
return generatorResolver.resolveDefaultBiomeProvider(worldName, id, () -> super.getDefaultBiomeProvider(worldName, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
|
public ChunkGenerator getDefaultWorldGenerator(@NotNull String worldName, @Nullable String id) {
|
||||||
return generatorResolver.resolveDefaultWorldGenerator(worldName, id);
|
return generatorResolver.resolveDefaultWorldGenerator(worldName, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-1
@@ -62,6 +62,7 @@ public final class IrisWorldGeneratorResolver {
|
|||||||
private static final int VALIDATION_STABILITY_ATTEMPTS = 2;
|
private static final int VALIDATION_STABILITY_ATTEMPTS = 2;
|
||||||
private static final Object SNAPSHOT_VALIDATION_LOCK = new Object();
|
private static final Object SNAPSHOT_VALIDATION_LOCK = new Object();
|
||||||
private static final String IRIS_DIMENSION_NAMESPACE = "iris";
|
private static final String IRIS_DIMENSION_NAMESPACE = "iris";
|
||||||
|
private static final String PLOT_SQUARED_DISCOVERY_WORLD = "CheckingPlotSquaredGenerator";
|
||||||
|
|
||||||
private final VolmitPlugin plugin;
|
private final VolmitPlugin plugin;
|
||||||
|
|
||||||
@@ -340,7 +341,12 @@ public final class IrisWorldGeneratorResolver {
|
|||||||
return fallback.get();
|
return fallback.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChunkGenerator resolveDefaultWorldGenerator(String worldName, String id) {
|
@Nullable
|
||||||
|
public ChunkGenerator resolveDefaultWorldGenerator(String worldName, @Nullable String id) {
|
||||||
|
if (isPlotSquaredGeneratorDiscoveryProbe(worldName, id)) {
|
||||||
|
Iris.debug("Ignoring PlotSquared generator discovery probe");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (isGeneratorDiscoveryProbe(worldName, id)) {
|
if (isGeneratorDiscoveryProbe(worldName, id)) {
|
||||||
Iris.debug("Generator discovery probe for loaded world " + worldName);
|
Iris.debug("Generator discovery probe for loaded world " + worldName);
|
||||||
return new IrisProbeChunkGenerator(worldName);
|
return new IrisProbeChunkGenerator(worldName);
|
||||||
@@ -370,6 +376,10 @@ public final class IrisWorldGeneratorResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isPlotSquaredGeneratorDiscoveryProbe(String worldName, String id) {
|
||||||
|
return PLOT_SQUARED_DISCOVERY_WORLD.equals(worldName) && id != null && id.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Multiverse-Core probes every enabled plugin by asking for a generator with an empty dimension
|
* Multiverse-Core probes every enabled plugin by asking for a generator with an empty dimension
|
||||||
* id and the name of a world that is already loaded. Bukkit never creates a world that is
|
* id and the name of a world that is already loaded. Bukkit never creates a world that is
|
||||||
|
|||||||
+30
-10
@@ -42,7 +42,6 @@ import art.arcane.iris.platform.bukkit.BukkitBlockState;
|
|||||||
import art.arcane.iris.spi.PlatformBlockState;
|
import art.arcane.iris.spi.PlatformBlockState;
|
||||||
import art.arcane.volmlib.util.data.Cuboid;
|
import art.arcane.volmlib.util.data.Cuboid;
|
||||||
import art.arcane.iris.util.common.data.IrisCustomData;
|
import art.arcane.iris.util.common.data.IrisCustomData;
|
||||||
import art.arcane.iris.util.common.data.registry.Materials;
|
|
||||||
import art.arcane.iris.util.common.director.DirectorExecutor;
|
import art.arcane.iris.util.common.director.DirectorExecutor;
|
||||||
import art.arcane.iris.util.common.director.specialhandlers.NullableDimensionHandler;
|
import art.arcane.iris.util.common.director.specialhandlers.NullableDimensionHandler;
|
||||||
import art.arcane.iris.util.common.plugin.VolmitSender;
|
import art.arcane.iris.util.common.plugin.VolmitSender;
|
||||||
@@ -89,6 +88,23 @@ import art.arcane.iris.core.localization.BukkitCommandMessagesExtended;
|
|||||||
import art.arcane.iris.core.localization.RuntimeUiMessages;
|
import art.arcane.iris.core.localization.RuntimeUiMessages;
|
||||||
@Director(name = "object", aliases = "o", origin = DirectorOrigin.PLAYER, description = "Iris object manipulation", descriptionKey = "iris.director.commandobject.director.iris_object_manipulation")
|
@Director(name = "object", aliases = "o", origin = DirectorOrigin.PLAYER, description = "Iris object manipulation", descriptionKey = "iris.director.commandobject.director.iris_object_manipulation")
|
||||||
public class CommandObject implements DirectorExecutor {
|
public class CommandObject implements DirectorExecutor {
|
||||||
|
static final Set<Material> PASTE_TRANSPARENT_BLOCKS = Set.of(
|
||||||
|
Material.AIR,
|
||||||
|
Material.CAVE_AIR,
|
||||||
|
Material.VOID_AIR,
|
||||||
|
Material.SHORT_GRASS,
|
||||||
|
Material.SNOW,
|
||||||
|
Material.VINE,
|
||||||
|
Material.TORCH,
|
||||||
|
Material.DEAD_BUSH,
|
||||||
|
Material.POPPY,
|
||||||
|
Material.DANDELION
|
||||||
|
);
|
||||||
|
|
||||||
|
static boolean isPasteTarget(Material material) {
|
||||||
|
return !PASTE_TRANSPARENT_BLOCKS.contains(material);
|
||||||
|
}
|
||||||
|
|
||||||
@Director(description = "Open an object studio world (grid of every object; dimension optional, defaults to all packs)", descriptionKey = "iris.director.commandobject.director.open_object_studio_world_grid_every_object_dimension_optional_defaults_all_packs", sync = true)
|
@Director(description = "Open an object studio world (grid of every object; dimension optional, defaults to all packs)", descriptionKey = "iris.director.commandobject.director.open_object_studio_world_grid_every_object_dimension_optional_defaults_all_packs", sync = true)
|
||||||
public void studio(
|
public void studio(
|
||||||
@Param(defaultValue = "null", description = "Optional dimension whose object pack to lay out; omit to aggregate objects from every pack", descriptionKey = "iris.director.commandobject.param.optional_dimension_whose_object_pack_lay_out_omit_aggregate_objects_from_every", aliases = "dim", customHandler = NullableDimensionHandler.class)
|
@Param(defaultValue = "null", description = "Optional dimension whose object pack to lay out; omit to aggregate objects from every pack", descriptionKey = "iris.director.commandobject.param.optional_dimension_whose_object_pack_lay_out_omit_aggregate_objects_from_every", aliases = "dim", customHandler = NullableDimensionHandler.class)
|
||||||
@@ -179,9 +195,6 @@ public class CommandObject implements DirectorExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Set<Material> skipBlocks = Set.of(Materials.GRASS, Material.SNOW, Material.VINE, Material.TORCH, Material.DEAD_BUSH,
|
|
||||||
Material.POPPY, Material.DANDELION);
|
|
||||||
|
|
||||||
public static IObjectPlacer createPlacer(World world, Map<Block, BlockData> futureBlockChanges, Engine targetEngine) {
|
public static IObjectPlacer createPlacer(World world, Map<Block, BlockData> futureBlockChanges, Engine targetEngine) {
|
||||||
return new IObjectPlacer() {
|
return new IObjectPlacer() {
|
||||||
@Override
|
@Override
|
||||||
@@ -545,15 +558,20 @@ public class CommandObject implements DirectorExecutor {
|
|||||||
scale = maxScale;
|
scale = maxScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
sender().playSound(Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1f, 1.5f);
|
|
||||||
|
|
||||||
IrisObjectPlacement placement = new IrisObjectPlacement();
|
IrisObjectPlacement placement = new IrisObjectPlacement();
|
||||||
placement.setRotation(IrisObjectRotation.of(0, rotate, 0));
|
placement.setRotation(IrisObjectRotation.of(0, rotate, 0));
|
||||||
|
|
||||||
VolmitSender commandSender = sender();
|
VolmitSender commandSender = sender();
|
||||||
Player player = player();
|
Player player = player();
|
||||||
ItemStack wand = player.getInventory().getItemInMainHand();
|
ItemStack wand = player.getInventory().getItemInMainHand();
|
||||||
Location block = player.getTargetBlock(skipBlocks, 256).getLocation().clone().add(0, 1, 0);
|
Block targetBlock = player.getTargetBlock(PASTE_TRANSPARENT_BLOCKS, 256);
|
||||||
|
if (!isPasteTarget(targetBlock.getType())) {
|
||||||
|
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_PLEASE_LOOK_AT_ANY_BLOCK_NOT_AT_SKY));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Location block = targetBlock.getLocation().clone().add(0, 1, 0);
|
||||||
|
|
||||||
|
commandSender.playSound(Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1f, 1.5f);
|
||||||
|
|
||||||
Map<Block, BlockData> futureChanges = new HashMap<>();
|
Map<Block, BlockData> futureChanges = new HashMap<>();
|
||||||
|
|
||||||
@@ -573,9 +591,11 @@ public class CommandObject implements DirectorExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onPlayerThread(player, () -> {
|
onPlayerThread(player, () -> {
|
||||||
Vector center = new Vector(placed.getCenter().getX(), placed.getCenter().getY(), placed.getCenter().getZ());
|
ObjectPasteBounds bounds = ObjectPasteBounds.resolve(placed, placement.getRotation(), block.getBlockX(),
|
||||||
ItemStack newWand = WandSVC.createWand(block.clone().subtract(center).add(placed.getW() - 1,
|
block.getBlockY() + placed.getCenter().getBlockY(), block.getBlockZ());
|
||||||
placed.getH() + center.getY() - 1, placed.getD() - 1), block.clone().subtract(center.clone().setY(0)));
|
Location minimum = new Location(block.getWorld(), bounds.minX(), bounds.minY(), bounds.minZ());
|
||||||
|
Location maximum = new Location(block.getWorld(), bounds.maxX(), bounds.maxY(), bounds.maxZ());
|
||||||
|
ItemStack newWand = WandSVC.createWand(maximum, minimum);
|
||||||
if (WandSVC.isWand(wand)) {
|
if (WandSVC.isWand(wand)) {
|
||||||
player.getInventory().setItemInMainHand(newWand);
|
player.getInventory().setItemInMainHand(newWand);
|
||||||
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_UPDATED_WAND_OBJECTS_IOB, MessageArgument.untrusted("value", placed.getLoadKey())));
|
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_UPDATED_WAND_OBJECTS_IOB, MessageArgument.untrusted("value", placed.getLoadKey())));
|
||||||
|
|||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
package art.arcane.iris.core.commands;
|
||||||
|
|
||||||
|
import art.arcane.iris.engine.object.IrisObject;
|
||||||
|
import art.arcane.iris.engine.object.IrisObjectRotation;
|
||||||
|
import art.arcane.iris.util.common.math.IrisBlockVector;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
record ObjectPasteBounds(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
|
||||||
|
static ObjectPasteBounds resolve(
|
||||||
|
IrisObject object,
|
||||||
|
IrisObjectRotation rotation,
|
||||||
|
int anchorX,
|
||||||
|
int anchorY,
|
||||||
|
int anchorZ
|
||||||
|
) {
|
||||||
|
IrisObject placedObject = Objects.requireNonNull(object, "object");
|
||||||
|
IrisObjectRotation placementRotation = Objects.requireNonNull(rotation, "rotation");
|
||||||
|
int[] xOffsets = {
|
||||||
|
-placedObject.getCenter().getBlockX(),
|
||||||
|
placedObject.getW() - 1 - placedObject.getCenter().getBlockX()
|
||||||
|
};
|
||||||
|
int[] yOffsets = {
|
||||||
|
-placedObject.getCenter().getBlockY(),
|
||||||
|
placedObject.getH() - 1 - placedObject.getCenter().getBlockY()
|
||||||
|
};
|
||||||
|
int[] zOffsets = {
|
||||||
|
-placedObject.getCenter().getBlockZ(),
|
||||||
|
placedObject.getD() - 1 - placedObject.getCenter().getBlockZ()
|
||||||
|
};
|
||||||
|
int minimumX = Integer.MAX_VALUE;
|
||||||
|
int minimumY = Integer.MAX_VALUE;
|
||||||
|
int minimumZ = Integer.MAX_VALUE;
|
||||||
|
int maximumX = Integer.MIN_VALUE;
|
||||||
|
int maximumY = Integer.MIN_VALUE;
|
||||||
|
int maximumZ = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
for (int xOffset : xOffsets) {
|
||||||
|
for (int yOffset : yOffsets) {
|
||||||
|
for (int zOffset : zOffsets) {
|
||||||
|
IrisBlockVector transformed = placementRotation.rotate(new IrisBlockVector(xOffset, yOffset, zOffset));
|
||||||
|
int worldX = anchorX + (int) Math.round(transformed.getX());
|
||||||
|
int worldY = anchorY + (int) Math.round(transformed.getY());
|
||||||
|
int worldZ = anchorZ + (int) Math.round(transformed.getZ());
|
||||||
|
minimumX = Math.min(minimumX, worldX);
|
||||||
|
minimumY = Math.min(minimumY, worldY);
|
||||||
|
minimumZ = Math.min(minimumZ, worldZ);
|
||||||
|
maximumX = Math.max(maximumX, worldX);
|
||||||
|
maximumY = Math.max(maximumY, worldY);
|
||||||
|
maximumZ = Math.max(maximumZ, worldZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ObjectPasteBounds(minimumX, minimumY, minimumZ, maximumX, maximumY, maximumZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
+43
-2
@@ -26,6 +26,7 @@ import java.util.Random;
|
|||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
@@ -231,7 +232,8 @@ public class IrisWorldGeneratorResolverTest {
|
|||||||
int resolverEnd = source.indexOf("private ChunkGenerator resolveFrozenWorldGenerator(", resolverStart);
|
int resolverEnd = source.indexOf("private ChunkGenerator resolveFrozenWorldGenerator(", resolverStart);
|
||||||
String resolver = source.substring(resolverStart, resolverEnd);
|
String resolver = source.substring(resolverStart, resolverEnd);
|
||||||
|
|
||||||
int probe = resolver.indexOf("isGeneratorDiscoveryProbe(worldName, id)");
|
int plotSquaredProbe = resolver.indexOf("isPlotSquaredGeneratorDiscoveryProbe(worldName, id)");
|
||||||
|
int probe = resolver.indexOf("isGeneratorDiscoveryProbe(worldName, id)", plotSquaredProbe);
|
||||||
int readiness = resolver.indexOf("IrisStartupValidation.requireWorldCreationReady()");
|
int readiness = resolver.indexOf("IrisStartupValidation.requireWorldCreationReady()");
|
||||||
int duplicateGuard = resolver.indexOf("requireWorldKeyAvailable(worldName, worldKey)");
|
int duplicateGuard = resolver.indexOf("requireWorldKeyAvailable(worldName, worldKey)");
|
||||||
int ownership = resolver.indexOf("requireOwnedWorld(worldName, levelRoot, worldKey)");
|
int ownership = resolver.indexOf("requireOwnedWorld(worldName, levelRoot, worldKey)");
|
||||||
@@ -241,7 +243,8 @@ public class IrisWorldGeneratorResolverTest {
|
|||||||
int shutdown = resolver.indexOf("Bukkit.shutdown()", report);
|
int shutdown = resolver.indexOf("Bukkit.shutdown()", report);
|
||||||
int rethrow = resolver.indexOf("throw failure", shutdown);
|
int rethrow = resolver.indexOf("throw failure", shutdown);
|
||||||
|
|
||||||
assertTrue(probe >= 0);
|
assertTrue(plotSquaredProbe >= 0);
|
||||||
|
assertTrue(probe > plotSquaredProbe);
|
||||||
assertTrue(readiness > probe);
|
assertTrue(readiness > probe);
|
||||||
assertTrue(duplicateGuard > readiness);
|
assertTrue(duplicateGuard > readiness);
|
||||||
assertTrue(ownership > duplicateGuard);
|
assertTrue(ownership > duplicateGuard);
|
||||||
@@ -256,6 +259,44 @@ public class IrisWorldGeneratorResolverTest {
|
|||||||
assertEquals(shutdownScope, resolverStart + shutdown, source.lastIndexOf("Bukkit.shutdown()"));
|
assertEquals(shutdownScope, resolverStart + shutdown, source.lastIndexOf("Bukkit.shutdown()"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void plotSquaredDiscoveryProbeIsIgnoredQuietly() {
|
||||||
|
try (MockedStatic<Bukkit> bukkit = mockStatic(Bukkit.class);
|
||||||
|
MockedStatic<Iris> iris = mockStatic(Iris.class)) {
|
||||||
|
bukkit.when(() -> Bukkit.getWorld("CheckingPlotSquaredGenerator")).thenReturn(null);
|
||||||
|
|
||||||
|
ChunkGenerator probe = new IrisWorldGeneratorResolver(null)
|
||||||
|
.resolveDefaultWorldGenerator("CheckingPlotSquaredGenerator", "");
|
||||||
|
|
||||||
|
assertNull("Iris cannot be used as a PlotSquared base generator", probe);
|
||||||
|
bukkit.verify(Bukkit::shutdown, never());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void plotSquaredSentinelWithDimensionIdUsesNormalOwnershipChecks() throws Exception {
|
||||||
|
File worldContainer = temporaryFolder.newFolder("plotsquared-non-probe");
|
||||||
|
File levelRoot = new File(worldContainer, "world");
|
||||||
|
assertTrue(levelRoot.mkdirs());
|
||||||
|
|
||||||
|
try (MockedStatic<Bukkit> bukkit = mockStatic(Bukkit.class);
|
||||||
|
MockedStatic<Iris> iris = mockStatic(Iris.class)) {
|
||||||
|
Server server = mock(Server.class);
|
||||||
|
when(server.getLevelDirectory()).thenReturn(levelRoot.toPath());
|
||||||
|
bukkit.when(Bukkit::getServer).thenReturn(server);
|
||||||
|
bukkit.when(Bukkit::getWorldContainer).thenReturn(worldContainer);
|
||||||
|
bukkit.when(Bukkit::getWorlds).thenReturn(List.of());
|
||||||
|
|
||||||
|
IllegalStateException failure = assertThrows(
|
||||||
|
IllegalStateException.class,
|
||||||
|
() -> new IrisWorldGeneratorResolver(null)
|
||||||
|
.resolveDefaultWorldGenerator("CheckingPlotSquaredGenerator", "overworld"));
|
||||||
|
|
||||||
|
assertTrue(failure.getMessage(), failure.getMessage().contains("CheckingPlotSquaredGenerator"));
|
||||||
|
bukkit.verify(Bukkit::shutdown, never());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void discoveryProbeOfLoadedWorldReturnsInertGeneratorQuietly() {
|
public void discoveryProbeOfLoadedWorldReturnsInertGeneratorQuietly() {
|
||||||
try (MockedStatic<Bukkit> bukkit = mockStatic(Bukkit.class);
|
try (MockedStatic<Bukkit> bukkit = mockStatic(Bukkit.class);
|
||||||
|
|||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package art.arcane.iris.core.commands;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class CommandObjectPasteRaycastTest {
|
||||||
|
@Test
|
||||||
|
public void rejectsEveryAirVariantAndConfiguredFoliageAsPasteTargets() {
|
||||||
|
assertFalse(CommandObject.isPasteTarget(Material.AIR));
|
||||||
|
assertFalse(CommandObject.isPasteTarget(Material.CAVE_AIR));
|
||||||
|
assertFalse(CommandObject.isPasteTarget(Material.VOID_AIR));
|
||||||
|
assertFalse(CommandObject.isPasteTarget(Material.SHORT_GRASS));
|
||||||
|
assertFalse(CommandObject.isPasteTarget(Material.SNOW));
|
||||||
|
assertFalse(CommandObject.isPasteTarget(Material.VINE));
|
||||||
|
assertFalse(CommandObject.isPasteTarget(Material.TORCH));
|
||||||
|
assertFalse(CommandObject.isPasteTarget(Material.DEAD_BUSH));
|
||||||
|
assertFalse(CommandObject.isPasteTarget(Material.POPPY));
|
||||||
|
assertFalse(CommandObject.isPasteTarget(Material.DANDELION));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void acceptsSolidBlocksAsPasteTargets() {
|
||||||
|
assertTrue(CommandObject.isPasteTarget(Material.STONE));
|
||||||
|
}
|
||||||
|
}
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
package art.arcane.iris.core.commands;
|
||||||
|
|
||||||
|
import art.arcane.iris.engine.object.IrisObject;
|
||||||
|
import art.arcane.iris.engine.object.IrisObjectRotation;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class ObjectPasteBoundsTest {
|
||||||
|
private static final int ANCHOR_X = 100;
|
||||||
|
private static final int ANCHOR_Y = 65;
|
||||||
|
private static final int ANCHOR_Z = -30;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void preservesPasteBoundsWithoutRotation() {
|
||||||
|
ObjectPasteBounds bounds = resolve(0);
|
||||||
|
|
||||||
|
assertEquals(new ObjectPasteBounds(98, 64, -31, 101, 66, -30), bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void followsQuarterTurnPasteFootprint() {
|
||||||
|
ObjectPasteBounds bounds = resolve(90);
|
||||||
|
|
||||||
|
assertEquals(new ObjectPasteBounds(99, 64, -31, 100, 66, -28), bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void followsHalfTurnPasteOffset() {
|
||||||
|
ObjectPasteBounds bounds = resolve(180);
|
||||||
|
|
||||||
|
assertEquals(new ObjectPasteBounds(99, 64, -30, 102, 66, -29), bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void followsThreeQuarterTurnPasteOffset() {
|
||||||
|
ObjectPasteBounds bounds = resolve(270);
|
||||||
|
|
||||||
|
assertEquals(new ObjectPasteBounds(100, 64, -32, 101, 66, -29), bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enclosesRoundedArbitraryAnglePasteFootprint() {
|
||||||
|
ObjectPasteBounds bounds = resolve(45);
|
||||||
|
|
||||||
|
assertEquals(new ObjectPasteBounds(98, 64, -31, 101, 66, -29), bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObjectPasteBounds resolve(int rotation) {
|
||||||
|
IrisObject object = new IrisObject(4, 3, 2);
|
||||||
|
return ObjectPasteBounds.resolve(object, IrisObjectRotation.of(0, rotation, 0), ANCHOR_X, ANCHOR_Y, ANCHOR_Z);
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
-11
@@ -38,13 +38,21 @@ public final class ModdedWorkspaceGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static File writeWorkspace(IrisData data, File folder) throws IOException {
|
public static File writeWorkspace(IrisData data, File folder) throws IOException {
|
||||||
JSONObject workspaceConfig = buildWorkspace(data);
|
return writeWorkspace(data, folder, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File writeWorkspace(IrisData data, File folder, boolean immediateSchemas) throws IOException {
|
||||||
|
JSONObject workspaceConfig = buildWorkspace(data, immediateSchemas);
|
||||||
File workspace = new File(folder, folder.getName() + ".code-workspace");
|
File workspace = new File(folder, folder.getName() + ".code-workspace");
|
||||||
IO.writeAll(workspace, workspaceConfig.toString(4));
|
IO.writeAll(workspace, workspaceConfig.toString(4));
|
||||||
return workspace;
|
return workspace;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JSONObject buildWorkspace(IrisData data) {
|
public static JSONObject buildWorkspace(IrisData data) {
|
||||||
|
return buildWorkspace(data, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JSONObject buildWorkspace(IrisData data, boolean immediateSchemas) {
|
||||||
JSONObject ws = new JSONObject();
|
JSONObject ws = new JSONObject();
|
||||||
JSONArray folders = new JSONArray();
|
JSONArray folders = new JSONArray();
|
||||||
JSONObject folder = new JSONObject();
|
JSONObject folder = new JSONObject();
|
||||||
@@ -74,17 +82,17 @@ public final class ModdedWorkspaceGenerator {
|
|||||||
json.put("editor.suggest.insertMode", "replace");
|
json.put("editor.suggest.insertMode", "replace");
|
||||||
settings.put("[json]", json);
|
settings.put("[json]", json);
|
||||||
settings.put("json.maxItemsComputed", 30000);
|
settings.put("json.maxItemsComputed", 30000);
|
||||||
settings.put("json.schemas", buildSchemas(data));
|
settings.put("json.schemas", buildSchemas(data, immediateSchemas));
|
||||||
ws.put("settings", settings);
|
ws.put("settings", settings);
|
||||||
return ws;
|
return ws;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JSONArray buildSchemas(IrisData data) {
|
private static JSONArray buildSchemas(IrisData data, boolean immediateSchemas) {
|
||||||
JSONArray schemas = new JSONArray();
|
JSONArray schemas = new JSONArray();
|
||||||
|
|
||||||
for (ResourceLoader<?> loader : data.getLoaders().v()) {
|
for (ResourceLoader<?> loader : data.getLoaders().v()) {
|
||||||
if (loader.supportsSchemas()) {
|
if (loader.supportsSchemas()) {
|
||||||
schemas.put(loader.buildSchema());
|
schemas.put(immediateSchemas ? loader.buildSchemaImmediately() : loader.buildSchema());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,14 +110,21 @@ public final class ModdedWorkspaceGenerator {
|
|||||||
entry.put("url", "./.iris/schema/snippet/" + snipType + "-schema.json");
|
entry.put("url", "./.iris/schema/snippet/" + snipType + "-schema.json");
|
||||||
schemas.put(entry);
|
schemas.put(entry);
|
||||||
File schemaFile = new File(data.getDataFolder(), ".iris/schema/snippet/" + snipType + "-schema.json");
|
File schemaFile = new File(data.getDataFolder(), ".iris/schema/snippet/" + snipType + "-schema.json");
|
||||||
J.attemptAsync(() -> {
|
if (immediateSchemas) {
|
||||||
try {
|
IO.writeAll(schemaFile, new SchemaBuilder(snippetClass, data).construct().toString(4));
|
||||||
IO.writeAll(schemaFile, new SchemaBuilder(snippetClass, data).construct().toString(4));
|
} else {
|
||||||
} catch (Throwable e) {
|
J.attemptAsync(() -> {
|
||||||
IrisLogging.reportError(e);
|
try {
|
||||||
}
|
IO.writeAll(schemaFile, new SchemaBuilder(snippetClass, data).construct().toString(4));
|
||||||
});
|
} catch (Throwable e) {
|
||||||
|
IrisLogging.reportError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
if (immediateSchemas) {
|
||||||
|
throw new IllegalStateException("Could not write snippet schema for " + snippetClass.getName(), e);
|
||||||
|
}
|
||||||
IrisLogging.reportError(e);
|
IrisLogging.reportError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-2
@@ -285,8 +285,8 @@ public final class ModdedStudioCommands {
|
|||||||
}
|
}
|
||||||
File workspace;
|
File workspace;
|
||||||
try {
|
try {
|
||||||
workspace = ModdedWorkspaceGenerator.writeWorkspace(IrisData.get(folder), folder);
|
workspace = ModdedWorkspaceGenerator.writeWorkspace(IrisData.get(folder), folder, open);
|
||||||
} catch (IOException e) {
|
} catch (Throwable e) {
|
||||||
LOGGER.error("Iris workspace write failed for {}", folder, e);
|
LOGGER.error("Iris workspace write failed for {}", folder, e);
|
||||||
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_FAILED_WRITE_WORKSPACE, MessageArgument.untrusted("value", folder.getAbsolutePath()), MessageArgument.untrusted("value2", String.valueOf(e.getMessage()))));
|
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_FAILED_WRITE_WORKSPACE, MessageArgument.untrusted("value", folder.getAbsolutePath()), MessageArgument.untrusted("value2", String.valueOf(e.getMessage()))));
|
||||||
return 0;
|
return 0;
|
||||||
@@ -393,6 +393,15 @@ public final class ModdedStudioCommands {
|
|||||||
server.execute(() -> IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PACK_HAS_NO_DIMENSIONS_JSON, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("pack2", pack))));
|
server.execute(() -> IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PACK_HAS_NO_DIMENSIONS_JSON, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("pack2", pack))));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
ModdedWorkspaceGenerator.writeWorkspace(data, packFolder, true);
|
||||||
|
} catch (Throwable workspaceError) {
|
||||||
|
LOGGER.error("Iris workspace write failed for {}", packFolder, workspaceError);
|
||||||
|
server.execute(() -> IrisModdedCommands.fail(source, IrisLanguage.plain(
|
||||||
|
ModdedCommandMessages.MODDED_STUDIO_COMMANDS_FAILED_WRITE_WORKSPACE,
|
||||||
|
MessageArgument.untrusted("value", packFolder.getAbsolutePath()),
|
||||||
|
MessageArgument.untrusted("value2", String.valueOf(workspaceError.getMessage())))));
|
||||||
|
}
|
||||||
server.execute(() -> {
|
server.execute(() -> {
|
||||||
if (owner.equals(CONSOLE_OWNER)) {
|
if (owner.equals(CONSOLE_OWNER)) {
|
||||||
injectConsole(source, server, dimensionId, pack, seed);
|
injectConsole(source, server, dimensionId, pack, seed);
|
||||||
|
|||||||
@@ -158,6 +158,14 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public JSONObject buildSchema() {
|
public JSONObject buildSchema() {
|
||||||
|
return buildSchema(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject buildSchemaImmediately() {
|
||||||
|
return buildSchema(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONObject buildSchema(boolean immediate) {
|
||||||
IrisLogging.debug("Building Schema " + objectClass.getSimpleName() + " " + root.getPath());
|
IrisLogging.debug("Building Schema " + objectClass.getSimpleName() + " " + root.getPath());
|
||||||
JSONObject o = new JSONObject();
|
JSONObject o = new JSONObject();
|
||||||
KList<String> fm = new KList<>();
|
KList<String> fm = new KList<>();
|
||||||
@@ -169,6 +177,15 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
|
|||||||
o.put("fileMatch", new JSONArray(fm.toArray()));
|
o.put("fileMatch", new JSONArray(fm.toArray()));
|
||||||
o.put("url", "./.iris/schema/" + getFolderName() + "-schema.json");
|
o.put("url", "./.iris/schema/" + getFolderName() + "-schema.json");
|
||||||
File a = new File(getManager().getDataFolder(), ".iris/schema/" + getFolderName() + "-schema.json");
|
File a = new File(getManager().getDataFolder(), ".iris/schema/" + getFolderName() + "-schema.json");
|
||||||
|
if (immediate) {
|
||||||
|
try {
|
||||||
|
IO.writeAll(a, new SchemaBuilder(objectClass, manager).construct().toString(4));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException("Could not write schema " + a.getAbsolutePath(), e);
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
String schemaPath = a.getAbsolutePath();
|
String schemaPath = a.getAbsolutePath();
|
||||||
if (schemaBuildQueue.add(schemaPath)) {
|
if (schemaBuildQueue.add(schemaPath)) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ package art.arcane.iris.core.project;
|
|||||||
import art.arcane.iris.core.IrisSettings;
|
import art.arcane.iris.core.IrisSettings;
|
||||||
import art.arcane.iris.core.loader.IrisData;
|
import art.arcane.iris.core.loader.IrisData;
|
||||||
import art.arcane.iris.core.loader.ResourceLoader;
|
import art.arcane.iris.core.loader.ResourceLoader;
|
||||||
import art.arcane.iris.core.localization.BukkitRuntimeMessages;
|
|
||||||
import art.arcane.iris.core.localization.IrisLanguage;
|
|
||||||
import art.arcane.iris.engine.object.IrisDimension;
|
|
||||||
import art.arcane.iris.engine.object.annotations.Snippet;
|
import art.arcane.iris.engine.object.annotations.Snippet;
|
||||||
import art.arcane.iris.spi.IrisLogging;
|
import art.arcane.iris.spi.IrisLogging;
|
||||||
import art.arcane.iris.util.common.plugin.VolmitSender;
|
import art.arcane.iris.util.common.plugin.VolmitSender;
|
||||||
@@ -34,7 +31,6 @@ import art.arcane.volmlib.util.format.Form;
|
|||||||
import art.arcane.volmlib.util.io.IO;
|
import art.arcane.volmlib.util.io.IO;
|
||||||
import art.arcane.volmlib.util.json.JSONArray;
|
import art.arcane.volmlib.util.json.JSONArray;
|
||||||
import art.arcane.volmlib.util.json.JSONObject;
|
import art.arcane.volmlib.util.json.JSONObject;
|
||||||
import art.arcane.volmlib.util.localization.MessageArgument;
|
|
||||||
import org.dom4j.Document;
|
import org.dom4j.Document;
|
||||||
import org.dom4j.Element;
|
import org.dom4j.Element;
|
||||||
|
|
||||||
@@ -45,7 +41,6 @@ import java.io.IOException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@@ -58,69 +53,35 @@ public class IrisCodeWorkspace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void openVSCode(VolmitSender sender) {
|
public void openVSCode(VolmitSender sender) {
|
||||||
|
J.attemptAsync(this::prepareAndOpenVSCode);
|
||||||
IrisDimension d = IrisData.loadAnyDimension(project.getName(), null);
|
|
||||||
J.attemptAsync(() ->
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
if (d == null) {
|
|
||||||
sender.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.IRIS_PROJECT_COULD_NOT_LOAD_DIMENSION, MessageArgument.untrusted("value", String.valueOf(project.getName()))));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (d.getLoader() == null) {
|
|
||||||
sender.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.IRIS_PROJECT_COULD_NOT_GET_DIMENSION_LOADER));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
File f = d.getLoader().getDataFolder();
|
|
||||||
|
|
||||||
if (!doOpenVSCode(f)) {
|
|
||||||
File ff = new File(d.getLoader().getDataFolder(), d.getLoadKey() + ".code-workspace");
|
|
||||||
IrisLogging.warn("Project missing code-workspace: " + ff.getAbsolutePath() + " Re-creating code workspace.");
|
|
||||||
|
|
||||||
try {
|
|
||||||
IO.writeAll(ff, createCodeWorkspaceConfig(false));
|
|
||||||
} catch (IOException e1) {
|
|
||||||
IrisLogging.reportError(e1);
|
|
||||||
e1.printStackTrace();
|
|
||||||
}
|
|
||||||
if (!doOpenVSCode(f)) {
|
|
||||||
IrisLogging.warn("Tried creating code workspace but failed a second time. Your project is likely corrupt.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
IrisLogging.reportError(e);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean doOpenVSCode(File f) throws IOException {
|
void prepareAndOpenVSCode() {
|
||||||
boolean foundWork = false;
|
boolean updated = updateWorkspace(true);
|
||||||
for (File i : Objects.requireNonNull(f.listFiles())) {
|
File workspace = getCodeWorkspaceFile();
|
||||||
if (i.getName().endsWith(".code-workspace")) {
|
if (!workspace.isFile()) {
|
||||||
foundWork = true;
|
IrisLogging.warn("Could not create the code workspace for project " + project.getName() + " at " + workspace.getAbsolutePath() + ".");
|
||||||
|
return;
|
||||||
if (IrisSettings.get().getStudio().isOpenVSCode()) {
|
|
||||||
if (!GraphicsEnvironment.isHeadless()) {
|
|
||||||
IrisLogging.msg("Opening VSCode. You may see the output from VSCode.");
|
|
||||||
IrisLogging.msg("VSCode output always starts with: '(node:#####) electron'");
|
|
||||||
Thread launcherThread = new Thread(() -> {
|
|
||||||
try {
|
|
||||||
Desktop.getDesktop().open(i);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
IrisLogging.reportError(e);
|
|
||||||
}
|
|
||||||
}, "Iris-VSCode-Launcher");
|
|
||||||
launcherThread.setDaemon(true);
|
|
||||||
launcherThread.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return foundWork;
|
if (!updated) {
|
||||||
|
IrisLogging.warn("Could not refresh every schema for " + workspace.getAbsolutePath() + "; the editor will not be opened with stale autocomplete data.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!IrisSettings.get().getStudio().isOpenVSCode() || GraphicsEnvironment.isHeadless()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IrisLogging.msg("Opening VSCode. You may see the output from VSCode.");
|
||||||
|
IrisLogging.msg("VSCode output always starts with: '(node:#####) electron'");
|
||||||
|
Thread launcherThread = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
Desktop.getDesktop().open(workspace);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
IrisLogging.reportError(e);
|
||||||
|
}
|
||||||
|
}, "Iris-VSCode-Launcher");
|
||||||
|
launcherThread.setDaemon(true);
|
||||||
|
launcherThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getCodeWorkspaceFile() {
|
public File getCodeWorkspaceFile() {
|
||||||
@@ -128,6 +89,10 @@ public class IrisCodeWorkspace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean updateWorkspace() {
|
public boolean updateWorkspace() {
|
||||||
|
return updateWorkspace(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean updateWorkspace(boolean immediateSchemas) {
|
||||||
project.getPath().mkdirs();
|
project.getPath().mkdirs();
|
||||||
File ws = getCodeWorkspaceFile();
|
File ws = getCodeWorkspaceFile();
|
||||||
|
|
||||||
@@ -136,7 +101,7 @@ public class IrisCodeWorkspace {
|
|||||||
// destroyed the author's workspace on every boot.
|
// destroyed the author's workspace on every boot.
|
||||||
String rendered;
|
String rendered;
|
||||||
try {
|
try {
|
||||||
rendered = createCodeWorkspaceConfig().toString(4);
|
rendered = createCodeWorkspaceConfig(immediateSchemas).toString(4);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
IrisLogging.reportError(e);
|
IrisLogging.reportError(e);
|
||||||
IrisLogging.warn("Could not generate the code workspace config for " + ws.getAbsolutePath() + "; leaving the existing workspace file untouched.");
|
IrisLogging.warn("Could not generate the code workspace config for " + ws.getAbsolutePath() + "; leaving the existing workspace file untouched.");
|
||||||
@@ -162,7 +127,7 @@ public class IrisCodeWorkspace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public JSONObject createCodeWorkspaceConfig() {
|
public JSONObject createCodeWorkspaceConfig() {
|
||||||
return createCodeWorkspaceConfig(true);
|
return createCodeWorkspaceConfig(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void writeIfChanged(File target, String rendered) throws IOException {
|
private static void writeIfChanged(File target, String rendered) throws IOException {
|
||||||
@@ -172,7 +137,7 @@ public class IrisCodeWorkspace {
|
|||||||
IO.writeAll(target, rendered);
|
IO.writeAll(target, rendered);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JSONObject createCodeWorkspaceConfig(boolean includeSchemas) {
|
private JSONObject createCodeWorkspaceConfig(boolean immediateSchemas) {
|
||||||
JSONObject ws = new JSONObject();
|
JSONObject ws = new JSONObject();
|
||||||
JSONArray folders = new JSONArray();
|
JSONArray folders = new JSONArray();
|
||||||
JSONObject folder = new JSONObject();
|
JSONObject folder = new JSONObject();
|
||||||
@@ -204,55 +169,68 @@ public class IrisCodeWorkspace {
|
|||||||
settings.put("json.maxItemsComputed", 30000);
|
settings.put("json.maxItemsComputed", 30000);
|
||||||
JSONArray schemas = new JSONArray();
|
JSONArray schemas = new JSONArray();
|
||||||
List<JSONObject> schemaEntries = new ArrayList<>();
|
List<JSONObject> schemaEntries = new ArrayList<>();
|
||||||
IrisData dm = null;
|
IrisData dm = IrisData.get(project.getPath());
|
||||||
if (includeSchemas) {
|
for (ResourceLoader<?> resourceLoader : dm.getLoaders().v()) {
|
||||||
dm = IrisData.get(project.getPath());
|
if (resourceLoader.supportsSchemas()) {
|
||||||
for (ResourceLoader<?> r : dm.getLoaders().v()) {
|
schemaEntries.add(immediateSchemas
|
||||||
if (r.supportsSchemas()) {
|
? resourceLoader.buildSchemaImmediately()
|
||||||
schemaEntries.add(r.buildSchema());
|
: resourceLoader.buildSchema());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (Class<?> i : sortedSnippets(dm.resolveSnippets())) {
|
for (Class<?> snippetClass : sortedSnippets(dm.resolveSnippets())) {
|
||||||
try {
|
try {
|
||||||
String snipType = i.getDeclaredAnnotation(Snippet.class).value();
|
String snipType = snippetClass.getDeclaredAnnotation(Snippet.class).value();
|
||||||
JSONObject o = new JSONObject();
|
JSONObject schemaEntry = new JSONObject();
|
||||||
KList<String> fm = new KList<>();
|
KList<String> fileMatches = new KList<>();
|
||||||
|
|
||||||
for (int g = 1; g < 8; g++) {
|
for (int depth = 1; depth < 8; depth++) {
|
||||||
fm.add("/snippet/" + snipType + Form.repeat("/*", g) + ".json");
|
fileMatches.add("/snippet/" + snipType + Form.repeat("/*", depth) + ".json");
|
||||||
}
|
}
|
||||||
|
|
||||||
o.put("fileMatch", new JSONArray(fm.toArray()));
|
schemaEntry.put("fileMatch", new JSONArray(fileMatches.toArray()));
|
||||||
o.put("url", "./.iris/schema/snippet/" + snipType + "-schema.json");
|
schemaEntry.put("url", "./.iris/schema/snippet/" + snipType + "-schema.json");
|
||||||
schemaEntries.add(o);
|
schemaEntries.add(schemaEntry);
|
||||||
|
File schemaFile = new File(dm.getDataFolder(), ".iris/schema/snippet/" + snipType + "-schema.json");
|
||||||
|
if (immediateSchemas) {
|
||||||
|
IO.writeAll(schemaFile, new SchemaBuilder(snippetClass, dm).construct().toString(4));
|
||||||
|
} else {
|
||||||
IrisData snippetData = dm;
|
IrisData snippetData = dm;
|
||||||
File a = new File(snippetData.getDataFolder(), ".iris/schema/snippet/" + snipType + "-schema.json");
|
|
||||||
J.attemptAsync(() -> {
|
J.attemptAsync(() -> {
|
||||||
try {
|
try {
|
||||||
IO.writeAll(a, new SchemaBuilder(i, snippetData).construct().toString(4));
|
IO.writeAll(schemaFile, new SchemaBuilder(snippetClass, snippetData).construct().toString(4));
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
IrisLogging.reportError(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
if (immediateSchemas) {
|
||||||
|
throw new IllegalStateException("Could not write snippet schema for " + snippetClass.getName(), e);
|
||||||
|
}
|
||||||
|
IrisLogging.reportError(e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
schemaEntries.sort(Comparator.comparing(entry -> entry.getString("url")));
|
schemaEntries.sort(Comparator.comparing(entry -> entry.getString("url")));
|
||||||
for (JSONObject entry : schemaEntries) {
|
for (JSONObject entry : schemaEntries) {
|
||||||
schemas.put(entry);
|
schemas.put(entry);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.put("json.schemas", schemas);
|
settings.put("json.schemas", schemas);
|
||||||
ws.put("settings", settings);
|
ws.put("settings", settings);
|
||||||
|
|
||||||
if (!includeSchemas) {
|
try {
|
||||||
return ws;
|
updateIntelliJSchemaMappings(schemas);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
IrisLogging.reportError(e);
|
||||||
|
IrisLogging.warn("Could not update IntelliJ schema mappings for " + project.getPath().getAbsolutePath() + "; VSCode workspace generation will continue.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ws;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateIntelliJSchemaMappings(JSONArray schemas) {
|
||||||
File schemasFile = new File(project.getPath(), ".idea" + File.separator + "jsonSchemas.xml");
|
File schemasFile = new File(project.getPath(), ".idea" + File.separator + "jsonSchemas.xml");
|
||||||
Document doc = IO.read(schemasFile);
|
Document doc = IO.read(schemasFile);
|
||||||
Element mappings = (Element) doc.selectSingleNode("//component[@name='JsonSchemaMappingsProjectConfiguration']");
|
Element mappings = (Element) doc.selectSingleNode("//component[@name='JsonSchemaMappingsProjectConfiguration']");
|
||||||
@@ -267,7 +245,7 @@ public class IrisCodeWorkspace {
|
|||||||
|
|
||||||
Element map = (Element) state.selectSingleNode("map");
|
Element map = (Element) state.selectSingleNode("map");
|
||||||
if (map == null) map = state.addElement("map");
|
if (map == null) map = state.addElement("map");
|
||||||
var schemaMap = new KMap<String, String>();
|
KMap<String, String> schemaMap = new KMap<>();
|
||||||
schemas.forEach(element -> {
|
schemas.forEach(element -> {
|
||||||
if (!(element instanceof JSONObject obj))
|
if (!(element instanceof JSONObject obj))
|
||||||
return;
|
return;
|
||||||
@@ -282,17 +260,17 @@ public class IrisCodeWorkspace {
|
|||||||
.map(node -> node.valueOf("@value"))
|
.map(node -> node.valueOf("@value"))
|
||||||
.forEach(schemaMap::remove);
|
.forEach(schemaMap::remove);
|
||||||
|
|
||||||
var ideaSchemas = map;
|
Element ideaSchemas = map;
|
||||||
schemaMap.forEach((url, dir) -> {
|
schemaMap.forEach((url, dir) -> {
|
||||||
var genName = UUID.randomUUID().toString();
|
String generatedName = UUID.randomUUID().toString();
|
||||||
|
|
||||||
var info = ideaSchemas.addElement("entry")
|
Element info = ideaSchemas.addElement("entry")
|
||||||
.addAttribute("key", genName)
|
.addAttribute("key", generatedName)
|
||||||
.addElement("value")
|
.addElement("value")
|
||||||
.addElement("SchemaInfo");
|
.addElement("SchemaInfo");
|
||||||
info.addElement("option")
|
info.addElement("option")
|
||||||
.addAttribute("name", "generatedName")
|
.addAttribute("name", "generatedName")
|
||||||
.addAttribute("value", genName);
|
.addAttribute("value", generatedName);
|
||||||
info.addElement("option")
|
info.addElement("option")
|
||||||
.addAttribute("name", "name")
|
.addAttribute("name", "name")
|
||||||
.addAttribute("value", dir);
|
.addAttribute("value", dir);
|
||||||
@@ -301,7 +279,7 @@ public class IrisCodeWorkspace {
|
|||||||
.addAttribute("value", url);
|
.addAttribute("value", url);
|
||||||
|
|
||||||
|
|
||||||
var item = info.addElement("option")
|
Element item = info.addElement("option")
|
||||||
.addAttribute("name", "patterns")
|
.addAttribute("name", "patterns")
|
||||||
.addElement("list")
|
.addElement("list")
|
||||||
.addElement("Item");
|
.addElement("Item");
|
||||||
@@ -318,7 +296,6 @@ public class IrisCodeWorkspace {
|
|||||||
if (!schemaMap.isEmpty()) {
|
if (!schemaMap.isEmpty()) {
|
||||||
IO.write(schemasFile, doc);
|
IO.write(schemasFile, doc);
|
||||||
}
|
}
|
||||||
return ws;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Class<?>> sortedSnippets(Set<Class<?>> snippets) {
|
private static List<Class<?>> sortedSnippets(Set<Class<?>> snippets) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import art.arcane.iris.engine.framework.PreservationRegistry;
|
|||||||
import art.arcane.iris.spi.IrisPlatform;
|
import art.arcane.iris.spi.IrisPlatform;
|
||||||
import art.arcane.iris.spi.IrisPlatforms;
|
import art.arcane.iris.spi.IrisPlatforms;
|
||||||
import art.arcane.iris.spi.IrisServices;
|
import art.arcane.iris.spi.IrisServices;
|
||||||
|
import art.arcane.iris.spi.PlatformRegistries;
|
||||||
import art.arcane.volmlib.util.json.JSONArray;
|
import art.arcane.volmlib.util.json.JSONArray;
|
||||||
import art.arcane.volmlib.util.json.JSONObject;
|
import art.arcane.volmlib.util.json.JSONObject;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
@@ -17,6 +18,7 @@ import org.junit.rules.TemporaryFolder;
|
|||||||
import org.mockito.Answers;
|
import org.mockito.Answers;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.attribute.FileTime;
|
import java.nio.file.attribute.FileTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -45,7 +47,9 @@ public class IrisCodeWorkspaceTest {
|
|||||||
previousSettings = IrisSettings.settings;
|
previousSettings = IrisSettings.settings;
|
||||||
IrisPlatforms.unbind();
|
IrisPlatforms.unbind();
|
||||||
IrisPlatform platform = mock(IrisPlatform.class, Answers.CALLS_REAL_METHODS);
|
IrisPlatform platform = mock(IrisPlatform.class, Answers.CALLS_REAL_METHODS);
|
||||||
|
PlatformRegistries registries = mock(PlatformRegistries.class);
|
||||||
when(platform.dataFolder()).thenReturn(temporaryFolder.getRoot());
|
when(platform.dataFolder()).thenReturn(temporaryFolder.getRoot());
|
||||||
|
when(platform.registries()).thenReturn(registries);
|
||||||
IrisPlatforms.bind(platform);
|
IrisPlatforms.bind(platform);
|
||||||
IrisSettings.settings = new IrisSettings();
|
IrisSettings.settings = new IrisSettings();
|
||||||
IrisServices.register(PreservationRegistry.class, new NoOpPreservationRegistry());
|
IrisServices.register(PreservationRegistry.class, new NoOpPreservationRegistry());
|
||||||
@@ -103,6 +107,43 @@ public class IrisCodeWorkspaceTest {
|
|||||||
assertEquals("Schema entries must be emitted in a boot-stable order", sorted, urls);
|
assertEquals("Schema entries must be emitted in a boot-stable order", sorted, urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void studioOpenCreatesAndRefreshesCanonicalWorkspaceAndMaterializesSchemas() throws Exception {
|
||||||
|
File pack = temporaryFolder.newFolder("studio-pack");
|
||||||
|
File canonicalWorkspace = new File(pack, "studio-pack.code-workspace");
|
||||||
|
File unrelatedWorkspace = new File(pack, "unrelated.code-workspace");
|
||||||
|
File intellijMappings = new File(pack, ".idea/jsonSchemas.xml");
|
||||||
|
Files.writeString(unrelatedWorkspace.toPath(), "unrelated", StandardCharsets.UTF_8);
|
||||||
|
Files.createDirectories(intellijMappings.toPath().getParent());
|
||||||
|
Files.writeString(intellijMappings.toPath(), "not xml", StandardCharsets.UTF_8);
|
||||||
|
IrisSettings.get().getStudio().setOpenVSCode(false);
|
||||||
|
|
||||||
|
IrisCodeWorkspace workspace = new IrisCodeWorkspace(new IrisProject(pack));
|
||||||
|
workspace.prepareAndOpenVSCode();
|
||||||
|
data = IrisData.get(pack);
|
||||||
|
|
||||||
|
JSONArray schemas = assertWorkspaceSchemasMaterialized(pack, canonicalWorkspace);
|
||||||
|
String firstSchemaPath = schemas.getJSONObject(0).getString("url").substring(2);
|
||||||
|
Files.writeString(canonicalWorkspace.toPath(), "{\"settings\":{\"json.schemas\":[]}}", StandardCharsets.UTF_8);
|
||||||
|
Files.delete(new File(pack, firstSchemaPath).toPath());
|
||||||
|
|
||||||
|
workspace.prepareAndOpenVSCode();
|
||||||
|
assertWorkspaceSchemasMaterialized(pack, canonicalWorkspace);
|
||||||
|
assertEquals("Studio open must target only the canonical workspace", "unrelated",
|
||||||
|
Files.readString(unrelatedWorkspace.toPath(), StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JSONArray assertWorkspaceSchemasMaterialized(File pack, File canonicalWorkspace) throws Exception {
|
||||||
|
JSONObject configuration = new JSONObject(Files.readString(canonicalWorkspace.toPath(), StandardCharsets.UTF_8));
|
||||||
|
JSONArray schemas = configuration.getJSONObject("settings").getJSONArray("json.schemas");
|
||||||
|
assertTrue("Studio open must declare schemas", schemas.length() > 0);
|
||||||
|
for (int index = 0; index < schemas.length(); index++) {
|
||||||
|
String relativePath = schemas.getJSONObject(index).getString("url").substring(2);
|
||||||
|
assertTrue("Studio open must write schema " + relativePath, new File(pack, relativePath).isFile());
|
||||||
|
}
|
||||||
|
return schemas;
|
||||||
|
}
|
||||||
|
|
||||||
private static final class NoOpPreservationRegistry implements PreservationRegistry {
|
private static final class NoOpPreservationRegistry implements PreservationRegistry {
|
||||||
@Override
|
@Override
|
||||||
public void register(Thread thread) {
|
public void register(Thread thread) {
|
||||||
|
|||||||
Reference in New Issue
Block a user