mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-07-12 18:04:01 +00:00
d
d
This commit is contained in:
@@ -19,3 +19,7 @@ dist/
|
||||
core/plugins/
|
||||
adapters/bukkit/plugin/plugins/
|
||||
adapters/*/run/
|
||||
|
||||
.codegraph/
|
||||
|
||||
plans/
|
||||
|
||||
@@ -21,10 +21,7 @@ package art.arcane.iris;
|
||||
import art.arcane.iris.engine.IrisWorldManager;
|
||||
|
||||
import art.arcane.iris.engine.framework.EngineWorldManagerProvider;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonParser;
|
||||
import art.arcane.iris.core.splash.IrisSplashPackScanner;
|
||||
import art.arcane.iris.core.IrisSettings;
|
||||
import art.arcane.iris.core.IrisWorlds;
|
||||
import art.arcane.iris.core.ServerConfigurator;
|
||||
@@ -58,8 +55,10 @@ import art.arcane.iris.engine.platform.BukkitChunkGenerator;
|
||||
import art.arcane.iris.core.safeguard.IrisSafeguard;
|
||||
import art.arcane.iris.engine.platform.PlatformChunkGenerator;
|
||||
import art.arcane.iris.platform.bukkit.BukkitPlatform;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.iris.spi.IrisPlatforms;
|
||||
import art.arcane.iris.spi.IrisServices;
|
||||
import art.arcane.iris.spi.LogLevel;
|
||||
import art.arcane.volmlib.integration.ReloadAware;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.collection.KMap;
|
||||
@@ -235,7 +234,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
getSender().sendMessage(string);
|
||||
} catch (Throwable e) {
|
||||
try {
|
||||
instance.getLogger().info(instance.getTag() + string.replaceAll("(<([^>]+)>)", ""));
|
||||
instance.getLogger().info(instance.getTag() + IrisLogging.clean(string));
|
||||
} catch (Throwable inner) {
|
||||
System.err.println("[Iris] Failed to emit log message: " + inner.getMessage());
|
||||
inner.printStackTrace(System.err);
|
||||
@@ -377,19 +376,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
}
|
||||
|
||||
private static String safeFormat(String format, Object... args) {
|
||||
if (format == null) {
|
||||
return "null";
|
||||
}
|
||||
|
||||
if (args == null || args.length == 0) {
|
||||
return format;
|
||||
}
|
||||
|
||||
try {
|
||||
return String.format(format, args);
|
||||
} catch (IllegalFormatException ignored) {
|
||||
return format;
|
||||
}
|
||||
return IrisLogging.format(format, args);
|
||||
}
|
||||
|
||||
public static void later(NastyRunnable object) {
|
||||
@@ -555,14 +542,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
BukkitPlatform.hostPlugin(this);
|
||||
BukkitPlatform.hostConsoleSender(Iris::getSender);
|
||||
BukkitPlatform.hostBridge(new BukkitPlatform.HostBridge(
|
||||
(level, message) -> {
|
||||
switch (level) {
|
||||
case DEBUG -> Iris.debug(message);
|
||||
case INFO -> Iris.info(message);
|
||||
case WARN -> Iris.warn(message);
|
||||
case ERROR -> Iris.error(message);
|
||||
}
|
||||
},
|
||||
Iris::bridgeLog,
|
||||
Iris::msg,
|
||||
Iris::reportError,
|
||||
(event) -> Iris.callEvent((org.bukkit.event.Event) event),
|
||||
@@ -574,6 +554,16 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
SlimJar.load();
|
||||
}
|
||||
|
||||
private static void bridgeLog(LogLevel level, String message) {
|
||||
LogLevel target = level == null ? LogLevel.INFO : level;
|
||||
switch (target) {
|
||||
case DEBUG -> Iris.debug(message);
|
||||
case INFO -> Iris.info(message);
|
||||
case WARN -> Iris.warn(message);
|
||||
case ERROR -> Iris.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
private void enable() {
|
||||
PaperLibBootstrap.install();
|
||||
SimdSupport.install();
|
||||
@@ -1279,49 +1269,24 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
}
|
||||
|
||||
static List<SplashPackMetadata> collectSplashPacks(File packFolder) {
|
||||
if (packFolder == null || !packFolder.isDirectory()) {
|
||||
List<IrisSplashPackScanner.SplashPackMetadata> scanned = IrisSplashPackScanner.collect(packFolder, Iris::reportError);
|
||||
if (scanned.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
File[] folders = packFolder.listFiles(File::isDirectory);
|
||||
if (folders == null || folders.length == 0) {
|
||||
return Collections.emptyList();
|
||||
List<SplashPackMetadata> packs = new ArrayList<>(scanned.size());
|
||||
for (IrisSplashPackScanner.SplashPackMetadata metadata : scanned) {
|
||||
packs.add(new SplashPackMetadata(metadata.name(), metadata.version()));
|
||||
}
|
||||
|
||||
List<SplashPackMetadata> packs = new ArrayList<>();
|
||||
for (File folder : folders) {
|
||||
SplashPackMetadata metadata = readSplashPack(folder);
|
||||
if (metadata != null) {
|
||||
packs.add(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
packs.sort(Comparator.comparing(SplashPackMetadata::name));
|
||||
return packs;
|
||||
}
|
||||
|
||||
static SplashPackMetadata readSplashPack(File pack) {
|
||||
if (pack == null || !pack.isDirectory()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String dimName = pack.getName();
|
||||
File dimensionFile = new File(pack, "dimensions/" + dimName + ".json");
|
||||
if (!dimensionFile.isFile()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try (FileReader r = new FileReader(dimensionFile)) {
|
||||
JsonObject json = JsonParser.parseReader(r).getAsJsonObject();
|
||||
if (!json.has("version")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SplashPackMetadata(dimName, json.get("version").getAsString());
|
||||
} catch (IOException | JsonParseException ex) {
|
||||
reportError("Failed to read splash metadata for dimension pack \"" + dimName + "\".", ex);
|
||||
IrisSplashPackScanner.SplashPackMetadata metadata = IrisSplashPackScanner.read(pack, Iris::reportError);
|
||||
if (metadata == null) {
|
||||
return null;
|
||||
}
|
||||
return new SplashPackMetadata(metadata.name(), metadata.version());
|
||||
}
|
||||
|
||||
private void printPack(SplashPackMetadata pack) {
|
||||
|
||||
@@ -20,6 +20,7 @@ package art.arcane.iris.fabric;
|
||||
|
||||
import art.arcane.iris.modded.IrisModdedChunkGenerator;
|
||||
import art.arcane.iris.modded.ModdedEngineBootstrap;
|
||||
import art.arcane.iris.modded.ModdedIrisLog;
|
||||
import art.arcane.iris.modded.ModdedParityProbe;
|
||||
import art.arcane.iris.modded.ModdedWorldCheck;
|
||||
import art.arcane.iris.modded.ModdedWorldEngines;
|
||||
@@ -49,12 +50,8 @@ import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public final class IrisFabricBootstrap implements ModInitializer {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
ModdedEngineBootstrap.initialize(new FabricModdedLoader());
|
||||
@@ -65,12 +62,12 @@ public final class IrisFabricBootstrap implements ModInitializer {
|
||||
String minecraftVersion = loader.getModContainer("minecraft")
|
||||
.map((ModContainer container) -> container.getMetadata().getVersion().getFriendlyString())
|
||||
.orElse("unknown");
|
||||
LOGGER.info("Iris {} bootstrapping on Minecraft {} (Fabric)", modVersion, minecraftVersion);
|
||||
ModdedIrisLog.info("Iris " + modVersion + " bootstrapping on Minecraft " + minecraftVersion + " (Fabric)");
|
||||
|
||||
ModdedEngineBootstrap.selfTest(IrisFabricBootstrap.class.getClassLoader());
|
||||
ModdedEngineBootstrap.bind();
|
||||
Registry.register(BuiltInRegistries.CHUNK_GENERATOR, Identifier.fromNamespaceAndPath("irisworldgen", "iris"), IrisModdedChunkGenerator.CODEC);
|
||||
LOGGER.info("Iris chunk generator registered as irisworldgen:iris");
|
||||
ModdedIrisLog.info("Iris chunk generator registered as irisworldgen:iris");
|
||||
ServerLifecycleEvents.SERVER_STOPPING.register((MinecraftServer server) -> {
|
||||
ModdedObjectUndo.clearAll();
|
||||
ModdedWandService.clearAll();
|
||||
@@ -85,13 +82,13 @@ public final class IrisFabricBootstrap implements ModInitializer {
|
||||
|
||||
String parity = System.getProperty("iris.parity");
|
||||
if (parity != null) {
|
||||
LOGGER.info("Iris parity probe armed: {}", parity);
|
||||
ModdedIrisLog.info("Iris parity probe armed: " + parity);
|
||||
ModdedParityProbe.schedule(parity);
|
||||
}
|
||||
|
||||
String worldCheck = System.getProperty("iris.worldcheck");
|
||||
if (worldCheck != null) {
|
||||
LOGGER.info("Iris world check armed");
|
||||
ModdedIrisLog.info("Iris world check armed");
|
||||
ModdedWorldCheck.schedule();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package art.arcane.iris.forge;
|
||||
|
||||
import art.arcane.iris.modded.IrisModdedChunkGenerator;
|
||||
import art.arcane.iris.modded.ModdedEngineBootstrap;
|
||||
import art.arcane.iris.modded.ModdedIrisLog;
|
||||
import art.arcane.iris.modded.ModdedParityProbe;
|
||||
import art.arcane.iris.modded.ModdedWorldCheck;
|
||||
import art.arcane.iris.modded.ModdedWorldEngines;
|
||||
@@ -42,20 +43,16 @@ import net.minecraftforge.fml.loading.VersionInfo;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Mod("irisworldgen")
|
||||
public final class IrisForgeBootstrap {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
|
||||
|
||||
public IrisForgeBootstrap(FMLJavaModLoadingContext context) {
|
||||
ModdedEngineBootstrap.initialize(new ForgeModdedLoader());
|
||||
String modVersion = ModList.getModContainerById("irisworldgen")
|
||||
.map((ModContainer container) -> container.getModInfo().getVersion().toString())
|
||||
.orElse("unknown");
|
||||
VersionInfo versionInfo = FMLLoader.versionInfo();
|
||||
LOGGER.info("Iris {} bootstrapping on Minecraft {} (Forge {})", modVersion, versionInfo.mcVersion(), versionInfo.forgeVersion());
|
||||
ModdedIrisLog.info("Iris " + modVersion + " bootstrapping on Minecraft " + versionInfo.mcVersion() + " (Forge " + versionInfo.forgeVersion() + ")");
|
||||
|
||||
ModdedEngineBootstrap.selfTest(IrisForgeBootstrap.class.getClassLoader());
|
||||
ModdedEngineBootstrap.bind();
|
||||
@@ -63,7 +60,7 @@ public final class IrisForgeBootstrap {
|
||||
DeferredRegister<MapCodec<? extends ChunkGenerator>> chunkGenerators = DeferredRegister.create(Registries.CHUNK_GENERATOR, "irisworldgen");
|
||||
chunkGenerators.register("iris", () -> IrisModdedChunkGenerator.CODEC);
|
||||
chunkGenerators.register(context.getModBusGroup());
|
||||
LOGGER.info("Iris chunk generator registered as irisworldgen:iris");
|
||||
ModdedIrisLog.info("Iris chunk generator registered as irisworldgen:iris");
|
||||
|
||||
ServerStoppingEvent.BUS.addListener((ServerStoppingEvent event) -> {
|
||||
ModdedObjectUndo.clearAll();
|
||||
@@ -79,13 +76,13 @@ public final class IrisForgeBootstrap {
|
||||
|
||||
String parity = System.getProperty("iris.parity");
|
||||
if (parity != null) {
|
||||
LOGGER.info("Iris parity probe armed: {}", parity);
|
||||
ModdedIrisLog.info("Iris parity probe armed: " + parity);
|
||||
ModdedParityProbe.schedule(parity);
|
||||
}
|
||||
|
||||
String worldCheck = System.getProperty("iris.worldcheck");
|
||||
if (worldCheck != null) {
|
||||
LOGGER.info("Iris world check armed");
|
||||
ModdedIrisLog.info("Iris world check armed");
|
||||
ModdedWorldCheck.schedule();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -82,7 +82,7 @@ public final class ModdedEngineBootstrap {
|
||||
throw new IllegalStateException("Iris core self-test failed: only " + loadedClasses + " of " + CORE_SELF_TEST_CLASSES.length + " engine classes initialized");
|
||||
}
|
||||
|
||||
LOGGER.info("Iris core loaded ({} classes ok)", loadedClasses);
|
||||
ModdedIrisLog.info("Iris core loaded (" + loadedClasses + " classes ok)");
|
||||
}
|
||||
|
||||
public static ModdedPlatform bind() {
|
||||
@@ -105,6 +105,7 @@ public final class ModdedEngineBootstrap {
|
||||
IrisServices.register(PreservationRegistry.class, new InertPreservation());
|
||||
IrisServices.register(EngineWorldManagerProvider.class, (EngineWorldManagerProvider) (Engine engine) -> new InertWorldManager());
|
||||
platform = created;
|
||||
ModdedIrisSplash.print(boundLoader);
|
||||
return created;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.IrisSettings;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.iris.spi.LogLevel;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public final class ModdedIrisLog {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
|
||||
private static volatile boolean DEBUG_SETTING_WARNING_LOGGED;
|
||||
|
||||
private ModdedIrisLog() {
|
||||
}
|
||||
|
||||
public static void log(LogLevel level, String message) {
|
||||
LogLevel target = level == null ? LogLevel.INFO : level;
|
||||
switch (target) {
|
||||
case DEBUG -> debug(message);
|
||||
case INFO -> info(message);
|
||||
case WARN -> warn(message);
|
||||
case ERROR -> error(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void debug(String message) {
|
||||
if (!debugEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.debug(clean(message));
|
||||
}
|
||||
|
||||
public static void info(String message) {
|
||||
LOGGER.info(clean(message));
|
||||
}
|
||||
|
||||
public static void warn(String message) {
|
||||
LOGGER.warn(clean(message));
|
||||
}
|
||||
|
||||
public static void error(String message) {
|
||||
LOGGER.error(clean(message));
|
||||
}
|
||||
|
||||
public static void error(String message, Throwable error) {
|
||||
if (error == null) {
|
||||
error(message);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.error(clean(message), error);
|
||||
}
|
||||
|
||||
public static String clean(String message) {
|
||||
return IrisLogging.clean(message);
|
||||
}
|
||||
|
||||
private static boolean debugEnabled() {
|
||||
try {
|
||||
IrisSettings settings = IrisSettings.settings != null ? IrisSettings.settings : IrisSettings.get();
|
||||
return settings != null && settings.getGeneral() != null && settings.getGeneral().isDebug();
|
||||
} catch (Throwable error) {
|
||||
warnDebugSetting(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void warnDebugSetting(Throwable error) {
|
||||
if (DEBUG_SETTING_WARNING_LOGGED) {
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_SETTING_WARNING_LOGGED = true;
|
||||
LOGGER.warn("Iris debug logging setting could not be read", error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.BuildConstants;
|
||||
import art.arcane.iris.core.IrisSettings;
|
||||
import art.arcane.iris.core.splash.IrisSplashPackScanner;
|
||||
import art.arcane.iris.core.splash.IrisSplashPackScanner.SplashPackMetadata;
|
||||
import art.arcane.iris.core.splash.IrisSplashRenderer;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
public final class ModdedIrisSplash {
|
||||
|
||||
private ModdedIrisSplash() {
|
||||
}
|
||||
|
||||
public static void print(ModdedLoader loader) {
|
||||
printPacks(loader);
|
||||
if (isLogoEnabled()) {
|
||||
printLogo(loader);
|
||||
}
|
||||
}
|
||||
|
||||
private static void printPacks(ModdedLoader loader) {
|
||||
File packFolder = loader.configDir().resolve("irisworldgen").resolve("packs").toFile();
|
||||
List<SplashPackMetadata> packs = IrisSplashPackScanner.collect(packFolder, IrisLogging::reportError);
|
||||
if (packs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
IrisLogging.info("Custom Dimensions: " + packs.size());
|
||||
for (SplashPackMetadata pack : packs) {
|
||||
IrisLogging.info(" " + pack.name() + " v" + pack.version());
|
||||
}
|
||||
}
|
||||
|
||||
private static void printLogo(ModdedLoader loader) {
|
||||
String padding = " ".repeat(4);
|
||||
String version = loader.modVersion();
|
||||
String releaseTrain = getReleaseTrain(version);
|
||||
String startupDate = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
|
||||
int javaVersion = getJavaVersion();
|
||||
String[] splash = IrisSplashRenderer.renderPlain();
|
||||
String[] info = new String[]{
|
||||
"",
|
||||
" Iris, Dimension Engine [" + releaseTrain + " RC.1.1.6]",
|
||||
" Version: " + version,
|
||||
" By: Volmit Software (Arcane Arts)",
|
||||
" Server: " + loader.platformName() + " / Minecraft " + loader.minecraftVersion(),
|
||||
" Java: " + javaVersion + " | Date: " + startupDate,
|
||||
" Commit: " + BuildConstants.COMMIT + "/" + BuildConstants.ENVIRONMENT,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
};
|
||||
|
||||
StringBuilder builder = new StringBuilder("\n\n");
|
||||
for (int i = 0; i < splash.length; i++) {
|
||||
builder.append(padding).append(splash[i]).append(info[i]).append('\n');
|
||||
}
|
||||
|
||||
IrisLogging.info(builder.toString());
|
||||
}
|
||||
|
||||
private static boolean isLogoEnabled() {
|
||||
try {
|
||||
return IrisSettings.get().getGeneral().isSplashLogoStartup();
|
||||
} catch (Throwable error) {
|
||||
IrisLogging.warn("Iris splash setting could not be read: " + error.getClass().getSimpleName());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getJavaVersion() {
|
||||
String version = System.getProperty("java.version");
|
||||
if (version.startsWith("1.")) {
|
||||
version = version.substring(2, 3);
|
||||
} else {
|
||||
int dot = version.indexOf('.');
|
||||
if (dot != -1) {
|
||||
version = version.substring(0, dot);
|
||||
}
|
||||
}
|
||||
return Integer.parseInt(version);
|
||||
}
|
||||
|
||||
private static String getReleaseTrain(String version) {
|
||||
String value = version == null ? "unknown" : version;
|
||||
int suffixIndex = value.indexOf('-');
|
||||
if (suffixIndex >= 0) {
|
||||
value = value.substring(0, suffixIndex);
|
||||
}
|
||||
String[] split = value.split("\\.");
|
||||
if (split.length >= 2) {
|
||||
return split[0] + "." + split[1];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -26,14 +26,11 @@ import art.arcane.iris.spi.PlatformRegistries;
|
||||
import art.arcane.iris.spi.PlatformScheduler;
|
||||
import art.arcane.iris.spi.PlatformStructureHooks;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class ModdedPlatform implements IrisPlatform {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
|
||||
private static volatile Consumer<Throwable> ERROR_SINK = null;
|
||||
|
||||
private final ModdedLoader loader;
|
||||
@@ -135,17 +132,12 @@ public final class ModdedPlatform implements IrisPlatform {
|
||||
|
||||
@Override
|
||||
public void log(LogLevel level, String message) {
|
||||
switch (level) {
|
||||
case DEBUG -> LOGGER.debug(message);
|
||||
case INFO -> LOGGER.info(message);
|
||||
case WARN -> LOGGER.warn(message);
|
||||
case ERROR -> LOGGER.error(message);
|
||||
}
|
||||
ModdedIrisLog.log(level, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void msg(String message) {
|
||||
LOGGER.info(message);
|
||||
ModdedIrisLog.info(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -156,7 +148,7 @@ public final class ModdedPlatform implements IrisPlatform {
|
||||
return;
|
||||
}
|
||||
if (error != null) {
|
||||
LOGGER.error("Iris reported error", error);
|
||||
ModdedIrisLog.error("Iris reported error", error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-2
@@ -28,6 +28,7 @@ import art.arcane.iris.modded.IrisModdedChunkGenerator;
|
||||
import art.arcane.iris.modded.ModdedEngineBootstrap;
|
||||
import art.arcane.iris.modded.ModdedLoader;
|
||||
import art.arcane.iris.modded.ModdedPackInstaller;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.volmlib.util.collection.KMap;
|
||||
import art.arcane.volmlib.util.math.Position2;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
@@ -42,6 +43,7 @@ import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.commands.SharedSuggestionProvider;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
@@ -84,7 +86,7 @@ public final class IrisModdedCommands {
|
||||
LiteralCommandNode<CommandSourceStack> root = dispatcher.register(rootTree());
|
||||
dispatcher.register(Commands.literal("ir").redirect(root));
|
||||
dispatcher.register(Commands.literal("irs").redirect(root));
|
||||
LOGGER.info("Iris /iris command tree registered");
|
||||
IrisLogging.info("Iris /iris command tree registered");
|
||||
}
|
||||
|
||||
private static LiteralArgumentBuilder<CommandSourceStack> rootTree() {
|
||||
@@ -322,7 +324,7 @@ public final class IrisModdedCommands {
|
||||
}
|
||||
|
||||
private static int pregenStatus(CommandSourceStack source) {
|
||||
String status = ModdedPregenJob.status();
|
||||
Component status = ModdedPregenJob.statusComponent();
|
||||
if (status == null) {
|
||||
fail(source, "No active pregeneration task.");
|
||||
return 0;
|
||||
@@ -747,6 +749,10 @@ public final class IrisModdedCommands {
|
||||
ModdedCommandFeedback.ok(source, message);
|
||||
}
|
||||
|
||||
static void ok(CommandSourceStack source, Component component) {
|
||||
ModdedCommandFeedback.ok(source, component);
|
||||
}
|
||||
|
||||
static void fail(CommandSourceStack source, String message) {
|
||||
ModdedCommandFeedback.fail(source, message);
|
||||
}
|
||||
|
||||
+84
@@ -20,7 +20,12 @@ package art.arcane.iris.modded.command;
|
||||
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
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 net.minecraft.network.chat.Style;
|
||||
import net.minecraft.network.chat.TextColor;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
@@ -31,6 +36,25 @@ import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
final class ModdedCommandFeedback {
|
||||
static final int IRIS = 0x1BB19E;
|
||||
static final int HEADER_A = 0x34EB6B;
|
||||
static final int HEADER_B = 0x32BFAD;
|
||||
static final int BACK = 0x6FE98F;
|
||||
static final int DARK_GREEN = 0x46826A;
|
||||
static final int DESCRIPTION_ICON = 0x3FE05A;
|
||||
static final int DESCRIPTION = 0x6AD97D;
|
||||
static final int USAGE_ICON = 0xBBE03F;
|
||||
static final int USAGE = 0xA8E0A2;
|
||||
static final int EXAMPLE_ICON = 0xC2F7D2;
|
||||
static final int PARAMETER = 0x5EF288;
|
||||
static final int PARAMETER_ALT = 0x32BFAD;
|
||||
static final int OPTIONAL = 0x4F4F4F;
|
||||
static final int CATEGORY = 0x9DE5B6;
|
||||
static final int REQUIRED = 0xDB4321;
|
||||
static final int REQUIRED_TEXT = 0xFAA796;
|
||||
static final int HOVER_TYPE = 0x8AD9AF;
|
||||
static final int VALUE = 0xC2F7D2;
|
||||
static final int PAGE_LINE_LENGTH = 75;
|
||||
private static final long MESSAGE_SOUND_COOLDOWN_MS = 650L;
|
||||
private static final long TAB_SOUND_COOLDOWN_MS = 175L;
|
||||
private static final Map<UUID, Long> MESSAGE_SOUNDS = new ConcurrentHashMap<>();
|
||||
@@ -44,6 +68,11 @@ final class ModdedCommandFeedback {
|
||||
playSuccess(source);
|
||||
}
|
||||
|
||||
static void ok(CommandSourceStack source, Component component) {
|
||||
source.sendSuccess(() -> component, false);
|
||||
playSuccess(source);
|
||||
}
|
||||
|
||||
static void fail(CommandSourceStack source, String message) {
|
||||
source.sendFailure(Component.literal(message).withStyle(ChatFormatting.RED));
|
||||
playFailure(source);
|
||||
@@ -53,6 +82,61 @@ final class ModdedCommandFeedback {
|
||||
source.sendSuccess(() -> component, false);
|
||||
}
|
||||
|
||||
static void clear(CommandSourceStack source) {
|
||||
if (source.getPlayer() != null) {
|
||||
send(source, Component.literal("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"));
|
||||
}
|
||||
}
|
||||
|
||||
static MutableComponent header(String title) {
|
||||
MutableComponent header = Component.empty();
|
||||
header.append(text(" ".repeat(18), HEADER_A, false, true));
|
||||
header.append(text(" " + title + " ", IRIS, true, false));
|
||||
header.append(text(" ".repeat(18), HEADER_B, false, true));
|
||||
return header;
|
||||
}
|
||||
|
||||
static MutableComponent footer() {
|
||||
return text(" ".repeat(PAGE_LINE_LENGTH), HEADER_B, false, true);
|
||||
}
|
||||
|
||||
static MutableComponent text(String value, int color) {
|
||||
return text(value, color, false, false);
|
||||
}
|
||||
|
||||
static MutableComponent text(String value, int color, boolean bold, boolean strikethrough) {
|
||||
return Component.literal(value).withStyle((Style style) -> {
|
||||
Style next = style.withColor(TextColor.fromRgb(color));
|
||||
if (bold) {
|
||||
next = next.withBold(true);
|
||||
}
|
||||
if (strikethrough) {
|
||||
next = next.withStrikethrough(true);
|
||||
}
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
static MutableComponent button(String label, String command, String hover, boolean runCommand) {
|
||||
ClickEvent clickEvent = runCommand ? new ClickEvent.RunCommand(command) : new ClickEvent.SuggestCommand(command);
|
||||
MutableComponent hoverText = text(hover, DESCRIPTION);
|
||||
return text(label, PARAMETER_ALT, true, false).withStyle((Style style) -> style
|
||||
.withClickEvent(clickEvent)
|
||||
.withHoverEvent(new HoverEvent.ShowText(hoverText)));
|
||||
}
|
||||
|
||||
static MutableComponent progressBar(double percent, int width) {
|
||||
double clamped = Math.max(0D, Math.min(100D, percent));
|
||||
int filled = (int) Math.round((clamped / 100D) * width);
|
||||
MutableComponent bar = Component.empty();
|
||||
bar.append(text("[", DARK_GREEN));
|
||||
for (int i = 0; i < width; i++) {
|
||||
bar.append(text(i < filled ? "|" : "·", i < filled ? PARAMETER : OPTIONAL));
|
||||
}
|
||||
bar.append(text("]", DARK_GREEN));
|
||||
return bar;
|
||||
}
|
||||
|
||||
static void tab(CommandSourceStack source) {
|
||||
ServerPlayer player = source.getPlayer();
|
||||
if (player == null || !claim(TAB_SOUNDS, player.getUUID(), TAB_SOUND_COOLDOWN_MS)) {
|
||||
|
||||
+182
-33
@@ -18,17 +18,32 @@
|
||||
|
||||
package art.arcane.iris.modded.command;
|
||||
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static art.arcane.iris.modded.command.ModdedCommandFeedback.BACK;
|
||||
import static art.arcane.iris.modded.command.ModdedCommandFeedback.CATEGORY;
|
||||
import static art.arcane.iris.modded.command.ModdedCommandFeedback.DARK_GREEN;
|
||||
import static art.arcane.iris.modded.command.ModdedCommandFeedback.DESCRIPTION;
|
||||
import static art.arcane.iris.modded.command.ModdedCommandFeedback.DESCRIPTION_ICON;
|
||||
import static art.arcane.iris.modded.command.ModdedCommandFeedback.EXAMPLE_ICON;
|
||||
import static art.arcane.iris.modded.command.ModdedCommandFeedback.HOVER_TYPE;
|
||||
import static art.arcane.iris.modded.command.ModdedCommandFeedback.OPTIONAL;
|
||||
import static art.arcane.iris.modded.command.ModdedCommandFeedback.PARAMETER;
|
||||
import static art.arcane.iris.modded.command.ModdedCommandFeedback.PARAMETER_ALT;
|
||||
import static art.arcane.iris.modded.command.ModdedCommandFeedback.REQUIRED;
|
||||
import static art.arcane.iris.modded.command.ModdedCommandFeedback.REQUIRED_TEXT;
|
||||
import static art.arcane.iris.modded.command.ModdedCommandFeedback.USAGE;
|
||||
import static art.arcane.iris.modded.command.ModdedCommandFeedback.USAGE_ICON;
|
||||
|
||||
final class ModdedCommandHelp {
|
||||
private static final Map<String, List<Entry>> SECTIONS = new LinkedHashMap<>();
|
||||
|
||||
@@ -89,7 +104,7 @@ final class ModdedCommandHelp {
|
||||
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", "", "Explain modded studio workflow"),
|
||||
Entry.command("open", "<pack> [seed]", "Open or prepare a dimension pack studio workflow"),
|
||||
Entry.command("close", "", "Explain modded studio workflow"),
|
||||
Entry.command("vscode", "", "Explain editor workflow"),
|
||||
Entry.command("update", "", "Explain workspace regeneration workflow"),
|
||||
@@ -143,53 +158,187 @@ final class ModdedCommandHelp {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sendHeader(source, normalized.isEmpty() ? "Iris Commands" : "/iris " + normalized);
|
||||
ModdedCommandFeedback.clear(source);
|
||||
|
||||
sendHeader(source, normalized);
|
||||
if (!normalized.isEmpty()) {
|
||||
ModdedCommandFeedback.send(source, clickable(" < Back", "/iris", "/iris", "Back to Iris command groups", true));
|
||||
ModdedCommandFeedback.send(source, backButton(normalized));
|
||||
}
|
||||
for (Entry entry : entries) {
|
||||
ModdedCommandFeedback.send(source, line(normalized, entry));
|
||||
}
|
||||
ModdedCommandFeedback.ok(source, "Click a group to open it, or click a command to place it in chat.");
|
||||
ModdedCommandFeedback.ok(source, footer());
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static void sendHeader(CommandSourceStack source, String title) {
|
||||
MutableComponent header = Component.literal("========== ").withStyle(ChatFormatting.DARK_GREEN, ChatFormatting.STRIKETHROUGH)
|
||||
.append(Component.literal(" " + title + " ").withStyle(ChatFormatting.GREEN, ChatFormatting.BOLD))
|
||||
.append(Component.literal(" ==========").withStyle(ChatFormatting.DARK_GREEN, ChatFormatting.STRIKETHROUGH));
|
||||
ModdedCommandFeedback.send(source, header);
|
||||
private static void sendHeader(CommandSourceStack source, String path) {
|
||||
String title = path.isEmpty() ? "/iris" : "/iris " + path;
|
||||
ModdedCommandFeedback.send(source, ModdedCommandFeedback.header(title));
|
||||
}
|
||||
|
||||
private static MutableComponent backButton(String path) {
|
||||
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
|
||||
.withClickEvent(new ClickEvent.RunCommand(command))
|
||||
.withHoverEvent(new HoverEvent.ShowText(hover)));
|
||||
}
|
||||
|
||||
private static MutableComponent line(String path, Entry entry) {
|
||||
String basePath = path.isEmpty() ? "" : " " + path;
|
||||
String command = "/iris" + basePath + " " + entry.name();
|
||||
String suggestion = entry.usage().isBlank() ? command : command + " " + entry.usage();
|
||||
String hover = entry.description() + (entry.usage().isBlank() ? "" : "\nUsage: " + suggestion);
|
||||
MutableComponent row = Component.literal(" ");
|
||||
row.append(clickable(entry.name(), command, suggestion, hover, entry.group()));
|
||||
if (entry.aliases().length > 0) {
|
||||
row.append(Component.literal(" " + String.join(", ", entry.aliases())).withStyle(ChatFormatting.DARK_GREEN));
|
||||
}
|
||||
row.append(Component.literal(" - ").withStyle(ChatFormatting.DARK_GRAY));
|
||||
row.append(Component.literal(entry.description()).withStyle(ChatFormatting.GRAY));
|
||||
MutableComponent row = Component.empty();
|
||||
row.append(clickableCommand(path, entry));
|
||||
row.append(nodes(entry));
|
||||
return row;
|
||||
}
|
||||
|
||||
private static MutableComponent clickable(String label, String command, String suggestion, String hover, boolean run) {
|
||||
Component hoverText = Component.literal(hover).withStyle(ChatFormatting.GREEN);
|
||||
if (run) {
|
||||
return Component.literal(label).withStyle((style) -> style
|
||||
.withColor(ChatFormatting.AQUA)
|
||||
.withBold(true)
|
||||
.withClickEvent(new ClickEvent.RunCommand(command))
|
||||
.withHoverEvent(new HoverEvent.ShowText(hoverText)));
|
||||
private static MutableComponent clickableCommand(String path, Entry entry) {
|
||||
String parent = path.isEmpty() ? "/iris" : "/iris " + path;
|
||||
String command = parent + " " + entry.name();
|
||||
String suggestion = entry.usage().isBlank() ? command : command + " " + entry.usage();
|
||||
ClickEvent clickEvent = entry.group() ? new ClickEvent.RunCommand(command) : new ClickEvent.SuggestCommand(suggestion);
|
||||
MutableComponent hover = entryHover(path, entry, suggestion);
|
||||
MutableComponent display = Component.empty();
|
||||
display.append(text(parent + " >", 0xFFFFFF));
|
||||
display.append(text("⇀", DARK_GREEN));
|
||||
display.append(text(" " + entry.name(), PARAMETER_ALT));
|
||||
return display.withStyle((style) -> style
|
||||
.withClickEvent(clickEvent)
|
||||
.withHoverEvent(new HoverEvent.ShowText(hover)));
|
||||
}
|
||||
|
||||
private static MutableComponent nodes(Entry entry) {
|
||||
if (entry.group()) {
|
||||
return text(" - Category of Commands", CATEGORY);
|
||||
}
|
||||
|
||||
return Component.literal(label).withStyle((style) -> style
|
||||
.withColor(ChatFormatting.GREEN)
|
||||
.withClickEvent(new ClickEvent.SuggestCommand(suggestion))
|
||||
.withHoverEvent(new HoverEvent.ShowText(hoverText)));
|
||||
List<String> tokens = usageTokens(entry.usage());
|
||||
if (tokens.isEmpty()) {
|
||||
return Component.empty();
|
||||
}
|
||||
|
||||
MutableComponent nodes = Component.empty();
|
||||
for (String token : tokens) {
|
||||
nodes.append(Component.literal(" "));
|
||||
nodes.append(parameter(token));
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private static MutableComponent parameter(String token) {
|
||||
String name = parameterName(token);
|
||||
boolean required = token.startsWith("<");
|
||||
MutableComponent title = Component.empty();
|
||||
if (required) {
|
||||
title.append(text("[", REQUIRED, true, false));
|
||||
title.append(text(name, PARAMETER, false, false));
|
||||
title.append(text("]", REQUIRED, true, false));
|
||||
} else {
|
||||
title.append(text("⊰", OPTIONAL));
|
||||
title.append(text(name, PARAMETER));
|
||||
title.append(text("⊱", OPTIONAL));
|
||||
}
|
||||
|
||||
MutableComponent hover = Component.empty();
|
||||
hover.append(text(name, PARAMETER));
|
||||
hover.append(Component.literal("\n"));
|
||||
hover.append(text("✎ ", DESCRIPTION_ICON));
|
||||
hover.append(text("Command parameter", DESCRIPTION));
|
||||
hover.append(Component.literal("\n"));
|
||||
if (required) {
|
||||
hover.append(text("⚠ ", REQUIRED));
|
||||
hover.append(text("This parameter is required.", REQUIRED_TEXT));
|
||||
} else {
|
||||
hover.append(text("✔ ", DESCRIPTION_ICON));
|
||||
hover.append(text("This parameter is 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));
|
||||
|
||||
return title.withStyle((style) -> style.withHoverEvent(new HoverEvent.ShowText(hover)));
|
||||
}
|
||||
|
||||
private static MutableComponent entryHover(String path, Entry entry, String suggestion) {
|
||||
MutableComponent hover = Component.empty();
|
||||
hover.append(text(names(entry), PARAMETER));
|
||||
hover.append(Component.literal("\n"));
|
||||
hover.append(text("✎ ", DESCRIPTION_ICON));
|
||||
hover.append(text(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));
|
||||
} else if (entry.usage().isBlank()) {
|
||||
hover.append(text("There are no parameters. Click to type command.", USAGE));
|
||||
} else {
|
||||
hover.append(text("Hover over all of the parameters to learn more.", USAGE));
|
||||
hover.append(Component.literal("\n"));
|
||||
hover.append(text("✦ ", EXAMPLE_ICON));
|
||||
hover.append(text(suggestion, PARAMETER));
|
||||
}
|
||||
|
||||
String parent = path.isEmpty() ? "/iris" : "/iris " + path;
|
||||
if (entry.aliases().length > 0) {
|
||||
hover.append(Component.literal("\n"));
|
||||
hover.append(text("Aliases: ", DARK_GREEN));
|
||||
List<String> aliases = new ArrayList<>(entry.aliases().length);
|
||||
for (String alias : entry.aliases()) {
|
||||
aliases.add(parent + " " + alias);
|
||||
}
|
||||
hover.append(text(String.join(", ", aliases), PARAMETER_ALT));
|
||||
}
|
||||
return hover;
|
||||
}
|
||||
|
||||
private static MutableComponent footer() {
|
||||
return ModdedCommandFeedback.footer();
|
||||
}
|
||||
|
||||
private static MutableComponent text(String value, int color) {
|
||||
return ModdedCommandFeedback.text(value, color, false, false);
|
||||
}
|
||||
|
||||
private static MutableComponent text(String value, int color, boolean bold, boolean strikethrough) {
|
||||
return ModdedCommandFeedback.text(value, color, bold, strikethrough);
|
||||
}
|
||||
|
||||
private static List<String> usageTokens(String usage) {
|
||||
if (usage == null || usage.isBlank()) {
|
||||
return List.of();
|
||||
}
|
||||
return List.of(usage.trim().split("\\s+"));
|
||||
}
|
||||
|
||||
private static String names(Entry entry) {
|
||||
if (entry.aliases().length == 0) {
|
||||
return entry.name();
|
||||
}
|
||||
|
||||
List<String> names = new ArrayList<>(entry.aliases().length + 1);
|
||||
names.add(entry.name());
|
||||
for (String alias : entry.aliases()) {
|
||||
names.add(alias);
|
||||
}
|
||||
return String.join(", ", names);
|
||||
}
|
||||
|
||||
private static String parameterName(String token) {
|
||||
String name = token;
|
||||
if ((name.startsWith("<") && name.endsWith(">")) || (name.startsWith("[") && name.endsWith("]"))) {
|
||||
name = name.substring(1, name.length() - 1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
private static String parentPath(String path) {
|
||||
int lastSpace = path.lastIndexOf(' ');
|
||||
if (lastSpace <= 0) {
|
||||
return "";
|
||||
}
|
||||
return path.substring(0, lastSpace);
|
||||
}
|
||||
|
||||
private static String normalize(String path) {
|
||||
|
||||
+58
-9
@@ -24,6 +24,8 @@ import art.arcane.iris.core.pregenerator.PregenTask;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.volmlib.util.format.Form;
|
||||
import art.arcane.volmlib.util.math.Position2;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -103,16 +105,63 @@ public final class ModdedPregenJob implements PregenListener {
|
||||
if (job == null) {
|
||||
return null;
|
||||
}
|
||||
long total = Math.max(1L, job.totalChunks);
|
||||
double percent = ((double) job.generated / (double) total) * 100D;
|
||||
return "Pregen " + job.dimension + ": "
|
||||
+ Form.f(job.generated) + "/" + Form.f(job.totalChunks)
|
||||
return job.statusText();
|
||||
}
|
||||
|
||||
public static Component statusComponent() {
|
||||
ModdedPregenJob job = ACTIVE.get();
|
||||
if (job == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
double percent = job.percent();
|
||||
MutableComponent status = Component.empty();
|
||||
status.append(ModdedCommandFeedback.header("Iris Pregen"));
|
||||
status.append(Component.literal("\n"));
|
||||
status.append(ModdedCommandFeedback.text("Dimension ", ModdedCommandFeedback.DARK_GREEN));
|
||||
status.append(ModdedCommandFeedback.text(job.dimension, ModdedCommandFeedback.PARAMETER_ALT));
|
||||
status.append(ModdedCommandFeedback.text(" · Method ", ModdedCommandFeedback.DARK_GREEN));
|
||||
status.append(ModdedCommandFeedback.text(job.method, ModdedCommandFeedback.PARAMETER));
|
||||
status.append(Component.literal("\n"));
|
||||
status.append(ModdedCommandFeedback.progressBar(percent, 32));
|
||||
status.append(ModdedCommandFeedback.text(" " + String.format("%.1f", percent) + "%", ModdedCommandFeedback.USAGE));
|
||||
status.append(Component.literal("\n"));
|
||||
status.append(ModdedCommandFeedback.text("Chunks ", ModdedCommandFeedback.DARK_GREEN));
|
||||
status.append(ModdedCommandFeedback.text(Form.f(job.generated) + "/" + Form.f(job.totalChunks), ModdedCommandFeedback.VALUE));
|
||||
status.append(ModdedCommandFeedback.text(" · Speed ", ModdedCommandFeedback.DARK_GREEN));
|
||||
status.append(ModdedCommandFeedback.text(Form.f((int) job.chunksPerSecond) + "/s", ModdedCommandFeedback.VALUE));
|
||||
status.append(Component.literal("\n"));
|
||||
status.append(ModdedCommandFeedback.text("ETA ", ModdedCommandFeedback.DARK_GREEN));
|
||||
status.append(ModdedCommandFeedback.text(Form.duration(job.eta, 2), ModdedCommandFeedback.VALUE));
|
||||
status.append(ModdedCommandFeedback.text(" · Elapsed ", ModdedCommandFeedback.DARK_GREEN));
|
||||
status.append(ModdedCommandFeedback.text(Form.duration(job.elapsed, 2), ModdedCommandFeedback.VALUE));
|
||||
if (job.pregenerator.paused()) {
|
||||
status.append(ModdedCommandFeedback.text(" · PAUSED", ModdedCommandFeedback.REQUIRED, true, false));
|
||||
}
|
||||
status.append(Component.literal("\n"));
|
||||
status.append(ModdedCommandFeedback.button("Pause/Resume", "/iris pregen pause", "Toggle pregeneration pause state", 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(Component.literal("\n"));
|
||||
status.append(ModdedCommandFeedback.footer());
|
||||
return status;
|
||||
}
|
||||
|
||||
private String statusText() {
|
||||
double percent = percent();
|
||||
return "Pregen " + dimension + ": "
|
||||
+ Form.f(generated) + "/" + Form.f(totalChunks)
|
||||
+ " (" + String.format("%.1f", percent) + "%), "
|
||||
+ Form.f((int) job.chunksPerSecond) + "/s"
|
||||
+ ", ETA " + Form.duration(job.eta, 2)
|
||||
+ ", elapsed " + Form.duration(job.elapsed, 2)
|
||||
+ ", method " + job.method
|
||||
+ (job.pregenerator.paused() ? ", PAUSED" : "");
|
||||
+ Form.f((int) chunksPerSecond) + "/s"
|
||||
+ ", ETA " + Form.duration(eta, 2)
|
||||
+ ", elapsed " + Form.duration(elapsed, 2)
|
||||
+ ", method " + method
|
||||
+ (pregenerator.paused() ? ", PAUSED" : "");
|
||||
}
|
||||
|
||||
private double percent() {
|
||||
long total = Math.max(1L, totalChunks);
|
||||
return ((double) generated / (double) total) * 100D;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+69
-6
@@ -38,11 +38,14 @@ import art.arcane.volmlib.util.json.JSONObject;
|
||||
import art.arcane.volmlib.util.math.M;
|
||||
import art.arcane.volmlib.util.math.Spiraler;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.arguments.LongArgumentType;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.slf4j.Logger;
|
||||
@@ -102,12 +105,12 @@ public final class ModdedStudioCommands {
|
||||
.then(Commands.argument("radius", IntegerArgumentType.integer(8, 1000))
|
||||
.executes((CommandContext<CommandSourceStack> context) -> regions(context.getSource(), IntegerArgumentType.getInteger(context, "radius")))));
|
||||
|
||||
root.then(message("open", "Studio worlds are temporary Bukkit worlds opened by the Bukkit studio toolchain; modded servers cannot open ad-hoc runtime dimensions. Edit the pack under config/irisworldgen/packs/<pack> and create a fresh world (or /iris regen) to see changes."));
|
||||
root.then(message("o", "Studio worlds are temporary Bukkit worlds opened by the Bukkit studio toolchain; modded servers cannot open ad-hoc runtime dimensions. Edit the pack under config/irisworldgen/packs/<pack> and create a fresh world (or /iris regen) to see changes."));
|
||||
root.then(message("close", "There are no studio worlds on modded servers (/iris studio open is Bukkit-only), so there is nothing to close."));
|
||||
root.then(message("x", "There are no studio worlds on modded servers (/iris studio open is Bukkit-only), so there is nothing to close."));
|
||||
root.then(message("tpstudio", "There are no studio worlds on modded servers to teleport to; /iris studio open is Bukkit-only."));
|
||||
root.then(message("stp", "There are no studio worlds on modded servers to teleport to; /iris studio open is Bukkit-only."));
|
||||
root.then(openTree("open"));
|
||||
root.then(openTree("o"));
|
||||
root.then(message("close", "There are no studio worlds on modded servers (/iris studio open prepares the pack workflow instead), so there is nothing to close."));
|
||||
root.then(message("x", "There are no studio worlds on modded servers (/iris studio open prepares the pack workflow instead), so there is nothing to close."));
|
||||
root.then(message("tpstudio", "There are no temporary Bukkit studio worlds on modded servers to teleport to; use /iris studio open <pack> to prepare the pack workflow instead."));
|
||||
root.then(message("stp", "There are no temporary Bukkit studio worlds on modded servers to teleport to; use /iris studio open <pack> to prepare the pack workflow instead."));
|
||||
root.then(message("vscode", "VSCode launch and workspace generation are desktop features of the Bukkit studio toolchain; edit config/irisworldgen/packs/<pack> directly in your editor."));
|
||||
root.then(message("vsc", "VSCode launch and workspace generation are desktop features of the Bukkit studio toolchain; edit config/irisworldgen/packs/<pack> directly in your editor."));
|
||||
root.then(message("update", "Workspace regeneration (.code-workspace + JSON schemas) reads Bukkit registries (SchemaBuilder); run /iris studio update on a Bukkit server against this pack."));
|
||||
@@ -128,6 +131,20 @@ public final class ModdedStudioCommands {
|
||||
return root;
|
||||
}
|
||||
|
||||
private static LiteralArgumentBuilder<CommandSourceStack> openTree(String name) {
|
||||
return Commands.literal(name)
|
||||
.executes((CommandContext<CommandSourceStack> context) -> openHelp(context.getSource()))
|
||||
.then(Commands.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES)
|
||||
.executes((CommandContext<CommandSourceStack> context) -> open(context.getSource(), StringArgumentType.getString(context, "pack"), 1337L))
|
||||
.then(Commands.argument("seed", LongArgumentType.longArg())
|
||||
.executes((CommandContext<CommandSourceStack> context) -> open(context.getSource(), StringArgumentType.getString(context, "pack"), LongArgumentType.getLong(context, "seed")))));
|
||||
}
|
||||
|
||||
private static int openHelp(CommandSourceStack source) {
|
||||
IrisModdedCommands.fail(source, "Provide a dimension pack: /iris studio open <pack> [seed]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static LiteralArgumentBuilder<CommandSourceStack> message(String name, String text) {
|
||||
return Commands.literal(name)
|
||||
.executes((CommandContext<CommandSourceStack> context) -> {
|
||||
@@ -159,6 +176,52 @@ public final class ModdedStudioCommands {
|
||||
return folder;
|
||||
}
|
||||
|
||||
private static int open(CommandSourceStack source, String pack, long seed) {
|
||||
File folder = resolvePack(source, pack);
|
||||
if (folder == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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");
|
||||
return 0;
|
||||
}
|
||||
|
||||
IrisModdedCommands.ok(source, studioOpenComponent(dimension, folder, seed));
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static MutableComponent studioOpenComponent(IrisDimension dimension, File folder, long seed) {
|
||||
String dimensionKey = dimension.getLoadKey() == null ? folder.getName() : dimension.getLoadKey();
|
||||
MutableComponent message = Component.empty();
|
||||
message.append(ModdedCommandFeedback.header("Iris Studio"));
|
||||
message.append(Component.literal("\n"));
|
||||
message.append(ModdedCommandFeedback.text("Opening studio for the \"", ModdedCommandFeedback.DARK_GREEN));
|
||||
message.append(ModdedCommandFeedback.text(dimension.getName(), ModdedCommandFeedback.PARAMETER_ALT));
|
||||
message.append(ModdedCommandFeedback.text("\" pack", ModdedCommandFeedback.DARK_GREEN));
|
||||
message.append(ModdedCommandFeedback.text(" (seed: " + seed + ")", ModdedCommandFeedback.VALUE));
|
||||
message.append(Component.literal("\n"));
|
||||
message.append(ModdedCommandFeedback.text("Pack ", ModdedCommandFeedback.DARK_GREEN));
|
||||
message.append(ModdedCommandFeedback.text(dimensionKey, ModdedCommandFeedback.PARAMETER));
|
||||
message.append(ModdedCommandFeedback.text(" is ready at ", ModdedCommandFeedback.DESCRIPTION));
|
||||
message.append(ModdedCommandFeedback.text(folder.getAbsolutePath(), ModdedCommandFeedback.VALUE));
|
||||
message.append(Component.literal("\n"));
|
||||
message.append(ModdedCommandFeedback.text("Modded servers cannot create Bukkit's temporary studio world at runtime; edit this pack directly, then use the matching world/datapack workflow or ", ModdedCommandFeedback.DESCRIPTION));
|
||||
message.append(ModdedCommandFeedback.button("/iris regen", "/iris regen", "Regenerate nearby chunks after editing this pack", false));
|
||||
message.append(ModdedCommandFeedback.text(" in an Iris dimension.", ModdedCommandFeedback.DESCRIPTION));
|
||||
message.append(Component.literal("\n"));
|
||||
message.append(ModdedCommandFeedback.button("Validate", "/iris pack validate " + dimensionKey, "Validate this pack before loading it", true));
|
||||
message.append(ModdedCommandFeedback.text(" ", ModdedCommandFeedback.OPTIONAL));
|
||||
message.append(ModdedCommandFeedback.button("World Help", "/iris world", "Open Iris world command help", true));
|
||||
message.append(ModdedCommandFeedback.text(" ", ModdedCommandFeedback.OPTIONAL));
|
||||
message.append(ModdedCommandFeedback.button("Package", "/iris studio package " + dimensionKey, "Package this dimension", false));
|
||||
message.append(Component.literal("\n"));
|
||||
message.append(ModdedCommandFeedback.footer());
|
||||
return message;
|
||||
}
|
||||
|
||||
private static int version(CommandSourceStack source, String pack) {
|
||||
File folder = resolvePack(source, pack);
|
||||
if (folder == null) {
|
||||
|
||||
@@ -20,6 +20,7 @@ package art.arcane.iris.neoforge;
|
||||
|
||||
import art.arcane.iris.modded.IrisModdedChunkGenerator;
|
||||
import art.arcane.iris.modded.ModdedEngineBootstrap;
|
||||
import art.arcane.iris.modded.ModdedIrisLog;
|
||||
import art.arcane.iris.modded.ModdedParityProbe;
|
||||
import art.arcane.iris.modded.ModdedWorldCheck;
|
||||
import art.arcane.iris.modded.ModdedWorldEngines;
|
||||
@@ -42,20 +43,16 @@ import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.neoforged.neoforge.event.server.ServerStoppingEvent;
|
||||
import net.neoforged.neoforge.event.tick.ServerTickEvent;
|
||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Mod("irisworldgen")
|
||||
public final class IrisNeoForgeBootstrap {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
|
||||
|
||||
public IrisNeoForgeBootstrap(IEventBus modBus) {
|
||||
ModdedEngineBootstrap.initialize(new NeoForgeModdedLoader());
|
||||
String modVersion = ModList.get().getModContainerById("irisworldgen")
|
||||
.map((ModContainer container) -> container.getModInfo().getVersion().toString())
|
||||
.orElse("unknown");
|
||||
VersionInfo versionInfo = FMLLoader.getCurrent().getVersionInfo();
|
||||
LOGGER.info("Iris {} bootstrapping on Minecraft {} (NeoForge {})", modVersion, versionInfo.mcVersion(), versionInfo.neoForgeVersion());
|
||||
ModdedIrisLog.info("Iris " + modVersion + " bootstrapping on Minecraft " + versionInfo.mcVersion() + " (NeoForge " + versionInfo.neoForgeVersion() + ")");
|
||||
|
||||
ModdedEngineBootstrap.selfTest(IrisNeoForgeBootstrap.class.getClassLoader());
|
||||
ModdedEngineBootstrap.bind();
|
||||
@@ -63,7 +60,7 @@ public final class IrisNeoForgeBootstrap {
|
||||
DeferredRegister<MapCodec<? extends ChunkGenerator>> chunkGenerators = DeferredRegister.create(Registries.CHUNK_GENERATOR, "irisworldgen");
|
||||
chunkGenerators.register("iris", () -> IrisModdedChunkGenerator.CODEC);
|
||||
chunkGenerators.register(modBus);
|
||||
LOGGER.info("Iris chunk generator registered as irisworldgen:iris");
|
||||
ModdedIrisLog.info("Iris chunk generator registered as irisworldgen:iris");
|
||||
|
||||
NeoForge.EVENT_BUS.addListener((ServerStoppingEvent event) -> {
|
||||
ModdedObjectUndo.clearAll();
|
||||
@@ -86,13 +83,13 @@ public final class IrisNeoForgeBootstrap {
|
||||
|
||||
String parity = System.getProperty("iris.parity");
|
||||
if (parity != null) {
|
||||
LOGGER.info("Iris parity probe armed: {}", parity);
|
||||
ModdedIrisLog.info("Iris parity probe armed: " + parity);
|
||||
ModdedParityProbe.schedule(parity);
|
||||
}
|
||||
|
||||
String worldCheck = System.getProperty("iris.worldcheck");
|
||||
if (worldCheck != null) {
|
||||
LOGGER.info("Iris world check armed");
|
||||
ModdedIrisLog.info("Iris world check armed");
|
||||
ModdedWorldCheck.schedule();
|
||||
}
|
||||
}
|
||||
|
||||
+90
-8
@@ -70,6 +70,9 @@ String fabricArtifactName = irisArtifactName('Fabric', "${minecraftVersion}+${lo
|
||||
String forgeArtifactName = irisArtifactName('Forge', "${minecraftVersion}+${loaderDisplayVersion(forgeVersion)}")
|
||||
String neoForgeArtifactName = irisArtifactName('NeoForge', "${minecraftVersion}+${loaderDisplayVersion(neoForgeVersion)}")
|
||||
extensions.extraProperties.set('bukkitArtifactName', bukkitArtifactName)
|
||||
extensions.extraProperties.set('fabricArtifactName', fabricArtifactName)
|
||||
extensions.extraProperties.set('forgeArtifactName', forgeArtifactName)
|
||||
extensions.extraProperties.set('neoForgeArtifactName', neoForgeArtifactName)
|
||||
|
||||
apply plugin: ApiGenerator
|
||||
|
||||
@@ -86,7 +89,13 @@ registerCustomOutputTask('PixelFury', 'C://Users/repix/workplace/Iris/1.21.3 - D
|
||||
registerCustomOutputTask('PixelFuryDev', 'C://Users/repix/workplace/Iris/1.21 - Development-v3/plugins')
|
||||
// ========================== UNIX ==============================
|
||||
registerCustomOutputTaskUnix('CyberpwnLT', '/Users/danielmills/development/server/plugins')
|
||||
registerCustomOutputTaskUnix('PsychoLT', '/Users/brianfopiano/Developer/RemoteGit/[Minecraft Server]/consumers/plugin-consumers/dropins/plugins')
|
||||
registerCustomOutputTaskUnix(
|
||||
'PsychoLT',
|
||||
'/Users/brianfopiano/Developer/RemoteGit/[Minecraft Server]/consumers/plugin-consumers/dropins/plugins',
|
||||
'/Users/brianfopiano/Developer/RemoteGit/[Minecraft Server]/consumers/fabric-mod-consumers/dropins/mods',
|
||||
'/Users/brianfopiano/Developer/RemoteGit/[Minecraft Server]/consumers/forge-mod-consumers/dropins/mods',
|
||||
'/Users/brianfopiano/Developer/RemoteGit/[Minecraft Server]/consumers/neoforge-mod-consumers/dropins/mods'
|
||||
)
|
||||
registerCustomOutputTaskUnix('PixelMac', '/Users/test/Desktop/mcserver/plugins')
|
||||
registerCustomOutputTaskUnix('CrazyDev22LT', '/home/julian/Desktop/server/plugins')
|
||||
// ==============================================================
|
||||
@@ -232,6 +241,14 @@ tasks.register('buildNeoforge', Copy) {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named('forgeJar').configure {
|
||||
mustRunAfter(tasks.named('fabricJar'))
|
||||
}
|
||||
|
||||
tasks.named('neoforgeJar').configure {
|
||||
mustRunAfter(tasks.named('forgeJar'))
|
||||
}
|
||||
|
||||
tasks.register('buildAll') {
|
||||
group = 'iris'
|
||||
dependsOn('buildBukkit', 'buildFabric', 'buildForge', 'buildNeoforge', ':spi:jar')
|
||||
@@ -375,14 +392,15 @@ void registerCustomOutputTask(String name, String path) {
|
||||
return
|
||||
}
|
||||
|
||||
tasks.register("build${name}", Copy) {
|
||||
group = 'development'
|
||||
outputs.upToDateWhen { false }
|
||||
dependsOn('iris')
|
||||
from(layout.buildDirectory.file((String) project.ext.bukkitArtifactName))
|
||||
into(file(path))
|
||||
rename { String ignored -> 'Iris.jar' }
|
||||
createCustomOutputTasks(name, path)
|
||||
}
|
||||
|
||||
void registerCustomOutputTask(String name, String path, String fabricPath, String forgePath, String neoForgePath) {
|
||||
if (!System.getProperty('os.name').toLowerCase().contains('windows')) {
|
||||
return
|
||||
}
|
||||
|
||||
createCustomOutputTasks(name, path, fabricPath, forgePath, neoForgePath)
|
||||
}
|
||||
|
||||
void registerCustomOutputTaskUnix(String name, String path) {
|
||||
@@ -390,6 +408,44 @@ void registerCustomOutputTaskUnix(String name, String path) {
|
||||
return
|
||||
}
|
||||
|
||||
createCustomOutputTasks(name, path)
|
||||
}
|
||||
|
||||
void registerCustomOutputTaskUnix(String name, String path, String fabricPath, String forgePath, String neoForgePath) {
|
||||
if (System.getProperty('os.name').toLowerCase().contains('windows')) {
|
||||
return
|
||||
}
|
||||
|
||||
createCustomOutputTasks(name, path, fabricPath, forgePath, neoForgePath)
|
||||
}
|
||||
|
||||
void createCustomOutputTasks(String name, String path) {
|
||||
createBukkitOutputTask(name, path)
|
||||
|
||||
tasks.register("buildAll${name}") {
|
||||
group = 'development'
|
||||
dependsOn("build${name}")
|
||||
}
|
||||
}
|
||||
|
||||
void createCustomOutputTasks(String name, String path, String fabricPath, String forgePath, String neoForgePath) {
|
||||
createBukkitOutputTask(name, path)
|
||||
createPlatformOutputTask(name, fabricPath, 'Fabric', 'buildFabric', "adapters/fabric/build/libs/${String.valueOf(project.ext.fabricArtifactName)}")
|
||||
createPlatformOutputTask(name, forgePath, 'Forge', 'buildForge', "adapters/forge/build/libs/${String.valueOf(project.ext.forgeArtifactName)}")
|
||||
createPlatformOutputTask(name, neoForgePath, 'Neoforge', 'buildNeoforge', "adapters/neoforge/build/libs/${String.valueOf(project.ext.neoForgeArtifactName)}")
|
||||
|
||||
tasks.register("buildAll${name}") {
|
||||
group = 'development'
|
||||
dependsOn(
|
||||
"build${name}",
|
||||
"build${name}Fabric",
|
||||
"build${name}Forge",
|
||||
"build${name}Neoforge"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
void createBukkitOutputTask(String name, String path) {
|
||||
tasks.register("build${name}", Copy) {
|
||||
group = 'development'
|
||||
outputs.upToDateWhen { false }
|
||||
@@ -397,5 +453,31 @@ void registerCustomOutputTaskUnix(String name, String path) {
|
||||
from(layout.buildDirectory.file((String) project.ext.bukkitArtifactName))
|
||||
into(file(path))
|
||||
rename { String ignored -> 'Iris.jar' }
|
||||
doFirst {
|
||||
delete(
|
||||
file("${path}/Iris-Fabric.jar"),
|
||||
file("${path}/Iris-Forge.jar"),
|
||||
file("${path}/Iris-Neoforge.jar")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void createPlatformOutputTask(String name, String path, String platform, String buildTask, String sourcePath) {
|
||||
tasks.register("build${name}${platform}", Copy) {
|
||||
group = 'development'
|
||||
outputs.upToDateWhen { false }
|
||||
dependsOn(buildTask)
|
||||
from(layout.projectDirectory.file(sourcePath))
|
||||
into(file(path))
|
||||
rename { String ignored -> "Iris-${platform}.jar" }
|
||||
doFirst {
|
||||
delete(
|
||||
file("${path}/Iris.jar"),
|
||||
file("${path}/Iris-Fabric.jar"),
|
||||
file("${path}/Iris-Forge.jar"),
|
||||
file("${path}/Iris-Neoforge.jar")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package art.arcane.iris.core.safeguard;
|
||||
|
||||
import art.arcane.iris.BuildConstants;
|
||||
import art.arcane.iris.core.IrisSettings;
|
||||
import art.arcane.iris.core.splash.IrisSplashRenderer;
|
||||
import art.arcane.iris.platform.bukkit.BukkitPlatform;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.iris.util.common.format.C;
|
||||
@@ -11,21 +12,12 @@ import org.bukkit.Bukkit;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public enum Mode {
|
||||
STABLE(C.IRIS, C.AQUA),
|
||||
WARNING(C.GOLD, C.YELLOW),
|
||||
UNSTABLE(C.RED, C.GOLD);
|
||||
|
||||
private static final int SPLASH_WIDTH = 53;
|
||||
private static final int SPLASH_HEIGHT = 11;
|
||||
private static final double SPLASH_CENTER_X = 26.0;
|
||||
private static final double SPLASH_CENTER_Y = 5.0;
|
||||
private static final double SPLASH_ASPECT = 2.15;
|
||||
private static final double SPLASH_EYE_A = 5.6;
|
||||
private static final double SPLASH_EYE_B = 4.6;
|
||||
|
||||
private final C color;
|
||||
private final C glow;
|
||||
private final String id;
|
||||
@@ -70,7 +62,7 @@ public enum Mode {
|
||||
String startupDate = getStartupDate();
|
||||
int javaVersion = getJavaVersion();
|
||||
|
||||
String[] splash = renderSplashLogo();
|
||||
String[] splash = IrisSplashRenderer.render(this::splashTone);
|
||||
|
||||
String[] info = new String[]{
|
||||
"",
|
||||
@@ -94,38 +86,6 @@ public enum Mode {
|
||||
IrisLogging.info(builder.toString());
|
||||
}
|
||||
|
||||
private String[] renderSplashLogo() {
|
||||
ThreadLocalRandom random = ThreadLocalRandom.current();
|
||||
double headThickness = 3.7 + random.nextDouble() * 0.6;
|
||||
double tipReach = 19.0 + random.nextDouble() * 4.0;
|
||||
double notchX = 3.5 + random.nextDouble() * 1.5;
|
||||
double[][][] spines = splashFinSpines(tipReach);
|
||||
double[][][] voids = splashNotchVoids(notchX);
|
||||
String[] lines = new String[SPLASH_HEIGHT];
|
||||
for (int y = 0; y < SPLASH_HEIGHT; y++) {
|
||||
StringBuilder line = new StringBuilder();
|
||||
String activeTone = null;
|
||||
for (int x = 0; x < SPLASH_WIDTH; x++) {
|
||||
char glyph = splashEyeGlyph(x, y);
|
||||
if (glyph == 0) {
|
||||
glyph = splashFinGlyph(x, y, spines, voids, headThickness);
|
||||
}
|
||||
if (glyph == 0 || glyph == ' ') {
|
||||
line.append(' ');
|
||||
continue;
|
||||
}
|
||||
String tone = splashTone(glyph);
|
||||
if (!tone.equals(activeTone)) {
|
||||
line.append(tone);
|
||||
activeTone = tone;
|
||||
}
|
||||
line.append(glyph);
|
||||
}
|
||||
lines[y] = line.toString();
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
private String splashTone(char glyph) {
|
||||
return switch (glyph) {
|
||||
case '=' -> C.GRAY.toString();
|
||||
@@ -136,129 +96,6 @@ public enum Mode {
|
||||
};
|
||||
}
|
||||
|
||||
private char splashEyeGlyph(int x, int y) {
|
||||
if (y == 5 && Math.abs(x - 26) <= 1) {
|
||||
if (x == 25) {
|
||||
return '(';
|
||||
}
|
||||
if (x == 27) {
|
||||
return ')';
|
||||
}
|
||||
return ' ';
|
||||
}
|
||||
double radius = splashEyeRadius(x, y);
|
||||
if (radius <= 0.50) {
|
||||
return '@';
|
||||
}
|
||||
if (radius <= 0.72) {
|
||||
return '%';
|
||||
}
|
||||
if (radius <= 0.88) {
|
||||
return '*';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private char splashFinGlyph(int x, int y, double[][][] spines, double[][][] voids, double headThickness) {
|
||||
if (splashEyeRadius(x, y) <= 1.26) {
|
||||
return 0;
|
||||
}
|
||||
double voidDistance = Double.MAX_VALUE;
|
||||
for (double[][] notch : voids) {
|
||||
for (double[] sample : notch) {
|
||||
double voidRadius = Math.max(0.25, 0.95 - 0.65 * sample[2]);
|
||||
double dx = (x - sample[0]) / SPLASH_ASPECT;
|
||||
double dy = y - sample[1];
|
||||
double distance = Math.sqrt(dx * dx + dy * dy) / voidRadius;
|
||||
if (distance < voidDistance) {
|
||||
voidDistance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (voidDistance < 1.0) {
|
||||
return 0;
|
||||
}
|
||||
double best = Double.MAX_VALUE;
|
||||
double bestT = 0.0;
|
||||
for (double[][] spine : spines) {
|
||||
for (int i = 0; i < spine.length; i++) {
|
||||
double t = i / (double) (spine.length - 1);
|
||||
double thickness = headThickness * Math.pow(1.0 - t, 1.3) + 0.6;
|
||||
double dx = (x - spine[i][0]) / SPLASH_ASPECT;
|
||||
double dy = y - spine[i][1];
|
||||
double distance = Math.sqrt(dx * dx + dy * dy) / thickness;
|
||||
if (distance < best) {
|
||||
best = distance;
|
||||
bestT = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
double shade = best
|
||||
+ Math.max(0.0, 3.0 - Math.min(x, SPLASH_WIDTH - 1 - x)) * 0.12
|
||||
+ Math.max(0.0, 0.10 - bestT) * 1.5
|
||||
+ Math.max(0.0, 1.30 - voidDistance) * 0.15;
|
||||
if (shade > 1.0) {
|
||||
return 0;
|
||||
}
|
||||
int level = shade <= 0.70 ? 0 : shade <= 0.90 ? 1 : 2;
|
||||
if (bestT > 0.97) {
|
||||
level += 2;
|
||||
} else if (bestT > 0.88) {
|
||||
level += 1;
|
||||
}
|
||||
return switch (Math.min(level, 2)) {
|
||||
case 0 -> '#';
|
||||
case 1 -> '=';
|
||||
default -> '-';
|
||||
};
|
||||
}
|
||||
|
||||
private double splashEyeRadius(int x, int y) {
|
||||
double dx = (x - SPLASH_CENTER_X) / SPLASH_ASPECT;
|
||||
double dy = y - SPLASH_CENTER_Y;
|
||||
double nx = dx / SPLASH_EYE_A;
|
||||
double ny = dy / SPLASH_EYE_B;
|
||||
return Math.sqrt(nx * nx + ny * ny);
|
||||
}
|
||||
|
||||
private double[][][] splashNotchVoids(double notchX) {
|
||||
double[][] control = {{notchX + 0.8, 12.0}, {notchX, 9.6}, {notchX - 1.4, 7.8}};
|
||||
int samples = 31;
|
||||
double[][] left = new double[samples][3];
|
||||
double[][] right = new double[samples][3];
|
||||
for (int i = 0; i < samples; i++) {
|
||||
double t = i / (double) (samples - 1);
|
||||
double u = 1.0 - t;
|
||||
double bx = u * u * control[0][0] + 2.0 * u * t * control[1][0] + t * t * control[2][0];
|
||||
double by = u * u * control[0][1] + 2.0 * u * t * control[1][1] + t * t * control[2][1];
|
||||
left[i][0] = bx;
|
||||
left[i][1] = by;
|
||||
left[i][2] = t;
|
||||
right[i][0] = (SPLASH_WIDTH - 1.0) - bx;
|
||||
right[i][1] = (SPLASH_HEIGHT - 1.0) - by;
|
||||
right[i][2] = t;
|
||||
}
|
||||
return new double[][][]{left, right};
|
||||
}
|
||||
|
||||
private double[][][] splashFinSpines(double tipReach) {
|
||||
double[][] control = {{-1.5, 9.8}, {0.4, 4.4}, {5.2, 1.0}, {tipReach, 0.4}};
|
||||
int samples = 61;
|
||||
double[][] left = new double[samples][2];
|
||||
double[][] right = new double[samples][2];
|
||||
for (int i = 0; i < samples; i++) {
|
||||
double t = i / (double) (samples - 1);
|
||||
double u = 1.0 - t;
|
||||
double bx = u * u * u * control[0][0] + 3.0 * u * u * t * control[1][0] + 3.0 * u * t * t * control[2][0] + t * t * t * control[3][0];
|
||||
double by = u * u * u * control[0][1] + 3.0 * u * u * t * control[1][1] + 3.0 * u * t * t * control[2][1] + t * t * t * control[3][1];
|
||||
left[i][0] = bx;
|
||||
left[i][1] = by;
|
||||
right[i][0] = (SPLASH_WIDTH - 1.0) - bx;
|
||||
right[i][1] = (SPLASH_HEIGHT - 1.0) - by;
|
||||
}
|
||||
return new double[][][]{left, right};
|
||||
}
|
||||
|
||||
private String wrap(String tag) {
|
||||
return C.BOLD.toString() + C.DARK_GRAY + "[" + C.BOLD + color + tag + C.BOLD + C.DARK_GRAY + "]" + C.RESET;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package art.arcane.iris.core.splash;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public final class IrisSplashPackScanner {
|
||||
private IrisSplashPackScanner() {
|
||||
}
|
||||
|
||||
public static List<SplashPackMetadata> collect(File packFolder, SplashPackErrorReporter reporter) {
|
||||
if (packFolder == null || !packFolder.isDirectory()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
File[] folders = packFolder.listFiles(File::isDirectory);
|
||||
if (folders == null || folders.length == 0) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
List<SplashPackMetadata> packs = new ArrayList<>(folders.length);
|
||||
for (File folder : folders) {
|
||||
SplashPackMetadata metadata = read(folder, reporter);
|
||||
if (metadata != null) {
|
||||
packs.add(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
packs.sort(Comparator.comparing(SplashPackMetadata::name));
|
||||
return packs;
|
||||
}
|
||||
|
||||
public static SplashPackMetadata read(File pack, SplashPackErrorReporter reporter) {
|
||||
if (pack == null || !pack.isDirectory()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String dimName = pack.getName();
|
||||
File dimensionFile = new File(pack, "dimensions/" + dimName + ".json");
|
||||
if (!dimensionFile.isFile()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try (FileReader reader = new FileReader(dimensionFile)) {
|
||||
JsonObject json = JsonParser.parseReader(reader).getAsJsonObject();
|
||||
if (!json.has("version")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SplashPackMetadata(dimName, json.get("version").getAsString());
|
||||
} catch (IOException | JsonParseException | IllegalStateException error) {
|
||||
report(reporter, "Failed to read splash metadata for dimension pack \"" + dimName + "\".", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void report(SplashPackErrorReporter reporter, String message, Throwable error) {
|
||||
if (reporter == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
reporter.report(message, error);
|
||||
}
|
||||
|
||||
public record SplashPackMetadata(String name, String version) {
|
||||
}
|
||||
|
||||
public interface SplashPackErrorReporter {
|
||||
void report(String message, Throwable error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package art.arcane.iris.core.splash;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public final class IrisSplashRenderer {
|
||||
private static final int SPLASH_WIDTH = 53;
|
||||
private static final int SPLASH_HEIGHT = 11;
|
||||
private static final double SPLASH_CENTER_X = 26.0;
|
||||
private static final double SPLASH_CENTER_Y = 5.0;
|
||||
private static final double SPLASH_ASPECT = 2.15;
|
||||
private static final double SPLASH_EYE_A = 5.6;
|
||||
private static final double SPLASH_EYE_B = 4.6;
|
||||
private static final SplashTone PLAIN_TONE = (glyph) -> "";
|
||||
|
||||
private IrisSplashRenderer() {
|
||||
}
|
||||
|
||||
public static String[] renderPlain() {
|
||||
return render(PLAIN_TONE);
|
||||
}
|
||||
|
||||
public static String[] render(SplashTone tone) {
|
||||
SplashTone splashTone = tone == null ? PLAIN_TONE : tone;
|
||||
ThreadLocalRandom random = ThreadLocalRandom.current();
|
||||
double headThickness = 3.7 + random.nextDouble() * 0.6;
|
||||
double tipReach = 19.0 + random.nextDouble() * 4.0;
|
||||
double notchX = 3.5 + random.nextDouble() * 1.5;
|
||||
double[][][] spines = splashFinSpines(tipReach);
|
||||
double[][][] voids = splashNotchVoids(notchX);
|
||||
String[] lines = new String[SPLASH_HEIGHT];
|
||||
for (int y = 0; y < SPLASH_HEIGHT; y++) {
|
||||
StringBuilder line = new StringBuilder();
|
||||
String activeTone = null;
|
||||
for (int x = 0; x < SPLASH_WIDTH; x++) {
|
||||
char glyph = splashEyeGlyph(x, y);
|
||||
if (glyph == 0) {
|
||||
glyph = splashFinGlyph(x, y, spines, voids, headThickness);
|
||||
}
|
||||
if (glyph == 0 || glyph == ' ') {
|
||||
line.append(' ');
|
||||
continue;
|
||||
}
|
||||
String glyphTone = splashTone.tone(glyph);
|
||||
if (!glyphTone.isEmpty() && !glyphTone.equals(activeTone)) {
|
||||
line.append(glyphTone);
|
||||
activeTone = glyphTone;
|
||||
}
|
||||
line.append(glyph);
|
||||
}
|
||||
lines[y] = line.toString();
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
private static char splashEyeGlyph(int x, int y) {
|
||||
if (y == 5 && Math.abs(x - 26) <= 1) {
|
||||
if (x == 25) {
|
||||
return '(';
|
||||
}
|
||||
if (x == 27) {
|
||||
return ')';
|
||||
}
|
||||
return ' ';
|
||||
}
|
||||
double radius = splashEyeRadius(x, y);
|
||||
if (radius <= 0.50) {
|
||||
return '@';
|
||||
}
|
||||
if (radius <= 0.72) {
|
||||
return '%';
|
||||
}
|
||||
if (radius <= 0.88) {
|
||||
return '*';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static char splashFinGlyph(int x, int y, double[][][] spines, double[][][] voids, double headThickness) {
|
||||
if (splashEyeRadius(x, y) <= 1.26) {
|
||||
return 0;
|
||||
}
|
||||
double voidDistance = Double.MAX_VALUE;
|
||||
for (double[][] notch : voids) {
|
||||
for (double[] sample : notch) {
|
||||
double voidRadius = Math.max(0.25, 0.95 - 0.65 * sample[2]);
|
||||
double dx = (x - sample[0]) / SPLASH_ASPECT;
|
||||
double dy = y - sample[1];
|
||||
double distance = Math.sqrt(dx * dx + dy * dy) / voidRadius;
|
||||
if (distance < voidDistance) {
|
||||
voidDistance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (voidDistance < 1.0) {
|
||||
return 0;
|
||||
}
|
||||
double best = Double.MAX_VALUE;
|
||||
double bestT = 0.0;
|
||||
for (double[][] spine : spines) {
|
||||
for (int i = 0; i < spine.length; i++) {
|
||||
double t = i / (double) (spine.length - 1);
|
||||
double thickness = headThickness * Math.pow(1.0 - t, 1.3) + 0.6;
|
||||
double dx = (x - spine[i][0]) / SPLASH_ASPECT;
|
||||
double dy = y - spine[i][1];
|
||||
double distance = Math.sqrt(dx * dx + dy * dy) / thickness;
|
||||
if (distance < best) {
|
||||
best = distance;
|
||||
bestT = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
double shade = best
|
||||
+ Math.max(0.0, 3.0 - Math.min(x, SPLASH_WIDTH - 1 - x)) * 0.12
|
||||
+ Math.max(0.0, 0.10 - bestT) * 1.5
|
||||
+ Math.max(0.0, 1.30 - voidDistance) * 0.15;
|
||||
if (shade > 1.0) {
|
||||
return 0;
|
||||
}
|
||||
int level = shade <= 0.70 ? 0 : shade <= 0.90 ? 1 : 2;
|
||||
if (bestT > 0.97) {
|
||||
level += 2;
|
||||
} else if (bestT > 0.88) {
|
||||
level += 1;
|
||||
}
|
||||
return switch (Math.min(level, 2)) {
|
||||
case 0 -> '#';
|
||||
case 1 -> '=';
|
||||
default -> '-';
|
||||
};
|
||||
}
|
||||
|
||||
private static double splashEyeRadius(int x, int y) {
|
||||
double dx = (x - SPLASH_CENTER_X) / SPLASH_ASPECT;
|
||||
double dy = y - SPLASH_CENTER_Y;
|
||||
double nx = dx / SPLASH_EYE_A;
|
||||
double ny = dy / SPLASH_EYE_B;
|
||||
return Math.sqrt(nx * nx + ny * ny);
|
||||
}
|
||||
|
||||
private static double[][][] splashNotchVoids(double notchX) {
|
||||
double[][] control = {{notchX + 0.8, 12.0}, {notchX, 9.6}, {notchX - 1.4, 7.8}};
|
||||
int samples = 31;
|
||||
double[][] left = new double[samples][3];
|
||||
double[][] right = new double[samples][3];
|
||||
for (int i = 0; i < samples; i++) {
|
||||
double t = i / (double) (samples - 1);
|
||||
double u = 1.0 - t;
|
||||
double bx = u * u * control[0][0] + 2.0 * u * t * control[1][0] + t * t * control[2][0];
|
||||
double by = u * u * control[0][1] + 2.0 * u * t * control[1][1] + t * t * control[2][1];
|
||||
left[i][0] = bx;
|
||||
left[i][1] = by;
|
||||
left[i][2] = t;
|
||||
right[i][0] = (SPLASH_WIDTH - 1.0) - bx;
|
||||
right[i][1] = (SPLASH_HEIGHT - 1.0) - by;
|
||||
right[i][2] = t;
|
||||
}
|
||||
return new double[][][]{left, right};
|
||||
}
|
||||
|
||||
private static double[][][] splashFinSpines(double tipReach) {
|
||||
double[][] control = {{-1.5, 9.8}, {0.4, 4.4}, {5.2, 1.0}, {tipReach, 0.4}};
|
||||
int samples = 61;
|
||||
double[][] left = new double[samples][2];
|
||||
double[][] right = new double[samples][2];
|
||||
for (int i = 0; i < samples; i++) {
|
||||
double t = i / (double) (samples - 1);
|
||||
double u = 1.0 - t;
|
||||
double bx = u * u * u * control[0][0] + 3.0 * u * u * t * control[1][0] + 3.0 * u * t * t * control[2][0] + t * t * t * control[3][0];
|
||||
double by = u * u * u * control[0][1] + 3.0 * u * u * t * control[1][1] + 3.0 * u * t * t * control[2][1] + t * t * t * control[3][1];
|
||||
left[i][0] = bx;
|
||||
left[i][1] = by;
|
||||
right[i][0] = (SPLASH_WIDTH - 1.0) - bx;
|
||||
right[i][1] = (SPLASH_HEIGHT - 1.0) - by;
|
||||
}
|
||||
return new double[][][]{left, right};
|
||||
}
|
||||
|
||||
public interface SplashTone {
|
||||
String tone(char glyph);
|
||||
}
|
||||
}
|
||||
@@ -19,13 +19,17 @@
|
||||
package art.arcane.iris.spi;
|
||||
|
||||
import java.util.IllegalFormatException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class IrisLogging {
|
||||
private static final Pattern LEGACY_COLOR = Pattern.compile("(?i)\\u00a7[0-9A-FK-ORX]");
|
||||
private static final Pattern MINI_MESSAGE_TAG = Pattern.compile("(?i)</?(?:reset|bold|b|italic|i|underlined?|u|strikethrough|st|obfuscated?|obf|black|dark_blue|dark_green|dark_aqua|dark_red|dark_purple|gold|gray|dark_gray|blue|green|aqua|red|light_purple|yellow|white|gradient|font|hover|click|rainbow)(?::[^>\\n]{0,96})?>|<#[0-9a-f]{6}>");
|
||||
|
||||
private IrisLogging() {
|
||||
}
|
||||
|
||||
public static void info(String format, Object... args) {
|
||||
emit(LogLevel.INFO, safeFormat(format, args));
|
||||
emit(LogLevel.INFO, format(format, args));
|
||||
}
|
||||
|
||||
public static void debug(String message) {
|
||||
@@ -33,11 +37,11 @@ public final class IrisLogging {
|
||||
}
|
||||
|
||||
public static void warn(String format, Object... args) {
|
||||
emit(LogLevel.WARN, safeFormat(format, args));
|
||||
emit(LogLevel.WARN, format(format, args));
|
||||
}
|
||||
|
||||
public static void error(String format, Object... args) {
|
||||
emit(LogLevel.ERROR, safeFormat(format, args));
|
||||
emit(LogLevel.ERROR, format(format, args));
|
||||
}
|
||||
|
||||
public static void msg(String message) {
|
||||
@@ -46,7 +50,7 @@ public final class IrisLogging {
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("[Iris] " + message);
|
||||
System.out.println("[Iris] " + clean(message));
|
||||
}
|
||||
|
||||
public static void reportError(String context, Throwable error) {
|
||||
@@ -76,21 +80,7 @@ public final class IrisLogging {
|
||||
}
|
||||
}
|
||||
|
||||
private static void emit(LogLevel level, String message) {
|
||||
if (IrisPlatforms.isBound()) {
|
||||
IrisPlatforms.get().log(level, message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (level == LogLevel.WARN || level == LogLevel.ERROR) {
|
||||
System.err.println("[Iris/" + level + "] " + message);
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("[Iris/" + level + "] " + message);
|
||||
}
|
||||
|
||||
private static String safeFormat(String format, Object... args) {
|
||||
public static String format(String format, Object... args) {
|
||||
if (format == null) {
|
||||
return "null";
|
||||
}
|
||||
@@ -105,4 +95,29 @@ public final class IrisLogging {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
public static String clean(String message) {
|
||||
if (message == null) {
|
||||
return "null";
|
||||
}
|
||||
|
||||
String legacyStripped = LEGACY_COLOR.matcher(message).replaceAll("");
|
||||
return MINI_MESSAGE_TAG.matcher(legacyStripped).replaceAll("");
|
||||
}
|
||||
|
||||
private static void emit(LogLevel level, String message) {
|
||||
LogLevel target = level == null ? LogLevel.INFO : level;
|
||||
if (IrisPlatforms.isBound()) {
|
||||
IrisPlatforms.get().log(target, message);
|
||||
return;
|
||||
}
|
||||
|
||||
String output = clean(message);
|
||||
if (target == LogLevel.WARN || target == LogLevel.ERROR) {
|
||||
System.err.println("[Iris/" + target + "] " + output);
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("[Iris/" + target + "] " + output);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user