mirror of
https://github.com/Multiverse/Multiverse-Core.git
synced 2026-11-04 16:50:03 -05:00
Compare commits
18
Commits
5.8.1-pre.2
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34185b90a9 | ||
|
|
5d57bbfda9 | ||
|
|
a78459ab77 | ||
|
|
0ad795b27f | ||
|
|
2765cf6f3e | ||
|
|
7a9b21f9a0 | ||
|
|
f55ceda42c | ||
|
|
893c69a6e1 | ||
|
|
6bb93766d4 | ||
|
|
fe49efe276 | ||
|
|
8de11b59a1 | ||
|
|
744450cbbb | ||
|
|
ecf107563d | ||
|
|
f7ea31d949 | ||
|
|
1bf4b99ecf | ||
|
|
4de4e5a68e | ||
|
|
f6a8a6b3b4 | ||
|
|
d485238c56 |
No files matched your search
@@ -0,0 +1,77 @@
|
||||
name: 'Generic: Artifact Comment'
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
plugin_name:
|
||||
description: 'Plugin name used by Generic: Test'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
comment-artifact:
|
||||
if: >-
|
||||
github.event_name == 'workflow_run' &&
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
# Run separately so fork builds can post comments without a write token.
|
||||
# Do not check out or execute code from the pull request in this workflow.
|
||||
- name: Find pull request and artifact
|
||||
id: artifact
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PLUGIN_NAME: ${{ inputs.plugin_name }}
|
||||
with:
|
||||
script: |
|
||||
const run = context.payload.workflow_run;
|
||||
// Fork runs can have an empty pull_requests array in the event.
|
||||
const pulls = await github.paginate(github.rest.pulls.list, {
|
||||
...context.repo,
|
||||
state: 'open',
|
||||
head: `${run.head_repository.owner.login}:${run.head_branch}`,
|
||||
per_page: 100,
|
||||
});
|
||||
const pull = pulls.find(pr =>
|
||||
pr.head.repo?.id === run.head_repository.id &&
|
||||
pr.head.ref === run.head_branch &&
|
||||
pr.head.sha === run.head_sha
|
||||
);
|
||||
if (!pull) {
|
||||
core.info('No open pull request matches this build; skipping comment.');
|
||||
return;
|
||||
}
|
||||
|
||||
const artifacts = await github.paginate(github.rest.actions.listWorkflowRunArtifacts, {
|
||||
...context.repo,
|
||||
run_id: run.id,
|
||||
per_page: 100,
|
||||
});
|
||||
const artifact = artifacts.find(item =>
|
||||
item.name === `${process.env.PLUGIN_NAME}-pr${pull.number}` && !item.expired
|
||||
);
|
||||
if (!artifact) {
|
||||
core.info('No build artifact is available; skipping comment.');
|
||||
return;
|
||||
}
|
||||
|
||||
core.setOutput('number', pull.number);
|
||||
core.setOutput('url', `${run.html_url}/artifacts/${artifact.id}`);
|
||||
|
||||
- name: Comment artifact
|
||||
if: steps.artifact.outputs.number != ''
|
||||
uses: marocchino/sticky-pull-request-comment@v3
|
||||
with:
|
||||
number: ${{ steps.artifact.outputs.number }}
|
||||
header: build-artifact-${{ inputs.plugin_name }}
|
||||
message: |
|
||||
### ✅ `${{ inputs.plugin_name }}` test build ready
|
||||
|
||||
Last updated for: [${{ github.event.workflow_run.head_sha }}](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.event.workflow_run.head_sha }})
|
||||
**Download:** [${{ inputs.plugin_name }}-pr${{ steps.artifact.outputs.number }}.zip](${{ steps.artifact.outputs.url }})
|
||||
|
||||
View [CI build and checks](${{ github.event.workflow_run.html_url }}) for more details.
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
files: '["${{ github.workspace }}/${{ inputs.plugin_name }}-${{ steps.release-info.outputs.tag_name }}.jar"]'
|
||||
name: ${{ steps.release-info.outputs.tag_name }}
|
||||
changelog: ${{ steps.release-artifact.outputs.body }}
|
||||
game_versions: 26.2, 26.1.2, 26.1.1, 26.1, 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2
|
||||
game_versions: 26.3, 26.2, 26.1.2, 26.1.1, 26.1, 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2
|
||||
version_type: ${{ steps.parse-release-type.outputs.release_type }}
|
||||
loaders: bukkit, spigot, paper, purpur
|
||||
dependencies: ${{ inputs.modrinth_dependencies }}
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
changelog: ${{ steps.release-artifact.outputs.body }}
|
||||
changelog_type: markdown
|
||||
display_name: ${{ steps.release-info.outputs.tag_name }}
|
||||
game_versions: 26.2, 26.1.2, 26.1.1, 26.1, 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2
|
||||
game_versions: 26.3, 26.2, 26.1.2, 26.1.1, 26.1, 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2
|
||||
release_type: ${{ steps.parse-release-type.outputs.release_type }}
|
||||
project_relations: ${{ inputs.dbo_project_relations }}
|
||||
file_path: ${{ github.workspace }}/${{ inputs.plugin_name }}-${{ steps.release-info.outputs.tag_name }}.jar
|
||||
@@ -136,5 +136,5 @@ jobs:
|
||||
channel: ${{ steps.parse-release-type.outputs.release_type }}
|
||||
files: '[{"path": "${{ github.workspace }}/${{ inputs.plugin_name }}-${{ steps.release-info.outputs.tag_name }}.jar", "platforms": ["PAPER"]}]'
|
||||
description: ${{ steps.release-artifact.outputs.body }}
|
||||
platform_dependencies: '{"PAPER": ["1.18.2-26.2"]}'
|
||||
platform_dependencies: '{"PAPER": ["1.18.2-26.3"]}'
|
||||
plugin_dependencies: ${{ inputs.hangar_plugin_dependencies }}
|
||||
@@ -36,13 +36,3 @@ jobs:
|
||||
with:
|
||||
name: ${{ inputs.plugin_name }}-pr${{ github.event.pull_request.number }}
|
||||
path: build/libs/${{ inputs.plugin_name }}-pr${{ github.event.pull_request.number }}.jar
|
||||
|
||||
comment-artifact:
|
||||
needs: test-and-artifact
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: benwoo1110/artifact-comment-action@v1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,15 @@
|
||||
name: 'PR: Artifact Comment'
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['PR: Test']
|
||||
types: [completed]
|
||||
|
||||
jobs:
|
||||
comment-artifact:
|
||||
permissions:
|
||||
actions: read
|
||||
pull-requests: write
|
||||
uses: ./.github/workflows/generic.artifact_comment.yml
|
||||
with:
|
||||
plugin_name: multiverse-core
|
||||
@@ -46,3 +46,6 @@ debug.txt
|
||||
|
||||
# Gradle
|
||||
.gradle
|
||||
|
||||
# Kotlin generated files
|
||||
/.kotlin/
|
||||
@@ -26,6 +26,7 @@ public enum MVCorei18n implements MessageKeyProvider {
|
||||
CONFIG_STRING_PARSER_INVALIDDOUBLE,
|
||||
CONFIG_STRING_PARSER_INVALIDFLOAT,
|
||||
CONFIG_STRING_PARSER_INVALIDLONG,
|
||||
CONFIG_STRING_PARSER_INVALIDMATERIAL,
|
||||
|
||||
// core config
|
||||
CORE_CONFIG_CUSTOMPORTALSEARCHRADIUS_NONNEGATIVE,
|
||||
|
||||
+2
-1
@@ -264,7 +264,8 @@ public final class AsyncSafetyTeleporterAction {
|
||||
Logging.finer("Teleported async %s to %s", teleportee.getName(), location);
|
||||
return Attempt.success(null);
|
||||
}
|
||||
Logging.warning("Failed to async teleport %s to %s", teleportee.getName(), location);
|
||||
Logging.warning("Failed to async teleport %s to %s. This is usually caused by another " +
|
||||
"plugin canceling the teleport event and not a bug with Multiverse.", teleportee.getName(), location);
|
||||
return Attempt.failure(TeleportFailureReason.TELEPORT_FAILED);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ public final class LoadedMultiverseWorld extends MultiverseWorld {
|
||||
worldConfig.load();
|
||||
worldConfig.setEnvironment(world.getEnvironment());
|
||||
worldConfig.setSeed(world.getSeed());
|
||||
worldConfig.setLegacyWorldName(world.getName());
|
||||
}
|
||||
|
||||
private void setupSpawnLocation(World world) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.util.Objects;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import io.vavr.control.Option;
|
||||
import io.vavr.control.Try;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Difficulty;
|
||||
@@ -26,6 +27,9 @@ import org.mvplugins.multiverse.core.config.node.ConfigNode;
|
||||
import org.mvplugins.multiverse.core.config.node.ListConfigNode;
|
||||
import org.mvplugins.multiverse.core.config.node.NodeGroup;
|
||||
import org.mvplugins.multiverse.core.economy.MVEconomist;
|
||||
import org.mvplugins.multiverse.core.exceptions.MultiverseException;
|
||||
import org.mvplugins.multiverse.core.locale.MVCorei18n;
|
||||
import org.mvplugins.multiverse.core.locale.message.Message;
|
||||
import org.mvplugins.multiverse.core.utils.MaterialConverter;
|
||||
import org.mvplugins.multiverse.core.utils.text.ChatTextFormatter;
|
||||
import org.mvplugins.multiverse.core.world.helpers.AliasNameConflictChecker;
|
||||
@@ -35,6 +39,8 @@ import org.mvplugins.multiverse.core.world.location.NullSpawnLocation;
|
||||
import org.mvplugins.multiverse.core.world.location.SpawnLocation;
|
||||
import org.mvplugins.multiverse.core.world.entity.EntitySpawnConfig;
|
||||
|
||||
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
|
||||
|
||||
/**
|
||||
* Represents nodes in a world configuration.
|
||||
*/
|
||||
@@ -169,11 +175,22 @@ final class WorldConfigNodes {
|
||||
final ConfigNode<Material> entryFeeCurrency = node(ConfigNode.builder("entry-fee.currency", Material.class)
|
||||
.defaultValue(MVEconomist.VAULT_ECONOMY_MATERIAL)
|
||||
.name("entryfee-currency")
|
||||
.stringParser((value, clazz) -> {
|
||||
if (value != null && value.equalsIgnoreCase(MVEconomist.VAULT_ECONOMY_CODE)) {
|
||||
return Try.success(MVEconomist.VAULT_ECONOMY_MATERIAL);
|
||||
}
|
||||
return Try.of(() -> MaterialConverter.stringToMaterial(value))
|
||||
.filter(Objects::nonNull)
|
||||
.orElse(Try.failure(new MultiverseException(Message.of(
|
||||
MVCorei18n.CONFIG_STRING_PARSER_INVALIDMATERIAL,
|
||||
replace("{input}").with(value)))));
|
||||
})
|
||||
.serializer(new NodeSerializer<>() {
|
||||
@Override
|
||||
public Material deserialize(Object object, Class<Material> type) {
|
||||
return Option.of(object)
|
||||
.map(String::valueOf)
|
||||
.filter(s -> !s.isBlank())
|
||||
.map(materialStr -> {
|
||||
if (materialStr.equalsIgnoreCase(MVEconomist.VAULT_ECONOMY_CODE)) {
|
||||
return MVEconomist.VAULT_ECONOMY_MATERIAL;
|
||||
@@ -306,6 +323,8 @@ final class WorldConfigNodes {
|
||||
.hidden());
|
||||
|
||||
final ConfigNode<String> legacyWorldName = node(ConfigNode.builder("read-only.legacy-world-name", String.class)
|
||||
.onLoadAndChange((oldValue, newValue) -> worldManager.getWorldStore().changeLegacyWorldName(
|
||||
oldValue, newValue, keyOrName.usableKey()))
|
||||
.hidden());
|
||||
|
||||
final ConfigNode<Long> seed = node(ConfigNode.builder("read-only.seed", Long.class)
|
||||
|
||||
@@ -54,6 +54,7 @@ import org.mvplugins.multiverse.core.utils.compatibility.WorldCreatorCompatibili
|
||||
import org.mvplugins.multiverse.core.utils.result.Attempt;
|
||||
import org.mvplugins.multiverse.core.utils.result.FailureReason;
|
||||
import org.mvplugins.multiverse.core.utils.FileUtils;
|
||||
import org.mvplugins.multiverse.core.utils.text.ChatTextFormatter;
|
||||
import org.mvplugins.multiverse.core.world.biomeprovider.BiomeProviderFactory;
|
||||
import org.mvplugins.multiverse.core.world.entity.EntityPurger;
|
||||
import org.mvplugins.multiverse.core.world.generators.GeneratorProvider;
|
||||
@@ -244,7 +245,10 @@ public final class WorldManager {
|
||||
.filter(world -> !isLoadedWorld(world) && world.isAutoLoad())
|
||||
.forEach(world -> loadWorld(LoadWorldOptions.world(world))
|
||||
.onFailure(failure ->
|
||||
Logging.severe("Failed to autoload world %s: %s", world.getName(), failure))
|
||||
Logging.severe("Failed to autoload world '%s': %s: %s",
|
||||
world.getName(),
|
||||
failure,
|
||||
ChatTextFormatter.removeColor(failure.getFailureMessage().formatted())))
|
||||
.onSuccess(newMVWorld ->
|
||||
Logging.fine("Autoloaded world %s", newMVWorld.getName())));
|
||||
}
|
||||
@@ -282,15 +286,24 @@ public final class WorldManager {
|
||||
WorldKeyOrName keyOrName = keyOrNameWithOptions.keyOrName();
|
||||
CreateWorldOptions options = keyOrNameWithOptions.options();
|
||||
String generatorString = generatorProvider.parseGeneratorString(keyOrName.usableName(), options.generator());
|
||||
WorldCreator worldCreator = WorldCreatorCompatibility.ofKeyOrName(keyOrName)
|
||||
.environment(options.environment())
|
||||
.generateStructures(options.generateStructures())
|
||||
.generatorSettings(options.generatorSettings())
|
||||
.seed(options.seed())
|
||||
.type(options.worldType());
|
||||
WorldCreatorCompatibility.setBonusChest(worldCreator, options.bonusChest());
|
||||
options.forcedSpawnPosition().peek(position -> WorldCreatorCompatibility.setForcedSpawnPosition(worldCreator, position));
|
||||
return addBiomeProviderToCreator(worldCreator, keyOrName.usableName(), options.biome())
|
||||
|
||||
return Try.of(() -> {
|
||||
WorldCreator creator = WorldCreatorCompatibility.ofKeyOrName(keyOrName)
|
||||
.environment(options.environment())
|
||||
.generateStructures(options.generateStructures())
|
||||
.generatorSettings(options.generatorSettings())
|
||||
.seed(options.seed())
|
||||
.type(options.worldType());
|
||||
WorldCreatorCompatibility.setBonusChest(creator, options.bonusChest());
|
||||
options.forcedSpawnPosition()
|
||||
.peek(position -> WorldCreatorCompatibility.setForcedSpawnPosition(creator, position));
|
||||
return creator;
|
||||
})
|
||||
.fold(throwable -> Attempt.<WorldCreator, WorldCreatorFailureReason>failure(WorldCreatorFailureReason.BUKKIT_CREATION_FAILED,
|
||||
Replace.WORLD.with(keyOrName.usableName()),
|
||||
Replace.ERROR.with(throwable)),
|
||||
Attempt::<WorldCreator, WorldCreatorFailureReason>success)
|
||||
.mapAttempt(creator -> addBiomeProviderToCreator(creator, keyOrName.usableName(), options.biome()))
|
||||
.mapAttempt(creator -> addGeneratorToCreator(creator, generatorString))
|
||||
.mapAttempt(this::createBukkitWorld)
|
||||
.transform(CreateFailureReason.WORLD_CREATOR_FAILED)
|
||||
@@ -359,11 +372,15 @@ public final class WorldManager {
|
||||
WorldKeyOrName keyOrName = keyOrNameWithOptions.keyOrName();
|
||||
ImportWorldOptions options = keyOrNameWithOptions.options();
|
||||
String generatorString = generatorProvider.parseGeneratorString(keyOrName.usableName(), options.generator());
|
||||
WorldCreator worldCreator = WorldCreatorCompatibility.ofKeyOrName(keyOrName)
|
||||
.environment(options.environment())
|
||||
.generatorSettings(options.generatorSettings());
|
||||
|
||||
return addBiomeProviderToCreator(worldCreator, keyOrName.usableName(), options.biome())
|
||||
return Try.of(() -> WorldCreatorCompatibility.ofKeyOrName(keyOrName)
|
||||
.environment(options.environment())
|
||||
.generatorSettings(options.generatorSettings()))
|
||||
.fold(throwable -> Attempt.<WorldCreator, WorldCreatorFailureReason>failure(WorldCreatorFailureReason.BUKKIT_CREATION_FAILED,
|
||||
Replace.WORLD.with(keyOrName.usableName()),
|
||||
Replace.ERROR.with(throwable)),
|
||||
Attempt::<WorldCreator, WorldCreatorFailureReason>success)
|
||||
.mapAttempt(creator -> addBiomeProviderToCreator(creator, keyOrName.usableName(), options.biome()))
|
||||
.mapAttempt(creator -> addGeneratorToCreator(creator, generatorString))
|
||||
.mapAttempt(this::createBukkitWorld)
|
||||
.transform(ImportFailureReason.WORLD_CREATOR_FAILED)
|
||||
@@ -528,10 +545,14 @@ public final class WorldManager {
|
||||
}
|
||||
}
|
||||
|
||||
WorldCreator worldCreator = WorldCreatorCompatibility.ofNameAndKey(mvWorld.getKey(), mvWorld.getName())
|
||||
.environment(mvWorld.getEnvironment())
|
||||
.seed(mvWorld.getSeed());
|
||||
return addBiomeProviderToCreator(worldCreator, mvWorld.getName(), mvWorld.getBiome())
|
||||
return Try.of(() -> WorldCreatorCompatibility.ofNameAndKey(mvWorld.getKey(), mvWorld.getName())
|
||||
.environment(mvWorld.getEnvironment())
|
||||
.seed(mvWorld.getSeed()))
|
||||
.fold(throwable -> Attempt.<WorldCreator, WorldCreatorFailureReason>failure(WorldCreatorFailureReason.BUKKIT_CREATION_FAILED,
|
||||
Replace.WORLD.with(mvWorld.getName()),
|
||||
Replace.ERROR.with(throwable)),
|
||||
Attempt::<WorldCreator, WorldCreatorFailureReason>success)
|
||||
.mapAttempt(creator -> addBiomeProviderToCreator(creator, mvWorld.getName(), mvWorld.getBiome()))
|
||||
.mapAttempt(creator -> addGeneratorToCreator(creator, mvWorld.getGenerator()))
|
||||
.mapAttempt(this::createBukkitWorld)
|
||||
.transform(LoadFailureReason.WORLD_CREATOR_FAILED)
|
||||
|
||||
@@ -120,6 +120,29 @@ final class WorldStore {
|
||||
unloadedList.add(unloadedMap.get(world.getKey().toString()));
|
||||
}
|
||||
|
||||
void changeLegacyWorldName(@Nullable String oldName, @Nullable String newName, @NotNull NamespacedKey worldKey) {
|
||||
if (Objects.equals(oldName, newName)) {
|
||||
return;
|
||||
}
|
||||
String key = worldKey.toString();
|
||||
updateLegacyWorldName(loadedMap, oldName, newName, key);
|
||||
updateLegacyWorldName(unloadedMap, oldName, newName, key);
|
||||
}
|
||||
|
||||
private <T extends MultiverseWorld> void updateLegacyWorldName(
|
||||
Map<String, T> map, @Nullable String oldName, @Nullable String newName, String key) {
|
||||
T world = map.get(key);
|
||||
if (world == null) {
|
||||
return;
|
||||
}
|
||||
if (oldName != null && !key.equalsIgnoreCase(oldName)) {
|
||||
map.remove(oldName, world);
|
||||
}
|
||||
if (newName != null) {
|
||||
map.put(newName, world);
|
||||
}
|
||||
}
|
||||
|
||||
void changeAlias(@Nullable String oldAlias, @Nullable String newAlias, @NotNull NamespacedKey worldKey) {
|
||||
if (Objects.equals(oldAlias, newAlias)) {
|
||||
// nothing changed, ignore
|
||||
|
||||
@@ -18,7 +18,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
|
||||
import org.mvplugins.multiverse.core.MultiverseCore;
|
||||
import org.mvplugins.multiverse.core.utils.ServerProperties;
|
||||
import org.mvplugins.multiverse.core.utils.result.Attempt;
|
||||
import org.mvplugins.multiverse.core.world.helpers.DimensionFinder;
|
||||
import org.mvplugins.multiverse.core.world.key.WorldKeyOrName;
|
||||
import org.mvplugins.multiverse.core.world.key.WorldKeyParseFailReason;
|
||||
|
||||
@@ -34,16 +36,18 @@ final class WorldsConfigManager {
|
||||
|
||||
private final SortedMap<WorldKeyOrName, WorldConfig> worldConfigMap;
|
||||
private final File worldConfigFile;
|
||||
private final ServerProperties serverProperties;
|
||||
private YamlConfiguration worldsConfig;
|
||||
|
||||
private final MultiverseCore multiverseCore;
|
||||
|
||||
@Inject
|
||||
WorldsConfigManager(@NotNull MultiverseCore core, @NotNull MultiverseCore multiverseCore) {
|
||||
WorldsConfigManager(@NotNull MultiverseCore multiverseCore, @NotNull ServerProperties serverProperties) {
|
||||
worldConfigMap = new TreeMap<>();
|
||||
worldConfigFile = core.getDataFolder().toPath().resolve(CONFIG_FILENAME).toFile();
|
||||
worldConfigFile = multiverseCore.getDataFolder().toPath().resolve(CONFIG_FILENAME).toFile();
|
||||
|
||||
this.multiverseCore = multiverseCore;
|
||||
this.serverProperties = serverProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,9 +206,59 @@ final class WorldsConfigManager {
|
||||
worldConfigMap.remove(s);
|
||||
}
|
||||
|
||||
migrateLevelNameChange();
|
||||
|
||||
return new NewAndRemovedWorlds(newWorldsAdded, worldsRemoved);
|
||||
}
|
||||
|
||||
private void migrateLevelNameChange() {
|
||||
serverProperties.getLevelName()
|
||||
.peek(levelName -> {
|
||||
boolean didMigrate = migrateLevelNameChange(
|
||||
WorldKeyOrName.parseKey(NamespacedKey.minecraft("overworld")),
|
||||
levelName
|
||||
);
|
||||
didMigrate = migrateLevelNameChange(
|
||||
WorldKeyOrName.parseKey(NamespacedKey.minecraft("the_nether")),
|
||||
DimensionFinder.DEFAULT_NETHER_FORMAT.replaceOverworld(levelName)
|
||||
) || didMigrate;
|
||||
didMigrate = migrateLevelNameChange(
|
||||
WorldKeyOrName.parseKey(NamespacedKey.minecraft("the_end")),
|
||||
DimensionFinder.DEFAULT_END_FORMAT.replaceOverworld(levelName)
|
||||
) || didMigrate;
|
||||
|
||||
if (didMigrate) {
|
||||
Logging.warning("We have detected and migrated your default world's name due to a level-name change.");
|
||||
Logging.warning("Please check your worlds.yml file and world list to ensure everything is correct.");
|
||||
Logging.warning("Additionally, all references to the old world name will no longer work. " +
|
||||
"Please update all references within your configuration files/plugins.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean migrateLevelNameChange(WorldKeyOrName keyOrName, String newWorldName) {
|
||||
return getWorldConfig(keyOrName).map(worldConfig -> {
|
||||
if (newWorldName.equals(worldConfig.getLegacyWorldName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean hasConflictKey = WorldKeyOrName.parseKey(newWorldName).map(this::getWorldConfig)
|
||||
.fold(failure -> false, Option::isDefined);
|
||||
if (hasConflictKey) {
|
||||
Logging.severe("Unable to migrate default world's name caused by level-name change.");
|
||||
Logging.severe("We have detected a clash between the default name '%s' and an existing non-default world in your worlds.yml file.", newWorldName);
|
||||
Logging.severe("Please change the level-name in server.properties to something else and restart the server.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Logging.info("Updating legacy world name for %s from %s to %s due to level-name change.",
|
||||
keyOrName, worldConfig.getLegacyWorldName(), newWorldName);
|
||||
worldConfig.setLegacyWorldName(newWorldName);
|
||||
worldConfig.save();
|
||||
return true;
|
||||
}).getOrElse(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the worlds.yml file has been loaded.
|
||||
*
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.SpawnCategory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.mvplugins.multiverse.core.config.CoreConfig;
|
||||
import org.mvplugins.multiverse.core.config.handle.MemoryConfigurationHandle;
|
||||
import org.mvplugins.multiverse.core.config.handle.StringPropertyHandle;
|
||||
@@ -16,13 +17,19 @@ import org.mvplugins.multiverse.core.config.node.ConfigNode;
|
||||
import org.mvplugins.multiverse.core.config.node.ListConfigNode;
|
||||
import org.mvplugins.multiverse.core.config.node.Node;
|
||||
import org.mvplugins.multiverse.core.config.node.NodeGroup;
|
||||
import org.mvplugins.multiverse.core.config.node.functions.NodeStringParser;
|
||||
import org.mvplugins.multiverse.core.config.node.serializer.NodeSerializer;
|
||||
import org.mvplugins.multiverse.core.exceptions.MultiverseException;
|
||||
import org.mvplugins.multiverse.core.locale.MVCorei18n;
|
||||
import org.mvplugins.multiverse.core.locale.message.Message;
|
||||
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
|
||||
import org.mvplugins.multiverse.core.world.MultiverseWorld;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
|
||||
|
||||
public final class SpawnCategoryConfig {
|
||||
|
||||
private final CoreConfig config;
|
||||
@@ -177,6 +184,7 @@ public final class SpawnCategoryConfig {
|
||||
final ConfigNode<Integer> tickRate = node(ConfigNode.builder("tick-rate", Integer.class)
|
||||
.defaultValue(-2)
|
||||
.suggester(input -> List.of("@unset", "@bukkit", "10", "100", "400", "1000"))
|
||||
.stringParser(SpawnValueStringParser.INSTANCE)
|
||||
.serializer(SpawnValueSerializer.INSTANCE)
|
||||
.onLoadAndChange((oldValue, newValue) -> applyConfigToWorld())
|
||||
.onChange((sender, oldValue, newValue) -> {
|
||||
@@ -193,6 +201,7 @@ public final class SpawnCategoryConfig {
|
||||
final ConfigNode<Integer> spawnLimit = node(ConfigNode.builder("spawn-limit", Integer.class)
|
||||
.defaultValue(-2)
|
||||
.suggester(input -> List.of("@unset", "@bukkit", "10", "100", "400", "1000"))
|
||||
.stringParser(SpawnValueStringParser.INSTANCE)
|
||||
.serializer(SpawnValueSerializer.INSTANCE)
|
||||
.onLoadAndChange((oldValue, newValue) -> applyConfigToWorld())
|
||||
.onChange((sender, oldValue, newValue) -> {
|
||||
@@ -215,6 +224,27 @@ public final class SpawnCategoryConfig {
|
||||
.build());
|
||||
}
|
||||
|
||||
private static class SpawnValueStringParser implements NodeStringParser<Integer> {
|
||||
private static final SpawnValueStringParser INSTANCE = new SpawnValueStringParser();
|
||||
|
||||
@Override
|
||||
public @NotNull Try<Integer> parse(@Nullable String input, @NotNull Class<Integer> type) {
|
||||
String str = String.valueOf(input).trim();
|
||||
if (str.equalsIgnoreCase("@unset")) {
|
||||
return Try.success(-2);
|
||||
}
|
||||
if (str.equalsIgnoreCase("@bukkit")) {
|
||||
return Try.success(-1);
|
||||
}
|
||||
try {
|
||||
return Try.success(Integer.parseInt(str));
|
||||
} catch (NumberFormatException e) {
|
||||
return Try.failure(new MultiverseException(Message.of(MVCorei18n.CONFIG_STRING_PARSER_INVALIDINTEGER,
|
||||
replace("{input}").with(input))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class SpawnValueSerializer implements NodeSerializer<Integer> {
|
||||
private static final SpawnValueSerializer INSTANCE = new SpawnValueSerializer();
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ mv-core.config.string.parser.invalidinteger=Unable to convert '{input}' to numbe
|
||||
mv-core.config.string.parser.invaliddouble=Unable to convert '{input}' to number. (double)
|
||||
mv-core.config.string.parser.invalidfloat=Unable to convert '{input}' to number. (float)
|
||||
mv-core.config.string.parser.invalidlong=Unable to convert '{input}' to number. (long)
|
||||
mv-core.config.string.parser.invalidmaterial='{input}' is not a valid material.
|
||||
|
||||
# core config
|
||||
mv-core.core.config.customportalsearchradius.nonnegative=The value must be greater than or equal to 0.
|
||||
|
||||
@@ -49,6 +49,38 @@ class WorldManagerTest : TestWithMockBukkit() {
|
||||
assertNotNull(world2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Legacy world name changes update loaded and unloaded lookups`() {
|
||||
val worldKey = world.key.toString()
|
||||
assertTrue(world.worldConfig.setLegacyWorldName("renamed_world").isSuccess)
|
||||
|
||||
assertFalse(worldManager.getWorld("world").isDefined)
|
||||
assertSame(world, worldManager.getLoadedWorld("RENAMED_WORLD").get())
|
||||
assertSame(world, worldManager.getLoadedWorld(worldKey).get())
|
||||
|
||||
assertTrue(worldManager.unloadWorld(UnloadWorldOptions.world(world)).isSuccess)
|
||||
assertFalse(worldManager.getWorld("world").isDefined)
|
||||
assertFalse(worldManager.getLoadedWorld("renamed_world").isDefined)
|
||||
assertEquals(world.key, worldManager.getUnloadedWorld("RENAMED_WORLD").get().key)
|
||||
assertEquals(world.key, worldManager.getUnloadedWorld(worldKey).get().key)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Setting up loaded world config refreshes legacy world name lookups`() {
|
||||
assertTrue(world.worldConfig.setLegacyWorldName("stale_name").isSuccess)
|
||||
assertTrue(world.worldConfig.save().isSuccess)
|
||||
|
||||
world.setWorldConfig(world.worldConfig)
|
||||
|
||||
assertEquals("world", world.name)
|
||||
assertSame(world, worldManager.getLoadedWorld("world").get())
|
||||
assertFalse(worldManager.getWorld("stale_name").isDefined)
|
||||
|
||||
assertTrue(worldManager.unloadWorld(UnloadWorldOptions.world(world)).isSuccess)
|
||||
assertTrue(worldManager.getUnloadedWorld("world").isDefined)
|
||||
assertFalse(worldManager.getWorld("stale_name").isDefined)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Create world with custom options`() {
|
||||
assertTrue(worldManager.createWorld(
|
||||
|
||||
Reference in new issue
Block a user