mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-28 23:10:32 +00:00
Rebase
This commit is contained in:
1 parent
d480abad50
commit
2145e90b1c
46 files changed
+1766
-167
No files matched your search
@@ -0,0 +1,71 @@
|
||||
import { readFile, writeFile } from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { setup, openStudio, closeStudio, sampleTerrain, readJson, writeJson,
|
||||
logPosition, logSince } from './studio-support.mjs'
|
||||
|
||||
export default {
|
||||
name: 'iris-studio-hotload-lifecycle',
|
||||
description: 'Generate cold chunks during Studio edits and close after saving an edit.',
|
||||
async run(context) {
|
||||
const fixture = await setup(context)
|
||||
const biomeFile = path.join(fixture.pack, 'biomes', 'flat.json')
|
||||
const original = await readFile(biomeFile, 'utf8')
|
||||
const logOffset = await logPosition(fixture)
|
||||
let active
|
||||
let x = 16384
|
||||
try {
|
||||
active = await openStudio(context, fixture)
|
||||
await sampleTerrain(context, fixture, 0, fixture.state.material)
|
||||
for (let cycle = 0; cycle < 3; cycle++) {
|
||||
await context.step(`Load cold chunks during hotload ${cycle + 1}`, async () => {
|
||||
const manifestFile = fixture.manifestPath(active.key)
|
||||
const before = await readJson(manifestFile)
|
||||
const biome = await readJson(biomeFile)
|
||||
const material = cycle % 2 === 0 ? 'gold_block' : 'diamond_block'
|
||||
biome.name = `Lifecycle terrain ${cycle + 1}`
|
||||
biome.layers[0].palette[0].block = `minecraft:${material}`
|
||||
const startedAt = Date.now()
|
||||
await writeJson(biomeFile, biome)
|
||||
x += 512
|
||||
await context.command(`/minecraft:tp @s ${x + 0.5} 101 0.5`, /Teleported/i, 15000)
|
||||
const after = await fixture.wait(async () => {
|
||||
const manifest = await readJson(manifestFile)
|
||||
return manifest.activeActivationId !== before.activeActivationId ? manifest : false
|
||||
}, 'Hotload did not complete while the player requested fresh chunks', 90000)
|
||||
const activationDurationMs = Date.now() - startedAt
|
||||
await fixture.wait(() => Math.abs(context.bot.entity.position.x - x - 0.5) < 1,
|
||||
'Cold chunk teleport did not arrive after hotload', 30000)
|
||||
context.report.iris.activations.push({ before: before.activeActivationId,
|
||||
after: after.activeActivationId, material, coldChunkRequests: 1, activationDurationMs })
|
||||
x += 2048
|
||||
await sampleTerrain(context, fixture, x, material)
|
||||
})
|
||||
}
|
||||
await context.step('Close Studio while a saved edit is activating', async () => {
|
||||
const biome = await readJson(biomeFile)
|
||||
biome.name = 'Lifecycle close terrain'
|
||||
await writeJson(biomeFile, biome)
|
||||
const pending = await fixture.wait(async () => {
|
||||
const manifest = await readJson(fixture.manifestPath(active.key))
|
||||
return manifest.pendingActivationId !== null ? manifest : false
|
||||
}, 'The saved close edit did not enter activation', 90000)
|
||||
context.report.iris.closePendingActivationId = pending.pendingActivationId
|
||||
await closeStudio(context, fixture, active)
|
||||
active = undefined
|
||||
await context.command('/minecraft:seed', /Seed:/i, 10000)
|
||||
})
|
||||
await context.step('Reopen the edited pack and retain usable generation', async () => {
|
||||
active = await openStudio(context, fixture)
|
||||
await sampleTerrain(context, fixture, 0, 'gold_block')
|
||||
await closeStudio(context, fixture, active)
|
||||
active = undefined
|
||||
})
|
||||
const log = await logSince(fixture, logOffset)
|
||||
context.expect(!/Studio generation update failed|Unable to (capture terrain|save the terrain boundary)|ClosedChannelException|Chunk system error|server has stopped responding|could not acquire its engine runtime|Failed to list files/i.test(log),
|
||||
'Studio lifecycle emitted a generation, snapshot, storage, or watchdog failure')
|
||||
} finally {
|
||||
if (active) await closeStudio(context, fixture, active)
|
||||
await writeFile(biomeFile, original)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,33 +20,20 @@ import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class PaperPluginMetadataTest {
|
||||
private static final List<String> OPTIONAL_PLUGIN_IDS = List.of(
|
||||
private static final List<String> JOINED_PLUGIN_IDS = List.of(
|
||||
"PlaceholderAPI",
|
||||
"CraftEngine",
|
||||
"Nexo",
|
||||
"Oraxen",
|
||||
"ItemsAdder",
|
||||
"SCore",
|
||||
"ExecutableItems",
|
||||
"MythicLib",
|
||||
"MMOItems",
|
||||
"eco",
|
||||
"EcoItems",
|
||||
"MythicMobs",
|
||||
"MythicCrucible",
|
||||
"KGenerators",
|
||||
"Multiverse-Core",
|
||||
"WorldEdit"
|
||||
);
|
||||
private static final List<String> JOINED_PLUGIN_IDS = OPTIONAL_PLUGIN_IDS.stream()
|
||||
.filter(pluginId -> !"ExecutableItems".equals(pluginId) && !"Multiverse-Core".equals(pluginId))
|
||||
.toList();
|
||||
private static final List<String> UNORDERED_PLUGIN_IDS = List.of(
|
||||
"MythicLib", "MMOItems", "MythicMobs", "MythicCrucible");
|
||||
private static final List<String> BUKKIT_SOFT_DEPEND_IDS = OPTIONAL_PLUGIN_IDS.stream()
|
||||
.filter(pluginId -> !"Multiverse-Core".equals(pluginId))
|
||||
.filter(pluginId -> !UNORDERED_PLUGIN_IDS.contains(pluginId))
|
||||
.toList();
|
||||
|
||||
@Test
|
||||
public void paperMetadataDeclaresBootstrapAndFoliaSupport() throws Exception {
|
||||
@@ -75,10 +62,13 @@ public class PaperPluginMetadataTest {
|
||||
+ " description: Allows survival players to fell Iris-managed trees with an axe.\n"
|
||||
+ " default: op"));
|
||||
for (String pluginId : JOINED_PLUGIN_IDS) {
|
||||
String loadOrder = UNORDERED_PLUGIN_IDS.contains(pluginId) ? "OMIT" : "BEFORE";
|
||||
assertTrue(metadata.contains(optionalDependencyBlock(pluginId, loadOrder, true)));
|
||||
assertTrue(metadata.contains(optionalDependencyBlock(pluginId, "OMIT", true)));
|
||||
}
|
||||
assertTrue(metadata.contains(optionalDependencyBlock("ExecutableItems", "BEFORE", false)));
|
||||
assertFalse(metadata.contains(" ExecutableItems:"));
|
||||
assertFalse(metadata.contains(" EcoItems:"));
|
||||
assertFalse(metadata.contains(" MythicLib:"));
|
||||
assertEquals(1, metadata.lines().filter(line -> line.equals(" load: AFTER")).count());
|
||||
assertEquals(0, metadata.lines().filter(line -> line.equals(" load: BEFORE")).count());
|
||||
assertTrue(metadata.contains(optionalDependencyBlock("Multiverse-Core", "AFTER", true)));
|
||||
}
|
||||
|
||||
@@ -95,7 +85,7 @@ public class PaperPluginMetadataTest {
|
||||
Map<String, Map<String, Object>> commands = metadata.getCommands();
|
||||
assertTrue(commands.containsKey("iris"));
|
||||
assertEquals(List.of("ir", "irs"), commands.get("iris").get("aliases"));
|
||||
assertEquals(BUKKIT_SOFT_DEPEND_IDS, metadata.getSoftDepend());
|
||||
assertTrue(metadata.getSoftDepend().isEmpty());
|
||||
assertEquals(List.of("Multiverse-Core"), metadata.getLoadBeforePlugins());
|
||||
Permission all = metadata.getPermissions().stream()
|
||||
.filter(permission -> "iris.all".equals(permission.getName()))
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
package art.arcane.iris.integration;
|
||||
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.volmlib.util.bukkit.papi.PlaceholderRegistration;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.server.PluginDisableEvent;
|
||||
import org.bukkit.event.server.PluginEnableEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.junit.Test;
|
||||
import org.mockito.MockedConstruction;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doCallRealMethod;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.mockConstruction;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class IrisPapiLifecycleTest {
|
||||
@Test
|
||||
public void lateEnableRegistersOnceAndReattachesAfterDisable() throws Exception {
|
||||
Iris iris = plugin(true);
|
||||
Plugin placeholders = namedPlugin("PlaceholderAPI");
|
||||
PluginManager plugins = mock(PluginManager.class);
|
||||
|
||||
try (MockedStatic<Bukkit> bukkit = mockStatic(Bukkit.class);
|
||||
MockedStatic<HandlerList> handlers = mockStatic(HandlerList.class);
|
||||
MockedStatic<PlaceholderRegistration> availability = mockStatic(PlaceholderRegistration.class);
|
||||
MockedStatic<IrisPapiInstaller> installer = mockStatic(IrisPapiInstaller.class);
|
||||
MockedConstruction<PlaceholderRegistration> registrations = mockConstruction(PlaceholderRegistration.class)) {
|
||||
bukkit.when(Bukkit::getPluginManager).thenReturn(plugins);
|
||||
availability.when(PlaceholderRegistration::isPlaceholderApiEnabled).thenReturn(true);
|
||||
installer.when(() -> IrisPapiInstaller.install(any(), any(), any())).thenReturn(true);
|
||||
|
||||
iris.onPlaceholderApiEnable(new PluginEnableEvent(placeholders));
|
||||
iris.onPlaceholderApiEnable(new PluginEnableEvent(placeholders));
|
||||
assertEquals(1, registrations.constructed().size());
|
||||
verify(plugins).registerEvents(any(IrisPapiListener.class), eq(iris));
|
||||
|
||||
iris.onPlaceholderApiDisable(new PluginDisableEvent(namedPlugin("Unrelated")));
|
||||
verify(registrations.constructed().getFirst(), never()).unregister();
|
||||
|
||||
iris.onPlaceholderApiDisable(new PluginDisableEvent(placeholders));
|
||||
verify(registrations.constructed().getFirst()).unregister();
|
||||
handlers.verify(() -> HandlerList.unregisterAll(any(IrisPapiListener.class)));
|
||||
|
||||
iris.onPlaceholderApiEnable(new PluginEnableEvent(placeholders));
|
||||
assertEquals(2, registrations.constructed().size());
|
||||
verify(plugins, times(2)).registerEvents(any(IrisPapiListener.class), eq(iris));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enableEventsCannotRegisterBeforeIrisIsReadyOrDuringShutdown() throws Exception {
|
||||
Iris iris = plugin(false);
|
||||
Plugin placeholders = namedPlugin("PlaceholderAPI");
|
||||
|
||||
try (MockedStatic<Bukkit> bukkit = mockStatic(Bukkit.class);
|
||||
MockedStatic<IrisPapiInstaller> installer = mockStatic(IrisPapiInstaller.class)) {
|
||||
iris.onPlaceholderApiEnable(new PluginEnableEvent(placeholders));
|
||||
installer.verifyNoInteractions();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unrelatedEnableEventsDoNotRegisterPlaceholders() throws Exception {
|
||||
Iris iris = plugin(true);
|
||||
|
||||
try (MockedStatic<Bukkit> bukkit = mockStatic(Bukkit.class);
|
||||
MockedStatic<IrisPapiInstaller> installer = mockStatic(IrisPapiInstaller.class)) {
|
||||
iris.onPlaceholderApiEnable(new PluginEnableEvent(namedPlugin("Unrelated")));
|
||||
installer.verifyNoInteractions();
|
||||
}
|
||||
}
|
||||
|
||||
private static Iris plugin(boolean active) throws ReflectiveOperationException {
|
||||
Iris iris = mock(Iris.class);
|
||||
when(iris.getLogger()).thenReturn(Logger.getAnonymousLogger());
|
||||
doCallRealMethod().when(iris).onPlaceholderApiEnable(any());
|
||||
doCallRealMethod().when(iris).onPlaceholderApiDisable(any());
|
||||
Field lifecycle = Iris.class.getDeclaredField("papiLifecycleActive");
|
||||
lifecycle.setAccessible(true);
|
||||
lifecycle.setBoolean(iris, active);
|
||||
return iris;
|
||||
}
|
||||
|
||||
private static Plugin namedPlugin(String name) {
|
||||
Plugin plugin = mock(Plugin.class);
|
||||
when(plugin.getName()).thenReturn(name);
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user