Skip to content
Draft
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
cace5b8
wip: source-based oxygen system
marcus8448 Aug 19, 2025
f115ede
feat: update sealer and bubble distributor implementations
marcus8448 Aug 19, 2025
978b818
fix: seal block adjacent to broken edge block
marcus8448 Aug 19, 2025
d9e5334
fix: unseal block adjacent to placed edge block
marcus8448 Aug 21, 2025
e1b3cbf
chore: simplify breathability calculation for edge positions
marcus8448 Aug 23, 2025
4dc8ac3
chore: improve coverage of delegated and generating chunks
marcus8448 Aug 23, 2025
2d0d6a8
chore: add mod id prefix to mixin-implemented interfaces
marcus8448 Aug 23, 2025
4bc2b87
fix: use precise location for eating checks
marcus8448 Aug 23, 2025
68570e2
chore: strip unnecessary mixin method prefixes
marcus8448 Aug 23, 2025
e738384
chore: add docs to the level accessor
marcus8448 Aug 23, 2025
45bf444
chore: test level breathability before block
marcus8448 Aug 23, 2025
43ed6e3
fix: remove ProtoChunkMixin from config, run datagen
marcus8448 Aug 23, 2025
e4c837b
fix: restore extinguishBlock call, stop copper from oxidizing with no…
marcus8448 Aug 25, 2025
ee8a7b3
fix: replace extinguishable block registry with extensions on the blo…
marcus8448 Aug 25, 2025
2edc0e6
wip: allow blocks to subscribe to atmospheric changes
marcus8448 Aug 26, 2025
9d66199
wip: track bubble distributor subscribers
marcus8448 Aug 27, 2025
51860b7
fix: persist bubble distributor atmospheric subscribers on save/load
marcus8448 Sep 1, 2025
ec77e8d
chore: don't bother tracking all block positions in the bubble
marcus8448 Sep 9, 2025
715e7a8
chore: reduce provider lookup resolution to per-section (from per-block)
marcus8448 Sep 9, 2025
bfc79c8
feat: rework public-facing apis for oxygen access
marcus8448 Sep 10, 2025
a5b498c
feat: add javadocs to public api
marcus8448 Sep 10, 2025
456257c
chore: delete TrackingBitSet
marcus8448 Sep 10, 2025
3180a33
fix: extinguish soul torches
marcus8448 Sep 11, 2025
7781788
chore: prefer ModifyReturnValue over WrapMethod
marcus8448 Sep 11, 2025
f04a888
chore: style fixes and renames from review
marcus8448 Sep 11, 2025
0d98a22
fix: notify listeners when bubble distributor disabled
marcus8448 Sep 12, 2025
b7e2fe5
fix: block placement close to oxygen sealer
marcus8448 Sep 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2019-2025 Team Galacticraft
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package dev.galacticraft.api.accessor;

import dev.galacticraft.api.block.entity.AtmosphereProvider;
import net.minecraft.core.BlockPos;

import java.util.Iterator;

public interface ChunkOxygenAccessor {
/**
* {@return the {@link AtmosphereProvider atmosphere providers} that service the chunk section at the given y-position}
* @param y the block y-height of the section to check.
*/
Iterator<AtmosphereProvider> galacticraft$getProviders(int y);

/**
* {@return the positions of the {@link AtmosphereProvider atmosphere providers} that service the chunk section at the given y-position}
* @param y the block y-height of the section to check.
*/
Iterator<BlockPos> galacticraft$getProviderPositions(int y);

/**
* Links the given atmosphere provider to the chunk section at the given index.
* @param sectionIndex the index of the chunk section being provided to.
* @param provider the location of the atmosphere provider being linked.
*/
void galacticraft$addAtmosphereProvider(int sectionIndex, BlockPos provider);

/**
* Unlinks the given atmosphere provider to the chunk section at the given index.
* @param sectionIndex the index of the chunk section that is no longer being provided to.
* @param provider the location of the atmosphere provider being unlinked.
*/
void galacticraft$removeAtmosphereProvider(int sectionIndex, BlockPos provider);
}
85 changes: 85 additions & 0 deletions src/main/java/dev/galacticraft/api/accessor/GCBlockExtensions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright (c) 2019-2025 Team Galacticraft
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package dev.galacticraft.api.accessor;

import dev.galacticraft.api.block.entity.AtmosphereProvider;
import dev.galacticraft.api.oxygen.OxygenUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.state.BlockState;

import java.util.Iterator;

public interface GCBlockExtensions {
/**
* {@return whether this block will transform on placement due to a lack of atmosphere}
* This should only be implemented if it is not possible to handle the transformation elsewhere.
* @param state the state being placed
*/
default boolean galacticraft$hasLegacyExtinguishTransform(BlockState state) {
return false;
}

/**
* Extinguishes the given state due to a lack of atmosphere.
* @param pos the position of the block in-world
* @param state the state being placed
* @return the transformed block state.
*/
default BlockState galacticraft$extinguishBlockPlace(BlockPos pos, BlockState state) {
return state;
}

/**
* {@return whether the given block state needs to be alerted to changes in atmospheric composition}
* @param state the state being checked
*/
default boolean galacticraft$hasAtmosphereListener(BlockState state) {
return false;
}

/**
* Called when the atmospheric composition changes.
* @param level the current level
* @param pos the position of the block
* @param state the current state of the block
* @param iterator all atmosphere providers at the given position
* @see #galacticraft$onAtmosphereChange(ServerLevel, BlockPos, BlockState, boolean)
*/
default void galacticraft$onAtmosphereChange(ServerLevel level, BlockPos pos, BlockState state, Iterator<AtmosphereProvider> iterator) {
this.galacticraft$onAtmosphereChange(level, pos, state, OxygenUtil.isBreathable(pos, iterator));
}


/**
* Called when the atmospheric composition changes.
* For blocks that just need to know if the position is breathable or not. Should NOT be called outside this class.
* @param level the current level
* @param pos the position of the block
* @param state the current state of the block
* @param breathable whether the given position is currently breathable
* @see #galacticraft$onAtmosphereChange(ServerLevel, BlockPos, BlockState, Iterator)
*/
default void galacticraft$onAtmosphereChange(ServerLevel level, BlockPos pos, BlockState state, boolean breathable) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,44 +23,34 @@
package dev.galacticraft.api.accessor;

import net.minecraft.core.BlockPos;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.block.state.BlockState;

public interface LevelOxygenAccessor {
/**
* @see LevelOxygenAccessorRO
*/
public interface LevelOxygenAccessor extends LevelOxygenAccessorRO {
/**
* Returns whether the supplied position in this world is breathable for entities
*
* @param pos the position to test
* @return whether the supplied position in the chunk is breathable for entities
* Adds an atmosphere provider to the chunk section at the given position
* @param sectionX the section x-position
* @param sectionY the section y-position
* @param sectionZ the section z-position
* @param provider the position of the provider to add
*/
default boolean isBreathable(BlockPos pos) {
return this.isBreathable(pos.getX(), pos.getY(), pos.getZ());
}

default boolean isBreathableChunk(LevelChunk chunk, int x, int y, int z) {
throw new RuntimeException("This should be overridden by mixin!");
}

default boolean isBreathable(int x, int y, int z) {
throw new RuntimeException("This should be overridden by mixin!");
}

default void setBreathable(BlockPos pos, boolean value) {
this.setBreathable(pos.getX(), pos.getY(), pos.getZ(), value);
}

default void setBreathable(int x, int y, int z, boolean value) {
throw new RuntimeException("This should be overridden by mixin!");
}
void galacticraft$addAtmosphereProvider(int sectionX, int sectionY, int sectionZ, BlockPos provider);

default void setBreathableChunk(LevelChunk chunk, int x, int y, int z, boolean value) {
throw new RuntimeException("This should be overridden by mixin!");
}

default boolean getDefaultBreathable() {
throw new RuntimeException("This should be overridden by mixin!");
}
/**
* Removes an atmosphere provider from the chunk section at the given position
* @param sectionX the section x-position
* @param sectionY the section y-position
* @param sectionZ the section z-position
* @param provider the position of the provider to remove
*/
void galacticraft$removeAtmosphereProvider(int sectionX, int sectionY, int sectionZ, BlockPos provider);

default void setDefaultBreathable(boolean breathable) {
throw new RuntimeException("This should be overridden by mixin!");
}
/**
* Notifies the block at the given position that the atmosphere has changed.
* @param pos the position where the atmosphere changed
* @param state the current block state of the block where the atmosphere changed
*/
default void galacticraft$notifyAtmosphereChange(BlockPos pos, BlockState state) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2019-2025 Team Galacticraft
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package dev.galacticraft.api.accessor;

import dev.galacticraft.api.block.entity.AtmosphereProvider;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;

import java.util.Iterator;

public interface LevelOxygenAccessorRO {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the RO stand for, out of curiosity?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It stands for read-only. The interface is split so that LevelReader can implement the read-only parts. It could be renamed to LevelOxygenReader or something if that's easier to understand.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that makes sense. Don't feel you have to rename it, but a comment saying read-only might be nice.

/**
* {@return the atmosphere providers for the given block position}
*/
default Iterator<AtmosphereProvider> galacticraft$getAtmosphereProviders(int x, int y, int z) {
throw new RuntimeException("This should be overridden by mixin!");
}

/**
* {@return the positions of the atmosphere providers for the given block position}
*/
default Iterator<BlockPos> galacticraft$getAtmosphereProviderLocations(int x, int y, int z) {
throw new RuntimeException("This should be overridden by mixin!");
}

/**
* {@return whether the given point in the level is breathable}
*/
default boolean galacticraft$isBreathable(double x, double y, double z) {
throw new RuntimeException("This should be overridden by mixin!");
}

/**
* {@return whether the block position in the level is breathable}
* It is undefined whether partially breathable block positions are breathable or not.
*/
default boolean galacticraft$isBreathable(int x, int y, int z) {
throw new RuntimeException("This should be overridden by mixin!");
}

/**
* {@return whether the block position in the level is breathable}
* It is undefined whether partially breathable block positions are breathable or not.
*/
default boolean galacticraft$isBreathable(BlockPos pos) {
throw new RuntimeException("This should be overridden by mixin!");
}

/**
* {@return whether this has a breathable atmosphere}
*/
default boolean galacticraft$isBreathable() {
throw new RuntimeException("This should be overridden by mixin!");
}

default boolean galacticraft$isBreathable(Vec3 point) {
return this.galacticraft$isBreathable(point.x, point.y, point.z);
}

default Iterator<BlockPos> galacticraft$getAtmosphereProviderLocations(BlockPos pos) {
return this.galacticraft$getAtmosphereProviderLocations(pos.getX(), pos.getY(), pos.getZ());
}

default Iterator<AtmosphereProvider> galacticraft$getAtmosphereProviders(BlockPos pos) {
return this.galacticraft$getAtmosphereProviders(pos.getX(), pos.getY(), pos.getZ());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright (c) 2019-2025 Team Galacticraft
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package dev.galacticraft.api.block.entity;

import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;

/**
* Describes a {@link BlockEntity} that generates an atmosphere somewhere in the world.
*
* @see dev.galacticraft.api.accessor.LevelOxygenAccessor
* @see dev.galacticraft.api.accessor.ChunkOxygenAccessor
*/
public interface AtmosphereProvider {
/**
* {@return whether the given point is breathable}
* @param x the x-coordinate to check
* @param y the y-coordinate to check
* @param z the z-coordinate to check
*/
boolean canBreathe(double x, double y, double z);

/**
* {@return whether the given block position is breathable}
* It is not defined where within the block is (or is not) breathable.
* @param x the x-position to check
* @param y the y-position to check
* @param z the z-position to check
*/
default boolean canBreathe(int x, int y, int z) {
return this.canBreathe(new BlockPos(x, y, z));
}

/**
* {@return whether the given block position is breathable}
* It is not defined where within the block is (or is not) breathable.
* @param pos the position to check
*/
boolean canBreathe(BlockPos pos);

/**
* Called when a block state is updated within a chunk that has this atmosphere provider attached.
* @param pos the position of block that was changed
* @param newState the new state being placed at the position.
*/
void notifyStateChange(BlockPos pos, BlockState newState);

/**
* Helper method to get a block entity instance.
* @return {@code this}
*/
default BlockEntity be() {
return (BlockEntity) this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,7 @@
* SOFTWARE.
*/

package dev.galacticraft.impl.internal.accessor;
package dev.galacticraft.api.block.entity;

import org.jetbrains.annotations.ApiStatus;

@ApiStatus.Internal
public interface InternalLevelOxygenAccessor {
boolean getDefaultBreathable();

void setDefaultBreathable(boolean breathable);
public interface SpaceFillingAtmosphereProvider extends AtmosphereProvider {
}
Loading