Gravity data

This commit is contained in:
XevianLight
2026-02-03 23:35:41 -07:00
parent 74731444ea
commit 331da7c78f
5 changed files with 219 additions and 3 deletions

View File

@@ -31,7 +31,7 @@ import java.util.Map;
*/
public class EnvironmentSavedData extends SavedData {
private final Long2IntOpenHashMap envData = new Long2IntOpenHashMap();
private static final Long2IntOpenHashMap envData = new Long2IntOpenHashMap();
private static final String NAME = "aphelion_environment";

View File

@@ -0,0 +1,166 @@
package net.xevianlight.aphelion.core.saveddata;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.saveddata.SavedData;
import net.xevianlight.aphelion.client.ClientOxygenCache;
import net.xevianlight.aphelion.core.saveddata.types.EnvironmentData;
import net.xevianlight.aphelion.core.saveddata.types.GravityData;
import net.xevianlight.aphelion.planet.Planet;
import net.xevianlight.aphelion.planet.PlanetCache;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Pattern:
* - World-level SavedData
* - Outer map keyed by section (chunkX, sectionY, chunkZ) packed into a long
* - Inner map keyed by localIndex (0..4095) -> packed int env value
*
* Sparse by design: blocks not present in the inner map are implicitly "default environment".
*/
public class GravitySavedData extends SavedData {
private final Long2IntOpenHashMap gravityData = new Long2IntOpenHashMap();
private static final String NAME = "aphelion_gravity";
public static GravitySavedData create() {
return new GravitySavedData();
}
@Override
@NotNull
public CompoundTag save(@NotNull CompoundTag tag, @NotNull HolderLookup.Provider provider) {
int size = gravityData.size();
long[] positions = new long[size];
int[] data = new int[size];
int i = 0;
for (var e : gravityData.long2IntEntrySet()) {
positions[i] = e.getLongKey();
data[i] = e.getIntValue();
i++;
}
tag.putLongArray("Position", positions);
tag.putIntArray("Value", data);
return tag;
}
public static GravitySavedData load(CompoundTag tag, HolderLookup.Provider lookupProvider) {
GravitySavedData data = create();
if (!tag.contains("Position", Tag.TAG_LONG_ARRAY) || !tag.contains("Value", Tag.TAG_INT_ARRAY)) { return data; }
long[] positions = tag.getLongArray("Position");
int[] values = tag.getIntArray("Value");
int length = Math.min(positions.length, values.length);
data.gravityData.ensureCapacity(length);
for (int i = 0; i < length; i++) {
data.gravityData.put(positions[i], values[i]);
}
return data;
}
private static final int ABSENT = Integer.MIN_VALUE;
public @Nullable GravityData getGravityRegionData (Level level, BlockPos center) {
GravityData data = GravityData.unpack(gravityData.getOrDefault(center.asLong(), ABSENT));
return data.pack() == ABSENT ? null : data;
}
public void removeGravityRegion (BlockPos pos) {
gravityData.remove(pos.asLong());
}
public void setGravityRegion (BlockPos pos, GravityData data) {
gravityData.put(pos.asLong(), data.pack());
}
/**
* Returns the cumulative sum of the acceleration for all gravity regions that overlap this block position
*/
public float getGravitySum (BlockPos pos) {
float sum = 0;
List<GravityData> regions = getGravityRegions(pos);
for (var e : regions) {
sum += e.getAccel();
}
return sum;
}
/**
* Returns the strongest acceleration among all gravity regions that overlap this block position
*/
public float getGravityMax (BlockPos pos) {
float max = 0;
List<GravityData> regions = getGravityRegions(pos);
for (var e : regions) {
var accel = e.getAccel();
if (accel > max) max = accel;
}
return max;
}
/**
* Returns a list of all gravity data objects overlapping this position. NOTE: does not contain position of the regions
*/
public List<GravityData> getGravityRegions (BlockPos pos) {
List<GravityData> regions = new ArrayList<>();
for (var entry : gravityData.long2IntEntrySet()) {
GravityData data = GravityData.unpack(entry.getIntValue());
BlockPos center = BlockPos.of(entry.getLongKey());
if (contains(pos, center, data.getRadius())) {
regions.add(data);
}
}
return regions;
}
private static boolean contains(BlockPos pos, BlockPos center, float radius) {
float distanceSquared = ((center.getX() - pos.getX()) * (center.getX() - pos.getX())) + ((center.getY() - pos.getY()) * (center.getY() - pos.getY())) + ((center.getZ() - pos.getZ()) * (center.getZ() - pos.getZ()));
return distanceSquared <= radius * radius;
}
private static float defaultGravity (Level level) {
Planet planet = PlanetCache.getByDimensionOrNull(level.dimension());
if (planet == null) return GravityData.DEFAULT_GRAVITY;
return planet.gravity();
}
public static GravitySavedData get(ServerLevel level) {
return level.getDataStorage().computeIfAbsent(
new Factory<>(GravitySavedData::create, GravitySavedData::load),
NAME
);
}
}

View File

@@ -0,0 +1,49 @@
package net.xevianlight.aphelion.core.saveddata.types;
public class GravityData {
private float accel;
private float radius;
public static final float DEFAULT_GRAVITY = 9.80665f; // 1G
public static final float GRAVITY_PRECISION = 100.0f;
public static final float RADIUS_PRECISION = 100.0f;
public static final float MAX_RADIUS = Short.MAX_VALUE / RADIUS_PRECISION;
public static final float MAX_GRAVITY = Short.MAX_VALUE / GRAVITY_PRECISION;
public GravityData(float accel, float radius) {
this.accel = accel;
this.radius = radius;
}
public int pack() {
int packed = 0;
packed |= (int) (this.accel * GRAVITY_PRECISION);
packed |= (int) (this.radius * RADIUS_PRECISION) << 16;
return packed;
}
public float getAccel() {
return accel;
}
public void setAccel(float accel) {
this.accel = accel;
}
public float getRadius() {
return radius;
}
public void setRadius(short radius) {
this.radius = radius;
}
public static GravityData unpack(int packed) {
float accel = (packed & 0xFFFF) / GRAVITY_PRECISION;
float radius = (packed >> 16) / RADIUS_PRECISION;
return new GravityData(accel, radius);
}
}

View File

@@ -12,14 +12,14 @@ public record Planet(
double orbitDistance,
ResourceKey<StarSystem> system,
boolean oxygen,
double gravity
float gravity
) {
public static final Codec<Planet> CODEC = RecordCodecBuilder.create(inst -> inst.group(
ResourceKey.codec(Registries.DIMENSION).fieldOf("dimension").forGetter(Planet::dimension),
Codec.DOUBLE.fieldOf("orbit_distance").forGetter(Planet::orbitDistance),
ResourceKey.codec(ModRegistries.STAR_SYSTEM).fieldOf("star_system").forGetter(Planet::system),
Codec.BOOL.fieldOf("oxygen").forGetter(Planet::oxygen),
Codec.DOUBLE.fieldOf("gravity").forGetter(Planet::gravity)
Codec.FLOAT.fieldOf("gravity").forGetter(Planet::gravity)
).apply(inst, Planet::new));
}

View File

@@ -23,6 +23,7 @@ public class OxygenService {
public static boolean hasOxygen(Entity entity) {
// Not sure if this is at the entity's feet, head, or the middle... research later
// Blockpos is from entity's feet ~Xev
BlockPos entityBlockPos = BlockPos.containing(entity.getX(), entity.getY(), entity.getZ());
return hasOxygen(entity.level(), entityBlockPos);
}