Fix contraption shadow rendering with optifine

- More conventional naming for fields of ContraptionMatrices
 - Encapsulate fields in ContraptionMatrices
 - Move creation to RenderLayerEvent
 - De-clutter RenderedContraption#beginFrame
 - Bump flywheel version
This commit is contained in:
Jozufozu 2021-08-05 15:05:32 -07:00
parent e7c7669d7f
commit 26fbe97ae6
16 changed files with 140 additions and 102 deletions

View file

@ -16,7 +16,7 @@ cursegradle_version = 1.4.0
# dependency versions # dependency versions
registrate_version = 1.0.10 registrate_version = 1.0.10
flywheel_version = 1.16-0.2.0.41 flywheel_version = 1.16-0.2.3.44
jei_version = 7.7.1.116 jei_version = 7.7.1.116
# curseforge information # curseforge information

View file

@ -44,7 +44,7 @@ public class DrillRenderer extends KineticTileEntityRenderer {
float time = AnimationTickHolder.getRenderTime() / 20; float time = AnimationTickHolder.getRenderTime() / 20;
float angle = (float) (((time * speed) % 360)); float angle = (float) (((time * speed) % 360));
MatrixStack m = matrices.contraptionStack; MatrixStack m = matrices.getModel();
m.pushPose(); m.pushPose();
MatrixTransformStack.of(m) MatrixTransformStack.of(m)
.centre() .centre()
@ -55,9 +55,9 @@ public class DrillRenderer extends KineticTileEntityRenderer {
superBuffer superBuffer
.transform(m) .transform(m)
.light(matrices.entityMatrix, .light(matrices.getWorld(),
ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld)) ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
.renderInto(matrices.entityStack, buffer.getBuffer(RenderType.solid())); .renderInto(matrices.getViewProjection(), buffer.getBuffer(RenderType.solid()));
m.popPose(); m.popPose();
} }

View file

@ -52,13 +52,13 @@ public class HarvesterRenderer extends SafeTileEntityRenderer<HarvesterTileEntit
if (context.contraption.stalled) if (context.contraption.stalled)
speed = 0; speed = 0;
superBuffer.transform(matrices.contraptionStack); superBuffer.transform(matrices.getModel());
transform(context.world, facing, superBuffer, speed); transform(context.world, facing, superBuffer, speed);
superBuffer superBuffer
.light(matrices.entityMatrix, .light(matrices.getWorld(),
ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld)) ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
.renderInto(matrices.entityStack, buffers.getBuffer(RenderType.cutoutMipped())); .renderInto(matrices.getViewProjection(), buffers.getBuffer(RenderType.cutoutMipped()));
} }
public static void transform(World world, Direction facing, SuperByteBuffer superBuffer, float speed) { public static void transform(World world, Direction facing, SuperByteBuffer superBuffer, float speed) {

View file

@ -56,9 +56,9 @@ public class PortableStorageInterfaceRenderer extends SafeTileEntityRenderer<Por
lit = te.isConnected(); lit = te.isConnected();
} }
render(blockState, lit, progress, matrices.contraptionStack, sbb -> sbb.light(matrices.entityMatrix, render(blockState, lit, progress, matrices.getModel(), sbb -> sbb.light(matrices.getWorld(),
ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld)) ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
.renderInto(matrices.entityStack, vb)); .renderInto(matrices.getViewProjection(), vb));
} }
private static void render(BlockState blockState, boolean lit, float progress, private static void render(BlockState blockState, boolean lit, float progress,

View file

@ -181,7 +181,7 @@ public class DeployerRenderer extends SafeTileEntityRenderer<DeployerTileEntity>
Vector3d offset = Vector3d.atLowerCornerOf(blockState.getValue(FACING) Vector3d offset = Vector3d.atLowerCornerOf(blockState.getValue(FACING)
.getNormal()).scale(factor); .getNormal()).scale(factor);
MatrixStack m = matrices.contraptionStack; MatrixStack m = matrices.getModel();
m.pushPose(); m.pushPose();
m.translate(offset.x, offset.y, offset.z); m.translate(offset.x, offset.y, offset.z);
@ -190,10 +190,10 @@ public class DeployerRenderer extends SafeTileEntityRenderer<DeployerTileEntity>
pole = transform(world, pole, blockState, pos, true); pole = transform(world, pole, blockState, pos, true);
hand = transform(world, hand, blockState, pos, false); hand = transform(world, hand, blockState, pos, false);
pole.light(matrices.entityMatrix, ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld)) pole.light(matrices.getWorld(), ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
.renderInto(matrices.entityStack, builder); .renderInto(matrices.getViewProjection(), builder);
hand.light(matrices.entityMatrix, ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld)) hand.light(matrices.getWorld(), ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
.renderInto(matrices.entityStack, builder); .renderInto(matrices.getViewProjection(), builder);
m.popPose(); m.popPose();
} }

View file

@ -192,7 +192,7 @@ public class SawRenderer extends SafeTileEntityRenderer<SawTileEntity> {
superBuffer = PartialBufferer.get(AllBlockPartials.SAW_BLADE_VERTICAL_INACTIVE, state); superBuffer = PartialBufferer.get(AllBlockPartials.SAW_BLADE_VERTICAL_INACTIVE, state);
} }
MatrixStack m = matrices.contraptionStack; MatrixStack m = matrices.getModel();
m.pushPose(); m.pushPose();
MatrixTransformStack.of(m) MatrixTransformStack.of(m)
.centre() .centre()
@ -205,8 +205,8 @@ public class SawRenderer extends SafeTileEntityRenderer<SawTileEntity> {
.unCentre(); .unCentre();
superBuffer.transform(m) superBuffer.transform(m)
.light(matrices.entityMatrix, ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld)) .light(matrices.getWorld(), ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
.renderInto(matrices.entityStack, buffer.getBuffer(RenderType.cutoutMipped())); .renderInto(matrices.getViewProjection(), buffer.getBuffer(RenderType.cutoutMipped()));
m.popPose(); m.popPose();
} }

View file

@ -1,7 +1,6 @@
package com.simibubi.create.content.contraptions.components.structureMovement; package com.simibubi.create.content.contraptions.components.structureMovement;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionMatrices;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher; import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher;
import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.IRenderTypeBuffer;
@ -39,7 +38,7 @@ public class ContraptionEntityRenderer<C extends AbstractContraptionEntity> exte
Contraption contraption = entity.getContraption(); Contraption contraption = entity.getContraption();
if (contraption != null) { if (contraption != null) {
ContraptionRenderDispatcher.render(entity, contraption, buffers); ContraptionRenderDispatcher.renderFromEntity(entity, contraption, buffers);
} }
} }

View file

@ -53,14 +53,14 @@ public class StabilizedBearingMovementBehaviour extends MovementBehaviour {
orientation = rotation; orientation = rotation;
superBuffer.transform(matrices.contraptionStack); superBuffer.transform(matrices.getModel());
superBuffer.rotateCentered(orientation); superBuffer.rotateCentered(orientation);
// render // render
superBuffer superBuffer
.light(matrices.entityMatrix, .light(matrices.getWorld(),
ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld)) ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
.renderInto(matrices.entityStack, buffer.getBuffer(RenderType.solid())); .renderInto(matrices.getViewProjection(), buffer.getBuffer(RenderType.solid()));
} }
@Override @Override

View file

@ -6,9 +6,16 @@ import com.simibubi.create.foundation.utility.AnimationTickHolder;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Matrix3f;
import net.minecraft.util.math.vector.Matrix4f; import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3d;
/**
* LIFETIME: one frame
*
* <p>
* ContraptionMatrices must be re-created per-contraption per-frame
* </p>
*/
public class ContraptionMatrices { public class ContraptionMatrices {
/** /**
@ -16,47 +23,68 @@ public class ContraptionMatrices {
*/ */
public static final ContraptionMatrices EMPTY = new ContraptionMatrices(); public static final ContraptionMatrices EMPTY = new ContraptionMatrices();
public final MatrixStack entityStack; private final MatrixStack modelViewProjection;
public final MatrixStack contraptionStack; private final MatrixStack viewProjection;
public final MatrixStack finalStack; private final MatrixStack model;
public final Matrix4f entityMatrix; private final Matrix4f world;
public final Matrix4f lightMatrix; private final Matrix4f light;
private ContraptionMatrices() { private ContraptionMatrices() {
this.entityStack = this.contraptionStack = this.finalStack = new MatrixStack(); this.viewProjection = this.model = this.modelViewProjection = new MatrixStack();
this.entityMatrix = new Matrix4f(); this.world = new Matrix4f();
this.lightMatrix = new Matrix4f(); this.light = new Matrix4f();
} }
public ContraptionMatrices(MatrixStack entityStack, AbstractContraptionEntity entity) { public ContraptionMatrices(MatrixStack viewProjection, AbstractContraptionEntity entity) {
this.entityStack = copyStack(entityStack); this.viewProjection = copyStack(viewProjection);
this.contraptionStack = new MatrixStack();
float partialTicks = AnimationTickHolder.getPartialTicks(); float partialTicks = AnimationTickHolder.getPartialTicks();
entity.doLocalTransforms(partialTicks, new MatrixStack[] { this.contraptionStack }); this.model = creatModelMatrix(entity, partialTicks);
entityMatrix = translateTo(entity, partialTicks); world = translateTo(entity, partialTicks);
lightMatrix = entityMatrix.copy(); light = getWorld().copy();
lightMatrix.multiply(contraptionStack.last().pose()); getLight().multiply(this.getModel()
.last().pose());
finalStack = copyStack(entityStack); modelViewProjection = copyStack(viewProjection);
transform(finalStack, contraptionStack); transform(getModelViewProjection(), this.getModel());
} }
public MatrixStack getFinalStack() { public MatrixStack getModelViewProjection() {
return finalStack; return modelViewProjection;
} }
public Matrix4f getFinalModel() { public MatrixStack getViewProjection() {
return finalStack.last().pose(); return viewProjection;
} }
public Matrix3f getFinalNormal() { public MatrixStack getModel() {
return finalStack.last().normal(); return model;
} }
public Matrix4f getFinalLight() { public Matrix4f getWorld() {
return lightMatrix; return world;
}
public Matrix4f getLight() {
return light;
}
public static Matrix4f createModelViewPartial(AbstractContraptionEntity entity, float pt, Vector3d cameraPos) {
float x = (float) (MathHelper.lerp(pt, entity.xOld, entity.getX()) - cameraPos.x);
float y = (float) (MathHelper.lerp(pt, entity.yOld, entity.getY()) - cameraPos.y);
float z = (float) (MathHelper.lerp(pt, entity.zOld, entity.getZ()) - cameraPos.z);
Matrix4f mat = Matrix4f.createTranslateMatrix(x, y, z);
Matrix4f modelMatrix = creatModelMatrix(entity, pt).last().pose();
mat.multiply(modelMatrix);
return mat;
}
public static MatrixStack creatModelMatrix(AbstractContraptionEntity entity, float partialTicks) {
MatrixStack model = new MatrixStack();
entity.doLocalTransforms(partialTicks, new MatrixStack[] { model});
return model;
} }
public static Matrix4f translateTo(Entity entity, float partialTicks) { public static Matrix4f translateTo(Entity entity, float partialTicks) {
@ -82,9 +110,4 @@ public class ContraptionMatrices {
return cms; return cms;
} }
public Matrix4f contraptionPose() {
return contraptionStack.last()
.pose();
}
} }

View file

@ -71,13 +71,24 @@ public class ContraptionRenderDispatcher {
reset(); reset();
} }
public static void render(AbstractContraptionEntity entity, Contraption contraption, IRenderTypeBuffer buffers) { public static void renderFromEntity(AbstractContraptionEntity entity, Contraption contraption, IRenderTypeBuffer buffers) {
World world = entity.level; World world = entity.level;
ContraptionRenderInfo renderInfo = WORLDS.get(world) ContraptionRenderInfo renderInfo = WORLDS.get(world)
.getRenderInfo(contraption); .getRenderInfo(contraption);
ContraptionMatrices matrices = renderInfo.getMatrices();
renderDynamic(world, renderInfo.renderWorld, contraption, renderInfo.getMatrices(), buffers); // something went wrong with the other rendering
if (matrices == null) return;
PlacementSimulationWorld renderWorld = renderInfo.renderWorld;
renderTileEntities(world, renderWorld, contraption, matrices, buffers);
if (buffers instanceof IRenderTypeBuffer.Impl)
((IRenderTypeBuffer.Impl) buffers).endBatch();
renderActors(world, renderWorld, contraption, matrices, buffers);
} }
public static PlacementSimulationWorld setupRenderWorld(World world, Contraption c) { public static PlacementSimulationWorld setupRenderWorld(World world, Contraption c) {
@ -96,18 +107,10 @@ public class ContraptionRenderDispatcher {
return renderWorld; return renderWorld;
} }
public static void renderDynamic(World world, PlacementSimulationWorld renderWorld, Contraption c,
ContraptionMatrices matrices, IRenderTypeBuffer buffer) {
renderTileEntities(world, renderWorld, c, matrices, buffer);
if (buffer instanceof IRenderTypeBuffer.Impl)
((IRenderTypeBuffer.Impl) buffer).endBatch();
renderActors(world, renderWorld, c, matrices, buffer);
}
public static void renderTileEntities(World world, PlacementSimulationWorld renderWorld, Contraption c, public static void renderTileEntities(World world, PlacementSimulationWorld renderWorld, Contraption c,
ContraptionMatrices matrices, IRenderTypeBuffer buffer) { ContraptionMatrices matrices, IRenderTypeBuffer buffer) {
TileEntityRenderHelper.renderTileEntities(world, renderWorld, c.specialRenderedTileEntities, TileEntityRenderHelper.renderTileEntities(world, renderWorld, c.specialRenderedTileEntities,
matrices.getFinalStack(), matrices.getFinalLight(), buffer); matrices.getModelViewProjection(), matrices.getLight(), buffer);
} }
protected static void renderActors(World world, PlacementSimulationWorld renderWorld, Contraption c, protected static void renderActors(World world, PlacementSimulationWorld renderWorld, Contraption c,
@ -120,7 +123,7 @@ public class ContraptionRenderDispatcher {
context.world = world; context.world = world;
Template.BlockInfo blockInfo = actor.getLeft(); Template.BlockInfo blockInfo = actor.getLeft();
MatrixStack m = matrices.contraptionStack; MatrixStack m = matrices.getModel();
m.pushPose(); m.pushPose();
MatrixTransformStack.of(m) MatrixTransformStack.of(m)
.translate(blockInfo.pos); .translate(blockInfo.pos);

View file

@ -1,6 +1,10 @@
package com.simibubi.create.content.contraptions.components.structureMovement.render; package com.simibubi.create.content.contraptions.components.structureMovement.render;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.event.BeginFrameEvent; import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.event.RenderLayerEvent;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity; import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.utility.AnimationTickHolder;
@ -30,29 +34,41 @@ public class ContraptionRenderInfo {
} }
public void beginFrame(BeginFrameEvent event) { public void beginFrame(BeginFrameEvent event) {
matrices = null;
AbstractContraptionEntity entity = contraption.entity; AbstractContraptionEntity entity = contraption.entity;
visible = event.getClippingHelper().isVisible(entity.getBoundingBoxForCulling().inflate(2)); visible = event.getClippingHelper().isVisible(entity.getBoundingBoxForCulling().inflate(2));
event.getStack().pushPose();
Vector3d cameraPos = event.getInfo()
.getPosition();
double x = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.xOld, entity.getX()) - cameraPos.x;
double y = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.yOld, entity.getY()) - cameraPos.y;
double z = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.zOld, entity.getZ()) - cameraPos.z;
event.getStack().translate(x, y, z);
matrices = new ContraptionMatrices(event.getStack(), entity);
event.getStack().popPose();
} }
public boolean isVisible() { public boolean isVisible() {
return visible && contraption.entity.isAlive(); return visible && contraption.entity.isAlive();
} }
/**
* Need to call this during RenderLayerEvent.
*/
public void setupMatrices(MatrixStack viewProjection, double camX, double camY, double camZ) {
if (matrices == null) {
AbstractContraptionEntity entity = contraption.entity;
viewProjection.pushPose();
double x = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.xOld, entity.getX()) - camX;
double y = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.yOld, entity.getY()) - camY;
double z = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.zOld, entity.getZ()) - camZ;
viewProjection.translate(x, y, z);
matrices = new ContraptionMatrices(viewProjection, entity);
viewProjection.popPose();
}
}
/**
* If #setupMatrices is called correctly, this will not return null
*/
public ContraptionMatrices getMatrices() { public ContraptionMatrices getMatrices() {
return matrices; return matrices;
} }

View file

@ -29,7 +29,11 @@ public abstract class ContraptionRenderManager<C extends ContraptionRenderInfo>
this.world = (World) world; this.world = (World) world;
} }
public abstract void renderLayer(RenderLayerEvent event); public void renderLayer(RenderLayerEvent event) {
for (C c : visible) {
c.setupMatrices(event.stack, event.camX, event.camY, event.camZ);
}
}
protected abstract C create(Contraption c); protected abstract C create(Contraption c);

View file

@ -38,6 +38,8 @@ public class FlwContraptionManager extends ContraptionRenderManager<RenderedCont
@Override @Override
public void renderLayer(RenderLayerEvent event) { public void renderLayer(RenderLayerEvent event) {
super.renderLayer(event);
if (visible.isEmpty()) return; if (visible.isEmpty()) return;
RenderType layer = event.getType(); RenderType layer = event.getType();

View file

@ -26,7 +26,6 @@ import net.minecraft.client.renderer.RenderType;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Matrix4f; import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3d; import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World; import net.minecraft.world.World;
@ -40,7 +39,7 @@ public class RenderedContraption extends ContraptionRenderInfo {
private final Map<RenderType, ModelRenderer> renderLayers = new HashMap<>(); private final Map<RenderType, ModelRenderer> renderLayers = new HashMap<>();
private Matrix4f model; private Matrix4f modelViewPartial;
private AxisAlignedBB lightBox; private AxisAlignedBB lightBox;
public RenderedContraption(Contraption contraption, PlacementSimulationWorld renderWorld) { public RenderedContraption(Contraption contraption, PlacementSimulationWorld renderWorld) {
@ -78,26 +77,17 @@ public class RenderedContraption extends ContraptionRenderInfo {
kinetics.beginFrame(event.getInfo()); kinetics.beginFrame(event.getInfo());
AbstractContraptionEntity entity = contraption.entity; Vector3d cameraPos = event.getCameraPos();
float pt = AnimationTickHolder.getPartialTicks();
AxisAlignedBB lightBox = GridAlignedBB.toAABB(lighter.lightVolume.getTextureVolume());
Vector3d cameraPos = event.getInfo() modelViewPartial = ContraptionMatrices.createModelViewPartial(contraption.entity, AnimationTickHolder.getPartialTicks(), cameraPos);
.getPosition();
float x = (float) (MathHelper.lerp(pt, entity.xOld, entity.getX()) - cameraPos.x); lightBox = GridAlignedBB.toAABB(lighter.lightVolume.getTextureVolume())
float y = (float) (MathHelper.lerp(pt, entity.yOld, entity.getY()) - cameraPos.y); .move(-cameraPos.x, -cameraPos.y, -cameraPos.z);
float z = (float) (MathHelper.lerp(pt, entity.zOld, entity.getZ()) - cameraPos.z);
model = Matrix4f.createTranslateMatrix(x, y, z);
model.multiply(getMatrices().contraptionPose());
this.lightBox = lightBox.move(-cameraPos.x, -cameraPos.y, -cameraPos.z);
} }
void setup(ContraptionProgram shader) { void setup(ContraptionProgram shader) {
if (model == null || lightBox == null) return; if (modelViewPartial == null || lightBox == null) return;
shader.bind(model, lightBox); shader.bind(modelViewPartial, lightBox);
lighter.lightVolume.bind(); lighter.lightVolume.bind();
} }

View file

@ -21,6 +21,7 @@ public class SBBContraptionManager extends ContraptionRenderManager<ContraptionR
@Override @Override
public void renderLayer(RenderLayerEvent event) { public void renderLayer(RenderLayerEvent event) {
super.renderLayer(event);
visible.forEach(info -> renderContraptionLayerSBB(event, info)); visible.forEach(info -> renderContraptionLayerSBB(event, info));
} }
@ -38,12 +39,12 @@ public class SBBContraptionManager extends ContraptionRenderManager<ContraptionR
SuperByteBuffer contraptionBuffer = CreateClient.BUFFER_CACHE.get(CONTRAPTION, Pair.of(renderInfo.contraption, layer), () -> buildStructureBuffer(renderInfo.renderWorld, renderInfo.contraption, layer)); SuperByteBuffer contraptionBuffer = CreateClient.BUFFER_CACHE.get(CONTRAPTION, Pair.of(renderInfo.contraption, layer), () -> buildStructureBuffer(renderInfo.renderWorld, renderInfo.contraption, layer));
if (!contraptionBuffer.isEmpty()) { if (!contraptionBuffer.isEmpty()) {
ContraptionMatrices matrices = renderInfo.getMatrices(); ContraptionMatrices matrices = renderInfo.getMatrices();
contraptionBuffer.transform(matrices.contraptionStack)
.light(matrices.entityMatrix) contraptionBuffer.transform(matrices.getModel())
.light(matrices.getWorld())
.hybridLight() .hybridLight()
.renderInto(matrices.entityStack, event.buffers.bufferSource() .renderInto(matrices.getViewProjection(), event.buffers.bufferSource()
.getBuffer(layer)); .getBuffer(layer));
} }

View file

@ -32,6 +32,6 @@ Technology that empowers the player.'''
[[dependencies.create]] [[dependencies.create]]
modId="flywheel" modId="flywheel"
mandatory=true mandatory=true
versionRange="[1.16-0.2,1.16-0.3)" versionRange="[1.16-0.2.3,1.16-0.3)"
ordering="AFTER" ordering="AFTER"
side="CLIENT" side="CLIENT"