Merge remote-tracking branch 'origin/1.19' into 1.19.1

This commit is contained in:
modmuss50 2022-07-21 20:20:45 +01:00
commit 437c2eec0b
16 changed files with 341 additions and 99 deletions

View file

@ -18,7 +18,7 @@ package net.fabricmc.fabric.mixin.item;
import java.util.function.Consumer; import java.util.function.Consumer;
import com.google.common.collect.HashMultimap; import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
@ -83,8 +83,8 @@ public abstract class ItemStackMixin {
) )
public Multimap<EntityAttribute, EntityAttributeModifier> hookGetAttributeModifiers(Item item, EquipmentSlot slot) { public Multimap<EntityAttribute, EntityAttributeModifier> hookGetAttributeModifiers(Item item, EquipmentSlot slot) {
ItemStack stack = (ItemStack) (Object) this; ItemStack stack = (ItemStack) (Object) this;
//we need to ensure it is modifiable for the callback //we need to ensure it is modifiable for the callback, use linked map to preserve ordering
Multimap<EntityAttribute, EntityAttributeModifier> attributeModifiers = HashMultimap.create(item.getAttributeModifiers(stack, slot)); Multimap<EntityAttribute, EntityAttributeModifier> attributeModifiers = LinkedHashMultimap.create(item.getAttributeModifiers(stack, slot));
ModifyItemAttributeModifiersCallback.EVENT.invoker().modifyAttributeModifiers(stack, slot, attributeModifiers); ModifyItemAttributeModifiersCallback.EVENT.invoker().modifyAttributeModifiers(stack, slot, attributeModifiers);
return attributeModifiers; return attributeModifiers;
} }

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.networking.client;
import com.mojang.blaze3d.systems.RenderSystem;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
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.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.font.MultilineText;
import net.minecraft.client.gui.screen.DisconnectedScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.MathHelper;
/**
* This mixin makes disconnect reason text scrollable.
*/
@Mixin(DisconnectedScreen.class)
public abstract class DisconnectedScreenMixin extends Screen {
@Shadow
private int reasonHeight;
@Unique
private int actualReasonHeight;
@Unique
private int scroll;
@Unique
private int maxScroll;
private DisconnectedScreenMixin() {
super(null);
}
// Inject to right after reasonHeight is stored, to make sure the back button have correct position.
@Inject(method = "init", at = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/screen/DisconnectedScreen;reasonHeight:I", opcode = Opcodes.PUTFIELD, shift = At.Shift.AFTER))
private void init(CallbackInfo ci) {
actualReasonHeight = reasonHeight;
reasonHeight = Math.min(reasonHeight, height - 100);
scroll = 0;
maxScroll = actualReasonHeight - reasonHeight;
}
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/font/MultilineText;drawCenterWithShadow(Lnet/minecraft/client/util/math/MatrixStack;II)I"))
private int render(MultilineText instance, MatrixStack matrixStack, int x, int y) {
double scale = client.getWindow().getScaleFactor();
RenderSystem.enableScissor(0, (int) (y * scale), (int) (width * scale), (int) (reasonHeight * scale));
instance.drawCenterWithShadow(matrixStack, x, y - scroll);
RenderSystem.disableScissor();
// Draw gradient at the top/bottom to indicate that the text is scrollable.
if (actualReasonHeight > reasonHeight) {
int startX = (width - instance.getMaxWidth()) / 2;
int endX = (width + instance.getMaxWidth()) / 2;
if (scroll > 0) {
fillGradient(matrixStack, startX, y, endX, y + 10, 0xFF000000, 0);
}
if (scroll < maxScroll) {
fillGradient(matrixStack, startX, y + reasonHeight - 10, endX, y + reasonHeight, 0, 0xFF000000);
}
}
return y + reasonHeight;
}
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double amount) {
scroll = MathHelper.clamp(scroll - (MathHelper.sign(amount) * client.textRenderer.fontHeight * 10), 0, maxScroll);
return super.mouseScrolled(mouseX, mouseY, amount);
}
}

View file

@ -6,7 +6,8 @@
"accessor.ConnectScreenAccessor", "accessor.ConnectScreenAccessor",
"accessor.MinecraftClientAccessor", "accessor.MinecraftClientAccessor",
"client.ClientLoginNetworkHandlerMixin", "client.ClientLoginNetworkHandlerMixin",
"client.ClientPlayNetworkHandlerMixin" "client.ClientPlayNetworkHandlerMixin",
"client.DisconnectedScreenMixin"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1

View file

@ -68,7 +68,7 @@ public final class GlobalReceiverRegistry<H> {
try { try {
final boolean replaced = this.handlers.putIfAbsent(channelName, handler) == null; final boolean replaced = this.handlers.putIfAbsent(channelName, handler) == null;
if (!replaced) { if (replaced) {
this.handleRegistration(channelName, handler); this.handleRegistration(channelName, handler);
} }

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.test.networking.disconnectscreen;
import net.minecraft.text.Text;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
public class DisconnectScreenTest implements ClientModInitializer {
@Override
public void onInitializeClient() {
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) ->
dispatcher.register(ClientCommandManager.literal("disconnect_screen_test").executes(context -> {
StringBuilder builder = new StringBuilder("A very long disconnect reason:");
for (int i = 0; i < 100; i++) {
builder.append("\nLine ").append(i + 1);
}
context.getSource().getPlayer().networkHandler.getConnection().disconnect(Text.of(builder.toString()));
return 1;
})));
}
}

View file

@ -17,6 +17,7 @@
], ],
"client": [ "client": [
"net.fabricmc.fabric.test.networking.channeltest.NetworkingChannelClientTest", "net.fabricmc.fabric.test.networking.channeltest.NetworkingChannelClientTest",
"net.fabricmc.fabric.test.networking.disconnectscreen.DisconnectScreenTest",
"net.fabricmc.fabric.test.networking.keybindreciever.NetworkingKeybindClientPacketTest", "net.fabricmc.fabric.test.networking.keybindreciever.NetworkingKeybindClientPacketTest",
"net.fabricmc.fabric.test.networking.login.NetworkingLoginQueryClientTest", "net.fabricmc.fabric.test.networking.login.NetworkingLoginQueryClientTest",
"net.fabricmc.fabric.test.networking.play.NetworkingPlayPacketClientTest" "net.fabricmc.fabric.test.networking.play.NetworkingPlayPacketClientTest"

View file

@ -23,8 +23,8 @@ import java.io.IOException;
import java.util.Locale; import java.util.Locale;
import java.util.Properties; import java.util.Properties;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess; import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
@ -89,7 +89,7 @@ public class Indigo implements ClientModInitializer {
} }
static { static {
File configDir = new File(FabricLoader.getInstance().getConfigDirectory(), "fabric"); File configDir = FabricLoader.getInstance().getConfigDir().resolve("fabric").toFile();
if (!configDir.exists()) { if (!configDir.exists()) {
if (!configDir.mkdir()) { if (!configDir.mkdir()) {

View file

@ -28,12 +28,11 @@ import static net.minecraft.util.math.Direction.UP;
import static net.minecraft.util.math.Direction.WEST; import static net.minecraft.util.math.Direction.WEST;
import java.util.BitSet; import java.util.BitSet;
import java.util.function.ToIntFunction;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import net.minecraft.block.Block; import net.minecraft.block.BlockState;
import net.minecraft.client.render.block.BlockModelRenderer; import net.minecraft.client.render.block.BlockModelRenderer;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
@ -55,10 +54,15 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
*/ */
@Environment(EnvType.CLIENT) @Environment(EnvType.CLIENT)
public class AoCalculator { public class AoCalculator {
@FunctionalInterface
public interface BrightnessFunc {
int apply(BlockPos pos, BlockState state);
}
/** Used to receive a method reference in constructor for ao value lookup. */ /** Used to receive a method reference in constructor for ao value lookup. */
@FunctionalInterface @FunctionalInterface
public interface AoFunc { public interface AoFunc {
float apply(BlockPos pos); float apply(BlockPos pos, BlockState state);
} }
/** /**
@ -81,10 +85,10 @@ public class AoCalculator {
private final BlockPos.Mutable lightPos = new BlockPos.Mutable(); private final BlockPos.Mutable lightPos = new BlockPos.Mutable();
private final BlockPos.Mutable searchPos = new BlockPos.Mutable(); private final BlockPos.Mutable searchPos = new BlockPos.Mutable();
private final BlockRenderInfo blockInfo; private final BlockRenderInfo blockInfo;
private final ToIntFunction<BlockPos> brightnessFunc; private final BrightnessFunc brightnessFunc;
private final AoFunc aoFunc; private final AoFunc aoFunc;
/** caches results of {@link #computeFace(Direction, boolean)} for the current block. */ /** caches results of {@link #computeFace(Direction, boolean, boolean)} for the current block. */
private final AoFaceData[] faceData = new AoFaceData[12]; private final AoFaceData[] faceData = new AoFaceData[12];
/** indicates which elements of {@link #faceData} have been computed for the current block. */ /** indicates which elements of {@link #faceData} have been computed for the current block. */
@ -97,7 +101,7 @@ public class AoCalculator {
public final float[] ao = new float[4]; public final float[] ao = new float[4];
public final int[] light = new int[4]; public final int[] light = new int[4];
public AoCalculator(BlockRenderInfo blockInfo, ToIntFunction<BlockPos> brightnessFunc, AoFunc aoFunc) { public AoCalculator(BlockRenderInfo blockInfo, BrightnessFunc brightnessFunc, AoFunc aoFunc) {
this.blockInfo = blockInfo; this.blockInfo = blockInfo;
this.brightnessFunc = brightnessFunc; this.brightnessFunc = brightnessFunc;
this.aoFunc = aoFunc; this.aoFunc = aoFunc;
@ -193,7 +197,7 @@ public class AoCalculator {
int flags = quad.geometryFlags(); int flags = quad.geometryFlags();
// force to block face if shape is full cube - matches vanilla logic // force to block face if shape is full cube - matches vanilla logic
if ((flags & LIGHT_FACE_FLAG) == 0 && (flags & AXIS_ALIGNED_FLAG) == AXIS_ALIGNED_FLAG && Block.isShapeFullCube(blockInfo.blockState.getCollisionShape(blockInfo.blockView, blockInfo.blockPos))) { if ((flags & LIGHT_FACE_FLAG) == 0 && (flags & AXIS_ALIGNED_FLAG) != 0 && blockInfo.blockState.isFullCube(blockInfo.blockView, blockInfo.blockPos)) {
flags |= LIGHT_FACE_FLAG; flags |= LIGHT_FACE_FLAG;
} }
@ -355,7 +359,7 @@ public class AoCalculator {
/** /**
* Computes smoothed brightness and Ao shading for four corners of a block face. * Computes smoothed brightness and Ao shading for four corners of a block face.
* Outer block face is what you normally see and what you get get when second * Outer block face is what you normally see and what you get when the second
* parameter is true. Inner is light *within* the block and usually darker. * parameter is true. Inner is light *within* the block and usually darker.
* It is blended with the outer face for inset surfaces, but is also used directly * It is blended with the outer face for inset surfaces, but is also used directly
* in vanilla logic for some blocks that aren't full opaque cubes. * in vanilla logic for some blocks that aren't full opaque cubes.
@ -371,45 +375,76 @@ public class AoCalculator {
final BlockRenderView world = blockInfo.blockView; final BlockRenderView world = blockInfo.blockView;
final BlockPos pos = blockInfo.blockPos; final BlockPos pos = blockInfo.blockPos;
final BlockState blockState = blockInfo.blockState;
final BlockPos.Mutable lightPos = this.lightPos; final BlockPos.Mutable lightPos = this.lightPos;
final BlockPos.Mutable searchPos = this.searchPos; final BlockPos.Mutable searchPos = this.searchPos;
BlockState searchState;
if (isOnBlockFace) {
lightPos.set(pos, lightFace);
} else {
lightPos.set(pos);
}
lightPos.set(isOnBlockFace ? pos.offset(lightFace) : pos);
AoFace aoFace = AoFace.get(lightFace); AoFace aoFace = AoFace.get(lightFace);
searchPos.set(lightPos).move(aoFace.neighbors[0]); // Vanilla was further offsetting the positions for opaque block checks in the
final int light0 = brightnessFunc.applyAsInt(searchPos); // direction of the light face, but it was actually mis-sampling and causing
final float ao0 = aoFunc.apply(searchPos); // visible artifacts in certain situations
searchPos.set(lightPos).move(aoFace.neighbors[1]);
final int light1 = brightnessFunc.applyAsInt(searchPos);
final float ao1 = aoFunc.apply(searchPos);
searchPos.set(lightPos).move(aoFace.neighbors[2]);
final int light2 = brightnessFunc.applyAsInt(searchPos);
final float ao2 = aoFunc.apply(searchPos);
searchPos.set(lightPos).move(aoFace.neighbors[3]);
final int light3 = brightnessFunc.applyAsInt(searchPos);
final float ao3 = aoFunc.apply(searchPos);
// vanilla was further offsetting these in the direction of the light face searchPos.set(lightPos, aoFace.neighbors[0]);
// but it was actually mis-sampling and causing visible artifacts in certain situation searchState = world.getBlockState(searchPos);
searchPos.set(lightPos).move(aoFace.neighbors[0]); //.setOffset(lightFace); final int light0 = brightnessFunc.apply(searchPos, searchState);
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) searchPos.move(lightFace); final float ao0 = aoFunc.apply(searchPos, searchState);
final boolean isClear0 = world.getBlockState(searchPos).getOpacity(world, searchPos) == 0;
searchPos.set(lightPos).move(aoFace.neighbors[1]); //.setOffset(lightFace); if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) searchPos.move(lightFace); searchPos.move(lightFace);
final boolean isClear1 = world.getBlockState(searchPos).getOpacity(world, searchPos) == 0; searchState = world.getBlockState(searchPos);
searchPos.set(lightPos).move(aoFace.neighbors[2]); //.setOffset(lightFace); }
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) searchPos.move(lightFace);
final boolean isClear2 = world.getBlockState(searchPos).getOpacity(world, searchPos) == 0; final boolean isClear0 = !searchState.shouldBlockVision(world, searchPos) || searchState.getOpacity(world, searchPos) == 0;
searchPos.set(lightPos).move(aoFace.neighbors[3]); //.setOffset(lightFace);
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) searchPos.move(lightFace); searchPos.set(lightPos, aoFace.neighbors[1]);
final boolean isClear3 = world.getBlockState(searchPos).getOpacity(world, searchPos) == 0; searchState = world.getBlockState(searchPos);
final int light1 = brightnessFunc.apply(searchPos, searchState);
final float ao1 = aoFunc.apply(searchPos, searchState);
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
searchPos.move(lightFace);
searchState = world.getBlockState(searchPos);
}
final boolean isClear1 = !searchState.shouldBlockVision(world, searchPos) || searchState.getOpacity(world, searchPos) == 0;
searchPos.set(lightPos, aoFace.neighbors[2]);
searchState = world.getBlockState(searchPos);
final int light2 = brightnessFunc.apply(searchPos, searchState);
final float ao2 = aoFunc.apply(searchPos, searchState);
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
searchPos.move(lightFace);
searchState = world.getBlockState(searchPos);
}
final boolean isClear2 = !searchState.shouldBlockVision(world, searchPos) || searchState.getOpacity(world, searchPos) == 0;
searchPos.set(lightPos, aoFace.neighbors[3]);
searchState = world.getBlockState(searchPos);
final int light3 = brightnessFunc.apply(searchPos, searchState);
final float ao3 = aoFunc.apply(searchPos, searchState);
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
searchPos.move(lightFace);
searchState = world.getBlockState(searchPos);
}
final boolean isClear3 = !searchState.shouldBlockVision(world, searchPos) || searchState.getOpacity(world, searchPos) == 0;
// c = corner - values at corners of face // c = corner - values at corners of face
int cLight0, cLight1, cLight2, cLight3; int cLight0, cLight1, cLight2, cLight3;
float cAo0, cAo1, cAo2, cAo3; float cAo0, cAo1, cAo2, cAo3;
// If neighbors on both side of the corner are opaque, then apparently we use the light/shade // If neighbors on both sides of the corner are opaque, then apparently we use the light/shade
// from one of the sides adjacent to the corner. If either neighbor is clear (no light subtraction) // from one of the sides adjacent to the corner. If either neighbor is clear (no light subtraction)
// then we use values from the outwardly diagonal corner. (outwardly = position is one more away from light face) // then we use values from the outwardly diagonal corner. (outwardly = position is one more away from light face)
if (!isClear2 && !isClear0) { if (!isClear2 && !isClear0) {
@ -417,8 +452,9 @@ public class AoCalculator {
cLight0 = light0; cLight0 = light0;
} else { } else {
searchPos.set(lightPos).move(aoFace.neighbors[0]).move(aoFace.neighbors[2]); searchPos.set(lightPos).move(aoFace.neighbors[0]).move(aoFace.neighbors[2]);
cAo0 = aoFunc.apply(searchPos); searchState = world.getBlockState(searchPos);
cLight0 = brightnessFunc.applyAsInt(searchPos); cAo0 = aoFunc.apply(searchPos, searchState);
cLight0 = brightnessFunc.apply(searchPos, searchState);
} }
if (!isClear3 && !isClear0) { if (!isClear3 && !isClear0) {
@ -426,8 +462,9 @@ public class AoCalculator {
cLight1 = light0; cLight1 = light0;
} else { } else {
searchPos.set(lightPos).move(aoFace.neighbors[0]).move(aoFace.neighbors[3]); searchPos.set(lightPos).move(aoFace.neighbors[0]).move(aoFace.neighbors[3]);
cAo1 = aoFunc.apply(searchPos); searchState = world.getBlockState(searchPos);
cLight1 = brightnessFunc.applyAsInt(searchPos); cAo1 = aoFunc.apply(searchPos, searchState);
cLight1 = brightnessFunc.apply(searchPos, searchState);
} }
if (!isClear2 && !isClear1) { if (!isClear2 && !isClear1) {
@ -435,8 +472,9 @@ public class AoCalculator {
cLight2 = light1; cLight2 = light1;
} else { } else {
searchPos.set(lightPos).move(aoFace.neighbors[1]).move(aoFace.neighbors[2]); searchPos.set(lightPos).move(aoFace.neighbors[1]).move(aoFace.neighbors[2]);
cAo2 = aoFunc.apply(searchPos); searchState = world.getBlockState(searchPos);
cLight2 = brightnessFunc.applyAsInt(searchPos); cAo2 = aoFunc.apply(searchPos, searchState);
cLight2 = brightnessFunc.apply(searchPos, searchState);
} }
if (!isClear3 && !isClear1) { if (!isClear3 && !isClear1) {
@ -444,22 +482,24 @@ public class AoCalculator {
cLight3 = light1; cLight3 = light1;
} else { } else {
searchPos.set(lightPos).move(aoFace.neighbors[1]).move(aoFace.neighbors[3]); searchPos.set(lightPos).move(aoFace.neighbors[1]).move(aoFace.neighbors[3]);
cAo3 = aoFunc.apply(searchPos); searchState = world.getBlockState(searchPos);
cLight3 = brightnessFunc.applyAsInt(searchPos); cAo3 = aoFunc.apply(searchPos, searchState);
cLight3 = brightnessFunc.apply(searchPos, searchState);
} }
// If on block face or neighbor isn't occluding, "center" will be neighbor brightness // If on block face or neighbor isn't occluding, "center" will be neighbor brightness
// Doesn't use light pos because logic not based solely on this block's geometry // Doesn't use light pos because logic not based solely on this block's geometry
int lightCenter; int lightCenter;
searchPos.set(pos).move(lightFace); searchPos.set(pos, lightFace);
searchState = world.getBlockState(searchPos);
if (isOnBlockFace || !world.getBlockState(searchPos).isOpaqueFullCube(world, searchPos)) { if (isOnBlockFace || !searchState.isOpaqueFullCube(world, searchPos)) {
lightCenter = brightnessFunc.applyAsInt(searchPos); lightCenter = brightnessFunc.apply(searchPos, searchState);
} else { } else {
lightCenter = brightnessFunc.applyAsInt(pos); lightCenter = brightnessFunc.apply(pos, blockState);
} }
float aoCenter = aoFunc.apply(isOnBlockFace ? lightPos : pos); float aoCenter = aoFunc.apply(lightPos, world.getBlockState(lightPos));
float worldBrightness = world.getBrightness(lightFace, shade); float worldBrightness = world.getBrightness(lightFace, shade);
result.a0 = ((ao3 + ao0 + cAo1 + aoCenter) * 0.25F) * worldBrightness; result.a0 = ((ao3 + ao0 + cAo1 + aoCenter) * 0.25F) * worldBrightness;
@ -495,12 +535,12 @@ public class AoCalculator {
if (b == 0) b = d; if (b == 0) b = d;
if (c == 0) c = d; if (c == 0) c = d;
// bitwise divide by 4, clamp to expected (positive) range // bitwise divide by 4, clamp to expected (positive) range
return a + b + c + d >> 2 & 16711935; return a + b + c + d >> 2 & 0xFF00FF;
} }
private static int meanInnerBrightness(int a, int b, int c, int d) { private static int meanInnerBrightness(int a, int b, int c, int d) {
// bitwise divide by 4, clamp to expected (positive) range // bitwise divide by 4, clamp to expected (positive) range
return a + b + c + d >> 2 & 16711935; return a + b + c + d >> 2 & 0xFF00FF;
} }
private static int nonZeroMin(int a, int b) { private static int nonZeroMin(int a, int b) {

View file

@ -28,16 +28,15 @@ import net.fabricmc.fabric.impl.client.indigo.Indigo;
*/ */
@FunctionalInterface @FunctionalInterface
public interface AoLuminanceFix { public interface AoLuminanceFix {
float apply(BlockView view, BlockPos pos); float apply(BlockView view, BlockPos pos, BlockState state);
AoLuminanceFix INSTANCE = Indigo.FIX_LUMINOUS_AO_SHADE ? AoLuminanceFix::fixed : AoLuminanceFix::vanilla; AoLuminanceFix INSTANCE = Indigo.FIX_LUMINOUS_AO_SHADE ? AoLuminanceFix::fixed : AoLuminanceFix::vanilla;
static float vanilla(BlockView view, BlockPos pos) { static float vanilla(BlockView view, BlockPos pos, BlockState state) {
return view.getBlockState(pos).getAmbientOcclusionLightLevel(view, pos); return state.getAmbientOcclusionLightLevel(view, pos);
} }
static float fixed(BlockView view, BlockPos pos) { static float fixed(BlockView view, BlockPos pos, BlockState state) {
final BlockState state = view.getBlockState(pos);
return state.getLuminance() == 0 ? state.getAmbientOcclusionLightLevel(view, pos) : 1f; return state.getLuminance() == 0 ? state.getAmbientOcclusionLightLevel(view, pos) : 1f;
} }
} }

View file

@ -19,7 +19,6 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.render;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumer;
@ -93,25 +92,22 @@ public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implemen
return; return;
} }
final RenderMaterialImpl.Value mat = quad.material(); tessellateQuad(quad, 0);
if (!mat.disableAo(0) && MinecraftClient.isAmbientOcclusionEnabled()) {
// needs to happen before offsets are applied
aoCalc.compute(quad, false);
}
tessellateQuad(quad, mat, 0);
} }
/** /**
* Determines color index and render layer, then routes to appropriate * Determines color index and render layer, then routes to appropriate
* tessellate routine based on material properties. * tessellate routine based on material properties.
*/ */
private void tessellateQuad(MutableQuadViewImpl quad, RenderMaterialImpl.Value mat, int textureIndex) { private void tessellateQuad(MutableQuadViewImpl quad, int textureIndex) {
final RenderMaterialImpl.Value mat = quad.material();
final int colorIndex = mat.disableColorIndex(textureIndex) ? -1 : quad.colorIndex(); final int colorIndex = mat.disableColorIndex(textureIndex) ? -1 : quad.colorIndex();
final RenderLayer renderLayer = blockInfo.effectiveRenderLayer(mat.blendMode(textureIndex)); final RenderLayer renderLayer = blockInfo.effectiveRenderLayer(mat.blendMode(textureIndex));
if (blockInfo.defaultAo && !mat.disableAo(textureIndex)) { if (blockInfo.defaultAo && !mat.disableAo(textureIndex)) {
// needs to happen before offsets are applied
aoCalc.compute(quad, false);
if (mat.emissive(textureIndex)) { if (mat.emissive(textureIndex)) {
tessellateSmoothEmissive(quad, renderLayer, colorIndex); tessellateSmoothEmissive(quad, renderLayer, colorIndex);
} else { } else {

View file

@ -16,11 +16,11 @@
package net.fabricmc.fabric.impl.client.indigo.renderer.render; package net.fabricmc.fabric.impl.client.indigo.renderer.render;
import static net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper.AXIS_ALIGNED_FLAG;
import static net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper.LIGHT_FACE_FLAG; import static net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper.LIGHT_FACE_FLAG;
import java.util.function.Function; import java.util.function.Function;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.render.LightmapTextureManager; import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
@ -177,8 +177,12 @@ public abstract class AbstractQuadRenderer {
// for reference. // for reference.
if (quad.cullFace() != null) { if (quad.cullFace() != null) {
mpos.move(quad.cullFace()); mpos.move(quad.cullFace());
} else if ((quad.geometryFlags() & LIGHT_FACE_FLAG) != 0 || Block.isShapeFullCube(blockState.getCollisionShape(blockInfo.blockView, pos))) { } else {
mpos.move(quad.lightFace()); final int flags = quad.geometryFlags();
if ((flags & LIGHT_FACE_FLAG) != 0 || ((flags & AXIS_ALIGNED_FLAG) != 0 && blockState.isFullCube(blockInfo.blockView, pos))) {
mpos.move(quad.lightFace());
}
} }
// Unfortunately cannot use brightness cache here unless we implement one specifically for flat lighting. See #329 // Unfortunately cannot use brightness cache here unless we implement one specifically for flat lighting. See #329

View file

@ -94,17 +94,17 @@ public class BlockRenderContext extends AbstractRenderContext {
} }
}; };
private int brightness(BlockPos pos) { private int brightness(BlockPos pos, BlockState state) {
if (blockInfo.blockView == null) { if (blockInfo.blockView == null) {
return LightmapTextureManager.MAX_LIGHT_COORDINATE; return LightmapTextureManager.MAX_LIGHT_COORDINATE;
} }
return WorldRenderer.getLightmapCoordinates(blockInfo.blockView, blockInfo.blockView.getBlockState(pos), pos); return WorldRenderer.getLightmapCoordinates(blockInfo.blockView, state, pos);
} }
private float aoLevel(BlockPos pos) { private float aoLevel(BlockPos pos, BlockState state) {
final BlockRenderView blockView = blockInfo.blockView; final BlockRenderView blockView = blockInfo.blockView;
return blockView == null ? 1f : AoLuminanceFix.INSTANCE.apply(blockView, pos); return blockView == null ? 1f : AoLuminanceFix.INSTANCE.apply(blockView, pos, state);
} }
private VertexConsumer outputBuffer(RenderLayer renderLayer) { private VertexConsumer outputBuffer(RenderLayer renderLayer) {

View file

@ -119,27 +119,27 @@ public class ChunkRenderInfo {
} }
/** /**
* Cached values for {@link BlockState#getBlockBrightness(BlockRenderView, BlockPos)}. * Cached values for {@link WorldRenderer#getLightmapCoordinates(BlockRenderView, BlockState, BlockPos)}.
* See also the comments for {@link #brightnessCache}. * See also the comments for {@link #brightnessCache}.
*/ */
int cachedBrightness(BlockPos pos) { int cachedBrightness(BlockPos pos, BlockState state) {
long key = pos.asLong(); long key = pos.asLong();
int result = brightnessCache.get(key); int result = brightnessCache.get(key);
if (result == Integer.MAX_VALUE) { if (result == Integer.MAX_VALUE) {
result = WorldRenderer.getLightmapCoordinates(blockView, blockView.getBlockState(pos), pos); result = WorldRenderer.getLightmapCoordinates(blockView, state, pos);
brightnessCache.put(key, result); brightnessCache.put(key, result);
} }
return result; return result;
} }
float cachedAoLevel(BlockPos pos) { float cachedAoLevel(BlockPos pos, BlockState state) {
long key = pos.asLong(); long key = pos.asLong();
float result = aoLevelCache.get(key); float result = aoLevelCache.get(key);
if (result == Float.MAX_VALUE) { if (result == Float.MAX_VALUE) {
result = AoLuminanceFix.INSTANCE.apply(blockView, pos); result = AoLuminanceFix.INSTANCE.apply(blockView, pos, state);
aoLevelCache.put(key, result); aoLevelCache.put(key, result);
} }

View file

@ -17,10 +17,14 @@
package net.fabricmc.fabric.mixin.resource.loader.client; package net.fabricmc.fabric.mixin.resource.loader.client;
import java.io.File; import java.io.File;
import java.util.concurrent.CompletableFuture;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.DynamicOps;
import com.mojang.serialization.JsonOps; import com.mojang.serialization.JsonOps;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@ -31,25 +35,29 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.world.CreateWorldScreen; import net.minecraft.client.gui.screen.world.CreateWorldScreen;
import net.minecraft.client.gui.screen.world.MoreOptionsDialog;
import net.minecraft.client.world.GeneratorOptionsHolder;
import net.minecraft.resource.DataPackSettings; import net.minecraft.resource.DataPackSettings;
import net.minecraft.resource.ResourceManager; import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.ResourcePackManager; import net.minecraft.resource.ResourcePackManager;
import net.minecraft.resource.ResourceType; import net.minecraft.resource.ResourceType;
import net.minecraft.server.SaveLoading;
import net.minecraft.util.Util; import net.minecraft.util.Util;
import net.minecraft.util.dynamic.RegistryOps; import net.minecraft.util.dynamic.RegistryOps;
import net.minecraft.util.registry.DynamicRegistryManager; import net.minecraft.util.registry.DynamicRegistryManager;
import net.minecraft.world.gen.GeneratorOptions; import net.minecraft.world.gen.GeneratorOptions;
import net.minecraft.world.gen.WorldPresets;
import net.fabricmc.fabric.impl.resource.loader.ModResourcePackCreator; import net.fabricmc.fabric.impl.resource.loader.ModResourcePackCreator;
import net.fabricmc.fabric.impl.resource.loader.ModResourcePackUtil; import net.fabricmc.fabric.impl.resource.loader.ModResourcePackUtil;
import net.fabricmc.fabric.mixin.resource.loader.ResourcePackManagerAccessor; import net.fabricmc.fabric.mixin.resource.loader.ResourcePackManagerAccessor;
@Mixin(CreateWorldScreen.class) @Mixin(CreateWorldScreen.class)
public abstract class CreateWorldScreenMixin { public abstract class CreateWorldScreenMixin extends Screen {
@Unique @Unique
private static DataPackSettings defaultDataPackSettings; private static DataPackSettings defaultDataPackSettings;
@ -60,8 +68,25 @@ public abstract class CreateWorldScreenMixin {
@Final @Final
private static Logger LOGGER; private static Logger LOGGER;
@Unique @Shadow
private static RegistryOps<JsonElement> loadedOps; @Final
public MoreOptionsDialog moreOptionsDialog;
@Shadow
protected DataPackSettings dataPackSettings;
@Shadow
private static SaveLoading.ServerConfig createServerConfig(ResourcePackManager resourcePackManager, DataPackSettings dataPackSettings) {
return null;
}
@Shadow
@Nullable
protected abstract Pair<File, ResourcePackManager> getScannedPack();
private CreateWorldScreenMixin() {
super(null);
}
@ModifyVariable(method = "create(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/client/gui/screen/Screen;)V", @ModifyVariable(method = "create(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/client/gui/screen/Screen;)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/world/CreateWorldScreen;createServerConfig(Lnet/minecraft/resource/ResourcePackManager;Lnet/minecraft/resource/DataPackSettings;)Lnet/minecraft/server/SaveLoading$ServerConfig;")) at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/world/CreateWorldScreen;createServerConfig(Lnet/minecraft/resource/ResourcePackManager;Lnet/minecraft/resource/DataPackSettings;)Lnet/minecraft/server/SaveLoading$ServerConfig;"))
@ -84,22 +109,34 @@ public abstract class CreateWorldScreenMixin {
@Redirect(method = "method_41854", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/registry/DynamicRegistryManager$Mutable;toImmutable()Lnet/minecraft/util/registry/DynamicRegistryManager$Immutable;")) @Redirect(method = "method_41854", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/registry/DynamicRegistryManager$Mutable;toImmutable()Lnet/minecraft/util/registry/DynamicRegistryManager$Immutable;"))
private static DynamicRegistryManager.Immutable loadDynamicRegistry(DynamicRegistryManager.Mutable mutableRegistryManager, ResourceManager dataPackManager) { private static DynamicRegistryManager.Immutable loadDynamicRegistry(DynamicRegistryManager.Mutable mutableRegistryManager, ResourceManager dataPackManager) {
// This loads the dynamic registry from the data pack // This loads the dynamic registry from the data pack
loadedOps = RegistryOps.ofLoaded(JsonOps.INSTANCE, mutableRegistryManager, dataPackManager); RegistryOps.ofLoaded(JsonOps.INSTANCE, mutableRegistryManager, dataPackManager);
return mutableRegistryManager.toImmutable(); return mutableRegistryManager.toImmutable();
} }
/** /**
* Fix GeneratorOptions not having custom dimensions. * Load the DynamicRegistryManager again to fix GeneratorOptions not having custom dimensions.
* Taken from {@link CreateWorldScreen#applyDataPacks(ResourcePackManager)}. * Taken from {@link CreateWorldScreen#applyDataPacks(ResourcePackManager)}.
*/ */
@SuppressWarnings("JavadocReference") @SuppressWarnings("JavadocReference")
@Redirect(method = "method_41854", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/gen/WorldPresets;createDefaultOptions(Lnet/minecraft/util/registry/DynamicRegistryManager;)Lnet/minecraft/world/gen/GeneratorOptions;")) @Inject(method = "startServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/world/CreateWorldScreen;clearDataPackTempDir()V"))
private static GeneratorOptions loadDatapackDimensions(DynamicRegistryManager dynamicRegistryManager) { private void loadDatapackDimensions(CallbackInfo ci) {
GeneratorOptions defaultGen = WorldPresets.createDefaultOptions(dynamicRegistryManager); CompletableFuture<Void> future = SaveLoading.load(createServerConfig(getScannedPack().getSecond(), dataPackSettings), (resourceManager, dataPackSettings1) -> {
RegistryOps<JsonElement> registryOps = RegistryOps.of(JsonOps.INSTANCE, dynamicRegistryManager); GeneratorOptionsHolder holder = moreOptionsDialog.getGeneratorOptionsHolder();
return GeneratorOptions.CODEC.encodeStart(registryOps, defaultGen) DynamicRegistryManager.Mutable newDrm = DynamicRegistryManager.createAndLoad();
.flatMap(json -> GeneratorOptions.CODEC.parse(loadedOps, json)) DynamicOps<JsonElement> heldOps = RegistryOps.of(JsonOps.INSTANCE, holder.dynamicRegistryManager());
.getOrThrow(false, Util.addPrefix("Error parsing worldgen settings after loading data packs: ", LOGGER::error)); DynamicOps<JsonElement> newOps = RegistryOps.ofLoaded(JsonOps.INSTANCE, newDrm, resourceManager);
DataResult<GeneratorOptions> result = GeneratorOptions.CODEC.encodeStart(heldOps, holder.generatorOptions())
.flatMap(json -> GeneratorOptions.CODEC.parse(newOps, json));
return Pair.of(result, newDrm.toImmutable());
}, (resourceManager, dataPackContents, drm, result) -> {
resourceManager.close();
GeneratorOptions options = result.getOrThrow(false, Util.addPrefix("Error parsing worldgen settings after loading data packs: ", LOGGER::error));
GeneratorOptionsHolder holder = new GeneratorOptionsHolder(options, result.lifecycle().add(drm.getRegistryLifecycle()), drm, dataPackContents);
((MoreOptionsDialogAccessor) moreOptionsDialog).callSetGeneratorOptionsHolder(holder);
return null;
}, Util.getMainWorkerExecutor(), client);
client.runTasks(future::isDone);
} }
@Inject(method = "getScannedPack", @Inject(method = "getScannedPack",

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.resource.loader.client;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
import net.minecraft.client.gui.screen.world.MoreOptionsDialog;
import net.minecraft.client.world.GeneratorOptionsHolder;
@Mixin(MoreOptionsDialog.class)
public interface MoreOptionsDialogAccessor {
@Invoker
void callSetGeneratorOptionsHolder(GeneratorOptionsHolder generatorOptionsHolder);
}

View file

@ -7,7 +7,8 @@
"CreateWorldScreenMixin", "CreateWorldScreenMixin",
"FontManagerResourceReloadListenerMixin", "FontManagerResourceReloadListenerMixin",
"GameOptionsMixin", "GameOptionsMixin",
"KeyedResourceReloadListenerClientMixin" "KeyedResourceReloadListenerClientMixin",
"MoreOptionsDialogAccessor"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1