Public Access
This commit is contained in:
1 parent
2da52f2658
commit
3242eb8192
6 files changed
+65
-8
No files matched your search
@@ -46,6 +46,10 @@ available, the platform is generated at Y=63. Reopen the book and click its prev
|
||||
Copies of the written book continue to resolve to the same persistent Age
|
||||
across server restarts.
|
||||
|
||||
New Age dimension folders use a Minecraft-safe lowercase form of the assigned
|
||||
name. Conflicts receive incrementing suffixes such as `_2` and `_3`; existing
|
||||
Age folders are never renamed.
|
||||
|
||||
Linking creates a bound Return Link to the exact source position and orientation
|
||||
according to the configured Return Link policy.
|
||||
|
||||
|
||||
@@ -210,6 +210,11 @@ Version 0.2.10.4 restores Mystcraft Legacy's standard linking sound for both
|
||||
Linking and Descriptive Books. Successful travel plays the sound at its source
|
||||
and destination with the legacy volume and pitch variation.
|
||||
|
||||
Version 0.2.10.5 derives each new Age's saved dimension folder from its
|
||||
player-assigned name. Names are converted to safe lowercase resource paths,
|
||||
and folder conflicts receive `_2`, `_3`, and later numeric suffixes. Existing
|
||||
UUID-based dimension folders remain unchanged for save compatibility.
|
||||
|
||||
The preview code is separated behind a renderer interface and uses an isolated
|
||||
Ponder level populated from server-authoritative destination snapshots.
|
||||
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ mod_name=Rivencraft
|
||||
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||
mod_license=LGPL-3.0-or-later
|
||||
# The mod version. Rivencraft uses the x.y.z.b policy documented in README.md.
|
||||
mod_version=0.2.10.4
|
||||
mod_version=0.2.10.5
|
||||
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
||||
# This should match the base package used for the mod sources.
|
||||
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
||||
|
||||
@@ -33,6 +33,28 @@ public record AgeDefinition(
|
||||
return ResourceLocation.fromNamespaceAndPath(Rivencraft.MOD_ID, "age/" + ageId);
|
||||
}
|
||||
|
||||
public static ResourceLocation namedDimensionId(String ageName) {
|
||||
StringBuilder pathName = new StringBuilder();
|
||||
boolean lastWasSeparator = false;
|
||||
for (int index = 0; index < ageName.length(); index++) {
|
||||
char character = Character.toLowerCase(ageName.charAt(index));
|
||||
if (character >= 'a' && character <= 'z' || character >= '0' && character <= '9') {
|
||||
pathName.append(character);
|
||||
lastWasSeparator = false;
|
||||
} else if (!lastWasSeparator && !pathName.isEmpty()) {
|
||||
pathName.append('_');
|
||||
lastWasSeparator = true;
|
||||
}
|
||||
}
|
||||
while (!pathName.isEmpty() && pathName.charAt(pathName.length() - 1) == '_') {
|
||||
pathName.deleteCharAt(pathName.length() - 1);
|
||||
}
|
||||
if (pathName.isEmpty()) {
|
||||
pathName.append("unnamed_age");
|
||||
}
|
||||
return ResourceLocation.fromNamespaceAndPath(Rivencraft.MOD_ID, "age/" + pathName);
|
||||
}
|
||||
|
||||
public AgeDefinition withSpawn(BlockPos spawn) {
|
||||
return new AgeDefinition(id, name, seed, generationProfile, generationVersion,
|
||||
createdGameTime, dimensionId, spawn.immutable());
|
||||
|
||||
@@ -71,14 +71,16 @@ public final class AgeRegistrySavedData extends SavedData {
|
||||
return existing;
|
||||
}
|
||||
|
||||
String name = normalizedName(reference.name());
|
||||
ResourceLocation dimensionId = availableDimensionId(name);
|
||||
AgeDefinition created = new AgeDefinition(
|
||||
reference.id(),
|
||||
normalizedName(reference.name()),
|
||||
ageSeed(worldSeed, reference.id()),
|
||||
name,
|
||||
ageSeed(worldSeed, dimensionId),
|
||||
AgeDefinition.RANDOM_VANILLA_PROFILE,
|
||||
AgeDefinition.CURRENT_GENERATION_VERSION,
|
||||
createdGameTime,
|
||||
AgeDefinition.dimensionId(reference.id()),
|
||||
dimensionId,
|
||||
null
|
||||
);
|
||||
ages.put(created.id(), created);
|
||||
@@ -150,9 +152,25 @@ public final class AgeRegistrySavedData extends SavedData {
|
||||
return normalized.isEmpty() ? "Unnamed Age" : normalized;
|
||||
}
|
||||
|
||||
private static long ageSeed(long worldSeed, UUID ageId) {
|
||||
private ResourceLocation availableDimensionId(String ageName) {
|
||||
ResourceLocation base = AgeDefinition.namedDimensionId(ageName);
|
||||
ResourceLocation candidate = base;
|
||||
int suffix = 2;
|
||||
while (dimensionIdInUse(candidate)) {
|
||||
candidate = ResourceLocation.fromNamespaceAndPath(
|
||||
base.getNamespace(), base.getPath() + "_" + suffix++
|
||||
);
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private boolean dimensionIdInUse(ResourceLocation candidate) {
|
||||
return ages.values().stream().anyMatch(age -> age.dimensionId().equals(candidate));
|
||||
}
|
||||
|
||||
private static long ageSeed(long worldSeed, ResourceLocation dimensionId) {
|
||||
// Dynamic Dimensions AR derives each level seed from the primary world
|
||||
// seed and the dimension key. Persist the same value as Age metadata.
|
||||
return worldSeed + AgeDefinition.dimensionId(ageId).hashCode();
|
||||
return worldSeed + dimensionId.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,8 @@ public final class LinkingGameTests {
|
||||
@GameTest(template = "empty")
|
||||
public static void ageDefinitionIsCreatedOnceWithDeterministicData(GameTestHelper helper) {
|
||||
UUID ageId = UUID.randomUUID();
|
||||
AgeReference reference = new AgeReference(ageId, "Test Age");
|
||||
String requestedName = "Test Age " + ageId;
|
||||
AgeReference reference = new AgeReference(ageId, requestedName);
|
||||
|
||||
AgeDefinition first = AgeRegistrySavedData.resolveOrCreate(helper.getLevel(), reference);
|
||||
AgeDefinition second = AgeRegistrySavedData.resolveOrCreate(
|
||||
@@ -91,10 +92,17 @@ public final class LinkingGameTests {
|
||||
AgeDefinition another = AgeRegistrySavedData.resolveOrCreate(
|
||||
helper.getLevel(), new AgeReference(UUID.randomUUID(), "Another Age")
|
||||
);
|
||||
AgeDefinition conflicting = AgeRegistrySavedData.resolveOrCreate(
|
||||
helper.getLevel(), new AgeReference(UUID.randomUUID(), requestedName)
|
||||
);
|
||||
|
||||
helper.assertValueEqual(second, first,
|
||||
"An existing Age UUID must retain its original immutable generation definition");
|
||||
helper.assertValueEqual(first.name(), "Test Age", "The first book name must become the canonical Age name");
|
||||
helper.assertValueEqual(first.name(), requestedName, "The first book name must become the canonical Age name");
|
||||
helper.assertValueEqual(first.dimensionId(), AgeDefinition.namedDimensionId(requestedName),
|
||||
"A new Age dimension folder must derive from the player-assigned name");
|
||||
helper.assertValueEqual(conflicting.dimensionId().getPath(), first.dimensionId().getPath() + "_2",
|
||||
"A conflicting Age dimension folder must receive an incrementing suffix");
|
||||
helper.assertValueEqual(first.generationProfile(), AgeDefinition.RANDOM_VANILLA_PROFILE,
|
||||
"A page-free book must use the random vanilla generation profile");
|
||||
helper.assertValueEqual(first.generationVersion(), AgeDefinition.CURRENT_GENERATION_VERSION,
|
||||
|
||||
Reference in new issue
Block a user