mirror of
https://github.com/XevianLight/Aphelion.git
synced 2026-05-11 01:50:56 +01:00
Gravity data
This commit is contained in:
@@ -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";
|
||||
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user