Compare commits
43
Commits
b5ca9dbc8f
...
0.1.4-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37db94649f | ||
|
|
896f4cccc0 | ||
|
|
de3c554e63 | ||
|
|
8417427059 | ||
|
|
ba5a32fb34 | ||
|
|
8b1c94151a | ||
|
|
3623024cce | ||
|
|
2dcbe0302e | ||
|
|
502bddd720 | ||
|
|
0c22fee8c0 | ||
|
|
f69f6eb883 | ||
|
|
cebd9aec96 | ||
|
|
2554a84950 | ||
|
|
49bbdff418 | ||
|
|
8995de9067 | ||
|
|
18b74212ca | ||
|
|
06190d0ef9 | ||
|
|
254a37d757 | ||
|
|
2b5bcf4098 | ||
|
|
4e00934353 | ||
|
|
c524451704 | ||
|
|
e26ce6d076 | ||
|
|
da3dfaee66 | ||
|
|
463335856f | ||
|
|
3315926f7c | ||
|
|
68d3fb6062 | ||
|
|
b108cc1f4a | ||
|
|
167010380e | ||
|
|
dd91f3844a | ||
|
|
b7cf3092da | ||
|
|
60afc12893 | ||
|
|
8677837a8f | ||
|
|
02f6cf7d71 | ||
|
|
85a46c0bba | ||
|
|
30f65bd914 | ||
|
|
bedb45c8aa | ||
|
|
1602520624 | ||
|
|
f69c379ae1 | ||
|
|
fbd0e23fb3 | ||
|
|
8dbb1d84c1 | ||
|
|
ab70c67a4f | ||
|
|
0a6aa90e23 | ||
|
|
22a7152f0d |
+2
-2
@@ -36,9 +36,9 @@ import net.minecraft.client.render.RenderSetup;
|
|||||||
|
|
||||||
Stonecutter rewrites inactive code into its own layout when switching versions (a single inactive line becomes `//code`), so write it that way to begin with, or run a switch and a reset before committing.
|
Stonecutter rewrites inactive code into its own layout when switching versions (a single inactive line becomes `//code`), so write it that way to begin with, or run a switch and a reset before committing.
|
||||||
|
|
||||||
When a newer version leaves something out that an older one implements, keep the older version's code behind a condition and mark the newer branch with a `TODO`, rather than dropping it for every version. `NametagsMixin` and the panorama in `SaturnScreenFabric.render` are examples. Search for `TODO` to find what newer versions still lack (tracked in #30), and remove the condition once a version gap is fixed.
|
When a newer version leaves something out that an older one implements, keep the older version's code behind a condition and mark the newer branch with a `TODO`, rather than dropping it for every version. The panorama in `SaturnScreenFabric.render` is an example. Search for `TODO` to find what newer versions still lack (tracked in #30), and remove the condition once a version gap is fixed.
|
||||||
|
|
||||||
A whole class that only exists for some versions (such as `NametagsMixin`, which hooks a method that 1.21.9 changed) can keep an empty class body on the other versions. An empty mixin changes nothing, and it keeps the mixin configs the same for every version, since JSON can't hold version comments.
|
A whole class that only exists for some versions (such as `MatrixStackMixin`, which only makes sense before 1.21.6's GUI rewrite) can keep an empty class body on the other versions. Before splitting a class like that, look for a hook that works on every version: nametags used to hook label rendering, which 1.21.9 changed, and now replace the name while the render state is built (`LivingEntityRendererMixin`), with no version condition at all. An empty mixin changes nothing, and it keeps the mixin configs the same for every version, since JSON can't hold version comments.
|
||||||
|
|
||||||
Inactive code sits inside a `/* … */` comment, so it can't contain `/* … */` comments itself: use `//` comments in code that's only active for some versions. For the same reason, don't nest multi-line version blocks; use `//?} else if <condition> {` for more than two version ranges, as in `PlayerEntityRendererMixin` and `SaturnRenderState`.
|
Inactive code sits inside a `/* … */` comment, so it can't contain `/* … */` comments itself: use `//` comments in code that's only active for some versions. For the same reason, don't nest multi-line version blocks; use `//?} else if <condition> {` for more than two version ranges, as in `PlayerEntityRendererMixin` and `SaturnRenderState`.
|
||||||
|
|
||||||
|
|||||||
+1
-1
Submodule common updated: 15fa973553...b0d75af8c4
+2
-2
@@ -6,10 +6,10 @@ org.gradle.caching=true
|
|||||||
# IntelliJ IDEA is not yet fully compatible with configuration cache, see: https://github.com/FabricMC/fabric-loom/issues/1349
|
# IntelliJ IDEA is not yet fully compatible with configuration cache, see: https://github.com/FabricMC/fabric-loom/issues/1349
|
||||||
org.gradle.configuration-cache=false
|
org.gradle.configuration-cache=false
|
||||||
|
|
||||||
mod_version=0.1.1-beta
|
mod_version=0.1.4-beta
|
||||||
|
|
||||||
# Saturn's emote library (git.selimaj.dev/saturnclientmc/emotes)
|
# Saturn's emote library (git.selimaj.dev/saturnclientmc/emotes)
|
||||||
emotes_version=0.1.0
|
emotes_version=0.1.1
|
||||||
|
|
||||||
# Loom, for every Minecraft version (read in settings.gradle.kts).
|
# Loom, for every Minecraft version (read in settings.gradle.kts).
|
||||||
loom_version=1.17.21
|
loom_version=1.17.21
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 591 KiB |
@@ -1,18 +1,28 @@
|
|||||||
package org.saturnclient.impl.cosmetics;
|
package org.saturnclient.impl.cosmetics;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
import org.joml.Matrix3f;
|
import org.joml.Matrix3f;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
|
import org.joml.Quaternionf;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
import org.joml.Vector4f;
|
import org.joml.Vector4f;
|
||||||
import org.saturnclient.client.player.SaturnPlayer;
|
import org.saturnclient.client.player.SaturnPlayer;
|
||||||
import org.saturnclient.common.ref.asset.IdentifierRef;
|
import org.saturnclient.common.ref.asset.IdentifierRef;
|
||||||
import org.saturnclient.config.Config;
|
import org.saturnclient.config.Config;
|
||||||
import org.saturnclient.cosmetics.Cloaks;
|
import org.saturnclient.cosmetics.Cloaks;
|
||||||
|
import org.saturnclient.emotes.core.Animation;
|
||||||
|
import org.saturnclient.emotes.core.Bone;
|
||||||
|
import org.saturnclient.emotes.core.bend.LimbBend;
|
||||||
|
import org.saturnclient.emotes.core.bend.Vec3;
|
||||||
|
import org.saturnclient.emotes.fabric.access.BendablePart;
|
||||||
import org.saturnclient.impl.cosmetics.utils.ShaderUtils;
|
import org.saturnclient.impl.cosmetics.utils.ShaderUtils;
|
||||||
import org.saturnclient.saturnclient.SaturnRenderState;
|
import org.saturnclient.saturnclient.SaturnRenderState;
|
||||||
|
|
||||||
|
import net.minecraft.client.model.ModelPart;
|
||||||
import net.minecraft.client.render.OverlayTexture;
|
import net.minecraft.client.render.OverlayTexture;
|
||||||
import net.minecraft.client.render.VertexConsumer;
|
import net.minecraft.client.render.VertexConsumer;
|
||||||
//? if >=1.21.9 {
|
//? if >=1.21.9 {
|
||||||
@@ -33,7 +43,6 @@ import net.minecraft.item.ItemStack;
|
|||||||
import net.minecraft.item.equipment.EquipmentAsset;
|
import net.minecraft.item.equipment.EquipmentAsset;
|
||||||
import net.minecraft.registry.RegistryKey;
|
import net.minecraft.registry.RegistryKey;
|
||||||
import net.minecraft.util.math.MathHelper;
|
import net.minecraft.util.math.MathHelper;
|
||||||
import net.minecraft.util.math.RotationAxis;
|
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -383,6 +392,50 @@ public class CloakFeatureRenderer extends FeatureRenderer<PlayerEntityRenderStat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A torso bend past this curves the back more than a flat cloak can follow. */
|
||||||
|
private static final float MAX_CLOAK_BEND = (float) Math.toRadians(10);
|
||||||
|
/** Whether each animation bends the torso past that at any point. */
|
||||||
|
private static final Map<Animation, Boolean> BENDS_TORSO = Collections.synchronizedMap(new WeakHashMap<>());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the player is playing an emote that curves the torso (a twerk, the griddy). The cloak
|
||||||
|
* would cut through the arched back, so it's hidden for the whole emote rather than only while
|
||||||
|
* the back is curved, which would flicker.
|
||||||
|
*/
|
||||||
|
private static boolean playingTorsoBend(SaturnPlayer player) {
|
||||||
|
return org.saturnclient.emotes.fabric.Emotes.playing(player.uuid)
|
||||||
|
.map(playing -> BENDS_TORSO.computeIfAbsent(playing.animation(), CloakFeatureRenderer::bendsTorso))
|
||||||
|
.orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean bendsTorso(Animation animation) {
|
||||||
|
for (float tick = 0; tick <= animation.length(); tick += 0.5f) {
|
||||||
|
if (animation.sample(tick).get(Bone.BODY).filter(body -> Math.abs(body.bend()) > MAX_CLOAK_BEND).isPresent()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An emote that bends the torso a little leans the chest from the hips, so the cloak hangs from
|
||||||
|
* where the top of the back ends up, turned as much as the torso is there.
|
||||||
|
*/
|
||||||
|
private static void followBend(MatrixStack matrices, ModelPart body) {
|
||||||
|
LimbBend bend = ((BendablePart) (Object) body).saturnEmotes$getBend();
|
||||||
|
if (bend == null || bend.isStraight()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The top of the torso, in the body's own pixels
|
||||||
|
float[] top = new float[3];
|
||||||
|
bend.bendPoint(0, 0, 0, top);
|
||||||
|
matrices.translate(top[0] / 16.0f, top[1] / 16.0f, top[2] / 16.0f);
|
||||||
|
|
||||||
|
Vec3 axis = bend.axis();
|
||||||
|
matrices.multiply(new Quaternionf().rotationAxis(bend.angleAt(0), axis.x(), axis.y(), axis.z()));
|
||||||
|
}
|
||||||
|
|
||||||
// Shift array left and insert new value at the start
|
// Shift array left and insert new value at the start
|
||||||
private void updateSegmentValues(float value) {
|
private void updateSegmentValues(float value) {
|
||||||
// Shift all elements one position to the right (from end to start)
|
// Shift all elements one position to the right (from end to start)
|
||||||
@@ -416,7 +469,7 @@ public class CloakFeatureRenderer extends FeatureRenderer<PlayerEntityRenderStat
|
|||||||
//?} else
|
//?} else
|
||||||
//SaturnPlayer player = SaturnPlayer.get(state.name);
|
//SaturnPlayer player = SaturnPlayer.get(state.name);
|
||||||
|
|
||||||
if (player == null) {
|
if (player == null || playingTorsoBend(player)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,17 +489,24 @@ public class CloakFeatureRenderer extends FeatureRenderer<PlayerEntityRenderStat
|
|||||||
|
|
||||||
matrices.push();
|
matrices.push();
|
||||||
|
|
||||||
|
// On the back: the whole player (which emotes like the backflip turn), then the body's position
|
||||||
|
// and turn (which crouching and emotes move), then the chest's lean when an emote bends the torso
|
||||||
|
PlayerEntityModel model = this.getContextModel();
|
||||||
|
//? if >=1.21.5 {
|
||||||
|
model.getRootPart().applyTransform(matrices);
|
||||||
|
model.body.applyTransform(matrices);
|
||||||
|
//?} else {
|
||||||
|
/*model.getRootPart().rotate(matrices);
|
||||||
|
model.body.rotate(matrices);
|
||||||
|
*///?}
|
||||||
|
followBend(matrices, model.body);
|
||||||
|
|
||||||
matrices.translate(0.0f, 0.0f, 0.12f);
|
matrices.translate(0.0f, 0.0f, 0.12f);
|
||||||
|
|
||||||
if (this.hasCustomModelForLayer(state.equippedChestStack, LayerType.HUMANOID)) {
|
if (this.hasCustomModelForLayer(state.equippedChestStack, LayerType.HUMANOID)) {
|
||||||
matrices.translate(0.0F, -0.053125F, 0.06875F);
|
matrices.translate(0.0F, -0.053125F, 0.06875F);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.isInSneakingPose) {
|
|
||||||
matrices.translate(0, 0.16f, 0.0f);
|
|
||||||
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(29.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
if (now - lastUpdate >= 20) {
|
if (now - lastUpdate >= 20) {
|
||||||
float velX = Math.min(0.8f, state.field_53537 / 108.0f);
|
float velX = Math.min(0.8f, state.field_53537 / 108.0f);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.saturnclient.impl.cosmetics;
|
package org.saturnclient.impl.cosmetics;
|
||||||
|
|
||||||
import org.joml.Quaternionf;
|
|
||||||
import org.saturnclient.client.player.SaturnPlayer;
|
import org.saturnclient.client.player.SaturnPlayer;
|
||||||
import org.saturnclient.impl.cosmetics.obj.MtlLoader;
|
import org.saturnclient.impl.cosmetics.obj.MtlLoader;
|
||||||
import org.saturnclient.impl.cosmetics.obj.ObjModel;
|
import org.saturnclient.impl.cosmetics.obj.ObjModel;
|
||||||
@@ -16,7 +15,6 @@ import net.minecraft.client.render.entity.feature.FeatureRendererContext;
|
|||||||
import net.minecraft.client.render.entity.model.PlayerEntityModel;
|
import net.minecraft.client.render.entity.model.PlayerEntityModel;
|
||||||
import net.minecraft.client.render.entity.state.PlayerEntityRenderState;
|
import net.minecraft.client.render.entity.state.PlayerEntityRenderState;
|
||||||
import net.minecraft.client.util.math.MatrixStack;
|
import net.minecraft.client.util.math.MatrixStack;
|
||||||
import net.minecraft.client.model.ModelPart;
|
|
||||||
|
|
||||||
public class HatFeatureRenderer extends FeatureRenderer<PlayerEntityRenderState, PlayerEntityModel> {
|
public class HatFeatureRenderer extends FeatureRenderer<PlayerEntityRenderState, PlayerEntityModel> {
|
||||||
public HatFeatureRenderer(FeatureRendererContext<PlayerEntityRenderState, PlayerEntityModel> context) {
|
public HatFeatureRenderer(FeatureRendererContext<PlayerEntityRenderState, PlayerEntityModel> context) {
|
||||||
@@ -41,11 +39,16 @@ public class HatFeatureRenderer extends FeatureRenderer<PlayerEntityRenderState,
|
|||||||
|
|
||||||
matrices.push();
|
matrices.push();
|
||||||
|
|
||||||
|
// On the head, as vanilla places head items: the whole player (which emotes like the backflip
|
||||||
|
// turn), then the head's position, turn and scale (which crouching and emotes move)
|
||||||
|
PlayerEntityModel model = this.getContextModel();
|
||||||
//? if >=1.21.5 {
|
//? if >=1.21.5 {
|
||||||
ModelPart head = this.getContextModel().head;
|
model.getRootPart().applyTransform(matrices);
|
||||||
matrices.multiply(new Quaternionf().rotationZYX(head.roll, head.yaw, head.pitch));
|
model.head.applyTransform(matrices);
|
||||||
//?} else
|
//?} else {
|
||||||
//this.getContextModel().head.rotate(matrices);
|
/*model.getRootPart().rotate(matrices);
|
||||||
|
model.head.rotate(matrices);
|
||||||
|
*///?}
|
||||||
|
|
||||||
ObjModel.cosmetic("hat", player.hat).render(MtlLoader.cosmetic(player.hat),
|
ObjModel.cosmetic("hat", player.hat).render(MtlLoader.cosmetic(player.hat),
|
||||||
matrices, output, light,
|
matrices, output, light,
|
||||||
|
|||||||
@@ -12,10 +12,9 @@ import net.minecraft.entity.LivingEntity;
|
|||||||
* Fabric implementation of {@link EntityFeature}.
|
* Fabric implementation of {@link EntityFeature}.
|
||||||
*
|
*
|
||||||
* Crosshair targeting is resolved via {@link MinecraftClient#crosshairTarget}.
|
* Crosshair targeting is resolved via {@link MinecraftClient#crosshairTarget}.
|
||||||
* Entity state for nametag rendering is supplied at render time by
|
* Entity state for nametags is built by LivingEntityRendererMixin, which
|
||||||
* {@link org.saturnclient.modules.mixins.LivingEntityRendererMixin} which
|
* writes health into the render state and then constructs a
|
||||||
* writes into the render state; {@link NametagsMixin} then constructs
|
* {@link RenderStateEntityState} from that snapshot for NametagsMod.
|
||||||
* a {@link RenderStateEntityState} from the already-extracted snapshot.
|
|
||||||
*/
|
*/
|
||||||
public class EntityFeatureImpl implements EntityFeature {
|
public class EntityFeatureImpl implements EntityFeature {
|
||||||
|
|
||||||
@@ -43,7 +42,7 @@ public class EntityFeatureImpl implements EntityFeature {
|
|||||||
* Minecraft render-state object) as a platform-neutral
|
* Minecraft render-state object) as a platform-neutral
|
||||||
* {@link EntityState} for use by {@link NametagsFeature}.
|
* {@link EntityState} for use by {@link NametagsFeature}.
|
||||||
*
|
*
|
||||||
* This is constructed inside {@link NametagsMixin} where both the
|
* This is constructed in LivingEntityRendererMixin, where both the
|
||||||
* render-state mixin interface and the entity name are available.
|
* render-state mixin interface and the entity name are available.
|
||||||
*/
|
*/
|
||||||
public static final class RenderStateEntityState implements EntityState {
|
public static final class RenderStateEntityState implements EntityState {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import org.saturnclient.common.feature.EntityFeature;
|
|||||||
* {@link net.minecraft.client.render.entity.state.LivingEntityRenderState}.
|
* {@link net.minecraft.client.render.entity.state.LivingEntityRenderState}.
|
||||||
*
|
*
|
||||||
* Carries the entity's health snapshot and classification into the render
|
* Carries the entity's health snapshot and classification into the render
|
||||||
* pipeline so that {@link NametagsMixin} can read them without touching
|
* render state, so NametagsMod's replacement nametag (applied in
|
||||||
* the live entity again.
|
* LivingEntityRendererMixin) can use them without touching the live entity again.
|
||||||
*/
|
*/
|
||||||
public interface HealthRenderState {
|
public interface HealthRenderState {
|
||||||
|
|
||||||
|
|||||||
@@ -1,101 +0,0 @@
|
|||||||
package org.saturnclient.impl.features.mixins.misc;
|
|
||||||
|
|
||||||
import net.minecraft.client.render.entity.EntityRenderer;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
//? if <1.21.9 {
|
|
||||||
/*import net.minecraft.client.render.VertexConsumerProvider;
|
|
||||||
import net.minecraft.client.render.entity.state.EntityRenderState;
|
|
||||||
import net.minecraft.client.render.entity.state.LivingEntityRenderState;
|
|
||||||
import net.minecraft.client.util.math.MatrixStack;
|
|
||||||
import net.minecraft.text.Text;
|
|
||||||
|
|
||||||
import org.saturnclient.impl.features.entity.EntityFeatureImpl;
|
|
||||||
import org.saturnclient.impl.features.entity.HealthRenderState;
|
|
||||||
import org.saturnclient.impl.features.mixins.render.LivingEntityRenderStateMixin;
|
|
||||||
import org.saturnclient.mod.mods.NametagsMod;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
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;
|
|
||||||
*///?}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Intercepts nametag rendering and replaces the displayed text with
|
|
||||||
* the string produced by {@link NametagsMod#getNametagString}.
|
|
||||||
*
|
|
||||||
* Key changes from the original:
|
|
||||||
*
|
|
||||||
* <ul>
|
|
||||||
* <li>The old {@code NametagsFabric.EntityStateImpl} bridge class is
|
|
||||||
* replaced by {@link EntityFeatureImpl.RenderStateEntityState},
|
|
||||||
* which wraps the {@link HealthRenderState} already injected into
|
|
||||||
* the render state by {@link LivingEntityRenderStateMixin}.</li>
|
|
||||||
* <li>The feature import changes from {@code Nametags} to
|
|
||||||
* {@link NametagsMod}; the two static methods
|
|
||||||
* ({@code shouldReplaceName} / {@code getNametagString}) are
|
|
||||||
* unchanged in signature.</li>
|
|
||||||
* <li>The entity's display name is extracted from the render state's
|
|
||||||
* {@code nameVisible} / text fields where available, falling back
|
|
||||||
* to the plain text string passed to this method.</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* The re-entrancy guard ({@code saturn$rendering}) is kept intact to
|
|
||||||
* prevent infinite recursion when we call {@code renderLabelIfPresent}
|
|
||||||
* with the replacement text.
|
|
||||||
*/
|
|
||||||
@Mixin(EntityRenderer.class)
|
|
||||||
public abstract class NametagsMixin {
|
|
||||||
// TODO: 1.21.9 moved label rendering to the render queue (renderLabelIfPresent no longer takes a
|
|
||||||
// VertexConsumerProvider), so custom nametags only work on older versions. On 1.21.9+ this mixin is
|
|
||||||
// empty but stays registered, so features.mixins.json is the same for every version.
|
|
||||||
//? if <1.21.9 {
|
|
||||||
/*// Guards against the recursive call we make with the replacement text.
|
|
||||||
@Unique
|
|
||||||
private static final ThreadLocal<Boolean> saturn$rendering = ThreadLocal.withInitial(() -> false);
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
protected abstract <S extends EntityRenderState> void renderLabelIfPresent(
|
|
||||||
S state, Text text,
|
|
||||||
MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light);
|
|
||||||
|
|
||||||
@Inject(method = "renderLabelIfPresent", at = @At("HEAD"), cancellable = true)
|
|
||||||
private <S extends EntityRenderState> void saturn$replaceNametag(
|
|
||||||
S state, Text text,
|
|
||||||
MatrixStack matrices, VertexConsumerProvider vertexConsumers,
|
|
||||||
int light, CallbackInfo ci) {
|
|
||||||
|
|
||||||
// Skip our own recursive call
|
|
||||||
if (saturn$rendering.get())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!NametagsMod.shouldReplaceName())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Only living entities carry health data
|
|
||||||
if (!(state instanceof LivingEntityRenderState living))
|
|
||||||
return;
|
|
||||||
if (!(living instanceof HealthRenderState hrs))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Build the platform-neutral EntityState from the render-state snapshot
|
|
||||||
// (health/type were written by LivingEntityRendererMixin at extract time).
|
|
||||||
String customName = text != null ? text.getString() : null;
|
|
||||||
EntityFeatureImpl.RenderStateEntityState entityState = new EntityFeatureImpl.RenderStateEntityState(
|
|
||||||
customName, hrs);
|
|
||||||
|
|
||||||
// Ask the feature for the replacement string
|
|
||||||
String replacement = NametagsMod.getNametagString(entityState);
|
|
||||||
if (replacement == null)
|
|
||||||
return; // feature decided not to replace this entity
|
|
||||||
|
|
||||||
ci.cancel();
|
|
||||||
saturn$rendering.set(true);
|
|
||||||
try {
|
|
||||||
renderLabelIfPresent(state, Text.literal(replacement), matrices, vertexConsumers, light);
|
|
||||||
} finally {
|
|
||||||
saturn$rendering.set(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*///?}
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,20 @@
|
|||||||
package org.saturnclient.impl.features.mixins.render;
|
package org.saturnclient.impl.features.mixins.render;
|
||||||
|
|
||||||
|
//? if >=1.21.6
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
//? if >=1.21.6
|
||||||
|
import net.minecraft.client.option.GameOptions;
|
||||||
import net.minecraft.client.render.GameRenderer;
|
import net.minecraft.client.render.GameRenderer;
|
||||||
|
|
||||||
|
//? if >=1.21.6
|
||||||
|
import org.saturnclient.impl.ui.SaturnScreenFabric;
|
||||||
import org.saturnclient.mod.mods.FreelookMod;
|
import org.saturnclient.mod.mods.FreelookMod;
|
||||||
import org.saturnclient.mod.mods.ZoomMod;
|
import org.saturnclient.mod.mods.ZoomMod;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
//? if >=1.21.6
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,4 +46,18 @@ public class GameRendererMixin {
|
|||||||
cir.setReturnValue(zoomedFov);
|
cir.setReturnValue(zoomedFov);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//? if >=1.21.6 {
|
||||||
|
// From 1.21.6 the menu blur's strength is the vanilla setting, read here every frame. While a
|
||||||
|
// Saturn screen is open, it's Saturn's background blur instead, faded in with the screen.
|
||||||
|
@Redirect(method = "render", at = @At(value = "INVOKE",
|
||||||
|
target = "Lnet/minecraft/client/option/GameOptions;getMenuBackgroundBlurrinessValue()I"))
|
||||||
|
private int useSaturnBlur(GameOptions options) {
|
||||||
|
if (MinecraftClient.getInstance().currentScreen instanceof SaturnScreenFabric saturnScreen) {
|
||||||
|
return saturnScreen.screen.getBackgroundBlur();
|
||||||
|
}
|
||||||
|
|
||||||
|
return options.getMenuBackgroundBlurrinessValue();
|
||||||
|
}
|
||||||
|
//?}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -4,7 +4,6 @@ import net.minecraft.client.render.entity.state.LivingEntityRenderState;
|
|||||||
|
|
||||||
import org.saturnclient.common.feature.EntityFeature;
|
import org.saturnclient.common.feature.EntityFeature;
|
||||||
import org.saturnclient.impl.features.entity.HealthRenderState;
|
import org.saturnclient.impl.features.entity.HealthRenderState;
|
||||||
import org.saturnclient.impl.features.mixins.misc.NametagsMixin;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
||||||
@@ -19,8 +18,8 @@ import org.spongepowered.asm.mixin.Unique;
|
|||||||
*
|
*
|
||||||
* The stored values are written by
|
* The stored values are written by
|
||||||
* {@link LivingEntityRendererMixin#saturn$captureHealth} each time a
|
* {@link LivingEntityRendererMixin#saturn$captureHealth} each time a
|
||||||
* render state is extracted from a live entity, and read by
|
* render state is extracted from a live entity, and read there right away for
|
||||||
* {@link NametagsMixin} at render time.
|
* NametagsMod's replacement nametag.
|
||||||
*/
|
*/
|
||||||
@Mixin(LivingEntityRenderState.class)
|
@Mixin(LivingEntityRenderState.class)
|
||||||
public class LivingEntityRenderStateMixin implements HealthRenderState {
|
public class LivingEntityRenderStateMixin implements HealthRenderState {
|
||||||
|
|||||||
+19
-8
@@ -6,24 +6,25 @@ import net.minecraft.entity.LivingEntity;
|
|||||||
import net.minecraft.entity.mob.HostileEntity;
|
import net.minecraft.entity.mob.HostileEntity;
|
||||||
import net.minecraft.entity.passive.PassiveEntity;
|
import net.minecraft.entity.passive.PassiveEntity;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
|
||||||
import org.saturnclient.common.feature.EntityFeature;
|
import org.saturnclient.common.feature.EntityFeature;
|
||||||
|
import org.saturnclient.impl.features.entity.EntityFeatureImpl;
|
||||||
import org.saturnclient.impl.features.entity.HealthRenderState;
|
import org.saturnclient.impl.features.entity.HealthRenderState;
|
||||||
|
import org.saturnclient.mod.mods.NametagsMod;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Captures each living entity's health and type into the render state
|
* Captures each living entity's health and type into the render state during
|
||||||
* during {@code updateRenderState}, so that nametag rendering can read
|
* {@code updateRenderState}, and applies {@link NametagsMod}'s replacement nametag to the state's
|
||||||
* them without touching the live entity again.
|
* display name.
|
||||||
*
|
*
|
||||||
* Previously imported {@code NametagsInterface.EntityType}; now uses
|
* Replacing the name here, while the render state is built, works the same on every version. Hooking
|
||||||
* {@link EntityFeature.EntityType} — the canonical enum shared across
|
* label rendering instead doesn't: 1.21.9 moved it to the render command queue. Player role icons are
|
||||||
* the whole module system.
|
* added after this, in PlayerEntityRendererMixin.
|
||||||
*
|
|
||||||
* The injection target and strategy are unchanged from the original.
|
|
||||||
*/
|
*/
|
||||||
@Mixin(LivingEntityRenderer.class)
|
@Mixin(LivingEntityRenderer.class)
|
||||||
public abstract class LivingEntityRendererMixin<T extends LivingEntity, S extends LivingEntityRenderState> {
|
public abstract class LivingEntityRendererMixin<T extends LivingEntity, S extends LivingEntityRenderState> {
|
||||||
@@ -47,5 +48,15 @@ public abstract class LivingEntityRendererMixin<T extends LivingEntity, S extend
|
|||||||
type = EntityFeature.EntityType.OTHER;
|
type = EntityFeature.EntityType.OTHER;
|
||||||
|
|
||||||
hrs.saturn$setEntityType(type);
|
hrs.saturn$setEntityType(type);
|
||||||
|
|
||||||
|
// Only entities that already show a label get a replacement one, as when this hooked label
|
||||||
|
// rendering.
|
||||||
|
if (NametagsMod.shouldReplaceName() && state.displayName != null) {
|
||||||
|
String replacement = NametagsMod.getNametagString(
|
||||||
|
new EntityFeatureImpl.RenderStateEntityState(state.displayName.getString(), hrs));
|
||||||
|
if (replacement != null) {
|
||||||
|
state.displayName = Text.literal(replacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,17 @@ package org.saturnclient.impl.provider;
|
|||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.toast.SystemToast;
|
import net.minecraft.client.toast.SystemToast;
|
||||||
|
//? if >=1.21.9 {
|
||||||
|
import net.minecraft.entity.player.SkinTextures;
|
||||||
|
//?} else
|
||||||
|
//import net.minecraft.client.util.SkinTextures;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
import org.saturnclient.common.provider.SaturnProvider;
|
import org.saturnclient.common.provider.SaturnProvider;
|
||||||
import org.saturnclient.common.ref.asset.IdentifierRef;
|
import org.saturnclient.common.ref.asset.IdentifierRef;
|
||||||
@@ -16,6 +22,9 @@ import org.saturnclient.impl.cosmetics.utils.IdentifierUtils;
|
|||||||
import org.saturnclient.saturnclient.SaturnClient;
|
import org.saturnclient.saturnclient.SaturnClient;
|
||||||
|
|
||||||
public class SaturnProviderImpl implements SaturnProvider {
|
public class SaturnProviderImpl implements SaturnProvider {
|
||||||
|
/** Looks up the signed-in player's skin; made once, since the skin provider caches it */
|
||||||
|
private Supplier<Identifier> playerSkin;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MinecraftClientRef getClient() {
|
public MinecraftClientRef getClient() {
|
||||||
return (MinecraftClientRef) SaturnClient.client;
|
return (MinecraftClientRef) SaturnClient.client;
|
||||||
@@ -50,6 +59,23 @@ public class SaturnProviderImpl implements SaturnProvider {
|
|||||||
/** Shown longer than the vanilla default, since these ask the player to act. */
|
/** Shown longer than the vanilla default, since these ask the player to act. */
|
||||||
private static final SystemToast.Type WARNING_TOAST = new SystemToast.Type(15_000L);
|
private static final SystemToast.Type WARNING_TOAST = new SystemToast.Type(15_000L);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentifierRef getPlayerSkin() {
|
||||||
|
if (playerSkin == null) {
|
||||||
|
MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
// 1.21.9 moved SkinTextures and renamed the skin provider's lookup.
|
||||||
|
//? if >=1.21.9 {
|
||||||
|
Supplier<SkinTextures> textures = client.getSkinProvider().supplySkinTextures(client.getGameProfile(), false);
|
||||||
|
playerSkin = () -> textures.get().body().texturePath();
|
||||||
|
//?} else {
|
||||||
|
/*Supplier<SkinTextures> textures = client.getSkinProvider().getSkinTexturesSupplier(client.getGameProfile());
|
||||||
|
playerSkin = () -> textures.get().texture();
|
||||||
|
*///?}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (IdentifierRef) (Object) playerSkin.get();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getModVersion() {
|
public String getModVersion() {
|
||||||
return FabricLoader.getInstance().getModContainer(SaturnClient.MOD_ID)
|
return FabricLoader.getInstance().getModContainer(SaturnClient.MOD_ID)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import org.joml.Quaternionf;
|
import org.joml.Quaternionf;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
import org.saturnclient.saturnclient.SaturnClient;
|
import org.saturnclient.saturnclient.SaturnClient;
|
||||||
import org.saturnclient.ui.ElementContext;
|
|
||||||
import org.saturnclient.ui.RenderScope;
|
import org.saturnclient.ui.RenderScope;
|
||||||
import org.saturnclient.ui.components.SkinPreview.EntityDrawer;
|
import org.saturnclient.ui.components.SkinPreview.EntityDrawer;
|
||||||
|
|
||||||
@@ -21,33 +20,32 @@ import net.minecraft.entity.LivingEntity;
|
|||||||
|
|
||||||
public class EntityDrawerImpl implements EntityDrawer {
|
public class EntityDrawerImpl implements EntityDrawer {
|
||||||
@Override
|
@Override
|
||||||
public void render(RenderScope renderScope, boolean negativeAngle, float angle, float scale, ElementContext ctx) {
|
public boolean render(RenderScope renderScope, int width, int height, float yaw) {
|
||||||
LivingEntity entity = SaturnClient.client.player;
|
LivingEntity entity = SaturnClient.client.player;
|
||||||
|
|
||||||
if (entity != null) {
|
if (entity == null) {
|
||||||
drawEntity(renderScope, 0, 0, 75, 78, 30, 0.0625F, ctx.mouseX / scale, ctx.mouseY / scale,
|
return false;
|
||||||
SaturnClient.client.player);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The player is about 1.8 blocks tall; leave some room above and below.
|
||||||
|
drawEntity(renderScope, 0, 0, width, height, (int) (height / 2.2F), 0.0625F, yaw, entity);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draws the entity in the box, turned by yaw degrees (0 faces the viewer) with its head straight.
|
||||||
public static void drawEntity(RenderScope renderScope, int x1, int y1, int x2, int y2, int size, float scale,
|
public static void drawEntity(RenderScope renderScope, int x1, int y1, int x2, int y2, int size, float scale,
|
||||||
float mouseX, float mouseY, LivingEntity entity) {
|
float yaw, LivingEntity entity) {
|
||||||
float f = (float) (x1 + x2) / 2.0F;
|
|
||||||
float g = (float) (y1 + y2) / 2.0F;
|
|
||||||
renderScope.enableScissor(x1, y1, x2, y2);
|
renderScope.enableScissor(x1, y1, x2, y2);
|
||||||
float h = (float) Math.atan((double) ((f - mouseX) / 40.0F));
|
|
||||||
float i = (float) Math.atan((double) ((g - mouseY) / 40.0F));
|
|
||||||
Quaternionf quaternionf = (new Quaternionf()).rotateZ((float) Math.PI);
|
Quaternionf quaternionf = (new Quaternionf()).rotateZ((float) Math.PI);
|
||||||
Quaternionf quaternionf2 = (new Quaternionf()).rotateX(i * 20.0F * ((float) Math.PI / 180F));
|
Quaternionf quaternionf2 = new Quaternionf();
|
||||||
quaternionf.mul(quaternionf2);
|
|
||||||
float j = entity.bodyYaw;
|
float j = entity.bodyYaw;
|
||||||
float k = entity.getYaw();
|
float k = entity.getYaw();
|
||||||
float l = entity.getPitch();
|
float l = entity.getPitch();
|
||||||
float m = entity.lastHeadYaw;
|
float m = entity.lastHeadYaw;
|
||||||
float n = entity.headYaw;
|
float n = entity.headYaw;
|
||||||
entity.bodyYaw = 180.0F + h * 20.0F;
|
entity.bodyYaw = 180.0F + yaw;
|
||||||
entity.setYaw(180.0F + h * 40.0F);
|
entity.setYaw(180.0F + yaw);
|
||||||
entity.setPitch(-i * 20.0F);
|
entity.setPitch(0.0F);
|
||||||
entity.headYaw = entity.getYaw();
|
entity.headYaw = entity.getYaw();
|
||||||
entity.lastHeadYaw = entity.getYaw();
|
entity.lastHeadYaw = entity.getYaw();
|
||||||
float o = entity.getScale();
|
float o = entity.getScale();
|
||||||
@@ -83,7 +81,6 @@ public class EntityDrawerImpl implements EntityDrawer {
|
|||||||
import org.joml.Quaternionf;
|
import org.joml.Quaternionf;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
import org.saturnclient.saturnclient.SaturnClient;
|
import org.saturnclient.saturnclient.SaturnClient;
|
||||||
import org.saturnclient.ui.ElementContext;
|
|
||||||
import org.saturnclient.ui.RenderScope;
|
import org.saturnclient.ui.RenderScope;
|
||||||
import org.saturnclient.ui.components.SkinPreview.EntityDrawer;
|
import org.saturnclient.ui.components.SkinPreview.EntityDrawer;
|
||||||
|
|
||||||
@@ -92,14 +89,12 @@ import net.minecraft.client.render.entity.EntityRenderDispatcher;
|
|||||||
import net.minecraft.entity.LivingEntity;
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
|
||||||
public class EntityDrawerImpl implements EntityDrawer {
|
public class EntityDrawerImpl implements EntityDrawer {
|
||||||
|
// Draws the entity in the box, turned by yaw degrees (0 faces the viewer) with its head straight.
|
||||||
public static void drawEntity(RenderScope renderScope, int x1, int y1, int x2, int y2, int size, float f,
|
public static void drawEntity(RenderScope renderScope, int x1, int y1, int x2, int y2, int size, float f,
|
||||||
float mouseX,
|
float yaw, LivingEntity entity) {
|
||||||
float mouseY, LivingEntity entity, float angle, boolean negativeAngle) {
|
|
||||||
float g = (float) (x1 + x2) / 2.0F;
|
float g = (float) (x1 + x2) / 2.0F;
|
||||||
float h = (float) (y1 + y2) / 2.0F;
|
float h = (float) (y1 + y2) / 2.0F;
|
||||||
renderScope.enableScissor(0, 6, x2, y2);
|
renderScope.enableScissor(x1, y1, x2, y2);
|
||||||
float i = (float) Math.atan((double) ((g - mouseX) / 40.0F));
|
|
||||||
float j = (float) Math.atan((double) ((h - mouseY) / 40.0F));
|
|
||||||
Quaternionf quaternionf = (new Quaternionf()).rotateZ(3.1415927F);
|
Quaternionf quaternionf = (new Quaternionf()).rotateZ(3.1415927F);
|
||||||
float k = entity.bodyYaw;
|
float k = entity.bodyYaw;
|
||||||
float l = entity.getYaw();
|
float l = entity.getYaw();
|
||||||
@@ -109,10 +104,10 @@ public class EntityDrawerImpl implements EntityDrawer {
|
|||||||
//? if <1.21.5
|
//? if <1.21.5
|
||||||
//float n = entity.prevHeadYaw;
|
//float n = entity.prevHeadYaw;
|
||||||
float o = entity.headYaw;
|
float o = entity.headYaw;
|
||||||
entity.setYaw(angle);
|
entity.setYaw(180.0F + yaw);
|
||||||
entity.bodyYaw = angle;
|
entity.bodyYaw = 180.0F + yaw;
|
||||||
entity.setPitch(-(j * 20.0F));
|
entity.setPitch(0.0F);
|
||||||
entity.headYaw = negativeAngle ? (i * 40.0F) + 180f : -(i * 40.0F);
|
entity.headYaw = entity.getYaw();
|
||||||
//? if >=1.21.5
|
//? if >=1.21.5
|
||||||
entity.lastHeadYaw = entity.getHeadYaw();
|
entity.lastHeadYaw = entity.getHeadYaw();
|
||||||
//? if <1.21.5
|
//? if <1.21.5
|
||||||
@@ -166,12 +161,15 @@ public class EntityDrawerImpl implements EntityDrawer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(RenderScope renderScope, boolean negativeAngle, float angle, float scale, ElementContext ctx) {
|
public boolean render(RenderScope renderScope, int width, int height, float yaw) {
|
||||||
LivingEntity entity = SaturnClient.client.player;
|
LivingEntity entity = SaturnClient.client.player;
|
||||||
if (entity != null) {
|
if (entity == null) {
|
||||||
drawEntity(renderScope, 0, 0, 75, 78, 30, 0.0625F, ctx.mouseX / scale, ctx.mouseY / scale, entity, angle,
|
return false;
|
||||||
negativeAngle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The player is about 1.8 blocks tall; leave some room above and below.
|
||||||
|
drawEntity(renderScope, 0, 0, width, height, (int) (height / 2.2F), 0.0625F, yaw, entity);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*///?}
|
*///?}
|
||||||
|
|||||||
@@ -0,0 +1,380 @@
|
|||||||
|
package org.saturnclient.impl.ui;
|
||||||
|
|
||||||
|
// Glass: a copy of the game behind the screen, blurred, drawn through each panel's rounded rectangle
|
||||||
|
// texture as a mask. There's a copy for each blur strength in use (menus and widgets have their own),
|
||||||
|
// blurred with vanilla's blur at that radius, so it holds the world or panorama without the screen's
|
||||||
|
// own animated background blur.
|
||||||
|
//
|
||||||
|
// The GUI rewrites split this into three implementations:
|
||||||
|
// - 1.21.6+: the GUI is drawn after the screen builds it, so the copy is taken from the main
|
||||||
|
// framebuffer while the screen builds its GUI, and glass is a GUI element with its own pipeline
|
||||||
|
// (shaders/core/glass.fsh). The blur is vanilla's effect rebuilt with the radius set.
|
||||||
|
// - 1.21.5: the screen draws straight into the framebuffer, blurring it first, so the game is saved
|
||||||
|
// before the blur (saveBackground). Glass is a render layer with its own pipeline
|
||||||
|
// (shaders/core/glass_pre_1_21_6.fsh), and the blur takes the radius as it runs.
|
||||||
|
// - 1.21.4: as 1.21.5, with GL framebuffers copied by blitting, and a shader program
|
||||||
|
// (shaders/core/glass.json) instead of a pipeline.
|
||||||
|
//? if >=1.21.6 {
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import com.mojang.blaze3d.pipeline.RenderPipeline;
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
import com.mojang.blaze3d.textures.FilterMode;
|
||||||
|
import com.mojang.blaze3d.textures.GpuTextureView;
|
||||||
|
import com.mojang.serialization.JsonOps;
|
||||||
|
|
||||||
|
import org.saturnclient.config.Config;
|
||||||
|
import org.saturnclient.saturnclient.SaturnClient;
|
||||||
|
|
||||||
|
import net.minecraft.client.gl.Framebuffer;
|
||||||
|
import net.minecraft.client.gl.PostEffectPipeline;
|
||||||
|
import net.minecraft.client.gl.PostEffectProcessor;
|
||||||
|
import net.minecraft.client.gl.RenderPipelines;
|
||||||
|
import net.minecraft.client.gl.SimpleFramebuffer;
|
||||||
|
import net.minecraft.client.render.DefaultFramebufferSet;
|
||||||
|
import net.minecraft.client.render.ProjectionMatrix2;
|
||||||
|
import net.minecraft.client.texture.AbstractTexture;
|
||||||
|
import net.minecraft.client.texture.TextureSetup;
|
||||||
|
import net.minecraft.client.util.Pool;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
|
public final class GlassRenderer {
|
||||||
|
public static final RenderPipeline PIPELINE = RenderPipeline.builder(RenderPipelines.POSITION_TEX_COLOR_SNIPPET)
|
||||||
|
.withLocation(Identifier.of("saturnclient", "pipeline/glass"))
|
||||||
|
.withFragmentShader(Identifier.of("saturnclient", "core/glass"))
|
||||||
|
.withSampler("Sampler1")
|
||||||
|
// The transforms block ends with LineWidth before 1.21.11 (see glass.fsh).
|
||||||
|
//? if <1.21.11
|
||||||
|
//.withShaderDefine("HAS_LINE_WIDTH")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private static final Identifier BLUR = Identifier.ofVanilla("post_effect/blur.json");
|
||||||
|
private static final ProjectionMatrix2 PROJECTION = new ProjectionMatrix2("saturn glass", 0.1f, 1000f, false);
|
||||||
|
// For the blurs' intermediate targets, freed once unused for a few frames
|
||||||
|
private static final Pool POOL = new Pool(3);
|
||||||
|
|
||||||
|
// By blur strength. A blur that failed to load is kept as null, so it isn't retried every frame.
|
||||||
|
private static final Map<Integer, PostEffectProcessor> BLURS = new HashMap<>();
|
||||||
|
private static final Map<Integer, SimpleFramebuffer> BACKDROPS = new HashMap<>();
|
||||||
|
|
||||||
|
private GlassRenderer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copies the main framebuffer and blurs the copy by blur. Call once a frame for each blur, while
|
||||||
|
// the screen builds its GUI, before drawing glass with it.
|
||||||
|
public static void captureBackdrop(int blur) {
|
||||||
|
Framebuffer main = SaturnClient.client.getFramebuffer();
|
||||||
|
int width = main.textureWidth;
|
||||||
|
int height = main.textureHeight;
|
||||||
|
|
||||||
|
SimpleFramebuffer backdrop = BACKDROPS.get(blur);
|
||||||
|
if (backdrop == null) {
|
||||||
|
backdrop = new SimpleFramebuffer("Saturn glass " + blur, width, height, false);
|
||||||
|
BACKDROPS.put(blur, backdrop);
|
||||||
|
} else if (backdrop.textureWidth != width || backdrop.textureHeight != height) {
|
||||||
|
backdrop.resize(width, height);
|
||||||
|
}
|
||||||
|
//? if <1.21.11
|
||||||
|
//backdrop.getColorAttachment().setTextureFilter(FilterMode.LINEAR, false);
|
||||||
|
|
||||||
|
RenderSystem.getDevice().createCommandEncoder().copyTextureToTexture(
|
||||||
|
main.getColorAttachment(), backdrop.getColorAttachment(), 0, 0, 0, 0, 0, width, height);
|
||||||
|
|
||||||
|
PostEffectProcessor processor = getBlur(blur);
|
||||||
|
if (processor != null) {
|
||||||
|
processor.render(backdrop, POOL);
|
||||||
|
}
|
||||||
|
POOL.decrementLifespan();
|
||||||
|
release(blur);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The mask, then the copy blurred by blur, as the glass pipeline samples them
|
||||||
|
public static TextureSetup textures(AbstractTexture mask, int blur) {
|
||||||
|
GpuTextureView backdrop = BACKDROPS.get(blur).getColorAttachmentView();
|
||||||
|
//? if >=1.21.11
|
||||||
|
return TextureSetup.of(mask.getGlTextureView(), mask.getSampler(), backdrop, RenderSystem.getSamplerCache().get(FilterMode.LINEAR));
|
||||||
|
//? if <1.21.11
|
||||||
|
//return TextureSetup.of(mask.getGlTextureView(), backdrop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frees the copies and blurs of strengths that are no longer set, besides kept
|
||||||
|
private static void release(int kept) {
|
||||||
|
BACKDROPS.entrySet().removeIf(entry -> {
|
||||||
|
boolean unused = entry.getKey() != kept && !isSet(entry.getKey());
|
||||||
|
if (unused) {
|
||||||
|
entry.getValue().delete();
|
||||||
|
}
|
||||||
|
return unused;
|
||||||
|
});
|
||||||
|
BLURS.entrySet().removeIf(entry -> {
|
||||||
|
boolean unused = entry.getKey() != kept && !isSet(entry.getKey());
|
||||||
|
if (unused && entry.getValue() != null) {
|
||||||
|
entry.getValue().close();
|
||||||
|
}
|
||||||
|
return unused;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isSet(int blur) {
|
||||||
|
return blur == Config.menuBlur.value || blur == Config.widgetBlur.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PostEffectProcessor getBlur(int blur) {
|
||||||
|
if (!BLURS.containsKey(blur)) {
|
||||||
|
BLURS.put(blur, loadBlur(blur));
|
||||||
|
}
|
||||||
|
return BLURS.get(blur);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vanilla's blur, with its radius set to blur. Vanilla's own leaves it at 0, which makes the shader
|
||||||
|
// read the menu blur setting (Saturn's animated background blur while a Saturn screen is open).
|
||||||
|
private static PostEffectProcessor loadBlur(int blur) {
|
||||||
|
try (Reader reader = SaturnClient.client.getResourceManager().getResourceOrThrow(BLUR).getReader()) {
|
||||||
|
JsonElement json = JsonParser.parseReader(reader);
|
||||||
|
setRadius(json, blur);
|
||||||
|
|
||||||
|
PostEffectPipeline pipeline = PostEffectPipeline.CODEC.parse(JsonOps.INSTANCE, json).getOrThrow();
|
||||||
|
return PostEffectProcessor.parseEffect(pipeline, SaturnClient.client.getTextureManager(),
|
||||||
|
DefaultFramebufferSet.MAIN_ONLY, Identifier.of("saturnclient", "glass_blur_" + blur), PROJECTION);
|
||||||
|
} catch (Exception e) {
|
||||||
|
SaturnClient.LOGGER.error("Couldn't load the glass blur", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets every "Radius" uniform in the effect
|
||||||
|
private static void setRadius(JsonElement json, int radius) {
|
||||||
|
if (json.isJsonArray()) {
|
||||||
|
json.getAsJsonArray().forEach(element -> setRadius(element, radius));
|
||||||
|
} else if (json.isJsonObject()) {
|
||||||
|
JsonObject object = json.getAsJsonObject();
|
||||||
|
if (object.has("name") && object.get("name").getAsString().equals("Radius")) {
|
||||||
|
object.addProperty("value", (float) radius);
|
||||||
|
}
|
||||||
|
object.entrySet().forEach(entry -> setRadius(entry.getValue(), radius));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//?} else if >=1.21.5 {
|
||||||
|
/*import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.pipeline.RenderPipeline;
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
import com.mojang.blaze3d.textures.FilterMode;
|
||||||
|
|
||||||
|
import org.saturnclient.config.Config;
|
||||||
|
import org.saturnclient.saturnclient.SaturnClient;
|
||||||
|
|
||||||
|
import net.minecraft.client.gl.Framebuffer;
|
||||||
|
import net.minecraft.client.gl.PostEffectProcessor;
|
||||||
|
import net.minecraft.client.gl.RenderPipelines;
|
||||||
|
import net.minecraft.client.gl.SimpleFramebuffer;
|
||||||
|
import net.minecraft.client.render.DefaultFramebufferSet;
|
||||||
|
import net.minecraft.client.render.RenderLayer;
|
||||||
|
import net.minecraft.client.render.RenderPhase;
|
||||||
|
import net.minecraft.client.util.Pool;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
|
public final class GlassRenderer {
|
||||||
|
private static final RenderPipeline PIPELINE = RenderPipeline.builder(RenderPipelines.POSITION_TEX_COLOR_SNIPPET)
|
||||||
|
.withLocation(Identifier.of("saturnclient", "pipeline/glass"))
|
||||||
|
.withFragmentShader(Identifier.of("saturnclient", "core/glass_pre_1_21_6"))
|
||||||
|
.withSampler("Sampler1")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private static final Identifier BLUR = Identifier.ofVanilla("blur");
|
||||||
|
// For the blur's intermediate target, freed once unused for a few frames
|
||||||
|
private static final Pool POOL = new Pool(3);
|
||||||
|
|
||||||
|
private static final Map<Integer, SimpleFramebuffer> BACKDROPS = new HashMap<>();
|
||||||
|
// By mask and blur
|
||||||
|
private static final Map<String, RenderLayer> LAYERS = new HashMap<>();
|
||||||
|
// The game behind the screen before the screen blurs it, which glass is blurred from
|
||||||
|
private static SimpleFramebuffer background;
|
||||||
|
|
||||||
|
private GlassRenderer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Saves the main framebuffer for glass. Call each frame before the screen blurs its background.
|
||||||
|
public static void saveBackground() {
|
||||||
|
background = copy(SaturnClient.client.getFramebuffer(), background, "Saturn glass background");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copies the saved background and blurs the copy by blur. Call once a frame for each blur, before
|
||||||
|
// drawing glass with it.
|
||||||
|
public static void captureBackdrop(int blur) {
|
||||||
|
Framebuffer from = background != null ? background : SaturnClient.client.getFramebuffer();
|
||||||
|
SimpleFramebuffer backdrop = copy(from, BACKDROPS.get(blur), "Saturn glass " + blur);
|
||||||
|
BACKDROPS.put(blur, backdrop);
|
||||||
|
|
||||||
|
PostEffectProcessor processor = SaturnClient.client.getShaderLoader().loadPostEffect(BLUR,
|
||||||
|
DefaultFramebufferSet.MAIN_ONLY);
|
||||||
|
if (processor != null) {
|
||||||
|
processor.render(backdrop, POOL, pass -> pass.setUniform("Radius", (float) blur));
|
||||||
|
}
|
||||||
|
POOL.decrementLifespan();
|
||||||
|
release(blur);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draws glass through mask with the copy blurred by blur
|
||||||
|
public static RenderLayer getLayer(Identifier mask, int blur) {
|
||||||
|
return LAYERS.computeIfAbsent(mask + " " + blur, key -> RenderLayer.of("saturn_glass", 786432, PIPELINE,
|
||||||
|
RenderLayer.MultiPhaseParameters.builder()
|
||||||
|
.texture(new RenderPhase.TextureBase(() -> {
|
||||||
|
RenderSystem.setShaderTexture(0,
|
||||||
|
SaturnClient.client.getTextureManager().getTexture(mask).getGlTexture());
|
||||||
|
RenderSystem.setShaderTexture(1, BACKDROPS.get(blur).getColorAttachment());
|
||||||
|
}, () -> {
|
||||||
|
}))
|
||||||
|
.build(false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frees the copies of strengths that are no longer set, besides kept
|
||||||
|
private static void release(int kept) {
|
||||||
|
BACKDROPS.entrySet().removeIf(entry -> {
|
||||||
|
boolean unused = entry.getKey() != kept && entry.getKey() != Config.menuBlur.value
|
||||||
|
&& entry.getKey() != Config.widgetBlur.value;
|
||||||
|
if (unused) {
|
||||||
|
entry.getValue().delete();
|
||||||
|
}
|
||||||
|
return unused;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copies from into to, made or resized to match
|
||||||
|
private static SimpleFramebuffer copy(Framebuffer from, SimpleFramebuffer to, String name) {
|
||||||
|
int width = from.textureWidth;
|
||||||
|
int height = from.textureHeight;
|
||||||
|
|
||||||
|
if (to == null) {
|
||||||
|
to = new SimpleFramebuffer(name, width, height, false);
|
||||||
|
} else if (to.textureWidth != width || to.textureHeight != height) {
|
||||||
|
to.resize(width, height);
|
||||||
|
}
|
||||||
|
to.getColorAttachment().setTextureFilter(FilterMode.LINEAR, false);
|
||||||
|
|
||||||
|
RenderSystem.getDevice().createCommandEncoder().copyTextureToTexture(
|
||||||
|
from.getColorAttachment(), to.getColorAttachment(), 0, 0, 0, 0, 0, width, height);
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*///?} else {
|
||||||
|
/*import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
import org.lwjgl.opengl.GL30;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.platform.GlStateManager;
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
|
||||||
|
import org.saturnclient.config.Config;
|
||||||
|
import org.saturnclient.saturnclient.SaturnClient;
|
||||||
|
|
||||||
|
import net.minecraft.client.gl.Defines;
|
||||||
|
import net.minecraft.client.gl.Framebuffer;
|
||||||
|
import net.minecraft.client.gl.PostEffectProcessor;
|
||||||
|
import net.minecraft.client.gl.ShaderProgramKey;
|
||||||
|
import net.minecraft.client.gl.SimpleFramebuffer;
|
||||||
|
import net.minecraft.client.render.DefaultFramebufferSet;
|
||||||
|
import net.minecraft.client.render.RenderLayer;
|
||||||
|
import net.minecraft.client.render.RenderPhase;
|
||||||
|
import net.minecraft.client.render.VertexFormat;
|
||||||
|
import net.minecraft.client.render.VertexFormats;
|
||||||
|
import net.minecraft.client.util.Pool;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
|
public final class GlassRenderer {
|
||||||
|
private static final ShaderProgramKey PROGRAM = new ShaderProgramKey(Identifier.of("saturnclient", "core/glass"),
|
||||||
|
VertexFormats.POSITION_TEXTURE_COLOR, Defines.EMPTY);
|
||||||
|
|
||||||
|
private static final Identifier BLUR = Identifier.ofVanilla("blur");
|
||||||
|
// For the blur's intermediate target, freed once unused for a few frames
|
||||||
|
private static final Pool POOL = new Pool(3);
|
||||||
|
|
||||||
|
private static final Map<Integer, SimpleFramebuffer> BACKDROPS = new HashMap<>();
|
||||||
|
// By mask and blur
|
||||||
|
private static final Map<String, RenderLayer> LAYERS = new HashMap<>();
|
||||||
|
// The game behind the screen before the screen blurs it, which glass is blurred from
|
||||||
|
private static SimpleFramebuffer background;
|
||||||
|
|
||||||
|
private GlassRenderer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Saves the main framebuffer for glass. Call each frame before the screen blurs its background.
|
||||||
|
public static void saveBackground() {
|
||||||
|
background = copy(SaturnClient.client.getFramebuffer(), background);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copies the saved background and blurs the copy by blur. Call once a frame for each blur, before
|
||||||
|
// drawing glass with it.
|
||||||
|
public static void captureBackdrop(int blur) {
|
||||||
|
Framebuffer from = background != null ? background : SaturnClient.client.getFramebuffer();
|
||||||
|
SimpleFramebuffer backdrop = copy(from, BACKDROPS.get(blur));
|
||||||
|
BACKDROPS.put(blur, backdrop);
|
||||||
|
|
||||||
|
PostEffectProcessor processor = SaturnClient.client.getShaderLoader().loadPostEffect(BLUR,
|
||||||
|
DefaultFramebufferSet.MAIN_ONLY);
|
||||||
|
if (processor != null) {
|
||||||
|
processor.setUniforms("Radius", blur);
|
||||||
|
processor.render(backdrop, POOL);
|
||||||
|
}
|
||||||
|
POOL.decrementLifespan();
|
||||||
|
release(blur);
|
||||||
|
SaturnClient.client.getFramebuffer().beginWrite(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draws glass through mask with the copy blurred by blur
|
||||||
|
public static RenderLayer getLayer(Identifier mask, int blur) {
|
||||||
|
return LAYERS.computeIfAbsent(mask + " " + blur, key -> RenderLayer.of("saturn_glass",
|
||||||
|
VertexFormats.POSITION_TEXTURE_COLOR, VertexFormat.DrawMode.QUADS, 786432,
|
||||||
|
RenderLayer.MultiPhaseParameters.builder()
|
||||||
|
.texture(new RenderPhase.TextureBase(() -> {
|
||||||
|
RenderSystem.setShaderTexture(0, mask);
|
||||||
|
RenderSystem.setShaderTexture(1, BACKDROPS.get(blur).getColorAttachment());
|
||||||
|
}, () -> {
|
||||||
|
}))
|
||||||
|
.program(new RenderPhase.ShaderProgram(PROGRAM))
|
||||||
|
.transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY)
|
||||||
|
.build(false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frees the copies of strengths that are no longer set, besides kept
|
||||||
|
private static void release(int kept) {
|
||||||
|
BACKDROPS.entrySet().removeIf(entry -> {
|
||||||
|
boolean unused = entry.getKey() != kept && entry.getKey() != Config.menuBlur.value
|
||||||
|
&& entry.getKey() != Config.widgetBlur.value;
|
||||||
|
if (unused) {
|
||||||
|
entry.getValue().delete();
|
||||||
|
}
|
||||||
|
return unused;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copies from into to, made or resized to match
|
||||||
|
private static SimpleFramebuffer copy(Framebuffer from, SimpleFramebuffer to) {
|
||||||
|
int width = from.textureWidth;
|
||||||
|
int height = from.textureHeight;
|
||||||
|
|
||||||
|
if (to == null) {
|
||||||
|
to = new SimpleFramebuffer(width, height, false);
|
||||||
|
} else if (to.textureWidth != width || to.textureHeight != height) {
|
||||||
|
to.resize(width, height);
|
||||||
|
}
|
||||||
|
to.setTexFilter(GL11.GL_LINEAR);
|
||||||
|
|
||||||
|
GlStateManager._glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, from.fbo);
|
||||||
|
GlStateManager._glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, to.fbo);
|
||||||
|
GlStateManager._glBlitFrameBuffer(0, 0, width, height, 0, 0, width, height, GL11.GL_COLOR_BUFFER_BIT,
|
||||||
|
GL11.GL_NEAREST);
|
||||||
|
SaturnClient.client.getFramebuffer().beginWrite(false);
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*///?}
|
||||||
@@ -5,7 +5,9 @@ package org.saturnclient.impl.ui;
|
|||||||
//? if >=1.21.6 {
|
//? if >=1.21.6 {
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.joml.Matrix3x2f;
|
import org.joml.Matrix3x2f;
|
||||||
@@ -40,7 +42,6 @@ import net.minecraft.client.render.entity.state.EntityRenderState;
|
|||||||
import net.minecraft.client.render.item.KeyedItemRenderState;
|
import net.minecraft.client.render.item.KeyedItemRenderState;
|
||||||
//?} else
|
//?} else
|
||||||
//import net.minecraft.client.render.item.ItemRenderState;
|
//import net.minecraft.client.render.item.ItemRenderState;
|
||||||
//? if >=1.21.11
|
|
||||||
import net.minecraft.client.texture.AbstractTexture;
|
import net.minecraft.client.texture.AbstractTexture;
|
||||||
import net.minecraft.client.texture.Sprite;
|
import net.minecraft.client.texture.Sprite;
|
||||||
import net.minecraft.client.texture.TextureSetup;
|
import net.minecraft.client.texture.TextureSetup;
|
||||||
@@ -61,6 +62,8 @@ public class RenderScopeImpl implements RenderScope {
|
|||||||
|
|
||||||
private ScissorStack scissorStack;
|
private ScissorStack scissorStack;
|
||||||
private int opacity = 0xFF << 24;
|
private int opacity = 0xFF << 24;
|
||||||
|
// The glass blurs made for this scope's frame
|
||||||
|
private final Set<Integer> capturedBlurs = new HashSet<>();
|
||||||
|
|
||||||
public RenderScopeImpl(DrawContext context) {
|
public RenderScopeImpl(DrawContext context) {
|
||||||
this(context.getMatrices(), ((DrawContextAccessor) (Object) context).getState());
|
this(context.getMatrices(), ((DrawContextAccessor) (Object) context).getState());
|
||||||
@@ -230,6 +233,25 @@ public class RenderScopeImpl implements RenderScope {
|
|||||||
this.scissorStack.peekLast()));
|
this.scissorStack.peekLast()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawGlass(IdentifierRef mask, int x, int y, int width, int height, int blur) {
|
||||||
|
int color = getColor(0xFFFFFFFF);
|
||||||
|
if (mask == null || color >>> 24 == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (capturedBlurs.add(blur)) {
|
||||||
|
GlassRenderer.captureBackdrop(blur);
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractTexture maskTexture = SaturnClient.client.getTextureManager().getTexture((Identifier) (Object) mask);
|
||||||
|
this.state.addSimpleElement(new TexturedQuadGuiElementRenderState(
|
||||||
|
GlassRenderer.PIPELINE,
|
||||||
|
GlassRenderer.textures(maskTexture, blur),
|
||||||
|
new Matrix3x2f(this.matrices.stack),
|
||||||
|
x, y, x + width, y + height, 0, 1, 0, 1, color,
|
||||||
|
this.scissorStack.peekLast()));
|
||||||
|
}
|
||||||
|
|
||||||
// Scissor management
|
// Scissor management
|
||||||
@Override
|
@Override
|
||||||
public void enableScissor(int x1, int y1, int x2, int y2) {
|
public void enableScissor(int x1, int y1, int x2, int y2) {
|
||||||
@@ -368,7 +390,9 @@ public class RenderScopeImpl implements RenderScope {
|
|||||||
//?} else {
|
//?} else {
|
||||||
/*import java.util.ArrayDeque;
|
/*import java.util.ArrayDeque;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -421,6 +445,8 @@ public class RenderScopeImpl implements RenderScope {
|
|||||||
public VertexConsumerProvider.Immediate vertexConsumers;
|
public VertexConsumerProvider.Immediate vertexConsumers;
|
||||||
private ScissorStack scissorStack = new ScissorStack();
|
private ScissorStack scissorStack = new ScissorStack();
|
||||||
private int opacity = 255 << 24;
|
private int opacity = 255 << 24;
|
||||||
|
// The glass blurs made for this scope's frame
|
||||||
|
private final Set<Integer> capturedBlurs = new HashSet<>();
|
||||||
private final ItemRenderState itemRenderState;
|
private final ItemRenderState itemRenderState;
|
||||||
|
|
||||||
// ----------------------------
|
// ----------------------------
|
||||||
@@ -660,6 +686,33 @@ public class RenderScopeImpl implements RenderScope {
|
|||||||
matrices.pop();
|
matrices.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawGlass(IdentifierRef mask, int x, int y, int width, int height, int blur) {
|
||||||
|
int color = getColor(0xFFFFFFFF);
|
||||||
|
if (mask == null || color >>> 24 == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (capturedBlurs.add(blur)) {
|
||||||
|
GlassRenderer.captureBackdrop(blur);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In quarter units, as drawTexturedQuad draws
|
||||||
|
int x1 = x * 4;
|
||||||
|
int x2 = (x + width) * 4;
|
||||||
|
int y1 = y * 4;
|
||||||
|
int y2 = (y + height) * 4;
|
||||||
|
|
||||||
|
matrices.push();
|
||||||
|
matrices.scale(0.25f, 0.25f, 1.0f);
|
||||||
|
Matrix4f matrix4f = matrices.peek().getPositionMatrix();
|
||||||
|
VertexConsumer vertexConsumer = vertexConsumers.getBuffer(GlassRenderer.getLayer((Identifier) (Object) mask, blur));
|
||||||
|
vertexConsumer.vertex(matrix4f, x1, y1, 0).texture(0, 0).color(color);
|
||||||
|
vertexConsumer.vertex(matrix4f, x1, y2, 0).texture(0, 1).color(color);
|
||||||
|
vertexConsumer.vertex(matrix4f, x2, y2, 0).texture(1, 1).color(color);
|
||||||
|
vertexConsumer.vertex(matrix4f, x2, y1, 0).texture(1, 0).color(color);
|
||||||
|
matrices.pop();
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------
|
// ----------------------------
|
||||||
// Items
|
// Items
|
||||||
// ----------------------------
|
// ----------------------------
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.saturnclient.impl.ui;
|
|||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
|
import org.saturnclient.config.Config;
|
||||||
import org.saturnclient.ui.RenderScope;
|
import org.saturnclient.ui.RenderScope;
|
||||||
import org.saturnclient.ui.SaturnScreen;
|
import org.saturnclient.ui.SaturnScreen;
|
||||||
import org.saturnclient.ui.SaturnScreen.ScreenProvider;
|
import org.saturnclient.ui.SaturnScreen.ScreenProvider;
|
||||||
@@ -14,6 +15,12 @@ import net.minecraft.client.gui.CubeMapRenderer;
|
|||||||
import net.minecraft.client.gui.DrawContext;
|
import net.minecraft.client.gui.DrawContext;
|
||||||
import net.minecraft.client.gui.RotatingCubeMapRenderer;
|
import net.minecraft.client.gui.RotatingCubeMapRenderer;
|
||||||
import net.minecraft.client.gui.screen.Screen;
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
//? if >=1.21.6 {
|
||||||
|
import net.minecraft.client.texture.CubemapTexture;
|
||||||
|
//?} else {
|
||||||
|
/*import net.minecraft.client.texture.ResourceTexture;
|
||||||
|
import net.minecraft.client.texture.TextureManager;
|
||||||
|
*///?}
|
||||||
//? if >=1.21.9
|
//? if >=1.21.9
|
||||||
import net.minecraft.client.input.KeyInput;
|
import net.minecraft.client.input.KeyInput;
|
||||||
//? if <1.21.6 {
|
//? if <1.21.6 {
|
||||||
@@ -21,8 +28,6 @@ import net.minecraft.client.input.KeyInput;
|
|||||||
import net.minecraft.client.render.DefaultFramebufferSet;
|
import net.minecraft.client.render.DefaultFramebufferSet;
|
||||||
import net.minecraft.client.util.Pool;
|
import net.minecraft.client.util.Pool;
|
||||||
*///?}
|
*///?}
|
||||||
import net.minecraft.client.texture.ResourceTexture;
|
|
||||||
import net.minecraft.client.texture.TextureManager;
|
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
@@ -41,14 +46,25 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider {
|
|||||||
//private final Pool pool = new Pool(3);
|
//private final Pool pool = new Pool(3);
|
||||||
public final SaturnScreen screen;
|
public final SaturnScreen screen;
|
||||||
|
|
||||||
|
/** Whether the panorama's textures are loaded; it isn't drawn before (e.g. under the splash). */
|
||||||
|
private static volatile boolean panoramaReady = false;
|
||||||
|
|
||||||
public static void preload(MinecraftClient client) {
|
public static void preload(MinecraftClient client) {
|
||||||
TextureManager textureManager = client.getTextureManager();
|
//? if >=1.21.6 {
|
||||||
|
// From 1.21.6 CubeMapRenderer draws from one cube map texture made of panorama_0..5.png.
|
||||||
|
// CubeMapRenderer.registerTextures only stores it for the next resource reload, which has
|
||||||
|
// already happened by now, so register it through the overload that loads it right away.
|
||||||
|
client.getTextureManager().registerTexture(PANORAMA, new CubemapTexture(PANORAMA));
|
||||||
|
//?} else {
|
||||||
|
/*TextureManager textureManager = client.getTextureManager();
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
String var10001 = PANORAMA.getPath();
|
String var10001 = PANORAMA.getPath();
|
||||||
Identifier tex = PANORAMA.withPath(var10001 + "_" + i + ".png");
|
Identifier tex = PANORAMA.withPath(var10001 + "_" + i + ".png");
|
||||||
ResourceTexture resourceTexture = new ResourceTexture(tex);
|
ResourceTexture resourceTexture = new ResourceTexture(tex);
|
||||||
textureManager.registerTexture(tex, resourceTexture);
|
textureManager.registerTexture(tex, resourceTexture);
|
||||||
}
|
}
|
||||||
|
*///?}
|
||||||
|
panoramaReady = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SaturnScreenFabric(SaturnScreen screen) {
|
public SaturnScreenFabric(SaturnScreen screen) {
|
||||||
@@ -71,23 +87,29 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider {
|
|||||||
}
|
}
|
||||||
long elapsed = Duration.between(screen.start, Instant.now()).toMillis();
|
long elapsed = Duration.between(screen.start, Instant.now()).toMillis();
|
||||||
|
|
||||||
mouseX *= 2;
|
screen.updateScale();
|
||||||
mouseY *= 2;
|
|
||||||
|
|
||||||
// TODO: the panorama and the background blur (screen.backgroundBlur) are only drawn before
|
mouseX = (int) (mouseX / screen.scale);
|
||||||
// 1.21.6 so far. See also issue #23.
|
mouseY = (int) (mouseY / screen.scale);
|
||||||
if (client.world == null && client.getCurrentServerEntry() == null) {
|
|
||||||
//? if <1.21.6
|
// From 1.21.6 the background, blur included, is drawn in renderBackground.
|
||||||
//ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, screen.backgroundOpacity, delta);
|
//? if <1.21.6 {
|
||||||
|
/*if (isShowingPanorama()) {
|
||||||
|
ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, screen.backgroundOpacity, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
//? if <1.21.6 {
|
// Glass is blurred from the game as it is before this screen blurs it.
|
||||||
/*PostEffectProcessor postEffectProcessor = this.client.getShaderLoader().loadPostEffect(
|
if (Config.usesGlass()) {
|
||||||
|
context.draw();
|
||||||
|
GlassRenderer.saveBackground();
|
||||||
|
}
|
||||||
|
|
||||||
|
float radius = screen.getBackgroundBlur();
|
||||||
|
PostEffectProcessor postEffectProcessor = radius < 1 ? null : this.client.getShaderLoader().loadPostEffect(
|
||||||
Identifier.ofVanilla("blur"),
|
Identifier.ofVanilla("blur"),
|
||||||
DefaultFramebufferSet.MAIN_ONLY);
|
DefaultFramebufferSet.MAIN_ONLY);
|
||||||
|
|
||||||
if (postEffectProcessor != null) {
|
if (postEffectProcessor != null) {
|
||||||
float radius = screen.backgroundBlur * Math.min((float) elapsed / 700, 1.0f);
|
|
||||||
//? if >=1.21.5
|
//? if >=1.21.5
|
||||||
postEffectProcessor.render(this.client.getFramebuffer(), this.pool, pass -> pass.setUniform("Radius", radius));
|
postEffectProcessor.render(this.client.getFramebuffer(), this.pool, pass -> pass.setUniform("Radius", radius));
|
||||||
//? if <1.21.5
|
//? if <1.21.5
|
||||||
@@ -102,6 +124,32 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider {
|
|||||||
screen.render(renderScope, mouseX, mouseY, delta, elapsed);
|
screen.render(renderScope, mouseX, mouseY, delta, elapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//? if >=1.21.6 {
|
||||||
|
// Vanilla's background would cover Saturn's panorama, and blurs by the vanilla setting. The
|
||||||
|
// strength of this blur comes from GameRendererMixin, which gives Saturn's setting instead.
|
||||||
|
@Override
|
||||||
|
public void renderBackground(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||||
|
// The panorama renderer has no opacity (and takes no delta) from 1.21.6, so it's drawn fully
|
||||||
|
// opaque.
|
||||||
|
if (isShowingPanorama()) {
|
||||||
|
ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (screen.getBackgroundBlur() >= 1) {
|
||||||
|
context.applyBlur();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client.world != null) {
|
||||||
|
renderDarkening(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//?}
|
||||||
|
|
||||||
|
/** Whether Saturn's panorama is behind the screen: outside a world, once it's loaded. */
|
||||||
|
private boolean isShowingPanorama() {
|
||||||
|
return panoramaReady && client.world == null && client.getCurrentServerEntry() == null;
|
||||||
|
}
|
||||||
|
|
||||||
// 1.21.9 moved mouse and key events to Click and KeyInput.
|
// 1.21.9 moved mouse and key events to Click and KeyInput.
|
||||||
//? if >=1.21.9 {
|
//? if >=1.21.9 {
|
||||||
@Override
|
@Override
|
||||||
@@ -206,11 +254,11 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getWidth() {
|
public int getWidth() {
|
||||||
return width * 2;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getHeight() {
|
public int getHeight() {
|
||||||
return height * 2;
|
return height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,7 @@ import org.saturnclient.saturnclient.SaturnClient;
|
|||||||
import org.saturnclient.common.provider.Providers;
|
import org.saturnclient.common.provider.Providers;
|
||||||
import org.saturnclient.client.ServiceClient;
|
import org.saturnclient.client.ServiceClient;
|
||||||
import org.saturnclient.emotes.fabric.Emotes;
|
import org.saturnclient.emotes.fabric.Emotes;
|
||||||
|
import org.saturnclient.impl.ui.SaturnScreenFabric;
|
||||||
import org.saturnclient.ui.screens.EmoteWheel;
|
import org.saturnclient.ui.screens.EmoteWheel;
|
||||||
import org.saturnclient.ui.screens.ShiftMenu;
|
import org.saturnclient.ui.screens.ShiftMenu;
|
||||||
|
|
||||||
@@ -51,12 +52,20 @@ public class KeyInputHandler {
|
|||||||
Providers.saturn.getClient().setScreen(new ShiftMenu());
|
Providers.saturn.getClient().setScreen(new ShiftMenu());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.openEmoteWheel.wasKeyPressed() && client.currentScreen == null
|
// The emote wheel key opens the wheel, and closes it again while it's open. Closing it
|
||||||
|
// from the screen's own key handling would reopen it on the next tick, since the key is
|
||||||
|
// still down and no screen is open by then.
|
||||||
|
if (Config.openEmoteWheel.wasKeyPressed()
|
||||||
//? if >=1.21.9 {
|
//? if >=1.21.9 {
|
||||||
&& !InputUtil.isKeyPressed(client.getWindow(), GLFW.GLFW_KEY_F3)) {
|
&& !InputUtil.isKeyPressed(client.getWindow(), GLFW.GLFW_KEY_F3)) {
|
||||||
//?} else
|
//?} else
|
||||||
//&& !InputUtil.isKeyPressed(client.getWindow().getHandle(), GLFW.GLFW_KEY_F3)) {
|
//&& !InputUtil.isKeyPressed(client.getWindow().getHandle(), GLFW.GLFW_KEY_F3)) {
|
||||||
Providers.saturn.getClient().setScreen(new EmoteWheel());
|
if (client.currentScreen == null) {
|
||||||
|
Providers.saturn.getClient().setScreen(new EmoteWheel());
|
||||||
|
} else if (client.currentScreen instanceof SaturnScreenFabric saturnScreen
|
||||||
|
&& saturnScreen.screen instanceof EmoteWheel) {
|
||||||
|
saturnScreen.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sneaking cancels your own emote, for everyone.
|
// Sneaking cancels your own emote, for everyone.
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
// Saturn's glass: the blurred game behind the screen, cut to a panel's shape. For 1.21.6 and
|
||||||
|
// later; glass_pre_1_21_6.fsh (with glass.json, the program 1.21.4 loads) is for older versions.
|
||||||
|
|
||||||
|
// Vanilla's transforms, laid out as the version has them: 1.21.6 to 1.21.10 end with LineWidth, which
|
||||||
|
// 1.21.11 dropped (GlassRenderer defines HAS_LINE_WIDTH there). Not imported: the include only exists
|
||||||
|
// from 1.21.6, and 1.21.4 and 1.21.5 still read every shader's imports while loading resources.
|
||||||
|
layout(std140) uniform DynamicTransforms {
|
||||||
|
mat4 ModelViewMat;
|
||||||
|
vec4 ColorModulator;
|
||||||
|
vec3 ModelOffset;
|
||||||
|
mat4 TextureMat;
|
||||||
|
#ifdef HAS_LINE_WIDTH
|
||||||
|
float LineWidth;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
// The panel's rounded rectangle, the same texture its background is drawn with
|
||||||
|
uniform sampler2D Sampler0;
|
||||||
|
// The game behind the screen, blurred, the size of the framebuffer
|
||||||
|
uniform sampler2D Sampler1;
|
||||||
|
|
||||||
|
in vec2 texCoord0;
|
||||||
|
in vec4 vertexColor;
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float mask = texture(Sampler0, texCoord0).a * vertexColor.a;
|
||||||
|
if (mask == 0.0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The blurred copy lines up with the framebuffer being drawn to.
|
||||||
|
vec2 screenUv = gl_FragCoord.xy / vec2(textureSize(Sampler1, 0));
|
||||||
|
fragColor = vec4(texture(Sampler1, screenUv).rgb, mask) * ColorModulator;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"vertex": "minecraft:core/position_tex_color",
|
||||||
|
"fragment": "saturnclient:core/glass_pre_1_21_6",
|
||||||
|
"samplers": [
|
||||||
|
{ "name": "Sampler0" },
|
||||||
|
{ "name": "Sampler1" }
|
||||||
|
],
|
||||||
|
"uniforms": [
|
||||||
|
{ "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
|
||||||
|
{ "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
|
||||||
|
{ "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#version 150
|
||||||
|
|
||||||
|
// Saturn's glass (see glass.fsh), for 1.21.4 and 1.21.5, which set uniforms one by one instead of in
|
||||||
|
// blocks.
|
||||||
|
|
||||||
|
uniform vec4 ColorModulator;
|
||||||
|
|
||||||
|
// The panel's rounded rectangle, the same texture its background is drawn with
|
||||||
|
uniform sampler2D Sampler0;
|
||||||
|
// The game behind the screen, blurred, the size of the framebuffer
|
||||||
|
uniform sampler2D Sampler1;
|
||||||
|
|
||||||
|
in vec2 texCoord0;
|
||||||
|
in vec4 vertexColor;
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float mask = texture(Sampler0, texCoord0).a * vertexColor.a;
|
||||||
|
if (mask == 0.0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The blurred copy lines up with the framebuffer being drawn to.
|
||||||
|
vec2 screenUv = gl_FragCoord.xy / vec2(textureSize(Sampler1, 0));
|
||||||
|
fragColor = vec4(texture(Sampler1, screenUv).rgb, mask) * ColorModulator;
|
||||||
|
}
|
||||||
@@ -9,7 +9,6 @@
|
|||||||
"render.LivingEntityRendererMixin",
|
"render.LivingEntityRendererMixin",
|
||||||
"render.LivingEntityRenderStateMixin",
|
"render.LivingEntityRenderStateMixin",
|
||||||
"render.BackgroundRendererMixin",
|
"render.BackgroundRendererMixin",
|
||||||
"misc.NametagsMixin",
|
|
||||||
"render.RenderMixin",
|
"render.RenderMixin",
|
||||||
"world.TpsMixin",
|
"world.TpsMixin",
|
||||||
"entity.CameraMixin",
|
"entity.CameraMixin",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ org.gradle.configuration-cache=false
|
|||||||
# check these on https://fabricmc.net/develop
|
# check these on https://fabricmc.net/develop
|
||||||
minecraft_version=1.21.10
|
minecraft_version=1.21.10
|
||||||
yarn_mappings=1.21.10+build.3
|
yarn_mappings=1.21.10+build.3
|
||||||
loader_version=0.19.0
|
loader_version=0.19.5
|
||||||
|
|
||||||
fabric_api_version=0.138.4+1.21.10
|
fabric_api_version=0.138.4+1.21.10
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ org.gradle.configuration-cache=false
|
|||||||
# check these on https://fabricmc.net/develop
|
# check these on https://fabricmc.net/develop
|
||||||
minecraft_version=1.21.11
|
minecraft_version=1.21.11
|
||||||
yarn_mappings=1.21.11+build.4
|
yarn_mappings=1.21.11+build.4
|
||||||
loader_version=0.19.0
|
loader_version=0.19.5
|
||||||
|
|
||||||
fabric_api_version=0.141.3+1.21.11
|
fabric_api_version=0.141.3+1.21.11
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ org.gradle.configuration-cache=false
|
|||||||
# check these on https://fabricmc.net/develop
|
# check these on https://fabricmc.net/develop
|
||||||
minecraft_version=1.21.4
|
minecraft_version=1.21.4
|
||||||
yarn_mappings=1.21.4+build.8
|
yarn_mappings=1.21.4+build.8
|
||||||
loader_version=0.18.4
|
loader_version=0.19.5
|
||||||
|
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
maven_group=org.saturnclient
|
maven_group=org.saturnclient
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ org.gradle.configuration-cache=false
|
|||||||
# check these on https://fabricmc.net/develop
|
# check these on https://fabricmc.net/develop
|
||||||
minecraft_version=1.21.5
|
minecraft_version=1.21.5
|
||||||
yarn_mappings=1.21.5+build.1
|
yarn_mappings=1.21.5+build.1
|
||||||
loader_version=0.18.4
|
loader_version=0.19.5
|
||||||
|
|
||||||
fabric_api_version=0.128.2+1.21.5
|
fabric_api_version=0.128.2+1.21.5
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ org.gradle.configuration-cache=false
|
|||||||
# check these on https://fabricmc.net/develop
|
# check these on https://fabricmc.net/develop
|
||||||
minecraft_version=1.21.6
|
minecraft_version=1.21.6
|
||||||
yarn_mappings=1.21.6+build.1
|
yarn_mappings=1.21.6+build.1
|
||||||
loader_version=0.18.5
|
loader_version=0.19.5
|
||||||
|
|
||||||
fabric_api_version=0.128.2+1.21.6
|
fabric_api_version=0.128.2+1.21.6
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ org.gradle.configuration-cache=false
|
|||||||
# check these on https://fabricmc.net/develop
|
# check these on https://fabricmc.net/develop
|
||||||
minecraft_version=1.21.7
|
minecraft_version=1.21.7
|
||||||
yarn_mappings=1.21.7+build.8
|
yarn_mappings=1.21.7+build.8
|
||||||
loader_version=0.19.0
|
loader_version=0.19.5
|
||||||
|
|
||||||
fabric_api_version=0.129.0+1.21.7
|
fabric_api_version=0.129.0+1.21.7
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ org.gradle.configuration-cache=false
|
|||||||
# check these on https://fabricmc.net/develop
|
# check these on https://fabricmc.net/develop
|
||||||
minecraft_version=1.21.8
|
minecraft_version=1.21.8
|
||||||
yarn_mappings=1.21.8+build.1
|
yarn_mappings=1.21.8+build.1
|
||||||
loader_version=0.19.0
|
loader_version=0.19.5
|
||||||
|
|
||||||
fabric_api_version=0.136.1+1.21.8
|
fabric_api_version=0.136.1+1.21.8
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ org.gradle.configuration-cache=false
|
|||||||
# check these on https://fabricmc.net/develop
|
# check these on https://fabricmc.net/develop
|
||||||
minecraft_version=1.21.9
|
minecraft_version=1.21.9
|
||||||
yarn_mappings=1.21.9+build.1
|
yarn_mappings=1.21.9+build.1
|
||||||
loader_version=0.19.0
|
loader_version=0.19.5
|
||||||
|
|
||||||
fabric_api_version=0.134.1+1.21.9
|
fabric_api_version=0.134.1+1.21.9
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user