Merge branch '1.18/dev' into 1.19/dev

Conflicts:
	build.gradle
	gradle.properties
	src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunk.java
	src/main/java/com/jozufozu/flywheel/core/virtual/VirtualRenderWorld.java
	src/main/java/com/jozufozu/flywheel/mixin/instancemanage/ChunkRebuildHooksMixin.java
	src/main/resources/flywheel.mixins.json
This commit is contained in:
PepperCode1 2023-08-23 11:50:49 -07:00
commit 23860ad857
30 changed files with 642 additions and 586 deletions

View file

@ -59,6 +59,8 @@ body:
label: Mod Version
description: The version of the mod you were using when the bug occured
options:
- "0.6.10"
- "0.6.9"
- "0.6.8.a"
- "0.6.8"
- "0.6.7"
@ -94,6 +96,7 @@ body:
label: Minecraft Version
description: The version of Minecraft you were using when the bug occured
options:
- "1.20.1"
- "1.19.2"
- "1.18.2"
- "1.18.1"

View file

@ -1,11 +1,13 @@
<div align="center">
<img src="https://i.imgur.com/yVFgPpr.png" alt="Logo by @voxel_dani on Twitter" width="250">
<h1>Flywheel</h1>
<h6>A modern engine for modded Minecraft.</h6>
<a href='https://ci.tterrag.com/job/Flywheel/job/Forge/job/1.18/'><img src='https://ci.tterrag.com/job/Flywheel/job/Forge/job/1.18/badge/icon' alt="Jenkins"></a>
<a href="https://discord.gg/xjD59ThnXy"><img src="https://img.shields.io/discord/841464837406195712?color=5865f2&label=Discord&style=flat" alt="Discord"></a>
<a href="https://www.curseforge.com/minecraft/mc-mods/flywheel"><img src="http://cf.way2muchnoise.eu/486392.svg" alt="Curseforge Downloads"></a>
<br>
<img src="https://i.imgur.com/yVFgPpr.png" alt="Logo by @voxel_dani on Twitter" width="250">
<h1>Flywheel</h1>
<h6>A modern engine for modded Minecraft.</h6>
<a href='https://ci.tterrag.com/job/Flywheel/job/Forge/job/1.18/'><img src='https://ci.tterrag.com/job/Flywheel/job/Forge/job/1.18/badge/icon' alt="Jenkins"></a>
<a href="https://github.com/Jozufozu/Flywheel/blob/HEAD/LICENSE.md"><img src="https://img.shields.io/github/license/Jozufozu/Flywheel?style=flat&color=900c3f" alt="License"></a>
<a href="https://discord.gg/xjD59ThnXy"><img src="https://img.shields.io/discord/841464837406195712?color=5865f2&label=Discord&style=flat" alt="Discord"></a>
<a href="https://www.curseforge.com/minecraft/mc-mods/flywheel"><img src="http://cf.way2muchnoise.eu/486392.svg" alt="Curseforge Downloads"></a>
<a href="https://modrinth.com/mod/flywheel"><img src="https://img.shields.io/modrinth/dt/flywheel?logo=modrinth&label=&suffix=%20&style=flat&color=242629&labelColor=5ca424&logoColor=1c1c1c" alt="Modrinth"></a>
<br></br>
</div>
### About
@ -16,13 +18,13 @@ graphics programming.
### Instancing
Flywheel provides an alternate, unified path for entity and tile entity rendering that takes advantage of GPU
Flywheel provides an alternate, unified path for entity and block entity rendering that takes advantage of GPU
instancing. In doing so, Flywheel gives the developer the flexibility to define their own vertex and instance formats,
and write custom shaders to ingest that data.
### Shaders
To accomodate the developer and leave more in the hands of the engine, Flywheel provides a custom shader loading and
To accommodate the developer and leave more in the hands of the engine, Flywheel provides a custom shader loading and
templating system to hide the details of the CPU/GPU interface. This system is a work in progress. There will be
breaking changes, and I make no guarantees of backwards compatibility.
@ -46,14 +48,15 @@ repositories {
}
dependencies {
implementation fg.deobf("com.jozufozu.flywheel:Flywheel-Forge:${flywheel_version}")
implementation fg.deobf("com.jozufozu.flywheel:flywheel-forge-${flywheel_minecraft_version}:${flywheel_version}")
}
```
`${flywheel_version}` gets replaced by the version of Flywheel you want to use, eg. `1.18-0.3.0.3`
`${flywheel_version}` gets replaced by the version of Flywheel you want to use, eg. `0.6.9`.
`${flywheel_minecraft_version}` gets replaced by the version of Minecraft, eg. `1.18.2`.
For a list of available Flywheel versions, you can check [the maven](https://maven.tterrag.com/com/jozufozu/flywheel/Flywheel-Forge/).
For a list of available Flywheel versions, you can check [the maven](https://maven.tterrag.com/com/jozufozu/flywheel/).
If you aren't using mixed mappings (or just want to be safe), add the following properties to your run configurations:
If you aren't using Mojang mappings (or just want to be safe), add the following properties to your run configurations:
```groovy
property 'mixin.env.remapRefMap', 'true'
property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg"

View file

@ -52,6 +52,7 @@ minecraft {
property 'flw.loadRenderDoc', 'true'
arg '-mixin.config=flywheel.mixins.json'
arg '-mixin.config=flywheel.sodium.mixins.json'
mods {
flywheel {
@ -67,6 +68,7 @@ minecraft {
property 'forge.logging.console.level', 'debug'
arg '-mixin.config=flywheel.mixins.json'
arg '-mixin.config=flywheel.sodium.mixins.json'
mods {
flywheel {
@ -159,7 +161,7 @@ jar {
'Implementation-Version' : project.jar.archiveVersion,
//'Implementation-Vendor': 'flywheel authors',
'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
'MixinConfigs' : 'flywheel.mixins.json'
'MixinConfigs' : 'flywheel.mixins.json,flywheel.sodium.mixins.json'
])
}
}

View file

@ -2,7 +2,7 @@ org.gradle.jvmargs = -Xmx3G
org.gradle.daemon = false
# mod version info
mod_version = 0.6.9
mod_version = 0.6.10
artifact_minecraft_version = 1.19.2
minecraft_version = 1.19.2

View file

@ -15,6 +15,7 @@ import com.jozufozu.flywheel.util.WorldAttached;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.api.distmarker.Dist;
@ -119,6 +120,21 @@ public class InstancedRenderDispatcher {
.loadEntities(world);
}
public static <T extends BlockEntity> boolean tryAddBlockEntity(T blockEntity) {
Level level = blockEntity.getLevel();
if (!Backend.canUseInstancing(level)) {
return false;
}
if (!InstancedRenderRegistry.canInstance(blockEntity.getType())) {
return false;
}
getBlockEntities(level).queueAdd(blockEntity);
return InstancedRenderRegistry.shouldSkipRender(blockEntity);
}
public static void getDebugString(List<String> debug) {
debug.add("");
debug.add("Flywheel: " + Flywheel.getVersion());

View file

@ -17,6 +17,7 @@ import it.unimi.dsi.fastutil.longs.LongSets;
import it.unimi.dsi.fastutil.shorts.ShortList;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos;
@ -36,13 +37,11 @@ import net.minecraft.world.ticks.BlackholeTickAccess;
import net.minecraft.world.ticks.TickContainerAccess;
public class VirtualChunk extends ChunkAccess {
public final VirtualRenderWorld world;
final VirtualRenderWorld world;
boolean needsLight;
final int x;
final int z;
private final VirtualChunkSection[] sections;
private final LevelChunkSection[] sections;
private boolean needsLight;
public VirtualChunk(VirtualRenderWorld world, int x, int z) {
super(new ChunkPos(x, z), UpgradeData.EMPTY, world, world.registryAccess()
@ -50,34 +49,39 @@ public class VirtualChunk extends ChunkAccess {
.orElseThrow(), 0L, null, null);
this.world = world;
this.needsLight = true;
this.x = x;
this.z = z;
int sectionCount = world.getSectionsCount();
this.sections = new LevelChunkSection[sectionCount];
this.sections = new VirtualChunkSection[sectionCount];
for (int i = 0; i < sectionCount; i++) {
sections[i] = new VirtualChunkSection(this, i << 4);
}
this.needsLight = true;
Mods.STARLIGHT.executeIfInstalled(() -> () -> {
((ExtendedChunk)this).setBlockNibbles(StarLightEngine.getFilledEmptyLight(this));
((ExtendedChunk)this).setSkyNibbles(StarLightEngine.getFilledEmptyLight(this));
((ExtendedChunk) this).setBlockNibbles(StarLightEngine.getFilledEmptyLight(this));
((ExtendedChunk) this).setSkyNibbles(StarLightEngine.getFilledEmptyLight(this));
});
}
@Override
public Stream<BlockPos> getLights() {
return world.blocksAdded.entrySet()
.stream()
.filter(it -> {
BlockPos blockPos = it.getKey();
boolean chunkContains = blockPos.getX() >> 4 == x && blockPos.getZ() >> 4 == z;
return chunkContains && it.getValue()
.getLightEmission(world, blockPos) != 0;
})
.map(Map.Entry::getKey);
@Nullable
public BlockState setBlockState(BlockPos pos, BlockState state, boolean isMoving) {
return null;
}
@Override
public void setBlockEntity(BlockEntity blockEntity) {
}
@Override
public void addEntity(Entity entity) {
}
@Override
public Set<BlockPos> getBlockEntitiesPos() {
return Collections.emptySet();
}
@Override
@ -85,113 +89,25 @@ public class VirtualChunk extends ChunkAccess {
return sections;
}
@Override
public ChunkStatus getStatus() {
return ChunkStatus.LIGHT;
}
@Nullable
@Override
public BlockState setBlockState(BlockPos p_177436_1_, BlockState p_177436_2_, boolean p_177436_3_) {
return null;
}
@Override
public void setBlockEntity(BlockEntity p_177426_2_) {}
@Override
public void addEntity(Entity p_76612_1_) {}
@Override
public Set<BlockPos> getBlockEntitiesPos() {
return null;
}
@Override
public Collection<Map.Entry<Heightmap.Types, Heightmap>> getHeightmaps() {
return Collections.emptySet();
}
@Override
public void setHeightmap(Heightmap.Types type, long[] data) {
}
@Override
public Heightmap getOrCreateHeightmapUnprimed(Heightmap.Types type) {
return null;
}
@Override
public void setHeightmap(Heightmap.Types p_201607_1_, long[] p_201607_2_) {}
@Override
public Heightmap getOrCreateHeightmapUnprimed(Heightmap.Types p_217303_1_) {
return null;
}
@Override
public int getHeight(Heightmap.Types p_201576_1_, int p_201576_2_, int p_201576_3_) {
public int getHeight(Heightmap.Types type, int x, int z) {
return 0;
}
@Override
public void setUnsaved(boolean p_177427_1_) {}
@Override
public boolean isUnsaved() {
return false;
}
@Override
public void removeBlockEntity(BlockPos p_177425_1_) {}
@Override
public ShortList[] getPostProcessing() {
return new ShortList[0];
}
@Nullable
@Override
public CompoundTag getBlockEntityNbt(BlockPos p_201579_1_) {
return null;
}
@Nullable
@Override
public CompoundTag getBlockEntityNbtForSaving(BlockPos p_223134_1_) {
return null;
}
@Override
public UpgradeData getUpgradeData() {
return null;
}
@Override
public void setInhabitedTime(long p_177415_1_) {}
@Override
public long getInhabitedTime() {
return 0;
}
@Override
public boolean isLightCorrect() {
return needsLight;
}
@Override
public void setLightCorrect(boolean needsLight) {
this.needsLight = needsLight;
}
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos) {
return null;
}
@Override
public BlockState getBlockState(BlockPos pos) {
return world.getBlockState(pos);
}
@Override
public FluidState getFluidState(BlockPos p_204610_1_) {
return null;
}
@Override
@Nullable
public StructureStart getStartForStructure(Structure structure) {
@ -199,7 +115,7 @@ public class VirtualChunk extends ChunkAccess {
}
@Override
public void setStartForStructure(Structure structure, StructureStart start) {
public void setStartForStructure(Structure structure, StructureStart structureStart) {
}
@Override
@ -212,7 +128,7 @@ public class VirtualChunk extends ChunkAccess {
}
@Override
public LongSet getReferencesForStructure(Structure structure) {
public LongSet getReferencesForStructure(Structure pStructure) {
return LongSets.emptySet();
}
@ -226,17 +142,60 @@ public class VirtualChunk extends ChunkAccess {
}
@Override
public void setAllReferences(Map<Structure, LongSet> structureReferences) {
public void setAllReferences(Map<Structure, LongSet> structureReferencesMap) {
}
@Override
public int getHeight() {
return world.getHeight();
public void setUnsaved(boolean unsaved) {
}
@Override
public int getMinBuildHeight() {
return world.getMinBuildHeight();
public boolean isUnsaved() {
return false;
}
@Override
public ChunkStatus getStatus() {
return ChunkStatus.LIGHT;
}
@Override
public void removeBlockEntity(BlockPos pos) {
}
@Override
public ShortList[] getPostProcessing() {
return new ShortList[0];
}
@Override
@Nullable
public CompoundTag getBlockEntityNbt(BlockPos pos) {
return null;
}
@Override
@Nullable
public CompoundTag getBlockEntityNbtForSaving(BlockPos pos) {
return null;
}
@Override
public Stream<BlockPos> getLights() {
return world.blockStates.entrySet()
.stream()
.filter(it -> {
BlockPos blockPos = it.getKey();
boolean chunkContains = SectionPos.blockToSectionCoord(blockPos.getX()) == chunkPos.x && SectionPos.blockToSectionCoord(blockPos.getZ()) == chunkPos.z;
return chunkContains && it.getValue()
.getLightEmission(world, blockPos) != 0;
})
.map(Map.Entry::getKey);
}
@Override
public TickContainerAccess<Block> getBlockTicks() {
return BlackholeTickAccess.emptyContainer();
}
@Override
@ -246,12 +205,41 @@ public class VirtualChunk extends ChunkAccess {
@Override
public TicksToSave getTicksForSerialization() {
return null;
throw new UnsupportedOperationException();
}
@Override
public TickContainerAccess<Block> getBlockTicks() {
return BlackholeTickAccess.emptyContainer();
public long getInhabitedTime() {
return 0;
}
@Override
public void setInhabitedTime(long amount) {
}
@Override
public boolean isLightCorrect() {
return needsLight;
}
@Override
public void setLightCorrect(boolean lightCorrect) {
this.needsLight = lightCorrect;
}
@Override
@Nullable
public BlockEntity getBlockEntity(BlockPos pos) {
return world.getBlockEntity(pos);
}
@Override
public BlockState getBlockState(BlockPos pos) {
return world.getBlockState(pos);
}
@Override
public FluidState getFluidState(BlockPos pos) {
return world.getFluidState(pos);
}
}

View file

@ -3,10 +3,10 @@ package com.jozufozu.flywheel.core.virtual;
import net.minecraft.core.Registry;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.material.FluidState;
public class VirtualChunkSection extends LevelChunkSection {
public VirtualChunk owner;
public final VirtualChunk owner;
public final int xStart;
public final int yStart;
@ -32,8 +32,12 @@ public class VirtualChunkSection extends LevelChunkSection {
}
@Override
public BlockState setBlockState(int p_177484_1_, int p_177484_2_, int p_177484_3_, BlockState p_177484_4_,
boolean p_177484_5_) {
throw new IllegalStateException("Chunk sections should not be mutated in a fake world.");
public FluidState getFluidState(int x, int y, int z) {
return getBlockState(x, y, z).getFluidState();
}
@Override
public BlockState setBlockState(int x, int y, int z, BlockState state, boolean useLocks) {
throw new UnsupportedOperationException("Chunk sections cannot be mutated in a fake world.");
}
}

View file

@ -1,62 +1,65 @@
package com.jozufozu.flywheel.core.virtual;
import java.util.HashMap;
import java.util.function.BooleanSupplier;
import net.minecraft.world.level.BlockGetter;
import org.jetbrains.annotations.Nullable;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.lighting.LevelLightEngine;
public class VirtualChunkSource extends ChunkSource {
private final VirtualRenderWorld world;
public final HashMap<Long, VirtualChunk> chunks = new HashMap<>();
private final Long2ObjectMap<VirtualChunk> chunks = new Long2ObjectOpenHashMap<>();
public VirtualChunkSource(VirtualRenderWorld world) {
this.world = world;
}
@Override
public BlockGetter getChunkForLighting(int x, int z) {
return getChunk(x, z);
}
@Override
public Level getLevel() {
return world;
}
@Override
public ChunkAccess getChunk(int x, int z, ChunkStatus status, boolean p_212849_4_) {
return getChunk(x, z);
}
public ChunkAccess getChunk(int x, int z) {
long pos = ChunkPos.asLong(x, z);
return chunks.computeIfAbsent(pos, $ -> new VirtualChunk(world, x, z));
}
@Override
@Nullable
public LevelChunk getChunk(int x, int z, boolean load) {
return null;
}
@Override
@Nullable
public ChunkAccess getChunk(int x, int z, ChunkStatus status, boolean load) {
return getChunk(x, z);
}
@Override
public void tick(BooleanSupplier hasTimeLeft, boolean tickChunks) {
}
@Override
public String gatherStats() {
return "WrappedChunkProvider";
}
@Override
public LevelLightEngine getLightEngine() {
return world.getLightEngine();
}
@Override
public void tick(BooleanSupplier p_202162_, boolean p_202163_) {
return "VirtualChunkSource";
}
@Override
public int getLoadedChunksCount() {
return 0;
}
@Override
public LevelLightEngine getLightEngine() {
return world.getLightEngine();
}
}

View file

@ -1,5 +1,7 @@
package com.jozufozu.flywheel.core.virtual;
import org.jetbrains.annotations.Nullable;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@ -32,6 +34,7 @@ public interface VirtualEmptyBlockGetter extends BlockAndTintGetter {
}
@Override
@Nullable
default BlockEntity getBlockEntity(BlockPos pos) {
return null;
}
@ -99,7 +102,7 @@ public interface VirtualEmptyBlockGetter extends BlockAndTintGetter {
}
@Override
public void onBlockEmissionIncrease(BlockPos pos, int p_164456_) {
public void onBlockEmissionIncrease(BlockPos pos, int emissionLevel) {
}
@Override
@ -108,16 +111,16 @@ public interface VirtualEmptyBlockGetter extends BlockAndTintGetter {
}
@Override
public int runUpdates(int p_164449_, boolean p_164450_, boolean p_164451_) {
return p_164449_;
public int runUpdates(int pos, boolean isQueueEmpty, boolean updateBlockLight) {
return pos;
}
@Override
public void updateSectionStatus(SectionPos pos, boolean p_75838_) {
public void updateSectionStatus(SectionPos pos, boolean isQueueEmpty) {
}
@Override
public void enableLightSources(ChunkPos pos, boolean p_164453_) {
public void enableLightSources(ChunkPos pos, boolean isQueueEmpty) {
}
@Override

View file

@ -10,14 +10,13 @@ import net.minecraft.world.level.entity.LevelEntityGetter;
import net.minecraft.world.phys.AABB;
public class VirtualLevelEntityGetter<T extends EntityAccess> implements LevelEntityGetter<T> {
@Override
public T get(int p_156931_) {
public T get(int id) {
return null;
}
@Override
public T get(UUID pUuid) {
public T get(UUID uuid) {
return null;
}
@ -27,15 +26,14 @@ public class VirtualLevelEntityGetter<T extends EntityAccess> implements LevelEn
}
@Override
public <U extends T> void get(EntityTypeTest<T, U> p_156935_, Consumer<U> p_156936_) {
public <U extends T> void get(EntityTypeTest<T, U> test, Consumer<U> consumer) {
}
@Override
public void get(AABB p_156937_, Consumer<T> p_156938_) {
public void get(AABB boundingBox, Consumer<T> consumer) {
}
@Override
public <U extends T> void get(EntityTypeTest<T, U> p_156932_, AABB p_156933_, Consumer<U> p_156934_) {
public <U extends T> void get(EntityTypeTest<T, U> test, AABB bounds, Consumer<U> consumer) {
}
}

View file

@ -3,16 +3,15 @@ package com.jozufozu.flywheel.core.virtual;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.FlywheelWorld;
import it.unimi.dsi.fastutil.objects.Object2ShortMap;
import it.unimi.dsi.fastutil.objects.Object2ShortOpenHashMap;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
@ -33,12 +32,15 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.entity.LevelEntityGetter;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.gameevent.GameEvent.Context;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
import net.minecraft.world.level.storage.WritableLevelData;
import net.minecraft.world.phys.Vec3;
@ -46,37 +48,40 @@ import net.minecraft.world.scores.Scoreboard;
import net.minecraft.world.ticks.LevelTickAccess;
public class VirtualRenderWorld extends Level implements FlywheelWorld {
public final Map<BlockPos, BlockState> blocksAdded = new HashMap<>();
public final Map<BlockPos, BlockEntity> besAdded = new HashMap<>();
public final Set<SectionPos> spannedSections = new HashSet<>();
private final BlockPos.MutableBlockPos scratch = new BlockPos.MutableBlockPos();
protected final Level level;
protected final LevelLightEngine lighter;
protected final VirtualChunkSource chunkSource;
protected final LevelEntityGetter<Entity> entityGetter = new VirtualLevelEntityGetter<>();
protected final int height;
protected final int minBuildHeight;
protected final int height;
protected final Vec3i biomeOffset;
protected final VirtualChunkSource chunkSource;
protected final LevelLightEngine lightEngine;
protected final Map<BlockPos, BlockState> blockStates = new HashMap<>();
protected final Map<BlockPos, BlockEntity> blockEntities = new HashMap<>();
protected final Object2ShortMap<SectionPos> nonEmptyBlockCounts = new Object2ShortOpenHashMap<>();
protected final LevelEntityGetter<Entity> entityGetter = new VirtualLevelEntityGetter<>();
protected final BlockPos.MutableBlockPos scratchPos = new BlockPos.MutableBlockPos();
public VirtualRenderWorld(Level level) {
this(level, Vec3i.ZERO, level.getHeight(), level.getMinBuildHeight());
this(level, Vec3i.ZERO);
}
public VirtualRenderWorld(Level level, Vec3i biomeOffset) {
this(level, biomeOffset, level.getHeight(), level.getMinBuildHeight());
this(level, level.getMinBuildHeight(), level.getHeight(), biomeOffset);
}
public VirtualRenderWorld(Level level, Vec3i biomeOffset, int height, int minBuildHeight) {
super((WritableLevelData) level.getLevelData(), level.dimension(), level.dimensionTypeRegistration(), level::getProfiler,
public VirtualRenderWorld(Level level, int minBuildHeight, int height, Vec3i biomeOffset) {
super((WritableLevelData) level.getLevelData(), level.dimension(), level.dimensionTypeRegistration(), level.getProfilerSupplier(),
true, false, 0, 0);
this.biomeOffset = biomeOffset;
this.level = level;
this.height = nextMultipleOf16(height);
this.minBuildHeight = nextMultipleOf16(minBuildHeight);
this.height = nextMultipleOf16(height);
this.biomeOffset = biomeOffset;
this.chunkSource = new VirtualChunkSource(this);
this.lighter = new LevelLightEngine(chunkSource, true, false);
this.lightEngine = new LevelLightEngine(chunkSource, true, false);
}
/**
@ -91,67 +96,145 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
}
}
/**
* Run this after you're done using setBlock().
*/
public void runLightingEngine() {
for (Map.Entry<BlockPos, BlockState> entry : blocksAdded.entrySet()) {
BlockPos pos = entry.getKey();
BlockState state = entry.getValue();
int light = state.getLightEmission(this, pos);
if (light > 0) {
lighter.onBlockEmissionIncrease(pos, light);
}
}
public void clear() {
blockStates.clear();
blockEntities.clear();
lighter.runUpdates(Integer.MAX_VALUE, false, false);
nonEmptyBlockCounts.forEach((sectionPos, nonEmptyBlockCount) -> {
if (nonEmptyBlockCount > 0) {
lightEngine.updateSectionStatus(sectionPos, true);
}
});
nonEmptyBlockCounts.clear();
runLightEngine();
}
public void setBlockEntities(Collection<BlockEntity> blockEntities) {
besAdded.clear();
blockEntities.forEach(be -> besAdded.put(be.getBlockPos(), be));
this.blockEntities.clear();
blockEntities.forEach(this::setBlockEntity);
}
public void clear() {
blocksAdded.clear();
/**
* Run this after you're done using setBlock().
*/
public void runLightEngine() {
blockStates.forEach((pos, state) -> {
int light = state.getLightEmission(this, pos);
if (light > 0) {
lightEngine.onBlockEmissionIncrease(pos, light);
}
});
lightEngine.runUpdates(Integer.MAX_VALUE, false, false);
}
// MEANINGFUL OVERRIDES
@Override
public boolean setBlock(BlockPos pos, BlockState newState, int flags) {
blocksAdded.put(pos, newState);
public LevelChunk getChunk(int x, int z) {
throw new UnsupportedOperationException();
}
public ChunkAccess actuallyGetChunk(int x, int z) {
return getChunk(x, z, ChunkStatus.FULL);
}
@Override
public ChunkAccess getChunk(BlockPos pos) {
return actuallyGetChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()));
}
@Override
public boolean setBlock(BlockPos pos, BlockState newState, int flags, int recursionLeft) {
if (isOutsideBuildHeight(pos)) {
return false;
}
pos = pos.immutable();
BlockState oldState = getBlockState(pos);
if (oldState == newState) {
return false;
}
blockStates.put(pos, newState);
SectionPos sectionPos = SectionPos.of(pos);
if (spannedSections.add(sectionPos)) {
lighter.updateSectionStatus(sectionPos, false);
short nonEmptyBlockCount = nonEmptyBlockCounts.getShort(sectionPos);
boolean prevEmpty = nonEmptyBlockCount == 0;
if (!oldState.isAir()) {
--nonEmptyBlockCount;
}
if (!newState.isAir()) {
++nonEmptyBlockCount;
}
nonEmptyBlockCounts.put(sectionPos, nonEmptyBlockCount);
boolean nowEmpty = nonEmptyBlockCount == 0;
if (prevEmpty != nowEmpty) {
lightEngine.updateSectionStatus(sectionPos, nowEmpty);
}
if ((flags & Block.UPDATE_SUPPRESS_LIGHT) == 0) {
lighter.checkBlock(pos);
lightEngine.checkBlock(pos);
}
return true;
}
@Override
public int getHeight() {
return height;
}
@Override
public int getMinBuildHeight() {
return minBuildHeight;
}
@Override
public ChunkSource getChunkSource() {
return chunkSource;
}
@Override
public LevelLightEngine getLightEngine() {
return lighter;
return lightEngine;
}
@Override
public BlockState getBlockState(BlockPos pos) {
if (isOutsideBuildHeight(pos)) {
return Blocks.VOID_AIR.defaultBlockState();
}
BlockState state = blockStates.get(pos);
if (state != null) {
return state;
}
return Blocks.AIR.defaultBlockState();
}
public BlockState getBlockState(int x, int y, int z) {
return getBlockState(scratchPos.set(x, y, z));
}
@Override
public FluidState getFluidState(BlockPos pos) {
if (isOutsideBuildHeight(pos)) {
return Fluids.EMPTY.defaultFluidState();
}
return getBlockState(pos).getFluidState();
}
@Override
@Nullable
public BlockEntity getBlockEntity(BlockPos pos) {
if (!isOutsideBuildHeight(pos)) {
return blockEntities.get(pos);
}
return null;
}
@Override
public void setBlockEntity(BlockEntity blockEntity) {
BlockPos pos = blockEntity.getBlockPos();
if (!isOutsideBuildHeight(pos)) {
blockEntities.put(pos, blockEntity);
}
}
@Override
public void removeBlockEntity(BlockPos pos) {
if (!isOutsideBuildHeight(pos)) {
blockEntities.remove(pos);
}
}
@Override
@ -160,52 +243,39 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
}
@Override
public BlockState getBlockState(@Nullable BlockPos pos) {
BlockState state = blocksAdded.get(pos);
if (state != null)
return state;
return Blocks.AIR.defaultBlockState();
public ChunkSource getChunkSource() {
return chunkSource;
}
@Override
public boolean setBlockAndUpdate(BlockPos pos, BlockState state) {
return setBlock(pos, state, 0);
public int getMinBuildHeight() {
return minBuildHeight;
}
@Override
@Nullable
public BlockEntity getBlockEntity(BlockPos pos) {
return besAdded.get(pos);
}
@Override
public boolean isStateAtPosition(BlockPos pos, Predicate<BlockState> condition) {
return condition.test(getBlockState(pos));
}
public BlockState getBlockState(int x, int y, int z) {
return getBlockState(scratch.set(x, y, z));
public int getHeight() {
return height;
}
// BIOME OFFSET
@Override
public Holder<Biome> getBiome(BlockPos pPos) {
return super.getBiome(pPos.offset(biomeOffset));
public Holder<Biome> getBiome(BlockPos pos) {
return super.getBiome(pos.offset(biomeOffset));
}
@Override
public Holder<Biome> getUncachedNoiseBiome(int pX, int pY, int pZ) {
public Holder<Biome> getNoiseBiome(int x, int y, int z) {
// Control flow should never reach this method,
// so we add biomeOffset in case some other mod calls this directly.
return level.getUncachedNoiseBiome(pX + biomeOffset.getX(), pY + biomeOffset.getY(), pZ + biomeOffset.getZ());
return level.getNoiseBiome(x + biomeOffset.getX(), y + biomeOffset.getY(), z + biomeOffset.getZ());
}
@Override
public Holder<Biome> getNoiseBiome(int pX, int pY, int pZ) {
public Holder<Biome> getUncachedNoiseBiome(int x, int y, int z) {
// Control flow should never reach this method,
// so we add biomeOffset in case some other mod calls this directly.
return level.getNoiseBiome(pX + biomeOffset.getX(), pY + biomeOffset.getY(), pZ + biomeOffset.getZ());
return level.getUncachedNoiseBiome(x + biomeOffset.getX(), y + biomeOffset.getY(), z + biomeOffset.getZ());
}
// RENDERING CONSTANTS
@ -216,22 +286,27 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
}
@Override
public float getShade(Direction p_230487_1_, boolean p_230487_2_) {
public float getShade(Direction direction, boolean shade) {
return 1f;
}
// THIN WRAPPERS AHEAD
// THIN WRAPPERS
@Override
public Scoreboard getScoreboard() {
return level.getScoreboard();
}
@Override
public RecipeManager getRecipeManager() {
return level.getRecipeManager();
}
@Override
public BiomeManager getBiomeManager() {
return level.getBiomeManager();
}
@Override
public RegistryAccess registryAccess() {
return level.registryAccess();
}
@Override
public LevelTickAccess<Block> getBlockTicks() {
return level.getBlockTicks();
@ -243,21 +318,46 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
}
@Override
public RecipeManager getRecipeManager() {
return level.getRecipeManager();
public RegistryAccess registryAccess() {
return level.registryAccess();
}
// ADDITIONAL OVERRRIDES
@Override
public void updateNeighbourForOutputSignal(BlockPos pos, Block block) {
}
@Override
public int getFreeMapId() {
return level.getFreeMapId();
public boolean isLoaded(BlockPos pos) {
return true;
}
@Override
public Scoreboard getScoreboard() {
return level.getScoreboard();
public boolean isAreaLoaded(BlockPos center, int range) {
return true;
}
// UNIMPORTANT CONSTANTS
// UNIMPORTANT IMPLEMENTATIONS
@Override
public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) {
}
@Override
public void playSeededSound(Player player, double x, double y, double z, SoundEvent soundEvent,
SoundSource soundSource, float volume, float pitch, long seed) {
}
@Override
public void playSeededSound(Player player, Entity entity, SoundEvent soundEvent, SoundSource soundSource,
float volume, float pitch, long seed) {
}
@Override
public String gatherChunkSourceStats() {
return "";
}
@Override
@Nullable
@ -272,13 +372,24 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
}
@Override
public boolean isLoaded(BlockPos pos) {
return true;
public void setMapData(String mapId, MapItemSavedData data) {
}
@Override
public boolean isAreaLoaded(BlockPos center, int range) {
return true;
public int getFreeMapId() {
return 0;
}
@Override
public void destroyBlockProgress(int breakerId, BlockPos pos, int progress) {
}
@Override
public void levelEvent(@Nullable Player player, int type, BlockPos pos, int data) {
}
@Override
public void gameEvent(GameEvent event, Vec3 position, Context context) {
}
@Override
@ -286,50 +397,6 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
return Collections.emptyList();
}
@Override
public String gatherChunkSourceStats() {
return "";
}
// NOOP
@Override
public void levelEvent(@Nullable Player player, int type, BlockPos pos, int data) {}
@Override
public void gameEvent(GameEvent p_220404_, Vec3 p_220405_, Context p_220406_) {}
@Override
public void playSeededSound(Player p_220363_, double p_220364_, double p_220365_, double p_220366_,
SoundEvent p_220367_, SoundSource p_220368_, float p_220369_, float p_220370_, long p_220371_) {}
@Override
public void playSeededSound(Player p_220372_, Entity p_220373_, SoundEvent p_220374_, SoundSource p_220375_,
float p_220376_, float p_220377_, long p_220378_) {}
@Override
public void playSound(@Nullable Player player, double x, double y, double z, SoundEvent soundIn,
SoundSource category, float volume, float pitch) {}
@Override
public void playSound(@Nullable Player p_217384_1_, Entity p_217384_2_, SoundEvent p_217384_3_,
SoundSource p_217384_4_, float p_217384_5_, float p_217384_6_) {}
@Override
public void setMapData(String pMapId, MapItemSavedData pData) {}
@Override
public void destroyBlockProgress(int breakerId, BlockPos pos, int progress) {}
@Override
public void updateNeighbourForOutputSignal(BlockPos p_175666_1_, Block p_175666_2_) {}
@Override
public void gameEvent(@Nullable Entity pEntity, GameEvent pEvent, BlockPos pPos) {}
@Override
public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) {}
// Override Starlight's ExtendedWorld interface methods:
public LevelChunk getChunkAtImmediately(final int chunkX, final int chunkZ) {

View file

@ -1,27 +0,0 @@
package com.jozufozu.flywheel.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.LightTexture;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
@Mixin(LevelRenderer.class)
public class FixFabulousDepthMixin {
@Inject(method = "renderLevel", at = @At(value = "INVOKE", ordinal = 1, target = "Lnet/minecraft/client/renderer/PostChain;process(F)V"))
private void disableTransparencyShaderDepth(PoseStack p_228426_1_, float p_228426_2_, long p_228426_3_, boolean p_228426_5_, Camera p_228426_6_, GameRenderer p_228426_7_, LightTexture p_228426_8_, Matrix4f p_228426_9_, CallbackInfo ci) {
GlStateManager._depthMask(false);
}
}

View file

@ -8,7 +8,6 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
@ -24,8 +23,6 @@ import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.RenderBuffers;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.MinecraftForge;
@ -33,7 +30,6 @@ import net.minecraftforge.common.MinecraftForge;
@OnlyIn(Dist.CLIENT)
@Mixin(value = LevelRenderer.class, priority = 1001) // Higher priority to go after sodium
public class LevelRendererMixin {
@Shadow
private ClientLevel level;
@ -63,17 +59,4 @@ public class LevelRendererMixin {
private void renderBlockBreaking(PoseStack stack, float p_228426_2_, long p_228426_3_, boolean p_228426_5_, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f p_228426_9_, CallbackInfo ci) {
CrumblingRenderer.render(level, camera, stack);
}
// Instancing
/**
* This gets called when a block is marked for rerender by vanilla.
*/
@Inject(at = @At("TAIL"), method = "setBlockDirty(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;)V")
private void checkUpdate(BlockPos pos, BlockState lastState, BlockState newState, CallbackInfo ci) {
if (Backend.isOn()) {
InstancedRenderDispatcher.getBlockEntities(level)
.update(level.getBlockEntity(pos));
}
}
}

View file

@ -7,7 +7,6 @@ import net.minecraft.client.Minecraft;
@Mixin(Minecraft.class)
public interface PausedPartialTickAccessor {
@Accessor("pausePartialTick")
float flywheel$getPartialTicksPaused();
float flywheel$getPausePartialTick();
}

View file

@ -0,0 +1,18 @@
package com.jozufozu.flywheel.mixin.fix;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.mojang.blaze3d.platform.GlStateManager;
import net.minecraft.client.renderer.LevelRenderer;
@Mixin(LevelRenderer.class)
public class FixFabulousDepthMixin {
@Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/PostChain;process(F)V", ordinal = 1))
private void flywheel$disableTransparencyShaderDepth(CallbackInfo ci) {
GlStateManager._depthMask(false);
}
}

View file

@ -0,0 +1,36 @@
package com.jozufozu.flywheel.mixin.fix;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.mojang.blaze3d.vertex.PoseStack;
@Mixin(PoseStack.class)
public class FixNormalScalingMixin {
/**
* Minecraft negates the normal matrix if all scales are equal and negative, but
* does not return afterward. This allows the rest of the method's logic to be
* applied, which negates the matrix again, resulting in the matrix being the
* same as in the beginning.
*/
@Inject(method = "scale(FFF)V", at = @At(value = "INVOKE", target = "Lcom/mojang/math/Matrix3f;mul(F)V", shift = Shift.AFTER), cancellable = true)
private void flywheel$returnAfterNegate(float x, float y, float z, CallbackInfo ci) {
ci.cancel();
}
/**
* Minecraft takes the inverse cube root of the product of all scales to provide a
* rough estimate for normalization so that it does not need to be done later. It
* does not make sense for this "normalization factor" to be negative though, as
* that would invert all normals. Additionally, Minecraft's fastInvCubeRoot method
* does not work for negative numbers.
*/
@ModifyArg(method = "scale(FFF)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;fastInvCubeRoot(F)F"))
private float flywheel$absInvCbrtInput(float input) {
return Math.abs(input);
}
}

View file

@ -1,82 +1,21 @@
package com.jozufozu.flywheel.mixin.instancemanage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Coerce;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
import com.jozufozu.flywheel.util.RenderChunkExtension;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
import net.minecraft.client.renderer.chunk.RenderChunkRegion;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
@Mixin(targets = "net.minecraft.client.renderer.chunk.ChunkRenderDispatcher$RenderChunk$RebuildTask")
public abstract class ChunkRebuildHooksMixin {
@Unique
private Level flywheel$level;
@Inject(method = "<init>(Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;Lnet/minecraft/world/level/ChunkPos;DLnet/minecraft/client/renderer/chunk/RenderChunkRegion;Z)V", at = @At("RETURN"))
private void setLevel(ChunkRenderDispatcher.RenderChunk this$1, ChunkPos pos, double p_194427_, RenderChunkRegion region, boolean p_194429_, CallbackInfo ci) {
flywheel$level = ((RenderChunkExtension) this$1).flywheel$getLevel();
}
@Redirect(method = "doTask", at = @At(value = "INVOKE", target = "Ljava/util/List;addAll(Ljava/util/Collection;)Z"))
private <E extends BlockEntity> boolean addAndFilterBEs(List<BlockEntity> self, Collection<? extends E> es) {
if (!Backend.canUseInstancing(flywheel$level)) {
return self.addAll(es);
public class ChunkRebuildHooksMixin {
@Inject(method = "handleBlockEntity(Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk$RebuildTask$CompileResults;Lnet/minecraft/world/level/block/entity/BlockEntity;)V", at = @At("HEAD"), cancellable = true)
private void flywheel$tryAddBlockEntity(@Coerce Object compileResults, BlockEntity blockEntity, CallbackInfo ci) {
if (InstancedRenderDispatcher.tryAddBlockEntity(blockEntity)) {
ci.cancel();
}
boolean added = false;
var instanced = new ArrayList<BlockEntity>();
for (E be : es) {
if (InstancedRenderRegistry.canInstance(be.getType())) {
instanced.add(be);
}
if (!InstancedRenderRegistry.shouldSkipRender(be)) {
self.add(be);
added = true;
}
}
InstancedRenderDispatcher.getBlockEntities(flywheel$level).queueAddAll(instanced);
return added;
}
@Redirect(method = "doTask", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;updateGlobalBlockEntities(Ljava/util/Collection;)V"))
private void addAndFilterBEs(ChunkRenderDispatcher.RenderChunk self, Collection<BlockEntity> bes) {
if (!Backend.canUseInstancing(flywheel$level)) {
((RenderChunkAccessor) self).flywheel$updateGlobalBlockEntities(bes);
return;
}
var global = new ArrayList<BlockEntity>();
var instanced = new ArrayList<BlockEntity>();
for (BlockEntity be : bes) {
if (InstancedRenderRegistry.canInstance(be.getType())) {
instanced.add(be);
}
if (!InstancedRenderRegistry.shouldSkipRender(be)) {
global.add(be);
}
}
InstancedRenderDispatcher.getBlockEntities(flywheel$level).queueAddAll(instanced);
((RenderChunkAccessor) self).flywheel$updateGlobalBlockEntities(global);
}
}

View file

@ -1,14 +0,0 @@
package com.jozufozu.flywheel.mixin.instancemanage;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
@Mixin(ChunkRenderDispatcher.class)
public interface ChunkRenderDispatcherAccessor {
@Accessor
ClientLevel getLevel();
}

View file

@ -0,0 +1,40 @@
package com.jozufozu.flywheel.mixin.instancemanage;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
@Mixin(LevelRenderer.class)
public class InstanceUpdateMixin {
@Shadow
private ClientLevel level;
/**
* This gets called when a block is marked for rerender by vanilla.
*/
@Inject(method = "setBlockDirty(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;)V", at = @At("TAIL"))
private void flywheel$checkUpdate(BlockPos pos, BlockState oldState, BlockState newState, CallbackInfo ci) {
if (!Backend.isOn()) {
return;
}
BlockEntity blockEntity = level.getBlockEntity(pos);
if (blockEntity == null) {
return;
}
InstancedRenderDispatcher.getBlockEntities(level)
.queueUpdate(blockEntity);
}
}

View file

@ -1,19 +0,0 @@
package com.jozufozu.flywheel.mixin.instancemanage;
import java.util.Collection;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
import net.minecraft.world.level.block.entity.BlockEntity;
/**
* For use in {@link ChunkRebuildHooksMixin#addAndFilterBEs(ChunkRenderDispatcher.RenderChunk, Collection)}
*/
@Mixin(ChunkRenderDispatcher.RenderChunk.class)
public interface RenderChunkAccessor {
@Invoker("updateGlobalBlockEntities")
void flywheel$updateGlobalBlockEntities(Collection<BlockEntity> blockEntities);
}

View file

@ -1,21 +0,0 @@
package com.jozufozu.flywheel.mixin.instancemanage;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
@Mixin(ChunkRenderDispatcher.RenderChunk.class)
public class RenderChunkMixin implements com.jozufozu.flywheel.util.RenderChunkExtension {
@Shadow(aliases = "this$0") // Optifine does not use the obfuscated name so the mapped name must be included as an alias
@Final
private ChunkRenderDispatcher this$0;
@Override
public ClientLevel flywheel$getLevel() {
return ((ChunkRenderDispatcherAccessor) this$0).getLevel();
}
}

View file

@ -1,65 +0,0 @@
package com.jozufozu.flywheel.mixin.instancemanage;
import java.util.ArrayList;
import java.util.List;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
import me.jellysquid.mods.sodium.client.render.chunk.data.ChunkRenderData;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
@Mixin(targets = "me.jellysquid.mods.sodium.client.render.chunk.data.ChunkRenderData$Builder", remap = false)
public class SodiumChunkRenderDataMixin {
@Unique
private List<BlockEntity> flywheel$blockEntities;
@Unique
private Level flywheel$level;
@Inject(method = "addBlockEntity", at = @At("HEAD"), cancellable = true, require = 0)
private void flywheel$onAddBlockEntity(BlockEntity be, boolean cull, CallbackInfo ci) {
if (flywheel$level == null) {
flywheel$level = be.getLevel();
}
if (!Backend.canUseInstancing(flywheel$level)) {
return;
}
if (InstancedRenderRegistry.canInstance(be.getType())) {
if (flywheel$blockEntities == null) {
flywheel$blockEntities = new ArrayList<>();
}
// Collect BEs in a temporary list to avoid excessive synchronization in InstancedRenderDispatcher.
flywheel$blockEntities.add(be);
}
if (InstancedRenderRegistry.shouldSkipRender(be)) {
ci.cancel();
}
}
@Inject(method = "build", at = @At("HEAD"))
private void flywheel$onBuild(CallbackInfoReturnable<ChunkRenderData> cir) {
if (flywheel$level == null || flywheel$blockEntities == null || !Backend.canUseInstancing(flywheel$level)) {
return;
}
InstancedRenderDispatcher.getBlockEntities(flywheel$level)
.queueAddAll(flywheel$blockEntities);
}
}

View file

@ -1,6 +1,8 @@
package com.jozufozu.flywheel.mixin.light;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@ -12,12 +14,12 @@ import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
@Mixin(ClientChunkCache.class)
public abstract class LightUpdateMixin extends ChunkSource {
@Shadow
@Final
ClientLevel level;
/**
* JUSTIFICATION: This method is called after a lighting tick once per subchunk where a
@ -25,12 +27,9 @@ public abstract class LightUpdateMixin extends ChunkSource {
* the rendering system that it needs to redraw a chunk. It does all that work asynchronously,
* and we should too.
*/
@Inject(at = @At("HEAD"), method = "onLightUpdate")
private void onLightUpdate(LightLayer type, SectionPos pos, CallbackInfo ci) {
ClientChunkCache thi = ((ClientChunkCache) (Object) this);
ClientLevel world = (ClientLevel) thi.getLevel();
LightUpdater.get(world)
.onLightUpdate(type, pos.asLong());
@Inject(method = "onLightUpdate(Lnet/minecraft/world/level/LightLayer;Lnet/minecraft/core/SectionPos;)V", at = @At("HEAD"))
private void flywheel$onLightUpdate(LightLayer layer, SectionPos pos, CallbackInfo ci) {
LightUpdater.get(level)
.onLightUpdate(layer, pos.asLong());
}
}

View file

@ -1,6 +1,7 @@
package com.jozufozu.flywheel.mixin.light;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@ -8,25 +9,42 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.RenderWork;
import com.jozufozu.flywheel.light.LightUpdater;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket;
@Mixin(ClientPacketListener.class)
public class NetworkLightUpdateMixin {
@Shadow
private ClientLevel level;
@Inject(at = @At("TAIL"), method = "handleLightUpdatePacket")
private void onLightPacket(ClientboundLightUpdatePacket packet, CallbackInfo ci) {
@Inject(method = "handleLevelChunkWithLight(Lnet/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket;)V", at = @At("TAIL"))
private void flywheel$onLevelChunkWithLight(ClientboundLevelChunkWithLightPacket packet, CallbackInfo ci) {
RenderWork.enqueue(() -> {
ClientLevel world = Minecraft.getInstance().level;
ClientLevel level = this.level;
if (world == null) return;
if (level == null) return;
int chunkX = packet.getX();
int chunkZ = packet.getZ();
LightUpdater.get(world)
LightUpdater.get(level)
.onLightPacket(chunkX, chunkZ);
});
}
@Inject(method = "handleLightUpdatePacket(Lnet/minecraft/network/protocol/game/ClientboundLightUpdatePacket;)V", at = @At("TAIL"))
private void flywheel$onLightUpdatePacket(ClientboundLightUpdatePacket packet, CallbackInfo ci) {
RenderWork.enqueue(() -> {
ClientLevel level = this.level;
if (level == null) return;
int chunkX = packet.getX();
int chunkZ = packet.getZ();
LightUpdater.get(level)
.onLightPacket(chunkX, chunkZ);
});
}

View file

@ -0,0 +1,23 @@
package com.jozufozu.flywheel.mixin.sodium;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import me.jellysquid.mods.sodium.client.render.chunk.tasks.ChunkRenderRebuildTask;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.world.level.block.entity.BlockEntity;
@Mixin(value = ChunkRenderRebuildTask.class, remap = false)
public class ChunkRenderRebuildTaskMixin {
@Redirect(method = "performBuild", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/blockentity/BlockEntityRenderDispatcher;getRenderer(Lnet/minecraft/world/level/block/entity/BlockEntity;)Lnet/minecraft/client/renderer/blockentity/BlockEntityRenderer;"))
private BlockEntityRenderer<?> flywheel$redirectGetRenderer(BlockEntityRenderDispatcher dispatcher, BlockEntity blockEntity) {
if (InstancedRenderDispatcher.tryAddBlockEntity(blockEntity)) {
return null;
}
return dispatcher.getRenderer(blockEntity);
}
}

View file

@ -0,0 +1,48 @@
package com.jozufozu.flywheel.mixin.sodium;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import com.google.common.base.Suppliers;
import net.minecraftforge.fml.loading.LoadingModList;
public class SodiumMixinPlugin implements IMixinConfigPlugin {
private static final Supplier<Boolean> IS_SODIUM_LOADED = Suppliers.memoize(() -> LoadingModList.get().getModFileById("rubidium") != null);
@Override
public void onLoad(String mixinPackage) {
}
@Override
public String getRefMapperConfig() {
return null;
}
@Override
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
return IS_SODIUM_LOADED.get();
}
@Override
public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) {
}
@Override
public List<String> getMixins() {
return null;
}
@Override
public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
}
@Override
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
}
}

View file

@ -37,7 +37,7 @@ public class AnimationTickHolder {
public static float getPartialTicks() {
Minecraft mc = Minecraft.getInstance();
return (mc.isPaused() ? ((PausedPartialTickAccessor) mc).flywheel$getPartialTicksPaused() : mc.getFrameTime());
return (mc.isPaused() ? ((PausedPartialTickAccessor) mc).flywheel$getPausePartialTick() : mc.getFrameTime());
}
// Unused but might be useful for debugging.

View file

@ -11,7 +11,7 @@ displayName = "Flywheel"
logoFile = "logo.png"
displayURL = "https://www.curseforge.com/minecraft/mc-mods/flywheel"
updateJSONURL = "https://api.modrinth.com/updates/flywheel/forge_updates.json"
authors = "Jozufozu"
authors = "Jozufozu, PepperCode1"
description = '''
A modern engine for modded minecraft.'''

View file

@ -12,7 +12,6 @@
"ClientLevelMixin",
"ClientMainMixin",
"EntityTypeMixin",
"FixFabulousDepthMixin",
"FrustumMixin",
"GlStateManagerMixin",
"LevelRendererAccessor",
@ -20,13 +19,12 @@
"PausedPartialTickAccessor",
"RenderTexturesMixin",
"RenderTypeMixin",
"fix.FixFabulousDepthMixin",
"fix.FixNormalScalingMixin",
"instancemanage.ChunkRebuildHooksMixin",
"instancemanage.ChunkRenderDispatcherAccessor",
"instancemanage.InstanceAddMixin",
"instancemanage.InstanceRemoveMixin",
"instancemanage.RenderChunkAccessor",
"instancemanage.RenderChunkMixin",
"instancemanage.SodiumChunkRenderDataMixin",
"instancemanage.InstanceUpdateMixin",
"light.LightUpdateMixin",
"light.NetworkLightUpdateMixin",
"matrix.Matrix3fMixin",

View file

@ -0,0 +1,14 @@
{
"required": true,
"minVersion": "0.8",
"package": "com.jozufozu.flywheel.mixin.sodium",
"compatibilityLevel": "JAVA_17",
"refmap": "flywheel.refmap.json",
"plugin": "com.jozufozu.flywheel.mixin.sodium.SodiumMixinConfigPlugin",
"client": [
"ChunkRenderRebuildTaskMixin"
],
"injectors": {
"defaultRequire": 1
}
}