跳转至内容
  • 版块
  • 最新
  • 热门
  • 标签
  • 群组
  • Feed
皮肤
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • 默认(Zephyr)
  • 不使用皮肤
折叠
品牌标识

VariedMC 魔改论坛

真冬M

真冬

@Mafuyu
Modding
关于
帖子
6
主题
2
分享
0
群组
1
粉丝
1
关注
3
Blog

帖子

最新 最佳 有争议的

  • 已经,没有modding的理由了
    真冬M 真冬

    @不是椰浆 尼的牌子豪多

    壁画石窟

  • 强制加载并渲染特定区块
    真冬M 真冬

    @忆然 除了勤劳跟踪狂,我都不知道在哪能用上2333

    灵感大王 区块 渲染 forge mixin

  • 强制加载并渲染特定区块
    真冬M 真冬

    我也不知道有什么实用价值但反正先写着。

    思路:
    1)强加载区块
    2)主动发送区块数据
    3)强制渲染区块

    强加载

    首先是强加载区块,我写了一个简单的类实现:

    public class ChunkLoader {
        private static final HashMap<String, ArrayList<ChunkPos>> loaders = new HashMap<>();
    
        public static void add(ServerLevel level, ChunkPos center) {
            String key = level.dimension().toString();
            if (!loaders.containsKey(key)) loaders.put(key, new ArrayList<>());
            if (!loaders.get(key).contains(center)) {
                loaders.get(key).add(center);
                ForgeChunkManager.forceChunk(level, MODID, center.getMiddleBlockPosition(0), center.x, center.z, true, true);
            }
        }
        public static void removeAll(ServerLevel level) {
            String key = level.dimension().toString();
            if (!loaders.containsKey(key)) return;
            if (loaders.get(key).isEmpty()) return;
            Iterator<ChunkPos> iterator = loaders.get(key).iterator();
            while (iterator.hasNext()) {
                ChunkPos center = iterator.next();
                iterator.remove();
                ForgeChunkManager.forceChunk(level, MODID, center.getMiddleBlockPosition(0), center.x, center.z, false, false);
            }
        }
    }
    

    写得比较草率,主打一个能用就行。
    这是在服务端运行的,区块要加载到服务端的区块缓存(ServerChunkCache)中,才能发送对应的区块数据。
    如果想安全地强加载区块,请使用Ticket系统,我这里只是分享思路,就简单用forceChunk方法敷衍过去了。

    发送区块数据

    这部分是mixin得到的,把ChunkMap中的playerLoadedChunk方法拿出来用,最终发送的是一个ClientboundLevelChunkWithLightPacket包。

    @Mixin(value = ChunkMap.class)
    @Implements(@Interface(iface = IChunkMap .class, prefix = "lazy$"))
    public abstract class ChunkMapMixin implements IChunkMap {
        @Shadow @Nullable protected abstract ChunkHolder getVisibleChunkIfPresent(long p_140328_);
    
        @Shadow protected abstract void playerLoadedChunk(ServerPlayer p_183761_, MutableObject<ClientboundLevelChunkWithLightPacket> p_183762_, LevelChunk p_183763_);
    
        public void loadLevelChunk(ServerPlayer player, ChunkPos chunkPos) {
            ChunkHolder chunkholder = this.getVisibleChunkIfPresent(chunkPos.toLong());
            if (chunkholder == null) return;
            LevelChunk levelchunk = chunkholder.getTickingChunk();
            if (levelchunk == null) return;
            this.playerLoadedChunk(player, new MutableObject<>(), levelchunk);
        }
    }
    

    默认情况下,区块更新是惰性的,要使用playerLoadedChunk方法,除了单独拎出来用,也可以插入到move方法中:

        @Inject(method = "move", at = @At("HEAD"))
        private void justMove(ServerPlayer player, CallbackInfo ci) {
            loadLevelChunk(player, ChunkPos.ZERO);
        }
    

    这个方法是默认情况下玩家更新区块的方法,插入到这里,等同于为玩家更新额外的区块。

    强制渲染

    这部分是最复杂的,需要进行非常非常多的mixin。
    让我们一步步走。
    首先,客户端收到ClientboundLevelChunkWithLightPacket包后需要进行处理,将区块数据存进客户端的区块缓存(ClientChunkCache)中,等待帧渲染将它抓出来渲染。
    这里出现了第一次渲染判定,读取区块缓存的时候,要检测区块坐标是否在视距范围内。
    那么我们将它干掉。

    @Mixin(targets = "net.minecraft.client.multiplayer.ClientChunkCache$Storage")
    public class ClientChunkCache$StorageMixin {
        @Inject(method = "inRange", at = @At("HEAD"), cancellable = true)
        private void modifyRange(int x, int z, CallbackInfoReturnable<Boolean> cir) {
            if (new ChunkPos(*****).equals(new ChunkPos(x, z))) {
                cir.setReturnValue(true);
            }
        }
    }
    

    如果轮到检测的这个区块和你想渲染的区块是同一个,就强制通过检测。
    说完了缓存,接下来就是渲染。
    最核心的渲染是在LevelRenderer里,这个类超级超级长。
    我其实不太想介绍这个部分,因为Embeddium在这个类中用Overwrite重写了非常多的方法,比如我下面要说的setupRender:

    @Mixin(LevelRenderer.class)
    public class LevelRendererMixin {
        @ModifyVariable(method = "setupRender",at = @At("STORE"), ordinal = 0)
        private double modifyX(double x) {
            // 改一下x。
            return x;
        }
        // 把y和z也改了,此处省略。
    }
    

    这么做可以强制转移渲染的中心点,转移到你想渲染的地方。
    是的,这个方法被重写了,重写后以客户端实例的摄像机位置为渲染中心了。

    Minecraft.getInstance().gameRenderer.getMainCamera().getPosition();
    

    如果要修改渲染中心就去改摄像机位置吧,反正强制渲染特定区块大概率是伴随着摄像机移动的。
    如果要考虑不安装Embeddium的情况,就用MixinPlugin区分一下:

    public class MixinPlugin implements IMixinConfigPlugin {
        @Override
        public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
            if (mixinClassName.equals("com.mafuyu404.examplemod.mixin.LevelRendererMixin")) {
                return !isClassLoaded("me.jellysquid.mods.sodium.client.SodiumClientMod"); // Sodium/Embeddium核心类
            }
            return true;
        }
    
        private static boolean isClassLoaded(String className) {
            try {
                Class.forName(className, false, getClassLoader());
                return true;
            } catch (ClassNotFoundException e) {
                return false;
            }
        }
        // 必要的补全此处省略。
    }
    

    不过,真有人会不装Embeddium吗?我测试的时候32视距给我提了快100帧……

    在处理完区块渲染,其实还有最后一步,那就是实体渲染。

    @Mixin(targets = "net.minecraft.server.level.ChunkMap$TrackedEntity")
    public class ChunkMap$TrackedEntityMixin {
        @ModifyVariable(method = "updatePlayer", at = @At(value = "STORE"))
        private Vec3 wwa(Vec3 direction) {
            // 改一下direction。
            return direction;
        }
    }
    

    很多优化模组都有远处实体剔除的功能,实体渲染其实可以看作与区块渲染独立。
    上面这个方法叫updatePlayer,其实是指更新本地玩家区块数据中的实体追踪数据,这里面会计算实体与玩家的距离,从而决定实体是否要渲染。
    direction其实就是玩家坐标与实体坐标构成的向量,设为零向量即可。

    结语

    内容就这么多了,其实是相当鸡肋的东西,之后想到什么再来补充吧。

    灵感大王 区块 渲染 forge mixin

  • 程序猿梗图
    真冬M 真冬

    Image_1061376123293768.jpg
    Image_1061371682756269.jpg
    Image_1061332528610442.jpg
    Image_1061301561331800.jpg
    Image_976141317588545.jpg
    1638441314448-2021-12-02-18.23.38.webp
    1648277883321-22118451-3961-4997-ae8b-de32f03ef7f9-qq图片20220326145519.webp

    壁画石窟

  • 怎么没有板式斑块
    真冬M 真冬

    原本我发了一个,被重置了捏。

    壁画石窟

  • 我永远喜欢kubejs!
    真冬M 真冬

    @忆然 但是真冬永远不会……

    壁画石窟
  • 1 / 1
  • 登录

  • 没有帐号? 注册

  • 登录或注册以进行搜索。
  • 第一个帖子
    最后一个帖子
0
  • 版块
  • 最新
  • 热门
  • 标签
  • 群组
  • Feed