Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package dev.galacticraft.dynamicdimensions.api;

import org.joml.Vector3f;

/**
* Generic physics/environment properties for a dynamic dimension.
*
* DynamicDimensions does not decide what these values mean.
* Optional compat layers, such as Sable compat, may consume them.
*/
public abstract class DynamicDimensionProperties {
public abstract int priority();

public abstract Vector3f baseGravity();

public abstract double basePressure();

public abstract float universalDrag();

public abstract Vector3f magneticNorth();
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ default boolean canDeleteDimension(@NotNull ResourceLocation id) {
*/
@Nullable ServerLevel createDynamicDimension(@NotNull ResourceLocation id, @NotNull ChunkGenerator chunkGenerator, @NotNull DimensionType type);

/**
* Registers a new dimension and applies optional dynamic-dimension properties.
*
* @since 0.10.0
Comment thread
maxryan008 marked this conversation as resolved.
*/
@Nullable ServerLevel createDynamicDimension(@NotNull ResourceLocation id, @NotNull ChunkGenerator chunkGenerator, @NotNull DimensionType type, @NotNull DynamicDimensionProperties properties);

/**
* Registers a new dimension and updates all clients with the new dimension.
* If world data already exists for this dimension it will be used, otherwise it will be created.
Expand All @@ -137,6 +144,67 @@ default boolean canDeleteDimension(@NotNull ResourceLocation id) {
*/
@Nullable ServerLevel loadDynamicDimension(@NotNull ResourceLocation id, @NotNull ChunkGenerator chunkGenerator, @NotNull DimensionType type);

/**
* Loads a dynamic dimension and applies optional dynamic-dimension properties.
*
* @since 0.10.0
*/
@Nullable ServerLevel loadDynamicDimension(@NotNull ResourceLocation id, @NotNull ChunkGenerator chunkGenerator, @NotNull DimensionType type, @NotNull DynamicDimensionProperties properties);

/**
* Sets properties for a dynamic dimension.
*
* <p>If the dimension is already loaded, compatible backends such as Sable
* may apply these immediately.</p>
*
* @since 0.10.0
*/
void setDimensionProperties(@NotNull ResourceKey<Level> key, @NotNull DynamicDimensionProperties properties);

/**
* Sets properties for a dynamic dimension.
*
* @since 0.10.0
*/
default void setDimensionProperties(@NotNull ResourceLocation id, @NotNull DynamicDimensionProperties properties) {
this.setDimensionProperties(ResourceKey.create(Registries.DIMENSION, id), properties);
}

/**
* Gets the currently stored properties for a dynamic dimension.
*
* @since 0.10.0
*/
@Nullable DynamicDimensionProperties getDimensionProperties(@NotNull ResourceKey<Level> key);

/**
* Gets the currently stored properties for a dynamic dimension.
*
* @since 0.10.0
*/
default @Nullable DynamicDimensionProperties getDimensionProperties(@NotNull ResourceLocation id) {
return this.getDimensionProperties(ResourceKey.create(Registries.DIMENSION, id));
}

/**
* Clears stored properties for a dynamic dimension.
*
* <p>If the dimension has been applied to a compatible backend such as Sable,
* this should also remove those backend properties.</p>
*
* @since 0.10.0
*/
void clearDimensionProperties(@NotNull ResourceKey<Level> key);

/**
* Clears stored properties for a dynamic dimension.
*
* @since 0.10.0
*/
default void clearDimensionProperties(@NotNull ResourceLocation id) {
this.clearDimensionProperties(ResourceKey.create(Registries.DIMENSION, id));
}

/**
* Deletes a dynamic dimension from the server.
* This may delete the dimension files permanently.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
package dev.galacticraft.dynamicdimensions.impl;

import com.google.common.collect.ImmutableList;
import dev.galacticraft.dynamicdimensions.api.DynamicDimensionProperties;
import dev.galacticraft.dynamicdimensions.api.DynamicDimensionRegistry;
import dev.galacticraft.dynamicdimensions.api.PlayerRemover;
import dev.galacticraft.dynamicdimensions.api.event.DynamicDimensionLoadCallback;
import dev.galacticraft.dynamicdimensions.impl.accessor.DynamicDimensionProvider;
import dev.galacticraft.dynamicdimensions.impl.accessor.PrimaryLevelDataAccessor;
import dev.galacticraft.dynamicdimensions.impl.compat.DynamicDimensionPhysicsCompat;
import dev.galacticraft.dynamicdimensions.impl.mixin.*;
import dev.galacticraft.dynamicdimensions.impl.network.S2CPackets;
import dev.galacticraft.dynamicdimensions.impl.registry.RegistryUtil;
Expand Down Expand Up @@ -60,7 +62,9 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class DynamicDimensionRegistryImpl implements DynamicDimensionRegistry {
Expand All @@ -69,6 +73,8 @@ public class DynamicDimensionRegistryImpl implements DynamicDimensionRegistry {
private final Registry<DimensionType> dimTypes;
private final Registry<LevelStem> stems;

private final Map<ResourceKey<Level>, DynamicDimensionProperties> dimensionProperties = new HashMap<>();

public DynamicDimensionRegistryImpl(MinecraftServer server) {
this.server = server;
this.dimTypes = server.registryAccess().registryOrThrow(Registries.DIMENSION_TYPE);
Expand All @@ -92,11 +98,58 @@ public void loadDynamicDimensions() {
return this.createDynamicLevel(id, generator, type, true);
}

@Override
public @Nullable ServerLevel createDynamicDimension(@NotNull ResourceLocation id, @NotNull ChunkGenerator generator, @NotNull DimensionType type, @NotNull DynamicDimensionProperties properties) {
ResourceKey<Level> key = ResourceKey.create(Registries.DIMENSION, id);
this.setDimensionProperties(key, properties);

ServerLevel level = this.createDynamicLevel(id, generator, type, true);
if (level == null) {
this.clearDimensionProperties(key);
}

return level;
}

@Override
public @Nullable ServerLevel loadDynamicDimension(@NotNull ResourceLocation id, @NotNull ChunkGenerator generator, @NotNull DimensionType type) {
return this.createDynamicLevel(id, generator, type, false);
}

@Override
public @Nullable ServerLevel loadDynamicDimension(@NotNull ResourceLocation id, @NotNull ChunkGenerator generator, @NotNull DimensionType type, @NotNull DynamicDimensionProperties properties) {
ResourceKey<Level> key = ResourceKey.create(Registries.DIMENSION, id);
this.setDimensionProperties(key, properties);

ServerLevel level = this.createDynamicLevel(id, generator, type, false);
if (level == null) {
this.clearDimensionProperties(key);
}

return level;
}

@Override
public void setDimensionProperties(@NotNull ResourceKey<Level> key, @NotNull DynamicDimensionProperties properties) {
this.dimensionProperties.put(key, properties);

if (this.server.getLevel(key) != null) {
DynamicDimensionPhysicsCompat.apply(key, properties);
}
}

@Override
public @Nullable DynamicDimensionProperties getDimensionProperties(@NotNull ResourceKey<Level> key) {
return this.dimensionProperties.get(key);
}


@Override
public void clearDimensionProperties(@NotNull ResourceKey<Level> key) {
this.dimensionProperties.remove(key);
DynamicDimensionPhysicsCompat.remove(key);
}

@Override
public boolean dynamicDimensionExists(@NotNull ResourceKey<Level> key) {
return this.dynamicDimensions.contains(key) || ((DynamicDimensionProvider) this.server).dynamicdimensions$isIdPendingCreation(key);
Expand Down Expand Up @@ -125,7 +178,9 @@ public boolean deleteDynamicDimension(@NotNull ResourceLocation id, @Nullable Pl
ResourceKey<Level> key = ResourceKey.create(Registries.DIMENSION, id);
if (!this.canDeleteDimension(key)) return false;

DynamicDimensionPhysicsCompat.remove(key);
((DynamicDimensionProvider) this.server).dynamicdimensions$removeLevel(key, remover, true);
this.dimensionProperties.remove(key);

return true;
}
Expand All @@ -136,7 +191,9 @@ public boolean unloadDynamicDimension(@NotNull ResourceLocation id, @Nullable Pl
ResourceKey<Level> key = ResourceKey.create(Registries.DIMENSION, id);
if (!this.canDeleteDimension(key)) return false;

DynamicDimensionPhysicsCompat.remove(key);
((DynamicDimensionProvider) this.server).dynamicdimensions$removeLevel(key, remover, false);

return true;
}

Expand Down Expand Up @@ -202,6 +259,11 @@ public boolean unloadDynamicDimension(@NotNull ResourceLocation id, @Nullable Pl

((DynamicDimensionProvider) this.server).dynamicdimensions$registerLevel(level);

DynamicDimensionProperties properties = this.dimensionProperties.get(key);
if (properties != null) {
DynamicDimensionPhysicsCompat.apply(key, properties);
}

final var serializedType = ((CompoundTag) DimensionType.DIRECT_CODEC.encode(stem.type().value(), RegistryOps.create(NbtOps.INSTANCE, this.server.registryAccess()), new CompoundTag()).getOrThrow());
for (ServerPlayer player : this.server.getPlayerList().getPlayers()) {
S2CPackets.sendCreateDimension(player, key.location(), serializedType);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package dev.galacticraft.dynamicdimensions.impl.compat;

import dev.galacticraft.dynamicdimensions.api.DynamicDimensionProperties;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;

public final class DynamicDimensionPhysicsCompat {
private DynamicDimensionPhysicsCompat() {
}

public static void apply(ResourceKey<Level> key, DynamicDimensionProperties properties) {
SableDimensionPhysicsCompat.apply(key, properties);
Comment thread
maxryan008 marked this conversation as resolved.
Outdated
}

public static void remove(ResourceKey<Level> key) {
SableDimensionPhysicsCompat.remove(key);
}
}
Comment thread
maxryan008 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package dev.galacticraft.dynamicdimensions.impl.compat;

import dev.galacticraft.dynamicdimensions.api.DynamicDimensionProperties;
import dev.galacticraft.dynamicdimensions.impl.Constants;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import org.joml.Vector3f;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Optional;

final class SableDimensionPhysicsCompat {
private static final String DATA_CLASS =
"dev.ryanhcode.sable.physics.config.dimension_physics.DimensionPhysicsData";

private static final String PHYSICS_CLASS =
"dev.ryanhcode.sable.physics.config.dimension_physics.DimensionPhysics";

private static Boolean loaded;

private SableDimensionPhysicsCompat() {
}

private static boolean isLoaded() {
if (loaded != null) {
return loaded;
}

try {
Class.forName(DATA_CLASS);
Class.forName(PHYSICS_CLASS);
loaded = true;
} catch (Throwable ignored) {
loaded = false;
}

return loaded;
}
Comment thread
maxryan008 marked this conversation as resolved.
Outdated

static void apply(ResourceKey<Level> key, DynamicDimensionProperties properties) {
if (!isLoaded()) {
return;
}

try {
Class<?> dataClass = Class.forName(DATA_CLASS);
Class<?> physicsClass = Class.forName(PHYSICS_CLASS);

Field mapField = dataClass.getDeclaredField("DIMENSION_PHYSICS_DATA");
mapField.setAccessible(true);

@SuppressWarnings("unchecked")
Map<ResourceKey<Level>, Object> map =
(Map<ResourceKey<Level>, Object>) mapField.get(null);

Constructor<?> constructor = physicsClass.getConstructor(
ResourceLocation.class,
int.class,
Optional.class,
Optional.class,
Optional.class,
Optional.class,
Optional.class
);

Object physics = constructor.newInstance(
key.location(),
properties.priority(),
Optional.of(properties.universalDrag()),
Optional.of(copy(properties.baseGravity())),
Optional.of(properties.basePressure()),
Optional.empty(),
Optional.of(copy(properties.magneticNorth()))
);

Object existing = map.get(key);
if (existing == null || priorityOf(existing) <= properties.priority()) {
map.put(key, physics);
}
} catch (Throwable throwable) {
Constants.LOGGER.warn("Failed to apply Sable physics properties for dynamic dimension '{}'", key.location(), throwable);
}
}

static void remove(ResourceKey<Level> key) {
if (!isLoaded()) {
return;
}

try {
Class<?> dataClass = Class.forName(DATA_CLASS);

Field mapField = dataClass.getDeclaredField("DIMENSION_PHYSICS_DATA");
mapField.setAccessible(true);

@SuppressWarnings("unchecked")
Map<ResourceKey<Level>, Object> map =
(Map<ResourceKey<Level>, Object>) mapField.get(null);

map.remove(key);
} catch (Throwable throwable) {
Constants.LOGGER.warn("Failed to remove Sable physics properties for dynamic dimension '{}'", key.location(), throwable);
}
}

private static int priorityOf(Object physics) {
try {
Method method = physics.getClass().getMethod("priority");
return (int) method.invoke(physics);
} catch (Throwable ignored) {
return Integer.MIN_VALUE;
}
}

private static Vector3f copy(Vector3f vector) {
return new Vector3f(vector.x(), vector.y(), vector.z());
}
}
Loading