This commit is contained in:
Brian Neumann-Fopiano
2026-07-22 14:56:50 -05:00
parent f6f06c1ab9
commit 9e1e202f15
135 changed files with 34492 additions and 1795 deletions
@@ -38,6 +38,7 @@ import art.arcane.iris.core.runtime.TransientWorldCleanupSupport;
import art.arcane.iris.core.runtime.WorldRuntimeControlService;
import art.arcane.iris.core.lifecycle.WorldLifecycleStaging;
import art.arcane.iris.core.link.IrisPapiExpansion;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.link.MultiverseCoreLink;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.core.nms.INMS;
@@ -606,6 +607,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
private void enable() {
alreadyDrained.set(false);
IrisLanguage.initialize();
PaperLibBootstrap.install();
SimdSupport.install();
services = new KMap<>();
@@ -1110,9 +1112,11 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
configHotloadEngine.processFileChange(file, ignored -> {
IrisSettings.invalidate();
IrisSettings.get();
IrisLanguage.reload();
return true;
}, ignored -> Iris.info("Hotloaded settings.json "));
}
IrisLanguage.update();
}
private static boolean isSettingsFile(File file) {
@@ -29,42 +29,45 @@ import art.arcane.volmlib.util.director.annotations.Param;
import java.util.List;
@Director(name = "datapack", aliases = {"datapacks", "dp"}, description = "Download & manage external datapack imports (Modrinth)")
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.BukkitCommandMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
@Director(name = "datapack", aliases = {"datapacks", "dp"}, description = "Download & manage external datapack imports (Modrinth)", descriptionKey = "iris.director.commanddatapack.director.download_manage_external_datapack_imports_modrinth")
public class CommandDatapack implements DirectorExecutor {
@Director(description = "Download/update every datapack listed in a pack dimension's 'datapackImports' and install it into the world so its structures register like vanilla", aliases = {"pull"})
@Director(description = "Download/update every datapack listed in a pack dimension's 'datapackImports' and install it into the world so its structures register like vanilla", descriptionKey = "iris.director.commanddatapack.director.download_update_every_datapack_listed_pack_dimension_s_datapackimports_install_it_into", aliases = {"pull"})
public void ingest(
@Param(description = "Restart the server when new datapacks are installed (required for new structures to register and generate)", defaultValue = "false")
@Param(description = "Restart the server when new datapacks are installed (required for new structures to register and generate)", descriptionKey = "iris.director.commanddatapack.param.restart_server_when_new_datapacks_are_installed_required_new_structures_register_generate", defaultValue = "false")
boolean restart
) {
VolmitSender sender = sender();
sender.sendMessage(C.GRAY + "Starting datapack ingest...");
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_DATAPACK_STARTING_DATAPACK_INGEST));
J.a(() -> DatapackIngestService.ingestAll(sender, restart));
}
@Director(description = "List configured datapack imports and their installed versions", aliases = {"ls"})
@Director(description = "List configured datapack imports and their installed versions", descriptionKey = "iris.director.commanddatapack.director.list_configured_datapack_imports_their_installed_versions", aliases = {"ls"})
public void list() {
VolmitSender sender = sender();
KList<String> configured = DatapackIngestService.collectConfiguredImports();
List<DatapackIngestService.Entry> installed = DatapackIngestService.installed();
sender.sendMessage(C.GREEN + "Configured datapack imports: " + C.WHITE + configured.size());
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_DATAPACK_CONFIGURED_DATAPACK_IMPORTS, MessageArgument.untrusted("value", configured.size())));
for (String url : configured) {
sender.sendMessage(C.GRAY + " - " + C.WHITE + url);
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_DATAPACK_MESSAGE, MessageArgument.untrusted("url", url)));
}
sender.sendMessage(C.GREEN + "Installed datapacks: " + C.WHITE + installed.size());
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_DATAPACK_INSTALLED_DATAPACKS, MessageArgument.untrusted("value", installed.size())));
for (DatapackIngestService.Entry entry : installed) {
sender.sendMessage(C.GRAY + " - " + C.WHITE + entry.id + C.GRAY + " " + (entry.versionNumber == null ? "?" : entry.versionNumber));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_DATAPACK_MESSAGE_2, MessageArgument.untrusted("value", entry.id), MessageArgument.untrusted("value2", (entry.versionNumber == null ? "?" : entry.versionNumber))));
}
if (configured.isEmpty()) {
sender.sendMessage(C.YELLOW + "Add Modrinth URLs to a dimension's 'datapackImports' list, then run /iris datapack ingest.");
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_DATAPACK_ADD_MODRINTH_URLS_DIMENSION_S_DATAPACKIMPORTS_LIST_THEN_RUN_IRIS));
}
}
@Director(description = "Remove an installed datapack by id (also delete its URL from datapackImports to keep it gone)", aliases = {"rm"})
@Director(description = "Remove an installed datapack by id (also delete its URL from datapackImports to keep it gone)", descriptionKey = "iris.director.commanddatapack.director.remove_installed_datapack_by_id_also_delete_its_url_from_datapackimports_keep", aliases = {"rm"})
public void remove(
@Param(description = "The datapack id (folder name) shown by /iris datapack list")
@Param(description = "The datapack id (folder name) shown by /iris datapack list", descriptionKey = "iris.director.commanddatapack.param.datapack_id_folder_name_shown_by_iris_datapack_list")
String id
) {
DatapackIngestService.remove(sender(), id);
@@ -64,15 +64,20 @@ import java.util.Enumeration;
import java.util.Map;
import java.util.TreeMap;
@Director(name = "Developer", origin = DirectorOrigin.BOTH, description = "Iris World Manager", aliases = {"dev"})
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.BukkitCommandMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
import art.arcane.iris.core.localization.BukkitCommandMessagesExtended;
import art.arcane.iris.core.localization.BukkitRuntimeMessages;
@Director(name = "Developer", origin = DirectorOrigin.BOTH, description = "Iris World Manager", descriptionKey = "iris.director.commanddeveloper.director.iris_world_manager", aliases = {"dev"})
public class CommandDeveloper implements DirectorExecutor {
@Director(description = "Get Loaded TectonicPlates Count", origin = DirectorOrigin.BOTH, sync = true)
@Director(description = "Get Loaded TectonicPlates Count", descriptionKey = "iris.director.commanddeveloper.director.get_loaded_tectonicplates_count", origin = DirectorOrigin.BOTH, sync = true)
public void EngineStatus() {
Iris.service(IrisEngineSVC.class)
.engineStatus(sender());
}
@Director(description = "Send a test exception to sentry")
@Director(description = "Send a test exception to sentry", descriptionKey = "iris.director.commanddeveloper.director.send_test_exception_sentry")
public void Sentry() {
Engine engine = engine();
Exception testException = new Exception("This is a test");
@@ -85,23 +90,23 @@ public class CommandDeveloper implements DirectorExecutor {
}
}
@Director(description = "Hash generated block output of a fixed area for determinism/identity testing", origin = DirectorOrigin.BOTH)
@Director(description = "Hash generated block output of a fixed area for determinism/identity testing", descriptionKey = "iris.director.commanddeveloper.director.hash_generated_block_output_fixed_area_determinism_identity_testing", origin = DirectorOrigin.BOTH)
public void genhash(
@Param(description = "The world to hash", contextual = true)
@Param(description = "The world to hash", descriptionKey = "iris.director.commanddeveloper.param.world_hash", contextual = true)
World world,
@Param(description = "Radius in chunks around the center", defaultValue = "4")
@Param(description = "Radius in chunks around the center", descriptionKey = "iris.director.commanddeveloper.param.radius_chunks_around_center", defaultValue = "4")
int radius,
@Param(description = "Center chunk X", defaultValue = "0")
@Param(description = "Center chunk X", descriptionKey = "iris.director.commanddeveloper.param.center_chunk_x", defaultValue = "0")
int centerX,
@Param(description = "Center chunk Z", defaultValue = "0")
@Param(description = "Center chunk Z", descriptionKey = "iris.director.commanddeveloper.param.center_chunk_z", defaultValue = "0")
int centerZ) {
if (world == null) {
sender().sendMessage(C.RED + "World is null.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_DEVELOPER_WORLD_IS_NULL));
return;
}
VolmitSender sender = sender();
sender.sendMessage(C.GREEN + "genhash started: " + ((radius * 2 + 1) * (radius * 2 + 1)) + " chunks...");
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_DEVELOPER_GENHASH_STARTED_CHUNKS, MessageArgument.untrusted("value", ((radius * 2 + 1) * (radius * 2 + 1)))));
J.a(() -> runGenhash(sender, world, radius, centerX, centerZ));
}
@@ -122,7 +127,7 @@ public class CommandDeveloper implements DirectorExecutor {
snapshot = loaded.getChunkSnapshot(false, false, false);
} catch (Throwable e) {
Iris.reportError(e);
sender.sendMessage(C.RED + "genhash failed at chunk " + rx + "," + rz + ": " + e.getMessage());
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_DEVELOPER_GENHASH_FAILED_AT_CHUNK, MessageArgument.untrusted("rx", rx), MessageArgument.untrusted("rz", rz), MessageArgument.untrusted("value", String.valueOf(e.getMessage()))));
return;
}
long chunkHash = 0L;
@@ -172,9 +177,7 @@ public class CommandDeveloper implements DirectorExecutor {
Iris.reportError(e);
}
sender.sendMessage(C.GREEN + "genhash global=" + C.GOLD + Long.toHexString(globalHash)
+ C.GREEN + " chunks=" + (side * side) + " solid=" + solidBlocks
+ " in " + Form.duration((long) (M.ms() - startMs), 1));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_DEVELOPER_GENHASH_GLOBAL_CHUNKS_SOLID, MessageArgument.untrusted("value", Long.toHexString(globalHash)), MessageArgument.untrusted("value2", (side * side)), MessageArgument.untrusted("solidBlocks", solidBlocks), MessageArgument.untrusted("value3", Form.duration((long) (M.ms() - startMs), 1))));
Iris.info("genhash world=" + world.getName() + " global=" + Long.toHexString(globalHash)
+ " chunks=" + (side * side) + " solidBlocks=" + solidBlocks + " -> " + out.getAbsolutePath());
}
@@ -185,29 +188,23 @@ public class CommandDeveloper implements DirectorExecutor {
return z ^ (z >>> 31);
}
@Director(description = "Update the pack of a world (UNSAFE!)", name = "update-world", aliases = "^world")
@Director(description = "Update the pack of a world (UNSAFE!)", descriptionKey = "iris.director.commanddeveloper.director.update_pack_world_unsafe", name = "update-world", aliases = "^world")
public void updateWorld(
@Param(description = "The world to update", contextual = true)
@Param(description = "The world to update", descriptionKey = "iris.director.commanddeveloper.param.world_update", contextual = true)
World world,
@Param(description = "The pack to install into the world", contextual = true, aliases = "dimension")
@Param(description = "The pack to install into the world", descriptionKey = "iris.director.commanddeveloper.param.pack_install_into_world", contextual = true, aliases = "dimension")
IrisDimension pack,
@Param(description = "Make sure to make a backup & read the warnings first!", defaultValue = "false", aliases = "c")
@Param(description = "Make sure to make a backup & read the warnings first!", descriptionKey = "iris.director.commanddeveloper.param.make_sure_make_backup_read_warnings_first", defaultValue = "false", aliases = "c")
boolean confirm,
@Param(description = "Should Iris download the pack again for you", defaultValue = "false", name = "fresh-download", aliases = {"fresh", "new"})
@Param(description = "Should Iris download the pack again for you", descriptionKey = "iris.director.commanddeveloper.param.should_iris_download_pack_again_you", defaultValue = "false", name = "fresh-download", aliases = {"fresh", "new"})
boolean freshDownload
) {
if (!confirm) {
sender().sendMessage(new String[]{
C.RED + "You should always make a backup before using this",
C.YELLOW + "Issues caused by this can be, but are not limited to:",
C.YELLOW + " - Broken chunks (cut-offs) between old and new chunks (before & after the update)",
C.YELLOW + " - Regenerated chunks that do not fit in with the old chunks",
C.YELLOW + " - Structures not spawning again when regenerating",
C.YELLOW + " - Caves not lining up",
C.YELLOW + " - Terrain layers not lining up",
C.RED + "Now that you are aware of the risks, and have made a back-up:",
C.RED + "/iris developer update-world " + world.getName() + " " + pack.getLoadKey() + " confirm=true"
});
sender().sendMessage(IrisLanguage.text(
BukkitRuntimeMessages.COMMAND_DEVELOPER_UPDATE_WORLD_WARNING,
MessageArgument.untrusted("world", world.getName()),
MessageArgument.untrusted("pack", pack.getLoadKey())
));
return;
}
@@ -221,16 +218,16 @@ public class CommandDeveloper implements DirectorExecutor {
Iris.service(StudioSVC.class).installIntoWorld(sender(), pack, folder);
}
@Director(description = "Test")
@Director(description = "Test", descriptionKey = "iris.director.commanddeveloper.director.test")
public void mantle(
@Param(name = "plate", description = "Dump the whole tectonic plate instead of a single section", defaultValue = "false")
@Param(name = "plate", description = "Dump the whole tectonic plate instead of a single section", descriptionKey = "iris.director.commanddeveloper.param.dump_whole_tectonic_plate_instead_single_section", defaultValue = "false")
boolean plate,
@Param(name = "name", description = "The dump file id under plugins/Iris/dump (pv.<id>.*)", defaultValue = "21474836474")
@Param(name = "name", description = "The dump file id under plugins/Iris/dump (pv.<id>.*)", descriptionKey = "iris.director.commanddeveloper.param.dump_file_id_under_plugins_iris_dump_pv_id", defaultValue = "21474836474")
String name
) throws Throwable {
Engine activeEngine = engine();
if (activeEngine == null) {
sender().sendMessage(C.RED + "Target an Iris world before reading a mantle dump.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_DEVELOPER_TARGET_IRIS_WORLD_BEFORE_READING_MANTLE_DUMP));
return;
}
File base = Iris.instance.getDataFile("dump", "pv." + name + ".ttp.lz4b.bin");
@@ -252,29 +249,29 @@ public class CommandDeveloper implements DirectorExecutor {
}
}
@Director(description = "Test")
@Director(description = "Test", descriptionKey = "iris.director.commanddeveloper.director.test_2")
public void packBenchmark(
@Param(description = "The pack to bench", aliases = {"pack"}, defaultValue = "overworld")
@Param(description = "The pack to bench", descriptionKey = "iris.director.commanddeveloper.param.pack_bench", aliases = {"pack"}, defaultValue = "overworld")
IrisDimension dimension,
@Param(description = "Radius in regions", defaultValue = "2048")
@Param(description = "Radius in regions", descriptionKey = "iris.director.commanddeveloper.param.radius_regions", defaultValue = "2048")
int radius,
@Param(description = "Open GUI while benchmarking", defaultValue = "false")
@Param(description = "Open GUI while benchmarking", descriptionKey = "iris.director.commanddeveloper.param.open_gui_while_benchmarking", defaultValue = "false")
boolean gui
) {
new IrisPackBenchmarking(dimension, radius, gui);
}
@Director(description = "Upgrade to another Minecraft version")
@Director(description = "Upgrade to another Minecraft version", descriptionKey = "iris.director.commanddeveloper.director.upgrade_another_minecraft_version")
public void upgrade(
@Param(description = "The version to upgrade to", defaultValue = "latest") DataVersion version) {
sender().sendMessage(C.GREEN + "Upgrading to " + version.getVersion() + "...");
@Param(description = "The version to upgrade to", descriptionKey = "iris.director.commanddeveloper.param.version_upgrade", defaultValue = "latest") DataVersion version) {
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_DEVELOPER_UPGRADING, MessageArgument.untrusted("value", version.getVersion())));
ServerConfigurator.installDataPacks(version.get(), false);
sender().sendMessage(C.GREEN + "Done upgrading! You can now update your server version to " + version.getVersion());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_DEVELOPER_DONE_UPGRADING_YOU_CAN_NOW_UPDATE_YOUR_SERVER_VERSION, MessageArgument.untrusted("value", version.getVersion())));
}
@Director(description = "test")
@Director(description = "test", descriptionKey = "iris.director.commanddeveloper.director.test_3")
public void mca (
@Param(description = "The world folder to scan for .mca region files") String world) {
@Param(description = "The world folder to scan for .mca region files", descriptionKey = "iris.director.commanddeveloper.param.world_folder_scan_mca_region_files") String world) {
try {
File[] McaFiles = new File(world, "region").listFiles((dir, name) -> name.endsWith(".mca"));
for (File mca : McaFiles) {
@@ -286,25 +283,25 @@ public class CommandDeveloper implements DirectorExecutor {
}
@Director(description = "Delete nearby chunk blocks for regen testing", name = "delete-chunk", aliases = {"dc"}, origin = DirectorOrigin.PLAYER, sync = true)
@Director(description = "Delete nearby chunk blocks for regen testing", descriptionKey = "iris.director.commanddeveloper.director.delete_nearby_chunk_blocks_regen_testing", name = "delete-chunk", aliases = {"dc"}, origin = DirectorOrigin.PLAYER, sync = true)
public void deleteChunk(
@Param(description = "Radius in chunks around your current chunk", defaultValue = "0")
@Param(description = "Radius in chunks around your current chunk", descriptionKey = "iris.director.commanddeveloper.param.radius_chunks_around_your_current_chunk", defaultValue = "0")
int radius
) {
if (radius < 0) {
sender().sendMessage(C.RED + "Radius must be 0 or greater.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_DEVELOPER_RADIUS_MUST_BE_0_GREATER));
return;
}
World world = player().getWorld();
if (!IrisToolbelt.isIrisWorld(world)) {
sender().sendMessage(C.RED + "This is not an Iris world.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_DEVELOPER_THIS_IS_NOT_IRIS_WORLD));
return;
}
PlatformChunkGenerator access = IrisToolbelt.access(world);
if (access == null || access.getEngine() == null) {
sender().sendMessage(C.RED + "The engine access for this world is null.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_DEVELOPER_ENGINE_ACCESS_THIS_WORLD_IS_NULL));
return;
}
@@ -312,14 +309,12 @@ public class CommandDeveloper implements DirectorExecutor {
int centerZ = player().getLocation().getBlockZ() >> 4;
int chunks = (radius * 2 + 1) * (radius * 2 + 1);
sender().sendMessage(C.GREEN + "Delete started: " + C.GOLD + chunks + C.GREEN
+ " chunk(s) around " + C.GOLD + centerX + "," + centerZ + C.GREEN
+ ". Clearing blocks to air.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_DEVELOPER_DELETE_STARTED_CHUNK_S_AROUND_CLEARING_BLOCKS_AIR, MessageArgument.untrusted("chunks", chunks), MessageArgument.untrusted("centerX", centerX), MessageArgument.untrusted("centerZ", centerZ)));
new ChunkClearer(world, access.getEngine(), sender(), centerX, centerZ, radius).start();
}
@Director(description = "Test", aliases = {"ip"})
@Director(description = "Test", descriptionKey = "iris.director.commanddeveloper.director.test_4", aliases = {"ip"})
public void network() {
try {
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
@@ -337,25 +332,25 @@ public class CommandDeveloper implements DirectorExecutor {
// --- Regen ---
@Director(name = "regen", aliases = {"rg"}, description = "Delete and regenerate nearby chunks in place using Iris generation", origin = DirectorOrigin.PLAYER, sync = true)
@Director(name = "regen", aliases = {"rg"}, description = "Delete and regenerate nearby chunks in place using Iris generation", descriptionKey = "iris.director.commanddeveloper.director.delete_regenerate_nearby_chunks_place_using_iris_generation", origin = DirectorOrigin.PLAYER, sync = true)
public void regen(
@Param(name = "radius", description = "The radius of nearby chunks", defaultValue = "5")
@Param(name = "radius", description = "The radius of nearby chunks", descriptionKey = "iris.director.commanddeveloper.param.radius_nearby_chunks", defaultValue = "5")
int radius
) {
if (radius < 0) {
sender().sendMessage(C.RED + "Radius must be 0 or greater.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_DEVELOPER_RADIUS_MUST_BE_0_GREATER_2));
return;
}
World world = player().getWorld();
if (!IrisToolbelt.isIrisWorld(world)) {
sender().sendMessage(C.RED + "You must be in an Iris world to use regen.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_DEVELOPER_YOU_MUST_BE_IRIS_WORLD_USE_REGEN));
return;
}
Engine engine = IrisToolbelt.access(world).getEngine();
if (engine == null) {
sender().sendMessage(C.RED + "The engine access for this world is null. Generate nearby chunks first.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_DEVELOPER_ENGINE_ACCESS_THIS_WORLD_IS_NULL_GENERATE_NEARBY_CHUNKS_FIRST));
return;
}
@@ -363,9 +358,7 @@ public class CommandDeveloper implements DirectorExecutor {
int centerZ = player().getLocation().getBlockZ() >> 4;
int chunks = (radius * 2 + 1) * (radius * 2 + 1);
sender().sendMessage(C.GREEN + "Regen started: " + C.GOLD + chunks + C.GREEN
+ " chunk(s) around " + C.GOLD + centerX + "," + centerZ + C.GREEN
+ ". Deleting and regenerating in place.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_DEVELOPER_REGEN_STARTED_CHUNK_S_AROUND_DELETING_REGENERATING_PLACE, MessageArgument.untrusted("chunks", chunks), MessageArgument.untrusted("centerX", centerX), MessageArgument.untrusted("centerZ", centerZ)));
Iris.info("Regen run start: world=" + world.getName()
+ " center=" + centerX + "," + centerZ
+ " radius=" + radius
@@ -374,43 +367,41 @@ public class CommandDeveloper implements DirectorExecutor {
new InPlaceChunkRegenerator(world, engine, sender(), centerX, centerZ, radius).start();
}
@Director(name = "goldenhash", aliases = {"gold"}, description = "Generate chunks into buffers (no world writes) and hash blocks+biomes; captures a golden file or verifies against an existing one. Resets mantle in the scanned area - use on disposable test worlds.", origin = DirectorOrigin.BOTH)
@Director(name = "goldenhash", aliases = {"gold"}, description = "Generate chunks into buffers (no world writes) and hash blocks+biomes; captures a golden file or verifies against an existing one. Resets mantle in the scanned area - use on disposable test worlds.", descriptionKey = "iris.director.commanddeveloper.director.generate_chunks_into_buffers_no_world_writes_hash_blocks_biomes_captures_golden", origin = DirectorOrigin.BOTH)
public void goldenhash(
@Param(description = "The world to scan", contextual = true)
@Param(description = "The world to scan", descriptionKey = "iris.director.commanddeveloper.param.world_scan", contextual = true)
World world,
@Param(name = "radius", description = "Radius in chunks around the center", defaultValue = "8")
@Param(name = "radius", description = "Radius in chunks around the center", descriptionKey = "iris.director.commanddeveloper.param.radius_chunks_around_center_2", defaultValue = "8")
int radius,
@Param(name = "center-x", description = "Center chunk X", defaultValue = "0")
@Param(name = "center-x", description = "Center chunk X", descriptionKey = "iris.director.commanddeveloper.param.center_chunk_x_2", defaultValue = "0")
int centerX,
@Param(name = "center-z", description = "Center chunk Z", defaultValue = "0")
@Param(name = "center-z", description = "Center chunk Z", descriptionKey = "iris.director.commanddeveloper.param.center_chunk_z_2", defaultValue = "0")
int centerZ,
@Param(name = "reset-mantle", description = "Delete mantle data in the scan area first for full regeneration from scratch", defaultValue = "true")
@Param(name = "reset-mantle", description = "Delete mantle data in the scan area first for full regeneration from scratch", descriptionKey = "iris.director.commanddeveloper.param.delete_mantle_data_scan_area_first_full_regeneration_from_scratch", defaultValue = "true")
boolean resetMantle,
@Param(name = "threads", description = "Concurrent chunk generations; 1 = strictly serial for order-dependence testing", defaultValue = "8")
@Param(name = "threads", description = "Concurrent chunk generations; 1 = strictly serial for order-dependence testing", descriptionKey = "iris.director.commanddeveloper.param.concurrent_chunk_generations_1_strictly_serial_order_dependence_testing", defaultValue = "8")
int threads,
@Param(name = "deep", description = "Also dump full per-chunk non-air blockstates for offline diffing", defaultValue = "false")
@Param(name = "deep", description = "Also dump full per-chunk non-air blockstates for offline diffing", descriptionKey = "iris.director.commanddeveloper.param.also_dump_full_per_chunk_non_air_blockstates_offline_diffing", defaultValue = "false")
boolean deep
) {
if (radius < 0) {
sender().sendMessage(C.RED + "Radius must be 0 or greater.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_DEVELOPER_RADIUS_MUST_BE_0_GREATER_3));
return;
}
if (world == null || !IrisToolbelt.isIrisWorld(world)) {
sender().sendMessage(C.RED + "Target must be an Iris world.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_DEVELOPER_TARGET_MUST_BE_IRIS_WORLD));
return;
}
PlatformChunkGenerator access = IrisToolbelt.access(world);
if (access == null || access.getEngine() == null) {
sender().sendMessage(C.RED + "The engine access for this world is null.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_DEVELOPER_ENGINE_ACCESS_THIS_WORLD_IS_NULL_2));
return;
}
int chunks = (radius * 2 + 1) * (radius * 2 + 1);
sender().sendMessage(C.GREEN + "GoldenHash started: " + C.GOLD + chunks + C.GREEN
+ " chunk(s) around " + C.GOLD + centerX + "," + centerZ + C.GREEN
+ " in buffers (world untouched).");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_DEVELOPER_GOLDENHASH_STARTED_CHUNK_S_AROUND_BUFFERS_WORLD_UNTOUCHED, MessageArgument.untrusted("chunks", chunks), MessageArgument.untrusted("centerX", centerX), MessageArgument.untrusted("centerZ", centerZ)));
Iris.info("goldenhash start: world=" + world.getName()
+ " center=" + centerX + "," + centerZ
+ " radius=" + radius
@@ -33,86 +33,89 @@ import java.awt.Desktop;
import java.awt.GraphicsEnvironment;
@Director(name = "edit", origin = DirectorOrigin.PLAYER, description = "Edit something")
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.BukkitCommandMessagesExtended;
import art.arcane.volmlib.util.localization.MessageArgument;
@Director(name = "edit", origin = DirectorOrigin.PLAYER, description = "Edit something", descriptionKey = "iris.director.commandedit.director.edit_something")
public class CommandEdit implements DirectorExecutor {
private boolean noStudio() {
if (!sender().isPlayer()) {
sender().sendMessage(C.RED + "Players only!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_EDIT_PLAYERS_ONLY));
return true;
}
if (!Iris.service(StudioSVC.class).isProjectOpen()) {
sender().sendMessage(C.RED + "No studio world is open!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_EDIT_NO_STUDIO_WORLD_IS_OPEN));
return true;
}
if (!engine().isStudio()) {
sender().sendMessage(C.RED + "You must be in a studio world!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_EDIT_YOU_MUST_BE_STUDIO_WORLD));
return true;
}
if (GraphicsEnvironment.isHeadless()) {
sender().sendMessage(C.RED + "Cannot open files in headless environments!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_EDIT_CANNOT_OPEN_FILES_HEADLESS_ENVIRONMENTS));
return true;
}
if (!Desktop.isDesktopSupported()) {
sender().sendMessage(C.RED + "Desktop is not supported by this environment!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_EDIT_DESKTOP_IS_NOT_SUPPORTED_BY_THIS_ENVIRONMENT));
return true;
}
return false;
}
@Director(description = "Edit the biome you specified", aliases = {"b"}, origin = DirectorOrigin.PLAYER)
public void biome(@Param(contextual = false, description = "The biome to edit") IrisBiome biome) {
@Director(description = "Edit the biome you specified", descriptionKey = "iris.director.commandedit.director.edit_biome_you_specified", aliases = {"b"}, origin = DirectorOrigin.PLAYER)
public void biome(@Param(contextual = false, description = "The biome to edit", descriptionKey = "iris.director.commandedit.param.biome_edit") IrisBiome biome) {
if (noStudio()) {
return;
}
try {
if (biome == null || biome.getLoadFile() == null) {
sender().sendMessage(C.GOLD + "Cannot find the file; Perhaps it was not loaded directly from a file?");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_EDIT_CANNOT_FIND_FILE_PERHAPS_IT_WAS_NOT_LOADED_DIRECTLY_FROM));
return;
}
Desktop.getDesktop().open(biome.getLoadFile());
sender().sendMessage(C.GREEN + "Opening " + biome.getTypeName() + " " + biome.getLoadFile().getName().split("\\Q.\\E")[0] + " in VSCode! ");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_EDIT_OPENING_VSCODE, MessageArgument.untrusted("value", biome.getTypeName()), MessageArgument.untrusted("value2", biome.getLoadFile().getName().split("\\Q.\\E")[0])));
} catch (Throwable e) {
Iris.reportError(e);
sender().sendMessage(C.RED + "Cant find the file. Or registrant does not exist");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_EDIT_CANT_FIND_FILE_REGISTRANT_DOES_NOT_EXIST));
}
}
@Director(description = "Edit the region you specified", aliases = {"r"}, origin = DirectorOrigin.PLAYER)
public void region(@Param(contextual = false, description = "The region to edit") IrisRegion region) {
@Director(description = "Edit the region you specified", descriptionKey = "iris.director.commandedit.director.edit_region_you_specified", aliases = {"r"}, origin = DirectorOrigin.PLAYER)
public void region(@Param(contextual = false, description = "The region to edit", descriptionKey = "iris.director.commandedit.param.region_edit") IrisRegion region) {
if (noStudio()) {
return;
}
try {
if (region == null || region.getLoadFile() == null) {
sender().sendMessage(C.GOLD + "Cannot find the file; Perhaps it was not loaded directly from a file?");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_EDIT_CANNOT_FIND_FILE_PERHAPS_IT_WAS_NOT_LOADED_DIRECTLY_FROM_2));
return;
}
Desktop.getDesktop().open(region.getLoadFile());
sender().sendMessage(C.GREEN + "Opening " + region.getTypeName() + " " + region.getLoadFile().getName().split("\\Q.\\E")[0] + " in VSCode! ");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_EDIT_OPENING_VSCODE_2, MessageArgument.untrusted("value", region.getTypeName()), MessageArgument.untrusted("value2", region.getLoadFile().getName().split("\\Q.\\E")[0])));
} catch (Throwable e) {
Iris.reportError(e);
sender().sendMessage(C.RED + "Cant find the file. Or registrant does not exist");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_EDIT_CANT_FIND_FILE_REGISTRANT_DOES_NOT_EXIST_2));
}
}
@Director(description = "Edit the dimension you specified", aliases = {"d"}, origin = DirectorOrigin.PLAYER)
public void dimension(@Param(contextual = false, description = "The dimension to edit") IrisDimension dimension) {
@Director(description = "Edit the dimension you specified", descriptionKey = "iris.director.commandedit.director.edit_dimension_you_specified", aliases = {"d"}, origin = DirectorOrigin.PLAYER)
public void dimension(@Param(contextual = false, description = "The dimension to edit", descriptionKey = "iris.director.commandedit.param.dimension_edit") IrisDimension dimension) {
if (noStudio()) {
return;
}
try {
if (dimension == null || dimension.getLoadFile() == null) {
sender().sendMessage(C.GOLD + "Cannot find the file; Perhaps it was not loaded directly from a file?");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_EDIT_CANNOT_FIND_FILE_PERHAPS_IT_WAS_NOT_LOADED_DIRECTLY_FROM_3));
return;
}
Desktop.getDesktop().open(dimension.getLoadFile());
sender().sendMessage(C.GREEN + "Opening " + dimension.getTypeName() + " " + dimension.getLoadFile().getName().split("\\Q.\\E")[0] + " in VSCode! ");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_EDIT_OPENING_VSCODE_3, MessageArgument.untrusted("value", dimension.getTypeName()), MessageArgument.untrusted("value2", dimension.getLoadFile().getName().split("\\Q.\\E")[0])));
} catch (Throwable e) {
Iris.reportError(e);
sender().sendMessage(C.RED + "Cant find the file. Or registrant does not exist");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_EDIT_CANT_FIND_FILE_REGISTRANT_DOES_NOT_EXIST_3));
}
}
@@ -50,61 +50,65 @@ import org.bukkit.entity.Player;
import org.bukkit.generator.structure.Structure;
import org.bukkit.util.StructureSearchResult;
@Director(name = "find", origin = DirectorOrigin.PLAYER, description = "Iris Find commands", aliases = "goto")
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.BukkitCommandMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
import art.arcane.iris.core.localization.BukkitCommandMessagesExtended;
@Director(name = "find", origin = DirectorOrigin.PLAYER, description = "Iris Find commands", descriptionKey = "iris.director.commandfind.director.iris_find_commands", aliases = "goto")
public class CommandFind implements DirectorExecutor {
@Director(description = "Find a biome")
@Director(description = "Find a biome", descriptionKey = "iris.director.commandfind.director.find_biome")
public void biome(
@Param(description = "The biome to look for")
@Param(description = "The biome to look for", descriptionKey = "iris.director.commandfind.param.biome_look")
IrisBiome biome,
@Param(description = "Should you be teleported", defaultValue = "true")
@Param(description = "Should you be teleported", descriptionKey = "iris.director.commandfind.param.should_you_be_teleported", defaultValue = "true")
boolean teleport
) {
Engine e = engine();
if (e == null) {
sender().sendMessage(C.GOLD + "Not in an Iris World!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_FIND_NOT_IRIS_WORLD));
return;
}
EngineBukkitOps.gotoBiome(e, biome, player(), teleport);
}
@Director(description = "Find a region")
@Director(description = "Find a region", descriptionKey = "iris.director.commandfind.director.find_region")
public void region(
@Param(description = "The region to look for")
@Param(description = "The region to look for", descriptionKey = "iris.director.commandfind.param.region_look")
IrisRegion region,
@Param(description = "Should you be teleported", defaultValue = "true")
@Param(description = "Should you be teleported", descriptionKey = "iris.director.commandfind.param.should_you_be_teleported_2", defaultValue = "true")
boolean teleport
) {
Engine e = engine();
if (e == null) {
sender().sendMessage(C.GOLD + "Not in an Iris World!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_FIND_NOT_IRIS_WORLD_2));
return;
}
EngineBukkitOps.gotoRegion(e, region, player(), teleport);
}
@Director(description = "Find a point of interest.")
@Director(description = "Find a point of interest.", descriptionKey = "iris.director.commandfind.director.find_point_interest")
public void poi(
@Param(description = "The type of PoI to look for.")
@Param(description = "The type of PoI to look for.", descriptionKey = "iris.director.commandfind.param.type_poi_look")
String type,
@Param(description = "Should you be teleported", defaultValue = "true")
@Param(description = "Should you be teleported", descriptionKey = "iris.director.commandfind.param.should_you_be_teleported_3", defaultValue = "true")
boolean teleport
) {
Engine e = engine();
if (e == null) {
sender().sendMessage(C.GOLD + "Not in an Iris World!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_FIND_NOT_IRIS_WORLD_3));
return;
}
EngineBukkitOps.gotoPOI(e, type, player(), teleport);
}
@Director(description = "Find a structure (a vanilla key like minecraft:village_plains or minecraft:stronghold, or an imported iris structure key)", sync = true)
@Director(description = "Find a structure (a vanilla key like minecraft:village_plains or minecraft:stronghold, or an imported iris structure key)", descriptionKey = "iris.director.commandfind.director.find_structure_vanilla_key_like_minecraft_village_plains_minecraft_stronghold_imported_iris", sync = true)
public void structure(
@Param(description = "The structure to look for (e.g. minecraft:village_plains, minecraft:stronghold, minecraft_ancient_city)", customHandler = StructureHandler.class)
@Param(description = "The structure to look for (e.g. minecraft:village_plains, minecraft:stronghold, minecraft_ancient_city)", descriptionKey = "iris.director.commandfind.param.structure_look_e_g_minecraft_village_plains_minecraft_stronghold_minecraft_ancient_city", customHandler = StructureHandler.class)
String structure
) {
VolmitSender commandSender = sender();
@@ -116,7 +120,7 @@ public class CommandFind implements DirectorExecutor {
Engine e = engine();
if (e == null) {
commandSender.sendMessage(C.GOLD + "Not in an Iris World!");
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_FIND_NOT_IRIS_WORLD));
return;
}
@@ -143,19 +147,19 @@ public class CommandFind implements DirectorExecutor {
}
if (nativeStructure == null) {
commandSender.sendMessage(C.RED + "Unknown structure: " + structureKey);
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_FIND_UNKNOWN_STRUCTURE, MessageArgument.untrusted("structureKey", structureKey)));
return;
}
Player target = player();
if (target == null) {
commandSender.sendMessage(C.GOLD + "Run this in-game to teleport to a structure.");
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_FIND_RUN_THIS_GAME_TELEPORT_STRUCTURE));
return;
}
World targetWorld = target.getWorld();
Location origin = target.getLocation();
commandSender.sendMessage(C.GRAY + "Locating " + structureKey + "...");
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_FIND_LOCATING, MessageArgument.untrusted("structureKey", structureKey)));
J.s(() -> {
try {
if (!StructureReachability.isReachable(e, structureKey)) {
@@ -196,14 +200,14 @@ public class CommandFind implements DirectorExecutor {
private void locateIrisStructure(Engine engine, String structure, VolmitSender commandSender) {
Player target = player();
if (target == null) {
commandSender.sendMessage(C.GOLD + "Run this in-game to teleport to a structure.");
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_FIND_RUN_THIS_GAME_TELEPORT_STRUCTURE_2));
return;
}
World targetWorld = target.getWorld();
Location origin = target.getLocation();
int blockX = origin.getBlockX();
int blockZ = origin.getBlockZ();
commandSender.sendMessage(C.GRAY + "Locating " + structure + "...");
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_FIND_LOCATING_2, MessageArgument.untrusted("structure", structure)));
J.a(() -> {
try {
IrisStructureLocator.LocateResult result =
@@ -230,17 +234,17 @@ public class CommandFind implements DirectorExecutor {
});
}
@Director(description = "Find an object")
@Director(description = "Find an object", descriptionKey = "iris.director.commandfind.director.find_object")
public void object(
@Param(description = "The object to look for", customHandler = ObjectHandler.class)
@Param(description = "The object to look for", descriptionKey = "iris.director.commandfind.param.object_look", customHandler = ObjectHandler.class)
String object,
@Param(description = "Should you be teleported", defaultValue = "true")
@Param(description = "Should you be teleported", descriptionKey = "iris.director.commandfind.param.should_you_be_teleported_4", defaultValue = "true")
boolean teleport
) {
Engine e = engine();
if (e == null) {
sender().sendMessage(C.GOLD + "Not in an Iris World!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_FIND_NOT_IRIS_WORLD_4));
return;
}
@@ -248,7 +252,7 @@ public class CommandFind implements DirectorExecutor {
if (studioPlayer != null) {
try {
if (ObjectStudioSaveService.get().teleportTo(studioPlayer, object)) {
sender().sendMessage(C.GREEN + "Object Studio: teleporting to " + object);
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_FIND_OBJECT_STUDIO_TELEPORTING, MessageArgument.untrusted("object", object)));
return;
}
} catch (Throwable t) {
@@ -261,7 +265,7 @@ public class CommandFind implements DirectorExecutor {
return;
}
sender().sendMessage(C.RED + object + " is not configured in any region/biome object placements.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_FIND_IS_NOT_CONFIGURED_ANY_REGION_BIOME_OBJECT_PLACEMENTS, MessageArgument.untrusted("object", object)));
}
private void prepareStructureTeleport(Player target, World world, VolmitSender commandSender, String structure,
@@ -291,8 +295,7 @@ public class CommandFind implements DirectorExecutor {
Location destination = new Location(world, at.getBlockX() + 0.5, y, at.getBlockZ() + 0.5);
J.runEntity(target, () -> {
BukkitPlatform.teleportAsync(target, destination);
commandSender.sendMessage(C.GREEN + "Teleported to " + structure + " @ "
+ at.getBlockX() + ", " + y + ", " + at.getBlockZ());
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_FIND_TELEPORTED, MessageArgument.untrusted("structure", structure), MessageArgument.untrusted("value", at.getBlockX()), MessageArgument.untrusted("y", y), MessageArgument.untrusted("value2", at.getBlockZ())));
});
} catch (Throwable t) {
sendStructureMessage(target, commandSender, C.RED + "Could not prepare the destination for " + structure + ".");
@@ -67,7 +67,13 @@ import static art.arcane.iris.core.service.EditSVC.deletingWorld;
import static art.arcane.iris.util.common.misc.ServerProperties.BUKKIT_YML;
import static org.bukkit.Bukkit.getServer;
@Director(name = "iris", aliases = {"ir", "irs"}, description = "Basic Command")
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.IrisMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
import art.arcane.iris.core.localization.BukkitCommandMessages;
import art.arcane.iris.core.localization.BukkitCommandMessagesExtended;
import art.arcane.iris.core.localization.RuntimeUiMessages;
@Director(name = "iris", aliases = {"ir", "irs"}, description = "Basic Command", descriptionKey = "iris.director.commandiris.director.basic_command")
public class CommandIris implements DirectorExecutor {
private CommandStudio studio;
private CommandPregen pregen;
@@ -85,37 +91,37 @@ public class CommandIris implements DirectorExecutor {
String worldNameToCheck = "YourWorldName";
VolmitSender sender = Iris.getSender();
@Director(description = "Create a new world", aliases = {"c"})
@Director(description = "Create a new world", descriptionKey = "iris.director.commandiris.director.create_new_world", aliases = {"c"})
public void create(
@Param(aliases = "world-name", description = "The name of the world to create")
@Param(aliases = "world-name", description = "The name of the world to create", descriptionKey = "iris.director.commandiris.param.name_world_create")
String name,
@Param(
aliases = {"dimension", "pack"},
description = "The dimension/pack to create the world with",
description = "The dimension/pack to create the world with", descriptionKey = "iris.director.commandiris.param.dimension_pack_create_world_with",
defaultValue = "default",
customHandler = PackDimensionTypeHandler.class
)
String type,
@Param(description = "The seed to generate the world with", defaultValue = "1337")
@Param(description = "The seed to generate the world with", descriptionKey = "iris.director.commandiris.param.seed_generate_world_with", defaultValue = "1337")
long seed,
@Param(aliases = "main-world", description = "Whether or not to automatically use this world as the main world", defaultValue = "false")
@Param(aliases = "main-world", description = "Whether or not to automatically use this world as the main world", descriptionKey = "iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world", defaultValue = "false")
boolean main
) {
String worldName = IrisWorldStorage.logicalName(IrisWorldStorage.keyFromName(name));
if (worldName.equalsIgnoreCase("iris")) {
sender().sendMessage(C.RED + "You cannot use the world name \"iris\" for creating worlds as Iris uses this directory for studio worlds.");
sender().sendMessage(C.RED + "May we suggest the name \"IrisWorld\" instead?");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_YOU_CANNOT_USE_WORLD_NAME_IRIS_CREATING_WORLDS_AS_IRIS));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_MAY_WE_SUGGEST_NAME_IRISWORLD_INSTEAD));
return;
}
if (worldName.equalsIgnoreCase("benchmark")) {
sender().sendMessage(C.RED + "You cannot use the world name \"benchmark\" for creating worlds as Iris uses this directory for Benchmarking Packs.");
sender().sendMessage(C.RED + "May we suggest the name \"IrisWorld\" instead?");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_YOU_CANNOT_USE_WORLD_NAME_BENCHMARK_CREATING_WORLDS_AS_IRIS));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_MAY_WE_SUGGEST_NAME_IRISWORLD_INSTEAD_2));
return;
}
if (IrisWorldStorage.dimensionRoot(worldName).exists()) {
sender().sendMessage(C.RED + "That folder already exists!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_THAT_FOLDER_ALREADY_EXISTS));
return;
}
@@ -125,15 +131,15 @@ public class CommandIris implements DirectorExecutor {
IrisDimension dimension = IrisToolbelt.getDimension(resolvedType);
if (dimension == null) {
sender().sendMessage(C.RED + "Could not find or download dimension \"" + resolvedType + "\".");
sender().sendMessage(C.YELLOW + "Try one of: overworld, vanilla, flat, theend");
sender().sendMessage(C.YELLOW + "Or download manually: /iris download " + resolvedType);
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_COULD_NOT_FIND_DOWNLOAD_DIMENSION, MessageArgument.untrusted("resolvedType", resolvedType)));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_TRY_ONE_OVERWORLD_VANILLA_FLAT_THEEND));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_DOWNLOAD_MANUALLY_IRIS_DOWNLOAD, MessageArgument.untrusted("resolvedType", resolvedType)));
return;
}
if (J.isFolia()) {
if (stageFoliaWorldCreation(worldName, dimension, seed, main)) {
sender().sendMessage(C.GREEN + "World staging completed. Restart the server to generate/load \"" + worldName + "\".");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_WORLD_STAGING_COMPLETED_RESTART_SERVER_GENERATE_LOAD, MessageArgument.untrusted("worldName", worldName)));
}
return;
}
@@ -154,15 +160,15 @@ public class CommandIris implements DirectorExecutor {
}));
}
} catch (Throwable e) {
sender().sendMessage(C.RED + "Exception raised during creation. See the console for more details.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_EXCEPTION_RAISED_DURING_CREATION_SEE_CONSOLE_MORE_DETAILS));
Iris.reportError("Exception raised during world creation for \"" + worldName + "\".", e);
worldCreation = false;
return;
}
worldCreation = false;
sender().sendMessage(C.GREEN + "Successfully created your world!");
if (main) sender().sendMessage(C.GREEN + "Your world will automatically be set as the main world when the server restarts.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_SUCCESSFULLY_CREATED_YOUR_WORLD));
if (main) sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_YOUR_WORLD_WILL_AUTOMATICALLY_BE_SET_AS_MAIN_WORLD_WHEN));
}
private boolean updateMainWorld(String newName) {
@@ -216,13 +222,13 @@ public class CommandIris implements DirectorExecutor {
}
private boolean stageFoliaWorldCreation(String name, IrisDimension dimension, long seed, boolean main) {
sender().sendMessage(C.YELLOW + "Runtime world creation is disabled on Folia.");
sender().sendMessage(C.YELLOW + "Preparing world files and bukkit.yml for next startup...");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_RUNTIME_WORLD_CREATION_IS_DISABLED_ON_FOLIA));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_PREPARING_WORLD_FILES_BUKKIT_YML_NEXT_STARTUP));
File worldFolder = IrisWorldStorage.dimensionRoot(name);
IrisDimension installed = Iris.service(StudioSVC.class).installIntoWorld(sender(), dimension, worldFolder);
if (installed == null) {
sender().sendMessage(C.RED + "Failed to stage world files for dimension \"" + dimension.getLoadKey() + "\".");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_FAILED_STAGE_WORLD_FILES_DIMENSION, MessageArgument.untrusted("value", dimension.getLoadKey())));
return false;
}
@@ -232,16 +238,16 @@ public class CommandIris implements DirectorExecutor {
if (main) {
if (updateMainWorld(name)) {
sender().sendMessage(C.GREEN + "Updated server.properties level-name to \"" + name + "\".");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_UPDATED_SERVER_PROPERTIES_LEVEL_NAME, MessageArgument.untrusted("name", name)));
} else {
sender().sendMessage(C.RED + "World was staged, but failed to update server.properties main world.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_WORLD_WAS_STAGED_BUT_FAILED_UPDATE_SERVER_PROPERTIES_MAIN_WORLD));
return false;
}
}
sender().sendMessage(C.GREEN + "Staged Iris world \"" + name + "\" with generator Iris:" + dimension.getLoadKey() + " and seed " + seed + ".");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_STAGED_IRIS_WORLD_WITH_GENERATOR_IRIS_SEED, MessageArgument.untrusted("name", name), MessageArgument.untrusted("value", dimension.getLoadKey()), MessageArgument.untrusted("seed", seed)));
if (main) {
sender().sendMessage(C.GREEN + "This world is now configured as main for next restart.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_THIS_WORLD_IS_NOW_CONFIGURED_AS_MAIN_NEXT_RESTART));
}
return true;
}
@@ -269,18 +275,18 @@ public class CommandIris implements DirectorExecutor {
Iris.info("Registered \"" + logicalWorldName + "\" in bukkit.yml");
return true;
} catch (IOException e) {
sender().sendMessage(C.RED + "Failed to update bukkit.yml: " + e.getMessage());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_FAILED_UPDATE_BUKKIT_YML, MessageArgument.untrusted("value", String.valueOf(e.getMessage()))));
Iris.error("Failed to update bukkit.yml!");
Iris.reportError(e);
return false;
}
}
@Director(description = "Teleport to another world", aliases = {"tp"}, sync = true)
@Director(description = "Teleport to another world", descriptionKey = "iris.director.commandiris.director.teleport_another_world", aliases = {"tp"}, sync = true)
public void teleport(
@Param(description = "World to teleport to")
@Param(description = "World to teleport to", descriptionKey = "iris.director.commandiris.param.world_teleport")
World world,
@Param(description = "Player to teleport", defaultValue = "---", customHandler = NullablePlayerHandler.class)
@Param(description = "Player to teleport", descriptionKey = "iris.director.commandiris.param.player_teleport", defaultValue = "---", customHandler = NullablePlayerHandler.class)
Player player
) {
if (player == null && sender().isPlayer())
@@ -288,30 +294,33 @@ public class CommandIris implements DirectorExecutor {
final Player target = player;
if (target == null) {
sender().sendMessage(C.RED + "The specified player does not exist.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_SPECIFIED_PLAYER_DOES_NOT_EXIST));
return;
}
final Location spawn = world.getSpawnLocation();
final Runnable teleportTask = () -> {
BukkitPlatform.teleportAsync(target, spawn);
new VolmitSender(target).sendMessage(C.GREEN + "You have been teleported to " + world.getName() + ".");
new VolmitSender(target).sendMessage(C.GREEN + IrisLanguage.text(
RuntimeUiMessages.TELEPORTED_TO_WORLD,
MessageArgument.untrusted("world", world.getName())
));
};
if (!J.runEntity(target, teleportTask)) {
teleportTask.run();
}
}
@Director(description = "Print version information")
@Director(description = "Print version information", descriptionKey = "iris.director.commandiris.director.print_version_information")
public void version() {
sender().sendMessage(C.GREEN + "Iris v" + Iris.instance.getDescription().getVersion() + " by Volmit Software");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_IRIS_V_BY_VOLMIT_SOFTWARE, MessageArgument.untrusted("value", Iris.instance.getDescription().getVersion())));
}
/*
/todo
@Director(description = "Benchmark a pack", origin = DirectorOrigin.CONSOLE)
@Director(description = "Benchmark a pack", descriptionKey = "iris.director.commandiris.director.benchmark_pack", origin = DirectorOrigin.CONSOLE)
public void packbenchmark(
@Param(description = "Dimension to benchmark")
@Param(description = "Dimension to benchmark", descriptionKey = "iris.director.commandiris.param.dimension_benchmark")
IrisDimension type
) throws InterruptedException {
@@ -320,11 +329,11 @@ public class CommandIris implements DirectorExecutor {
IrisPackBenchmarking.runBenchmark();
} */
@Director(description = "Print world height information", origin = DirectorOrigin.PLAYER)
@Director(description = "Print world height information", descriptionKey = "iris.director.commandiris.director.print_world_height_information", origin = DirectorOrigin.PLAYER)
public void height() {
if (sender().isPlayer()) {
sender().sendMessage(C.GREEN + "" + sender().player().getWorld().getMinHeight() + " to " + sender().player().getWorld().getMaxHeight());
sender().sendMessage(C.GREEN + "Total Height: " + (sender().player().getWorld().getMaxHeight() - sender().player().getWorld().getMinHeight()));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_TO, MessageArgument.untrusted("value", sender().player().getWorld().getMinHeight()), MessageArgument.untrusted("value2", sender().player().getWorld().getMaxHeight())));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_TOTAL_HEIGHT, MessageArgument.untrusted("value", (sender().player().getWorld().getMaxHeight() - sender().player().getWorld().getMinHeight()))));
} else {
World mainWorld = getServer().getWorlds().get(0);
Iris.info(C.GREEN + "" + mainWorld.getMinHeight() + " to " + mainWorld.getMaxHeight());
@@ -332,7 +341,7 @@ public class CommandIris implements DirectorExecutor {
}
}
@Director(description = "Check access of all worlds.", aliases = {"accesslist"})
@Director(description = "Check access of all worlds.", descriptionKey = "iris.director.commandiris.director.check_access_all_worlds", aliases = {"accesslist"})
public void worlds() {
KList<World> IrisWorlds = new KList<>();
KList<World> BukkitWorlds = new KList<>();
@@ -349,13 +358,13 @@ public class CommandIris implements DirectorExecutor {
}
if (sender().isPlayer()) {
sender().sendMessage(C.BLUE + "Iris Worlds: ");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_IRIS_WORLDS));
for (World IrisWorld : IrisWorlds.copy()) {
sender().sendMessage(C.IRIS + "- " +IrisWorld.getName());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_MESSAGE, MessageArgument.untrusted("value", IrisWorld.getName())));
}
sender().sendMessage(C.GOLD + "Bukkit Worlds: ");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_BUKKIT_WORLDS));
for (World BukkitWorld : BukkitWorlds.copy()) {
sender().sendMessage(C.GRAY + "- " +BukkitWorld.getName());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_MESSAGE_2, MessageArgument.untrusted("value", BukkitWorld.getName())));
}
} else {
Iris.info(C.BLUE + "Iris Worlds: ");
@@ -370,37 +379,37 @@ public class CommandIris implements DirectorExecutor {
}
}
@Director(description = "Remove an Iris world", aliases = {"rm"}, sync = true)
@Director(description = "Remove an Iris world", descriptionKey = "iris.director.commandiris.director.remove_iris_world", aliases = {"rm"}, sync = true)
public void remove(
@Param(description = "The world to remove")
@Param(description = "The world to remove", descriptionKey = "iris.director.commandiris.param.world_remove")
World world,
@Param(description = "Whether to also remove the folder (if set to false, just does not load the world)", defaultValue = "true")
@Param(description = "Whether to also remove the folder (if set to false, just does not load the world)", descriptionKey = "iris.director.commandiris.param.whether_also_remove_folder_if_set_false_just_does_not_load_world", defaultValue = "true")
boolean delete
) {
if (!IrisToolbelt.isIrisWorld(world)) {
sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList()));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_THIS_IS_NOT_IRIS_WORLD_IRIS_WORLDS, MessageArgument.untrusted("value", String.join(", ", getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList()))));
return;
}
sender().sendMessage(C.GREEN + "Removing world: " + world.getName());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_REMOVING_WORLD, MessageArgument.untrusted("value", world.getName())));
if (!IrisToolbelt.evacuate(world)) {
sender().sendMessage(C.RED + "Failed to evacuate world: " + world.getName());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_FAILED_EVACUATE_WORLD, MessageArgument.untrusted("value", world.getName())));
return;
}
if (!WorldLifecycleService.get().unload(world, false)) {
sender().sendMessage(C.RED + "Failed to unload world: " + world.getName());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_FAILED_UNLOAD_WORLD, MessageArgument.untrusted("value", world.getName())));
return;
}
try {
if (IrisToolbelt.removeWorld(world)) {
sender().sendMessage(C.GREEN + "Successfully removed " + world.getName() + " from bukkit.yml");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_SUCCESSFULLY_REMOVED_FROM_BUKKIT_YML, MessageArgument.untrusted("value", world.getName())));
} else {
sender().sendMessage(C.YELLOW + "Looks like the world was already removed from bukkit.yml");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_LOOKS_LIKE_WORLD_WAS_ALREADY_REMOVED_FROM_BUKKIT_YML));
}
} catch (IOException e) {
sender().sendMessage(C.RED + "Failed to save bukkit.yml because of " + e.getMessage());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_FAILED_SAVE_BUKKIT_YML_BECAUSE, MessageArgument.untrusted("value", String.valueOf(e.getMessage()))));
Iris.reportError("Failed to remove world \"" + world.getName() + "\" from bukkit.yml.", e);
}
IrisToolbelt.evacuate(world, "Deleting world");
@@ -414,16 +423,16 @@ public class CommandIris implements DirectorExecutor {
int retries = 12;
if (deleteDirectory(world.getWorldFolder())) {
sender.sendMessage(C.GREEN + "Successfully removed world folder");
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_IRIS_SUCCESSFULLY_REMOVED_WORLD_FOLDER));
} else {
while(true){
if (deleteDirectory(world.getWorldFolder())){
sender.sendMessage(C.GREEN + "Successfully removed world folder");
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_IRIS_SUCCESSFULLY_REMOVED_WORLD_FOLDER_2));
break;
}
retries--;
if (retries == 0){
sender.sendMessage(C.RED + "Failed to remove world folder");
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_IRIS_FAILED_REMOVE_WORLD_FOLDER));
break;
}
J.sleep(3000);
@@ -446,78 +455,90 @@ public class CommandIris implements DirectorExecutor {
return dir.delete();
}
@Director(description = "Toggle debug")
@Director(description = "Toggle debug", descriptionKey = "iris.director.commandiris.director.toggle_debug")
public void debug() {
boolean to = !IrisSettings.get().getGeneral().isDebug();
IrisSettings.get().getGeneral().setDebug(to);
IrisSettings.get().forceSave();
sender().sendMessage(C.GREEN + "Set debug to: " + to);
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_SET_DEBUG, MessageArgument.untrusted("to", to)));
}
@Director(description = "Download a project.", aliases = "dl")
@Director(description = "Download a project.", descriptionKey = "iris.director.commandiris.director.download_project", aliases = "dl")
public void download(
@Param(name = "pack", description = "The pack to download", aliases = "project")
@Param(name = "pack", description = "The pack to download", descriptionKey = "iris.director.commandiris.param.pack_download", aliases = "project")
String pack,
@Param(name = "branch", description = "The branch to download from", defaultValue = "stable")
@Param(name = "branch", description = "The branch to download from", descriptionKey = "iris.director.commandiris.param.branch_download_from", defaultValue = "stable")
String branch,
@Param(name = "overwrite", description = "Whether or not to overwrite the pack with the downloaded one", aliases = "force", defaultValue = "false")
@Param(name = "overwrite", description = "Whether or not to overwrite the pack with the downloaded one", descriptionKey = "iris.director.commandiris.param.whether_not_overwrite_pack_with_downloaded_one", aliases = "force", defaultValue = "false")
boolean overwrite
) {
if (PackDownloader.isDefaultOverworld(pack)) {
sender().sendMessage(C.GREEN + "Downloading pack: " + pack + " (beta release)" + (overwrite ? " overwriting" : ""));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_DOWNLOADING_PACK_BETA_RELEASE, MessageArgument.untrusted("pack", pack), MessageArgument.trusted("value", overwrite ? IrisLanguage.text(RuntimeUiMessages.DOWNLOAD_OVERWRITE_SUFFIX) : "")));
Iris.service(StudioSVC.class).downloadDefaultOverworld(sender(), overwrite);
} else {
sender().sendMessage(C.GREEN + "Downloading pack: " + pack + "/" + branch + (overwrite ? " overwriting" : ""));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_DOWNLOADING_PACK, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("branch", branch), MessageArgument.trusted("value", overwrite ? IrisLanguage.text(RuntimeUiMessages.DOWNLOAD_OVERWRITE_SUFFIX) : "")));
Iris.service(StudioSVC.class).downloadSearch(sender(), "IrisDimensions/" + pack + "/" + branch, overwrite);
}
ServerConfigurator.installDataPacksIfChanged(true);
}
@Director(description = "Get metrics for your world", aliases = "measure", origin = DirectorOrigin.PLAYER)
@Director(description = "Get metrics for your world", descriptionKey = "iris.director.commandiris.director.get_metrics_your_world", aliases = "measure", origin = DirectorOrigin.PLAYER)
public void metrics() {
if (!IrisToolbelt.isIrisWorld(world())) {
sender().sendMessage(C.RED + "You must be in an Iris world");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_YOU_MUST_BE_IRIS_WORLD));
return;
}
sender().sendMessage(C.GREEN + "Sending metrics...");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_SENDING_METRICS));
engine().printMetrics(sender());
}
@Director(description = "Reload configuration file (this is also done automatically)")
@Director(description = "Reload configuration file (this is also done automatically)", descriptionKey = "iris.director.commandiris.director.reload_configuration_file_this_is_also_done_automatically")
public void reload() {
IrisSettings.invalidate();
IrisSettings.get();
sender().sendMessage(C.GREEN + "Hotloaded settings");
boolean localeLoaded = IrisLanguage.reload();
if (localeLoaded) {
sender().sendMessage(C.GREEN + IrisLanguage.text(
IrisMessages.COMMAND_RELOAD_SUCCESS,
MessageArgument.trusted("locale", IrisLanguage.activeLocale())
));
return;
}
sender().sendMessage(C.YELLOW + IrisLanguage.text(
IrisMessages.COMMAND_RELOAD_FAILED,
MessageArgument.untrusted("locale", IrisSettings.get().getGeneral().getLanguage()),
MessageArgument.trusted("activeLocale", IrisLanguage.activeLocale())
));
}
@Director(description = "Unload an Iris World", origin = DirectorOrigin.PLAYER, sync = true)
@Director(description = "Unload an Iris World", descriptionKey = "iris.director.commandiris.director.unload_iris_world", origin = DirectorOrigin.PLAYER, sync = true)
public void unloadWorld(
@Param(description = "The world to unload")
@Param(description = "The world to unload", descriptionKey = "iris.director.commandiris.param.world_unload")
World world
) {
if (!IrisToolbelt.isIrisWorld(world)) {
sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList()));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_THIS_IS_NOT_IRIS_WORLD_IRIS_WORLDS_2, MessageArgument.untrusted("value", String.join(", ", getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList()))));
return;
}
sender().sendMessage(C.GREEN + "Unloading world: " + world.getName());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_UNLOADING_WORLD, MessageArgument.untrusted("value", world.getName())));
try {
IrisToolbelt.evacuate(world);
boolean unloaded = WorldLifecycleService.get().unload(world, false);
if (unloaded) {
sender().sendMessage(C.GREEN + "World unloaded successfully.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_WORLD_UNLOADED_SUCCESSFULLY));
} else {
sender().sendMessage(C.RED + "Failed to unload the world.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_FAILED_UNLOAD_WORLD_2));
}
} catch (Exception e) {
sender().sendMessage(C.RED + "Failed to unload the world: " + e.getMessage());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_FAILED_UNLOAD_WORLD_3, MessageArgument.untrusted("value", String.valueOf(e.getMessage()))));
Iris.reportError("Failed to unload world \"" + world.getName() + "\".", e);
}
}
@Director(description = "Load an Iris World", origin = DirectorOrigin.PLAYER, sync = true, aliases = {"import"})
@Director(description = "Load an Iris World", descriptionKey = "iris.director.commandiris.director.load_iris_world", origin = DirectorOrigin.PLAYER, sync = true, aliases = {"import"})
public void loadWorld(
@Param(description = "The name of the world to load")
@Param(description = "The name of the world to load", descriptionKey = "iris.director.commandiris.param.name_world_load")
String world
) {
String logicalWorldName = IrisWorldStorage.logicalName(IrisWorldStorage.keyFromName(world));
@@ -526,7 +547,7 @@ public class CommandIris implements DirectorExecutor {
WorldEngine = logicalWorldName;
if (!worldExists) {
sender().sendMessage(C.YELLOW + logicalWorldName + " Doesnt exist on the server.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_DOESNT_EXIST_ON_SERVER, MessageArgument.untrusted("logicalWorldName", logicalWorldName)));
return;
}
@@ -541,45 +562,45 @@ public class CommandIris implements DirectorExecutor {
String fileName = file.getName();
if (fileName.endsWith(".json")) {
dimension = fileName.substring(0, fileName.length() - 5);
sender().sendMessage(C.BLUE + "Generator: " + dimension);
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_GENERATOR, MessageArgument.untrusted("dimension", dimension)));
}
}
}
}
} else {
sender().sendMessage(C.GOLD + logicalWorldName + " is not an iris world.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_IS_NOT_IRIS_WORLD, MessageArgument.untrusted("logicalWorldName", logicalWorldName)));
return;
}
if (dimension == null) {
sender().sendMessage(C.RED + "Could not determine Iris dimension for " + logicalWorldName + ".");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_COULD_NOT_DETERMINE_IRIS_DIMENSION, MessageArgument.untrusted("logicalWorldName", logicalWorldName)));
return;
}
sender().sendMessage(C.GREEN + "Loading world: " + logicalWorldName);
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_LOADING_WORLD, MessageArgument.untrusted("logicalWorldName", logicalWorldName)));
if (!registerWorldInBukkitYml(logicalWorldName, dimension, null)) {
return;
}
if (J.isFolia()) {
sender().sendMessage(C.YELLOW + "Folia cannot load new worlds at runtime. Restart the server to load \"" + logicalWorldName + "\".");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_FOLIA_CANNOT_LOAD_NEW_WORLDS_AT_RUNTIME_RESTART_SERVER_LOAD, MessageArgument.untrusted("logicalWorldName", logicalWorldName)));
return;
}
Iris.instance.checkForBukkitWorlds(logicalWorldName::equals);
sender().sendMessage(C.GREEN + logicalWorldName + " loaded successfully.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_LOADED_SUCCESSFULLY, MessageArgument.untrusted("logicalWorldName", logicalWorldName)));
}
@Director(description = "Evacuate an iris world", origin = DirectorOrigin.PLAYER, sync = true)
@Director(description = "Evacuate an iris world", descriptionKey = "iris.director.commandiris.director.evacuate_iris_world", origin = DirectorOrigin.PLAYER, sync = true)
public void evacuate(
@Param(description = "Evacuate the world")
@Param(description = "Evacuate the world", descriptionKey = "iris.director.commandiris.param.evacuate_world")
World world
) {
if (!IrisToolbelt.isIrisWorld(world)) {
sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList()));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_THIS_IS_NOT_IRIS_WORLD_IRIS_WORLDS_3, MessageArgument.untrusted("value", String.join(", ", getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList()))));
return;
}
sender().sendMessage(C.GREEN + "Evacuating world" + world.getName());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_EVACUATING_WORLD, MessageArgument.untrusted("value", world.getName())));
IrisToolbelt.evacuate(world);
}
@@ -83,13 +83,18 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
@Director(name = "object", aliases = "o", origin = DirectorOrigin.PLAYER, description = "Iris object manipulation")
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.BukkitCommandMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
import art.arcane.iris.core.localization.BukkitCommandMessagesExtended;
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")
public class CommandObject implements DirectorExecutor {
@Director(description = "Open an object studio world (grid of every object; dimension optional, defaults to 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(
@Param(defaultValue = "null", description = "Optional dimension whose object pack to lay out; omit to aggregate objects from every pack", 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)
IrisDimension dimension,
@Param(defaultValue = "1337", description = "The seed to generate the studio with", aliases = "s")
@Param(defaultValue = "1337", description = "The seed to generate the studio with", descriptionKey = "iris.director.commandobject.param.seed_generate_studio_with", aliases = "s")
long seed
) {
VolmitSender commandSender = sender();
@@ -130,7 +135,7 @@ public class CommandObject implements DirectorExecutor {
}
if (hostDimension == null || sources.isEmpty()) {
commandSender.sendMessage(C.RED + "No packs with objects were found on this server.");
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_OBJECT_NO_PACKS_WITH_OBJECTS_WERE_FOUND_ON_THIS_SERVER));
return;
}
@@ -140,7 +145,7 @@ public class CommandObject implements DirectorExecutor {
if (k != null) totalObjects += k.length;
}
if (totalObjects == 0) {
commandSender.sendMessage(C.RED + "No objects to place across the selected pack(s).");
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_OBJECT_NO_OBJECTS_PLACE_ACROSS_SELECTED_PACK_S));
return;
}
@@ -151,8 +156,7 @@ public class CommandObject implements DirectorExecutor {
String scope = dimension == null
? ("all packs [" + sources.size() + "]")
: ("\"" + hostDimension.getName() + "\"");
commandSender.sendMessage(C.GREEN + "Opening Object Studio for " + scope + " ("
+ totalObjects + " objects)");
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_OBJECT_OPENING_OBJECT_STUDIO_OBJECTS, MessageArgument.untrusted("scope", scope), MessageArgument.untrusted("totalObjects", totalObjects)));
IrisDimension finalHost = hostDimension;
try {
@@ -176,7 +180,7 @@ public class CommandObject implements DirectorExecutor {
});
} catch (Throwable e) {
Iris.reportError("Failed to open object studio world \"" + finalHost.getLoadKey() + "\".", e);
commandSender.sendMessage(C.RED + "Failed to open object studio: " + e.getMessage());
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_OBJECT_FAILED_OPEN_OBJECT_STUDIO, MessageArgument.untrusted("value", String.valueOf(e.getMessage()))));
}
}
@@ -269,14 +273,14 @@ public class CommandObject implements DirectorExecutor {
};
}
@Director(description = "Check the composition of an object")
@Director(description = "Check the composition of an object", descriptionKey = "iris.director.commandobject.director.check_composition_object")
public void analyze(
@Param(description = "The object to analyze", customHandler = ObjectHandler.class)
@Param(description = "The object to analyze", descriptionKey = "iris.director.commandobject.param.object_analyze", customHandler = ObjectHandler.class)
String object
) {
IrisObject o = IrisData.loadAnyObject(object, data());
sender().sendMessage("Object Size: " + o.getW() + " * " + o.getH() + " * " + o.getD() + "");
sender().sendMessage("Blocks Used: " + NumberFormat.getIntegerInstance().format(o.getBlocks().size()));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_OBJECT_SIZE, MessageArgument.untrusted("value", o.getW()), MessageArgument.untrusted("value2", o.getH()), MessageArgument.untrusted("value3", o.getD())));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_BLOCKS_USED, MessageArgument.untrusted("value", NumberFormat.getIntegerInstance().format(o.getBlocks().size()))));
var queue = o.getBlocks().values();
Map<Material, Set<BlockData>> unsorted = new HashMap<>();
@@ -309,7 +313,7 @@ public class CommandObject implements DirectorExecutor {
.sorted().toList();
Set<Material> sortedMats = new TreeSet<>(Comparator.comparingInt(materials::get).reversed());
sortedMats.addAll(sortedMatsList);
sender().sendMessage("== Blocks in object ==");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_BLOCKS_OBJECT));
int n = 0;
for (Material mat : sortedMats) {
@@ -331,35 +335,35 @@ public class CommandObject implements DirectorExecutor {
n++;
if (n >= 10) {
sender().sendMessage(" + " + (sortedMats.size() - n) + " other block types");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_OTHER_BLOCK_TYPES, MessageArgument.untrusted("value", (sortedMats.size() - n))));
return;
}
}
}
@Director(description = "Shrink an object to its minimum size")
public void shrink(@Param(description = "The object to shrink", customHandler = ObjectHandler.class) String object) {
@Director(description = "Shrink an object to its minimum size", descriptionKey = "iris.director.commandobject.director.shrink_object_its_minimum_size")
public void shrink(@Param(description = "The object to shrink", descriptionKey = "iris.director.commandobject.param.object_shrink", customHandler = ObjectHandler.class) String object) {
IrisObject o = IrisData.loadAnyObject(object, data());
sender().sendMessage("Current Object Size: " + o.getW() + " * " + o.getH() + " * " + o.getD());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_CURRENT_OBJECT_SIZE, MessageArgument.untrusted("value", o.getW()), MessageArgument.untrusted("value2", o.getH()), MessageArgument.untrusted("value3", o.getD())));
o.shrinkwrap();
sender().sendMessage("New Object Size: " + o.getW() + " * " + o.getH() + " * " + o.getD());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_NEW_OBJECT_SIZE, MessageArgument.untrusted("value", o.getW()), MessageArgument.untrusted("value2", o.getH()), MessageArgument.untrusted("value3", o.getD())));
try {
o.write(o.getLoadFile());
} catch (IOException e) {
sender().sendMessage("Failed to save object " + o.getLoadFile() + ": " + e.getMessage());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_FAILED_SAVE_OBJECT, MessageArgument.untrusted("value", o.getLoadFile()), MessageArgument.untrusted("value2", String.valueOf(e.getMessage()))));
e.printStackTrace();
}
}
@Director(description = "Grow organic branches through the canopy so every leaf survives vanilla decay",
@Director(description = "Grow organic branches through the canopy so every leaf survives vanilla decay", descriptionKey = "iris.director.commandobject.director.grow_organic_branches_through_canopy_so_every_leaf_survives_vanilla_decay",
origin = DirectorOrigin.BOTH)
public void plausibilize(
@Param(description = "Object key, prefix (trees/), or filesystem path",
@Param(description = "Object key, prefix (trees/), or filesystem path", descriptionKey = "iris.director.commandobject.param.object_key_prefix_trees_filesystem_path",
customHandler = ObjectTargetHandler.class)
String target,
@Param(name = "dryrun", description = "dryrun=true analyzes only, writes nothing", defaultValue = "false")
@Param(name = "dryrun", description = "dryrun=true analyzes only, writes nothing", descriptionKey = "iris.director.commandobject.param.dryrun_true_analyzes_only_writes_nothing", defaultValue = "false")
boolean dryRun,
@Param(name = "reach", description = "reach=N max branch length in blocks from existing wood; farther leaf clusters are pinned persistent instead. reach=0 grows unlimited", defaultValue = "12")
@Param(name = "reach", description = "reach=N max branch length in blocks from existing wood; farther leaf clusters are pinned persistent instead. reach=0 grows unlimited", descriptionKey = "iris.director.commandobject.param.reach_n_max_branch_length_blocks_from_existing_wood_farther_leaf_clusters", defaultValue = "12")
int reach
) {
IrisData nearest = data();
@@ -368,19 +372,23 @@ public class CommandObject implements DirectorExecutor {
targets = resolveFromPacks(target);
}
if (targets.isEmpty()) {
sender().sendMessage(C.RED + "No objects matched: " + target);
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_NO_OBJECTS_MATCHED, MessageArgument.untrusted("target", target)));
return;
}
sender().sendMessage(C.IRIS + "Plausibilize [reach=" + reach + (dryRun ? ", DRY" : "")
+ "] queued " + targets.size() + " object(s)");
sender().sendMessage(IrisLanguage.text(
BukkitCommandMessagesExtended.COMMAND_OBJECT_PLAUSIBILIZE_REACH_QUEUED_OBJECT_S,
MessageArgument.trusted("reach", reach),
MessageArgument.trusted("value", dryRun ? IrisLanguage.text(RuntimeUiMessages.TREE_DRY_SUFFIX) : ""),
MessageArgument.trusted("value2", targets.size())
));
org.bukkit.command.CommandSender s = sender();
List<TreePlausibilizeBatch.Target> queued = targets;
J.a(() -> TreePlausibilizeBatch.run(queued, dryRun, reach, nearest, (String line) ->
s.sendMessage(line.startsWith("Done:") || line.startsWith("Totals:") || line.startsWith("[")
? C.IRIS + line
: C.GRAY + " " + line)));
J.a(() -> TreePlausibilizeBatch.run(queued, dryRun, reach, nearest, (TreePlausibilizeBatch.Output output) ->
s.sendMessage(output.headline()
? C.IRIS + output.text()
: C.GRAY + " " + output.text())));
}
private static List<TreePlausibilizeBatch.Target> resolveFromPacks(String target) {
@@ -408,7 +416,7 @@ public class CommandObject implements DirectorExecutor {
return out;
}
@Director(description = "Convert .schem files in the 'convert' folder to .iob files.")
@Director(description = "Convert .schem files in the 'convert' folder to .iob files.", descriptionKey = "iris.director.commandobject.director.convert_schem_files_convert_folder_iob_files")
public void convert () {
try {
IrisConverter.convertSchematics(sender());
@@ -418,26 +426,26 @@ public class CommandObject implements DirectorExecutor {
}
@Director(description = "Get a powder that reveals objects", aliases = "d")
@Director(description = "Get a powder that reveals objects", descriptionKey = "iris.director.commandobject.director.get_powder_that_reveals_objects", aliases = "d")
public void dust() {
player().getInventory().addItem(WandSVC.createDust());
sender().playSound(Sound.AMBIENT_SOUL_SAND_VALLEY_ADDITIONS, 1f, 1.5f);
}
@Director(description = "Contract a selection based on your looking direction", aliases = "-")
@Director(description = "Contract a selection based on your looking direction", descriptionKey = "iris.director.commandobject.director.contract_selection_based_on_your_looking_direction", aliases = "-")
public void contract(
@Param(description = "The amount to inset by", defaultValue = "1")
@Param(description = "The amount to inset by", descriptionKey = "iris.director.commandobject.param.amount_inset_by", defaultValue = "1")
int amount
) {
if (!WandSVC.isHoldingWand(player())) {
sender().sendMessage("Hold your wand.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_HOLD_YOUR_WAND));
return;
}
Location[] b = WandSVC.getCuboid(player());
if (b == null || b[0] == null || b[1] == null) {
sender().sendMessage("No area selected.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_NO_AREA_SELECTED));
return;
}
Location a1 = b[0].clone();
@@ -453,13 +461,13 @@ public class CommandObject implements DirectorExecutor {
sender().playSound(Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 1f, 0.55f);
}
@Director(description = "Set point 1 to look", aliases = "p1")
@Director(description = "Set point 1 to look", descriptionKey = "iris.director.commandobject.director.set_point_1_look", aliases = "p1")
public void position1(
@Param(description = "Whether to use your current position, or where you look", defaultValue = "true")
@Param(description = "Whether to use your current position, or where you look", descriptionKey = "iris.director.commandobject.param.whether_use_your_current_position_where_you_look", defaultValue = "true")
boolean here
) {
if (!WandSVC.isHoldingWand(player())) {
sender().sendMessage("Ready your Wand.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_READY_YOUR_WAND));
return;
}
@@ -479,13 +487,13 @@ public class CommandObject implements DirectorExecutor {
}
}
@Director(description = "Set point 2 to look", aliases = "p2")
@Director(description = "Set point 2 to look", descriptionKey = "iris.director.commandobject.director.set_point_2_look", aliases = "p2")
public void position2(
@Param(description = "Whether to use your current position, or where you look", defaultValue = "true")
@Param(description = "Whether to use your current position, or where you look", descriptionKey = "iris.director.commandobject.param.whether_use_your_current_position_where_you_look_2", defaultValue = "true")
boolean here
) {
if (!WandSVC.isHoldingWand(player())) {
sender().sendMessage("Ready your Wand.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_READY_YOUR_WAND_2));
return;
}
@@ -506,24 +514,24 @@ public class CommandObject implements DirectorExecutor {
}
}
@Director(description = "Paste an object", sync = true)
@Director(description = "Paste an object", descriptionKey = "iris.director.commandobject.director.paste_object", sync = true)
public void paste(
@Param(description = "The object to paste", customHandler = ObjectHandler.class)
@Param(description = "The object to paste", descriptionKey = "iris.director.commandobject.param.object_paste", customHandler = ObjectHandler.class)
String object,
@Param(description = "Whether or not to edit the object (need to hold wand)", defaultValue = "false")
@Param(description = "Whether or not to edit the object (need to hold wand)", descriptionKey = "iris.director.commandobject.param.whether_not_edit_object_need_hold_wand", defaultValue = "false")
boolean edit,
@Param(description = "The amount of degrees to rotate by", defaultValue = "0")
@Param(description = "The amount of degrees to rotate by", descriptionKey = "iris.director.commandobject.param.amount_degrees_rotate_by", defaultValue = "0")
int rotate,
@Param(description = "The factor by which to scale the object placement", defaultValue = "1")
@Param(description = "The factor by which to scale the object placement", descriptionKey = "iris.director.commandobject.param.factor_by_which_scale_object_placement", defaultValue = "1")
double scale
// ,
// @Param(description = "The scale interpolator to use", defaultValue = "none")
// @Param(description = "The scale interpolator to use", descriptionKey = "iris.director.commandobject.param.scale_interpolator_use", defaultValue = "none")
// IrisObjectPlacementScaleInterpolator interpolator
) {
IrisObject o = IrisData.loadAnyObject(object, data());
double maxScale = Double.max(10 - o.getBlocks().size() / 10000d, 1);
if (scale > maxScale) {
sender().sendMessage(C.YELLOW + "Indicated scale exceeds maximum. Downscaled to maximum: " + maxScale);
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_INDICATED_SCALE_EXCEEDS_MAXIMUM_DOWNSCALED_MAXIMUM, MessageArgument.untrusted("maxScale", maxScale)));
scale = maxScale;
}
@@ -552,70 +560,70 @@ public class CommandObject implements DirectorExecutor {
if (WandSVC.isWand(wand)) {
wand = newWand;
player().getInventory().setItemInMainHand(wand);
sender().sendMessage("Updated wand for " + "objects/" + o.getLoadKey() + ".iob ");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_UPDATED_WAND_OBJECTS_IOB, MessageArgument.untrusted("value", o.getLoadKey())));
} else {
int slot = WandSVC.findWand(player().getInventory());
if (slot == -1) {
player().getInventory().addItem(newWand);
sender().sendMessage("Given new wand for " + "objects/" + o.getLoadKey() + ".iob ");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_GIVEN_NEW_WAND_OBJECTS_IOB, MessageArgument.untrusted("value", o.getLoadKey())));
} else {
player().getInventory().setItem(slot, newWand);
sender().sendMessage("Updated wand for " + "objects/" + o.getLoadKey() + ".iob ");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_UPDATED_WAND_OBJECTS_IOB_2, MessageArgument.untrusted("value", o.getLoadKey())));
}
}
} else {
sender().sendMessage(C.IRIS + "Placed " + object);
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_PLACED, MessageArgument.untrusted("object", object)));
}
}
@Director(description = "Save an object")
@Director(description = "Save an object", descriptionKey = "iris.director.commandobject.director.save_object")
public void save(
@Param(description = "The dimension to store the object in", contextual = true)
@Param(description = "The dimension to store the object in", descriptionKey = "iris.director.commandobject.param.dimension_store_object", contextual = true)
IrisDimension dimension,
@Param(description = "The file to store it in, can use / for subfolders")
@Param(description = "The file to store it in, can use / for subfolders", descriptionKey = "iris.director.commandobject.param.file_store_it_can_use_subfolders")
String name,
@Param(description = "Overwrite existing object files", defaultValue = "false", aliases = "force")
@Param(description = "Overwrite existing object files", descriptionKey = "iris.director.commandobject.param.overwrite_existing_object_files", defaultValue = "false", aliases = "force")
boolean overwrite,
@Param(description = "Use legacy TileState serialization if possible", defaultValue = "true")
@Param(description = "Use legacy TileState serialization if possible", descriptionKey = "iris.director.commandobject.param.use_legacy_tilestate_serialization_if_possible", defaultValue = "true")
boolean legacy
) {
IrisObject o = WandSVC.createSchematic(player(), legacy);
if (o == null) {
sender().sendMessage(C.YELLOW + "You need to hold your wand!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_YOU_NEED_HOLD_YOUR_WAND));
return;
}
File file = Iris.service(StudioSVC.class).getWorkspaceFile(dimension.getLoadKey(), "objects", name + ".iob");
if (file.exists() && !overwrite) {
sender().sendMessage(C.RED + "File already exists. Set overwrite=true to overwrite it.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_FILE_ALREADY_EXISTS_SET_OVERWRITE_TRUE_OVERWRITE_IT));
return;
}
try {
o.write(file, sender());
} catch (IOException e) {
sender().sendMessage(C.RED + "Failed to save object because of an IOException: " + e.getMessage());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_FAILED_SAVE_OBJECT_BECAUSE_IOEXCEPTION, MessageArgument.untrusted("value", String.valueOf(e.getMessage()))));
Iris.reportError(e);
}
sender().playSound(Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1f, 1.5f);
sender().sendMessage(C.GREEN + "Successfully object to saved: " + dimension.getLoadKey() + "/objects/" + name);
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_SUCCESSFULLY_OBJECT_SAVED_OBJECTS, MessageArgument.untrusted("value", dimension.getLoadKey()), MessageArgument.untrusted("name", name)));
}
@Director(description = "Shift a selection in your looking direction")
@Director(description = "Shift a selection in your looking direction", descriptionKey = "iris.director.commandobject.director.shift_selection_your_looking_direction")
public void shift(
@Param(description = "The amount to shift by", defaultValue = "1")
@Param(description = "The amount to shift by", descriptionKey = "iris.director.commandobject.param.amount_shift_by", defaultValue = "1")
int amount
) {
if (!WandSVC.isHoldingWand(player())) {
sender().sendMessage("Hold your wand.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_HOLD_YOUR_WAND_2));
return;
}
Location[] b = WandSVC.getCuboid(player());
if (b == null || b[0] == null || b[1] == null) {
sender().sendMessage("No area selected.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_NO_AREA_SELECTED_2));
return;
}
Location a1 = b[0].clone();
@@ -634,52 +642,52 @@ public class CommandObject implements DirectorExecutor {
sender().playSound(Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 1f, 0.55f);
}
@Director(description = "Undo a number of pastes", aliases = "u")
@Director(description = "Undo a number of pastes", descriptionKey = "iris.director.commandobject.director.undo_number_pastes", aliases = "u")
public void undo(
@Param(description = "The amount of pastes to undo", defaultValue = "1")
@Param(description = "The amount of pastes to undo", descriptionKey = "iris.director.commandobject.param.amount_pastes_undo", defaultValue = "1")
int amount
) {
ObjectSVC service = Iris.service(ObjectSVC.class);
int actualReverts = Math.min(service.getUndos().size(), amount);
service.revertChanges(actualReverts);
sender().sendMessage(C.BLUE + "Reverted " + actualReverts + C.BLUE +" pastes!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_REVERTED_PASTES, MessageArgument.untrusted("actualReverts", actualReverts)));
}
@Director(description = "Gets an object wand and grabs the current WorldEdit selection.", aliases = "we", origin = DirectorOrigin.PLAYER)
@Director(description = "Gets an object wand and grabs the current WorldEdit selection.", descriptionKey = "iris.director.commandobject.director.gets_object_wand_grabs_current_worldedit_selection", aliases = "we", origin = DirectorOrigin.PLAYER)
public void we() {
if (!Bukkit.getPluginManager().isPluginEnabled("WorldEdit")) {
sender().sendMessage(C.RED + "You can't get a WorldEdit selection without WorldEdit, you know.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_YOU_CAN_T_GET_WORLDEDIT_SELECTION_WITHOUT_WORLDEDIT_YOU_KNOW));
return;
}
Cuboid locs = WorldEditLink.getSelection(sender().player());
if (locs == null) {
sender().sendMessage(C.RED + "You don't have a WorldEdit selection in this world.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_YOU_DON_T_HAVE_WORLDEDIT_SELECTION_THIS_WORLD));
return;
}
sender().player().getInventory().addItem(WandSVC.createWand(locs.getLowerNE(), locs.getUpperSW()));
sender().sendMessage(C.GREEN + "A fresh wand with your current WorldEdit selection on it!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_FRESH_WAND_WITH_YOUR_CURRENT_WORLDEDIT_SELECTION_ON_IT));
}
@Director(description = "Get an object wand", sync = true)
@Director(description = "Get an object wand", descriptionKey = "iris.director.commandobject.director.get_object_wand", sync = true)
public void wand() {
player().getInventory().addItem(WandSVC.createWand());
sender().playSound(Sound.ITEM_ARMOR_EQUIP_NETHERITE, 1f, 1.5f);
sender().sendMessage(C.GREEN + "Poof! Good luck building!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_POOF_GOOD_LUCK_BUILDING));
}
@Director(name = "x&y", description = "Autoselect up, down & out", sync = true)
@Director(name = "x&y", description = "Autoselect up, down & out", descriptionKey = "iris.director.commandobject.director.autoselect_up_down_out", sync = true)
public void xay() {
if (!WandSVC.isHoldingWand(player())) {
sender().sendMessage(C.YELLOW + "Hold your wand!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_HOLD_YOUR_WAND_3));
return;
}
Location[] b = WandSVC.getCuboid(player());
if (b == null || b[0] == null || b[1] == null) {
sender().sendMessage("No area selected.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_NO_AREA_SELECTED_3));
return;
}
Location a1 = b[0].clone();
@@ -718,19 +726,19 @@ public class CommandObject implements DirectorExecutor {
player().getInventory().setItemInMainHand(WandSVC.createWand(b[0], b[1]));
player().updateInventory();
sender().playSound(Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 1f, 0.55f);
sender().sendMessage(C.GREEN + "Auto-select complete!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_AUTO_SELECT_COMPLETE));
}
@Director(name = "x+y", description = "Autoselect up & out", sync = true)
@Director(name = "x+y", description = "Autoselect up & out", descriptionKey = "iris.director.commandobject.director.autoselect_up_out", sync = true)
public void xpy() {
if (!WandSVC.isHoldingWand(player())) {
sender().sendMessage(C.YELLOW + "Hold your wand!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_HOLD_YOUR_WAND_4));
return;
}
Location[] b = WandSVC.getCuboid(player());
if (b == null || b[0] == null || b[1] == null) {
sender().sendMessage("No area selected.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_NO_AREA_SELECTED_4));
return;
}
b[0].add(new Vector(0, 1, 0));
@@ -759,6 +767,6 @@ public class CommandObject implements DirectorExecutor {
player().getInventory().setItemInMainHand(WandSVC.createWand(b[0], b[1]));
player().updateInventory();
sender().playSound(Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 1f, 0.55f);
sender().sendMessage(C.GREEN + "Auto-select complete!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_OBJECT_AUTO_SELECT_COMPLETE_2));
}
}
@@ -25,32 +25,36 @@ import art.arcane.iris.core.pack.PackValidationRegistry;
import art.arcane.iris.core.pack.PackValidationResult;
import art.arcane.iris.core.pack.PackValidator;
import art.arcane.iris.util.common.director.DirectorExecutor;
import art.arcane.iris.util.common.format.C;
import art.arcane.iris.util.common.plugin.VolmitSender;
import art.arcane.volmlib.util.director.annotations.Director;
import art.arcane.volmlib.util.director.annotations.Param;
import art.arcane.volmlib.util.localization.TextKey;
import java.io.File;
import java.util.List;
@Director(name = "pack", aliases = {"pk"}, description = "Pack validation and maintenance")
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.BukkitCommandMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
import art.arcane.iris.core.localization.BukkitRuntimeMessages;
@Director(name = "pack", aliases = {"pk"}, description = "Pack validation and maintenance", descriptionKey = "iris.director.commandpack.director.pack_validation_maintenance")
public class CommandPack implements DirectorExecutor {
@Director(description = "Validate a pack (or all packs) and re-publish results", aliases = {"v"})
@Director(description = "Validate a pack (or all packs) and re-publish results", descriptionKey = "iris.director.commandpack.director.validate_pack_all_packs_re_publish_results", aliases = {"v"})
public void validate(
@Param(description = "The pack folder name to validate (leave empty for all)", defaultValue = "")
@Param(description = "The pack folder name to validate (leave empty for all)", descriptionKey = "iris.director.commandpack.param.pack_folder_name_validate_leave_empty_all", defaultValue = "")
String pack
) {
VolmitSender s = sender();
File packsRoot = Iris.instance.getDataFolder("packs");
if (!packsRoot.isDirectory()) {
s.sendMessage(C.RED + "packs/ folder not found.");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_PACKS_FOLDER_NOT_FOUND));
return;
}
if (pack == null || pack.isBlank()) {
File[] dirs = packsRoot.listFiles(File::isDirectory);
if (dirs == null || dirs.length == 0) {
s.sendMessage(C.YELLOW + "No packs to validate.");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_NO_PACKS_VALIDATE));
return;
}
int broken = 0;
@@ -60,23 +64,23 @@ public class CommandPack implements DirectorExecutor {
broken++;
}
}
s.sendMessage(C.GREEN + "Validation complete. Broken packs: " + broken + "/" + dirs.length);
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_VALIDATION_COMPLETE_BROKEN_PACKS, MessageArgument.untrusted("broken", String.valueOf(broken)), MessageArgument.untrusted("value", String.valueOf(dirs.length))));
return;
}
File target = PackDirectoryResolver.resolveExisting(packsRoot, pack);
if (target == null) {
s.sendMessage(C.RED + "Pack '" + pack + "' not found under packs/.");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_PACK_NOT_FOUND_UNDER_PACKS, MessageArgument.untrusted("pack", String.valueOf(pack))));
return;
}
runValidate(s, target);
}
@Director(description = "Preview or apply unused-resource cleanup", aliases = {"c"})
@Director(description = "Preview or apply unused-resource cleanup", descriptionKey = "iris.director.commandpack.director.preview_apply_unused_resource_cleanup", aliases = {"c"})
public void cleanup(
@Param(description = "The pack folder name to clean")
@Param(description = "The pack folder name to clean", descriptionKey = "iris.director.commandpack.param.pack_folder_name_clean")
String pack,
@Param(description = "preview or apply", defaultValue = "preview")
@Param(description = "preview or apply", descriptionKey = "iris.director.commandpack.param.preview_apply", defaultValue = "preview")
String mode
) {
VolmitSender s = sender();
@@ -87,43 +91,47 @@ public class CommandPack implements DirectorExecutor {
if ("apply".equalsIgnoreCase(mode)) {
PackResourceCleanup.ApplyResult result = PackResourceCleanup.apply(packFolder);
if (!result.success()) {
s.sendMessage(C.RED + result.error());
reportPaths(s, result.quarantinedPaths(), "still quarantined");
s.sendMessage(IrisLanguage.text(
BukkitRuntimeMessages.COMMAND_PACK_CLEANUP_FAILED,
MessageArgument.untrusted("error", String.valueOf(result.error()))
));
reportPaths(s, result.quarantinedPaths(), BukkitRuntimeMessages.COMMAND_PACK_PATH_STILL_QUARANTINED);
return;
}
if (!result.changed()) {
s.sendMessage(C.GREEN + "No cleanup candidates found for pack '" + pack + "'.");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_NO_CLEANUP_CANDIDATES_FOUND_PACK, MessageArgument.untrusted("pack", String.valueOf(pack))));
return;
}
s.sendMessage(C.GREEN + "Quarantined " + result.quarantinedPaths().size()
+ " cleanup candidate(s) under " + result.quarantinePath() + ".");
reportPaths(s, result.quarantinedPaths(), "quarantined");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_QUARANTINED_CLEANUP_CANDIDATE_S_UNDER, MessageArgument.untrusted("size", String.valueOf(result.quarantinedPaths().size())), MessageArgument.untrusted("quarantinePath", String.valueOf(result.quarantinePath()))));
reportPaths(s, result.quarantinedPaths(), BukkitRuntimeMessages.COMMAND_PACK_PATH_QUARANTINED);
return;
}
if (!"preview".equalsIgnoreCase(mode)) {
s.sendMessage(C.RED + "Cleanup mode must be preview or apply.");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_CLEANUP_MODE_MUST_BE_PREVIEW_APPLY));
return;
}
PackResourceCleanup.Preview preview = PackResourceCleanup.preview(packFolder);
if (!preview.success()) {
s.sendMessage(C.RED + preview.error());
s.sendMessage(IrisLanguage.text(
BukkitRuntimeMessages.COMMAND_PACK_CLEANUP_FAILED,
MessageArgument.untrusted("error", String.valueOf(preview.error()))
));
return;
}
if (!preview.hasCandidates()) {
s.sendMessage(C.GREEN + "No cleanup candidates found for pack '" + pack + "'.");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_NO_CLEANUP_CANDIDATES_FOUND_PACK_2, MessageArgument.untrusted("pack", String.valueOf(pack))));
return;
}
s.sendMessage(C.YELLOW + "Cleanup preview for pack '" + pack + "': "
+ preview.candidatePaths().size() + " candidate(s). No files were changed.");
reportPaths(s, preview.candidatePaths(), "candidate");
s.sendMessage(C.GRAY + "Run /iris pack cleanup " + pack + " mode=apply to quarantine these candidates after a fresh scan.");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_CLEANUP_PREVIEW_PACK_CANDIDATE_S_NO_FILES_WERE_CHANGED, MessageArgument.untrusted("pack", String.valueOf(pack)), MessageArgument.untrusted("size", String.valueOf(preview.candidatePaths().size()))));
reportPaths(s, preview.candidatePaths(), BukkitRuntimeMessages.COMMAND_PACK_PATH_CANDIDATE);
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_RUN_IRIS_PACK_CLEANUP_MODE_APPLY_QUARANTINE_THESE_CANDIDATES_AFTER_FRESH_SCAN, MessageArgument.untrusted("pack", String.valueOf(pack))));
}
@Director(description = "Preview or apply restoration of the latest quarantine", aliases = {"r"})
@Director(description = "Preview or apply restoration of the latest quarantine", descriptionKey = "iris.director.commandpack.director.preview_apply_restoration_latest_quarantine", aliases = {"r"})
public void restore(
@Param(description = "The pack folder name to restore")
@Param(description = "The pack folder name to restore", descriptionKey = "iris.director.commandpack.param.pack_folder_name_restore")
String pack,
@Param(description = "preview or apply", defaultValue = "preview")
@Param(description = "preview or apply", descriptionKey = "iris.director.commandpack.param.preview_apply_2", defaultValue = "preview")
String mode
) {
VolmitSender s = sender();
@@ -134,69 +142,78 @@ public class CommandPack implements DirectorExecutor {
if ("apply".equalsIgnoreCase(mode)) {
PackResourceCleanup.RestoreResult result = PackResourceCleanup.restoreLatest(packFolder);
if (!result.conflicts().isEmpty()) {
s.sendMessage(C.RED + "Restore refused because " + result.conflicts().size() + " destination(s) already exist.");
reportPaths(s, result.conflicts(), "conflict");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_RESTORE_REFUSED_BECAUSE_DESTINATION_S_ALREADY_EXIST, MessageArgument.untrusted("size", String.valueOf(result.conflicts().size()))));
reportPaths(s, result.conflicts(), BukkitRuntimeMessages.COMMAND_PACK_PATH_CONFLICT);
return;
}
if (!result.success()) {
s.sendMessage(C.RED + result.error());
s.sendMessage(IrisLanguage.text(
BukkitRuntimeMessages.COMMAND_PACK_RESTORE_FAILED,
MessageArgument.untrusted("error", String.valueOf(result.error()))
));
return;
}
if (!result.changed()) {
s.sendMessage(C.YELLOW + "Nothing to restore for pack '" + pack + "'.");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_NOTHING_RESTORE_PACK, MessageArgument.untrusted("pack", String.valueOf(pack))));
return;
}
s.sendMessage(C.GREEN + "Restored " + result.restoredPaths().size()
+ " file(s) from " + result.dumpPath() + ".");
reportPaths(s, result.restoredPaths(), "restored");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_RESTORED_FILE_S_FROM, MessageArgument.untrusted("size", String.valueOf(result.restoredPaths().size())), MessageArgument.untrusted("dumpPath", String.valueOf(result.dumpPath()))));
reportPaths(s, result.restoredPaths(), BukkitRuntimeMessages.COMMAND_PACK_PATH_RESTORED);
return;
}
if (!"preview".equalsIgnoreCase(mode)) {
s.sendMessage(C.RED + "Restore mode must be preview or apply.");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_RESTORE_MODE_MUST_BE_PREVIEW_APPLY));
return;
}
PackResourceCleanup.RestorePreview preview = PackResourceCleanup.previewRestore(packFolder);
if (!preview.success()) {
s.sendMessage(C.RED + preview.error());
s.sendMessage(IrisLanguage.text(
BukkitRuntimeMessages.COMMAND_PACK_RESTORE_FAILED,
MessageArgument.untrusted("error", String.valueOf(preview.error()))
));
return;
}
if (!preview.hasFiles()) {
s.sendMessage(C.YELLOW + "Nothing to restore for pack '" + pack + "'.");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_NOTHING_RESTORE_PACK_2, MessageArgument.untrusted("pack", String.valueOf(pack))));
return;
}
s.sendMessage(C.YELLOW + "Restore preview for " + preview.dumpPath() + ": "
+ preview.filePaths().size() + " file(s). No files were changed.");
reportPaths(s, preview.filePaths(), "file");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_RESTORE_PREVIEW_FILE_S_NO_FILES_WERE_CHANGED, MessageArgument.untrusted("dumpPath", String.valueOf(preview.dumpPath())), MessageArgument.untrusted("size", String.valueOf(preview.filePaths().size()))));
reportPaths(s, preview.filePaths(), BukkitRuntimeMessages.COMMAND_PACK_PATH_FILE);
if (!preview.conflicts().isEmpty()) {
s.sendMessage(C.RED + "Restore is blocked by " + preview.conflicts().size() + " existing destination(s).");
reportPaths(s, preview.conflicts(), "conflict");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_RESTORE_IS_BLOCKED_BY_EXISTING_DESTINATION_S, MessageArgument.untrusted("size", String.valueOf(preview.conflicts().size()))));
reportPaths(s, preview.conflicts(), BukkitRuntimeMessages.COMMAND_PACK_PATH_CONFLICT);
return;
}
s.sendMessage(C.GRAY + "Run /iris pack restore " + pack + " mode=apply to restore after a fresh conflict check.");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_RUN_IRIS_PACK_RESTORE_MODE_APPLY_RESTORE_AFTER_FRESH_CONFLICT_CHECK, MessageArgument.untrusted("pack", String.valueOf(pack))));
}
@Director(description = "Show cached validation status for a pack", aliases = {"s"})
@Director(description = "Show cached validation status for a pack", descriptionKey = "iris.director.commandpack.director.show_cached_validation_status_pack", aliases = {"s"})
public void status(
@Param(description = "The pack folder name", defaultValue = "")
@Param(description = "The pack folder name", descriptionKey = "iris.director.commandpack.param.pack_folder_name", defaultValue = "")
String pack
) {
VolmitSender s = sender();
if (pack == null || pack.isBlank()) {
if (PackValidationRegistry.snapshot().isEmpty()) {
s.sendMessage(C.YELLOW + "No validation results recorded. Run /iris pack validate first.");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_NO_VALIDATION_RESULTS_RECORDED_RUN_IRIS_PACK_VALIDATE_FIRST));
return;
}
PackValidationRegistry.snapshot().forEach((name, result) -> {
String tag = result.isLoadable() ? (C.GREEN + "OK") : (C.RED + "BROKEN");
s.sendMessage(tag + C.RESET + " " + name
+ C.GRAY + " (blocking=" + result.getBlockingErrors().size()
+ ", warnings=" + result.getWarnings().size() + ")");
TextKey key = result.isLoadable()
? BukkitRuntimeMessages.COMMAND_PACK_STATUS_OK
: BukkitRuntimeMessages.COMMAND_PACK_STATUS_BROKEN;
s.sendMessage(IrisLanguage.text(
key,
MessageArgument.untrusted("pack", name),
MessageArgument.trusted("blocking", result.getBlockingErrors().size()),
MessageArgument.trusted("warnings", result.getWarnings().size())
));
});
return;
}
PackValidationResult result = PackValidationRegistry.get(pack);
if (result == null) {
s.sendMessage(C.YELLOW + "No validation result for '" + pack + "'. Run /iris pack validate " + pack + ".");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_NO_VALIDATION_RESULT_RUN_IRIS_PACK_VALIDATE, MessageArgument.untrusted("pack", String.valueOf(pack)), MessageArgument.untrusted("pack2", String.valueOf(pack))));
return;
}
reportResult(s, result);
@@ -210,50 +227,56 @@ public class CommandPack implements DirectorExecutor {
return result;
} catch (Throwable e) {
Iris.reportError("Pack validation failed for '" + packFolder.getName() + "'", e);
s.sendMessage(C.RED + "Validation of '" + packFolder.getName() + "' failed: " + e.getMessage());
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_VALIDATION_FAILED, MessageArgument.untrusted("name", String.valueOf(packFolder.getName())), MessageArgument.untrusted("error", String.valueOf(e.getMessage()))));
return null;
}
}
private void reportResult(VolmitSender s, PackValidationResult result) {
if (result.isLoadable()) {
s.sendMessage(C.GREEN + "Pack '" + result.getPackName() + "' is loadable."
+ C.GRAY + " (warnings=" + result.getWarnings().size() + ")");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_PACK_IS_LOADABLE_WARNINGS, MessageArgument.untrusted("packName", String.valueOf(result.getPackName())), MessageArgument.untrusted("size", String.valueOf(result.getWarnings().size()))));
} else {
s.sendMessage(C.RED + "Pack '" + result.getPackName() + "' is BROKEN:");
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_PACK_IS_BROKEN, MessageArgument.untrusted("packName", String.valueOf(result.getPackName()))));
for (String reason : result.getBlockingErrors()) {
s.sendMessage(C.RED + " - " + reason);
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_MESSAGE, MessageArgument.untrusted("reason", String.valueOf(reason))));
}
}
int wMax = Math.min(10, result.getWarnings().size());
for (int i = 0; i < wMax; i++) {
s.sendMessage(C.YELLOW + " ! " + result.getWarnings().get(i));
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_MESSAGE_2, MessageArgument.untrusted("value", String.valueOf(result.getWarnings().get(i)))));
}
if (result.getWarnings().size() > wMax) {
s.sendMessage(C.GRAY + " ... and " + (result.getWarnings().size() - wMax) + " more warning(s).");
s.sendMessage(IrisLanguage.text(
BukkitRuntimeMessages.COMMAND_PACK_MORE_WARNING_S,
MessageArgument.trusted("count", result.getWarnings().size() - wMax)
));
}
}
private File findPack(VolmitSender sender, String pack) {
if (pack == null || pack.isBlank()) {
sender.sendMessage(C.RED + "You must specify a pack name.");
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_PACK_YOU_MUST_SPECIFY_PACK_NAME));
return null;
}
File packFolder = PackDirectoryResolver.resolveExisting(Iris.instance.getDataFolder("packs"), pack);
if (packFolder == null) {
sender.sendMessage(C.RED + "Pack '" + pack + "' not found under packs/.");
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_PACK_PACK_NOT_FOUND_UNDER_PACKS, MessageArgument.untrusted("pack", pack)));
return null;
}
return packFolder;
}
private void reportPaths(VolmitSender sender, List<String> paths, String label) {
private void reportPaths(VolmitSender sender, List<String> paths, TextKey label) {
int max = Math.min(10, paths.size());
for (int i = 0; i < max; i++) {
sender.sendMessage(C.GRAY + " - " + label + ": " + paths.get(i));
sender.sendMessage(IrisLanguage.text(
BukkitCommandMessages.COMMAND_PACK_MESSAGE,
MessageArgument.untrusted("label", IrisLanguage.text(label)),
MessageArgument.untrusted("value", paths.get(i))
));
}
if (paths.size() > max) {
sender.sendMessage(C.GRAY + " ... and " + (paths.size() - max) + " more.");
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_PACK_MORE, MessageArgument.untrusted("value", (paths.size() - max))));
}
}
}
@@ -31,34 +31,38 @@ import art.arcane.volmlib.util.math.Position2;
import org.bukkit.World;
import org.bukkit.util.Vector;
@Director(name = "pregen", aliases = "pregenerate", description = "Pregenerate your Iris worlds!")
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.BukkitCommandMessagesExtended;
import art.arcane.iris.core.localization.RuntimeUiMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
@Director(name = "pregen", aliases = "pregenerate", description = "Pregenerate your Iris worlds!", descriptionKey = "iris.director.commandpregen.director.pregenerate_your_iris_worlds")
public class CommandPregen implements DirectorExecutor {
@Director(description = "Pregenerate a world")
@Director(description = "Pregenerate a world", descriptionKey = "iris.director.commandpregen.director.pregenerate_world")
public void start(
@Param(description = "The radius of the pregen in blocks", aliases = "size")
@Param(description = "The radius of the pregen in blocks", descriptionKey = "iris.director.commandpregen.param.radius_pregen_blocks", aliases = "size")
int radius,
@Param(description = "The world to pregen", contextual = true)
@Param(description = "The world to pregen", descriptionKey = "iris.director.commandpregen.param.world_pregen", contextual = true)
World world,
@Param(aliases = "middle", description = "The center location of the pregen. Use \"me\" for your current location", defaultValue = "0,0")
@Param(aliases = "middle", description = "The center location of the pregen. Use \"me\" for your current location", descriptionKey = "iris.director.commandpregen.param.center_location_pregen_use_me_your_current_location", defaultValue = "0,0")
Vector center,
@Param(description = "Open the Iris pregen gui", defaultValue = "true")
@Param(description = "Open the Iris pregen gui", descriptionKey = "iris.director.commandpregen.param.open_iris_pregen_gui", defaultValue = "true")
boolean gui,
@Param(name = "serial", description = "Generate only one chunk at a time", defaultValue = "false")
@Param(name = "serial", description = "Generate only one chunk at a time", descriptionKey = "iris.director.commandpregen.param.generate_only_one_chunk_at_time", defaultValue = "false")
boolean serial
) {
if (radius <= 0) {
sender().sendMessage(C.RED + "Pregen radius must be greater than zero blocks.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_PREGEN_PREGEN_RADIUS_MUST_BE_GREATER_THAN_ZERO_BLOCKS));
return;
}
if (serial && !IrisToolbelt.supportsStrictSerialPregeneration()) {
sender().sendMessage(C.RED + "Strict serial pregeneration requires Paper or a Paper-compatible server.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_PREGEN_STRICT_SERIAL_PREGENERATION_REQUIRES_PAPER_PAPER_COMPATIBLE_SERVER));
return;
}
try {
if (sender().isPlayer() && access() == null) {
sender().sendMessage(C.RED + "The engine access for this world is null!");
sender().sendMessage(C.RED + "Please make sure the world is loaded & the engine is initialized. Generate a new chunk, for example.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_PREGEN_ENGINE_ACCESS_THIS_WORLD_IS_NULL));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_PREGEN_PLEASE_MAKE_SURE_WORLD_IS_LOADED_ENGINE_IS_INITIALIZED_GENERATE));
}
PregenTask task = PregenTask
.builder()
@@ -76,48 +80,42 @@ public class CommandPregen implements DirectorExecutor {
sender().sendMessage(msg);
Iris.info(msg);
} catch (Throwable e) {
sender().sendMessage(C.RED + "Failed to start pregeneration. See console for details.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_PREGEN_FAILED_START_PREGENERATION_SEE_CONSOLE_DETAILS));
Iris.reportError(e);
e.printStackTrace();
}
}
@Director(description = "Stop the active pregeneration task", aliases = "x")
@Director(description = "Stop the active pregeneration task", descriptionKey = "iris.director.commandpregen.director.stop_active_pregeneration_task", aliases = "x")
public void stop() {
if (PregeneratorJob.shutdownInstance()) {
String message = C.BLUE + "Pregen stop requested; finishing active work before cancellation.";
sender().sendMessage(message);
Iris.info(message);
} else {
sender().sendMessage(C.YELLOW + "No active pregeneration tasks to stop");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_PREGEN_NO_ACTIVE_PREGENERATION_TASKS_STOP));
}
}
@Director(description = "Pause / continue the active pregeneration task", aliases = {"resume"})
@Director(description = "Pause / continue the active pregeneration task", descriptionKey = "iris.director.commandpregen.director.pause_continue_active_pregeneration_task", aliases = {"resume"})
public void pause() {
if (PregeneratorJob.pauseResume()) {
sender().sendMessage(C.GREEN + "Paused/unpaused pregeneration task, now: " + (PregeneratorJob.isPaused() ? "Paused" : "Running") + ".");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_PREGEN_PAUSED_UNPAUSED_PREGENERATION_TASK_NOW, MessageArgument.trusted("value", IrisLanguage.text(PregeneratorJob.isPaused() ? RuntimeUiMessages.STATUS_PAUSED : RuntimeUiMessages.STATUS_RUNNING))));
} else {
sender().sendMessage(C.YELLOW + "No active pregeneration tasks to pause/unpause.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_PREGEN_NO_ACTIVE_PREGENERATION_TASKS_PAUSE_UNPAUSE));
}
}
@Director(description = "Show the active pregeneration status")
@Director(description = "Show the active pregeneration status", descriptionKey = "iris.director.commandpregen.director.show_active_pregeneration_status")
public void status() {
PregeneratorJob.PregenProgress progress = PregeneratorJob.progressSnapshot();
if (progress == null) {
sender().sendMessage(C.YELLOW + "No active pregeneration task.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_PREGEN_NO_ACTIVE_PREGENERATION_TASK));
return;
}
String world = progress.worldName() == null ? "?" : progress.worldName();
sender().sendMessage(C.GREEN + "Pregen " + C.GOLD + world + C.GREEN + ": " + C.GOLD + Form.f(progress.generated()) + "/" + Form.f(progress.totalChunks())
+ C.GREEN + " (" + C.GOLD + String.format("%.1f", progress.percent()) + "%" + C.GREEN + ")"
+ (progress.paused() ? C.YELLOW + " PAUSED" : ""));
sender().sendMessage(C.GREEN + "Speed: " + C.GOLD + Form.f((int) progress.chunksPerSecond()) + "/s" + C.GREEN
+ " ETA: " + C.GOLD + Form.duration(progress.eta(), 2) + C.GREEN
+ " Elapsed: " + C.GOLD + Form.duration(progress.elapsed(), 2) + C.GREEN
+ " Method: " + C.GOLD + progress.method()
+ (progress.failed() > 0 ? C.RED + " Failed: " + Form.f(progress.failed()) : ""));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_PREGEN_PREGEN, MessageArgument.untrusted("world", world), MessageArgument.untrusted("value", Form.f(progress.generated())), MessageArgument.untrusted("value2", Form.f(progress.totalChunks())), MessageArgument.untrusted("value3", String.format("%.1f", progress.percent())), MessageArgument.untrusted("value4", (progress.paused() ? C.YELLOW + " PAUSED" : ""))));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_PREGEN_SPEED_S_ETA_ELAPSED_METHOD, MessageArgument.untrusted("value", Form.f((int) progress.chunksPerSecond())), MessageArgument.untrusted("value2", Form.duration(progress.eta(), 2)), MessageArgument.untrusted("value3", Form.duration(progress.elapsed(), 2)), MessageArgument.untrusted("value4", progress.method()), MessageArgument.untrusted("value5", (progress.failed() > 0 ? C.RED + " Failed: " + Form.f(progress.failed()) : ""))));
}
}
@@ -67,67 +67,71 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
@Director(name = "structure", aliases = {"struct", "str"}, description = "Iris structure tools (index, import, info)")
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.BukkitCommandMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
import art.arcane.iris.core.localization.BukkitCommandMessagesExtended;
@Director(name = "structure", aliases = {"struct", "str"}, description = "Iris structure tools (index, import, info)", descriptionKey = "iris.director.commandstructure.director.iris_structure_tools_index_import_info")
public class CommandStructure implements DirectorExecutor {
@Director(description = "Regenerate structure-index.json listing all vanilla, datapack & iris structures", aliases = {"ls"}, origin = DirectorOrigin.BOTH)
@Director(description = "Regenerate structure-index.json listing all vanilla, datapack & iris structures", descriptionKey = "iris.director.commandstructure.director.regenerate_structure_index_json_listing_all_vanilla_datapack_iris_structures", aliases = {"ls"}, origin = DirectorOrigin.BOTH)
public void list(
@Param(description = "The dimension whose pack to index", aliases = "dim")
@Param(description = "The dimension whose pack to index", descriptionKey = "iris.director.commandstructure.param.dimension_whose_pack_index", aliases = "dim")
IrisDimension dimension
) {
IrisData data = dimension.getLoader();
if (data == null) {
sender().sendMessage(C.RED + "Could not resolve the pack for dimension " + dimension.getLoadKey());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_COULD_NOT_RESOLVE_PACK_DIMENSION, MessageArgument.untrusted("value", dimension.getLoadKey())));
return;
}
File file = StructureIndexService.write(data);
sender().sendMessage(C.GREEN + "Wrote structure index: " + C.WHITE + file.getPath());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_WROTE_STRUCTURE_INDEX, MessageArgument.untrusted("value", file.getPath())));
}
@Director(name = "import", description = "Import EVERY structure - vanilla AND ingested datapacks - into this pack as editable Iris resources, always overwriting. Rebuilds jigsaw structures (villages, outposts, datapack jigsaws) as editable pool/piece graphs, imports every structure template NBT as an object, and assembles the multi-template structures (shipwrecks, ruined portals, ocean ruins, nether fossils). Run after ingesting a datapack and restarting. Regenerate chunks or use a fresh world for the imported copies to place.", aliases = {"import-all", "reimport", "imp", "all"}, origin = DirectorOrigin.BOTH)
@Director(name = "import", description = "Import EVERY structure - vanilla AND ingested datapacks - into this pack as editable Iris resources, always overwriting. Rebuilds jigsaw structures (villages, outposts, datapack jigsaws) as editable pool/piece graphs, imports every structure template NBT as an object, and assembles the multi-template structures (shipwrecks, ruined portals, ocean ruins, nether fossils). Run after ingesting a datapack and restarting. Regenerate chunks or use a fresh world for the imported copies to place.", descriptionKey = "iris.director.commandstructure.director.import_every_structure_vanilla_ingested_datapacks_into_this_pack_as_editable_iris", aliases = {"import-all", "reimport", "imp", "all"}, origin = DirectorOrigin.BOTH)
public void importAll(
@Param(description = "The dimension whose pack to import into", aliases = "dim")
@Param(description = "The dimension whose pack to import into", descriptionKey = "iris.director.commandstructure.param.dimension_whose_pack_import_into", aliases = "dim")
IrisDimension dimension
) {
IrisData data = dimension.getLoader();
if (data == null) {
sender().sendMessage(C.RED + "Could not resolve the pack for dimension " + dimension.getLoadKey());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_COULD_NOT_RESOLVE_PACK_DIMENSION_2, MessageArgument.untrusted("value", dimension.getLoadKey())));
return;
}
sender().sendMessage(C.GREEN + "Importing all vanilla & datapack structures into " + C.WHITE + dimension.getLoadKey() + C.GREEN + " (overwrite)...");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_IMPORTING_ALL_VANILLA_DATAPACK_STRUCTURES_INTO_OVERWRITE, MessageArgument.untrusted("value", dimension.getLoadKey())));
BulkStructureImporter.Report jigsaws = BulkStructureImporter.importAllVanilla(data, StructureImporter.Mode.OVERWRITE, true, sender());
BulkStructureImporter.Report templates = BulkStructureImporter.importAllTemplates(data, StructureImporter.Mode.OVERWRITE, sender());
BulkStructureImporter.Report groups = BulkStructureImporter.importTemplateGroups(data, StructureImporter.Mode.OVERWRITE, sender());
StructureCaptureImporter.Report captured = StructureCaptureImporter.importAllStructures(data, StructureImporter.Mode.OVERWRITE, sender());
int imported = jigsaws.imported() + templates.imported() + groups.imported() + captured.imported();
int failed = jigsaws.failed() + templates.failed() + groups.failed() + captured.failed();
sender().sendMessage(C.GREEN + "Import complete: " + C.WHITE + imported + C.GREEN + " structures/objects written, " + C.WHITE + failed + C.GREEN + " failed.");
sender().sendMessage(C.GRAY + "Reference them from a biome/region/dimension 'structures' list, or run /iris structure list " + dimension.getLoadKey() + " to refresh the index. Regenerate chunks for changes to take effect.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_IMPORT_COMPLETE_STRUCTURES_OBJECTS_WRITTEN_FAILED, MessageArgument.untrusted("imported", imported), MessageArgument.untrusted("failed", failed)));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_REFERENCE_THEM_FROM_BIOME_REGION_DIMENSION_STRUCTURES_LIST_RUN_IRIS, MessageArgument.untrusted("value", dimension.getLoadKey())));
}
@Director(name = "capture", description = "Capture code-generated structures that have no NBT template (swamp huts, igloos, etc.) into editable Iris objects by generating each one in a throwaway scratch world and reading back its blocks. Skips structures that already import as a structure, structures wider/taller than the capture cap (strongholds, mansions, monuments stay vanilla), and anything that will not generate in a flat overworld. Each captured structure becomes a single-piece Iris structure you can place from a biome/region/dimension 'structures' list. Runs automatically as the last pass of /iris structure import.", aliases = {"cap"}, origin = DirectorOrigin.BOTH)
@Director(name = "capture", description = "Capture code-generated structures that have no NBT template (swamp huts, igloos, etc.) into editable Iris objects by generating each one in a throwaway scratch world and reading back its blocks. Skips structures that already import as a structure, structures wider/taller than the capture cap (strongholds, mansions, monuments stay vanilla), and anything that will not generate in a flat overworld. Each captured structure becomes a single-piece Iris structure you can place from a biome/region/dimension 'structures' list. Runs automatically as the last pass of /iris structure import.", descriptionKey = "iris.director.commandstructure.director.capture_code_generated_structures_that_have_no_nbt_template_swamp_huts_igloos", aliases = {"cap"}, origin = DirectorOrigin.BOTH)
public void capture(
@Param(description = "The dimension whose pack to capture into", aliases = "dim")
@Param(description = "The dimension whose pack to capture into", descriptionKey = "iris.director.commandstructure.param.dimension_whose_pack_capture_into", aliases = "dim")
IrisDimension dimension
) {
IrisData data = dimension.getLoader();
if (data == null) {
sender().sendMessage(C.RED + "Could not resolve the pack for dimension " + dimension.getLoadKey());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_COULD_NOT_RESOLVE_PACK_DIMENSION_3, MessageArgument.untrusted("value", dimension.getLoadKey())));
return;
}
StructureCaptureImporter.Report report = StructureCaptureImporter.importAllStructures(data, StructureImporter.Mode.OVERWRITE, sender());
sender().sendMessage(C.GRAY + "Captured " + report.imported() + " structures. Place them from a 'structures' list and regenerate chunks. Delete a structures/*.json to re-capture it.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_CAPTURED_STRUCTURES_PLACE_THEM_FROM_STRUCTURES_LIST_REGENERATE_CHUNKS_DELETE, MessageArgument.untrusted("value", report.imported())));
}
@Director(description = "Verify native structure eligibility and locate Iris-placed structures without running blocking native searches.", aliases = {"locateall"}, origin = DirectorOrigin.BOTH, sync = true)
@Director(description = "Verify native structure eligibility and locate Iris-placed structures without running blocking native searches.", descriptionKey = "iris.director.commandstructure.director.verify_native_structure_eligibility_locate_iris_placed_structures_without_running_blocking_native", aliases = {"locateall"}, origin = DirectorOrigin.BOTH, sync = true)
public void verify(
@Param(description = "The dimension to verify", aliases = "dim")
@Param(description = "The dimension to verify", descriptionKey = "iris.director.commandstructure.param.dimension_verify", aliases = "dim")
IrisDimension dimension,
@Param(description = "Search radius in chunks around the origin (larger is much slower)", defaultValue = "48")
@Param(description = "Search radius in chunks around the origin (larger is much slower)", descriptionKey = "iris.director.commandstructure.param.search_radius_chunks_around_origin_larger_is_much_slower", defaultValue = "48")
int radius
) {
World world = resolveIrisWorld(dimension);
if (world == null) {
sender().sendMessage(C.RED + "No loaded Iris world found for " + dimension.getLoadKey() + ". Join or create one first (the search runs against a live world).");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_NO_LOADED_IRIS_WORLD_FOUND_JOIN_CREATE_ONE_FIRST_SEARCH, MessageArgument.untrusted("value", dimension.getLoadKey())));
return;
}
boolean senderIsPlayer = sender() != null && sender().isPlayer();
@@ -138,7 +142,7 @@ public class CommandStructure implements DirectorExecutor {
PlatformChunkGenerator access = IrisToolbelt.access(world);
Engine engine = access == null ? null : access.getEngine();
if (engine == null) {
sender().sendMessage(C.RED + "The selected Iris world has no active generator engine.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_SELECTED_IRIS_WORLD_HAS_NO_ACTIVE_GENERATOR_ENGINE));
return;
}
KList<String> structureKeys = new KList<>();
@@ -151,9 +155,7 @@ public class CommandStructure implements DirectorExecutor {
}
VolmitSender commandSender = sender();
Player target = senderIsPlayer ? player() : null;
commandSender.sendMessage(C.GREEN + "Verifying structures in " + C.WHITE + world.getName()
+ C.GREEN + " from " + center.getBlockX() + "," + center.getBlockZ()
+ " within " + searchRadius + " chunks...");
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STRUCTURE_VERIFYING_STRUCTURES_FROM_WITHIN_CHUNKS, MessageArgument.untrusted("value", world.getName()), MessageArgument.untrusted("value2", center.getBlockX()), MessageArgument.untrusted("value3", center.getBlockZ()), MessageArgument.untrusted("searchRadius", searchRadius)));
int centerX = center.getBlockX();
int centerZ = center.getBlockZ();
J.a(() -> runVerification(engine, structureKeys, centerX, centerZ, searchRadius, commandSender, target));
@@ -278,28 +280,28 @@ public class CommandStructure implements DirectorExecutor {
&& dimensionKey.equals(generator.getEngine().getDimension().getLoadKey());
}
@Director(description = "Resolve an iris structure's jigsaw graph and report piece count & bounds", origin = DirectorOrigin.BOTH)
@Director(description = "Resolve an iris structure's jigsaw graph and report piece count & bounds", descriptionKey = "iris.director.commandstructure.director.resolve_iris_structure_s_jigsaw_graph_report_piece_count_bounds", origin = DirectorOrigin.BOTH)
public void info(
@Param(description = "The dimension whose pack holds the structure", aliases = "dim")
@Param(description = "The dimension whose pack holds the structure", descriptionKey = "iris.director.commandstructure.param.dimension_whose_pack_holds_structure", aliases = "dim")
IrisDimension dimension,
@Param(description = "The iris structure key to inspect")
@Param(description = "The iris structure key to inspect", descriptionKey = "iris.director.commandstructure.param.iris_structure_key_inspect")
String structure
) {
IrisData data = dimension.getLoader();
if (data == null) {
sender().sendMessage(C.RED + "Could not resolve the pack for dimension " + dimension.getLoadKey());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_COULD_NOT_RESOLVE_PACK_DIMENSION_4, MessageArgument.untrusted("value", dimension.getLoadKey())));
return;
}
IrisStructure s = data.load(IrisStructure.class, structure, false);
if (s == null) {
sender().sendMessage(C.RED + "No iris structure '" + structure + "' in this pack");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_NO_IRIS_STRUCTURE_THIS_PACK, MessageArgument.untrusted("structure", structure)));
return;
}
StructureAssembler assembler = StructureAssembler.forData(
data, s, new IrisPosition(0, 64, 0));
KList<PlacedStructurePiece> pieces = assembler.assemble(new RNG(1234));
if (pieces == null || pieces.isEmpty()) {
sender().sendMessage(C.RED + "Structure '" + structure + "' assembled 0 pieces (check startPool '" + s.getStartPool() + "')");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_STRUCTURE_ASSEMBLED_0_PIECES_CHECK_STARTPOOL, MessageArgument.untrusted("structure", structure), MessageArgument.untrusted("value", s.getStartPool())));
return;
}
int minX = Integer.MAX_VALUE;
@@ -312,24 +314,24 @@ public class CommandStructure implements DirectorExecutor {
maxX = Math.max(maxX, p.getMaxX());
maxZ = Math.max(maxZ, p.getMaxZ());
}
sender().sendMessage(C.GREEN + "Structure '" + structure + "': " + C.WHITE + pieces.size() + C.GREEN + " pieces, footprint " + C.WHITE + (maxX - minX + 1) + "x" + (maxZ - minZ + 1) + C.GREEN + " blocks (sample seed 1234)");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_STRUCTURE_PIECES_FOOTPRINT_X_BLOCKS_SAMPLE_SEED_1234, MessageArgument.untrusted("structure", structure), MessageArgument.untrusted("value", pieces.size()), MessageArgument.untrusted("value2", (maxX - minX + 1)), MessageArgument.untrusted("value3", (maxZ - minZ + 1))));
}
@Director(description = "Assemble and place an iris structure at your location (studio testing)", aliases = {"p"}, origin = DirectorOrigin.PLAYER, sync = true)
@Director(description = "Assemble and place an iris structure at your location (studio testing)", descriptionKey = "iris.director.commandstructure.director.assemble_place_iris_structure_at_your_location_studio_testing", aliases = {"p"}, origin = DirectorOrigin.PLAYER, sync = true)
public void place(
@Param(description = "The dimension whose pack holds the structure", aliases = "dim")
@Param(description = "The dimension whose pack holds the structure", descriptionKey = "iris.director.commandstructure.param.dimension_whose_pack_holds_structure_2", aliases = "dim")
IrisDimension dimension,
@Param(description = "The iris structure key to place")
@Param(description = "The iris structure key to place", descriptionKey = "iris.director.commandstructure.param.iris_structure_key_place")
String structure
) {
IrisData data = dimension.getLoader();
if (data == null) {
sender().sendMessage(C.RED + "Could not resolve the pack for dimension " + dimension.getLoadKey());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_COULD_NOT_RESOLVE_PACK_DIMENSION_5, MessageArgument.untrusted("value", dimension.getLoadKey())));
return;
}
IrisStructure s = data.load(IrisStructure.class, structure, false);
if (s == null) {
sender().sendMessage(C.RED + "No iris structure '" + structure + "' in this pack");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_NO_IRIS_STRUCTURE_THIS_PACK_2, MessageArgument.untrusted("structure", structure)));
return;
}
Location loc = player().getLocation();
@@ -338,7 +340,7 @@ public class CommandStructure implements DirectorExecutor {
RNG rng = new RNG((long) loc.getBlockX() * 341873128712L + loc.getBlockZ());
KList<PlacedStructurePiece> pieces = assembler.assemble(rng);
if (pieces == null || pieces.isEmpty()) {
sender().sendMessage(C.RED + "Structure '" + structure + "' assembled 0 pieces");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_STRUCTURE_ASSEMBLED_0_PIECES, MessageArgument.untrusted("structure", structure)));
return;
}
Map<Block, BlockData> future = new HashMap<>();
@@ -353,7 +355,7 @@ public class CommandStructure implements DirectorExecutor {
}
p.getObject().place(p.getX(), p.getY(), p.getZ(), placer, config, rng, null, null, data);
}
sender().sendMessage(C.GREEN + "Placed '" + structure + "' (" + pieces.size() + " pieces) at your location.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STRUCTURE_PLACED_PIECES_AT_YOUR_LOCATION, MessageArgument.untrusted("structure", structure), MessageArgument.untrusted("value", pieces.size())));
}
}
@@ -26,6 +26,7 @@ import art.arcane.iris.core.gui.NoiseExplorerGUI;
import art.arcane.iris.core.gui.VisionGUI;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.core.project.IrisProject;
import art.arcane.iris.core.localization.RuntimeUiMessages;
import art.arcane.iris.core.service.BoardSVC;
import art.arcane.iris.core.service.StudioSVC;
import art.arcane.iris.core.structure.BulkStructureImporter;
@@ -99,7 +100,11 @@ import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
@Director(name = "studio", aliases = {"std", "s"}, description = "Studio Commands")
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.BukkitCommandMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
import art.arcane.iris.core.localization.BukkitCommandMessagesExtended;
@Director(name = "studio", aliases = {"std", "s"}, description = "Studio Commands", descriptionKey = "iris.director.commandstudio.director.studio_commands")
public class CommandStudio implements DirectorExecutor {
private CommandEdit edit;
//private CommandDeepSearch deepSearch;
@@ -108,37 +113,37 @@ public class CommandStudio implements DirectorExecutor {
return duration.toString().substring(2).replaceAll("(\\d[HMS])(?!$)", "$1 ").toLowerCase();
}
@Director(description = "Open a new studio world", aliases = "o", sync = true)
@Director(description = "Open a new studio world", descriptionKey = "iris.director.commandstudio.director.open_new_studio_world", aliases = "o", sync = true)
public void open(
@Param(description = "The dimension pack to open a studio for", aliases = "dim", customHandler = DimensionHandler.class)
@Param(description = "The dimension pack to open a studio for", descriptionKey = "iris.director.commandstudio.param.dimension_pack_open_studio", aliases = "dim", customHandler = DimensionHandler.class)
IrisDimension dimension,
@Param(defaultValue = "1337", description = "The seed to generate the studio with", aliases = "s")
@Param(defaultValue = "1337", description = "The seed to generate the studio with", descriptionKey = "iris.director.commandstudio.param.seed_generate_studio_with", aliases = "s")
long seed) {
sender().sendMessage(C.GREEN + "Opening studio for the \"" + dimension.getName() + "\" pack (seed: " + seed + ")");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_OPENING_STUDIO_PACK_SEED, MessageArgument.untrusted("value", dimension.getName()), MessageArgument.untrusted("seed", seed)));
Iris.service(StudioSVC.class).open(sender(), seed, dimension.getLoadKey());
}
@Director(description = "Import vanilla trees, mushrooms & objects (and structures/jigsaw) from the server into a pack's objects/vanilla folder", aliases = {"importv", "iv"}, origin = DirectorOrigin.BOTH)
@Director(description = "Import vanilla trees, mushrooms & objects (and structures/jigsaw) from the server into a pack's objects/vanilla folder", descriptionKey = "iris.director.commandstudio.director.import_vanilla_trees_mushrooms_objects_structures_jigsaw_from_server_into_pack_s", aliases = {"importv", "iv"}, origin = DirectorOrigin.BOTH)
public void importvanilla(
@Param(description = "The dimension pack to import vanilla content into", aliases = {"pack", "dim"}, customHandler = DimensionHandler.class)
@Param(description = "The dimension pack to import vanilla content into", descriptionKey = "iris.director.commandstudio.param.dimension_pack_import_vanilla_content_into", aliases = {"pack", "dim"}, customHandler = DimensionHandler.class)
IrisDimension dimension,
@Param(description = "How many variants to capture per tree/object feature", defaultValue = "3")
@Param(description = "How many variants to capture per tree/object feature", descriptionKey = "iris.director.commandstudio.param.how_many_variants_capture_per_tree_object_feature", defaultValue = "3")
int variants,
@Param(description = "Also import vanilla & datapack structures/jigsaw into the pack", defaultValue = "true")
@Param(description = "Also import vanilla & datapack structures/jigsaw into the pack", descriptionKey = "iris.director.commandstudio.param.also_import_vanilla_datapack_structures_jigsaw_into_pack", defaultValue = "true")
boolean structures
) {
if (dimension == null) {
sender().sendMessage(C.RED + "Provide a dimension pack: /iris std importvanilla pack=<dimension>");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_PROVIDE_DIMENSION_PACK_IRIS_STD_IMPORTVANILLA_PACK_DIMENSION));
return;
}
IrisData data = dimension.getLoader();
if (data == null) {
sender().sendMessage(C.RED + "Could not resolve the pack for dimension " + dimension.getLoadKey());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_COULD_NOT_RESOLVE_PACK_DIMENSION, MessageArgument.untrusted("value", dimension.getLoadKey())));
return;
}
VolmitSender sender = sender();
sender.sendMessage(C.GREEN + "Importing vanilla content into " + C.WHITE + dimension.getLoadKey() + C.GREEN + "...");
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_IMPORTING_VANILLA_CONTENT_INTO, MessageArgument.untrusted("value", dimension.getLoadKey())));
FeatureImporter.Report features = FeatureImporter.importAllObjectFeatures(data, variants, sender);
int imported = features.imported();
@@ -152,73 +157,72 @@ public class CommandStudio implements DirectorExecutor {
failed += jigsaws.failed() + templates.failed() + groups.failed();
}
sender.sendMessage(C.GREEN + "importvanilla complete: " + C.WHITE + imported + C.GREEN + " objects/structures written, " + C.WHITE + failed + C.GREEN + " failed.");
sender.sendMessage(C.GRAY + "Trees/objects are under objects/vanilla/...; reference them from biome object placements.");
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_IMPORTVANILLA_COMPLETE_OBJECTS_STRUCTURES_WRITTEN_FAILED, MessageArgument.untrusted("imported", imported), MessageArgument.untrusted("failed", failed)));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_TREES_OBJECTS_ARE_UNDER_OBJECTS_VANILLA_REFERENCE_THEM_FROM_BIOME));
}
@Director(description = "Open VSCode for a dimension", aliases = {"vsc"})
@Director(description = "Open VSCode for a dimension", descriptionKey = "iris.director.commandstudio.director.open_vscode_dimension", aliases = {"vsc"})
public void vscode(
@Param(defaultValue = "default", description = "The dimension to open VSCode for", aliases = "dim", customHandler = DimensionHandler.class)
@Param(defaultValue = "default", description = "The dimension to open VSCode for", descriptionKey = "iris.director.commandstudio.param.dimension_open_vscode", aliases = "dim", customHandler = DimensionHandler.class)
IrisDimension dimension
) {
sender().sendMessage(C.GREEN + "Opening VSCode for the \"" + dimension.getName() + "\" pack");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_OPENING_VSCODE_PACK, MessageArgument.untrusted("value", dimension.getName())));
Iris.service(StudioSVC.class).openVSCode(sender(), dimension.getLoadKey());
}
@Director(description = "Close an open studio project", aliases = {"x"}, sync = true)
@Director(description = "Close an open studio project", descriptionKey = "iris.director.commandstudio.director.close_open_studio_project", aliases = {"x"}, sync = true)
public void close() {
VolmitSender commandSender = sender();
if (!Iris.service(StudioSVC.class).isProjectOpen()) {
commandSender.sendMessage(C.RED + "No open studio projects.");
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_NO_OPEN_STUDIO_PROJECTS));
return;
}
commandSender.sendMessage(C.YELLOW + "Closing studio...");
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_CLOSING_STUDIO));
Iris.service(StudioSVC.class).close().whenComplete((result, throwable) -> J.s(() -> {
if (throwable != null) {
commandSender.sendMessage(C.RED + "Studio close failed: " + throwable.getMessage());
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_STUDIO_CLOSE_FAILED, MessageArgument.untrusted("value", String.valueOf(throwable.getMessage()))));
return;
}
if (result != null && result.failureCause() != null) {
commandSender.sendMessage(C.RED + "Studio close failed: " + result.failureCause().getMessage());
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_STUDIO_CLOSE_FAILED_2, MessageArgument.untrusted("value", String.valueOf(result.failureCause().getMessage()))));
return;
}
if (result != null && result.startupCleanupQueued()) {
commandSender.sendMessage(C.YELLOW + "Studio closed. Remaining world-family cleanup was queued for startup fallback.");
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_STUDIO_CLOSED_REMAINING_WORLD_FAMILY_CLEANUP_WAS_QUEUED_STARTUP_FALLBACK));
return;
}
commandSender.sendMessage(C.GREEN + "Studio closed.");
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_STUDIO_CLOSED));
}));
}
@Director(description = "Toggle your Studio debug scoreboard", aliases = {"board", "sidebar", "sb"}, origin = DirectorOrigin.PLAYER)
@Director(description = "Toggle your Studio debug scoreboard", descriptionKey = "iris.director.commandstudio.director.toggle_your_studio_debug_scoreboard", aliases = {"board", "sidebar", "sb"}, origin = DirectorOrigin.PLAYER)
public void scoreboard() {
Player target = player();
VolmitSender commandSender = sender();
if (!J.runEntity(target, () -> {
PlatformChunkGenerator generator = IrisToolbelt.access(target.getWorld());
if (generator == null || !generator.isStudio()) {
commandSender.sendMessage(C.RED + "You must be in a Studio world to toggle the debug scoreboard.");
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_YOU_MUST_BE_STUDIO_WORLD_TOGGLE_DEBUG_SCOREBOARD));
return;
}
boolean visible = Iris.service(BoardSVC.class).toggle(target);
commandSender.sendMessage((visible ? C.GREEN : C.YELLOW)
+ "Studio debug scoreboard " + (visible ? "enabled." : "disabled."));
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_STUDIO_DEBUG_SCOREBOARD, MessageArgument.trusted("value", visible ? C.GREEN : C.YELLOW), MessageArgument.trusted("value2", IrisLanguage.text(visible ? RuntimeUiMessages.STATUS_ENABLED : RuntimeUiMessages.STATUS_DISABLED))));
})) {
commandSender.sendMessage(C.RED + "Could not update the Studio debug scoreboard right now.");
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_COULD_NOT_UPDATE_STUDIO_DEBUG_SCOREBOARD_RIGHT_NOW));
}
}
@Director(description = "Create a new studio project", aliases = "+", sync = true)
@Director(description = "Create a new studio project", descriptionKey = "iris.director.commandstudio.director.create_new_studio_project", aliases = "+", sync = true)
public void create(
@Param(description = "The name of this new Iris Project.", defaultValue = "studio")
@Param(description = "The name of this new Iris Project.", descriptionKey = "iris.director.commandstudio.param.name_this_new_iris_project", defaultValue = "studio")
String name,
@Param(
description = "Copy the contents of an existing project in your packs folder and use it as a template in this new project.",
description = "Copy the contents of an existing project in your packs folder and use it as a template in this new project.", descriptionKey = "iris.director.commandstudio.param.copy_contents_existing_project_your_packs_folder_use_it_as_template_this",
contextual = true,
customHandler = NullableDimensionHandler.class
)
@@ -239,23 +243,23 @@ public class CommandStudio implements DirectorExecutor {
}
}
@Director(description = "Get the version of a pack")
@Director(description = "Get the version of a pack", descriptionKey = "iris.director.commandstudio.director.get_version_pack")
public void version(
@Param(defaultValue = "default", description = "The dimension get the version of", aliases = "dim", contextual = true, customHandler = DimensionHandler.class)
@Param(defaultValue = "default", description = "The dimension get the version of", descriptionKey = "iris.director.commandstudio.param.dimension_get_version", aliases = "dim", contextual = true, customHandler = DimensionHandler.class)
IrisDimension dimension
) {
sender().sendMessage(C.GREEN + "The \"" + dimension.getName() + "\" pack has version: " + dimension.getVersion());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_PACK_HAS_VERSION, MessageArgument.untrusted("value", dimension.getName()), MessageArgument.untrusted("value2", dimension.getVersion())));
}
@Director(description = "Open the noise explorer (External GUI)", aliases = {"nmap"})
@Director(description = "Open the noise explorer (External GUI)", descriptionKey = "iris.director.commandstudio.director.open_noise_explorer_external_gui", aliases = {"nmap"})
public void noise(
@Param(description = "Optional pack generator to preview", defaultValue = "null", contextual = true)
@Param(description = "Optional pack generator to preview", descriptionKey = "iris.director.commandstudio.param.optional_pack_generator_preview", defaultValue = "null", contextual = true)
IrisGenerator generator,
@Param(description = "The seed to preview the generator with", defaultValue = "12345")
@Param(description = "The seed to preview the generator with", descriptionKey = "iris.director.commandstudio.param.seed_preview_generator_with", defaultValue = "12345")
long seed
) {
if (noGUI()) return;
sender().sendMessage(C.GREEN + "Opening Noise Explorer!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_OPENING_NOISE_EXPLORER));
if (generator == null) {
NoiseExplorerGUI.launch();
@@ -266,11 +270,11 @@ public class CommandStudio implements DirectorExecutor {
NoiseExplorerGUI.launch(supplier, "Custom Generator");
}
@Director(description = "Show loot if a chest were right here", origin = DirectorOrigin.PLAYER, sync = true)
@Director(description = "Show loot if a chest were right here", descriptionKey = "iris.director.commandstudio.director.show_loot_if_chest_were_right_here", origin = DirectorOrigin.PLAYER, sync = true)
public void loot(
@Param(description = "Fast insertion of items in virtual inventory (may cause performance drop)", defaultValue = "false")
@Param(description = "Fast insertion of items in virtual inventory (may cause performance drop)", descriptionKey = "iris.director.commandstudio.param.fast_insertion_items_virtual_inventory_may_cause_performance_drop", defaultValue = "false")
boolean fast,
@Param(description = "Whether or not to append to the inventory currently open (if false, clears opened inventory)", defaultValue = "true")
@Param(description = "Whether or not to append to the inventory currently open (if false, clears opened inventory)", descriptionKey = "iris.director.commandstudio.param.whether_not_append_inventory_currently_open_if_false_clears_opened_inventory", defaultValue = "true")
boolean add
) {
if (noStudio()) return;
@@ -282,7 +286,7 @@ public class CommandStudio implements DirectorExecutor {
EngineBukkitOps.addItems(engine(), true, inv, tables, InventorySlotType.STORAGE, player().getWorld(), player().getLocation().getBlockX(), player().getLocation().getBlockY(), player().getLocation().getBlockZ());
} catch (Throwable e) {
Iris.reportError(e);
sender().sendMessage(C.RED + "Cannot add items to virtual inventory because of: " + e.getMessage());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_CANNOT_ADD_ITEMS_VIRTUAL_INVENTORY_BECAUSE, MessageArgument.untrusted("value", String.valueOf(e.getMessage()))));
return;
}
@@ -298,7 +302,7 @@ public class CommandStudio implements DirectorExecutor {
{
if (!player.getOpenInventory().getType().equals(InventoryType.CHEST)) {
J.csr(ta.get());
sender.sendMessage(C.GREEN + "Opened inventory!");
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_OPENED_INVENTORY));
return;
}
@@ -309,15 +313,15 @@ public class CommandStudio implements DirectorExecutor {
EngineBukkitOps.addItems(engine, true, inv, tables, InventorySlotType.STORAGE, player.getWorld(), player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ());
}, fast ? 5 : 35));
sender().sendMessage(C.GREEN + "Opening inventory now!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_OPENING_INVENTORY_NOW));
player().openInventory(inv);
}
@Director(description = "Calculate the chance for each region to generate", origin = DirectorOrigin.PLAYER)
public void regions(@Param(description = "The radius in chunks", defaultValue = "500") int radius) {
@Director(description = "Calculate the chance for each region to generate", descriptionKey = "iris.director.commandstudio.director.calculate_chance_each_region_generate", origin = DirectorOrigin.PLAYER)
public void regions(@Param(description = "The radius in chunks", descriptionKey = "iris.director.commandstudio.param.radius_chunks", defaultValue = "500") int radius) {
var engine = engine();
if (engine == null) {
sender().sendMessage(C.RED + "Only works in an Iris world!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_ONLY_WORKS_IRIS_WORLD));
return;
}
var sender = sender();
@@ -329,11 +333,14 @@ public class CommandStudio implements DirectorExecutor {
engine.getDimension().getRegions().forEach(key -> data.put(key, new AtomicInteger(0)));
var multiBurst = new MultiBurst("Region Sampler");
var executor = multiBurst.burst(radius * radius);
sender.sendMessage(C.GRAY + "Generating data...");
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_GENERATING_DATA));
var loc = player.getLocation();
int totalTasks = d * d;
AtomicInteger completedTasks = new AtomicInteger(0);
int c = J.ar(() -> sender.sendProgress((double) completedTasks.get() / totalTasks, "Finding regions"), 0);
int c = J.ar(() -> sender.sendProgress(
(double) completedTasks.get() / totalTasks,
IrisLanguage.text(RuntimeUiMessages.FINDING_REGIONS)
), 0);
new Spiraler(d, d, (x, z) -> executor.queue(() -> {
var region = engine.getRegion((x << 4) + 8, (z << 4) + 8);
data.computeIfAbsent(region.getLoadKey(), (k) -> new AtomicInteger(0))
@@ -344,43 +351,43 @@ public class CommandStudio implements DirectorExecutor {
multiBurst.close();
J.car(c);
sender.sendMessage(C.GREEN + "Done!");
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_DONE));
var loader = engine.getData().getRegionLoader();
data.forEach((k, v) -> sender.sendMessage(C.GREEN + k + ": " + loader.load(k).getRarity() + " / " + Form.f((double) v.get() / totalTasks * 100, 2) + "%"));
data.forEach((k, v) -> sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_MESSAGE, MessageArgument.untrusted("k", k), MessageArgument.untrusted("value", loader.load(k).getRarity()), MessageArgument.untrusted("value2", Form.f((double) v.get() / totalTasks * 100, 2)))));
});
}
@Director(description = "Render a world map (External GUI)", aliases = "render")
@Director(description = "Render a world map (External GUI)", descriptionKey = "iris.director.commandstudio.director.render_world_map_external_gui", aliases = "render")
public void map(
@Param(name = "world", description = "The world to open the generator for", contextual = true)
@Param(name = "world", description = "The world to open the generator for", descriptionKey = "iris.director.commandstudio.param.world_open_generator", contextual = true)
World world
) {
if (noGUI()) return;
if (!IrisToolbelt.isIrisWorld(world)) {
sender().sendMessage(C.RED + "You need to be in or specify an Iris-generated world!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_YOU_NEED_BE_SPECIFY_IRIS_GENERATED_WORLD));
return;
}
VisionGUI.launch(IrisToolbelt.access(world).getEngine());
sender().sendMessage(C.GREEN + "Opening map!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_OPENING_MAP));
}
@Director(description = "Package a dimension into a compressed format", aliases = "package")
@Director(description = "Package a dimension into a compressed format", descriptionKey = "iris.director.commandstudio.director.package_dimension_into_compressed_format", aliases = "package")
public void pkg(
@Param(name = "dimension", description = "The dimension pack to compress", contextual = true, defaultValue = "default", customHandler = DimensionHandler.class)
@Param(name = "dimension", description = "The dimension pack to compress", descriptionKey = "iris.director.commandstudio.param.dimension_pack_compress", contextual = true, defaultValue = "default", customHandler = DimensionHandler.class)
IrisDimension dimension,
@Param(name = "obfuscate", description = "Whether or not to obfuscate the pack", defaultValue = "false")
@Param(name = "obfuscate", description = "Whether or not to obfuscate the pack", descriptionKey = "iris.director.commandstudio.param.whether_not_obfuscate_pack", defaultValue = "false")
boolean obfuscate,
@Param(name = "minify", description = "Whether or not to minify the pack", defaultValue = "true")
@Param(name = "minify", description = "Whether or not to minify the pack", descriptionKey = "iris.director.commandstudio.param.whether_not_minify_pack", defaultValue = "true")
boolean minify
) {
Iris.service(StudioSVC.class).compilePackage(sender(), dimension.getLoadKey(), obfuscate, minify);
}
@Director(description = "Profiles the performance of a dimension", origin = DirectorOrigin.PLAYER)
@Director(description = "Profiles the performance of a dimension", descriptionKey = "iris.director.commandstudio.director.profiles_performance_dimension", origin = DirectorOrigin.PLAYER)
public void profile(
@Param(description = "The dimension to profile", contextual = true, defaultValue = "default", customHandler = DimensionHandler.class)
@Param(description = "The dimension to profile", descriptionKey = "iris.director.commandstudio.param.dimension_profile", contextual = true, defaultValue = "default", customHandler = DimensionHandler.class)
IrisDimension dimension
) {
// Todo: Make this more accurate
@@ -405,7 +412,7 @@ public class CommandStudio implements DirectorExecutor {
KMap<String, Double> biomeTimings = new KMap<>();
KMap<String, Double> regionTimings = new KMap<>();
sender().sendMessage("Calculating Performance Metrics for Noise generators");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_CALCULATING_PERFORMANCE_METRICS_NOISE_GENERATORS));
for (NoiseStyle i : NoiseStyle.values()) {
CNG c = i.create(new RNG(i.hashCode()));
@@ -433,7 +440,7 @@ public class CommandStudio implements DirectorExecutor {
fileText.add("");
sender().sendMessage("Calculating Interpolator Timings...");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_CALCULATING_INTERPOLATOR_TIMINGS));
for (InterpolationMethod i : InterpolationMethod.values()) {
IrisInterpolator in = new IrisInterpolator();
@@ -463,7 +470,7 @@ public class CommandStudio implements DirectorExecutor {
fileText.add("");
sender().sendMessage("Processing Generator Scores: ");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_PROCESSING_GENERATOR_SCORES));
KMap<String, KList<String>> btx = new KMap<>();
@@ -563,7 +570,7 @@ public class CommandStudio implements DirectorExecutor {
m += mmm;
fileText.add("Average Score: " + m);
sender().sendMessage("Score: " + Form.duration(m, 0));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_SCORE, MessageArgument.untrusted("value", Form.duration(m, 0))));
try {
IO.writeAll(report, fileText.toString("\n"));
@@ -572,7 +579,7 @@ public class CommandStudio implements DirectorExecutor {
e.printStackTrace();
}
sender().sendMessage(C.GREEN + "Done! " + report.getPath());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_DONE, MessageArgument.untrusted("value", report.getPath())));
}
private PlatformChunkGenerator resolveProfileGenerator(IrisDimension dimension) {
@@ -617,32 +624,32 @@ public class CommandStudio implements DirectorExecutor {
return engineDimension.getLoadKey().equalsIgnoreCase(dimension.getLoadKey());
}
@Director(description = "Spawn an Iris entity", aliases = "summon", origin = DirectorOrigin.PLAYER)
@Director(description = "Spawn an Iris entity", descriptionKey = "iris.director.commandstudio.director.spawn_iris_entity", aliases = "summon", origin = DirectorOrigin.PLAYER)
public void spawn(
@Param(description = "The entity to spawn")
@Param(description = "The entity to spawn", descriptionKey = "iris.director.commandstudio.param.entity_spawn")
IrisEntity entity,
@Param(description = "The location to spawn the entity at", contextual = true)
@Param(description = "The location to spawn the entity at", descriptionKey = "iris.director.commandstudio.param.location_spawn_entity_at", contextual = true)
Vector location
) {
if (!IrisToolbelt.isIrisWorld(player().getWorld())) {
sender().sendMessage(C.RED + "You have to be in an Iris world to spawn entities properly. Trying to spawn the best we can do.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_YOU_HAVE_BE_IRIS_WORLD_SPAWN_ENTITIES_PROPERLY_TRYING_SPAWN));
}
entity.spawn(engine(), new Location(world(), location.getX(), location.getY(), location.getZ()));
}
@Director(description = "Teleport to the active studio world", aliases = "stp", origin = DirectorOrigin.PLAYER, sync = true)
@Director(description = "Teleport to the active studio world", descriptionKey = "iris.director.commandstudio.director.teleport_active_studio_world", aliases = "stp", origin = DirectorOrigin.PLAYER, sync = true)
public void tpstudio() {
if (!Iris.service(StudioSVC.class).isProjectOpen()) {
sender().sendMessage(C.RED + "No studio world is open!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_NO_STUDIO_WORLD_IS_OPEN));
return;
}
if (IrisToolbelt.isIrisWorld(world()) && engine().isStudio()) {
sender().sendMessage(C.RED + "You are already in a studio world!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_YOU_ARE_ALREADY_STUDIO_WORLD));
return;
}
sender().sendMessage(C.GREEN + "Sending you to the studio world!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_SENDING_YOU_STUDIO_WORLD));
Player player = player();
IrisWorld studioWorld = Iris.service(StudioSVC.class)
.getActiveProject()
@@ -653,30 +660,30 @@ public class CommandStudio implements DirectorExecutor {
.thenRun(() -> player.setGameMode(GameMode.SPECTATOR));
}
@Director(description = "Update your dimension projects VSCode workspace")
@Director(description = "Update your dimension projects VSCode workspace", descriptionKey = "iris.director.commandstudio.director.update_your_dimension_projects_vscode_workspace")
public void update(
@Param(description = "The dimension to update the workspace of", contextual = true, defaultValue = "default", customHandler = DimensionHandler.class)
@Param(description = "The dimension to update the workspace of", descriptionKey = "iris.director.commandstudio.param.dimension_update_workspace", contextual = true, defaultValue = "default", customHandler = DimensionHandler.class)
IrisDimension dimension
) {
sender().sendMessage(C.GOLD + "Updating Code Workspace for " + dimension.getName() + "...");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_UPDATING_CODE_WORKSPACE, MessageArgument.untrusted("value", dimension.getName())));
if (new IrisProject(dimension.getLoader().getDataFolder()).updateWorkspace()) {
sender().sendMessage(C.GREEN + "Updated Code Workspace for " + dimension.getName());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_UPDATED_CODE_WORKSPACE, MessageArgument.untrusted("value", dimension.getName())));
} else {
sender().sendMessage(C.RED + "Invalid project: " + dimension.getName() + ". Try deleting the code-workspace file and try again.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_INVALID_PROJECT_TRY_DELETING_CODE_WORKSPACE_FILE_TRY_AGAIN, MessageArgument.untrusted("value", dimension.getName())));
}
}
@Director(aliases = "find-objects", description = "Capture an IGenData chunk report for nearby chunks")
@Director(aliases = "find-objects", description = "Capture an IGenData chunk report for nearby chunks", descriptionKey = "iris.director.commandstudio.director.capture_igendata_chunk_report_nearby_chunks")
public void objects() {
if (!IrisToolbelt.isIrisWorld(player().getWorld())) {
sender().sendMessage(C.RED + "You must be in an Iris world");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_YOU_MUST_BE_IRIS_WORLD));
return;
}
World world = player().getWorld();
if (!IrisToolbelt.isIrisWorld(world)) {
sender().sendMessage("You must be in an iris world.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_YOU_MUST_BE_IRIS_WORLD_2));
return;
}
KList<Chunk> chunks = new KList<>();
@@ -694,7 +701,7 @@ public class CommandStudio implements DirectorExecutor {
}
new Spiraler(3, 3, (x, z) -> chunks.addIfMissing(world.getChunkAt(x + bx, z + bz))).drain();
sender().sendMessage("Capturing IGenData from " + chunks.size() + " nearby chunks.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_CAPTURING_IGENDATA_FROM_NEARBY_CHUNKS, MessageArgument.untrusted("value", chunks.size())));
try {
File ff = Iris.instance.getDataFile("reports/" + M.ms() + ".txt");
PrintWriter pw = new PrintWriter(ff);
@@ -788,7 +795,7 @@ public class CommandStudio implements DirectorExecutor {
pw.println();
pw.close();
sender().sendMessage("Reported to: " + ff.getPath());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_REPORTED, MessageArgument.untrusted("value", ff.getPath())));
} catch (FileNotFoundException e) {
e.printStackTrace();
Iris.reportError(e);
@@ -831,7 +838,7 @@ public class CommandStudio implements DirectorExecutor {
*/
private boolean noGUI() {
if (!IrisSettings.get().getGui().isUseServerLaunchedGuis()) {
sender().sendMessage(C.RED + "You must have server launched GUIs enabled in the settings!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_YOU_MUST_HAVE_SERVER_LAUNCHED_GUIS_ENABLED_SETTINGS));
return true;
}
return false;
@@ -842,15 +849,15 @@ public class CommandStudio implements DirectorExecutor {
*/
private boolean noStudio() {
if (!sender().isPlayer()) {
sender().sendMessage(C.RED + "Players only!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_PLAYERS_ONLY));
return true;
}
if (!Iris.service(StudioSVC.class).isProjectOpen()) {
sender().sendMessage(C.RED + "No studio world is open!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_NO_STUDIO_WORLD_IS_OPEN_2));
return true;
}
if (!IrisToolbelt.isStudio(world())) {
sender().sendMessage(C.RED + "You must be in a studio world!");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_YOU_MUST_BE_STUDIO_WORLD));
return true;
}
return false;
@@ -44,46 +44,50 @@ import org.bukkit.block.data.BlockData;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicInteger;
@Director(name = "what", origin = DirectorOrigin.PLAYER, description = "Iris What?")
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.BukkitCommandMessagesExtended;
import art.arcane.iris.core.localization.RuntimeUiMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
@Director(name = "what", origin = DirectorOrigin.PLAYER, description = "Iris What?", descriptionKey = "iris.director.commandwhat.director.iris_what")
public class CommandWhat implements DirectorExecutor {
@Director(description = "What is in my hand?", origin = DirectorOrigin.PLAYER)
@Director(description = "What is in my hand?", descriptionKey = "iris.director.commandwhat.director.what_is_my_hand", origin = DirectorOrigin.PLAYER)
public void hand() {
try {
BlockData bd = player().getInventory().getItemInMainHand().getType().createBlockData();
if (!bd.getMaterial().equals(Material.AIR)) {
sender().sendMessage("Material: " + C.GREEN + bd.getMaterial().name());
sender().sendMessage("Full: " + C.WHITE + bd.getAsString(true));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_MATERIAL, MessageArgument.untrusted("value", bd.getMaterial().name())));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_FULL, MessageArgument.untrusted("value", bd.getAsString(true))));
} else {
sender().sendMessage("Please hold a block/item");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_PLEASE_HOLD_BLOCK_ITEM));
}
} catch (Throwable e) {
Iris.reportError(e);
Material bd = player().getInventory().getItemInMainHand().getType();
if (!bd.equals(Material.AIR)) {
sender().sendMessage("Material: " + C.GREEN + bd.name());
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_MATERIAL_2, MessageArgument.untrusted("value", bd.name())));
} else {
sender().sendMessage("Please hold a block/item");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_PLEASE_HOLD_BLOCK_ITEM_2));
}
}
}
@Director(description = "What biome am i in?", origin = DirectorOrigin.PLAYER)
@Director(description = "What biome am i in?", descriptionKey = "iris.director.commandwhat.director.what_biome_am_i", origin = DirectorOrigin.PLAYER)
public void biome() {
try {
IrisBiome b = engine().getBiome(player().getLocation().getBlockX(), player().getLocation().getBlockY() - player().getWorld().getMinHeight(), player().getLocation().getBlockZ());
Biome derivative = b.getDerivative();
NamespacedKey derivativeKey = resolveBiomeKey(derivative);
sender().sendMessage("IBiome: " + b.getLoadKey() + " (" + (derivativeKey == null ? "unregistered" : derivativeKey.getKey()) + ")");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_IBIOME, MessageArgument.untrusted("value", b.getLoadKey()), MessageArgument.untrusted("value2", derivativeKey == null ? IrisLanguage.plain(RuntimeUiMessages.STATUS_UNREGISTERED) : derivativeKey.getKey())));
} catch (Throwable e) {
Iris.reportError(e);
Biome biome = player().getLocation().getBlock().getBiome();
NamespacedKey key = resolveBiomeKey(biome);
sender().sendMessage("Non-Iris Biome: " + (key == null ? "unregistered" : key));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_NON_IRIS_BIOME, MessageArgument.untrusted("value", key == null ? IrisLanguage.plain(RuntimeUiMessages.STATUS_UNREGISTERED) : key)));
if (key == null || key.getKey().equals("custom")) {
try {
sender().sendMessage("Data Pack Biome: " + INMS.get().getTrueBiomeBaseKey(player().getLocation()) + " (ID: " + INMS.get().getTrueBiomeBaseId(INMS.get().getTrueBiomeBase(player().getLocation())) + ")");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_DATA_PACK_BIOME_ID, MessageArgument.untrusted("value", INMS.get().getTrueBiomeBaseKey(player().getLocation())), MessageArgument.untrusted("value2", INMS.get().getTrueBiomeBaseId(INMS.get().getTrueBiomeBase(player().getLocation())))));
} catch (Throwable ee) {
Iris.reportError(ee);
}
@@ -91,66 +95,66 @@ public class CommandWhat implements DirectorExecutor {
}
}
@Director(description = "What region am i in?", origin = DirectorOrigin.PLAYER)
@Director(description = "What region am i in?", descriptionKey = "iris.director.commandwhat.director.what_region_am_i", origin = DirectorOrigin.PLAYER)
public void region() {
try {
Chunk chunk = world().getChunkAt(player().getLocation().getBlockX() >> 4, player().getLocation().getBlockZ() >> 4);
IrisRegion r = EngineBukkitOps.getRegion(engine(), chunk);
sender().sendMessage("IRegion: " + r.getLoadKey() + " (" + r.getName() + ")");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_IREGION, MessageArgument.untrusted("value", r.getLoadKey()), MessageArgument.untrusted("value2", r.getName())));
} catch (Throwable e) {
Iris.reportError(e);
sender().sendMessage(C.IRIS + "Iris worlds only.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_IRIS_WORLDS_ONLY));
}
}
@Director(description = "What block am i looking at?", origin = DirectorOrigin.PLAYER)
@Director(description = "What block am i looking at?", descriptionKey = "iris.director.commandwhat.director.what_block_am_i_looking_at", origin = DirectorOrigin.PLAYER)
public void block() {
BlockData bd;
try {
bd = player().getTargetBlockExact(128, FluidCollisionMode.NEVER).getBlockData();
} catch (NullPointerException e) {
Iris.reportError(e);
sender().sendMessage("Please look at any block, not at the sky");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_PLEASE_LOOK_AT_ANY_BLOCK_NOT_AT_SKY));
bd = null;
}
if (bd != null) {
sender().sendMessage("Material: " + C.GREEN + bd.getMaterial().name());
sender().sendMessage("Full: " + C.WHITE + bd.getAsString(true));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_MATERIAL_3, MessageArgument.untrusted("value", bd.getMaterial().name())));
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_FULL_2, MessageArgument.untrusted("value", bd.getAsString(true))));
if (BukkitBlockResolution.isStorage(bd)) {
sender().sendMessage(C.YELLOW + "* Storage Block (Loot Capable)");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_STORAGE_BLOCK_LOOT_CAPABLE));
}
if (BukkitBlockResolution.isLit(bd)) {
sender().sendMessage(C.YELLOW + "* Lit Block (Light Capable)");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_LIT_BLOCK_LIGHT_CAPABLE));
}
if (BukkitBlockResolution.isFoliage(bd)) {
sender().sendMessage(C.YELLOW + "* Foliage Block");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_FOLIAGE_BLOCK));
}
if (BukkitBlockResolution.isDecorant(bd)) {
sender().sendMessage(C.YELLOW + "* Decorant Block");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_DECORANT_BLOCK));
}
if (BukkitBlockResolution.isFluid(bd)) {
sender().sendMessage(C.YELLOW + "* Fluid Block");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_FLUID_BLOCK));
}
if (BukkitBlockResolution.isFoliagePlantable(bd)) {
sender().sendMessage(C.YELLOW + "* Plantable Foliage Block");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_PLANTABLE_FOLIAGE_BLOCK));
}
if (BukkitBlockResolution.isSolid(bd)) {
sender().sendMessage(C.YELLOW + "* Solid Block");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_SOLID_BLOCK));
}
}
}
@Director(description = "Show markers in chunk", origin = DirectorOrigin.PLAYER)
public void markers(@Param(description = "Marker name such as cave_floor or cave_ceiling") String marker) {
@Director(description = "Show markers in chunk", descriptionKey = "iris.director.commandwhat.director.show_markers_chunk", origin = DirectorOrigin.PLAYER)
public void markers(@Param(description = "Marker name such as cave_floor or cave_ceiling", descriptionKey = "iris.director.commandwhat.param.marker_name_such_as_cave_floor_cave_ceiling") String marker) {
Chunk c = player().getLocation().getChunk();
if (IrisToolbelt.isIrisWorld(c.getWorld())) {
@@ -167,9 +171,9 @@ public class CommandWhat implements DirectorExecutor {
}
}
sender().sendMessage("Found " + v.get() + " Nearby Markers (" + marker + ")");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_FOUND_NEARBY_MARKERS, MessageArgument.untrusted("value", v.get()), MessageArgument.untrusted("marker", marker)));
} else {
sender().sendMessage(C.IRIS + "Iris worlds only.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_IRIS_WORLDS_ONLY_2));
}
}
@@ -29,6 +29,7 @@ import art.arcane.iris.util.common.format.C;
import art.arcane.iris.util.common.plugin.IrisService;
import art.arcane.iris.util.common.plugin.VolmitSender;
import art.arcane.iris.util.common.scheduling.J;
import art.arcane.volmlib.util.director.DirectorEngineOptions;
import art.arcane.volmlib.util.director.compat.DirectorEngineFactory;
import art.arcane.volmlib.util.director.context.DirectorContextRegistry;
import art.arcane.volmlib.util.director.runtime.DirectorExecutionMode;
@@ -38,7 +39,8 @@ import art.arcane.volmlib.util.director.runtime.DirectorInvocationHook;
import art.arcane.volmlib.util.director.runtime.DirectorRuntimeEngine;
import art.arcane.volmlib.util.director.runtime.DirectorRuntimeNode;
import art.arcane.volmlib.util.director.runtime.DirectorSender;
import art.arcane.volmlib.util.director.visual.DirectorVisualCommand;
import art.arcane.volmlib.util.director.help.DirectorMiniMenu;
import art.arcane.volmlib.util.director.help.DirectorMiniMenu.DirectorHelpPage;
import art.arcane.volmlib.util.math.RNG;
import org.bukkit.Sound;
import org.bukkit.command.Command;
@@ -57,12 +59,14 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.IrisMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
public class CommandSVC implements IrisService, CommandExecutor, TabCompleter, DirectorInvocationHook {
private static final String ROOT_COMMAND = "iris";
private static final String ROOT_PERMISSION = "iris.all";
private final transient AtomicCache<DirectorRuntimeEngine> directorCache = new AtomicCache<>();
private final transient AtomicCache<DirectorVisualCommand> helpCache = new AtomicCache<>();
@Override
public void onEnable() {
@@ -84,11 +88,13 @@ public class CommandSVC implements IrisService, CommandExecutor, TabCompleter, D
public DirectorRuntimeEngine getDirector() {
return directorCache.aquireNastyPrint(() -> DirectorEngineFactory.create(
new CommandIris(),
null,
buildDirectorContexts(),
this::dispatchDirector,
this,
DirectorSystem.handlers
DirectorEngineOptions.builder()
.contexts(buildDirectorContexts())
.dispatcher(this::dispatchDirector)
.invocationHook(this)
.legacyHandlers(DirectorSystem.handlers)
.textResolver(IrisLanguage.directorResolver())
.build()
));
}
@@ -155,7 +161,10 @@ public class CommandSVC implements IrisService, CommandExecutor, TabCompleter, D
void executeRoot(CommandSender sender, String label, String[] args) {
if (!sender.hasPermission(ROOT_PERMISSION)) {
sender.sendMessage("You lack the Permission '" + ROOT_PERMISSION + "'");
sender.sendMessage(IrisLanguage.text(
IrisMessages.COMMAND_PERMISSION_DENIED,
MessageArgument.trusted("permission", ROOT_PERMISSION)
));
return;
}
@@ -212,25 +221,24 @@ public class CommandSVC implements IrisService, CommandExecutor, TabCompleter, D
playFailureSound(sender);
if (result.getMessage() == null || result.getMessage().trim().isEmpty()) {
new VolmitSender(sender).sendMessage(C.RED + "Unknown Iris Command");
new VolmitSender(sender).sendMessage(C.RED + IrisLanguage.text(IrisMessages.COMMAND_UNKNOWN));
}
}
private boolean sendHelpIfRequested(CommandSender sender, String[] args) {
Optional<DirectorVisualCommand.HelpRequest> request = DirectorVisualCommand.resolveHelp(getHelpRoot(), Arrays.asList(args));
Optional<DirectorHelpPage> request = DirectorMiniMenu.resolveHelp(getDirector(), Arrays.asList(args), 17);
if (request.isEmpty()) {
return false;
}
VolmitSender volmitSender = new VolmitSender(sender);
volmitSender.sendDirectorHelp(request.get().command(), request.get().page());
DirectorMiniMenu.deliver(sender, DirectorMiniMenu.render(
request.get(),
DirectorMiniMenu.Theme.irisGreen(),
IrisLanguage.directorResolver()
));
return true;
}
private DirectorVisualCommand getHelpRoot() {
return helpCache.aquireNastyPrint(() -> DirectorVisualCommand.createRoot(getDirector()));
}
private DirectorExecutionResult runDirector(CommandSender sender, String label, String[] args) {
try {
return getDirector().execute(new DirectorInvocation(new BukkitDirectorSender(sender), label, Arrays.asList(args)));
@@ -3,7 +3,6 @@ package art.arcane.iris.core.service;
import art.arcane.iris.core.loader.ResourceLoader;
import art.arcane.iris.engine.framework.MeteredCache;
import art.arcane.iris.spi.IrisServices;
import art.arcane.iris.util.common.format.C;
import art.arcane.iris.util.common.plugin.VolmitSender;
import art.arcane.iris.util.project.stream.utility.CachedDoubleStream2D;
import art.arcane.iris.util.project.stream.utility.CachedStream2D;
@@ -12,6 +11,10 @@ import art.arcane.volmlib.util.format.Form;
import java.util.List;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.BukkitCommandMessages;
import art.arcane.iris.core.localization.RuntimeUiMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
final class IrisEngineStatus {
private IrisEngineStatus() {
}
@@ -20,30 +23,40 @@ final class IrisEngineStatus {
CacheSummary caches = summarizeCaches();
MaintenanceMetrics metrics = snapshot.metrics();
sender.sendMessage(C.DARK_PURPLE + "-------------------------");
sender.sendMessage(C.DARK_PURPLE + "Status:");
sender.sendMessage(C.DARK_PURPLE + "- Service: " + C.LIGHT_PURPLE + (snapshot.serviceRunning() ? "Running" : "Stopped"));
sender.sendMessage(C.DARK_PURPLE + "- Metrics: " + C.LIGHT_PURPLE + (snapshot.metricsRunning() ? "Running" : "Stopped"));
sender.sendMessage(C.DARK_PURPLE + "- Maintenance Period: " + C.LIGHT_PURPLE + Form.duration(snapshot.maintenancePeriodMillis()));
sender.sendMessage(C.DARK_PURPLE + "- Worker Parallelism: " + C.LIGHT_PURPLE + snapshot.workerParallelism());
sender.sendMessage(C.DARK_PURPLE + "- Active World Tasks: " + C.LIGHT_PURPLE + metrics.activeTasks());
sender.sendMessage(C.DARK_PURPLE + "Tectonic Plates:");
sender.sendMessage(C.DARK_PURPLE + "- Configured Retention: " + C.LIGHT_PURPLE + Form.duration(snapshot.retentionMillis()));
sender.sendMessage(C.DARK_PURPLE + "- Heap Usage: " + C.LIGHT_PURPLE + Form.pc(snapshot.heapUsage()));
sender.sendMessage(C.DARK_PURPLE + "- Resident: " + C.LIGHT_PURPLE + metrics.residentTectonicPlates());
sender.sendMessage(C.DARK_PURPLE + "- Queued: " + C.LIGHT_PURPLE + metrics.queuedTectonicPlates());
sender.sendMessage(C.DARK_PURPLE + "- Average Idle Duration: " + C.LIGHT_PURPLE + Form.duration(metrics.averageIdleDuration(), 2));
sender.sendMessage(C.DARK_PURPLE + "- Max Idle Duration: " + C.LIGHT_PURPLE + Form.duration(metrics.maxIdleDuration(), 2));
sender.sendMessage(C.DARK_PURPLE + "- Min Idle Duration: " + C.LIGHT_PURPLE + Form.duration(metrics.minIdleDuration(), 2));
sender.sendMessage(C.DARK_PURPLE + "Caches:");
sender.sendMessage(C.DARK_PURPLE + "- Resource: " + C.LIGHT_PURPLE + caches.sizes()[0] + " (" + caches.counts()[0] + ")");
sender.sendMessage(C.DARK_PURPLE + "- 2D Stream: " + C.LIGHT_PURPLE + caches.sizes()[1] + " (" + caches.counts()[1] + ")");
sender.sendMessage(C.DARK_PURPLE + "- 3D Stream: " + C.LIGHT_PURPLE + caches.sizes()[2] + " (" + caches.counts()[2] + ")");
sender.sendMessage(C.DARK_PURPLE + "- Other: " + C.LIGHT_PURPLE + caches.sizes()[3] + " (" + caches.counts()[3] + ")");
sender.sendMessage(C.DARK_PURPLE + "Other:");
sender.sendMessage(C.DARK_PURPLE + "- Iris Worlds: " + C.LIGHT_PURPLE + metrics.worlds());
sender.sendMessage(C.DARK_PURPLE + "- Loaded Chunks: " + C.LIGHT_PURPLE + metrics.loadedChunks());
sender.sendMessage(C.DARK_PURPLE + "-------------------------");
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_MESSAGE));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_STATUS));
sender.sendMessage(IrisLanguage.text(
BukkitCommandMessages.IRIS_ENGINE_STATUS_SERVICE,
MessageArgument.trusted("value", status(snapshot.serviceRunning()))
));
sender.sendMessage(IrisLanguage.text(
BukkitCommandMessages.IRIS_ENGINE_STATUS_METRICS,
MessageArgument.trusted("value", status(snapshot.metricsRunning()))
));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_MAINTENANCE_PERIOD, MessageArgument.untrusted("value", Form.duration(snapshot.maintenancePeriodMillis()))));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_WORKER_PARALLELISM, MessageArgument.untrusted("value", snapshot.workerParallelism())));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_ACTIVE_WORLD_TASKS, MessageArgument.untrusted("value", metrics.activeTasks())));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_TECTONIC_PLATES));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_CONFIGURED_RETENTION, MessageArgument.untrusted("value", Form.duration(snapshot.retentionMillis()))));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_HEAP_USAGE, MessageArgument.untrusted("value", Form.pc(snapshot.heapUsage()))));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_RESIDENT, MessageArgument.untrusted("value", metrics.residentTectonicPlates())));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_QUEUED, MessageArgument.untrusted("value", metrics.queuedTectonicPlates())));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_AVERAGE_IDLE_DURATION, MessageArgument.untrusted("value", Form.duration(metrics.averageIdleDuration(), 2))));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_MAX_IDLE_DURATION, MessageArgument.untrusted("value", Form.duration(metrics.maxIdleDuration(), 2))));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_MIN_IDLE_DURATION, MessageArgument.untrusted("value", Form.duration(metrics.minIdleDuration(), 2))));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_CACHES));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_RESOURCE, MessageArgument.untrusted("value", caches.sizes()[0]), MessageArgument.untrusted("value2", caches.counts()[0])));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_2D_STREAM, MessageArgument.untrusted("value", caches.sizes()[1]), MessageArgument.untrusted("value2", caches.counts()[1])));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_3D_STREAM, MessageArgument.untrusted("value", caches.sizes()[2]), MessageArgument.untrusted("value2", caches.counts()[2])));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_OTHER, MessageArgument.untrusted("value", caches.sizes()[3]), MessageArgument.untrusted("value2", caches.counts()[3])));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_OTHER_2));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_IRIS_WORLDS, MessageArgument.untrusted("value", metrics.worlds())));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_LOADED_CHUNKS, MessageArgument.untrusted("value", metrics.loadedChunks())));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.IRIS_ENGINE_STATUS_MESSAGE_2));
}
private static String status(boolean running) {
return IrisLanguage.text(running ? RuntimeUiMessages.STATUS_RUNNING : RuntimeUiMessages.STATUS_STOPPED);
}
private static CacheSummary summarizeCaches() {
@@ -22,11 +22,14 @@ import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.World;
import art.arcane.iris.Iris;
import art.arcane.iris.core.IrisSettings;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.RuntimeUiMessages;
import art.arcane.iris.core.edit.DustRevealer;
import art.arcane.iris.core.link.WorldEditLink;
import art.arcane.iris.core.wand.WandSelection;
@@ -53,11 +56,11 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.util.BlockVector;
import org.bukkit.util.Vector;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
@@ -103,7 +106,7 @@ public class WandSVC implements IrisService {
@Override
public String getName() {
return "Scanning Selection";
return IrisLanguage.text(RuntimeUiMessages.JOB_SCANNING_SELECTION);
}
@Override
@@ -253,10 +256,11 @@ public class WandSVC implements IrisService {
ItemStack is = new ItemStack(Material.GLOWSTONE_DUST);
is.addUnsafeEnchantment(Enchantment.FIRE_ASPECT, 1);
ItemMeta im = is.getItemMeta();
im.setDisplayName(C.BOLD + "" + C.YELLOW + "Dust of Revealing");
im.setDisplayName(C.BOLD + "" + C.YELLOW + IrisLanguage.text(RuntimeUiMessages.DUST_NAME));
im.setUnbreakable(true);
im.addItemFlags(ItemFlag.values());
im.setLore(new KList<String>().qadd("Right click on a block to reveal it's placement structure!"));
im.setLore(new KList<String>().qadd(IrisLanguage.text(RuntimeUiMessages.DUST_LORE)));
im.getPersistentDataContainer().set(dustKey(), PersistentDataType.BYTE, (byte) 1);
is.setItemMeta(im);
return is;
@@ -269,19 +273,11 @@ public class WandSVC implements IrisService {
* @return The slot number the wand is in. Or -1 if none are found
*/
public static int findWand(Inventory inventory) {
ItemStack wand = createWand(); //Create blank wand
ItemMeta meta = wand.getItemMeta();
meta.setLore(new ArrayList<>()); //We are resetting the lore as the lore differs between wands
wand.setItemMeta(meta);
for (int s = 0; s < inventory.getSize(); s++) {
ItemStack stack = inventory.getItem(s);
if (stack == null) continue;
meta = stack.getItemMeta();
meta.setLore(new ArrayList<>()); //Reset the lore on this too so we can compare them
stack.setItemMeta(meta); //We dont need to clone the item as items from .get are cloned
if (wand.isSimilar(stack)) return s; //If the name, material and NBT is the same
if (stack != null && isWand(stack)) {
return s;
}
}
return -1;
}
@@ -297,10 +293,14 @@ public class WandSVC implements IrisService {
ItemStack is = new ItemStack(Material.BLAZE_ROD);
is.addUnsafeEnchantment(Enchantment.FIRE_ASPECT, 1);
ItemMeta im = is.getItemMeta();
im.setDisplayName(C.BOLD + "" + C.GOLD + "Wand of Iris");
im.setDisplayName(C.BOLD + "" + C.GOLD + IrisLanguage.text(RuntimeUiMessages.WAND_NAME));
im.setUnbreakable(true);
im.addItemFlags(ItemFlag.values());
im.setLore(new KList<String>().add(locationToString(a), locationToString(b)));
im.setLore(new KList<String>().add(
a == null ? IrisLanguage.text(RuntimeUiMessages.WAND_LORE_FIRST) : locationToString(a),
b == null ? IrisLanguage.text(RuntimeUiMessages.WAND_LORE_SECOND) : locationToString(b)
));
im.getPersistentDataContainer().set(wandKey(), PersistentDataType.BYTE, (byte) 1);
is.setItemMeta(im);
return is;
@@ -343,7 +343,13 @@ public class WandSVC implements IrisService {
* @return True if it is
*/
public static boolean isWand(ItemStack is) {
if (is.getItemMeta() == null) return false;
if (is == null || is.getItemMeta() == null) {
return false;
}
Byte marker = is.getItemMeta().getPersistentDataContainer().get(wandKey(), PersistentDataType.BYTE);
if (marker != null && marker == (byte) 1) {
return true;
}
return is.getType().equals(wand.getType()) &&
is.getItemMeta().getDisplayName().equals(wand.getItemMeta().getDisplayName()) &&
is.getItemMeta().getEnchants().equals(wand.getItemMeta().getEnchants()) &&
@@ -517,7 +523,19 @@ public class WandSVC implements IrisService {
* @return True if it is
*/
public boolean isDust(ItemStack is) {
return is.isSimilar(dust);
if (is == null || is.getItemMeta() == null) {
return false;
}
Byte marker = is.getItemMeta().getPersistentDataContainer().get(dustKey(), PersistentDataType.BYTE);
return (marker != null && marker == (byte) 1) || is.isSimilar(dust);
}
private static NamespacedKey wandKey() {
return new NamespacedKey(Iris.instance, "wand");
}
private static NamespacedKey dustKey() {
return new NamespacedKey(Iris.instance, "dust");
}
/**
@@ -1,6 +1,9 @@
package art.arcane.iris.client;
import art.arcane.iris.core.localization.ClientUiMessages;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.spi.protocol.IrisMessage;
import art.arcane.volmlib.util.localization.MessageArgument;
import java.util.ArrayDeque;
@@ -25,7 +28,7 @@ public final class IrisClientToasts {
public void enqueueHotload(String packKey, int changedFiles, boolean failed, String message) {
int kind = failed ? IrisMessage.Toast.KIND_ERROR : IrisMessage.Toast.KIND_SUCCESS;
enqueue(kind, "Studio Hotload", hotloadBody(packKey, changedFiles, failed, message));
enqueue(kind, IrisLanguage.plain(ClientUiMessages.TOAST_STUDIO_HOTLOAD), hotloadBody(packKey, changedFiles, failed, message));
}
public Pending poll() {
@@ -51,14 +54,14 @@ public final class IrisClientToasts {
builder.append(pack);
}
if (changedFiles != 0) {
append(builder, changedFiles + (changedFiles == 1 ? " file" : " files"));
append(builder, IrisLanguage.plain(ClientUiMessages.TOAST_CHANGED_FILES, MessageArgument.trusted("count", changedFiles)));
}
String text = normalize(message);
if (!text.isEmpty()) {
append(builder, text);
}
if (builder.isEmpty()) {
return failed ? "reload failed" : "reloaded";
return IrisLanguage.plain(failed ? ClientUiMessages.TOAST_RELOAD_FAILED : ClientUiMessages.TOAST_RELOADED);
}
return builder.toString();
}
@@ -1,6 +1,10 @@
package art.arcane.iris.client;
import art.arcane.iris.core.localization.ClientUiMessages;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.RuntimeUiMessages;
import art.arcane.iris.spi.protocol.IrisMessage;
import art.arcane.volmlib.util.localization.MessageArgument;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphicsExtractor;
@@ -47,9 +51,14 @@ public final class IrisPregenHud {
double percent = progress.chunksTotal() > 0L
? clampPercent((double) progress.chunksDone() / (double) progress.chunksTotal() * 100.0D)
: 0.0D;
String title = "Iris Pregen";
String stats = String.format("%,d / %,d (%.1f%%)", progress.chunksDone(), progress.chunksTotal(), percent);
String tail = paused ? "PAUSED" : rateAndEta(progress);
String title = IrisLanguage.plain(RuntimeUiMessages.PREGEN_HEADER);
String stats = IrisLanguage.plain(
ClientUiMessages.PREGEN_STATS,
MessageArgument.trusted("done", String.format("%,d", progress.chunksDone())),
MessageArgument.trusted("total", String.format("%,d", progress.chunksTotal())),
MessageArgument.trusted("percent", String.format("%.1f", percent))
);
String tail = paused ? IrisLanguage.plain(ClientUiMessages.PREGEN_PAUSED) : rateAndEta(progress);
int accent = paused ? PAUSED_COLOR : BAR_RUNNING_COLOR;
int lineHeight = font.lineHeight;
@@ -120,11 +129,15 @@ public final class IrisPregenHud {
}
private static String rateAndEta(IrisMessage.PregenProgress progress) {
String rate = String.format("%,.0f/s", progress.chunksPerSecond());
String rate = String.format("%,.0f", progress.chunksPerSecond());
if (progress.etaMillis() > 0L) {
return rate + " ETA " + formatDuration(progress.etaMillis());
return IrisLanguage.plain(
ClientUiMessages.PREGEN_RATE_ETA,
MessageArgument.trusted("rate", rate),
MessageArgument.trusted("eta", formatDuration(progress.etaMillis()))
);
}
return rate;
return IrisLanguage.plain(ClientUiMessages.PREGEN_RATE, MessageArgument.trusted("rate", rate));
}
private static String formatDuration(long etaMillis) {
@@ -133,12 +146,12 @@ public final class IrisPregenHud {
long minutes = (totalSeconds % 3600L) / 60L;
long seconds = totalSeconds % 60L;
if (hours > 0L) {
return hours + "h " + minutes + "m";
return IrisLanguage.plain(ClientUiMessages.DURATION_HOURS_MINUTES, MessageArgument.trusted("hours", hours), MessageArgument.trusted("minutes", minutes));
}
if (minutes > 0L) {
return minutes + "m " + seconds + "s";
return IrisLanguage.plain(ClientUiMessages.DURATION_MINUTES_SECONDS, MessageArgument.trusted("minutes", minutes), MessageArgument.trusted("seconds", seconds));
}
return seconds + "s";
return IrisLanguage.plain(ClientUiMessages.DURATION_SECONDS, MessageArgument.trusted("seconds", seconds));
}
private static double clampPercent(double value) {
@@ -1,6 +1,9 @@
package art.arcane.iris.client;
import art.arcane.iris.core.localization.ClientUiMessages;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.spi.protocol.IrisMessage;
import art.arcane.volmlib.util.localization.MessageArgument;
import com.mojang.blaze3d.platform.NativeImage;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphicsExtractor;
@@ -45,7 +48,7 @@ public final class IrisVisionScreen extends Screen {
private String renderedDimensionKey;
public IrisVisionScreen() {
super(Component.literal("Iris Vision"));
super(Component.literal(IrisLanguage.plain(ClientUiMessages.VISION_TITLE)));
this.textures = new LinkedHashMap<>(64, 0.75f, true);
this.centerBlockX = 0.0D;
this.centerBlockZ = 0.0D;
@@ -72,14 +75,14 @@ public final class IrisVisionScreen extends Screen {
graphics.fill(0, 0, width, height, BACKGROUND_COLOR);
IrisClientSession session = IrisClient.session();
if (!session.isReady()) {
drawCentered(graphics, "Connecting to Iris server...", MUTED_COLOR);
drawHeader(graphics, "Iris Vision", "not connected");
drawCentered(graphics, IrisLanguage.plain(ClientUiMessages.VISION_CONNECTING), MUTED_COLOR);
drawHeader(graphics, IrisLanguage.plain(ClientUiMessages.VISION_TITLE), IrisLanguage.plain(ClientUiMessages.VISION_NOT_CONNECTED));
return;
}
IrisMessage.DimensionStatus status = IrisClient.dimension().status();
if (!IrisClient.visionAvailable() || status == null) {
drawCentered(graphics, "Not an Iris world", MUTED_COLOR);
drawHeader(graphics, "Iris Vision", status == null ? "no dimension data" : label(status));
drawCentered(graphics, IrisLanguage.plain(ClientUiMessages.VISION_NOT_IRIS_WORLD), MUTED_COLOR);
drawHeader(graphics, IrisLanguage.plain(ClientUiMessages.VISION_TITLE), status == null ? IrisLanguage.plain(ClientUiMessages.VISION_NO_DIMENSION_DATA) : label(status));
return;
}
syncWorld(status);
@@ -159,8 +162,18 @@ public final class IrisVisionScreen extends Screen {
drawMarkers(graphics, mouseX, mouseY, minTileX, maxTileX, minTileZ, maxTileZ, originX, originY, blocksPerPixel);
drawPlayer(graphics, blocksPerPixel);
drawHeader(graphics, "Iris Vision", label(status) + " zoom " + zoom + " x" + (long) centerBlockX + " z" + (long) centerBlockZ);
drawFooter(graphics, "Drag to pan Scroll to zoom Esc to close");
drawHeader(
graphics,
IrisLanguage.plain(ClientUiMessages.VISION_TITLE),
IrisLanguage.plain(
ClientUiMessages.VISION_HEADER_DETAIL,
MessageArgument.trusted("status", label(status)),
MessageArgument.trusted("zoom", zoom),
MessageArgument.trusted("x", (long) centerBlockX),
MessageArgument.trusted("z", (long) centerBlockZ)
)
);
drawFooter(graphics, IrisLanguage.plain(ClientUiMessages.VISION_FOOTER_HINT));
}
private void requestMissing(IrisClientTileCache cache, List<IrisTileKey> missing, int centerTileX, int centerTileZ) {
@@ -319,8 +332,14 @@ public final class IrisVisionScreen extends Screen {
}
private static String label(IrisMessage.DimensionStatus status) {
String pack = status.packKey() == null || status.packKey().isEmpty() ? "" : " pack " + status.packKey();
return status.dimensionKey() + pack;
if (status.packKey() == null || status.packKey().isEmpty()) {
return String.valueOf(status.dimensionKey());
}
return IrisLanguage.plain(
ClientUiMessages.VISION_DIMENSION_PACK,
MessageArgument.untrusted("dimension", String.valueOf(status.dimensionKey())),
MessageArgument.untrusted("pack", status.packKey())
);
}
private record TileTexture(Identifier id, DynamicTexture texture) {
@@ -1,6 +1,9 @@
package art.arcane.iris.client;
import art.arcane.iris.core.localization.ClientUiMessages;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.spi.protocol.IrisMessage;
import art.arcane.volmlib.util.localization.MessageArgument;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphicsExtractor;
@@ -44,25 +47,31 @@ public final class IrisWhatOverlay {
IrisClient.cursor().requestFor(blockX, blockZ);
IrisMessage.CursorInfo info = IrisClient.cursor().latest();
List<String> lines = new ArrayList<>();
List<OverlayLine> lines = new ArrayList<>();
if (info == null) {
lines.add("querying " + blockX + ", " + blockZ + "...");
lines.add(new OverlayLine(IrisLanguage.plain(ClientUiMessages.WHAT_QUERYING, MessageArgument.trusted("x", blockX), MessageArgument.trusted("z", blockZ)), false));
} else {
lines.add("Biome: " + display(info.biomeKey()));
lines.add("Region: " + display(info.regionKey()));
lines.add(new OverlayLine(IrisLanguage.plain(ClientUiMessages.WHAT_BIOME, MessageArgument.untrusted("biome", display(info.biomeKey()))), false));
lines.add(new OverlayLine(IrisLanguage.plain(ClientUiMessages.WHAT_REGION, MessageArgument.untrusted("region", display(info.regionKey()))), false));
if (info.caveBiomeKey() != null && !info.caveBiomeKey().isEmpty()) {
lines.add("Cave: " + display(info.caveBiomeKey()));
lines.add(new OverlayLine(IrisLanguage.plain(ClientUiMessages.WHAT_CAVE, MessageArgument.untrusted("cave", display(info.caveBiomeKey()))), false));
}
lines.add("Height: " + info.height() + " (" + info.blockX() + ", " + info.blockZ() + ")");
lines.add(new OverlayLine(IrisLanguage.plain(
ClientUiMessages.WHAT_HEIGHT,
MessageArgument.trusted("height", info.height()),
MessageArgument.trusted("x", info.blockX()),
MessageArgument.trusted("z", info.blockZ())
), true));
}
draw(graphics, minecraft.font, lines);
}
private static void draw(GuiGraphicsExtractor graphics, Font font, List<String> lines) {
private static void draw(GuiGraphicsExtractor graphics, Font font, List<OverlayLine> lines) {
int lineHeight = font.lineHeight;
int contentWidth = font.width("Iris What");
for (String line : lines) {
contentWidth = Math.max(contentWidth, font.width(line));
String title = IrisLanguage.plain(ClientUiMessages.WHAT_TITLE);
int contentWidth = font.width(title);
for (OverlayLine line : lines) {
contentWidth = Math.max(contentWidth, font.width(line.text()));
}
int originX = graphics.guiWidth() / 2 + CURSOR_OFFSET;
int originY = graphics.guiHeight() / 2 + CURSOR_OFFSET;
@@ -71,10 +80,10 @@ public final class IrisWhatOverlay {
graphics.fill(originX - PADDING, originY - PADDING, originX + contentWidth + PADDING, originY + contentHeight + PADDING, PANEL_COLOR);
int cursorY = originY;
graphics.text(font, "Iris What", originX, cursorY, TITLE_COLOR);
graphics.text(font, title, originX, cursorY, TITLE_COLOR);
cursorY += lineHeight + ROW_GAP;
for (String line : lines) {
graphics.text(font, line, originX, cursorY, line.startsWith("Height") ? MUTED_COLOR : TEXT_COLOR);
for (OverlayLine line : lines) {
graphics.text(font, line.text(), originX, cursorY, line.muted() ? MUTED_COLOR : TEXT_COLOR);
cursorY += lineHeight + ROW_GAP;
}
}
@@ -82,4 +91,7 @@ public final class IrisWhatOverlay {
private static String display(String key) {
return key == null || key.isEmpty() ? "-" : key;
}
private record OverlayLine(String text, boolean muted) {
}
}
@@ -4,3 +4,4 @@ accessible field net/minecraft/server/MinecraftServer executor Ljava/util/concur
accessible field net/minecraft/server/MinecraftServer storageSource Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;
accessible field net/minecraft/server/packs/repository/PackRepository sources Ljava/util/Set;
mutable field net/minecraft/server/packs/repository/PackRepository sources Ljava/util/Set;
accessible field net/minecraft/core/MappedRegistry frozen Z
@@ -1,3 +1,4 @@
public net.minecraft.server.MinecraftServer levels
public net.minecraft.server.MinecraftServer executor
public net.minecraft.server.MinecraftServer storageSource
public net.minecraft.core.MappedRegistry frozen
@@ -152,9 +152,19 @@ public final class ModdedDimensionManager {
}
public static Handle createPersistent(MinecraftServer server, String dimensionId, String pack, String packDimensionKey, long seed) {
Handle handle = create(server, dimensionId, pack, packDimensionKey, seed);
ModdedDimensionRegistryStore.put(server, new ModdedDimensionRegistryStore.PersistentDimension(dimensionId, pack, packDimensionKey, seed));
return handle;
try {
return create(server, dimensionId, pack, packDimensionKey, seed);
} catch (Throwable e) {
ModdedDimensionRegistryStore.remove(server, dimensionId);
if (e instanceof RuntimeException runtimeException) {
throw runtimeException;
}
if (e instanceof Error fatalError) {
throw fatalError;
}
throw new IllegalStateException("Iris runtime dimension injection failed for " + dimensionId, e);
}
}
public static boolean removePersistent(MinecraftServer server, String dimensionId, boolean wipeStorage) {
@@ -265,9 +275,10 @@ public final class ModdedDimensionManager {
IrisDimension dimension = loadPackDimension(pack, packDimensionKey);
String typeRef = ModdedForcedDatapack.dimensionTypeRef(dimension);
ResourceKey<DimensionType> typeKey = ResourceKey.create(Registries.DIMENSION_TYPE, Identifier.parse(typeRef));
Holder.Reference<DimensionType> packType = ModdedForcedDatapack.requireRegisteredDimensionType(
ModdedRuntimeRegistry.ensureCustomBiomes(registryAccess, dimension, pack);
ModdedRuntimeRegistry.ensureDimensionType(registryAccess, registry, typeKey, typeRef, dimension);
return ModdedForcedDatapack.requireRegisteredDimensionType(
typeRef, registry.get(typeKey), pack, packDimensionKey);
return packType;
}
private static IrisDimension loadPackDimension(String pack, String packDimensionKey) {
@@ -19,6 +19,7 @@
package art.arcane.iris.modded;
import art.arcane.iris.core.gui.GuiHost;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.engine.decorator.DecoratorPlatformHooks;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.framework.EngineEffectsProvider;
@@ -260,6 +261,7 @@ public final class ModdedEngineBootstrap {
ModdedIrisLog.info("Iris " + moddedLoader.modVersion() + " bootstrapping on Minecraft " + moddedLoader.minecraftVersion() + " (" + loaderDescription + ")");
selfTest(moddedLoader.getClass().getClassLoader());
bind();
IrisLanguage.initialize();
MainWorldService.reconcileEarly();
chunkGeneratorRegistration.run();
ModdedIrisLog.info("Iris chunk generator registered as irisworldgen:iris");
@@ -19,6 +19,8 @@
package art.arcane.iris.modded;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.RuntimeUiMessages;
import art.arcane.iris.core.nms.datapack.DataVersion;
import art.arcane.iris.core.nms.datapack.IDataFixer;
import art.arcane.iris.engine.object.IrisDimension;
@@ -85,7 +87,7 @@ public final class ModdedForcedDatapack {
private static Pack requireReadablePack(Path directory) {
PackLocationInfo location = new PackLocationInfo(
PACK_ID,
Component.literal("Iris World Generation"),
Component.literal(IrisLanguage.plain(RuntimeUiMessages.FORCED_DATAPACK_NAME)),
PackSource.BUILT_IN,
Optional.empty());
PackSelectionConfig selection = new PackSelectionConfig(true, Pack.Position.TOP, true);
@@ -18,7 +18,10 @@
package art.arcane.iris.modded;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.PackDownloadMessages;
import art.arcane.iris.core.pack.PackDownloader;
import art.arcane.volmlib.util.localization.MessageArgument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,11 +41,11 @@ public final class ModdedPackInstaller {
public static boolean install(Path configDir, String pack, String branch, Consumer<String> feedback) {
if (pack == null || !PACK_NAME.matcher(pack).matches()) {
feedback.accept("Invalid pack name '" + pack + "' (allowed: a-z, 0-9, _ and -)");
feedback.accept(IrisLanguage.plain(PackDownloadMessages.INVALID_PACK_NAME, MessageArgument.untrusted("pack", String.valueOf(pack))));
return false;
}
if (branch == null || !BRANCH_NAME.matcher(branch).matches()) {
feedback.accept("Invalid branch name '" + branch + "' (allowed: letters, digits, . _ and -)");
feedback.accept(IrisLanguage.plain(PackDownloadMessages.INVALID_BRANCH_NAME, MessageArgument.untrusted("branch", String.valueOf(branch))));
return false;
}
@@ -54,7 +57,11 @@ public final class ModdedPackInstaller {
return PackDownloader.download(packs, "IrisDimensions/" + pack, branch, true, false, feedback) != null;
} catch (IOException error) {
LOGGER.error("Iris pack download failed for IrisDimensions/{} ({})", pack, branch, error);
feedback.accept("Pack download failed: " + error.getClass().getSimpleName() + (error.getMessage() == null ? "" : " - " + error.getMessage()));
feedback.accept(IrisLanguage.plain(
PackDownloadMessages.DOWNLOAD_FAILED,
MessageArgument.untrusted("type", error.getClass().getSimpleName()),
MessageArgument.trusted("errorMessage", IrisLanguage.errorDetail(error))
));
return false;
}
}
@@ -0,0 +1,136 @@
/*
* Iris is a World Generator for Minecraft Servers
* Copyright (c) 2026 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.modded;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.core.nms.datapack.DataVersion;
import art.arcane.iris.core.nms.datapack.IDataFixer;
import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.object.IrisBiomeCustom;
import art.arcane.iris.engine.object.IrisDimension;
import art.arcane.iris.util.common.data.DataProvider;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;
import net.minecraft.core.Holder;
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.RegistrationInfo;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.dimension.DimensionType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.HashSet;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
public final class ModdedRuntimeRegistry {
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
private static final Object LOCK = new Object();
private ModdedRuntimeRegistry() {
}
static void ensureDimensionType(RegistryAccess registryAccess, Registry<DimensionType> registry,
ResourceKey<DimensionType> typeKey, String typeRef, IrisDimension dimension) {
if (registry.get(typeKey).isPresent()) {
return;
}
IDataFixer fixer = DataVersion.getLatest().get();
String json = dimension.getDimensionType().toJson(fixer);
DimensionType type = decode(registryAccess, DimensionType.DIRECT_CODEC, json, typeRef);
registerIntoFrozen(registry, typeKey, type, typeRef);
LOGGER.info("Iris registered runtime dimension type '{}'", typeRef);
}
static void ensureCustomBiomes(RegistryAccess registryAccess, IrisDimension dimension, String pack) {
File packFolder = ModdedWorldEngines.packFolder(pack);
if (!packFolder.isDirectory()) {
return;
}
Registry<Biome> registry = registryAccess.lookupOrThrow(Registries.BIOME);
IrisData data = IrisData.get(packFolder);
DataProvider provider = () -> data;
IDataFixer fixer = DataVersion.getLatest().get();
String namespace = dimension.getLoadKey().toLowerCase(Locale.ROOT);
Set<String> seen = new HashSet<>();
int registered = 0;
for (IrisBiome irisBiome : dimension.getAllBiomes(provider)) {
if (!irisBiome.isCustom()) {
continue;
}
for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) {
String biomeId = customBiome.getId();
if (!seen.add(biomeId)) {
continue;
}
String biomeRef = namespace + ":" + biomeId;
ResourceKey<Biome> biomeKey = ResourceKey.create(Registries.BIOME, Identifier.parse(biomeRef));
if (registry.get(biomeKey).isPresent()) {
continue;
}
String json = customBiome.generateJson(fixer);
Biome biome = decode(registryAccess, Biome.DIRECT_CODEC, json, biomeRef);
registerIntoFrozen(registry, biomeKey, biome, biomeRef);
registered++;
}
}
if (registered > 0) {
LOGGER.info("Iris registered {} runtime biome(s) for pack '{}'", registered, pack);
}
}
private static <T> T decode(RegistryAccess registryAccess, Codec<T> codec, String json, String ref) {
JsonElement element = JsonParser.parseString(json);
RegistryOps<JsonElement> ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess);
return codec.parse(ops, element).getOrThrow((String message) ->
new IllegalStateException("Iris could not decode runtime registry entry '" + ref + "': " + message));
}
private static <T> Holder.Reference<T> registerIntoFrozen(Registry<T> registry, ResourceKey<T> key, T value, String ref) {
if (!(registry instanceof MappedRegistry<T> mapped)) {
throw new IllegalStateException("Iris cannot register '" + ref + "' at runtime: "
+ registry.getClass().getName() + " is not a MappedRegistry");
}
synchronized (LOCK) {
Optional<Holder.Reference<T>> raced = registry.get(key);
if (raced.isPresent()) {
return raced.get();
}
boolean wasFrozen = mapped.frozen;
mapped.frozen = false;
try {
return mapped.register(key, value, RegistrationInfo.BUILT_IN);
} finally {
if (wasFrozen) {
mapped.freeze();
}
}
}
}
}
@@ -19,6 +19,7 @@
package art.arcane.iris.modded.command;
import art.arcane.iris.core.IrisSettings;
import art.arcane.iris.core.localization.IrisMessages;
import art.arcane.iris.core.gui.GuiHost;
import art.arcane.iris.core.loader.IrisRegistrant;
import art.arcane.iris.core.pack.PackDownloader;
@@ -106,6 +107,10 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.function.Predicate;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.ModdedCommandMessages;
import art.arcane.iris.core.localization.RuntimeUiMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
public final class IrisModdedCommands {
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
private static final Predicate<CommandSourceStack> GATE = Commands.hasPermission(Commands.LEVEL_GAMEMASTERS);
@@ -386,27 +391,27 @@ public final class IrisModdedCommands {
private static int editBiome(CommandSourceStack source, String key) {
Engine engine = engineFor(source.getLevel());
if (engine == null) {
fail(source, "This dimension is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS));
return 0;
}
IrisBiome biome;
if (key == null || key.isBlank()) {
ServerPlayer player = source.getPlayer();
if (player == null) {
fail(source, "Console must name a biome: /iris edit biome <key>");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_CONSOLE_MUST_NAME_BIOME_IRIS_EDIT_BIOME_KEY));
return 0;
}
BlockPos pos = player.blockPosition();
try {
biome = engine.getBiome(pos.getX(), pos.getY() - engine.getMinHeight(), pos.getZ());
} catch (Throwable e) {
fail(source, "Biome lookup failed: " + e.getClass().getSimpleName());
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_BIOME_LOOKUP_FAILED, MessageArgument.untrusted("value", e.getClass().getSimpleName())));
return 0;
}
} else {
biome = engine.getData().getBiomeLoader().load(key.trim());
if (biome == null) {
fail(source, "Unknown biome: " + key);
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_UNKNOWN_BIOME, MessageArgument.untrusted("key", key)));
return 0;
}
}
@@ -416,27 +421,27 @@ public final class IrisModdedCommands {
private static int editRegion(CommandSourceStack source, String key) {
Engine engine = engineFor(source.getLevel());
if (engine == null) {
fail(source, "This dimension is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_2));
return 0;
}
IrisRegion region;
if (key == null || key.isBlank()) {
ServerPlayer player = source.getPlayer();
if (player == null) {
fail(source, "Console must name a region: /iris edit region <key>");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_CONSOLE_MUST_NAME_REGION_IRIS_EDIT_REGION_KEY));
return 0;
}
BlockPos pos = player.blockPosition();
try {
region = engine.getRegion(pos.getX(), pos.getZ());
} catch (Throwable e) {
fail(source, "Region lookup failed: " + e.getClass().getSimpleName());
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_REGION_LOOKUP_FAILED, MessageArgument.untrusted("value", e.getClass().getSimpleName())));
return 0;
}
} else {
region = engine.getData().getRegionLoader().load(key.trim());
if (region == null) {
fail(source, "Unknown region: " + key);
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_UNKNOWN_REGION, MessageArgument.untrusted("key", key)));
return 0;
}
}
@@ -446,7 +451,7 @@ public final class IrisModdedCommands {
private static int editDimension(CommandSourceStack source) {
Engine engine = engineFor(source.getLevel());
if (engine == null) {
fail(source, "This dimension is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_3));
return 0;
}
return openJson(source, engine.getDimension());
@@ -454,11 +459,11 @@ public final class IrisModdedCommands {
private static int openJson(CommandSourceStack source, IrisRegistrant registrant) {
if (!GuiHost.isAvailable() || !Desktop.isDesktopSupported()) {
fail(source, "Cannot open files here: " + ModdedGuiHost.guiUnavailableReason());
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_CANNOT_OPEN_FILES_HERE, MessageArgument.untrusted("value", ModdedGuiHost.guiUnavailableReason())));
return 0;
}
if (registrant == null || registrant.getLoadFile() == null || !registrant.getLoadFile().isFile()) {
fail(source, "Cannot find the file; perhaps it was not loaded directly from a file?");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_CANNOT_FIND_FILE_PERHAPS_IT_WAS_NOT_LOADED_DIRECTLY_FROM));
return 0;
}
File file = registrant.getLoadFile();
@@ -466,29 +471,29 @@ public final class IrisModdedCommands {
Desktop.getDesktop().open(file);
} catch (Throwable e) {
LOGGER.error("Iris edit failed to open {}", file, e);
fail(source, "Could not open " + file.getName() + ": " + e.getClass().getSimpleName());
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_COULD_NOT_OPEN, MessageArgument.untrusted("value", file.getName()), MessageArgument.untrusted("value2", e.getClass().getSimpleName())));
return 0;
}
ok(source, "Opening " + registrant.getTypeName() + " " + file.getName() + " in your editor.");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_OPENING_YOUR_EDITOR, MessageArgument.untrusted("value", registrant.getTypeName()), MessageArgument.untrusted("value2", file.getName())));
return 1;
}
private static int tp(CommandSourceStack source, ServerLevel level, ServerPlayer target) {
ServerPlayer player = target != null ? target : source.getPlayer();
if (player == null) {
fail(source, "Console must name a player: /iris tp <dimension> <player>");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_CONSOLE_MUST_NAME_PLAYER_IRIS_TP_DIMENSION_PLAYER));
return 0;
}
if (!(level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator)) {
fail(source, level.dimension().identifier() + " is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_IS_NOT_GENERATED_BY_IRIS, MessageArgument.untrusted("value", level.dimension().identifier())));
return 0;
}
String dimensionId = level.dimension().identifier().toString();
if (!ModdedDimensionManager.teleport(player, source.getServer(), dimensionId, 8.5D, Double.MIN_VALUE, 8.5D)) {
fail(source, "Teleport failed: dimension " + dimensionId + " is not loaded.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORT_FAILED_DIMENSION_IS_NOT_LOADED, MessageArgument.untrusted("dimensionId", dimensionId)));
return 0;
}
ok(source, "Teleporting " + player.getScoreboardName() + " to " + dimensionId + "...");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORTING, MessageArgument.untrusted("value", player.getScoreboardName()), MessageArgument.untrusted("dimensionId", dimensionId)));
return 1;
}
@@ -496,16 +501,16 @@ public final class IrisModdedCommands {
MinecraftServer server = source.getServer();
ServerLevel level = target != null ? target : source.getLevel();
if (!(level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator)) {
fail(source, level.dimension().identifier() + " is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_IS_NOT_GENERATED_BY_IRIS_2, MessageArgument.untrusted("value", level.dimension().identifier())));
return 0;
}
ServerLevel fallback = server.overworld();
if (fallback == level) {
fail(source, "Cannot evacuate the primary world; there is nowhere to send players.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_CANNOT_EVACUATE_PRIMARY_WORLD_THERE_IS_NOWHERE_SEND_PLAYERS));
return 0;
}
int count = ModdedDimensionManager.evacuate(server, level);
ok(source, "Evacuated " + count + " player(s) from " + level.dimension().identifier() + " to " + fallback.dimension().identifier() + ".");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_EVACUATED_PLAYER_S_FROM, MessageArgument.untrusted("count", count), MessageArgument.untrusted("value", level.dimension().identifier()), MessageArgument.untrusted("value2", fallback.dimension().identifier())));
return 1;
}
@@ -513,7 +518,7 @@ public final class IrisModdedCommands {
boolean to = !IrisSettings.get().getGeneral().isDebug();
IrisSettings.get().getGeneral().setDebug(to);
IrisSettings.get().forceSave();
ok(source, "Set debug to: " + to);
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_SET_DEBUG, MessageArgument.untrusted("to", to)));
return 1;
}
@@ -522,39 +527,51 @@ public final class IrisModdedCommands {
IrisSettings.invalidate();
}
IrisSettings.get();
ok(source, "Hotloaded settings");
boolean localeLoaded = IrisLanguage.reload();
if (localeLoaded) {
ok(source, IrisLanguage.plain(
IrisMessages.COMMAND_RELOAD_SUCCESS,
MessageArgument.trusted("locale", IrisLanguage.activeLocale())
));
return 1;
}
fail(source, IrisLanguage.plain(
IrisMessages.COMMAND_RELOAD_FAILED,
MessageArgument.untrusted("locale", IrisSettings.get().getGeneral().getLanguage()),
MessageArgument.trusted("activeLocale", IrisLanguage.activeLocale())
));
return 1;
}
private static int whatHand(CommandSourceStack source) {
ServerPlayer player = source.getPlayer();
if (player == null) {
fail(source, "This command can only be used by players (it inspects your held item).");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_IT_INSPECTS));
return 0;
}
ItemStack stack = player.getMainHandItem();
if (stack.isEmpty()) {
fail(source, "Your main hand is empty.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_YOUR_MAIN_HAND_IS_EMPTY));
return 0;
}
ok(source, "Hand: " + BuiltInRegistries.ITEM.getKey(stack.getItem()) + " x" + stack.getCount());
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_HAND_X, MessageArgument.untrusted("value", BuiltInRegistries.ITEM.getKey(stack.getItem())), MessageArgument.untrusted("value2", stack.getCount())));
return 1;
}
private static int regen(CommandSourceStack source, int radius) {
ServerPlayer player = source.getPlayer();
if (player == null) {
fail(source, "This command can only be used by players.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS));
return 0;
}
ServerLevel level = source.getLevel();
if (!(level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator irisGenerator)) {
fail(source, "This dimension is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_4));
return 0;
}
Engine engine = engineFor(level);
if (engine == null) {
fail(source, "This dimension is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_5));
return 0;
}
ModdedRegen.start(source, level, irisGenerator, engine, player, radius);
@@ -570,15 +587,15 @@ public final class IrisModdedCommands {
Engine engine = engineFor(level);
if (engine == null) {
if (withDimension) {
fail(source, level.dimension().identifier() + " is not generated by Iris; see /iris info for loaded Iris dimensions.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_IS_NOT_GENERATED_BY_IRIS_SEE_IRIS_INFO_LOADED_IRIS, MessageArgument.untrusted("value", level.dimension().identifier())));
} else {
fail(source, "The current dimension (" + level.dimension().identifier() + ") is not generated by Iris. Name one explicitly: /iris pregen start " + radius + " <dimension>; see /iris info for loaded Iris dimensions.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_CURRENT_DIMENSION_IS_NOT_GENERATED_BY_IRIS_NAME_ONE_EXPLICITLY, MessageArgument.untrusted("value", level.dimension().identifier()), MessageArgument.untrusted("radius", radius)));
}
return 0;
}
boolean showGui = gui && ModdedGuiHost.isGuiLaunchable();
if (!ModdedPregenJob.start(source.getServer(), level, engine, radius, centerX, centerZ, showGui, sync, !nocache)) {
fail(source, "A pregeneration task is already running. Stop it first with /iris pregen stop.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_PREGENERATION_TASK_IS_ALREADY_RUNNING_STOP_IT_FIRST_WITH_IRIS));
return 0;
}
ModdedPregenBossBar.begin(source.getPlayer());
@@ -591,35 +608,34 @@ public final class IrisModdedCommands {
guiNote = " (GUI requested but unavailable: " + ModdedGuiHost.guiUnavailableReason() + ")";
}
String modeNote = " Mode: " + (sync ? "sync" : "async") + (nocache ? ", cache disabled." : ", resumable (checkpoint cache).");
ok(source, "Pregen started in " + level.dimension().identifier() + " of " + (radius * 2) + " by " + (radius * 2)
+ " blocks from " + centerX + "," + centerZ + "." + modeNote + " Progress logs to console; see /iris pregen status." + guiNote);
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_PREGEN_STARTED_BY_BLOCKS_FROM_PROGRESS_LOGS_CONSOLE_SEE_IRIS, MessageArgument.untrusted("value", level.dimension().identifier()), MessageArgument.untrusted("value2", (radius * 2)), MessageArgument.untrusted("value3", (radius * 2)), MessageArgument.untrusted("centerX", centerX), MessageArgument.untrusted("centerZ", centerZ), MessageArgument.untrusted("modeNote", modeNote), MessageArgument.untrusted("guiNote", guiNote)));
return 1;
}
private static int pregenStop(CommandSourceStack source) {
if (ModdedPregenJob.stop()) {
ModdedPregenBossBar.clear();
ok(source, "Stopping pregeneration; finishing up the current region...");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_STOPPING_PREGENERATION_FINISHING_UP_CURRENT_REGION));
return 1;
}
fail(source, "No active pregeneration task to stop.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_NO_ACTIVE_PREGENERATION_TASK_STOP));
return 0;
}
private static int pregenPause(CommandSourceStack source) {
Boolean paused = ModdedPregenJob.pauseResume();
if (paused == null) {
fail(source, "No active pregeneration task to pause/resume.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_NO_ACTIVE_PREGENERATION_TASK_PAUSE_RESUME));
return 0;
}
ok(source, "Pregeneration is now " + (paused.booleanValue() ? "paused" : "running") + ".");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_PREGENERATION_IS_NOW, MessageArgument.trusted("value", IrisLanguage.plain(paused.booleanValue() ? RuntimeUiMessages.STATUS_PAUSED_LOWER : RuntimeUiMessages.STATUS_RUNNING_LOWER))));
return 1;
}
private static int pregenStatus(CommandSourceStack source) {
Component status = ModdedPregenJob.statusComponent();
if (status == null) {
fail(source, "No active pregeneration task.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_NO_ACTIVE_PREGENERATION_TASK));
return 0;
}
ok(source, status);
@@ -629,8 +645,7 @@ public final class IrisModdedCommands {
private static int version(CommandSourceStack source) {
ModdedLoader loader = ModdedEngineBootstrap.loader();
int engines = engineCount(source.getServer());
ok(source, "Iris " + loader.modVersion() + " by Volmit Software on " + loader.platformName()
+ " (Minecraft " + loader.minecraftVersion() + "), " + engines + " Iris dimension(s)");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_IRIS_BY_VOLMIT_SOFTWARE_ON_MINECRAFT_IRIS_DIMENSION_S, MessageArgument.untrusted("value", loader.modVersion()), MessageArgument.untrusted("value2", loader.platformName()), MessageArgument.untrusted("value3", loader.minecraftVersion()), MessageArgument.untrusted("engines", engines)));
return 1;
}
@@ -661,9 +676,13 @@ public final class IrisModdedCommands {
+ " generated=" + engine.getGenerated()
+ " data=" + engine.getData().getDataFolder().getAbsolutePath());
}
ok(source, "Loaded dimensions: " + total + " (" + iris + " Iris)");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_LOADED_DIMENSIONS_IRIS, MessageArgument.untrusted("total", total), MessageArgument.untrusted("iris", iris)));
if (lines.isEmpty()) {
ok(source, filter == null ? "No Iris dimensions are loaded." : "No Iris dimension matches '" + filter + "'.");
if (filter == null) {
ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_NO_IRIS_DIMENSIONS_ARE_LOADED));
} else {
ok(source, IrisLanguage.plain(RuntimeUiMessages.MODDED_NO_DIMENSION_MATCH, MessageArgument.untrusted("filter", filter)));
}
return 0;
}
for (String line : lines) {
@@ -675,58 +694,58 @@ public final class IrisModdedCommands {
private static int what(CommandSourceStack source) {
ServerPlayer player = source.getPlayer();
if (player == null) {
fail(source, "This command can only be used by players.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_2));
return 0;
}
ServerLevel level = source.getLevel();
Engine engine = engineFor(level);
if (engine == null) {
fail(source, "This dimension is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_6));
return 0;
}
BlockPos pos = player.blockPosition();
int relativeY = pos.getY() - engine.getMinHeight();
try {
IrisBiome biome = engine.getBiome(pos.getX(), relativeY, pos.getZ());
ok(source, "Biome: " + biome.getLoadKey() + " (" + biome.getName() + ")");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_BIOME, MessageArgument.untrusted("value", biome.getLoadKey()), MessageArgument.untrusted("value2", biome.getName())));
} catch (Throwable e) {
fail(source, "Biome lookup failed: " + e.getClass().getSimpleName());
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_BIOME_LOOKUP_FAILED_2, MessageArgument.untrusted("value", e.getClass().getSimpleName())));
}
try {
IrisRegion region = engine.getRegion(pos.getX(), pos.getZ());
ok(source, "Region: " + region.getLoadKey() + " (" + region.getName() + ")");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_REGION, MessageArgument.untrusted("value", region.getLoadKey()), MessageArgument.untrusted("value2", region.getName())));
} catch (Throwable e) {
fail(source, "Region lookup failed: " + e.getClass().getSimpleName());
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_REGION_LOOKUP_FAILED_2, MessageArgument.untrusted("value", e.getClass().getSimpleName())));
}
try {
IrisBiome cave = engine.getCaveBiome(pos.getX(), relativeY, pos.getZ());
ok(source, "Cave biome: " + (cave == null ? "none" : cave.getLoadKey()));
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_CAVE_BIOME, MessageArgument.untrusted("value", cave == null ? IrisLanguage.plain(RuntimeUiMessages.STATUS_NONE) : cave.getLoadKey())));
} catch (Throwable e) {
fail(source, "Cave biome lookup failed: " + e.getClass().getSimpleName());
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_CAVE_BIOME_LOOKUP_FAILED, MessageArgument.untrusted("value", e.getClass().getSimpleName())));
}
int surfaceY = level.getHeight(Heightmap.Types.WORLD_SURFACE, pos.getX(), pos.getZ());
BlockState surface = level.getBlockState(new BlockPos(pos.getX(), surfaceY - 1, pos.getZ()));
ok(source, "Surface block: " + BuiltInRegistries.BLOCK.getKey(surface.getBlock()) + " (y=" + (surfaceY - 1) + ")");
ok(source, "Position: " + pos.getX() + " " + pos.getY() + " " + pos.getZ() + " (chunk " + (pos.getX() >> 4) + "," + (pos.getZ() >> 4) + ")");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_SURFACE_BLOCK_Y, MessageArgument.untrusted("value", BuiltInRegistries.BLOCK.getKey(surface.getBlock())), MessageArgument.untrusted("value2", (surfaceY - 1))));
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_POSITION_CHUNK, MessageArgument.untrusted("value", pos.getX()), MessageArgument.untrusted("value2", pos.getY()), MessageArgument.untrusted("value3", pos.getZ()), MessageArgument.untrusted("value4", (pos.getX() >> 4)), MessageArgument.untrusted("value5", (pos.getZ() >> 4))));
return 1;
}
private static int whatBlock(CommandSourceStack source) {
ServerPlayer player = source.getPlayer();
if (player == null) {
fail(source, "This command can only be used by players (it inspects the block you are looking at).");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_IT_INSPECTS_2));
return 0;
}
HitResult hit = player.pick(128.0D, 1.0F, false);
if (hit.getType() != HitResult.Type.BLOCK || !(hit instanceof BlockHitResult blockHit)) {
fail(source, "Look at a block, not the sky.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_LOOK_AT_BLOCK_NOT_SKY));
return 0;
}
ServerLevel level = source.getLevel();
BlockPos pos = blockHit.getBlockPos();
BlockState state = level.getBlockState(pos);
PlatformBlockState platform = ModdedBlockState.of(state, null);
ok(source, "Block: " + platform.key() + " (y=" + pos.getY() + ")");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_BLOCK_Y, MessageArgument.untrusted("value", platform.key()), MessageArgument.untrusted("value2", pos.getY())));
List<String> flags = new ArrayList<>();
if (platform.isSolid()) {
flags.add("solid");
@@ -761,20 +780,27 @@ public final class IrisModdedCommands {
if (platform.hasTileEntity()) {
flags.add("tile entity");
}
ok(source, flags.isEmpty() ? "Properties: (none)" : "Properties: " + String.join(", ", flags));
if (flags.isEmpty()) {
ok(source, IrisLanguage.plain(IrisMessages.MODDED_PROPERTIES_NONE));
return 1;
}
ok(source, IrisLanguage.plain(
IrisMessages.MODDED_PROPERTIES,
MessageArgument.untrusted("properties", String.join(", ", flags))
));
return 1;
}
private static int whatMarkers(CommandSourceStack source, String markerRaw) {
ServerPlayer player = source.getPlayer();
if (player == null) {
fail(source, "This command can only be used by players (markers render as particles around you).");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_MARKERS_RENDER));
return 0;
}
ServerLevel level = source.getLevel();
Engine engine = engineFor(level);
if (engine == null) {
fail(source, "This dimension is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_7));
return 0;
}
String marker = markerRaw.trim();
@@ -782,7 +808,7 @@ public final class IrisModdedCommands {
int chunkX = origin.getX() >> 4;
int chunkZ = origin.getZ() >> 4;
MinecraftServer server = source.getServer();
ok(source, "Scanning for '" + marker + "' markers around you...");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_SCANNING_MARKERS_AROUND_YOU, MessageArgument.untrusted("marker", marker)));
Thread thread = new Thread(() -> {
List<int[]> hits = new ArrayList<>();
MatterMarker matterMarker = new MatterMarker(marker);
@@ -796,7 +822,7 @@ public final class IrisModdedCommands {
}
} catch (Throwable e) {
LOGGER.error("Iris marker scan failed for {}", marker, e);
server.execute(() -> fail(source, "Marker scan failed: " + e.getClass().getSimpleName()));
server.execute(() -> fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_MARKER_SCAN_FAILED, MessageArgument.untrusted("value", e.getClass().getSimpleName()))));
return;
}
server.execute(() -> {
@@ -805,7 +831,7 @@ public final class IrisModdedCommands {
hit[0] + 0.5D, hit[1] + 1.0D, hit[2] + 0.5D,
3, 0.2D, 0.2D, 0.2D, 0.0D);
}
ok(source, "Found " + hits.size() + " nearby marker(s) (" + marker + ")");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_FOUND_NEARBY_MARKER_S, MessageArgument.untrusted("value", hits.size()), MessageArgument.untrusted("marker", marker)));
});
}, "Iris Marker Scan");
thread.setDaemon(true);
@@ -816,18 +842,18 @@ public final class IrisModdedCommands {
private static int gotoBiome(CommandSourceStack source, String key) {
ServerPlayer player = source.getPlayer();
if (player == null) {
fail(source, "This command can only be used by players.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_3));
return 0;
}
ServerLevel level = source.getLevel();
Engine engine = engineFor(level);
if (engine == null) {
fail(source, "This dimension is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_8));
return 0;
}
IrisBiome biome = engine.getData().getBiomeLoader().load(key.trim());
if (biome == null) {
fail(source, "Unknown biome: " + key);
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_UNKNOWN_BIOME_2, MessageArgument.untrusted("key", key)));
return 0;
}
locate(source, level, engine, player, Locator.surfaceBiome(biome.getLoadKey()), "biome " + biome.getLoadKey());
@@ -837,22 +863,22 @@ public final class IrisModdedCommands {
private static int gotoRegion(CommandSourceStack source, String key) {
ServerPlayer player = source.getPlayer();
if (player == null) {
fail(source, "This command can only be used by players.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_4));
return 0;
}
ServerLevel level = source.getLevel();
Engine engine = engineFor(level);
if (engine == null) {
fail(source, "This dimension is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_9));
return 0;
}
IrisRegion region = engine.getData().getRegionLoader().load(key.trim());
if (region == null) {
fail(source, "Unknown region: " + key);
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_UNKNOWN_REGION_2, MessageArgument.untrusted("key", key)));
return 0;
}
if (!engine.getDimension().getRegions().contains(region.getLoadKey())) {
fail(source, region.getLoadKey() + " is not defined in the dimension!");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_IS_NOT_DEFINED_DIMENSION, MessageArgument.untrusted("value", region.getLoadKey())));
return 0;
}
locate(source, level, engine, player, Locator.region(region.getLoadKey()), "region " + region.getLoadKey());
@@ -863,19 +889,17 @@ public final class IrisModdedCommands {
ServerLevel level = source.getLevel();
Engine engine = engineFor(level);
if (engine == null) {
fail(source, "This dimension is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_10));
return 0;
}
String key = keyRaw.trim();
if (!engine.hasObjectPlacement(key)) {
fail(source, key + " is not configured in any region/biome object placements ("
+ engine.getData().getObjectLoader().getPossibleKeys().length + " object keys loaded).");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_IS_NOT_CONFIGURED_ANY_REGION_BIOME_OBJECT_PLACEMENTS_OBJECT_KEYS, MessageArgument.untrusted("key", key), MessageArgument.untrusted("value", engine.getData().getObjectLoader().getPossibleKeys().length)));
return 0;
}
ServerPlayer player = source.getPlayer();
if (player == null) {
fail(source, "This command can only be used by players. (object key '" + key + "' resolved against "
+ engine.getData().getObjectLoader().getPossibleKeys().length + " loaded object keys)");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_OBJECT_KEY, MessageArgument.untrusted("key", key), MessageArgument.untrusted("value", engine.getData().getObjectLoader().getPossibleKeys().length)));
return 0;
}
locate(source, level, engine, player, Locator.object(key), "object " + key);
@@ -886,17 +910,17 @@ public final class IrisModdedCommands {
ServerLevel level = source.getLevel();
Engine engine = engineFor(level);
if (engine == null) {
fail(source, "This dimension is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_11));
return 0;
}
String key = keyRaw.trim();
if (key.isEmpty()) {
fail(source, "Name an Iris or native structure to locate.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_NAME_IRIS_NATIVE_STRUCTURE_LOCATE));
return 0;
}
ServerPlayer player = source.getPlayer();
if (player == null) {
fail(source, "This command can only be used by players.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_5));
return 0;
}
Optional<NativeStructureTarget> resolved = resolveNativeStructure(source, level, engine, key);
@@ -905,7 +929,7 @@ public final class IrisModdedCommands {
locateIrisStructure(source, level, engine, player, key);
return 1;
}
fail(source, "Unknown structure '" + key + "'. Use tab completion to choose an Iris placement or a registered native/datapack structure.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_UNKNOWN_STRUCTURE_USE_TAB_COMPLETION_CHOOSE_IRIS_PLACEMENT_REGISTERED_NATIVE, MessageArgument.untrusted("key", key)));
return 0;
}
NativeStructureTarget target = resolved.get();
@@ -924,7 +948,7 @@ public final class IrisModdedCommands {
fail(source, nativeUnavailableMessage(target.key(), target.availability()));
return 0;
}
ok(source, "Searching for native structure " + target.key() + " within " + NATIVE_STRUCTURE_LOCATE_RADIUS + " chunks...");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_SEARCHING_NATIVE_STRUCTURE_WITHIN_CHUNKS, MessageArgument.untrusted("value", target.key()), MessageArgument.untrusted("NATIVESTRUCTURELOCATERADIUS", NATIVE_STRUCTURE_LOCATE_RADIUS)));
runNativeStructureLocate(source, level, player, target);
return 1;
}
@@ -934,18 +958,17 @@ public final class IrisModdedCommands {
MinecraftServer server = source.getServer();
int blockX = player.blockPosition().getX();
int blockZ = player.blockPosition().getZ();
ok(source, "Searching for Iris-placed structure " + key + "...");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_SEARCHING_IRIS_PLACED_STRUCTURE, MessageArgument.untrusted("key", key)));
Thread thread = new Thread(() -> {
try {
IrisStructureLocator.LocateResult result =
IrisStructureLocator.locate(engine, key, blockX, blockZ, 1024);
if (result.status() == IrisStructureLocator.LocateStatus.SEARCH_LIMIT_REACHED) {
server.execute(() -> fail(source, "Unable to locate Iris-placed structure " + key
+ ": the density search safety limit was reached before the full 1024-chunk radius was searched."));
server.execute(() -> fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_UNABLE_LOCATE_IRIS_PLACED_STRUCTURE_DENSITY_SEARCH_SAFETY_LIMIT_WAS, MessageArgument.untrusted("key", key))));
return;
}
if (!result.found()) {
server.execute(() -> fail(source, "Could not find Iris-placed structure " + key + " within 1024 chunks."));
server.execute(() -> fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_COULD_NOT_FIND_IRIS_PLACED_STRUCTURE_WITHIN_1024_CHUNKS, MessageArgument.untrusted("key", key))));
return;
}
int targetX = result.originX();
@@ -955,7 +978,7 @@ public final class IrisModdedCommands {
"Iris-placed structure " + key));
} catch (Throwable e) {
LOGGER.error("Iris structure locate failed for {}", key, e);
server.execute(() -> fail(source, "Search failed: " + e.getClass().getSimpleName()));
server.execute(() -> fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_SEARCH_FAILED, MessageArgument.untrusted("value", e.getClass().getSimpleName()))));
}
}, "Iris Structure Locator");
thread.setDaemon(true);
@@ -984,8 +1007,7 @@ public final class IrisModdedCommands {
NATIVE_STRUCTURE_LOCATE_RADIUS,
false);
if (found == null) {
fail(source, "Could not find native structure " + target.key() + " within "
+ NATIVE_STRUCTURE_LOCATE_RADIUS + " chunks.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_COULD_NOT_FIND_NATIVE_STRUCTURE_WITHIN_CHUNKS, MessageArgument.untrusted("value", target.key()), MessageArgument.untrusted("NATIVESTRUCTURELOCATERADIUS", NATIVE_STRUCTURE_LOCATE_RADIUS)));
return;
}
BlockPos position = found.getFirst();
@@ -998,18 +1020,18 @@ public final class IrisModdedCommands {
"native structure " + target.key());
} catch (Throwable e) {
LOGGER.error("Native structure locate failed for {}", target.key(), e);
fail(source, "Search for native structure " + target.key() + " failed: " + e.getClass().getSimpleName());
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_SEARCH_NATIVE_STRUCTURE_FAILED, MessageArgument.untrusted("value", target.key()), MessageArgument.untrusted("value2", e.getClass().getSimpleName())));
}
}
private static void teleportToStructure(CommandSourceStack source, ServerLevel level, ServerPlayer player,
int targetX, int targetY, int targetZ, String label) {
if (player.hasDisconnected() || player.isRemoved()) {
fail(source, "The player disconnected before the structure search completed.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_PLAYER_DISCONNECTED_BEFORE_STRUCTURE_SEARCH_COMPLETED));
return;
}
if (player.level() != level) {
fail(source, "You changed dimensions before the structure search completed; run the command again.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_YOU_CHANGED_DIMENSIONS_BEFORE_STRUCTURE_SEARCH_COMPLETED_RUN_COMMAND_AGAIN));
return;
}
level.getChunk(targetX >> 4, targetZ >> 4);
@@ -1017,17 +1039,17 @@ public final class IrisModdedCommands {
boolean teleported = player.teleportTo(level, targetX + 0.5D, clampedY, targetZ + 0.5D,
Set.<Relative>of(), player.getYRot(), player.getXRot(), false);
if (!teleported) {
fail(source, "Found " + label + " at " + targetX + " " + clampedY + " " + targetZ + ", but teleportation failed.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_FOUND_AT_BUT_TELEPORTATION_FAILED, MessageArgument.untrusted("label", label), MessageArgument.untrusted("targetX", targetX), MessageArgument.untrusted("clampedY", clampedY), MessageArgument.untrusted("targetZ", targetZ)));
return;
}
ok(source, "Teleported to " + label + " at " + targetX + " " + clampedY + " " + targetZ);
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORTED_AT, MessageArgument.untrusted("label", label), MessageArgument.untrusted("targetX", targetX), MessageArgument.untrusted("clampedY", clampedY), MessageArgument.untrusted("targetZ", targetZ)));
}
static int verifyStructures(CommandSourceStack source, String keyRaw) {
ServerLevel level = source.getLevel();
Engine engine = engineFor(level);
if (engine == null) {
fail(source, "This dimension is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_12));
return 0;
}
String key = keyRaw == null ? "" : keyRaw.trim();
@@ -1056,11 +1078,7 @@ public final class IrisModdedCommands {
}
}
int irisPlaced = IrisStructureLocator.placedKeys(engine).size();
ok(source, "Structure reachability: " + available + " native generation-eligible, " + irisPlaced
+ " Iris-placed, " + disabled + " native disabled, " + suppressed
+ " native replaced by Iris placements, " + unreachableBiomes
+ " native excluded by this pack's biomes, and " + unsupported
+ " registered native structures unsupported in this dimension. Use /iris structure verify <key> for one structure.");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_STRUCTURE_REACHABILITY_NATIVE_GENERATION_ELIGIBLE_IRIS_PLACED_NATIVE_DISABLED_NATIVE, MessageArgument.untrusted("available", available), MessageArgument.untrusted("irisPlaced", irisPlaced), MessageArgument.untrusted("disabled", disabled), MessageArgument.untrusted("suppressed", suppressed), MessageArgument.untrusted("unreachableBiomes", unreachableBiomes), MessageArgument.untrusted("unsupported", unsupported)));
return 1;
}
@@ -1068,24 +1086,22 @@ public final class IrisModdedCommands {
Optional<NativeStructureTarget> target = resolveNativeStructure(source, level, engine, key);
if (target.isEmpty()) {
if (IrisStructureLocator.isPlaced(engine, key)) {
ok(source, "Structure " + key + " is Iris-placed and locatable with /iris goto structure " + key + ".");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_STRUCTURE_IS_IRIS_PLACED_LOCATABLE_WITH_IRIS_GOTO_STRUCTURE, MessageArgument.untrusted("key", key), MessageArgument.untrusted("key2", key)));
return 1;
}
fail(source, "Unknown structure '" + key + "'. It is neither Iris-placed nor registered by vanilla or a datapack.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_UNKNOWN_STRUCTURE_IT_IS_NEITHER_IRIS_PLACED_NOR_REGISTERED_BY, MessageArgument.untrusted("key", key)));
return 0;
}
NativeStructureTarget resolved = target.get();
if (resolved.availability() == NativeStructureAvailability.IRIS_SUPPRESSED) {
ok(source, "Structure " + resolved.key()
+ " is explicitly replaced by an Iris placement and locatable with /iris goto structure "
+ resolved.key() + ".");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_STRUCTURE_IS_EXPLICITLY_REPLACED_BY_IRIS_PLACEMENT_LOCATABLE_WITH_IRIS, MessageArgument.untrusted("value", resolved.key()), MessageArgument.untrusted("value2", resolved.key())));
return 1;
}
if (resolved.availability() != NativeStructureAvailability.AVAILABLE) {
fail(source, nativeUnavailableMessage(resolved.key(), resolved.availability()));
return 0;
}
ok(source, "Native structure " + resolved.key() + " is enabled, supported by this dimension's generator state, and locatable with /iris goto structure " + resolved.key() + ".");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_NATIVE_STRUCTURE_IS_ENABLED_SUPPORTED_BY_THIS_DIMENSION_S_GENERATOR, MessageArgument.untrusted("value", resolved.key()), MessageArgument.untrusted("value2", resolved.key())));
return 1;
}
@@ -1162,13 +1178,13 @@ public final class IrisModdedCommands {
ServerLevel level = source.getLevel();
Engine engine = engineFor(level);
if (engine == null) {
fail(source, "This dimension is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_13));
return 0;
}
String type = typeRaw.trim();
ServerPlayer player = source.getPlayer();
if (player == null) {
fail(source, "This command can only be used by players. (POI type '" + type + "' accepted)");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_POI_TYPE, MessageArgument.untrusted("type", type)));
return 0;
}
locate(source, level, engine, player, Locator.poi(type), "POI " + type);
@@ -1179,13 +1195,13 @@ public final class IrisModdedCommands {
MinecraftServer server = source.getServer();
int chunkX = player.blockPosition().getX() >> 4;
int chunkZ = player.blockPosition().getZ() >> 4;
ok(source, "Searching for " + label + "...");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_SEARCHING, MessageArgument.untrusted("label", label)));
CompletableFuture<Position2> search;
try {
search = locator.find(engine, new Position2(chunkX, chunkZ), LOCATE_TIMEOUT_MS, (Integer checks) -> {
});
} catch (WrongEngineBroException e) {
fail(source, "The engine for this world has been closed; rejoin the dimension and try again.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_ENGINE_THIS_WORLD_HAS_BEEN_CLOSED_REJOIN_DIMENSION_TRY_AGAIN));
return;
}
UUID playerId = player.getUUID();
@@ -1212,7 +1228,7 @@ public final class IrisModdedCommands {
LOGGER.error("Iris locate failed for {}", label, failure);
server.execute(() -> {
if (ACTIVE_LOCATE_REQUESTS.remove(playerId, search)) {
fail(source, "Search failed: " + failure);
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_SEARCH_FAILED_2, MessageArgument.untrusted("failure", failure)));
}
});
return;
@@ -1220,7 +1236,7 @@ public final class IrisModdedCommands {
if (at == null) {
server.execute(() -> {
if (ACTIVE_LOCATE_REQUESTS.remove(playerId, search)) {
fail(source, "Could not find " + label + " within the search timeout.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_COULD_NOT_FIND_WITHIN_SEARCH_TIMEOUT, MessageArgument.untrusted("label", label)));
}
});
return;
@@ -1240,9 +1256,9 @@ public final class IrisModdedCommands {
IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) {
int blockY = engine.getMinHeight() + engine.getHeight(blockX, blockZ, false) + 2;
player.teleportTo(level, blockX + 0.5D, blockY, blockZ + 0.5D, Set.<Relative>of(), player.getYRot(), player.getXRot(), false);
ok(source, "Teleported to " + label + " at " + blockX + " " + blockY + " " + blockZ);
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORTED_AT_2, MessageArgument.untrusted("label", label), MessageArgument.untrusted("blockX", blockX), MessageArgument.untrusted("blockY", blockY), MessageArgument.untrusted("blockZ", blockZ)));
} catch (GenerationSessionException e) {
fail(source, "The engine changed while locating " + label + "; try again.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_ENGINE_CHANGED_WHILE_LOCATING_TRY_AGAIN, MessageArgument.untrusted("label", label)));
}
}
@@ -1259,11 +1275,11 @@ public final class IrisModdedCommands {
ServerLevel level = source.getLevel();
Engine engine = engineFor(level);
if (engine == null) {
fail(source, "This dimension is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_14));
return 0;
}
ok(source, "World seed: " + level.getSeed());
ok(source, "Engine seed: " + engine.getSeedManager().getSeed() + " (mixed: " + engine.getSeedManager().getFullMixedSeed() + ")");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_WORLD_SEED, MessageArgument.untrusted("value", level.getSeed())));
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_ENGINE_SEED_MIXED, MessageArgument.untrusted("value", engine.getSeedManager().getSeed()), MessageArgument.untrusted("value2", engine.getSeedManager().getFullMixedSeed())));
return 1;
}
@@ -1271,7 +1287,7 @@ public final class IrisModdedCommands {
ServerLevel level = source.getLevel();
Engine engine = engineFor(level);
if (engine == null) {
fail(source, "This dimension is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_15));
return 0;
}
ModdedGoldenHash.start(source, level, engine, radius, threads, mode);
@@ -1282,14 +1298,14 @@ public final class IrisModdedCommands {
MinecraftServer server = source.getServer();
boolean defaultOverworld = PackDownloader.isDefaultOverworld(pack);
String downloadSource = defaultOverworld ? "beta release" : "branch " + branch;
ok(source, "Downloading IrisDimensions/" + pack + " (" + downloadSource + ")...");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_DOWNLOADING_IRISDIMENSIONS, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("downloadSource", downloadSource)));
Thread thread = new Thread(() -> {
boolean installed = ModdedPackInstaller.install(ModdedEngineBootstrap.loader().configDir(), pack, branch,
(String message) -> server.execute(() -> ok(source, message)));
if (installed) {
server.execute(() -> ok(source, "Pack '" + pack + "' installed. Its exact dimension types and custom biomes join the forced Iris datapack on the next server restart; restart before creating worlds from this pack."));
server.execute(() -> ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_PACK_INSTALLED_ITS_EXACT_DIMENSION_TYPES_CUSTOM_BIOMES_JOIN_FORCED, MessageArgument.untrusted("pack", pack))));
} else {
server.execute(() -> fail(source, "Pack download failed for " + pack + " (" + downloadSource + "; see console)."));
server.execute(() -> fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_PACK_DOWNLOAD_FAILED_SEE_CONSOLE, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("downloadSource", downloadSource))));
}
}, "Iris Pack Download");
thread.setDaemon(true);
@@ -1301,17 +1317,17 @@ public final class IrisModdedCommands {
ServerLevel level = source.getLevel();
Engine engine = engineFor(level);
if (engine == null) {
fail(source, "This dimension is not generated by Iris.");
fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_16));
return 0;
}
ok(source, "Generated: " + engine.getGenerated() + " chunk(s), " + String.format("%.1f", engine.getGeneratedPerSecond()) + "/s");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_GENERATED_CHUNK_S_S, MessageArgument.untrusted("value", engine.getGenerated()), MessageArgument.untrusted("value2", String.format("%.1f", engine.getGeneratedPerSecond()))));
KMap<String, Double> pulled = engine.getMetrics().pull();
Map<String, Double> sorted = new TreeMap<>(pulled);
for (Map.Entry<String, Double> entry : sorted.entrySet()) {
if (entry.getValue() == null || entry.getValue() <= 0D) {
continue;
}
ok(source, " " + entry.getKey() + ": " + String.format("%.2f", entry.getValue()) + "ms");
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_MS, MessageArgument.untrusted("value", entry.getKey()), MessageArgument.untrusted("value2", String.format("%.2f", entry.getValue()))));
}
return 1;
}
@@ -24,6 +24,12 @@ import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.HoverEvent;
import net.minecraft.network.chat.MutableComponent;
import art.arcane.iris.core.localization.IrisMessages;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.ModdedHelpMessages;
import art.arcane.volmlib.util.director.help.DirectorHelpMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
import art.arcane.volmlib.util.localization.TextKey;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@@ -50,124 +56,124 @@ final class ModdedCommandHelp {
static {
SECTIONS.put("", List.of(
Entry.command("version", "", "Print version information"),
Entry.command("info", "[dimension]", "List loaded Iris dimensions and pack details"),
Entry.command("what", "[block|hand|markers]", "Inspect the Iris biome, region, cave biome, surface and chunk at your position, the block you look at, your held item, or nearby markers"),
Entry.group("find", "Find and teleport to Iris biomes, regions, objects, Iris structures, native structures and points of interest", "goto"),
Entry.command("tp", "<dimension> [player]", "Teleport yourself or a named player into a loaded Iris dimension"),
Entry.command("evacuate", "[dimension]", "Teleport every player out of an Iris dimension to the primary world spawn"),
Entry.command("seed", "", "Print world and engine seed information"),
Entry.command("debug", "", "Toggle Iris debug logging and save settings.json"),
Entry.command("reload", "", "Reload settings.json (also hotloaded automatically every 3s)"),
Entry.command("download", "<pack> [branch]", "Download a pack project", "dl"),
Entry.command("metrics", "", "Print generation metrics for your current Iris dimension", "measure"),
Entry.command("regen", "[radius]", "Delete and regenerate nearby chunks in place", "rg"),
Entry.group("pregen", "Pregenerate an Iris dimension", "pregenerate"),
Entry.command("wand", "", "Get an Iris object wand"),
Entry.group("object", "Object wand, save, paste, analyze and undo tools", "o"),
Entry.group("edit", "Open pack biome, region and dimension json files in your desktop editor"),
Entry.command("create", "<name> <pack|pack:dimensionKey> [seed]", "Create and inject a persistent Iris dimension; quote pack:dimensionKey to pick a specific pack dimension"),
Entry.group("studio", "Pack project creation, packaging and reports", "std", "s"),
Entry.group("pack", "Pack validation and maintenance", "pk"),
Entry.group("world", "Runtime Iris dimension creation, removal and status", "w"),
Entry.group("datapack", "World datapack install and status helpers", "datapacks", "dp"),
Entry.group("structure", "Iris structure index, info and placement tools", "struct", "str"),
Entry.command("goldenhash", "[radius] [threads] [capture|verify]", "Generate deterministic block hashes for parity testing", "gold"),
Entry.group("developer", "Developer diagnostics: Sentry test, network interfaces, region-file scan", "dev")
Entry.command("version", "", ModdedHelpMessages.COMMAND_VERSION_PRINT_VERSION_INFORMATION),
Entry.command("info", "[dimension]", ModdedHelpMessages.COMMAND_INFO_LIST_LOADED_IRIS_DIMENSIONS_AND_PACK_DETAILS),
Entry.command("what", "[block|hand|markers]", ModdedHelpMessages.COMMAND_WHAT_INSPECT_THE_IRIS_BIOME_REGION_CAVE_BIOME_SURFACE_AND_CHUNK_AT_YOUR),
Entry.group("find", ModdedHelpMessages.GROUP_FIND_FIND_AND_TELEPORT_TO_IRIS_BIOMES_REGIONS_OBJECTS_IRIS_STRUCTURES_NATIVE_STRUCTURES, "goto"),
Entry.command("tp", "<dimension> [player]", ModdedHelpMessages.COMMAND_TP_TELEPORT_YOURSELF_OR_A_NAMED_PLAYER_INTO_A_LOADED_IRIS_DIMENSION),
Entry.command("evacuate", "[dimension]", ModdedHelpMessages.COMMAND_EVACUATE_TELEPORT_EVERY_PLAYER_OUT_OF_AN_IRIS_DIMENSION_TO_THE_PRIMARY_WORLD),
Entry.command("seed", "", ModdedHelpMessages.COMMAND_SEED_PRINT_WORLD_AND_ENGINE_SEED_INFORMATION),
Entry.command("debug", "", ModdedHelpMessages.COMMAND_DEBUG_TOGGLE_IRIS_DEBUG_LOGGING_AND_SAVE_SETTINGS_JSON),
Entry.command("reload", "", ModdedHelpMessages.COMMAND_RELOAD_RELOAD_SETTINGS_JSON_ALSO_HOTLOADED_AUTOMATICALLY_EVERY_3S),
Entry.command("download", "<pack> [branch]", ModdedHelpMessages.COMMAND_DOWNLOAD_DOWNLOAD_A_PACK_PROJECT, "dl"),
Entry.command("metrics", "", ModdedHelpMessages.COMMAND_METRICS_PRINT_GENERATION_METRICS_FOR_YOUR_CURRENT_IRIS_DIMENSION, "measure"),
Entry.command("regen", "[radius]", ModdedHelpMessages.COMMAND_REGEN_DELETE_AND_REGENERATE_NEARBY_CHUNKS_IN_PLACE, "rg"),
Entry.group("pregen", ModdedHelpMessages.GROUP_PREGEN_PREGENERATE_AN_IRIS_DIMENSION, "pregenerate"),
Entry.command("wand", "", ModdedHelpMessages.COMMAND_WAND_GET_AN_IRIS_OBJECT_WAND),
Entry.group("object", ModdedHelpMessages.GROUP_OBJECT_OBJECT_WAND_SAVE_PASTE_ANALYZE_AND_UNDO_TOOLS, "o"),
Entry.group("edit", ModdedHelpMessages.GROUP_EDIT_OPEN_PACK_BIOME_REGION_AND_DIMENSION_JSON_FILES_IN_YOUR_DESKTOP_EDITOR),
Entry.command("create", "<name> <pack|pack:dimensionKey> [seed]", ModdedHelpMessages.COMMAND_CREATE_CREATE_AND_INJECT_A_PERSISTENT_IRIS_DIMENSION_QUOTE_PACK_DIMENSIONKEY_TO_PICK),
Entry.group("studio", ModdedHelpMessages.GROUP_STUDIO_PACK_PROJECT_CREATION_PACKAGING_AND_REPORTS, "std", "s"),
Entry.group("pack", ModdedHelpMessages.GROUP_PACK_PACK_VALIDATION_AND_MAINTENANCE, "pk"),
Entry.group("world", ModdedHelpMessages.GROUP_WORLD_RUNTIME_IRIS_DIMENSION_CREATION_REMOVAL_AND_STATUS, "w"),
Entry.group("datapack", ModdedHelpMessages.GROUP_DATAPACK_WORLD_DATAPACK_INSTALL_AND_STATUS_HELPERS, "datapacks", "dp"),
Entry.group("structure", ModdedHelpMessages.GROUP_STRUCTURE_IRIS_STRUCTURE_INDEX_INFO_AND_PLACEMENT_TOOLS, "struct", "str"),
Entry.command("goldenhash", "[radius] [threads] [capture|verify]", ModdedHelpMessages.COMMAND_GOLDENHASH_GENERATE_DETERMINISTIC_BLOCK_HASHES_FOR_PARITY_TESTING, "gold"),
Entry.group("developer", ModdedHelpMessages.GROUP_DEVELOPER_DEVELOPER_DIAGNOSTICS_SENTRY_TEST_NETWORK_INTERFACES_REGION_FILE_SCAN, "dev")
));
SECTIONS.put("find", List.of(
Entry.command("biome", "<key>", "Find an Iris biome"),
Entry.command("region", "<key>", "Find an Iris region"),
Entry.command("object", "<key>", "Find an object placement"),
Entry.command("structure", "<key>", "Find an Iris-placed or native/datapack structure"),
Entry.command("poi", "<type>", "Find a supported point of interest")
Entry.command("biome", "<key>", ModdedHelpMessages.COMMAND_BIOME_FIND_AN_IRIS_BIOME),
Entry.command("region", "<key>", ModdedHelpMessages.COMMAND_REGION_FIND_AN_IRIS_REGION),
Entry.command("object", "<key>", ModdedHelpMessages.COMMAND_OBJECT_FIND_AN_OBJECT_PLACEMENT),
Entry.command("structure", "<key>", ModdedHelpMessages.COMMAND_STRUCTURE_FIND_AN_IRIS_PLACED_OR_NATIVE_DATAPACK_STRUCTURE),
Entry.command("poi", "<type>", ModdedHelpMessages.COMMAND_POI_FIND_A_SUPPORTED_POINT_OF_INTEREST)
));
SECTIONS.put("goto", SECTIONS.get("find"));
SECTIONS.put("edit", List.of(
Entry.command("biome", "[key]", "Open a biome json in your desktop editor; no key opens the biome at your position"),
Entry.command("region", "[key]", "Open a region json in your desktop editor; no key opens the region at your position"),
Entry.command("dimension", "", "Open the current pack's dimension json in your desktop editor")
Entry.command("biome", "[key]", ModdedHelpMessages.COMMAND_BIOME_OPEN_A_BIOME_JSON_IN_YOUR_DESKTOP_EDITOR_NO_KEY_OPENS_THE),
Entry.command("region", "[key]", ModdedHelpMessages.COMMAND_REGION_OPEN_A_REGION_JSON_IN_YOUR_DESKTOP_EDITOR_NO_KEY_OPENS_THE),
Entry.command("dimension", "", ModdedHelpMessages.COMMAND_DIMENSION_OPEN_THE_CURRENT_PACK_S_DIMENSION_JSON_IN_YOUR_DESKTOP_EDITOR)
));
SECTIONS.put("pregen", List.of(
Entry.command("start", "<radius> [dimension] [at] [x] [z] [gui] [sync] [nocache]", "Start pregeneration; radius in blocks, resumable checkpoint cache on by default, center via 'at <x> <z>', flags compose in any order"),
Entry.command("stop", "", "Stop the active pregeneration task", "x"),
Entry.command("pause", "", "Pause or resume pregeneration", "resume"),
Entry.command("status", "", "Show pregeneration status")
Entry.command("start", "<radius> [dimension] [at] [x] [z] [gui] [sync] [nocache]", ModdedHelpMessages.COMMAND_START_START_PREGENERATION_RADIUS_IN_BLOCKS_RESUMABLE_CHECKPOINT_CACHE_ON_BY_DEFAULT_CENTER),
Entry.command("stop", "", ModdedHelpMessages.COMMAND_STOP_STOP_THE_ACTIVE_PREGENERATION_TASK, "x"),
Entry.command("pause", "", ModdedHelpMessages.COMMAND_PAUSE_PAUSE_OR_RESUME_PREGENERATION, "resume"),
Entry.command("status", "", ModdedHelpMessages.COMMAND_STATUS_SHOW_PREGENERATION_STATUS)
));
SECTIONS.put("pregenerate", SECTIONS.get("pregen"));
SECTIONS.put("object", List.of(
Entry.command("wand", "", "Get an Iris object wand"),
Entry.command("dust", "", "Get dust that reveals object placements", "d"),
Entry.command("save", "[overwrite] <name>", "Save the selected wand volume as an object"),
Entry.command("paste", "[at] [x] [y] [z] [rotate] [degrees] <key>", "Paste an object at your position or a given position, optionally rotated"),
Entry.command("expand", "[amount]", "Expand the wand selection in your looking direction"),
Entry.command("contract", "[amount]", "Contract the wand selection in your looking direction", "-"),
Entry.command("shift", "[amount]", "Shift the wand selection in your looking direction"),
Entry.command("position1", "[look]", "Set selection point 1", "p1"),
Entry.command("position2", "[look]", "Set selection point 2", "p2"),
Entry.command("x+y", "", "Autoselect up and out", "xpy"),
Entry.command("x&y", "", "Autoselect up, down and out", "xay"),
Entry.command("analyze", "<key>", "Show object composition"),
Entry.command("shrink", "<key>", "Shrink an object to its minimum size"),
Entry.command("plausibilize", "<key|prefix/> [dryrun=true] [reach=N]", "Grow branches so tree leaves survive vanilla decay"),
Entry.command("undo", "[amount]", "Undo pasted objects", "u")
Entry.command("wand", "", ModdedHelpMessages.COMMAND_WAND_GET_AN_IRIS_OBJECT_WAND_2),
Entry.command("dust", "", ModdedHelpMessages.COMMAND_DUST_GET_DUST_THAT_REVEALS_OBJECT_PLACEMENTS, "d"),
Entry.command("save", "[overwrite] <name>", ModdedHelpMessages.COMMAND_SAVE_SAVE_THE_SELECTED_WAND_VOLUME_AS_AN_OBJECT),
Entry.command("paste", "[at] [x] [y] [z] [rotate] [degrees] <key>", ModdedHelpMessages.COMMAND_PASTE_PASTE_AN_OBJECT_AT_YOUR_POSITION_OR_A_GIVEN_POSITION_OPTIONALLY_ROTATED),
Entry.command("expand", "[amount]", ModdedHelpMessages.COMMAND_EXPAND_EXPAND_THE_WAND_SELECTION_IN_YOUR_LOOKING_DIRECTION),
Entry.command("contract", "[amount]", ModdedHelpMessages.COMMAND_CONTRACT_CONTRACT_THE_WAND_SELECTION_IN_YOUR_LOOKING_DIRECTION, "-"),
Entry.command("shift", "[amount]", ModdedHelpMessages.COMMAND_SHIFT_SHIFT_THE_WAND_SELECTION_IN_YOUR_LOOKING_DIRECTION),
Entry.command("position1", "[look]", ModdedHelpMessages.COMMAND_POSITION1_SET_SELECTION_POINT_1, "p1"),
Entry.command("position2", "[look]", ModdedHelpMessages.COMMAND_POSITION2_SET_SELECTION_POINT_2, "p2"),
Entry.command("x+y", "", ModdedHelpMessages.COMMAND_X_Y_AUTOSELECT_UP_AND_OUT, "xpy"),
Entry.command("x&y", "", ModdedHelpMessages.COMMAND_X_Y_AUTOSELECT_UP_DOWN_AND_OUT, "xay"),
Entry.command("analyze", "<key>", ModdedHelpMessages.COMMAND_ANALYZE_SHOW_OBJECT_COMPOSITION),
Entry.command("shrink", "<key>", ModdedHelpMessages.COMMAND_SHRINK_SHRINK_AN_OBJECT_TO_ITS_MINIMUM_SIZE),
Entry.command("plausibilize", "<key|prefix/> [dryrun=true] [reach=N]", ModdedHelpMessages.COMMAND_PLAUSIBILIZE_GROW_BRANCHES_SO_TREE_LEAVES_SURVIVE_VANILLA_DECAY),
Entry.command("undo", "[amount]", ModdedHelpMessages.COMMAND_UNDO_UNDO_PASTED_OBJECTS, "u")
));
SECTIONS.put("o", SECTIONS.get("object"));
SECTIONS.put("studio", List.of(
Entry.command("create", "<name> [template]", "Create a new pack project", "+"),
Entry.command("package", "[pack]", "Package a dimension into a compressed format"),
Entry.command("version", "[pack]", "Print a pack version"),
Entry.command("regions", "[radius]", "Calculate nearby region distribution"),
Entry.command("open", "<pack> [seed]", "Open a temporary studio dimension for a pack", "o"),
Entry.command("close", "", "Close the open studio dimension and discard its world", "x"),
Entry.command("tpstudio", "", "Teleport into the open studio dimension", "stp"),
Entry.command("status", "", "Show the open studio dimension and its pack"),
Entry.command("noise", "[generator] [seed]", "Open the Noise Explorer GUI on the server display", "nmap"),
Entry.command("map", "", "Open the Vision map GUI on the server display", "render"),
Entry.command("vscode", "[pack]", "Regenerate the .code-workspace for a pack and open it in your desktop editor", "vsc"),
Entry.command("update", "[pack]", "Regenerate the .code-workspace for a pack"),
Entry.command("importvanilla", "", "Explain vanilla import workflow", "importv", "iv")
Entry.command("create", "<name> [template]", ModdedHelpMessages.COMMAND_CREATE_CREATE_A_NEW_PACK_PROJECT, "+"),
Entry.command("package", "[pack]", ModdedHelpMessages.COMMAND_PACKAGE_PACKAGE_A_DIMENSION_INTO_A_COMPRESSED_FORMAT),
Entry.command("version", "[pack]", ModdedHelpMessages.COMMAND_VERSION_PRINT_A_PACK_VERSION),
Entry.command("regions", "[radius]", ModdedHelpMessages.COMMAND_REGIONS_CALCULATE_NEARBY_REGION_DISTRIBUTION),
Entry.command("open", "<pack> [seed]", ModdedHelpMessages.COMMAND_OPEN_OPEN_A_TEMPORARY_STUDIO_DIMENSION_FOR_A_PACK, "o"),
Entry.command("close", "", ModdedHelpMessages.COMMAND_CLOSE_CLOSE_THE_OPEN_STUDIO_DIMENSION_AND_DISCARD_ITS_WORLD, "x"),
Entry.command("tpstudio", "", ModdedHelpMessages.COMMAND_TPSTUDIO_TELEPORT_INTO_THE_OPEN_STUDIO_DIMENSION, "stp"),
Entry.command("status", "", ModdedHelpMessages.COMMAND_STATUS_SHOW_THE_OPEN_STUDIO_DIMENSION_AND_ITS_PACK),
Entry.command("noise", "[generator] [seed]", ModdedHelpMessages.COMMAND_NOISE_OPEN_THE_NOISE_EXPLORER_GUI_ON_THE_SERVER_DISPLAY, "nmap"),
Entry.command("map", "", ModdedHelpMessages.COMMAND_MAP_OPEN_THE_VISION_MAP_GUI_ON_THE_SERVER_DISPLAY, "render"),
Entry.command("vscode", "[pack]", ModdedHelpMessages.COMMAND_VSCODE_REGENERATE_THE_CODE_WORKSPACE_FOR_A_PACK_AND_OPEN_IT_IN_YOUR, "vsc"),
Entry.command("update", "[pack]", ModdedHelpMessages.COMMAND_UPDATE_REGENERATE_THE_CODE_WORKSPACE_FOR_A_PACK),
Entry.command("importvanilla", "", ModdedHelpMessages.COMMAND_IMPORTVANILLA_EXPLAIN_VANILLA_IMPORT_WORKFLOW, "importv", "iv")
));
SECTIONS.put("std", SECTIONS.get("studio"));
SECTIONS.put("s", SECTIONS.get("studio"));
SECTIONS.put("pack", List.of(
Entry.command("validate", "[pack]", "Validate a pack or every pack", "v"),
Entry.command("cleanup", "<pack> [apply]", "Preview or quarantine unused-resource candidates", "c"),
Entry.command("restore", "<pack> [apply]", "Preview or restore the latest quarantine", "r"),
Entry.command("status", "[pack]", "Show cached validation status", "s")
Entry.command("validate", "[pack]", ModdedHelpMessages.COMMAND_VALIDATE_VALIDATE_A_PACK_OR_EVERY_PACK, "v"),
Entry.command("cleanup", "<pack> [apply]", ModdedHelpMessages.COMMAND_CLEANUP_PREVIEW_OR_QUARANTINE_UNUSED_RESOURCE_CANDIDATES, "c"),
Entry.command("restore", "<pack> [apply]", ModdedHelpMessages.COMMAND_RESTORE_PREVIEW_OR_RESTORE_THE_LATEST_QUARANTINE, "r"),
Entry.command("status", "[pack]", ModdedHelpMessages.COMMAND_STATUS_SHOW_CACHED_VALIDATION_STATUS, "s")
));
SECTIONS.put("pk", SECTIONS.get("pack"));
SECTIONS.put("world", List.of(
Entry.command("enable", "<dimension> <pack|pack:dimensionKey> [seed|random]", "Create and inject a persistent Iris dimension at runtime; downloads the pack if missing, quote pack:dimensionKey to pick a specific pack dimension", "create"),
Entry.command("replace-overworld", "<pack|pack:dimensionKey> [seed|random]", "Inject an Iris primary world and route players there instead of the vanilla overworld"),
Entry.command("disable", "<dimension>", "Evacuate and unload an Iris dimension; world data on disk is kept for re-enabling"),
Entry.command("delete", "<dimension>", "Disable an Iris dimension and wipe its chunk and mantle data from disk", "remove", "rm"),
Entry.command("list", "", "List loaded Iris dimensions", "ls"),
Entry.command("status", "", "Show loaded Iris dimensions and the configured primary world")
Entry.command("enable", "<dimension> <pack|pack:dimensionKey> [seed|random]", ModdedHelpMessages.COMMAND_ENABLE_CREATE_AND_INJECT_A_PERSISTENT_IRIS_DIMENSION_AT_RUNTIME_DOWNLOADS_THE_PACK, "create"),
Entry.command("replace-overworld", "<pack|pack:dimensionKey> [seed|random]", ModdedHelpMessages.COMMAND_REPLACE_OVERWORLD_INJECT_AN_IRIS_PRIMARY_WORLD_AND_ROUTE_PLAYERS_THERE_INSTEAD_OF_THE),
Entry.command("disable", "<dimension>", ModdedHelpMessages.COMMAND_DISABLE_EVACUATE_AND_UNLOAD_AN_IRIS_DIMENSION_WORLD_DATA_ON_DISK_IS_KEPT),
Entry.command("delete", "<dimension>", ModdedHelpMessages.COMMAND_DELETE_DISABLE_AN_IRIS_DIMENSION_AND_WIPE_ITS_CHUNK_AND_MANTLE_DATA_FROM, "remove", "rm"),
Entry.command("list", "", ModdedHelpMessages.COMMAND_LIST_LIST_LOADED_IRIS_DIMENSIONS, "ls"),
Entry.command("status", "", ModdedHelpMessages.COMMAND_STATUS_SHOW_LOADED_IRIS_DIMENSIONS_AND_THE_CONFIGURED_PRIMARY_WORLD)
));
SECTIONS.put("w", SECTIONS.get("world"));
SECTIONS.put("datapack", List.of(
Entry.command("status", "", "Check loaded Iris dimension type overrides"),
Entry.command("install", "", "Install dimension type overrides for loaded Iris dimensions"),
Entry.command("list", "", "List configured and installed datapacks", "ls"),
Entry.command("ingest", "", "Explain Bukkit datapack ingest workflow", "pull"),
Entry.command("remove", "<id>", "Explain datapack removal workflow", "rm")
Entry.command("status", "", ModdedHelpMessages.COMMAND_STATUS_CHECK_LOADED_IRIS_DIMENSION_TYPE_OVERRIDES),
Entry.command("install", "", ModdedHelpMessages.COMMAND_INSTALL_INSTALL_DIMENSION_TYPE_OVERRIDES_FOR_LOADED_IRIS_DIMENSIONS),
Entry.command("list", "", ModdedHelpMessages.COMMAND_LIST_LIST_CONFIGURED_AND_INSTALLED_DATAPACKS, "ls"),
Entry.command("ingest", "", ModdedHelpMessages.COMMAND_INGEST_EXPLAIN_BUKKIT_DATAPACK_INGEST_WORKFLOW, "pull"),
Entry.command("remove", "<id>", ModdedHelpMessages.COMMAND_REMOVE_EXPLAIN_DATAPACK_REMOVAL_WORKFLOW, "rm")
));
SECTIONS.put("datapacks", SECTIONS.get("datapack"));
SECTIONS.put("dp", SECTIONS.get("datapack"));
SECTIONS.put("structure", List.of(
Entry.command("list", "", "Regenerate structure-index.json", "ls"),
Entry.command("info", "<key>", "Resolve an Iris structure graph and report bounds"),
Entry.command("place", "<key>", "Assemble and place an Iris structure at your location", "p"),
Entry.command("import", "", "Explain Bukkit structure import workflow", "import-all", "reimport", "imp", "all"),
Entry.command("capture", "", "Explain Bukkit structure capture workflow", "cap"),
Entry.command("verify", "[key]", "Report native and Iris structure reachability in the current dimension", "locateall")
Entry.command("list", "", ModdedHelpMessages.COMMAND_LIST_REGENERATE_STRUCTURE_INDEX_JSON, "ls"),
Entry.command("info", "<key>", ModdedHelpMessages.COMMAND_INFO_RESOLVE_AN_IRIS_STRUCTURE_GRAPH_AND_REPORT_BOUNDS),
Entry.command("place", "<key>", ModdedHelpMessages.COMMAND_PLACE_ASSEMBLE_AND_PLACE_AN_IRIS_STRUCTURE_AT_YOUR_LOCATION, "p"),
Entry.command("import", "", ModdedHelpMessages.COMMAND_IMPORT_EXPLAIN_BUKKIT_STRUCTURE_IMPORT_WORKFLOW, "import-all", "reimport", "imp", "all"),
Entry.command("capture", "", ModdedHelpMessages.COMMAND_CAPTURE_EXPLAIN_BUKKIT_STRUCTURE_CAPTURE_WORKFLOW, "cap"),
Entry.command("verify", "[key]", ModdedHelpMessages.COMMAND_VERIFY_REPORT_NATIVE_AND_IRIS_STRUCTURE_REACHABILITY_IN_THE_CURRENT_DIMENSION, "locateall")
));
SECTIONS.put("struct", SECTIONS.get("structure"));
SECTIONS.put("str", SECTIONS.get("structure"));
SECTIONS.put("developer", List.of(
Entry.command("sentry", "", "Send a test exception to the Iris error reporter"),
Entry.command("network", "", "List network interfaces and their addresses", "ip")
Entry.command("sentry", "", ModdedHelpMessages.COMMAND_SENTRY_SEND_A_TEST_EXCEPTION_TO_THE_IRIS_ERROR_REPORTER),
Entry.command("network", "", ModdedHelpMessages.COMMAND_NETWORK_LIST_NETWORK_INTERFACES_AND_THEIR_ADDRESSES, "ip")
));
SECTIONS.put("dev", SECTIONS.get("developer"));
}
@@ -179,7 +185,10 @@ final class ModdedCommandHelp {
String normalized = normalize(path);
List<Entry> entries = SECTIONS.get(normalized);
if (entries == null) {
ModdedCommandFeedback.fail(source, "Unknown Iris help section: " + normalized);
ModdedCommandFeedback.fail(source, IrisLanguage.plain(
IrisMessages.MODDED_HELP_UNKNOWN_SECTION,
MessageArgument.untrusted("section", normalized)
));
return 0;
}
@@ -208,9 +217,11 @@ final class ModdedCommandHelp {
String parent = parentPath(path);
String command = parent.isEmpty() ? "/iris" : "/iris help " + parent;
MutableComponent hover = Component.empty()
.append(text("Click to go back to ", DARK_GREEN))
.append(text(parent.isEmpty() ? "Iris" : parent, PARAMETER_ALT));
return text("〈 Back", BACK).withStyle((style) -> style
.append(text(IrisLanguage.plain(
IrisMessages.MODDED_HELP_BACK_HOVER,
MessageArgument.untrusted("parent", parent.isEmpty() ? "Iris" : parent)
), DARK_GREEN));
return text("" + IrisLanguage.plain(DirectorHelpMessages.BACK), BACK).withStyle((style) -> style
.withClickEvent(new ClickEvent.RunCommand(command))
.withHoverEvent(new HoverEvent.ShowText(hover)));
}
@@ -239,7 +250,7 @@ final class ModdedCommandHelp {
private static MutableComponent nodes(Entry entry) {
if (entry.group()) {
return text(" - Category of Commands", CATEGORY);
return text(" - " + IrisLanguage.plain(DirectorHelpMessages.COMMAND_GROUP), CATEGORY);
}
List<String> tokens = usageTokens(entry.usage());
@@ -273,18 +284,18 @@ final class ModdedCommandHelp {
hover.append(text(name, PARAMETER));
hover.append(Component.literal("\n"));
hover.append(text("", DESCRIPTION_ICON));
hover.append(text("Command parameter", DESCRIPTION));
hover.append(text(IrisLanguage.plain(IrisMessages.MODDED_HELP_COMMAND_PARAMETER), DESCRIPTION));
hover.append(Component.literal("\n"));
if (required) {
hover.append(text("", REQUIRED));
hover.append(text("This parameter is required.", REQUIRED_TEXT));
hover.append(text(IrisLanguage.plain(IrisMessages.MODDED_HELP_PARAMETER_REQUIRED), REQUIRED_TEXT));
} else {
hover.append(text("", DESCRIPTION_ICON));
hover.append(text("This parameter is optional.", USAGE));
hover.append(text(IrisLanguage.plain(IrisMessages.MODDED_HELP_PARAMETER_OPTIONAL), USAGE));
}
hover.append(Component.literal("\n"));
hover.append(text("", DARK_GREEN));
hover.append(text("This parameter is read as text by Brigadier.", HOVER_TYPE));
hover.append(text(IrisLanguage.plain(IrisMessages.MODDED_HELP_BRIGADIER_TEXT), HOVER_TYPE));
return title.withStyle((style) -> style.withHoverEvent(new HoverEvent.ShowText(hover)));
}
@@ -294,15 +305,15 @@ final class ModdedCommandHelp {
hover.append(text(names(entry), PARAMETER));
hover.append(Component.literal("\n"));
hover.append(text("", DESCRIPTION_ICON));
hover.append(text(entry.description(), DESCRIPTION));
hover.append(text(IrisLanguage.plain(entry.description()), DESCRIPTION));
hover.append(Component.literal("\n"));
hover.append(text("", USAGE_ICON));
if (entry.group()) {
hover.append(text("This is a command category. Click to run.", USAGE));
hover.append(text(IrisLanguage.plain(DirectorHelpMessages.COMMAND_GROUP), USAGE));
} else if (entry.usage().isBlank()) {
hover.append(text("There are no parameters. Click to type command.", USAGE));
hover.append(text(IrisLanguage.plain(DirectorHelpMessages.NO_PARAMETERS), USAGE));
} else {
hover.append(text("Hover over all of the parameters to learn more.", USAGE));
hover.append(text(IrisLanguage.plain(DirectorHelpMessages.PARAMETERS), USAGE));
hover.append(Component.literal("\n"));
hover.append(text("", EXAMPLE_ICON));
hover.append(text(suggestion, PARAMETER));
@@ -311,7 +322,7 @@ final class ModdedCommandHelp {
String parent = path.isEmpty() ? "/iris" : "/iris " + path;
if (entry.aliases().length > 0) {
hover.append(Component.literal("\n"));
hover.append(text("Aliases: ", DARK_GREEN));
hover.append(text(IrisLanguage.plain(DirectorHelpMessages.ALIASES) + ": ", DARK_GREEN));
List<String> aliases = new ArrayList<>(entry.aliases().length);
for (String alias : entry.aliases()) {
aliases.add(parent + " " + alias);
@@ -324,11 +335,11 @@ final class ModdedCommandHelp {
private static MutableComponent opNotice() {
MutableComponent notice = Component.empty();
notice.append(text("", REQUIRED));
notice.append(text("Iris commands need operator permission (level 2). ", REQUIRED_TEXT));
notice.append(text("Run ", DESCRIPTION));
notice.append(text("/op <you>", PARAMETER_ALT));
notice.append(text(" from the console (or enable cheats in singleplayer); ", DESCRIPTION));
notice.append(text("until then these commands will not run or tab-complete.", USAGE));
notice.append(text(IrisLanguage.plain(IrisMessages.MODDED_HELP_OPERATOR_NOTICE) + " ", REQUIRED_TEXT));
notice.append(text(IrisLanguage.plain(
IrisMessages.MODDED_HELP_OPERATOR_INSTRUCTION,
MessageArgument.trusted("command", "/op <you>")
), DESCRIPTION));
return notice;
}
@@ -404,12 +415,12 @@ final class ModdedCommandHelp {
return normalized;
}
private record Entry(String name, String usage, String description, boolean group, String... aliases) {
static Entry command(String name, String usage, String description, String... aliases) {
private record Entry(String name, String usage, TextKey description, boolean group, String... aliases) {
static Entry command(String name, String usage, TextKey description, String... aliases) {
return new Entry(name, usage, description, false, aliases);
}
static Entry group(String name, String description, String... aliases) {
static Entry group(String name, TextKey description, String... aliases) {
return new Entry(name, "", description, true, aliases);
}
}
@@ -45,6 +45,10 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.function.Predicate;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.ModdedCommandMessages;
import art.arcane.iris.core.localization.RuntimeUiMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
public final class ModdedDatapackCommands {
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
private static final Predicate<CommandSourceStack> GATE = Commands.hasPermission(Commands.LEVEL_GAMEMASTERS);
@@ -118,8 +122,7 @@ public final class ModdedDatapackCommands {
Engine engine = IrisModdedCommands.engineFor(level);
if (engine == null || engine.getDimension() == null) {
IrisModdedCommands.ok(source, dimensionId + ": type=" + typeKey + " active=" + activeMin + ".." + activeMax
+ " logical=" + active.logicalHeight() + " (pack=" + irisGenerator.dimensionKey() + ", engine not started; pack heights unknown)");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_TYPE_ACTIVE_LOGICAL_PACK_ENGINE_NOT_STARTED_PACK_HEIGHTS_UNKNOWN, MessageArgument.untrusted("dimensionId", dimensionId), MessageArgument.untrusted("typeKey", typeKey), MessageArgument.untrusted("activeMin", activeMin), MessageArgument.untrusted("activeMax", activeMax), MessageArgument.untrusted("value", active.logicalHeight()), MessageArgument.untrusted("value2", irisGenerator.dimensionKey())));
continue;
}
IrisDimension dimension = engine.getDimension();
@@ -128,22 +131,18 @@ public final class ModdedDatapackCommands {
int packLogical = dimension.getLogicalHeight();
boolean matches = packMin == activeMin && packMax == activeMax && packLogical == active.logicalHeight();
File override = overrideFile(server, irisGenerator.dimensionKey());
IrisModdedCommands.ok(source, dimensionId + ": type=" + typeKey
+ " active=" + activeMin + ".." + activeMax + " logical=" + active.logicalHeight()
+ " | pack '" + dimension.getLoadKey() + "' wants " + packMin + ".." + packMax + " logical=" + packLogical
+ " | " + (matches ? "MATCH" : "MISMATCH")
+ (override.isFile() ? " (world datapack override installed)" : ""));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_TYPE_ACTIVE_LOGICAL_PACK_WANTS_LOGICAL, MessageArgument.untrusted("dimensionId", dimensionId), MessageArgument.untrusted("typeKey", typeKey), MessageArgument.untrusted("activeMin", activeMin), MessageArgument.untrusted("activeMax", activeMax), MessageArgument.untrusted("value", active.logicalHeight()), MessageArgument.untrusted("value2", dimension.getLoadKey()), MessageArgument.untrusted("packMin", packMin), MessageArgument.untrusted("packMax", packMax), MessageArgument.untrusted("packLogical", packLogical), MessageArgument.trusted("value3", IrisLanguage.plain(matches ? RuntimeUiMessages.STATUS_MATCH : RuntimeUiMessages.STATUS_MISMATCH)), MessageArgument.trusted("value4", override.isFile() ? IrisLanguage.plain(RuntimeUiMessages.DATAPACK_OVERRIDE_SUFFIX) : "")));
if (!matches) {
mismatches++;
IrisModdedCommands.fail(source, " WARNING: the active dimension type does not match the pack. Iris will refuse to start this world engine instead of clipping terrain. Run /iris world enable <dimension> <pack> for new worlds, or /iris datapack install for already-loaded Iris dimensions, then restart.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_WARNING_ACTIVE_DIMENSION_TYPE_DOES_NOT_MATCH_PACK_IRIS_WILL));
}
}
if (irisLevels == 0) {
IrisModdedCommands.fail(source, "No Iris dimensions are loaded.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_NO_IRIS_DIMENSIONS_ARE_LOADED));
return 0;
}
if (mismatches == 0) {
IrisModdedCommands.ok(source, "All " + irisLevels + " Iris dimension(s) match their pack height ranges.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_ALL_IRIS_DIMENSION_S_MATCH_THEIR_PACK_HEIGHT_RANGES, MessageArgument.untrusted("irisLevels", irisLevels)));
}
return 1;
}
@@ -157,7 +156,7 @@ public final class ModdedDatapackCommands {
}
Engine engine = IrisModdedCommands.engineFor(level);
if (engine == null || engine.getDimension() == null) {
IrisModdedCommands.fail(source, level.dimension().identifier() + ": engine not started; cannot derive its dimension type yet.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_ENGINE_NOT_STARTED_CANNOT_DERIVE_ITS_DIMENSION_TYPE_YET, MessageArgument.untrusted("value", level.dimension().identifier())));
continue;
}
IrisDimension dimension = engine.getDimension();
@@ -166,7 +165,7 @@ public final class ModdedDatapackCommands {
json = dimension.getDimensionType().toJson(DataVersion.getLatest().get());
} catch (Throwable e) {
LOGGER.error("Iris dimension type generation failed for {}", dimension.getLoadKey(), e);
IrisModdedCommands.fail(source, level.dimension().identifier() + ": dimension type generation failed: " + e.getMessage());
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_DIMENSION_TYPE_GENERATION_FAILED, MessageArgument.untrusted("value", level.dimension().identifier()), MessageArgument.untrusted("value2", String.valueOf(e.getMessage()))));
continue;
}
File output = overrideFile(server, irisGenerator.dimensionKey());
@@ -176,11 +175,11 @@ public final class ModdedDatapackCommands {
written.add(output.getPath());
} catch (IOException e) {
LOGGER.error("Iris dimension type write failed for {}", output, e);
IrisModdedCommands.fail(source, "Failed to write " + output + ": " + e.getMessage());
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_FAILED_WRITE, MessageArgument.untrusted("output", output), MessageArgument.untrusted("value", String.valueOf(e.getMessage()))));
}
}
if (written.isEmpty()) {
IrisModdedCommands.fail(source, "No Iris dimensions with running engines found; nothing was installed.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_NO_IRIS_DIMENSIONS_WITH_RUNNING_ENGINES_FOUND_NOTHING_WAS_INSTALLED));
return 0;
}
@@ -200,14 +199,14 @@ public final class ModdedDatapackCommands {
written.add(mcmeta.getPath());
} catch (IOException e) {
LOGGER.error("Iris pack.mcmeta write failed for {}", mcmeta, e);
IrisModdedCommands.fail(source, "Failed to write " + mcmeta + ": " + e.getMessage());
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_FAILED_WRITE_2, MessageArgument.untrusted("mcmeta", mcmeta), MessageArgument.untrusted("value", String.valueOf(e.getMessage()))));
return 0;
}
for (String path : written) {
IrisModdedCommands.ok(source, "Wrote " + path);
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_WROTE, MessageArgument.untrusted("path", path)));
}
IrisModdedCommands.ok(source, "World datapack '" + WORLD_PACK_NAME + "' dimension type overrides installed. Restart the server for the dimension types to apply.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_WORLD_DATAPACK_DIMENSION_TYPE_OVERRIDES_INSTALLED_RESTART_SERVER_DIMENSION_TYPES, MessageArgument.untrusted("WORLDPACKNAME", WORLD_PACK_NAME)));
return 1;
}
@@ -239,12 +238,12 @@ public final class ModdedDatapackCommands {
}
}
IrisModdedCommands.ok(source, "Configured datapack imports: " + configured.size());
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_CONFIGURED_DATAPACK_IMPORTS, MessageArgument.untrusted("value", configured.size())));
for (String url : configured) {
IrisModdedCommands.ok(source, " - " + url);
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_MESSAGE, MessageArgument.untrusted("url", url)));
}
if (!configured.isEmpty()) {
IrisModdedCommands.ok(source, "Modrinth ingest is Bukkit-only; install these manually into world/datapacks if needed.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_MODRINTH_INGEST_IS_BUKKIT_ONLY_INSTALL_THESE_MANUALLY_INTO_WORLD));
}
File datapacks = worldDatapacksFolder(server);
@@ -257,9 +256,9 @@ public final class ModdedDatapackCommands {
}
}
}
IrisModdedCommands.ok(source, "Installed world datapacks: " + names.size());
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_INSTALLED_WORLD_DATAPACKS, MessageArgument.untrusted("value", names.size())));
for (String name : names) {
IrisModdedCommands.ok(source, " - " + name);
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_MESSAGE_2, MessageArgument.untrusted("name", name)));
}
return 1;
}
@@ -33,6 +33,9 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.function.Predicate;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.ModdedCommandMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
final class ModdedDeveloperCommands {
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
private static final Predicate<CommandSourceStack> GATE = Commands.hasPermission(Commands.LEVEL_GAMEMASTERS);
@@ -53,7 +56,7 @@ final class ModdedDeveloperCommands {
private static int sentry(CommandSourceStack source) {
IrisPlatforms.get().reportError(new Exception("This is an Iris Sentry test exception"));
ModdedCommandFeedback.ok(source, "Dispatched a test exception to the Iris error reporter (Sentry if enabled).");
ModdedCommandFeedback.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DEVELOPER_COMMANDS_DISPATCHED_TEST_EXCEPTION_IRIS_ERROR_REPORTER_SENTRY_IF_ENABLED));
return 1;
}
@@ -63,13 +66,13 @@ final class ModdedDeveloperCommands {
for (NetworkInterface networkInterface : Collections.list(interfaces)) {
ModdedCommandFeedback.ok(source, networkInterface.getDisplayName());
for (InetAddress address : Collections.list(networkInterface.getInetAddresses())) {
ModdedCommandFeedback.ok(source, " " + address.getHostAddress());
ModdedCommandFeedback.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DEVELOPER_COMMANDS_MESSAGE, MessageArgument.untrusted("value", address.getHostAddress())));
}
}
return 1;
} catch (SocketException error) {
LOGGER.error("Iris developer network dump failed", error);
ModdedCommandFeedback.fail(source, "Network scan failed: " + error.getClass().getSimpleName());
ModdedCommandFeedback.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DEVELOPER_COMMANDS_NETWORK_SCAN_FAILED, MessageArgument.untrusted("value", error.getClass().getSimpleName())));
return 0;
}
}
@@ -18,10 +18,13 @@
package art.arcane.iris.modded.command;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.RuntimeUiMessages;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.object.IrisRegion;
import art.arcane.iris.modded.ModdedBlockState;
import art.arcane.volmlib.util.localization.MessageArgument;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.DustParticleOptions;
import net.minecraft.network.chat.Component;
@@ -50,7 +53,7 @@ public final class ModdedDustRevealer {
public static void reveal(ServerPlayer player, ServerLevel level, BlockPos pos) {
Engine engine = IrisModdedCommands.engineFor(level);
if (engine == null) {
player.sendSystemMessage(Component.literal("This dimension is not generated by Iris."));
player.sendSystemMessage(Component.literal(IrisLanguage.plain(RuntimeUiMessages.DUST_IRIS_WORLD_REQUIRED)));
return;
}
describe(player, level, engine, pos);
@@ -60,7 +63,10 @@ public final class ModdedDustRevealer {
if (key == null) {
return;
}
player.sendSystemMessage(Component.literal("Found object " + key));
player.sendSystemMessage(Component.literal(IrisLanguage.plain(
RuntimeUiMessages.DUST_FOUND_OBJECT,
MessageArgument.untrusted("object", key)
)));
MinecraftServer server = level.getServer();
BlockPos origin = pos.immutable();
Thread thread = new Thread(() -> {
@@ -71,8 +77,11 @@ public final class ModdedDustRevealer {
hit.getX() + 0.5D, hit.getY() + 0.5D, hit.getZ() + 0.5D,
3, 0.25D, 0.25D, 0.25D, 0.0D);
}
player.sendSystemMessage(Component.literal("Revealed " + hits.size() + " block(s) of " + key
+ (hits.size() >= MAX_HITS ? " (capped)" : "")));
player.sendSystemMessage(Component.literal(IrisLanguage.plain(
hits.size() >= MAX_HITS ? RuntimeUiMessages.DUST_REVEALED_CAPPED : RuntimeUiMessages.DUST_REVEALED,
MessageArgument.trusted("count", hits.size()),
MessageArgument.untrusted("object", key)
)));
});
}, "Iris Dust Revealer");
thread.setDaemon(true);
@@ -133,31 +142,72 @@ public final class ModdedDustRevealer {
IrisRegion region = safe(() -> engine.getRegion(x, z));
List<String> lines = new ArrayList<>();
lines.add("--- Iris Dust @ " + x + ", " + y + ", " + z + " ---");
lines.add("Block: " + ModdedBlockState.serialize(level.getBlockState(pos)));
lines.add(IrisLanguage.plain(
RuntimeUiMessages.DUST_HEADER,
MessageArgument.trusted("x", x),
MessageArgument.trusted("y", y),
MessageArgument.trusted("z", z)
));
lines.add(IrisLanguage.plain(
RuntimeUiMessages.DUST_BLOCK,
MessageArgument.untrusted("block", ModdedBlockState.serialize(level.getBlockState(pos)))
));
if (offset > 0) {
lines.add("Position: +" + offset + " ABOVE surface (surface Y=" + surfaceY + ")");
lines.add(IrisLanguage.plain(
RuntimeUiMessages.DUST_POSITION_ABOVE,
MessageArgument.trusted("offset", offset),
MessageArgument.trusted("surfaceY", surfaceY)
));
} else if (offset < 0) {
lines.add("Position: " + (-offset) + " below surface (surface Y=" + surfaceY + ")");
lines.add(IrisLanguage.plain(
RuntimeUiMessages.DUST_POSITION_BELOW,
MessageArgument.trusted("offset", -offset),
MessageArgument.trusted("surfaceY", surfaceY)
));
} else {
lines.add("Position: at surface (Y=" + surfaceY + ")");
lines.add(IrisLanguage.plain(
RuntimeUiMessages.DUST_POSITION_AT,
MessageArgument.trusted("surfaceY", surfaceY)
));
}
lines.add("Object @block: " + (objectKey == null ? "none" : objectKey));
lines.add(IrisLanguage.plain(
RuntimeUiMessages.DUST_OBJECT_AT_BLOCK,
MessageArgument.untrusted(
"object",
objectKey == null ? IrisLanguage.plain(RuntimeUiMessages.DUST_NONE) : objectKey
)
));
if (surfaceBiome != null) {
lines.add("Surface biome: " + surfaceBiome.getLoadKey());
lines.add(IrisLanguage.plain(
RuntimeUiMessages.DUST_SURFACE_BIOME,
MessageArgument.untrusted("biome", surfaceBiome.getLoadKey())
));
}
if (biomeHere != null && (surfaceBiome == null || !biomeHere.getLoadKey().equals(surfaceBiome.getLoadKey()))) {
lines.add("Biome @Y: " + biomeHere.getLoadKey());
lines.add(IrisLanguage.plain(
RuntimeUiMessages.DUST_BIOME_AT_Y,
MessageArgument.untrusted("biome", biomeHere.getLoadKey())
));
}
if (caveBiome != null && (surfaceBiome == null || !caveBiome.getLoadKey().equals(surfaceBiome.getLoadKey()))) {
lines.add("Cave/Mantle biome: " + caveBiome.getLoadKey());
lines.add(IrisLanguage.plain(
RuntimeUiMessages.DUST_CAVE_BIOME,
MessageArgument.untrusted("biome", caveBiome.getLoadKey())
));
}
if (region != null) {
lines.add("Region: " + region.getLoadKey() + " (" + region.getName() + ")");
lines.add(IrisLanguage.plain(
RuntimeUiMessages.DUST_REGION,
MessageArgument.untrusted("region", region.getLoadKey()),
MessageArgument.untrusted("name", region.getName())
));
}
Set<String> objects = safe(() -> engine.getObjectsAt(x >> 4, z >> 4));
if (objects != null && !objects.isEmpty()) {
lines.add("Objects in chunk: " + objects);
lines.add(IrisLanguage.plain(
RuntimeUiMessages.DUST_OBJECTS_IN_CHUNK,
MessageArgument.untrusted("objects", objects)
));
}
for (String line : lines) {
player.sendSystemMessage(Component.literal(line));
@@ -36,6 +36,10 @@ import java.io.File;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.ModdedCommandMessages;
import art.arcane.iris.core.localization.RuntimeProgressMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
public final class ModdedGoldenHash {
public enum Mode {
AUTO,
@@ -80,13 +84,18 @@ public final class ModdedGoldenHash {
public static void start(CommandSourceStack source, ServerLevel level, Engine engine, int radius, int threads, Mode mode) {
if (!ACTIVE.compareAndSet(false, true)) {
IrisModdedCommands.fail(source, "A goldenhash scan is already running.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_GOLDEN_HASH_GOLDENHASH_SCAN_IS_ALREADY_RUNNING));
return;
}
ModdedGoldenHash scan = new ModdedGoldenHash(source, level, engine, radius, threads, mode);
int boundedRadius = Math.max(0, radius);
int chunks = (boundedRadius * 2 + 1) * (boundedRadius * 2 + 1);
scan.ok("GoldenHash started: " + chunks + " chunk(s) around 0,0 in buffers (world untouched), threads=" + Math.max(1, threads) + " mode=" + mode);
scan.ok(IrisLanguage.plain(
RuntimeProgressMessages.GOLDEN_STARTED,
MessageArgument.trusted("chunks", chunks),
MessageArgument.trusted("threads", Math.max(1, threads)),
MessageArgument.untrusted("mode", mode)
));
LOGGER.info("goldenhash start: dim={} seed={} radius={} threads={} mode={} file={}",
engine.getDimension().getLoadKey(), engine.getSeedManager().getSeed(), boundedRadius, Math.max(1, threads), mode, scan.hashEngine.getGoldenFile().getName());
Thread thread = new Thread(() -> {
@@ -161,14 +170,25 @@ public final class ModdedGoldenHash {
int doneCount = hashed.incrementAndGet();
int stride = total <= 64 ? 1 : 32;
if (doneCount % stride == 0 || doneCount == total) {
ModdedGoldenHash.this.ok("[" + doneCount + "/" + total + "] chunk " + chunkX + "," + chunkZ + " hashed");
ModdedGoldenHash.this.ok(IrisLanguage.plain(
RuntimeProgressMessages.GOLDEN_CHUNK_HASHED,
MessageArgument.trusted("done", doneCount),
MessageArgument.trusted("total", total),
MessageArgument.trusted("x", chunkX),
MessageArgument.trusted("z", chunkZ)
));
}
}
@Override
public void chunkFailed(int chunkX, int chunkZ, Throwable error) {
LOGGER.error("goldenhash chunk {},{} failed", chunkX, chunkZ, error);
ModdedGoldenHash.this.fail("Chunk " + chunkX + "," + chunkZ + " FAILED: " + error.getClass().getSimpleName());
ModdedGoldenHash.this.fail(IrisLanguage.plain(
RuntimeProgressMessages.GOLDEN_CHUNK_FAILED,
MessageArgument.trusted("x", chunkX),
MessageArgument.trusted("z", chunkZ),
MessageArgument.untrusted("type", error.getClass().getSimpleName())
));
}
};
}
@@ -69,6 +69,10 @@ import java.util.Map;
import java.util.UUID;
import java.util.function.Predicate;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.ModdedCommandMessages;
import art.arcane.iris.core.localization.RuntimeUiMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
public final class ModdedObjectCommands {
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
private static final Predicate<CommandSourceStack> GATE = Commands.hasPermission(Commands.LEVEL_GAMEMASTERS);
@@ -218,55 +222,55 @@ public final class ModdedObjectCommands {
public static int giveWand(CommandSourceStack source) {
ServerPlayer player = source.getPlayer();
if (player == null) {
IrisModdedCommands.fail(source, "This command can only be used by players. (the wand is a physical item)");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_WAND_IS));
return 0;
}
if (!player.getInventory().add(ModdedWandService.createWand())) {
IrisModdedCommands.fail(source, "Your inventory is full.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_YOUR_INVENTORY_IS_FULL));
return 0;
}
IrisModdedCommands.ok(source, "Poof! Good luck building! (left click = corner 1, right click = corner 2)");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_POOF_GOOD_LUCK_BUILDING_LEFT_CLICK_CORNER_1_RIGHT_CLICK));
return 1;
}
private static int giveDust(CommandSourceStack source) {
ServerPlayer player = source.getPlayer();
if (player == null) {
IrisModdedCommands.fail(source, "This command can only be used by players. (the dust is a physical item)");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_DUST_IS));
return 0;
}
if (!player.getInventory().add(ModdedWandService.createDust())) {
IrisModdedCommands.fail(source, "Your inventory is full.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_YOUR_INVENTORY_IS_FULL_2));
return 0;
}
IrisModdedCommands.ok(source, "Right click a block to reveal the object it belongs to.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_RIGHT_CLICK_BLOCK_REVEAL_OBJECT_IT_BELONGS));
return 1;
}
private static int save(CommandSourceStack source, String nameRaw, boolean overwrite) {
ServerPlayer player = source.getPlayer();
if (player == null) {
IrisModdedCommands.fail(source, "This command can only be used by players. (saving captures your wand selection)");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_SAVING_CAPTURES));
return 0;
}
ServerLevel level = player.level();
Engine engine = IrisModdedCommands.engineFor(level);
if (engine == null) {
IrisModdedCommands.fail(source, "This dimension is not generated by Iris; objects save into the dimension's pack.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_OBJECTS_SAVE_INTO));
return 0;
}
if (!ModdedWandService.isHoldingWand(player)) {
IrisModdedCommands.fail(source, "Hold your Iris wand. (/iris wand)");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_HOLD_YOUR_IRIS_WAND_IRIS_WAND));
return 0;
}
ModdedWandService.Selection selection = ModdedWandService.selection(player);
if (selection == null) {
IrisModdedCommands.fail(source, "No area selected. Left/right click blocks with the wand first.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_NO_AREA_SELECTED_LEFT_RIGHT_CLICK_BLOCKS_WITH_WAND_FIRST));
return 0;
}
String name = nameRaw.trim().replace('\\', '/');
if (name.isEmpty() || name.contains("..")) {
IrisModdedCommands.fail(source, "Invalid object name: " + nameRaw);
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_INVALID_OBJECT_NAME, MessageArgument.untrusted("nameRaw", nameRaw)));
return 0;
}
BlockPos min = selection.min();
@@ -276,12 +280,12 @@ public final class ModdedObjectCommands {
int d = max.getZ() - min.getZ() + 1;
long volume = (long) w * h * d;
if (volume > MAX_SAVE_VOLUME) {
IrisModdedCommands.fail(source, "Selection too large: " + volume + " blocks (max " + MAX_SAVE_VOLUME + ").");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_SELECTION_TOO_LARGE_BLOCKS_MAX, MessageArgument.untrusted("volume", volume), MessageArgument.untrusted("MAXSAVEVOLUME", MAX_SAVE_VOLUME)));
return 0;
}
File file = new File(engine.getData().getDataFolder(), "objects" + File.separator + name.replace('/', File.separatorChar) + ".iob");
if (file.exists() && !overwrite) {
IrisModdedCommands.fail(source, "File already exists. Use /iris object save overwrite " + name);
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_FILE_ALREADY_EXISTS_USE_IRIS_OBJECT_SAVE_OVERWRITE, MessageArgument.untrusted("name", name)));
return 0;
}
int[] tilesSkipped = {0};
@@ -295,7 +299,7 @@ public final class ModdedObjectCommands {
object.write(file);
} catch (IOException e) {
LOGGER.error("Iris object save failed for {}", file.getAbsolutePath(), e);
IrisModdedCommands.fail(source, "Failed to save object: " + e.getMessage());
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_FAILED_SAVE_OBJECT, MessageArgument.untrusted("value", String.valueOf(e.getMessage()))));
return 0;
}
StringBuilder tileNote = new StringBuilder();
@@ -308,8 +312,7 @@ public final class ModdedObjectCommands {
} else if (tilesSkipped[0] > 0) {
tileNote.append(" (").append(tilesSkipped[0]).append(" tile state(s) could not be captured)");
}
IrisModdedCommands.ok(source, "Saved " + engine.getData().getDataFolder().getName() + "/objects/" + name + ".iob: "
+ w + "x" + h + "x" + d + ", " + object.getBlocks().size() + " block(s)" + tileNote);
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_SAVED_OBJECTS_IOB_X_X_BLOCK_S, MessageArgument.untrusted("value", engine.getData().getDataFolder().getName()), MessageArgument.untrusted("name", name), MessageArgument.untrusted("w", w), MessageArgument.untrusted("h", h), MessageArgument.untrusted("d", d), MessageArgument.untrusted("value2", object.getBlocks().size()), MessageArgument.untrusted("tileNote", tileNote)));
LOGGER.info("Iris object save: {} {}x{}x{} blocks={} tilesSaved={} tilesSkipped={} -> {}", name, w, h, d, object.getBlocks().size(), tilesSaved[0], tilesSkipped[0], file.getAbsolutePath());
return 1;
}
@@ -371,7 +374,7 @@ public final class ModdedObjectCommands {
LOGGER.error("Iris object load failed for {}", key, e);
}
if (object == null || object.getBlocks().size() == 0) {
IrisModdedCommands.fail(source, "Unknown or empty object: " + key);
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_UNKNOWN_EMPTY_OBJECT, MessageArgument.untrusted("key", key)));
return 0;
}
@@ -379,12 +382,12 @@ public final class ModdedObjectCommands {
BlockPos target = at;
if (target == null) {
if (player == null) {
IrisModdedCommands.fail(source, "Console must specify coordinates: /iris object paste at <x> <y> <z> [rotate <degrees>] " + key);
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_CONSOLE_MUST_SPECIFY_COORDINATES_IRIS_OBJECT_PASTE_AT_X_Y, MessageArgument.untrusted("key", key)));
return 0;
}
HitResult hit = player.pick(TARGET_RANGE, 1.0F, false);
if (hit.getType() != HitResult.Type.BLOCK || !(hit instanceof BlockHitResult blockHit)) {
IrisModdedCommands.fail(source, "You are not looking at a block within " + (int) TARGET_RANGE + " blocks.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_YOU_ARE_NOT_LOOKING_AT_BLOCK_WITHIN_BLOCKS, MessageArgument.untrusted("value", (int) TARGET_RANGE)));
return 0;
}
target = blockHit.getBlockPos().above();
@@ -398,14 +401,13 @@ public final class ModdedObjectCommands {
} catch (Throwable e) {
LOGGER.error("Iris paste failed for {}", key, e);
ModdedObjectUndo.record(player == null ? ModdedObjectUndo.CONSOLE : player.getUUID(), level, placer.undoSnapshot());
IrisModdedCommands.fail(source, "Paste failed: " + e.getClass().getSimpleName() + " (partial changes recorded for undo)");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_PASTE_FAILED_PARTIAL_CHANGES_RECORDED_UNDO, MessageArgument.untrusted("value", e.getClass().getSimpleName())));
return 0;
}
UUID owner = player == null ? ModdedObjectUndo.CONSOLE : player.getUUID();
ModdedObjectUndo.record(owner, level, placer.undoSnapshot());
String tileNote = tileNote(placer);
IrisModdedCommands.ok(source, "Placed " + key + " at " + target.getX() + " " + target.getY() + " " + target.getZ()
+ " rot=" + rotation + " (" + placer.writes() + " write(s), " + placer.nonAirWrites() + " non-air" + tileNote + ")");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_PLACED_AT_ROT_WRITE_S_NON_AIR, MessageArgument.untrusted("key", key), MessageArgument.untrusted("value", target.getX()), MessageArgument.untrusted("value2", target.getY()), MessageArgument.untrusted("value3", target.getZ()), MessageArgument.untrusted("rotation", rotation), MessageArgument.untrusted("value4", placer.writes()), MessageArgument.untrusted("value5", placer.nonAirWrites()), MessageArgument.untrusted("tileNote", tileNote)));
LOGGER.info("Iris paste: {} at {},{},{} rot={} writes={} nonAir={} tilesRestored={} tilesSkipped={}",
key, target.getX(), target.getY(), target.getZ(), rotation, placer.writes(), placer.nonAirWrites(), placer.restoredTiles(), placer.skippedTiles());
return placer.writes() > 0 ? 1 : 0;
@@ -414,16 +416,16 @@ public final class ModdedObjectCommands {
private static int resize(CommandSourceStack source, int amount, ResizeOp op) {
ServerPlayer player = source.getPlayer();
if (player == null) {
IrisModdedCommands.fail(source, "This command can only be used by players.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS));
return 0;
}
if (!ModdedWandService.isHoldingWand(player)) {
IrisModdedCommands.fail(source, "Hold your Iris wand. (/iris wand)");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_HOLD_YOUR_IRIS_WAND_IRIS_WAND_2));
return 0;
}
ModdedWandService.Selection selection = ModdedWandService.selection(player);
if (selection == null) {
IrisModdedCommands.fail(source, "No area selected.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_NO_AREA_SELECTED));
return 0;
}
Direction direction = Direction.getApproximateNearest(player.getLookAngle());
@@ -458,26 +460,25 @@ public final class ModdedObjectCommands {
BlockPos first = new BlockPos(mins[0], mins[1], mins[2]);
BlockPos second = new BlockPos(maxs[0], maxs[1], maxs[2]);
ModdedWandService.setSelection(player, first, second);
IrisModdedCommands.ok(source, op.name().toLowerCase(Locale.ROOT) + " " + amount + " " + direction.getName()
+ ": " + describe(first, second));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_MESSAGE, MessageArgument.untrusted("value", op.name().toLowerCase(Locale.ROOT)), MessageArgument.untrusted("amount", amount), MessageArgument.untrusted("value2", direction.getName()), MessageArgument.untrusted("value3", describe(first, second))));
return 1;
}
private static int position(CommandSourceStack source, boolean first, boolean look) {
ServerPlayer player = source.getPlayer();
if (player == null) {
IrisModdedCommands.fail(source, "This command can only be used by players.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_2));
return 0;
}
if (!ModdedWandService.isHoldingWand(player)) {
IrisModdedCommands.fail(source, "Ready your wand. (/iris wand)");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_READY_YOUR_WAND_IRIS_WAND));
return 0;
}
BlockPos pos;
if (look) {
HitResult hit = player.pick(TARGET_RANGE, 1.0F, false);
if (hit.getType() != HitResult.Type.BLOCK || !(hit instanceof BlockHitResult blockHit)) {
IrisModdedCommands.fail(source, "You are not looking at a block.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_YOU_ARE_NOT_LOOKING_AT_BLOCK));
return 0;
}
pos = blockHit.getBlockPos();
@@ -492,23 +493,23 @@ public final class ModdedObjectCommands {
} else {
ModdedWandService.setSelection(player, fallback, pos);
}
IrisModdedCommands.ok(source, "Position " + (first ? 1 : 2) + " set to " + pos.getX() + " " + pos.getY() + " " + pos.getZ());
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_POSITION_SET, MessageArgument.untrusted("value", (first ? 1 : 2)), MessageArgument.untrusted("value2", pos.getX()), MessageArgument.untrusted("value3", pos.getY()), MessageArgument.untrusted("value4", pos.getZ())));
return 1;
}
private static int autoSelect(CommandSourceStack source, boolean down) {
ServerPlayer player = source.getPlayer();
if (player == null) {
IrisModdedCommands.fail(source, "This command can only be used by players.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_3));
return 0;
}
if (!ModdedWandService.isHoldingWand(player)) {
IrisModdedCommands.fail(source, "Hold your wand!");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_HOLD_YOUR_WAND));
return 0;
}
ModdedWandService.Selection selection = ModdedWandService.selection(player);
if (selection == null) {
IrisModdedCommands.fail(source, "No area selected.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_NO_AREA_SELECTED_2));
return 0;
}
ServerLevel level = player.level();
@@ -516,7 +517,7 @@ public final class ModdedObjectCommands {
BlockPos max = selection.max();
long volume = (long) (max.getX() - min.getX() + 1) * (max.getY() - min.getY() + 1) * (max.getZ() - min.getZ() + 1);
if (volume > MAX_AUTOSELECT_VOLUME) {
IrisModdedCommands.fail(source, "Selection too large for auto-select: " + volume + " blocks (max " + MAX_AUTOSELECT_VOLUME + ").");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_SELECTION_TOO_LARGE_AUTO_SELECT_BLOCKS_MAX, MessageArgument.untrusted("volume", volume), MessageArgument.untrusted("MAXAUTOSELECTVOLUME", MAX_AUTOSELECT_VOLUME)));
return 0;
}
int levelMinY = level.getMinY();
@@ -561,7 +562,7 @@ public final class ModdedObjectCommands {
BlockPos first = new BlockPos(minX, bottomY, minZ);
BlockPos second = new BlockPos(maxX, topMaxY, maxZ);
ModdedWandService.setSelection(player, first, second);
IrisModdedCommands.ok(source, "Auto-select complete: " + describe(first, second));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_AUTO_SELECT_COMPLETE, MessageArgument.untrusted("value", describe(first, second))));
return 1;
}
@@ -590,11 +591,11 @@ public final class ModdedObjectCommands {
LOGGER.error("Iris object load failed for {}", key, e);
}
if (object == null) {
IrisModdedCommands.fail(source, "Unknown object: " + key);
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_UNKNOWN_OBJECT, MessageArgument.untrusted("key", key)));
return 0;
}
IrisModdedCommands.ok(source, "Object Size: " + object.getW() + " * " + object.getH() + " * " + object.getD());
IrisModdedCommands.ok(source, "Blocks Used: " + object.getBlocks().size());
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_OBJECT_SIZE, MessageArgument.untrusted("value", object.getW()), MessageArgument.untrusted("value2", object.getH()), MessageArgument.untrusted("value3", object.getD())));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_BLOCKS_USED, MessageArgument.untrusted("value", object.getBlocks().size())));
Map<String, Integer> counts = new HashMap<>();
Iterator<PlatformBlockState> values = object.getBlocks().values();
while (values.hasNext()) {
@@ -603,15 +604,15 @@ public final class ModdedObjectCommands {
}
List<Map.Entry<String, Integer>> sorted = new ArrayList<>(counts.entrySet());
sorted.sort(Comparator.comparingInt((Map.Entry<String, Integer> entry) -> entry.getValue()).reversed());
IrisModdedCommands.ok(source, "== Blocks in object ==");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_BLOCKS_OBJECT));
int shown = 0;
for (Map.Entry<String, Integer> entry : sorted) {
IrisModdedCommands.ok(source, " - " + entry.getKey() + " * " + entry.getValue());
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_MESSAGE_2, MessageArgument.untrusted("value", entry.getKey()), MessageArgument.untrusted("value2", entry.getValue())));
shown++;
if (shown >= 10) {
int remaining = sorted.size() - shown;
if (remaining > 0) {
IrisModdedCommands.ok(source, " + " + remaining + " other block state(s)");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_OTHER_BLOCK_STATE_S, MessageArgument.untrusted("remaining", remaining)));
}
break;
}
@@ -630,22 +631,22 @@ public final class ModdedObjectCommands {
LOGGER.error("Iris object load failed for {}", key, e);
}
if (object == null) {
IrisModdedCommands.fail(source, "Unknown object: " + key);
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_UNKNOWN_OBJECT_2, MessageArgument.untrusted("key", key)));
return 0;
}
IrisModdedCommands.ok(source, "Current Object Size: " + object.getW() + " * " + object.getH() + " * " + object.getD());
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_CURRENT_OBJECT_SIZE, MessageArgument.untrusted("value", object.getW()), MessageArgument.untrusted("value2", object.getH()), MessageArgument.untrusted("value3", object.getD())));
object.shrinkwrap();
IrisModdedCommands.ok(source, "New Object Size: " + object.getW() + " * " + object.getH() + " * " + object.getD());
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_NEW_OBJECT_SIZE, MessageArgument.untrusted("value", object.getW()), MessageArgument.untrusted("value2", object.getH()), MessageArgument.untrusted("value3", object.getD())));
File file = object.getLoadFile();
if (file == null) {
IrisModdedCommands.fail(source, "Object has no load file; cannot persist the shrink.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_OBJECT_HAS_NO_LOAD_FILE_CANNOT_PERSIST_SHRINK));
return 0;
}
try {
object.write(file);
} catch (IOException e) {
LOGGER.error("Iris object shrink save failed for {}", file.getAbsolutePath(), e);
IrisModdedCommands.fail(source, "Failed to save object " + file.getName() + ": " + e.getMessage());
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_FAILED_SAVE_OBJECT_2, MessageArgument.untrusted("value", file.getName()), MessageArgument.untrusted("value2", String.valueOf(e.getMessage()))));
return 0;
}
return 1;
@@ -667,7 +668,7 @@ public final class ModdedObjectCommands {
try {
reach = Integer.parseInt(lower.substring("reach=".length()));
} catch (NumberFormatException e) {
IrisModdedCommands.fail(source, "Invalid reach: " + token);
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_INVALID_REACH, MessageArgument.untrusted("token", token)));
return 0;
}
continue;
@@ -680,16 +681,20 @@ public final class ModdedObjectCommands {
String target = targetBuilder.toString();
List<TreePlausibilizeBatch.Target> targets = TreePlausibilizeBatch.resolve(target, data);
if (targets.isEmpty()) {
IrisModdedCommands.fail(source, "No objects matched: " + target);
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_NO_OBJECTS_MATCHED, MessageArgument.untrusted("target", target)));
return 0;
}
IrisModdedCommands.ok(source, "Plausibilize [reach=" + reach + (dryRun ? ", DRY" : "")
+ "] queued " + targets.size() + " object(s)");
IrisModdedCommands.ok(source, IrisLanguage.plain(
ModdedCommandMessages.MODDED_OBJECT_COMMANDS_PLAUSIBILIZE_REACH_QUEUED_OBJECT_S,
MessageArgument.trusted("reach", reach),
MessageArgument.trusted("value", dryRun ? IrisLanguage.plain(RuntimeUiMessages.TREE_DRY_SUFFIX) : ""),
MessageArgument.trusted("value2", targets.size())
));
boolean dry = dryRun;
int reachFinal = reach;
MinecraftServer server = source.getServer();
J.a(() -> TreePlausibilizeBatch.run(targets, dry, reachFinal, data, (String line) ->
server.execute(() -> IrisModdedCommands.ok(source, line))));
J.a(() -> TreePlausibilizeBatch.run(targets, dry, reachFinal, data, (TreePlausibilizeBatch.Output output) ->
server.execute(() -> IrisModdedCommands.ok(source, output.text()))));
return 1;
}
@@ -698,11 +703,11 @@ public final class ModdedObjectCommands {
UUID owner = player == null ? ModdedObjectUndo.CONSOLE : player.getUUID();
int available = ModdedObjectUndo.size(owner);
if (available == 0) {
IrisModdedCommands.fail(source, "Nothing to undo.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_NOTHING_UNDO));
return 0;
}
int reverted = ModdedObjectUndo.undo(owner, Math.min(amount, available));
IrisModdedCommands.ok(source, "Reverted " + reverted + " paste(s)!");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_REVERTED_PASTE_S, MessageArgument.untrusted("reverted", reverted)));
return 1;
}
@@ -39,6 +39,9 @@ import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.ModdedCommandMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
public final class ModdedPackCommands {
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
private static final Predicate<CommandSourceStack> GATE = Commands.hasPermission(Commands.LEVEL_GAMEMASTERS);
@@ -101,7 +104,7 @@ public final class ModdedPackCommands {
private static int validate(CommandSourceStack source, String pack) {
File packsRoot = packsRoot();
if (!packsRoot.isDirectory()) {
IrisModdedCommands.fail(source, "Packs folder not found: " + packsRoot.getAbsolutePath());
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_PACKS_FOLDER_NOT_FOUND, MessageArgument.untrusted("value", packsRoot.getAbsolutePath())));
return 0;
}
@@ -109,7 +112,7 @@ public final class ModdedPackCommands {
if (pack == null || pack.isBlank()) {
File[] dirs = packsRoot.listFiles(File::isDirectory);
if (dirs == null || dirs.length == 0) {
IrisModdedCommands.fail(source, "No packs to validate under " + packsRoot.getAbsolutePath());
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_NO_PACKS_VALIDATE_UNDER, MessageArgument.untrusted("value", packsRoot.getAbsolutePath())));
return 0;
}
for (File dir : dirs) {
@@ -118,14 +121,14 @@ public final class ModdedPackCommands {
} else {
File target = PackDirectoryResolver.resolveExisting(packsRoot, pack);
if (target == null) {
IrisModdedCommands.fail(source, "Pack '" + pack + "' not found under " + packsRoot.getAbsolutePath());
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_PACK_NOT_FOUND_UNDER, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("value", packsRoot.getAbsolutePath())));
return 0;
}
targets.add(target);
}
MinecraftServer server = source.getServer();
IrisModdedCommands.ok(source, "Validating " + targets.size() + " pack(s)...");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_VALIDATING_PACK_S, MessageArgument.untrusted("value", targets.size())));
Thread thread = new Thread(() -> {
int broken = 0;
for (File target : targets) {
@@ -138,12 +141,12 @@ public final class ModdedPackCommands {
server.execute(() -> report(source, result));
} catch (Throwable e) {
LOGGER.error("Iris pack validation failed for {}", target.getName(), e);
server.execute(() -> IrisModdedCommands.fail(source, "Validation of '" + target.getName() + "' failed: " + e.getMessage()));
server.execute(() -> IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_VALIDATION_FAILED, MessageArgument.untrusted("value", target.getName()), MessageArgument.untrusted("value2", String.valueOf(e.getMessage())))));
broken++;
}
}
int brokenTotal = broken;
server.execute(() -> IrisModdedCommands.ok(source, "Validation complete. Broken packs: " + brokenTotal + "/" + targets.size()));
server.execute(() -> IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_VALIDATION_COMPLETE_BROKEN_PACKS, MessageArgument.untrusted("brokenTotal", brokenTotal), MessageArgument.untrusted("value", targets.size()))));
}, "Iris Pack Validator");
thread.setDaemon(true);
thread.start();
@@ -153,7 +156,7 @@ public final class ModdedPackCommands {
private static int cleanup(CommandSourceStack source, String pack, boolean apply) {
File packFolder = PackDirectoryResolver.resolveExisting(packsRoot(), pack);
if (packFolder == null) {
IrisModdedCommands.fail(source, "Pack '" + pack + "' not found under " + packsRoot().getAbsolutePath());
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_PACK_NOT_FOUND_UNDER_2, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("value", packsRoot().getAbsolutePath())));
return 0;
}
MinecraftServer server = source.getServer();
@@ -174,7 +177,7 @@ public final class ModdedPackCommands {
private static int restore(CommandSourceStack source, String pack, boolean apply) {
File packFolder = PackDirectoryResolver.resolveExisting(packsRoot(), pack);
if (packFolder == null) {
IrisModdedCommands.fail(source, "Pack '" + pack + "' not found under " + packsRoot().getAbsolutePath());
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_PACK_NOT_FOUND_UNDER_3, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("value", packsRoot().getAbsolutePath())));
return 0;
}
MinecraftServer server = source.getServer();
@@ -196,21 +199,19 @@ public final class ModdedPackCommands {
if (pack == null || pack.isBlank()) {
Map<String, PackValidationResult> snapshot = PackValidationRegistry.snapshot();
if (snapshot.isEmpty()) {
IrisModdedCommands.fail(source, "No validation results recorded. Run /iris pack validate first.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_NO_VALIDATION_RESULTS_RECORDED_RUN_IRIS_PACK_VALIDATE_FIRST));
return 0;
}
for (Map.Entry<String, PackValidationResult> entry : snapshot.entrySet()) {
PackValidationResult result = entry.getValue();
String tag = result.isLoadable() ? "OK" : "BROKEN";
IrisModdedCommands.ok(source, tag + " " + entry.getKey()
+ " (blocking=" + result.getBlockingErrors().size()
+ ", warnings=" + result.getWarnings().size() + ")");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_BLOCKING_WARNINGS, MessageArgument.untrusted("tag", tag), MessageArgument.untrusted("value", entry.getKey()), MessageArgument.untrusted("value2", result.getBlockingErrors().size()), MessageArgument.untrusted("value3", result.getWarnings().size())));
}
return 1;
}
PackValidationResult result = PackValidationRegistry.get(pack);
if (result == null) {
IrisModdedCommands.fail(source, "No validation result for '" + pack + "'. Run /iris pack validate " + pack + ".");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_NO_VALIDATION_RESULT_RUN_IRIS_PACK_VALIDATE, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("pack2", pack)));
return 0;
}
report(source, result);
@@ -219,20 +220,19 @@ public final class ModdedPackCommands {
private static void report(CommandSourceStack source, PackValidationResult result) {
if (result.isLoadable()) {
IrisModdedCommands.ok(source, "Pack '" + result.getPackName() + "' is loadable."
+ " (warnings=" + result.getWarnings().size() + ")");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_PACK_IS_LOADABLE_WARNINGS, MessageArgument.untrusted("value", result.getPackName()), MessageArgument.untrusted("value2", result.getWarnings().size())));
} else {
IrisModdedCommands.fail(source, "Pack '" + result.getPackName() + "' is BROKEN:");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_PACK_IS_BROKEN, MessageArgument.untrusted("value", result.getPackName())));
for (String reason : result.getBlockingErrors()) {
IrisModdedCommands.fail(source, " - " + reason);
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_MESSAGE, MessageArgument.untrusted("reason", reason)));
}
}
int warningMax = Math.min(10, result.getWarnings().size());
for (int i = 0; i < warningMax; i++) {
IrisModdedCommands.ok(source, " ! " + result.getWarnings().get(i));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_MESSAGE_2, MessageArgument.untrusted("value", result.getWarnings().get(i))));
}
if (result.getWarnings().size() > warningMax) {
IrisModdedCommands.ok(source, " ... and " + (result.getWarnings().size() - warningMax) + " more warning(s).");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_MORE_WARNING_S, MessageArgument.untrusted("value", (result.getWarnings().size() - warningMax))));
}
}
@@ -242,13 +242,12 @@ public final class ModdedPackCommands {
return;
}
if (!result.hasCandidates()) {
IrisModdedCommands.ok(source, "No cleanup candidates found for pack '" + pack + "'.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_NO_CLEANUP_CANDIDATES_FOUND_PACK, MessageArgument.untrusted("pack", pack)));
return;
}
IrisModdedCommands.ok(source, "Cleanup preview for pack '" + pack + "': "
+ result.candidatePaths().size() + " candidate(s). No files were changed.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_CLEANUP_PREVIEW_PACK_CANDIDATE_S_NO_FILES_WERE_CHANGED, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("value", result.candidatePaths().size())));
reportPaths(source, result.candidatePaths(), "candidate");
IrisModdedCommands.ok(source, "Run /iris pack cleanup " + pack + " apply to quarantine after a fresh scan.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_RUN_IRIS_PACK_CLEANUP_APPLY_QUARANTINE_AFTER_FRESH_SCAN, MessageArgument.untrusted("pack", pack)));
}
private static void reportCleanupApply(CommandSourceStack source, String pack, PackResourceCleanup.ApplyResult result) {
@@ -258,11 +257,10 @@ public final class ModdedPackCommands {
return;
}
if (!result.changed()) {
IrisModdedCommands.ok(source, "No cleanup candidates found for pack '" + pack + "'.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_NO_CLEANUP_CANDIDATES_FOUND_PACK_2, MessageArgument.untrusted("pack", pack)));
return;
}
IrisModdedCommands.ok(source, "Quarantined " + result.quarantinedPaths().size()
+ " cleanup candidate(s) under " + result.quarantinePath() + ".");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_QUARANTINED_CLEANUP_CANDIDATE_S_UNDER, MessageArgument.untrusted("value", result.quarantinedPaths().size()), MessageArgument.untrusted("value2", result.quarantinePath())));
reportPaths(source, result.quarantinedPaths(), "quarantined");
}
@@ -272,23 +270,22 @@ public final class ModdedPackCommands {
return;
}
if (!result.hasFiles()) {
IrisModdedCommands.ok(source, "Nothing to restore for pack '" + pack + "'.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_NOTHING_RESTORE_PACK, MessageArgument.untrusted("pack", pack)));
return;
}
IrisModdedCommands.ok(source, "Restore preview for " + result.dumpPath() + ": "
+ result.filePaths().size() + " file(s). No files were changed.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_RESTORE_PREVIEW_FILE_S_NO_FILES_WERE_CHANGED, MessageArgument.untrusted("value", result.dumpPath()), MessageArgument.untrusted("value2", result.filePaths().size())));
reportPaths(source, result.filePaths(), "file");
if (!result.conflicts().isEmpty()) {
IrisModdedCommands.fail(source, "Restore is blocked by " + result.conflicts().size() + " existing destination(s).");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_RESTORE_IS_BLOCKED_BY_EXISTING_DESTINATION_S, MessageArgument.untrusted("value", result.conflicts().size())));
reportPaths(source, result.conflicts(), "conflict");
return;
}
IrisModdedCommands.ok(source, "Run /iris pack restore " + pack + " apply to restore after a fresh conflict check.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_RUN_IRIS_PACK_RESTORE_APPLY_RESTORE_AFTER_FRESH_CONFLICT_CHECK, MessageArgument.untrusted("pack", pack)));
}
private static void reportRestoreApply(CommandSourceStack source, String pack, PackResourceCleanup.RestoreResult result) {
if (!result.conflicts().isEmpty()) {
IrisModdedCommands.fail(source, "Restore refused because " + result.conflicts().size() + " destination(s) already exist.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_RESTORE_REFUSED_BECAUSE_DESTINATION_S_ALREADY_EXIST, MessageArgument.untrusted("value", result.conflicts().size())));
reportPaths(source, result.conflicts(), "conflict");
return;
}
@@ -297,21 +294,20 @@ public final class ModdedPackCommands {
return;
}
if (!result.changed()) {
IrisModdedCommands.ok(source, "Nothing to restore for pack '" + pack + "'.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_NOTHING_RESTORE_PACK_2, MessageArgument.untrusted("pack", pack)));
return;
}
IrisModdedCommands.ok(source, "Restored " + result.restoredPaths().size()
+ " file(s) from " + result.dumpPath() + ".");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_RESTORED_FILE_S_FROM, MessageArgument.untrusted("value", result.restoredPaths().size()), MessageArgument.untrusted("value2", result.dumpPath())));
reportPaths(source, result.restoredPaths(), "restored");
}
private static void reportPaths(CommandSourceStack source, List<String> paths, String label) {
int max = Math.min(10, paths.size());
for (int i = 0; i < max; i++) {
IrisModdedCommands.ok(source, " - " + label + ": " + paths.get(i));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_MESSAGE_3, MessageArgument.untrusted("label", label), MessageArgument.untrusted("value", paths.get(i))));
}
if (paths.size() > max) {
IrisModdedCommands.ok(source, " ... and " + (paths.size() - max) + " more.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_MORE, MessageArgument.untrusted("value", (paths.size() - max))));
}
}
}
@@ -1,13 +1,16 @@
package art.arcane.iris.modded.command;
import art.arcane.iris.core.gui.PregeneratorJob;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.RuntimeUiMessages;
import art.arcane.iris.core.protocol.IrisProtocolServer;
import art.arcane.iris.core.protocol.IrisSession;
import art.arcane.iris.spi.IrisServices;
import art.arcane.iris.spi.protocol.IrisProtocol;
import art.arcane.volmlib.util.format.Form;
import art.arcane.volmlib.util.localization.MessageArgument;
import art.arcane.volmlib.util.localization.TextKey;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerBossEvent;
import net.minecraft.server.level.ServerPlayer;
@@ -35,7 +38,12 @@ public final class ModdedPregenBossBar {
return;
}
viewer = player.getUUID();
bar = new ServerBossEvent(BAR_ID, Component.literal("Iris Pregen starting..."), BossEvent.BossBarColor.GREEN, BossEvent.BossBarOverlay.PROGRESS);
bar = new ServerBossEvent(
BAR_ID,
Component.literal(IrisLanguage.plain(RuntimeUiMessages.PREGEN_STARTING)),
BossEvent.BossBarColor.GREEN,
BossEvent.BossBarOverlay.PROGRESS
);
bar.setProgress(0.0F);
bar.addPlayer(player);
sinceUpdate = UPDATE_INTERVAL_TICKS;
@@ -73,22 +81,38 @@ public final class ModdedPregenBossBar {
}
private static Component nameFor(PregeneratorJob.PregenProgress progress) {
MutableComponent name = Component.empty();
name.append(ModdedCommandFeedback.text("Iris Pregen ", ModdedCommandFeedback.DARK_GREEN));
name.append(ModdedCommandFeedback.text(Form.f(progress.generated()) + "/" + Form.f(progress.totalChunks()), ModdedCommandFeedback.VALUE));
name.append(ModdedCommandFeedback.text(" " + String.format("%.1f", progress.percent()) + "%", ModdedCommandFeedback.USAGE));
TextKey message = progress.paused()
? RuntimeUiMessages.PREGEN_BOSSBAR_PAUSED
: RuntimeUiMessages.PREGEN_BOSSBAR_RUNNING;
if (progress.paused()) {
name.append(ModdedCommandFeedback.text(" PAUSED", ModdedCommandFeedback.REQUIRED));
return name;
return ModdedCommandFeedback.text(IrisLanguage.plain(
message,
MessageArgument.trusted("generated", Form.f(progress.generated())),
MessageArgument.trusted("total", Form.f(progress.totalChunks())),
MessageArgument.trusted("percent", String.format("%.1f", progress.percent()))
), ModdedCommandFeedback.DARK_GREEN);
}
name.append(ModdedCommandFeedback.text(" " + Form.f((int) progress.chunksPerSecond()) + "/s", ModdedCommandFeedback.VALUE));
if (progress.eta() > 0L) {
name.append(ModdedCommandFeedback.text(" ETA " + Form.duration(progress.eta(), 1), ModdedCommandFeedback.DARK_GREEN));
}
if (progress.failed() > 0L) {
name.append(ModdedCommandFeedback.text(" failed " + Form.f(progress.failed()), ModdedCommandFeedback.REQUIRED));
}
return name;
String eta = progress.eta() > 0L
? IrisLanguage.plain(
RuntimeUiMessages.PREGEN_ETA_FRAGMENT,
MessageArgument.trusted("eta", Form.duration(progress.eta(), 1))
)
: "";
String failed = progress.failed() > 0L
? IrisLanguage.plain(
RuntimeUiMessages.PREGEN_FAILED_FRAGMENT,
MessageArgument.trusted("failed", Form.f(progress.failed()))
)
: "";
return ModdedCommandFeedback.text(IrisLanguage.plain(
message,
MessageArgument.trusted("generated", Form.f(progress.generated())),
MessageArgument.trusted("total", Form.f(progress.totalChunks())),
MessageArgument.trusted("percent", String.format("%.1f", progress.percent())),
MessageArgument.trusted("speed", Form.f((int) progress.chunksPerSecond())),
MessageArgument.trusted("eta", eta),
MessageArgument.trusted("failed", failed)
), ModdedCommandFeedback.DARK_GREEN);
}
private static boolean hasClientPregenHud(ServerPlayer player) {
@@ -19,6 +19,8 @@
package art.arcane.iris.modded.command;
import art.arcane.iris.core.gui.PregeneratorJob;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.RuntimeUiMessages;
import art.arcane.iris.core.pregenerator.PregenPerformanceProfile;
import art.arcane.iris.core.pregenerator.PregenTask;
import art.arcane.iris.core.pregenerator.PregeneratorMethod;
@@ -27,6 +29,7 @@ import art.arcane.iris.core.pregenerator.methods.CachedPregenMethod;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.volmlib.util.format.Form;
import art.arcane.volmlib.util.math.Position2;
import art.arcane.volmlib.util.localization.MessageArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.server.MinecraftServer;
@@ -119,41 +122,66 @@ public final class ModdedPregenJob {
}
MutableComponent status = Component.empty();
status.append(ModdedCommandFeedback.header("Iris Pregen"));
status.append(ModdedCommandFeedback.header(IrisLanguage.plain(RuntimeUiMessages.PREGEN_HEADER)));
status.append(Component.literal("\n"));
status.append(ModdedCommandFeedback.text("Dimension ", ModdedCommandFeedback.DARK_GREEN));
status.append(ModdedCommandFeedback.text(dimension, ModdedCommandFeedback.PARAMETER_ALT));
status.append(ModdedCommandFeedback.text(" · Method ", ModdedCommandFeedback.DARK_GREEN));
status.append(ModdedCommandFeedback.text(progress.method(), ModdedCommandFeedback.PARAMETER));
status.append(ModdedCommandFeedback.text(IrisLanguage.plain(
RuntimeUiMessages.PREGEN_STATUS_CONTEXT,
MessageArgument.untrusted("dimension", dimension),
MessageArgument.untrusted("method", progress.method())
), ModdedCommandFeedback.DARK_GREEN));
status.append(Component.literal("\n"));
status.append(ModdedCommandFeedback.progressBar(progress.percent(), 32));
status.append(ModdedCommandFeedback.text(" " + String.format("%.1f", progress.percent()) + "%", ModdedCommandFeedback.USAGE));
status.append(ModdedCommandFeedback.text(" " + IrisLanguage.plain(
RuntimeUiMessages.PREGEN_STATUS_PROGRESS,
MessageArgument.trusted("percent", String.format("%.1f", progress.percent()))
), ModdedCommandFeedback.USAGE));
status.append(Component.literal("\n"));
status.append(ModdedCommandFeedback.text("Chunks ", ModdedCommandFeedback.DARK_GREEN));
status.append(ModdedCommandFeedback.text(Form.f(progress.generated()) + "/" + Form.f(progress.totalChunks()), ModdedCommandFeedback.VALUE));
status.append(ModdedCommandFeedback.text(" · Speed ", ModdedCommandFeedback.DARK_GREEN));
status.append(ModdedCommandFeedback.text(Form.f((int) progress.chunksPerSecond()) + "/s", ModdedCommandFeedback.VALUE));
if (progress.failed() > 0) {
status.append(ModdedCommandFeedback.text(" · Failed ", ModdedCommandFeedback.DARK_GREEN));
status.append(ModdedCommandFeedback.text(Form.f(progress.failed()), ModdedCommandFeedback.REQUIRED));
}
status.append(ModdedCommandFeedback.text(chunksStatus(progress), ModdedCommandFeedback.DARK_GREEN));
status.append(Component.literal("\n"));
status.append(ModdedCommandFeedback.text("ETA ", ModdedCommandFeedback.DARK_GREEN));
status.append(ModdedCommandFeedback.text(Form.duration(progress.eta(), 2), ModdedCommandFeedback.VALUE));
status.append(ModdedCommandFeedback.text(" · Elapsed ", ModdedCommandFeedback.DARK_GREEN));
status.append(ModdedCommandFeedback.text(Form.duration(progress.elapsed(), 2), ModdedCommandFeedback.VALUE));
if (progress.paused()) {
status.append(ModdedCommandFeedback.text(" · PAUSED", ModdedCommandFeedback.REQUIRED, true, false));
}
status.append(ModdedCommandFeedback.text(IrisLanguage.plain(
progress.paused()
? RuntimeUiMessages.PREGEN_STATUS_TIME_PAUSED
: RuntimeUiMessages.PREGEN_STATUS_TIME,
MessageArgument.trusted("eta", Form.duration(progress.eta(), 2)),
MessageArgument.trusted("elapsed", Form.duration(progress.elapsed(), 2))
), ModdedCommandFeedback.DARK_GREEN));
status.append(Component.literal("\n"));
status.append(ModdedCommandFeedback.button("Pause/Resume", "/iris pregen pause", "Toggle pregeneration pause state", true));
status.append(ModdedCommandFeedback.button(
IrisLanguage.plain(RuntimeUiMessages.PREGEN_PAUSE_BUTTON),
"/iris pregen pause",
IrisLanguage.plain(RuntimeUiMessages.PREGEN_PAUSE_HOVER),
true
));
status.append(ModdedCommandFeedback.text(" ", ModdedCommandFeedback.OPTIONAL));
status.append(ModdedCommandFeedback.button("Stop", "/iris pregen stop", "Finish the current region and stop pregeneration", true));
status.append(ModdedCommandFeedback.button(
IrisLanguage.plain(RuntimeUiMessages.PREGEN_STOP_BUTTON),
"/iris pregen stop",
IrisLanguage.plain(RuntimeUiMessages.PREGEN_STOP_HOVER),
true
));
status.append(Component.literal("\n"));
status.append(ModdedCommandFeedback.footer());
return status;
}
private static String chunksStatus(PregeneratorJob.PregenProgress progress) {
if (progress.failed() > 0) {
return IrisLanguage.plain(
RuntimeUiMessages.PREGEN_STATUS_CHUNKS_FAILED,
MessageArgument.trusted("generated", Form.f(progress.generated())),
MessageArgument.trusted("total", Form.f(progress.totalChunks())),
MessageArgument.trusted("speed", Form.f((int) progress.chunksPerSecond())),
MessageArgument.trusted("failed", Form.f(progress.failed()))
);
}
return IrisLanguage.plain(
RuntimeUiMessages.PREGEN_STATUS_CHUNKS,
MessageArgument.trusted("generated", Form.f(progress.generated())),
MessageArgument.trusted("total", Form.f(progress.totalChunks())),
MessageArgument.trusted("speed", Form.f((int) progress.chunksPerSecond()))
);
}
private static ActivePregen matchingActive(String worldIdentity) {
ActivePregen active = ACTIVE.get();
return active != null && active.targets(worldIdentity) ? active : null;
@@ -61,6 +61,8 @@ import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.ModdedCommandMessages;
public final class ModdedRegen {
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
private static final int APPLY_AHEAD = 8;
@@ -88,7 +90,7 @@ public final class ModdedRegen {
public static void start(CommandSourceStack source, ServerLevel level, IrisModdedChunkGenerator generator, Engine engine, ServerPlayer player, int radius) {
if (!ACTIVE.compareAndSet(false, true)) {
IrisModdedCommands.fail(source, "A regen is already running.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_REGEN_REGEN_IS_ALREADY_RUNNING));
return;
}
int centerX = player.blockPosition().getX() >> 4;
@@ -48,6 +48,9 @@ import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.ModdedCommandMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
public final class ModdedStructureCommands {
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
private static final Predicate<CommandSourceStack> GATE = Commands.hasPermission(Commands.LEVEL_GAMEMASTERS);
@@ -116,7 +119,7 @@ public final class ModdedStructureCommands {
private static IrisData dataFor(CommandSourceStack source) {
Engine engine = IrisModdedCommands.engineFor(source.getLevel());
if (engine == null) {
IrisModdedCommands.fail(source, "This dimension is not generated by Iris. Run this from an Iris dimension so the pack can be resolved.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STRUCTURE_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_RUN_THIS_FROM));
return null;
}
return engine.getData();
@@ -128,7 +131,7 @@ public final class ModdedStructureCommands {
return 0;
}
File file = StructureIndexService.write(data);
IrisModdedCommands.ok(source, "Wrote structure index: " + file.getPath());
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STRUCTURE_COMMANDS_WROTE_STRUCTURE_INDEX, MessageArgument.untrusted("value", file.getPath())));
return 1;
}
@@ -140,14 +143,14 @@ public final class ModdedStructureCommands {
String key = keyRaw.trim();
IrisStructure structure = data.load(IrisStructure.class, key, false);
if (structure == null) {
IrisModdedCommands.fail(source, "No iris structure '" + key + "' in this pack");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STRUCTURE_COMMANDS_NO_IRIS_STRUCTURE_THIS_PACK, MessageArgument.untrusted("key", key)));
return 0;
}
StructureAssembler assembler = StructureAssembler.forData(
data, structure, new IrisPosition(0, 64, 0));
KList<PlacedStructurePiece> pieces = assembler.assemble(new RNG(1234));
if (pieces == null || pieces.isEmpty()) {
IrisModdedCommands.fail(source, "Structure '" + key + "' assembled 0 pieces (check startPool '" + structure.getStartPool() + "')");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STRUCTURE_COMMANDS_STRUCTURE_ASSEMBLED_0_PIECES_CHECK_STARTPOOL, MessageArgument.untrusted("key", key), MessageArgument.untrusted("value", structure.getStartPool())));
return 0;
}
int minX = Integer.MAX_VALUE;
@@ -160,15 +163,14 @@ public final class ModdedStructureCommands {
maxX = Math.max(maxX, piece.getMaxX());
maxZ = Math.max(maxZ, piece.getMaxZ());
}
IrisModdedCommands.ok(source, "Structure '" + key + "': " + pieces.size() + " pieces, footprint "
+ (maxX - minX + 1) + "x" + (maxZ - minZ + 1) + " blocks (sample seed 1234)");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STRUCTURE_COMMANDS_STRUCTURE_PIECES_FOOTPRINT_X_BLOCKS_SAMPLE_SEED_1234, MessageArgument.untrusted("key", key), MessageArgument.untrusted("value", pieces.size()), MessageArgument.untrusted("value2", (maxX - minX + 1)), MessageArgument.untrusted("value3", (maxZ - minZ + 1))));
return 1;
}
private static int place(CommandSourceStack source, String keyRaw) {
ServerPlayer player = source.getPlayer();
if (player == null) {
IrisModdedCommands.fail(source, "This command can only be used by players (the structure is assembled at your position).");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STRUCTURE_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_STRUCTURE_IS));
return 0;
}
ServerLevel level = source.getLevel();
@@ -180,7 +182,7 @@ public final class ModdedStructureCommands {
String key = keyRaw.trim();
IrisStructure structure = data.load(IrisStructure.class, key, false);
if (structure == null) {
IrisModdedCommands.fail(source, "No iris structure '" + key + "' in this pack");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STRUCTURE_COMMANDS_NO_IRIS_STRUCTURE_THIS_PACK_2, MessageArgument.untrusted("key", key)));
return 0;
}
int originX = player.blockPosition().getX();
@@ -191,7 +193,7 @@ public final class ModdedStructureCommands {
RNG rng = new RNG((long) originX * 341873128712L + originZ);
KList<PlacedStructurePiece> pieces = assembler.assemble(rng);
if (pieces == null || pieces.isEmpty()) {
IrisModdedCommands.fail(source, "Structure '" + key + "' assembled 0 pieces");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STRUCTURE_COMMANDS_STRUCTURE_ASSEMBLED_0_PIECES, MessageArgument.untrusted("key", key)));
return 0;
}
ModdedObjectPlacer placer = new ModdedObjectPlacer(level, engine);
@@ -210,12 +212,12 @@ public final class ModdedStructureCommands {
} catch (Throwable e) {
LOGGER.error("Iris structure place failed for {}", key, e);
ModdedObjectUndo.record(owner, level, placer.undoSnapshot());
IrisModdedCommands.fail(source, "Place failed: " + e.getClass().getSimpleName() + " (partial changes recorded for undo)");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STRUCTURE_COMMANDS_PLACE_FAILED_PARTIAL_CHANGES_RECORDED_UNDO, MessageArgument.untrusted("value", e.getClass().getSimpleName())));
return 0;
}
ModdedObjectUndo.record(owner, level, placer.undoSnapshot());
String tileNote = ModdedObjectCommands.tileNote(placer);
IrisModdedCommands.ok(source, "Placed '" + key + "' (" + pieces.size() + " pieces, " + placer.writes() + " write(s)" + tileNote + ") at your location. /iris object undo to revert.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STRUCTURE_COMMANDS_PLACED_PIECES_WRITE_S_AT_YOUR_LOCATION_IRIS_OBJECT_UNDO, MessageArgument.untrusted("key", key), MessageArgument.untrusted("value", pieces.size()), MessageArgument.untrusted("value2", placer.writes()), MessageArgument.untrusted("tileNote", tileNote)));
return 1;
}
@@ -82,6 +82,9 @@ import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.ModdedCommandMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
public final class ModdedStudioCommands {
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
private static final Predicate<CommandSourceStack> GATE = Commands.hasPermission(Commands.LEVEL_GAMEMASTERS);
@@ -184,7 +187,7 @@ public final class ModdedStudioCommands {
}
private static int openHelp(CommandSourceStack source) {
IrisModdedCommands.fail(source, "Provide a dimension pack: /iris studio open <pack> [seed]");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PROVIDE_DIMENSION_PACK_IRIS_STUDIO_OPEN_PACK_SEED));
return 0;
}
@@ -215,22 +218,22 @@ public final class ModdedStudioCommands {
}
if (generatorKey == null || generatorKey.isBlank()) {
NoiseExplorerGUI.launch();
IrisModdedCommands.ok(source, "Opening the Noise Explorer on the server display.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_OPENING_NOISE_EXPLORER_ON_SERVER_DISPLAY));
return 1;
}
if (engine == null) {
IrisModdedCommands.fail(source, "This dimension is not generated by Iris; run /iris studio noise from an Iris dimension to resolve generators, or omit the generator name.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_RUN_IRIS_STUDIO));
return 0;
}
IrisGenerator generator = engine.getData().getGeneratorLoader().load(generatorKey.trim());
if (generator == null) {
IrisModdedCommands.fail(source, "Unknown generator '" + generatorKey + "' in pack " + engine.getDimension().getLoadKey() + ".");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_UNKNOWN_GENERATOR_PACK, MessageArgument.untrusted("generatorKey", generatorKey), MessageArgument.untrusted("value", engine.getDimension().getLoadKey())));
return 0;
}
long mixedSeed = new RNG(seed).nextParallelRNG(3245).lmax();
Supplier<Function2<Double, Double, Double>> supplier = () -> (Double x, Double z) -> generator.getHeight(x, z, mixedSeed);
NoiseExplorerGUI.launch(supplier, generatorKey.trim());
IrisModdedCommands.ok(source, "Opening the Noise Explorer for generator '" + generatorKey.trim() + "' (seed " + seed + ").");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_OPENING_NOISE_EXPLORER_GENERATOR_SEED, MessageArgument.untrusted("value", generatorKey.trim()), MessageArgument.untrusted("seed", seed)));
return 1;
}
@@ -238,7 +241,7 @@ public final class ModdedStudioCommands {
ServerLevel level = source.getLevel();
Engine engine = IrisModdedCommands.engineFor(level);
if (engine == null) {
IrisModdedCommands.fail(source, "This dimension is not generated by Iris; stand in an Iris (or studio) dimension and run /iris studio map.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_STAND_IRIS_STUDIO));
return 0;
}
if (!GuiHost.isAvailable() || !IrisSettings.get().getGui().isUseServerLaunchedGuis()) {
@@ -248,7 +251,7 @@ public final class ModdedStudioCommands {
ServerPlayer player = source.getPlayer();
ModdedGuiHost.bindContext(source.getServer(), level, engine, player == null ? null : player.getUUID());
VisionGUI.launch(engine);
IrisModdedCommands.ok(source, "Opening the Vision map for " + level.dimension().identifier() + " on the server display.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_OPENING_VISION_MAP_ON_SERVER_DISPLAY, MessageArgument.untrusted("value", level.dimension().identifier())));
return 1;
}
@@ -269,10 +272,10 @@ public final class ModdedStudioCommands {
workspace = ModdedWorkspaceGenerator.writeWorkspace(IrisData.get(folder), folder);
} catch (IOException e) {
LOGGER.error("Iris workspace write failed for {}", folder, e);
IrisModdedCommands.fail(source, "Failed to write workspace for " + folder.getAbsolutePath() + ": " + 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;
}
IrisModdedCommands.ok(source, "Workspace regenerated: " + workspace.getAbsolutePath() + " with JSON schemas for autocomplete.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_WORKSPACE_REGENERATED_WITH_JSON_SCHEMAS_AUTOCOMPLETE, MessageArgument.untrusted("value", workspace.getAbsolutePath())));
if (!open) {
return 1;
}
@@ -284,10 +287,10 @@ public final class ModdedStudioCommands {
Desktop.getDesktop().open(workspace);
} catch (Throwable e) {
LOGGER.error("Iris workspace open failed for {}", workspace, e);
IrisModdedCommands.fail(source, "Could not open " + workspace.getName() + ": " + e.getClass().getSimpleName());
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_COULD_NOT_OPEN, MessageArgument.untrusted("value", workspace.getName()), MessageArgument.untrusted("value2", e.getClass().getSimpleName())));
return 0;
}
IrisModdedCommands.ok(source, "Opening " + workspace.getName() + " in your editor.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_OPENING_YOUR_EDITOR, MessageArgument.untrusted("value", workspace.getName())));
return 1;
}
@@ -319,14 +322,14 @@ public final class ModdedStudioCommands {
if (name == null || name.isBlank()) {
Engine engine = IrisModdedCommands.engineFor(source.getLevel());
if (engine == null || engine.getDimension() == null) {
IrisModdedCommands.fail(source, "This dimension is not generated by Iris; specify a pack name explicitly.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_SPECIFY_PACK_NAME));
return null;
}
name = engine.getDimension().getLoadKey();
}
File folder = new File(ModdedPackCommands.packsRoot(), name);
if (!folder.isDirectory() || !new File(folder, "dimensions").isDirectory()) {
IrisModdedCommands.fail(source, "Pack '" + name + "' not found under " + ModdedPackCommands.packsRoot().getAbsolutePath());
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PACK_NOT_FOUND_UNDER, MessageArgument.untrusted("name", name), MessageArgument.untrusted("value", ModdedPackCommands.packsRoot().getAbsolutePath())));
return null;
}
return folder;
@@ -346,14 +349,14 @@ public final class ModdedStudioCommands {
private static int open(CommandSourceStack source, String pack, long seed) {
if (pack == null || pack.isBlank()) {
IrisModdedCommands.fail(source, "Provide a dimension pack: /iris studio open <pack> [seed]");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PROVIDE_DIMENSION_PACK_IRIS_STUDIO_OPEN_PACK_SEED_2));
return 0;
}
ServerPlayer player = source.getPlayer();
UUID owner = player == null ? CONSOLE_OWNER : player.getUUID();
String dimensionId = player == null ? studioConsoleDimensionId() : studioDimensionId(player);
MinecraftServer server = source.getServer();
IrisModdedCommands.ok(source, "Opening studio for '" + pack + "' (seed " + seed + ")...");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_OPENING_STUDIO_SEED, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("seed", seed)));
Thread thread = new Thread(() -> openAsync(source, server, owner, dimensionId, pack, seed), "Iris Studio Open");
thread.setDaemon(true);
thread.start();
@@ -364,18 +367,18 @@ public final class ModdedStudioCommands {
try {
File packFolder = new File(ModdedPackCommands.packsRoot(), pack);
if (!new File(packFolder, "dimensions/" + pack + ".json").isFile()) {
server.execute(() -> IrisModdedCommands.ok(source, "Pack '" + pack + "' missing; downloading IrisDimensions/" + pack + "..."));
server.execute(() -> IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PACK_MISSING_DOWNLOADING_IRISDIMENSIONS, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("pack2", pack))));
boolean installed = ModdedPackInstaller.install(ModdedEngineBootstrap.loader().configDir(), pack, "master",
(String line) -> server.execute(() -> IrisModdedCommands.ok(source, line)));
if (!installed || !new File(packFolder, "dimensions/" + pack + ".json").isFile()) {
server.execute(() -> IrisModdedCommands.fail(source, "Pack '" + pack + "' could not be downloaded; check the name and try /iris download " + pack + "."));
server.execute(() -> IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PACK_COULD_NOT_BE_DOWNLOADED_CHECK_NAME_TRY_IRIS_DOWNLOAD, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("pack2", pack))));
return;
}
}
IrisData data = IrisData.get(packFolder);
IrisDimension dimension = data.getDimensionLoader().load(pack);
if (dimension == null) {
server.execute(() -> IrisModdedCommands.fail(source, "Pack '" + pack + "' has no dimensions/" + pack + ".json"));
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;
}
server.execute(() -> {
@@ -387,7 +390,7 @@ public final class ModdedStudioCommands {
});
} catch (Throwable e) {
LOGGER.error("Iris studio open failed for {}", pack, e);
server.execute(() -> IrisModdedCommands.fail(source, "Studio open failed: " + e.getClass().getSimpleName() + (e.getMessage() == null ? "" : " - " + e.getMessage())));
server.execute(() -> IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_STUDIO_OPEN_FAILED, MessageArgument.untrusted("value", e.getClass().getSimpleName()), MessageArgument.trusted("errorMessage", IrisLanguage.errorDetail(e)))));
}
}
@@ -397,7 +400,7 @@ public final class ModdedStudioCommands {
handle = ModdedDimensionManager.create(server, dimensionId, pack, pack, seed);
} catch (Throwable e) {
LOGGER.error("Iris console studio injection failed for {} ({})", dimensionId, pack, e);
IrisModdedCommands.fail(source, "Studio injection failed: " + e.getClass().getSimpleName() + (e.getMessage() == null ? "" : " - " + e.getMessage()));
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_STUDIO_INJECTION_FAILED, MessageArgument.untrusted("value", e.getClass().getSimpleName()), MessageArgument.trusted("errorMessage", IrisLanguage.errorDetail(e))));
return;
}
STUDIOS.put(CONSOLE_OWNER, dimensionId);
@@ -411,10 +414,10 @@ public final class ModdedStudioCommands {
} catch (Throwable e) {
LOGGER.error("Iris console studio surface probe failed for {}", dimensionId, e);
}
IrisModdedCommands.ok(source, "Console studio open: " + dimensionId + " now runs '" + pack + "' seed " + seed + " (transient; not written to iris-dimensions.json and cleared on restart).");
IrisModdedCommands.ok(source, "Enter it with: /execute in " + dimensionId + " run tp @s 8.5 " + surface + " 8.5");
IrisModdedCommands.ok(source, "Pregen it with: /iris pregen start <radius> " + dimensionId);
IrisModdedCommands.ok(source, "Remove it with: /iris studio close");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_CONSOLE_STUDIO_OPEN_NOW_RUNS_SEED_TRANSIENT_NOT_WRITTEN_IRIS, MessageArgument.untrusted("dimensionId", dimensionId), MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("seed", seed)));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_ENTER_IT_WITH_EXECUTE_RUN_TP_S_8_5_8, MessageArgument.untrusted("dimensionId", dimensionId), MessageArgument.untrusted("surface", surface)));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PREGEN_IT_WITH_IRIS_PREGEN_START_RADIUS, MessageArgument.untrusted("dimensionId", dimensionId)));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_REMOVE_IT_WITH_IRIS_STUDIO_CLOSE));
}
private static void injectAndTeleport(CommandSourceStack source, MinecraftServer server, UUID owner, String dimensionId, String pack, long seed) {
@@ -427,7 +430,7 @@ public final class ModdedStudioCommands {
handle = ModdedDimensionManager.create(server, dimensionId, pack, pack, seed);
} catch (Throwable e) {
LOGGER.error("Iris studio injection failed for {} ({})", dimensionId, pack, e);
IrisModdedCommands.fail(source, "Studio injection failed: " + e.getClass().getSimpleName() + (e.getMessage() == null ? "" : " - " + e.getMessage()));
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_STUDIO_INJECTION_FAILED_2, MessageArgument.untrusted("value", e.getClass().getSimpleName()), MessageArgument.trusted("errorMessage", IrisLanguage.errorDetail(e))));
return;
}
STUDIOS.put(owner, dimensionId);
@@ -442,7 +445,7 @@ public final class ModdedStudioCommands {
LOGGER.error("Iris studio surface probe failed for {}", dimensionId, e);
}
player.teleportTo(studio, 8.5D, surface, 8.5D, java.util.Set.<Relative>of(), player.getYRot(), player.getXRot(), false);
IrisModdedCommands.ok(source, "Studio open: " + dimensionId + " now runs '" + pack + "' seed " + seed + ". Use /iris studio close when done.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_STUDIO_OPEN_NOW_RUNS_SEED_USE_IRIS_STUDIO_CLOSE_WHEN, MessageArgument.untrusted("dimensionId", dimensionId), MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("seed", seed)));
}
private static int close(CommandSourceStack source) {
@@ -451,17 +454,17 @@ public final class ModdedStudioCommands {
UUID owner = player == null ? CONSOLE_OWNER : player.getUUID();
String dimensionId = STUDIOS.remove(owner);
if (dimensionId == null) {
IrisModdedCommands.fail(source, "You do not have an open studio. Use /iris studio open <pack> first.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_YOU_DO_NOT_HAVE_OPEN_STUDIO_USE_IRIS_STUDIO_OPEN));
return 0;
}
try {
ModdedDimensionManager.remove(server, dimensionId, true);
} catch (Throwable e) {
LOGGER.error("Iris studio close failed for {}", dimensionId, e);
IrisModdedCommands.fail(source, "Studio close failed: " + e.getClass().getSimpleName() + (e.getMessage() == null ? "" : " - " + e.getMessage()));
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_STUDIO_CLOSE_FAILED, MessageArgument.untrusted("value", e.getClass().getSimpleName()), MessageArgument.trusted("errorMessage", IrisLanguage.errorDetail(e))));
return 0;
}
IrisModdedCommands.ok(source, "Studio closed: " + dimensionId + " was evacuated, unloaded and its region data deleted.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_STUDIO_CLOSED_WAS_EVACUATED_UNLOADED_ITS_REGION_DATA_DELETED, MessageArgument.untrusted("dimensionId", dimensionId)));
return 1;
}
@@ -475,14 +478,14 @@ public final class ModdedStudioCommands {
}
}
if (studios.isEmpty()) {
IrisModdedCommands.ok(source, "No studio dimensions are currently open.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_NO_STUDIO_DIMENSIONS_ARE_CURRENTLY_OPEN));
return 1;
}
IrisModdedCommands.ok(source, "Active studio dimension(s): " + studios.size());
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_ACTIVE_STUDIO_DIMENSION_S, MessageArgument.untrusted("value", studios.size())));
for (ModdedDimensionManager.Handle handle : studios) {
UUID owner = ownerOf(handle.dimensionId());
String ownerName = owner == null ? "unclaimed" : ownerName(server, owner);
IrisModdedCommands.ok(source, " " + handle.dimensionId() + ": pack '" + handle.pack() + "' seed " + handle.seed() + " owner " + ownerName);
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PACK_SEED_OWNER, MessageArgument.untrusted("value", handle.dimensionId()), MessageArgument.untrusted("value2", handle.pack()), MessageArgument.untrusted("value3", handle.seed()), MessageArgument.untrusted("ownerName", ownerName)));
}
return 1;
}
@@ -507,19 +510,19 @@ public final class ModdedStudioCommands {
private static int tpStudio(CommandSourceStack source) {
ServerPlayer player = source.getPlayer();
if (player == null) {
IrisModdedCommands.fail(source, "This command can only be used by players.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS));
return 0;
}
MinecraftServer server = source.getServer();
String dimensionId = STUDIOS.get(player.getUUID());
if (dimensionId == null) {
IrisModdedCommands.fail(source, "You do not have an open studio. Use /iris studio open <pack> first.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_YOU_DO_NOT_HAVE_OPEN_STUDIO_USE_IRIS_STUDIO_OPEN_2));
return 0;
}
ServerLevel studio = ModdedDimensionManager.level(server, dimensionId);
if (studio == null) {
STUDIOS.remove(player.getUUID());
IrisModdedCommands.fail(source, "Your studio dimension is no longer loaded. Use /iris studio open <pack> again.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_YOUR_STUDIO_DIMENSION_IS_NO_LONGER_LOADED_USE_IRIS_STUDIO));
return 0;
}
int surface = studio.getMaxY();
@@ -532,7 +535,7 @@ public final class ModdedStudioCommands {
LOGGER.error("Iris tpstudio surface probe failed", e);
}
player.teleportTo(studio, 8.5D, surface, 8.5D, java.util.Set.<Relative>of(), player.getYRot(), player.getXRot(), false);
IrisModdedCommands.ok(source, "Teleported to your studio (" + dimensionId + ").");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_TELEPORTED_YOUR_STUDIO, MessageArgument.untrusted("dimensionId", dimensionId)));
return 1;
}
@@ -544,36 +547,36 @@ public final class ModdedStudioCommands {
IrisData data = IrisData.get(folder);
IrisDimension dimension = data.getDimensionLoader().load(folder.getName());
if (dimension == null) {
IrisModdedCommands.fail(source, "Pack '" + folder.getName() + "' has no dimensions/" + folder.getName() + ".json");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PACK_HAS_NO_DIMENSIONS_JSON_2, MessageArgument.untrusted("value", folder.getName()), MessageArgument.untrusted("value2", folder.getName())));
return 0;
}
IrisModdedCommands.ok(source, "The \"" + dimension.getName() + "\" pack has version: " + dimension.getVersion());
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PACK_HAS_VERSION, MessageArgument.untrusted("value", dimension.getName()), MessageArgument.untrusted("value2", dimension.getVersion())));
return 1;
}
private static int create(CommandSourceStack source, String nameRaw, String template) {
String name = nameRaw.toLowerCase(Locale.ROOT);
if (!PROJECT_NAME.matcher(name).matches()) {
IrisModdedCommands.fail(source, "Invalid project name '" + nameRaw + "' (allowed: a-z, 0-9, _ and -)");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_INVALID_PROJECT_NAME_ALLOWED_Z_0_9, MessageArgument.untrusted("nameRaw", nameRaw)));
return 0;
}
File packsRoot = ModdedPackCommands.packsRoot();
File target = new File(packsRoot, name);
if (target.exists()) {
IrisModdedCommands.fail(source, "Pack '" + name + "' already exists at " + target.getAbsolutePath());
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PACK_ALREADY_EXISTS_AT, MessageArgument.untrusted("name", name), MessageArgument.untrusted("value", target.getAbsolutePath())));
return 0;
}
MinecraftServer server = source.getServer();
IrisModdedCommands.ok(source, "Creating project '" + name + "' from template '" + template + "'...");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_CREATING_PROJECT_FROM_TEMPLATE, MessageArgument.untrusted("name", name), MessageArgument.untrusted("template", template)));
Thread thread = new Thread(() -> {
try {
File templateFolder = new File(packsRoot, template);
if (!new File(templateFolder, "dimensions/" + template + ".json").isFile()) {
server.execute(() -> IrisModdedCommands.ok(source, "Template '" + template + "' is not installed; downloading IrisDimensions/" + template + "..."));
server.execute(() -> IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_TEMPLATE_IS_NOT_INSTALLED_DOWNLOADING_IRISDIMENSIONS, MessageArgument.untrusted("template", template), MessageArgument.untrusted("template2", template))));
boolean installed = ModdedPackInstaller.install(ModdedEngineBootstrap.loader().configDir(), template, "master",
(String line) -> server.execute(() -> IrisModdedCommands.ok(source, line)));
if (!installed || !new File(templateFolder, "dimensions/" + template + ".json").isFile()) {
server.execute(() -> IrisModdedCommands.fail(source, "Template '" + template + "' could not be downloaded; install a pack with dimensions/" + template + ".json first."));
server.execute(() -> IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_TEMPLATE_COULD_NOT_BE_DOWNLOADED_INSTALL_PACK_WITH_DIMENSIONS_JSON, MessageArgument.untrusted("template", template), MessageArgument.untrusted("template2", template))));
return;
}
}
@@ -584,12 +587,12 @@ public final class ModdedStudioCommands {
LOGGER.error("Iris studio create workspace generation failed for {}", name, e);
}
server.execute(() -> {
IrisModdedCommands.ok(source, "Created project '" + name + "' at " + target.getAbsolutePath());
IrisModdedCommands.ok(source, "Edit dimensions/" + name + ".json and the rest of the pack. A VSCode workspace with JSON schema autocomplete was generated; open it or rerun /iris studio vscode.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_CREATED_PROJECT_AT, MessageArgument.untrusted("name", name), MessageArgument.untrusted("value", target.getAbsolutePath())));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_EDIT_DIMENSIONS_JSON_REST_PACK_VSCODE_WORKSPACE_WITH_JSON_SCHEMA, MessageArgument.untrusted("name", name)));
});
} catch (Throwable e) {
LOGGER.error("Iris studio create failed for {}", name, e);
server.execute(() -> IrisModdedCommands.fail(source, "Project creation failed: " + e.getClass().getSimpleName() + (e.getMessage() == null ? "" : " - " + e.getMessage())));
server.execute(() -> IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PROJECT_CREATION_FAILED, MessageArgument.untrusted("value", e.getClass().getSimpleName()), MessageArgument.trusted("errorMessage", IrisLanguage.errorDetail(e)))));
}
}, "Iris Studio Create");
thread.setDaemon(true);
@@ -604,14 +607,14 @@ public final class ModdedStudioCommands {
}
MinecraftServer server = source.getServer();
String dimKey = folder.getName();
IrisModdedCommands.ok(source, "Packaging dimension '" + dimKey + "'...");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PACKAGING_DIMENSION, MessageArgument.untrusted("dimKey", dimKey)));
Thread thread = new Thread(() -> {
try {
File result = compilePackage(folder, dimKey);
server.execute(() -> IrisModdedCommands.ok(source, "Package compiled: " + result.getAbsolutePath()));
server.execute(() -> IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PACKAGE_COMPILED, MessageArgument.untrusted("value", result.getAbsolutePath()))));
} catch (Throwable e) {
LOGGER.error("Iris package failed for {}", dimKey, e);
server.execute(() -> IrisModdedCommands.fail(source, "Packaging failed: " + e.getClass().getSimpleName() + (e.getMessage() == null ? "" : " - " + e.getMessage())));
server.execute(() -> IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PACKAGING_FAILED, MessageArgument.untrusted("value", e.getClass().getSimpleName()), MessageArgument.trusted("errorMessage", IrisLanguage.errorDetail(e)))));
}
}, "Iris Studio Package");
thread.setDaemon(true);
@@ -758,18 +761,18 @@ public final class ModdedStudioCommands {
private static int regions(CommandSourceStack source, int radius) {
ServerPlayer player = source.getPlayer();
if (player == null) {
IrisModdedCommands.fail(source, "This command can only be used by players (sampling is centered on your position).");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_SAMPLING_IS));
return 0;
}
Engine engine = IrisModdedCommands.engineFor(source.getLevel());
if (engine == null) {
IrisModdedCommands.fail(source, "This dimension is not generated by Iris.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS));
return 0;
}
MinecraftServer server = source.getServer();
int blockX = player.blockPosition().getX();
int blockZ = player.blockPosition().getZ();
IrisModdedCommands.ok(source, "Sampling region distribution in " + (radius * 2) + "x" + (radius * 2) + " chunks around you...");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_SAMPLING_REGION_DISTRIBUTION_X_CHUNKS_AROUND_YOU, MessageArgument.untrusted("value", (radius * 2)), MessageArgument.untrusted("value2", (radius * 2))));
Thread thread = new Thread(() -> {
try {
int diameter = radius * 2;
@@ -787,11 +790,11 @@ public final class ModdedStudioCommands {
server.execute(() -> counts.forEach((String key, AtomicInteger count) -> {
IrisRegion region = engine.getData().getRegionLoader().load(key);
String rarity = region == null ? "?" : String.valueOf(region.getRarity());
IrisModdedCommands.ok(source, key + ": rarity=" + rarity + " / " + Form.f((double) count.get() / totalTasks * 100, 2) + "%");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_RARITY, MessageArgument.untrusted("key", key), MessageArgument.untrusted("rarity", rarity), MessageArgument.untrusted("value", Form.f((double) count.get() / totalTasks * 100, 2))));
}));
} catch (Throwable e) {
LOGGER.error("Iris region sampling failed", e);
server.execute(() -> IrisModdedCommands.fail(source, "Region sampling failed: " + e.getClass().getSimpleName()));
server.execute(() -> IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_REGION_SAMPLING_FAILED, MessageArgument.untrusted("value", e.getClass().getSimpleName()))));
}
}, "Iris Region Sampler");
thread.setDaemon(true);
@@ -18,6 +18,9 @@
package art.arcane.iris.modded.command;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.RuntimeUiMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.core.component.DataComponents;
@@ -78,10 +81,10 @@ public final class ModdedWandService {
public static ItemStack createWand() {
ItemStack stack = new ItemStack(Items.BLAZE_ROD);
stack.set(DataComponents.CUSTOM_NAME, Component.literal("Wand of Iris").withStyle(ChatFormatting.BOLD, ChatFormatting.GOLD));
stack.set(DataComponents.CUSTOM_NAME, Component.literal(IrisLanguage.plain(RuntimeUiMessages.WAND_NAME)).withStyle(ChatFormatting.BOLD, ChatFormatting.GOLD));
stack.set(DataComponents.LORE, new ItemLore(List.of(
Component.literal("Left click a block to set the first corner"),
Component.literal("Right click a block to set the second corner"))));
Component.literal(IrisLanguage.plain(RuntimeUiMessages.WAND_LORE_FIRST)),
Component.literal(IrisLanguage.plain(RuntimeUiMessages.WAND_LORE_SECOND)))));
stack.set(DataComponents.UNBREAKABLE, Unit.INSTANCE);
stack.set(DataComponents.ENCHANTMENT_GLINT_OVERRIDE, Boolean.TRUE);
stack.set(DataComponents.CUSTOM_DATA, CustomData.of(flagTag(WAND_TAG)));
@@ -90,9 +93,9 @@ public final class ModdedWandService {
public static ItemStack createDust() {
ItemStack stack = new ItemStack(Items.GLOWSTONE_DUST);
stack.set(DataComponents.CUSTOM_NAME, Component.literal("Dust of Revealing").withStyle(ChatFormatting.BOLD, ChatFormatting.YELLOW));
stack.set(DataComponents.CUSTOM_NAME, Component.literal(IrisLanguage.plain(RuntimeUiMessages.DUST_NAME)).withStyle(ChatFormatting.BOLD, ChatFormatting.YELLOW));
stack.set(DataComponents.LORE, new ItemLore(List.of(
Component.literal("Right click on a block to reveal it's placement structure!"))));
Component.literal(IrisLanguage.plain(RuntimeUiMessages.DUST_LORE)))));
stack.set(DataComponents.UNBREAKABLE, Unit.INSTANCE);
stack.set(DataComponents.ENCHANTMENT_GLINT_OVERRIDE, Boolean.TRUE);
stack.set(DataComponents.CUSTOM_DATA, CustomData.of(flagTag(DUST_TAG)));
@@ -161,7 +164,13 @@ public final class ModdedWandService {
return first ? new Selection(dimension, corner, other) : new Selection(dimension, other, corner);
});
level.playSound(null, pos, SoundEvents.END_PORTAL_FRAME_FILL, SoundSource.PLAYERS, 1.0F, first ? 0.67F : 1.17F);
player.sendOverlayMessage(Component.literal("Position " + (first ? 1 : 2) + " set to " + corner.getX() + ", " + corner.getY() + ", " + corner.getZ()));
player.sendOverlayMessage(Component.literal(IrisLanguage.plain(
RuntimeUiMessages.WAND_POSITION_SET,
MessageArgument.trusted("position", first ? 1 : 2),
MessageArgument.trusted("x", corner.getX()),
MessageArgument.trusted("y", corner.getY()),
MessageArgument.trusted("z", corner.getZ())
)));
}
public static Selection selection(ServerPlayer player) {
@@ -51,6 +51,10 @@ import java.util.Locale;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Predicate;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.ModdedCommandMessages;
import art.arcane.iris.core.localization.RuntimeUiMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
public final class ModdedWorldCommands {
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
private static final Predicate<CommandSourceStack> GATE = Commands.hasPermission(Commands.LEVEL_GAMEMASTERS);
@@ -169,13 +173,13 @@ public final class ModdedWorldCommands {
if (packFolder.isDirectory()) {
return enableInstalled(source, server, dimensionId, pack, packDimension, seed);
}
IrisModdedCommands.ok(source, "Pack '" + pack + "' is not installed; downloading IrisDimensions/" + pack + "...");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_PACK_IS_NOT_INSTALLED_DOWNLOADING_IRISDIMENSIONS, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("pack2", pack)));
Thread thread = new Thread(() -> {
boolean installed = ModdedPackInstaller.install(ModdedEngineBootstrap.loader().configDir(), pack, "master",
(String line) -> server.execute(() -> IrisModdedCommands.ok(source, line)));
server.execute(() -> {
if (!installed || !packFolder.isDirectory()) {
IrisModdedCommands.fail(source, "Pack '" + pack + "' could not be downloaded; check the name or install it with /iris download " + pack + ".");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_PACK_COULD_NOT_BE_DOWNLOADED_CHECK_NAME_INSTALL_IT_WITH, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("pack2", pack)));
return;
}
enableInstalled(source, server, dimensionId, pack, packDimension, seed);
@@ -197,11 +201,11 @@ public final class ModdedWorldCommands {
ModdedDimensionManager.createPersistent(server, dimensionId, pack, packDimension, seed);
} catch (Throwable e) {
LOGGER.error("Iris world injection failed for {} (pack={} dim={})", dimensionId, pack, packDimension, e);
IrisModdedCommands.fail(source, "Failed to inject Iris world '" + dimensionId + "': " + e.getClass().getSimpleName() + (e.getMessage() == null ? "" : " - " + e.getMessage()));
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_FAILED_INJECT_IRIS_WORLD, MessageArgument.untrusted("dimensionId", dimensionId), MessageArgument.untrusted("value", e.getClass().getSimpleName()), MessageArgument.trusted("errorMessage", IrisLanguage.errorDetail(e))));
return 0;
}
IrisModdedCommands.ok(source, "Created Iris world " + dimensionId + " from pack '" + pack + "' dimension '" + packDimension + "' (seed " + seed + ").");
IrisModdedCommands.ok(source, "It is live now and re-injected on every startup. Teleport in with /iris world status or a portal; no restart required.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_CREATED_IRIS_WORLD_FROM_PACK_DIMENSION_SEED, MessageArgument.untrusted("dimensionId", dimensionId), MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("packDimension", packDimension), MessageArgument.untrusted("seed", seed)));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_IT_IS_LIVE_NOW_RE_INJECTED_ON_EVERY_STARTUP_TELEPORT));
return 1;
}
@@ -228,21 +232,21 @@ public final class ModdedWorldCommands {
ModdedDimensionManager.createPersistent(server, dimensionId, pack, packDimension, seed);
} catch (Throwable e) {
LOGGER.error("Iris primary world injection failed for {} (pack={} dim={})", dimensionId, pack, packDimension, e);
IrisModdedCommands.fail(source, "Failed to inject Iris primary world: " + e.getClass().getSimpleName() + (e.getMessage() == null ? "" : " - " + e.getMessage()));
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_FAILED_INJECT_IRIS_PRIMARY_WORLD, MessageArgument.untrusted("value", e.getClass().getSimpleName()), MessageArgument.trusted("errorMessage", IrisLanguage.errorDetail(e))));
return 0;
}
ModdedModConfig.setPrimaryWorld(dimensionId);
ModdedPrimaryWorldRouter.clear();
IrisModdedCommands.ok(source, "Iris primary world set to " + dimensionId + " (pack '" + pack + "' dimension '" + packDimension + "' seed " + seed + ").");
IrisModdedCommands.ok(source, "The vanilla overworld generator cannot be hot-swapped, so this does NOT regenerate the existing overworld.");
IrisModdedCommands.ok(source, "Instead, " + dimensionId + " is now the configured primary world: players in the vanilla overworld are routed there on join, and it re-injects on every startup.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_IRIS_PRIMARY_WORLD_SET_PACK_DIMENSION_SEED, MessageArgument.untrusted("dimensionId", dimensionId), MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("packDimension", packDimension), MessageArgument.untrusted("seed", seed)));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_VANILLA_OVERWORLD_GENERATOR_CANNOT_BE_HOT_SWAPPED_SO_THIS_DOES));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_INSTEAD_IS_NOW_CONFIGURED_PRIMARY_WORLD_PLAYERS_VANILLA_OVERWORLD_ARE, MessageArgument.untrusted("dimensionId", dimensionId)));
return 1;
}
private static int clearMainWorld(CommandSourceStack source) {
ModdedModConfig.setMainWorld("", 0L);
MainWorldService.clearOverride();
IrisModdedCommands.ok(source, "Iris main world override cleared. The overworld keeps its current generator; edit server.properties level-type and restart to change it back.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_IRIS_MAIN_WORLD_OVERRIDE_CLEARED_OVERWORLD_KEEPS_ITS_CURRENT_GENERATOR));
return 1;
}
@@ -258,7 +262,7 @@ public final class ModdedWorldCommands {
try {
seed = Long.parseLong(seedRaw.trim());
} catch (NumberFormatException e) {
IrisModdedCommands.fail(source, "Invalid seed '" + seedRaw + "'. Use a number or 'random'.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_INVALID_SEED_USE_NUMBER_RANDOM, MessageArgument.untrusted("seedRaw", seedRaw)));
return 0;
}
}
@@ -272,13 +276,13 @@ public final class ModdedWorldCommands {
if (packFolder.isDirectory()) {
return applyMainWorld(source, pack, packDimension, packRaw, seed);
}
IrisModdedCommands.ok(source, "Pack '" + pack + "' is not installed; downloading IrisDimensions/" + pack + "...");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_PACK_IS_NOT_INSTALLED_DOWNLOADING_IRISDIMENSIONS_2, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("pack2", pack)));
Thread thread = new Thread(() -> {
boolean installed = ModdedPackInstaller.install(ModdedEngineBootstrap.loader().configDir(), pack, "master",
(String line) -> server.execute(() -> IrisModdedCommands.ok(source, line)));
server.execute(() -> {
if (!installed || !packFolder.isDirectory()) {
IrisModdedCommands.fail(source, "Pack '" + pack + "' could not be downloaded; check the name or install it with /iris download " + pack + ".");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_PACK_COULD_NOT_BE_DOWNLOADED_CHECK_NAME_INSTALL_IT_WITH_2, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("pack2", pack)));
return;
}
applyMainWorld(source, pack, packDimension, packRaw, seed);
@@ -299,23 +303,23 @@ public final class ModdedWorldCommands {
}
} catch (Throwable e) {
LOGGER.error("Iris main world pack load failed for {} (dim={})", pack, packDimension, e);
IrisModdedCommands.fail(source, "Pack '" + pack + "' is not ready yet (still loading or validating). Try the command again in a moment.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_PACK_IS_NOT_READY_YET_STILL_LOADING_VALIDATING_TRY_COMMAND, MessageArgument.untrusted("pack", pack)));
return 0;
}
ModdedModConfig.setMainWorld(packRef, seed);
if (!MainWorldService.stage(packRef, seed)) {
IrisModdedCommands.fail(source, "Failed to write server.properties; check file permissions and set level-type manually.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_FAILED_WRITE_SERVER_PROPERTIES_CHECK_FILE_PERMISSIONS_SET_LEVEL_TYPE));
return 0;
}
String preset = MainWorldService.presetIdFor(packRef);
IrisModdedCommands.ok(source, "Iris main world set to '" + pack + "' (preset " + preset + ", seed " + (seed == 0L ? "random" : Long.toString(seed)) + ").");
IrisModdedCommands.ok(source, "server.properties level-type is now " + preset + ". On the next restart the overworld, nether, and end regenerate as this Iris world.");
IrisModdedCommands.ok(source, "Player data (inventories, advancements, stats) is kept; existing terrain in those dimensions is replaced. This applies once.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_IRIS_MAIN_WORLD_SET_PRESET_SEED, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("preset", preset), MessageArgument.untrusted("value", seed == 0L ? IrisLanguage.plain(RuntimeUiMessages.STATUS_RANDOM) : Long.toString(seed))));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_SERVER_PROPERTIES_LEVEL_TYPE_IS_NOW_ON_NEXT_RESTART_OVERWORLD, MessageArgument.untrusted("preset", preset)));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_PLAYER_DATA_INVENTORIES_ADVANCEMENTS_STATS_IS_KEPT_EXISTING_TERRAIN_THOSE));
if (ModdedModConfig.get().mainWorldAutoRestart()) {
IrisModdedCommands.ok(source, "mainWorldAutoRestart is enabled - stopping the server now so your restart wrapper brings it back on the new main world.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_MAINWORLDAUTORESTART_IS_ENABLED_STOPPING_SERVER_NOW_SO_YOUR_RESTART_WRAPPER));
source.getServer().halt(false);
} else {
IrisModdedCommands.ok(source, "Restart the server now to generate it. (Set mainWorldAutoRestart=true in modded.json to have Iris stop the server for you.)");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_RESTART_SERVER_NOW_GENERATE_IT_SET_MAINWORLDAUTORESTART_TRUE_MODDED_JSON));
}
return 1;
}
@@ -325,12 +329,11 @@ public final class ModdedWorldCommands {
ModdedStartup.requirePackForWorldCreation(pack);
return false;
} catch (BrokenPackException e) {
IrisModdedCommands.fail(source, "Refusing to create world '" + dimensionId
+ "' using pack '" + pack + "' because required validation failed:");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_REFUSING_CREATE_WORLD_USING_PACK_BECAUSE_REQUIRED_VALIDATION_FAILED, MessageArgument.untrusted("dimensionId", dimensionId), MessageArgument.untrusted("pack", pack)));
for (String reason : e.getReasons()) {
IrisModdedCommands.fail(source, " - " + reason);
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_MESSAGE, MessageArgument.untrusted("reason", reason)));
}
IrisModdedCommands.fail(source, "Fix the pack and run /iris pack validate " + pack + " to revalidate.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_FIX_PACK_RUN_IRIS_PACK_VALIDATE_REVALIDATE, MessageArgument.untrusted("pack", pack)));
return true;
}
}
@@ -338,13 +341,13 @@ public final class ModdedWorldCommands {
private static boolean loadPackDimension(CommandSourceStack source, String pack, String packDimension) {
File packFolder = new File(ModdedPackCommands.packsRoot(), pack);
if (!packFolder.isDirectory()) {
IrisModdedCommands.fail(source, "Pack '" + pack + "' was not found under " + ModdedPackCommands.packsRoot().getAbsolutePath());
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_PACK_WAS_NOT_FOUND_UNDER, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("value", ModdedPackCommands.packsRoot().getAbsolutePath())));
return false;
}
IrisData data = IrisData.get(packFolder);
IrisDimension dimension = data.getDimensionLoader().load(packDimension);
if (dimension == null) {
IrisModdedCommands.fail(source, "Pack '" + pack + "' does not contain dimensions/" + packDimension + ".json");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_PACK_DOES_NOT_CONTAIN_DIMENSIONS_JSON, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("packDimension", packDimension)));
return false;
}
return true;
@@ -364,7 +367,7 @@ public final class ModdedWorldCommands {
&& packDimension.matches("[A-Za-z0-9_/.-]+") && !packDimension.contains("..")) {
return true;
}
IrisModdedCommands.fail(source, "Invalid pack reference '" + pack + (pack.equals(packDimension) ? "" : ":" + packDimension) + "'. Use pack or pack:dimensionKey.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_INVALID_PACK_REFERENCE_USE_PACK_PACK_DIMENSIONKEY, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("value", (pack.equals(packDimension) ? "" : ":" + packDimension))));
return false;
}
@@ -379,7 +382,7 @@ public final class ModdedWorldCommands {
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
IrisModdedCommands.fail(source, "Invalid seed '" + seedRaw + "'. Use a number or 'random'.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_INVALID_SEED_USE_NUMBER_RANDOM_2, MessageArgument.untrusted("seedRaw", seedRaw)));
return null;
}
}
@@ -398,7 +401,7 @@ public final class ModdedWorldCommands {
removed = ModdedDimensionManager.removePersistent(server, dimensionId, wipeStorage);
} catch (Throwable e) {
LOGGER.error("Iris world removal failed for {}", dimensionId, e);
IrisModdedCommands.fail(source, "Failed to remove Iris world '" + dimensionId + "': " + e.getClass().getSimpleName() + (e.getMessage() == null ? "" : " - " + e.getMessage()));
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_FAILED_REMOVE_IRIS_WORLD, MessageArgument.untrusted("dimensionId", dimensionId), MessageArgument.untrusted("value", e.getClass().getSimpleName()), MessageArgument.trusted("errorMessage", IrisLanguage.errorDetail(e))));
return 0;
}
if (dimensionId.equals(ModdedModConfig.get().primaryWorld())) {
@@ -407,17 +410,17 @@ public final class ModdedWorldCommands {
}
if (!removed) {
if (wipeStorage) {
IrisModdedCommands.ok(source, "Iris world '" + dimensionId + "' was not loaded; cleared its persistent registry entry and wiped its stored chunk/mantle data.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_IRIS_WORLD_WAS_NOT_LOADED_CLEARED_ITS_PERSISTENT_REGISTRY_ENTRY, MessageArgument.untrusted("dimensionId", dimensionId)));
} else {
IrisModdedCommands.ok(source, "Iris world '" + dimensionId + "' was not loaded; cleared its persistent registry entry. World data on disk is kept.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_IRIS_WORLD_WAS_NOT_LOADED_CLEARED_ITS_PERSISTENT_REGISTRY_ENTRY_2, MessageArgument.untrusted("dimensionId", dimensionId)));
}
return 1;
}
if (wipeStorage) {
IrisModdedCommands.ok(source, "Deleted Iris world '" + dimensionId + "': evacuated, unloaded, chunk/mantle data wiped, and dropped from the startup registry.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_DELETED_IRIS_WORLD_EVACUATED_UNLOADED_CHUNK_MANTLE_DATA_WIPED_DROPPED, MessageArgument.untrusted("dimensionId", dimensionId)));
} else {
IrisModdedCommands.ok(source, "Disabled Iris world '" + dimensionId + "': evacuated, unloaded, and dropped from the startup registry.");
IrisModdedCommands.ok(source, "World data on disk is kept; re-enable with /iris world enable " + dimensionId + " <pack> or delete it with /iris world delete " + dimensionId + ".");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_DISABLED_IRIS_WORLD_EVACUATED_UNLOADED_DROPPED_FROM_STARTUP_REGISTRY, MessageArgument.untrusted("dimensionId", dimensionId)));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_WORLD_DATA_ON_DISK_IS_KEPT_RE_ENABLE_WITH_IRIS, MessageArgument.untrusted("dimensionId", dimensionId), MessageArgument.untrusted("dimensionId2", dimensionId)));
}
return 1;
}
@@ -428,27 +431,27 @@ public final class ModdedWorldCommands {
for (ServerLevel level : server.getAllLevels()) {
if (level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator generator) {
loaded++;
IrisModdedCommands.ok(source, "Loaded Iris level: " + level.dimension().identifier() + " -> pack '" + generator.activePack() + "' dimension '" + generator.activeDimensionKey() + "'");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_LOADED_IRIS_LEVEL_PACK_DIMENSION, MessageArgument.untrusted("value", level.dimension().identifier()), MessageArgument.untrusted("value2", generator.activePack()), MessageArgument.untrusted("value3", generator.activeDimensionKey())));
}
}
String primary = ModdedModConfig.get().primaryWorld();
if (!primary.isBlank()) {
IrisModdedCommands.ok(source, "Primary world: " + primary + (ModdedModConfig.get().routePlayersToPrimaryWorld() ? " (players routed there)" : " (routing disabled)"));
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_PRIMARY_WORLD, MessageArgument.untrusted("primary", primary), MessageArgument.trusted("value", IrisLanguage.plain(ModdedModConfig.get().routePlayersToPrimaryWorld() ? RuntimeUiMessages.PRIMARY_PLAYERS_ROUTED_SUFFIX : RuntimeUiMessages.PRIMARY_ROUTING_DISABLED_SUFFIX))));
}
if (loaded == 0) {
IrisModdedCommands.fail(source, "No Iris dimensions are currently loaded. Create one with /iris world create <name> <pack>.");
IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_NO_IRIS_DIMENSIONS_ARE_CURRENTLY_LOADED_CREATE_ONE_WITH_IRIS));
}
return loaded > 0 ? 1 : 0;
}
private static int list(CommandSourceStack source) {
List<String> dimensions = loadedIrisDimensions(source.getServer());
IrisModdedCommands.ok(source, "Loaded Iris dimensions: " + dimensions.size());
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_LOADED_IRIS_DIMENSIONS, MessageArgument.untrusted("value", dimensions.size())));
for (String dimension : dimensions) {
IrisModdedCommands.ok(source, " - " + dimension);
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_MESSAGE_2, MessageArgument.untrusted("dimension", dimension)));
}
if (dimensions.isEmpty()) {
IrisModdedCommands.ok(source, "Use /iris world create <name> <pack> to inject one without restarting.");
IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_USE_IRIS_WORLD_CREATE_NAME_PACK_INJECT_ONE_WITHOUT_RESTARTING));
}
return 1;
}
@@ -19,6 +19,7 @@
package art.arcane.iris.modded.service;
import art.arcane.iris.core.IrisSettings;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.spi.IrisPlatforms;
import net.minecraft.server.MinecraftServer;
import org.slf4j.Logger;
@@ -52,12 +53,14 @@ public final class ModdedSettingsHotloadService implements ModdedTickableService
lastPollAt = now;
long modified = settingsFile().lastModified();
if (modified == lastModified) {
IrisLanguage.update();
return;
}
if (IrisSettings.settings != null) {
IrisSettings.invalidate();
}
IrisSettings.get();
IrisLanguage.reload();
lastModified = settingsFile().lastModified();
LOGGER.info("Hotloaded settings.json");
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "Vorgenerierungs-HUD umschalten"
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "Alternar HUD de pregeneración"
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "Vaihda esigeneroinnin HUD"
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "Activer ou désactiver le HUD de prégénération"
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "הפעלה או כיבוי של תצוגת הקדם-יצירה"
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "Attiva o disattiva l'HUD di pregenerazione"
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "事前生成 HUD の切り替え"
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "사전 생성 HUD 전환"
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "Perjungti išankstinio generavimo HUD"
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "HUD voor vooraf genereren in- of uitschakelen"
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "Przełącz interfejs wstępnego generowania"
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "Alternar HUD de pré-geração"
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "Переключить HUD предварительной генерации"
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "Ön oluşturma HUD'unu aç veya kapat"
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "Bật hoặc tắt HUD tạo trước"
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "切换预生成 HUD"
}
@@ -0,0 +1,4 @@
{
"key.categories.irisworldgen.iris": "Iris",
"key.irisworldgen.toggle_pregen_hud": "切換預先生成 HUD"
}
@@ -0,0 +1,85 @@
package art.arcane.iris.modded;
import art.arcane.volmlib.util.localization.VolmitLocales;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.junit.Test;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class IrisModLanguageAssetsTest {
private static final String ROOT = "assets/irisworldgen/lang/";
private static final String TOGGLE_KEY = "key.irisworldgen.toggle_pregen_hud";
@Test
public void minecraftLanguageAssetsMatchSharedLocaleManifest() throws Exception {
JsonObject english = read("en_us");
assertEquals(2, english.size());
for (String locale : VolmitLocales.nonEnglish()) {
String minecraftLocale = VolmitLocales.minecraftCode(locale);
JsonObject translated = read(minecraftLocale);
assertEquals(minecraftLocale, english.keySet(), translated.keySet());
for (String key : english.keySet()) {
JsonElement value = translated.get(key);
assertTrue(minecraftLocale + ": " + key, value.isJsonPrimitive());
assertTrue(minecraftLocale + ": " + key, value.getAsJsonPrimitive().isString());
assertFalse(minecraftLocale + ": " + key, value.getAsString().isBlank());
}
assertFalse(
minecraftLocale + " contains an English HUD label",
english.get(TOGGLE_KEY).getAsString().equals(translated.get(TOGGLE_KEY).getAsString())
);
}
}
@Test
public void minecraftLanguageResourceSetMatchesSharedLocaleManifestAndEnglishBaseline() throws Exception {
Set<String> expected = VolmitLocales.nonEnglish().stream()
.map(VolmitLocales::minecraftCode)
.map(locale -> locale + ".json")
.collect(Collectors.toCollection(LinkedHashSet::new));
expected.add("en_us.json");
assertEquals(expected, resourceFiles());
}
private JsonObject read(String locale) throws Exception {
String resource = ROOT + locale + ".json";
InputStream input = IrisModLanguageAssetsTest.class.getClassLoader().getResourceAsStream(resource);
assertNotNull("Missing mod language asset: " + resource, input);
try (InputStream stream = input;
InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
JsonElement parsed = JsonParser.parseReader(reader);
assertTrue("Mod language asset is not an object: " + resource, parsed.isJsonObject());
return parsed.getAsJsonObject();
}
}
private Set<String> resourceFiles() throws Exception {
URL resource = IrisModLanguageAssetsTest.class.getClassLoader().getResource(ROOT);
assertNotNull("Missing mod language resource directory", resource);
assertEquals("file", resource.getProtocol());
try (Stream<Path> paths = Files.list(Path.of(resource.toURI()))) {
return paths
.filter(Files::isRegularFile)
.map(path -> path.getFileName().toString())
.collect(Collectors.toUnmodifiableSet());
}
}
}
@@ -1,3 +1,4 @@
public net.minecraft.server.MinecraftServer levels
public net.minecraft.server.MinecraftServer executor
public net.minecraft.server.MinecraftServer storageSource
public net.minecraft.core.MappedRegistry frozen