皮皮网

【在线游戏分析源码库】【keystone 源码克隆】【php源码icloud】源码抽离

2024-12-24 11:24:18 来源:源码录像的设备

1.代码拆分-使用SplitChunks
2.【Poco笔记】线程Thread
3.知物由学 | 端游代码保护:从原生代码到游戏引擎
4.[Angular 组件库 NG-ZORRO 基础入门] - 源码初窥: core
5.Lifecycle源码解析

源码抽离

代码拆分-使用SplitChunks

       前言

       探索代码优化的源码抽离世界,最近开始接触项目优化工作,源码抽离其中涉及三方组件的源码抽离拆分。在未进行拆分前,源码抽离可能存在两个场景:单一js文件过大,源码抽离影响缓存效率;无法有效管理第三方库。源码抽离在线游戏分析源码库利用`splitChunks`工具,源码抽离可以将模块进行分割,源码抽离并提取重复代码,源码抽离解决上述问题。源码抽离

       概念区分 - module、源码抽离bundle、源码抽离chunk

       深入理解`splitChunks`之前,源码抽离先梳理几个概念。源码抽离module:模块,源码抽离在webpack中,任何文件都可视为模块,需要配置loader将其转换为支持打包的文件。chunk:编译完成待输出时,webpack将module按特定规则组合成一个个chunk。bundle:webpack处理完chunk文件后,生成供浏览器运行的代码。

       chunk与bundle的关系

       探析chunk的构成与bundle之间的关联。chunk有两种形式:初始化(initial)chunk,即入口起点的主chunk,包含入口起点及其依赖的所有模块;非初始化(non-initial)chunk,用于延迟加载,可能在使用动态导入或`SplitChunksPlugin`时出现。keystone 源码克隆

       通过入口产生的chunk

       假设目录结构如下:index.js, another-module.js, webpack.config.js, package.json添加script配置,运行webpack并使用ndb追踪代码执行。通过命令启动浏览器,点击播放按钮执行build命令,追踪chunk到bundle的流转。

       chunk处理步骤概览

       从`Compilation`类的`seal`方法出发,首先搜集chunks,然后调用`createChunkAssets`方法生成source,为输出文件做准备;通过`compilation.emitAssets`方法记录资源信息到`compilation.assets`对象;一系列回调最终调用`onCompiled`方法,将assets信息写入输出目录,生成bundle文件。

       Demo2 - 动态导入

       将`index.js`中的lodash通过`import`方式导入,动态导入返回promise,通过`then`获取导入信息。修改`webpack.config.js`入口为单个`index.js`。源码追踪显示,初始化文件新增一个名为`index`的chunk,但在模块分析中识别到`import`方式,为`index.js`模块增加了`AsyncDependenciesBlock`标记,经过处理生成一个名为`null`的chunk。

       总结:`chunk`是源代码中的抽象,封装定义如何将模块组写入文件,而`bundle`则是输出目录的文件。

       解决隐患 - `splitChunks`配置

       在上述示例中,存在三方模块重复引用的问题。通过简单的`optimization.splitChunks`配置,实现了lodash的php源码icloud抽离,降低了单个入口文件的大小。总结使用心得,`splitChunks`主要用于代码优化,针对不同场景配置`chunks`选项,如`all`、`async`、`initial`以及自定义函数,以达到高效拆分效果。

       比较`async`、`initial`、`all`的区别

       在示例中增加`another.js`,静态导入lodash,对比`async`、`all`、`initial`的不同效果。默认情况下,`initial`影响HTML文件中的脚本标签,而`async`仅针对动态导入,`all`则考虑更多场景,适合存在复用模块的情况,但需权衡动态导入及其内部依赖的抽离。

       splitChunks.cacheGroups

       在使用`splitChunks`基础上,通过`cacheGroups`实现更细粒度的代码拆分,进一步优化项目结构。

       总结

       通过`splitChunks`配置,实现三方组件的高效管理与拆分,优化代码结构与加载效率。变声软件源码理解模块、bundle、chunk之间的关系,以及如何利用`splitChunks`与`cacheGroups`进行代码拆分与优化,是提升项目性能的关键步骤。

【Poco笔记】线程Thread

        Poco的Thread是对标准库std::thread的封装,同时它类似Java一样,提供了Runnable接口。所以使用上是对标Java的。

        与标准库不同的是,Poco::Thread创建和运行时相分离的。这一点标准库设计确实不太友好。例如下面例子。

        同样看例子

        由上面可见,使用基本跟Java类似。创建与运行也分离了。

        看一下主要的运行接口,摘自Poco1.9源码

        源码文件主要包含

        1.Thread.h/Thread.cpp

        提供外部调用接口

        在Thread.cpp中定义了两种Holder, RunnableHolder和CallableHolder。Holder技术是Poco框架中经常用到的,是对某一种类型对象的指针包装。

        Runnable为线程运行类的基类,

        Callable为带一个参数的方法

        2.Thread_POSIX.h/Thread_POSIX.cpp

        3.Thread_VX.h/Thread_VX.cpp

        4.Thread_WIN.h/Thread_WIN.cpp

        5.Thread_WINCE.h/Thread_WINCE.cpp

        这几个文件,每个文件中都定义了ThreadImpl,用于不同平台下的具体实现,Thread私有继承ThreadImp,ThreadImp用于哪一个文件由编译宏决定。

        顺便说一下POSIX系统下的实现。因为使用的是c++,当时没有thread类,所以所有的实现都是使用pthread库来实现的。具体的使用请参考pthread技术文档。

        6.ThreadLocal.h/ThreadLocal.cpp

        ThreadLocal中定义了三个类, TLSAbstractSlot类, TLSSlot类, ThreadLocalStorageç±»

        TLSAbstractSlot是基类,TLSSlot是模板类,通过模板技术包裹了具体的类型。ThreadLocalStorage是用于线程存储,具体是通过一个map来实现。

        因为1.9使用的是c++,还没有引用local_thread关键字,所以这里是通过这种方式实现。

        ThreadLocalStorage定义如下

        那么Poco::Thread的tls是如何定义的?

        源码文件比较少,主要如下文件

        1.Thread.h/Thread.cpp

        2.Thread_STD.h/Thread_POSIX.cpp/Thread_VX.cpp/Thread_WIN.cpp

        Thread.h 主要对实现类ThreadImp的包装,并定义了对外接口。

        Thread_STD.h定义了内部实现,主要提供了ThreadImpç±»

        Thread_POSIX.cpp/Thread_VX.cpp/Thread_WIN.cpp分别定义不同平台下的兼容实现

        在Thread_STD.h中定义了几个重要类型

        在Thread.cpp中增加了两种

        private修饰的ThreadData,定义了线程内部数据。 1.9中源码分别定义在各个平台实现类中,这里抽离出来定义在Thread.cpp中。较之前的定义,这里额外的是新增了std::thread指针。因为直接引用了c++中的thread,有些实现直接借助于它。

知物由学 | 端游代码保护:从原生代码到游戏引擎

       近年来,移动端游戏迅速崛起,凭借其便利性和趣味性,吸引了海量玩家。然而,为了追求更佳的游戏体验,部分玩家选择在PC上使用模拟器操控手游,虽在操作和沉浸感上有改善,但性能方面仍有所欠缺。

       为了满足这类玩家需求,游戏厂商尝试将手游与PC整合,实现跨平台体验。如《阴阳师》、《第五人格》、《荒野行动》等游戏支持PC端运行,相较于手机或模拟器,体验更佳。

       然而,外挂现象也随PC端游戏的扩展而日益严重。PC版游戏面临应用权限混乱、取证困难等问题,令厂商陷入困境,linux ramdisk源码同时为外挂提供了可乘之机。

       反外挂通常涉及静态代码保护与动态运行对抗两大部分。本文聚焦静态代码保护,探索在易盾端游反外挂代码保护中,PE代码保护的应用及面向游戏引擎的代码保护策略,进而提出一种通用游戏逻辑代码保护方案。

       1. 通用代码保护

       1.1 PE代码保护概述

       PE代码保护聚焦于原生代码保护,针对x架构的二进制文件(PE文件)进行保护。该技术已有多年历史,从世纪初开始发展,催生了如“UPX”壳等具有影响力的加固思路与加密算法。

       PE加固技术主要分为整体加密、混淆与虚拟机保护三类,旨在对抗静态分析与动态调试。

       1)整体加密:通过压缩/加密壳与附属功能实现,如IAT加密、反调试与完整性校验。

       2)混淆:包括花指令、指令变形、代码乱序与字符串加密等,旨在提升静态分析与动态调试难度。

       3)虚拟机保护:引入私有指令集,将原生汇编指令转化为虚拟运行时指令,以实现保护。

       1.2 游戏逻辑外挂原理

       从攻击者视角,实现游戏外挂,主要关注两点:关键数值修改与关键函数操纵。传统代码保护对静态分析与动态调试具有较好防御效果,但针对特定游戏逻辑篡改类外挂,效果有限。

       不同游戏引擎(如Unity3D、UE4)引入了运行时解释器,使得传统PE保护方案难以覆盖,从而提出了针对游戏引擎的保护方案。

       2. 游戏引擎保护

       易盾端游代码保护方案针对Unity3D引擎,包括Mono DLL整体加密、方法级加密、格式私有化、IL2CPP global-metadata 加密与指令抽取等技术。

       2.1 Unity3D端游代码保护

       1)Mono DLL整体加密:加密后的DLL格式改变,使用反编译工具无法解析。

       2)方法级加密:关键IL指令抽离至外部,内存中无法完整逻辑。

       3)格式私有化:关键加密信息用私有格式存储,运行时不会恢复。

       4)IL2CPP global-metadata 加密:通过自定义加密算法保护解析文件,破坏符号解析。

       5)IL2CPP 指令抽取:游戏核心代码抽离外部,配合乱序变形引擎,内存中无法完整获取。

       2.2 通用游戏引擎保护方案

       针对不同游戏引擎与开发语言,需设计通用且性能良好的保护方案。方案需兼顾通用性、性能与安全性,采用定制AST引擎解析源码,并结合混淆与少量核心代码虚拟化,以实现轻量级虚拟机保护。

       3. 端游代码保护总结

       代码保护是端游反外挂的关键,影响游戏体验与安全性。然而,仅依赖代码保护不足以应对所有外挂挑战。未来,将深入探讨运行时反外挂策略,为游戏安全提供更全面的解决方案。

[Angular 组件库 NG-ZORRO 基础入门] - 源码初窥: core

       在探索和了解了典型组件的源码之后,我们进一步深入 NG-ZORRO 组件库的核心结构,发现了一个关键的策略来解决组件间共用属性、功能导致的重复编写问题。NG-ZORRO 支持近 种组件,为避免每种组件都需要重复定义相同的属性或功能,开发团队采用了将公共方法和定义抽离至 `core` 文件夹的策略。

       当处理组件的通用属性时,我们发现像 `nzSize` 这样的属性在多个组件如 `Input` 和 `Button` 中被广泛使用。解决这一问题的方法在于引入 `types` 文件夹,这个文件夹记录了哪些组件支持特定属性,便于我们查询和重复利用。

       动画效果是 Angular 开发中常见的元素,Angular 官方文档提供了详尽的指南。NG-ZORRO 提供了多样化的动画,使页面元素呈现丰富的动态变化。例如在 `Collapse` 折叠面板组件中,通过 `nzActive` 属性操控动画状态,实现元素的展开与收起效果。这一功能在实际开发中非常实用,使用动画使页面交互更加直观。

       某些组件,如 `Tag`,在其动态删除操作中应用了淡入淡出动画,该动画机制相较于需要单独配置的状态传递更为简便,直接提升视觉效果和用户体验。NG-ZORRO 内含多种动画类型,如 `moveUpMotion` 和 `slideMotion`,通过探索源码可以轻易找到使用方式。

       对于不希望使用动画的场景,NG-ZORRO 提供了 `NzNoAnimationDirective`,允许开发者在模板层面对特定元素禁用动画效果。通过替换 `BrowserAnimationsModule` 为 `NoopAnimationsModule`,可实现全局禁用动画。

       总结这一系列核心文件夹——`core` 包含了如 `types` 和 `animations` 等内容,对于项目开发而言,应考虑抽离公共部分,实现跨组件复用,以减少代码冗余和提高开发效率。通过借鉴 NG-ZORRO 的实践,开发者可以优化代码结构,提升组件复用性,同时保持代码的简洁性和易维护性。

Lifecycle源码解析

       作者:Gs 转载地址: /post/

       1、猜想

       如果是我们实现Lifecycle的功能,我们会如何设计?

       2、入口

       既然Activity或者Fragment作为生命周期的所有者,并且在他们中增加了LifecycleObserver,那么我们就从Activity或者Fragment作为探索Lifecycle原理的入口。在Activity或者Fragment中使用Lifecycle时,我们通常会看到如下代码:

       我们进入getLifecycle()方法。注:以Activity中的代码为例。

       这是Activity的父类ComponentActivity中的代码:

       getLifecycle()返回的mLifecycleRegistry,直接使用new创建。

       LifecycleRegistry的构造方法必须传递LifecycleOwner参数。而ComponentActivity已经实现了LifecycleOwner接口,所以可以直接

       LifecycleOwner接口很简单,只有一个getLifecycle()抽象方法。

       所以我们的Activity或者Fragment作为生命周期的所有者,同时也实现了LifecycleOwner接口,通过getLifecycle()方法获取LifecycleRegistry对象,LifecycleRegistry也就是实现生命周期分发的类。

       LifecycleRegistry在lifecycle-runtime包中。

       3、生命周期事件分发

       我们看到Activity的父类ComponentActivity实现了LifecycleOwner接口,并且创建了LifecycleRegistry对象。那么生命周期的分发也应该在ComponentActivity的各个生命周期方法中吧。然而,我们看到ComponentActivity中只复写了onCreate()方法,没有其他生命周期方法。

       里面有一句代码

       ReportFragment不是在上面中和LifecycleRegistry在lifecycle-runtime包中一起出现的吗?所以ReportFragment一定是为了实现Lifecycle功能。

       injectIfNeededIn()方法很简单,就是创建ReportFragment加入到Activity中。但是它里面包含了各个生命周期方法,而且都调用了分发方法dispatch()。参数就是我们在自定义LifecycleObserver中给方法加的注释事件。

       至此,我们找到了生命周期事件的分发方法dispatch(Event event),方法内部使用LifecycleRegistry的handleLifecycleEvent(event)分发事件。上面我们也说过LifecycleRegistry就是实现生命周期分发的类。而ReportFragment的作用就是获取生命周期而已,因为Fragment生命周期是依附Activity的。好处是把这部分逻辑抽离出来,实现Activity的无侵入。如果你对加载库Glide比较熟悉,就会知道它也是使用透明Fragment获取生命周期的。

       4、生命周期事件处理

       LifecycleRegistry继承自Lifecycle。

       Lifecycle使用两种主要枚举跟踪其关联组件的生命周期状态:

       Event触发的时机:

       您可以将状态看作图中的节点,将事件看作这些节点之间的边。上一节中,我们知道ReportFragment生命周期发生变化时,都会调用LifecycleRegistry中的handleLifecycleEvent()方法。因此,我们先看一下handleLifecycleEvent()方法。