This commit is contained in:
modmuss50 2021-11-25 15:57:07 +00:00
parent 6f8dfbb3f7
commit d154e2c6fb
10 changed files with 162 additions and 92 deletions
fabric-containers-v0/src/main/java/net/fabricmc/fabric
fabric-rendering-data-attachment-v1
gradle.properties

View file

@ -100,7 +100,7 @@ public class ContainerProviderImpl implements ContainerProviderRegistry {
}
player.currentScreenHandler = screenHandler;
((ServerPlayerEntityAccessor) player).callOnSpawn(screenHandler);
((ServerPlayerEntityAccessor) player).callOnScreenHandlerOpened(screenHandler);
}
public <C extends ScreenHandler> C createContainer(int syncId, Identifier identifier, PlayerEntity player, PacketByteBuf buf) {

View file

@ -31,5 +31,5 @@ public interface ServerPlayerEntityAccessor {
void setScreenHandlerSyncId(int syncId);
@Invoker()
void callOnSpawn(ScreenHandler screenHandler);
void callOnScreenHandlerOpened(ScreenHandler screenHandler);
}

View file

@ -4,3 +4,7 @@ version = getSubprojectVersion(project)
moduleDependencies(project, [
'fabric-api-base'
])
loom {
accessWidenerPath = file("src/main/resources/fabric-rendering-data-attachment-v1.accesswidener")
}

View file

@ -0,0 +1,25 @@
/*
* 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.impl.rendering.data.attachment;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Internal
public interface RenderDataObjectConsumer {
void fabric_acceptRenderDataObjects(Long2ObjectOpenHashMap<Object> renderDataObjects);
}

View file

@ -16,105 +16,27 @@
package net.fabricmc.fabric.mixin.rendering.data.attachment.client;
import java.util.ConcurrentModificationException;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.render.chunk.ChunkRendererRegion;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.WorldChunk;
import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView;
import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachmentBlockEntity;
import net.fabricmc.fabric.impl.rendering.data.attachment.RenderDataObjectConsumer;
@Mixin(ChunkRendererRegion.class)
public abstract class MixinChunkRendererRegion implements RenderAttachedBlockView {
public abstract class MixinChunkRendererRegion implements RenderAttachedBlockView, RenderDataObjectConsumer {
private Long2ObjectOpenHashMap<Object> fabric_renderDataObjects;
private static final AtomicInteger ERROR_COUNTER = new AtomicInteger();
private static final Logger LOGGER = LogManager.getLogger();
@Inject(at = @At("RETURN"), method = "create", locals = LocalCapture.CAPTURE_FAILHARD)
private static void init(World world, BlockPos startPos, BlockPos endPos, int chunkRadius, CallbackInfoReturnable<ChunkRendererRegion> info, int i, int j, int k, int l, WorldChunk[][] chunks) {
// instantiated lazily - avoids allocation for chunks without any data objects - which is most of them!
Long2ObjectOpenHashMap<Object> map = null;
for (WorldChunk[] chunkOuter : chunks) {
for (WorldChunk chunk : chunkOuter) {
// Hash maps in chunks should generally not be modified outside of client thread
// but does happen in practice, due to mods or inconsistent vanilla behaviors, causing
// CMEs when we iterate the map. (Vanilla does not iterate these maps when it builds
// the chunk cache and does not suffer from this problem.)
//
// We handle this simply by retrying until it works. Ugly but effective.
for (;;) {
try {
map = mapChunk(chunk, startPos, endPos, map);
break;
} catch (ConcurrentModificationException e) {
final int count = ERROR_COUNTER.incrementAndGet();
if (count <= 5) {
LOGGER.warn("[Render Data Attachment] Encountered CME during render region build. A mod is accessing or changing chunk data outside the main thread. Retrying.", e);
if (count == 5) {
LOGGER.info("[Render Data Attachment] Subsequent exceptions will be suppressed.");
}
}
}
}
}
}
ChunkRendererRegion rendererRegion = info.getReturnValue();
if (map != null && rendererRegion != null) {
((MixinChunkRendererRegion) (Object) rendererRegion).fabric_renderDataObjects = map;
}
}
private static Long2ObjectOpenHashMap<Object> mapChunk(WorldChunk chunk, BlockPos posFrom, BlockPos posTo, Long2ObjectOpenHashMap<Object> map) {
final int xMin = posFrom.getX();
final int xMax = posTo.getX();
final int zMin = posFrom.getZ();
final int zMax = posTo.getZ();
final int yMin = posFrom.getY();
final int yMax = posTo.getY();
for (Map.Entry<BlockPos, BlockEntity> entry : chunk.getBlockEntities().entrySet()) {
final BlockPos entPos = entry.getKey();
if (entPos.getX() >= xMin && entPos.getX() <= xMax
&& entPos.getY() >= yMin && entPos.getY() <= yMax
&& entPos.getZ() >= zMin && entPos.getZ() <= zMax) {
final Object o = ((RenderAttachmentBlockEntity) entry.getValue()).getRenderAttachmentData();
if (o != null) {
if (map == null) {
map = new Long2ObjectOpenHashMap<>();
}
map.put(entPos.asLong(), o);
}
}
}
return map;
}
@Override
public Object getBlockEntityRenderAttachment(BlockPos pos) {
return fabric_renderDataObjects == null ? null : fabric_renderDataObjects.get(pos.asLong());
}
// Called in MixinChunkRendererRegionBuilder
@Override
public void fabric_acceptRenderDataObjects(Long2ObjectOpenHashMap<Object> renderDataObjects) {
this.fabric_renderDataObjects = renderDataObjects;
}
}

View file

@ -0,0 +1,114 @@
/*
* 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.rendering.data.attachment.client;
import java.util.ConcurrentModificationException;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.class_6850;
import net.minecraft.client.render.chunk.ChunkRendererRegion;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.WorldChunk;
import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachmentBlockEntity;
import net.fabricmc.fabric.impl.rendering.data.attachment.RenderDataObjectConsumer;
@Mixin(class_6850.class)
public abstract class MixinChunkRendererRegionBuilder {
private static final AtomicInteger ERROR_COUNTER = new AtomicInteger();
private static final Logger LOGGER = LogManager.getLogger();
@Inject(at = @At("RETURN"), method = "method_39969", locals = LocalCapture.CAPTURE_FAILHARD)
private void create(World world, BlockPos startPos, BlockPos endPos, int chunkRadius, CallbackInfoReturnable<ChunkRendererRegion> info, int i, int j, int k, int l, class_6850.class_6851[][] chunkData) {
// instantiated lazily - avoids allocation for chunks without any data objects - which is most of them!
Long2ObjectOpenHashMap<Object> map = null;
for (class_6850.class_6851[] chunkDataOuter : chunkData) {
for (class_6850.class_6851 data : chunkDataOuter) {
// Hash maps in chunks should generally not be modified outside of client thread
// but does happen in practice, due to mods or inconsistent vanilla behaviors, causing
// CMEs when we iterate the map. (Vanilla does not iterate these maps when it builds
// the chunk cache and does not suffer from this problem.)
//
// We handle this simply by retrying until it works. Ugly but effective.
for (;;) {
try {
map = mapChunk(data.method_39971(), startPos, endPos, map);
break;
} catch (ConcurrentModificationException e) {
final int count = ERROR_COUNTER.incrementAndGet();
if (count <= 5) {
LOGGER.warn("[Render Data Attachment] Encountered CME during render region build. A mod is accessing or changing chunk data outside the main thread. Retrying.", e);
if (count == 5) {
LOGGER.info("[Render Data Attachment] Subsequent exceptions will be suppressed.");
}
}
}
}
}
}
ChunkRendererRegion rendererRegion = info.getReturnValue();
if (map != null && rendererRegion != null) {
((RenderDataObjectConsumer) rendererRegion).fabric_acceptRenderDataObjects(map);
}
}
private static Long2ObjectOpenHashMap<Object> mapChunk(WorldChunk chunk, BlockPos posFrom, BlockPos posTo, Long2ObjectOpenHashMap<Object> map) {
final int xMin = posFrom.getX();
final int xMax = posTo.getX();
final int zMin = posFrom.getZ();
final int zMax = posTo.getZ();
final int yMin = posFrom.getY();
final int yMax = posTo.getY();
for (Map.Entry<BlockPos, BlockEntity> entry : chunk.getBlockEntities().entrySet()) {
final BlockPos entPos = entry.getKey();
if (entPos.getX() >= xMin && entPos.getX() <= xMax
&& entPos.getY() >= yMin && entPos.getY() <= yMax
&& entPos.getZ() >= zMin && entPos.getZ() <= zMax) {
final Object o = ((RenderAttachmentBlockEntity) entry.getValue()).getRenderAttachmentData();
if (o != null) {
if (map == null) {
map = new Long2ObjectOpenHashMap<>();
}
map.put(entPos.asLong(), o);
}
}
}
return map;
}
}

View file

@ -0,0 +1,3 @@
accessWidener v2 named
accessible class net/minecraft/class_6850$class_6851

View file

@ -7,7 +7,8 @@
"MixinViewableWorld"
],
"client": [
"client.MixinChunkRendererRegion"
"client.MixinChunkRendererRegion",
"client.MixinChunkRendererRegionBuilder"
],
"injectors": {
"defaultRequire": 1

View file

@ -25,5 +25,6 @@
],
"custom": {
"fabric-api:module-lifecycle": "stable"
}
},
"accessWidener": "fabric-rendering-data-attachment-v1.accesswidener"
}

View file

@ -1,8 +1,8 @@
org.gradle.jvmargs=-Xmx2560M
version=0.43.0
minecraft_version=1.18-pre7
yarn_version=+build.2
minecraft_version=1.18-rc1
yarn_version=+build.1
loader_version=0.12.5
prerelease=true