关于244128133

Unity3D游戏程序员

关于Unity安卓包违反了谷歌设备和网络滥用政策,问题的解决( CVE-2025-59489)

特色

运营反馈我们的包被谷歌警告了,说我们的包违反了网络滥用政策,具体原因是说Unity2017之后的版本有一个漏洞,但是没有说是什么漏洞,这真是大海捞针,于是通过不断的查找,发现确实有其他人也遇到这个问题,具体漏洞就是( CVE-2025-59489),所以决定记录一下。

具体漏洞详情

CVE ID: CVE-2025-59489

发现日期: 2025年6月4日

发现者: GMO Flatt Security Inc. 的 RyotaK

补丁发布日期: 2025年10月2日

受影响的操作系统:请参阅受影响的操作系统表

受影响版本:请参阅 Unity 编辑器版本表

已打补丁的版本:请参阅 Unity 编辑器版本表

漏洞类型: CWE-426:不受信任的搜索路径

严重程度:

CVSS评分: 8.4

CVSS 向量字符串: CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

开发潜力

该漏洞可能允许在运行 Unity 应用程序的终端用户设备上执行本地代码并访问机密信息。代码执行权限将限制在易受攻击应用程序的权限级别内,信息泄露也将仅限于易受攻击应用程序可访问的信息。目前没有证据表明该漏洞已被利用,也没有对用户或客户造成任何影响。

Unity编辑器版本表

使用指定版本的 Unity 编辑器在补丁版本之前构建的应用程序被认为存在漏洞。

当前支持版本

受影响版本已打补丁版本
6000.3全部6000.3.0b4
6000.2全部6000.2.6f2
6000.0 升全部6000.0.58f2
2022.3 xLTS全部2022.3.67f2
2021.3 xLTS全部2021.3.56f2

我们已将修复程序扩展到已停止支持的 Unity 编辑器版本,包括 Unity 2019.1 及更高版本。

已停止支持的版本

受影响版本已打补丁版本
6000.1全部6000.1.17f1
2023.2全部2023.2.22f1
2023.1全部2023.1.22f1
2022.3 LTS全部2022.3.62f2
2022.2全部2022.2.23f1
2022.1全部2022.1.25f1
2021.3 LTS全部2021.3.45f2
2021.2全部2021.2.20f1
2021.1全部2021.1.29f1
2020.3全部2020.3.49f1
2020.2全部2020.2.8f1
2020.1全部2020.1.18f1
2019.4 LTS全部2019.4.41f1
2019.3全部2019.3.17f1
2019.2全部2019.2.23f1
2019.1全部2019.1.15f1
2018.4全部不适用
2018.3全部不适用
2018.2全部不适用
2018.1全部不适用
2017.4全部不适用
2017.32017.3.0b9+不适用
2017.22017.2.0p4+不适用
2017.12017.1.2p4+不适用

解决方案其实有2种:

第一是升级你的引擎,具体版本请查看以上官方公布的。

第二就是使用Unity提供的工具给你的安卓包重新打一个包。

打补丁方法如下:

Unity Windows版补丁

Mac版补丁(Apple Silicon Mac)

Mac版补丁(适用于英特尔 Mac)

Linux版补丁

以windos版为例


1.解压压缩包后打开此执行文件

2.找到自己安装包路径,然后点击修补

注意:
1.安装包路径不要有中文

2.版本代码要比原来的高,查询版本可以看到当前版本,如果想保持版本不变,就填0

Static Batching、Dynamic Batching 、GPU Instancing的对比

  1. 什么是 GPU Instancing?
    GPU Instancing(GPU 实例化)是一种现代图形渲染技术,允许你用一次 Draw Call在 GPU 上同时渲染大量相同 Mesh 和材质但变换(位置、旋转、缩放)不同的物体。

典型应用:大面积草地、森林、士兵、金币、粒子等。

  1. 原理简述
    传统渲染:每个物体一个 Draw Call,CPU 需要多次提交渲染指令,效率低。
    Instancing:CPU 只发一次 Draw Call,告诉 GPU:“这是同一个模型和材质,但有 N 个实例,每个实例有不同的变换矩阵(和可选参数)。”
    GPU 在渲染时自动为每个实例应用不同的变换和属性。
  2. Unity 中的 GPU Instancing
    开启方式:在材质面板勾选“Enable GPU Instancing”即可。
    代码方式:使用 Graphics.DrawMeshInstanced、DrawMeshInstancedIndirect 等 API。
    Shader 支持:Shader 里要支持 instancing(Unity 标准 Shader 已支持)。
  3. 优点
    极大减少 Draw Call 数量,大幅提升渲染效率。
    CPU 负担小,适合大量重复物体。
    支持每个实例不同的变换、颜色等属性(通过 instanced properties)。
  4. 局限与注意事项
    所有实例必须用同一个 Mesh 和材质(但可以通过 instanced properties 传递不同参数)。
    不支持完全不同的模型或材质。
    Shader 必须支持 Instancing。
    每次 Draw Call 的实例数量有上限(通常 500~1000,受平台和 API 限制)。

微信小游戏性能评测标准

为什么需要性能评测标准?

微信小游戏性能评测标准建立的初衷是希望能引导开发者优化相关性能数据,提升用户体验。 评测标准根据小游戏整体的性能数据表现,结合操作系统、机型分档、网络条件等多种维度建立。

开发者需要关注哪些性能指标

从小游戏的运行周期来看,主要由启动和运行两个阶段产生性能问题。

  • 启动阶段:启动时长,该数据将显著影响用户打开留存率
  • 运行阶段:内存峰值、内存Crash率、CPU占用、流畅度、网络等
  • 其他兼容性问题,包括JS异常、黑屏等严重问题

评测环境与方法

从游戏的生产过程来看,我们主要从开发与现网两个环境进行评测。

开发阶段:

  • 评测过程的客观环境更为稳定(比如固定的机型基线,网络环境等)
  • Profile数据更为详细,方便掌握性能细节

现网阶段:

  • 基于统计角度进行评测,从整体采样数据取反映游戏质量
  • 覆盖开发测试阶段无法预估的业务场景,比如网络异常、特定条件下的JS异常等

评测标准细则

品类评测标准

评测标准更新时间:2024-08-05(历史现网标准请查阅历史评测标准

评测标准依赖于现网真实玩家上报的性能数据,并结合游戏所属品类进行分类统计。

品类游戏玩法评测标准
大盘全部大盘评测标准
休闲消除、答题、模拟经营、塔防、捕鱼、益智休闲品类评测标准
角色卡牌、MMO、ARPG、回合、战争策略角色品类评测标准
棋牌棋类、牌类棋牌品类评测标准
动作跑酷、竞速、音乐舞蹈、益智、飞行射击、体育、对战动作品类评测标准
竞技MOBA、枪战、桌游、对战竞技品类评测标准
其他其他其他品类评测标准

评测方法

性能基线

评测小游戏性能首先需要确定性能基线, 即先确定机型设备条件,开发者可通过机型档位映射获取机型档位的参考机型。

现网阶段

性能报告

为了能够帮助开发者快速了解游戏整体的性能情况,平台通过对评测标准和游戏性能数据的整合,面向开发者提供一个较为全面的大盘性能监控系统,详细可通过 性能监控系统 进行了解和使用。

性能数据

开发者可通过 小游戏数据助手(数据-性能分析)或 MP-研发工具箱-性能数据 获取游戏的现网玩家的性能采集数据:

开发阶段

此阶段为当前小游戏新版本还未上线时进行评测的方法,开发者可以利用PerfDog 或小游戏云测试进行数据获取,并参照性能基线和云测性能标准进行性能验证。

云测试评测标准

评测标准更新时间:2024-08-05(历史云测试评测标准请查阅历史评测标准

评测工具
PerfDog

测试方法说明:
1)启动性能:采用录屏分帧方法获取,取10次测试平均值
2)运行性能:完成游戏主流程对局5~10min, PerfDog记录性能数据并上传,取平均值,每种机型测试3组数据再取平均,内存峰值取最大值

小游戏云测试

云测试服务是一套完整易用的在线测试服务,以帮助开发者更高效、更全面地进行自动化游戏性能测试、兼容性测试。更多详情可查阅小游戏云测试

影响性能的客观因素

机型分档

更新时间:2024-08-05(历史机型分档占比请查阅文档

微信小游戏的玩家所使用的机型设备也是千差万别,但机型设备又是极为影响性能评测的一个因素。因此,我们需要对众多的机型设备进行一致性分档,唯有此才能更好的定位出现性能瓶颈的设备。
目前我们的机型分档主要参考机型的CPU、GPU、内存等硬件因素进行分档,将设备分为高中低三档,涵盖>99%以上用户数据,开发者可通过如下途径获取平台用户设备的相关数据:

网络环境

目前微信小游戏网络主要为WiFi和5G类型,更多网络概况可通过《微信小游戏开发者技术手册-设备兼容篇》 进行了解。

微信版本与基础库版本

微信客户端iOS与Android以各自不同的迭代速度更新版本,基础库与客户端之间的关系可查阅《基础库》
公共库版本迭代节奏较快,每个版本都会带来新特性与已知BUG的修复。现网的版本分布可查阅《基础库版本分布》

微信小游戏开发者技术手册-安全篇

特色

一、代码加固

为提高微信小游戏代码的安全性,平台针对小游戏游戏场景提供了定制化的深度保护功能,可实现对小游戏前端代码混淆,以防止代码暴露,提高攻击者阅读、二次开发成本,一定程度上也能提高黑产制作外挂的成本;还为代码增加了水印能力,防止核心代码被搬运或二次上线到其它平台。

“游戏深度保护” 插件只需在微信开发者工具中直接进行安装即可,操作便捷,无需额外耗时。建议涉及小游戏中的核心算法逻辑、版权内容等需要安全保护的内容,都可以进行深度保护。本插件由小游戏平台提供,不会泄露原代码。开发者也应注意自身开发规范的严谨性,保证游戏的代码质量,保护游戏用户账号安全。

以下是插件各项能力以及使用介绍。

1.1 代码混淆

代码混淆是利用抽象语法树(AST)解析,对代码进行功能等价的转换。主要做两件事情:名字擦除、结构打乱。

名字擦除是指将代码中的变量名、函数名、类名进行重命名,将属性名、常量值等隐藏起来。经过名字擦除的代码,搜索不到原代码中的名字。可以有效地规避攻击者做关键字搜索,定位代码逻辑。

结构打乱是指将代码的控制流等价转换,比如对象定义、类的定义、以及块内的代码,进行打乱重排。经过结构打乱后的代码,很难分析它的执行流程,可以有效地防止攻击者读懂代码或预测逻辑。

以下分别是示例代码和经过本插件混淆后的代码。可以看到,原代码中的变量、属性、字面量等信息皆被擦除或隐藏,原代码结构也被打乱,很难分析执行顺序。

1.2 代码水印

代码水印是在代码混淆时,放置到代码中一段隐蔽且唯一的信息。它具备以下特点:

  • 隐蔽性:水印本身也是代码,放在混淆代码中,参与运行,具备比较强的隐蔽性。
  • 等价性:水印不会影响原代码逻辑。
  • 唯一性:水印是根据微信小游戏的身份、加水印时间等信息经过加密生成的,是唯一的。

水印在一定程度上可以证明开发者与游戏的关联关系,从而用于辅助判断代码与开发者的归属关系。当开发者怀疑加固的代码被搬运到其它平台时,开发者可以寻求平台帮助提取水印以用于辅助判断。

1.3 体积&性能

根据大盘已有加固案例:平均体积膨胀在 30% 以内。如果原代码本身比较小,膨胀比率可能略大一些。原代码比较大时(>1M),膨胀比例会接近于前述值;性能、cpu、内存几乎不变。

如果原代码已经是压缩甚至是混淆过的代码,那么膨胀率可能会比较高。建议开发者直接使用源代码进行加固。

1.4 如何使用

申请白名单,下载并安装 1.06.2401020 或以上版本的开发者工具、安装 “游戏深度保护” 插件即可使用。详细说明见:游戏深度保护 | 微信开放社区(文章置顶评论有申请白名单方式)。

二、内容安全升级开放

基于游戏领域海量数据积累和多年行业经验,研发游戏垂类的智能模型和定制策略方案,高效过滤游戏领域常见的谩骂、低俗、营销广告等内容及各种文字对抗变种。

2.1 单日限额提升

当前很多小游戏单日UGC文本数已经超过接口msgSecCheck的200w/day的限额,特别是小游戏周末高峰做活动期间UGC量级可能突增好几倍,导致小游戏的晚高峰UGC内容无法过内容安全审核,存在非常大的安全风险。

因此平台单独提供一个扩大限额(1000w/day)的新接口供超额的小游戏开发者使用,新接口和老接口(小程序和小游戏共用的接口msgSecCheck)所有协议完全一样,开发者使用切换时只需要更改调用链接即可,切换后不用再请求老接口。同时新接口还有三个升级优化点:

  • 新接口平均耗时降低300ms,提升聊天互动场景的用户体验
  • 取消”用户未在近两小时访问小游戏”的请求限制
  • 取消”不合法的openid”用户请求限制,也就是非微信端用户的UGC内容也可以访问
  • 为了防止刷量和攻击,新接口使用需要开发者联系小助手进行申请。
  • 目前开放了文本接口,图片新接口预计2024年内开放。 (取消限频,取消用户限制,增加低俗软色情能力)。

小游戏文本内容安全新接口:https://api.weixin.qq.com/wxa/game/content_spam/msg_sec_check?access_token=ACCESS_TOKEN

仅url路径不同,其他输入输出参数可参考老接口msgSecCheck文档

2.2 游戏垂类专属

除了红线类(敏感、色情、违法等)内容的基础识别能力模型之外,游戏业务目前更多更常见黄线类的辱骂攻击低俗挑逗灰产广告引流三大类。特别是在游戏公屏群聊,私聊互动,社区帖子评论场景,昵称签名等场景下会存在大量谩骂、低俗内容,这些都非常容易损害正常用户体验和小游戏的口碑,还会存在玩家投诉和监管的压力。此外在小游戏中营销广告引流内容批量刷屏时,也会损害小游戏开发者利益。

基于多年游戏行业的数据经验积累,基于大数据训练更适合游戏的文本模型和定制安全策略,目前具备以下内容识别能力:

  • 辱骂攻击,低俗挑逗,软色情文本
  • 变种辱骂低俗文本:包括同音、谐音、同义词、emoji、拼音、缩写等组合对抗文本
  • 群聊和评论场景拆字叠楼对抗文本 (非实时)
  • 灰黑产广告引流内容
  • 文本语种类型识别
  • 文本无意义内容

同时针对黑灰产广告引流内容,每天还会从发表内容,发表频次,发表行为等多维度发现可疑内容,并经过人工审核确认后加词和加黑种子快速与黑灰产对抗。此外还会根据日常发现的灰产引流样本来定期更新文本营销引流模型,持续拦截新的灰黑产引流变种内容。

  • 上述游戏垂类的黄线类内容识别能力已在老接口msgSecCheck上线生效,默认生效辱骂、低俗、广告引流。

2.3 策略可定制化

当前小游戏种类,产品形态,数量都非常多样性,因此通用的内容识别能力和模型保障在小游戏整体数据上的效果。针对一些特殊的、个性化的小游戏内容安全需求,可以切换小游戏内容安全新接口的方式接入,在黄线类内容的识别能力、策略上可以进行定制化调整和运营。

例如,某小游戏开发者可能想要将一些无意义、灌水刷经验的评论内容进行沉底,避免影响小游戏内社区的UGC内容质量,这时候就可申请配置”无意义文本模型”。或者开发者不想要管控一些日常的广告内容(可能存在一些活动广告),可以申请取消”文本营销引流”相关模型策略。更多定制需求可以联系小助手

  • 需要切换新接口才能使用策略定制化能力,并且可定制范围只限于黄线内容。

三、小游戏对抗外挂实践

3.1 认识小游戏外挂

不管是app游戏,还是微信小游戏,都很容易面临外挂的攻击,外挂对游戏造成的影响主要有:

  • 用户投诉
  • 影响游戏公平性(如排行榜)
  • 游戏正常收益受损

就目前而言,小游戏外挂可以分为两类,协议挂和内存挂,本文将分别介绍这两类外挂,以及一些对抗方式。

3.1.1 协议挂

黑产利用小游戏客户端JS代码泄露+中间人攻击抓包的方式,获得小游戏与开发者后台之间通信的所有细节,从而实现篡改游戏远端存储数据或者实现脱机的一种外挂,常用手法:

  • 反编译获取小游戏源代码逆向
  • 中间人攻击抓包分析参数

部分偏离线玩法的小游戏提供了存档功能,由于代码未混淆,且部分游戏后台未对用户数据做校验,游戏内的存档数据可以被任意修改,并重新签名覆盖,如下图,游戏的存档数据为一串json字符串,通过逆向游戏代码,提取出加密和解密函数,可以任意修改游戏数据后覆盖存档。

 

3.1.2 内存挂

黑产利用内存搜索工具,定位游戏内存数值并直接修改的一种外挂,常用工具:

  • PC端:CheatEngine
  • 安卓端:GG修改器

以飞机大战举例,修改游戏内数值:

3.2 外挂对抗

3.2.1 协议挂对抗实践

协议挂能够成立有两个前提:

  • 攻击者反编译获取小游戏代码逆向逻辑
  • 攻击者利用中间人攻击抓包分析游戏与后台的协议

其中反编译获取小游戏代码逻辑,可以使用第一章中的 游戏深度保护插件 来对抗。针对抓包,则有三种方案可以进行对抗,对抗思路如图:

3.2.1.1 HTTPS加密网络通道

小程序加密网络通道 | 微信开放文档

示例代码如下,开发者可以选择在游戏关键逻辑处,比如用户数据存档,游戏内活动等重要功能时对传输的数据加密,避免被攻击者直接阅读明文窃取重要信息。

const somedata = 'xxxxx'
const userCryptoManager = wx.getUserCryptoManager()
userCryptoManager.getLatestUserKey({
success({encryptKey, iv, version, expireTime}) {
const encryptedData = someAESEncryptMethod(encryptKey, iv, somedata)
wx.request({
data: encryptedData,
success(res) {
const decryptedData = someAESDEcryptMethod(encryptKey, iv, res.data)
console.log(decryptedData)
}
})
}
})

在开发者后台则通过后台接口来获取解密的密钥。

curl -X POST "https://api.weixin.qq.com/wxa/business/getuserencryptkey?access_token=ACCESS_TOKEN&openid=OPENID&signature=SIGNATURE&sig_method=hmac_sha256"

3.2.1.2 TCP协议

如果有进一步的要求,可直接在TCP层面实现,参考TCPSocket | 微信开放文档

3.2.1.3 Donuts网关方案

Donuts 提供了微信私有链路服务,可用于提升小游戏关键网络请求的安全性。

产品介绍

const gateway = wx.cloud.services.Gateway({
// 接入域名,可以在网关控制台中获取
domain: 'xxxxxxx.sh.wxcloudrun.com',
})
// 赋值到 cloud 对象上,方便后续调用
wx.cloud.gateway = gateway
 
wx.cloud.gateway.call({
// 去除域名后的接口路径,中文需要转译
path: `/api/save`,
// 请求方法
method: 'POST',
// 数据
data: 'data'
}).then(result => {
console.log('微信网关访问结果:', result)
})

3.2.2 内存挂对抗

内存挂本质上是一些数值被外部修改器给修改了,为什么会被修改呢?因为在v8引擎中,整数的数值是通过SMI结构来存储的,其最后一位数值是用来作为标记的,标识是否为堆栈对象或者Smi。因此,游戏的内存数值与游戏展示出来的数值存在两倍的关系,即:内存内数值 = 游戏内数值*2,不少外部经验贴也是这么写的:

如图,某游戏内的数值变化代码如下,对象里的数值可以直接被内存搜索修改

3.2.2.1 数值篡改感知

基于此,黑产制作了多款如秒杀、加血、加伤害等的外挂,其本质都是搜索关键数值,并直接修改,但是黑产的外挂工具并不能实现精确定位,毕竟大部分外挂使用者不懂得如何通过一步步的操作来筛选无用的内存值。因此黑产工具有一个特点,就是只搜一个或多个关键数值,然后一次性全部修改。

因此开发者可以考虑,针对关键的数值做双写校验逻辑,即同时写值和写入校验(如hash值)。然后定时、或者在关键业务逻辑处校验:一旦数值与校验值不匹配,则可以认为游戏被外挂攻击了,做进一步动作。

/////////////////////////////// 感知篡改需要用到的 hash 函数和 检查函数
function MyHash(val){
return val + 10;
}
 
function CheckCoin(obj){
if(obj.coin_hash !== MyHash(obj.coin)){
console.info("检测到外挂")
// 继续做其它事:退出游戏,或者上报等
}
}
 
///////////////////////////// 以下是原业务逻辑代码区
let obj = {}
 
// 写关键数值时, 也同步写一个校验值
obj.coin = 10;
obj.coin_hash = MyHash(obj.coin) // 写校验值
 
 
// 感知内存篡改方式1,定时每五秒检查一次
setInterval(CheckCoin, 5000, obj)
 
// 感知内存篡改方式2,在关键业务逻辑处检查
function uploadCoin(obj){
// 在上传云端金币时,先检查一次
CheckCoin(obj);
// 校查无问题,再做剩余逻辑
}

3.2.2.2 数值隐藏

Proxy 是 es6 引入的特性,其关键性质是,对一个代理对象上的属性读取或写入的动作,都可以被拦截转发到自定义逻辑。借此可以实现数据在内存中的隐藏, 从而隔断搜索,我们来看一个最简单的模型,即一个对象 obj,和对应的金币属性coin,示例代码如下:

let obj = {}
 
let p_obj = new Proxy(obj, {
get(target, key, receiver) {
// 如果为数值,则返回减7
if (typeof target[key] === 'number') {
return target[key] - 7
}
return Reflect.get(target, key ,receiver)
},
set(target, key, value, receiver) {
// 如果为数值,则写入加7
if (typeof value === 'number') {
value += 7
}
return Reflect.set(target, key, value, receiver)
}
 
});
 
// 后续所有的操作都针对代理对象
p_obj.coin = 10
 
// 不影响原有的逻辑
console.assert(p_obj.coin === 10, 'p_obj.coin === 10')
 

通过给原有的对象套上一层代理,从而实现游戏运行时,展示在界面上的数值,与实际存储在内存中的数值是不一样的。这就达到了阻断搜索的目标。注意 Proxy 是 es6 的特性,如果要兼容 es5,那么需要用 defineProperty 来等价实现,或者使用 es6 转 es5 工具支持。

 

3.3 其他需要注意的安全问题

3.3.1 游戏内禁止内置GM逻辑

游戏正式版发布的代码后禁止含有gm开关等逻辑,黑产可修改代码并重新打包替换,如下图是某游戏代码内置了gm的测试逻辑,导致被黑产利用:

3.3.2 保护游戏的自定义登录态

  • 重复使用wx.login code且未校验绑定关系

小游戏开发者应正确地使用 code,指引。如下图是某游戏存在的问题:

  1. 对外暴露了一个接口,可重复使用wx.login的code
  2. 开发者后台未实际校验code与openid的对应关系

开发者第一次使用wx.login获取到的code登录,获得用户的openid,第二次重复使用code+openid查询用户自定义登录态

但实际测试发现code可固定为黑产用户A的,同时可结合任意用户B的openid查询到用户B的自定义登录态(猜测开发者后台并未校验code和openid的绑定关系)

黑产可借此制作协议挂,实现修改任意用户的数据

  • 登录态接口禁止泄露

禁止后台直接暴露可通过游戏内uid获取自定义登录态的接口,黑产可遍历uid修改任意用户数据

  • 禁止将openid直接当自定义登录态

同上,黑产可遍历openid来修改任意用户的数据

3.3.3 邀请好友功能

游戏内邀请好友获得奖励的功能容易被外部黑产利用,如图:

针对上述黑产场景,可以考虑

  • 风险用户扫描,针对有可能存在恶意注册、营销作弊的黑产问题

参考安全风控接口指南 | 微信开放文档,针对风险等级>=3的用户,可以采取一些措施避免

  • 开发者后台需要区分微信/其他渠道进入

若没有区分渠道,黑产可通过分享卡片获得分享参数,绕开微信场景,伪造其他场景渠道进入

没有公网IP如何实现内网服务器访问?

发现好久没写博客,准备把我的阿里云服务器降一下配置,因为使用率不高,只是挂一个博客而已,多出来的40GB数据盘可以释放掉了,这样每个月大概可以节省14块钱,虽然不多,但是以年为单位算的话累计起来还是挺多的。

后续打算在家里搭建一个内网服务器,使用FRP内网穿透实现外网访问内网。毕竟阿里云服务器有太多局限性,带宽、云盘、CPU、内存每一个都要单独算钱。

不过也不能完全不用,因为现在三大运营商的宽带都不给你公网IP地址了,没有公网IP地址意味着你无法把内网的服务器提供给外网使用,后续需要搭建服务器或者NAS都很困难。

最近几天打电话给电信说了很多就是不肯给公网IP,说现在开固定IP的宽带要拉专线,一个月700多块钱,我听了快吐血,普通家庭谁用得起一个月700的宽带,这不是变相的捆绑销售吗?

虽然说IPV4地址匮乏,但是也不至于这样来恶心我们。

还好作为一个程序员,这难不倒我,你不给我公网IP,那就另找方法,用阿里云的服务器搭建一个FRP服务就行了,这样远程桌面可以使用XTCP实现点对点通信,也不消耗阿里云的流量。

搭建完成后发现这个远程桌面比向日葵好用太多了还不会限速,还用什么第三方远程软件啊,真是香,后悔没有早点搭建。

接下来就是给家里搭建一台内网服务器+NAS,实现外网正常访问。

CRC循环冗余校验码

什么是CRC校验?CRC即循环冗余校验码:是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。
CRC校验原理:其根本思想就是先在要发送的帧后面附加一个数(这个就是用来校验的校验码,但要注意,这里的数也是二进制序列的,下同),生成一个新帧发送给接收端。当然,这个附加的数不是随意的,它要使所生成的新帧能与发送端和接收端共同选定的某个特定数整除(注意,这里不是直接采用二进制除法,而是采用一种称之为“ 2除法”)。到达接收端后,再把接收到的新帧除以(同样采用“ 2除法”)这个选定的除数。因为在发送端发送数据帧之前就已通过附加一个数,做了“去余”处理(也就已经能整除了),所以结果应该是没有余数。如果有余数,则表明该帧在传输过程中出现了差错。模2除法:模2除法与算术除法类似,但每一位除的结果不影响其它位,即不向上一位借位,所以实际上就是异或。在循环冗余校验码(CRC)的计算中有应用到模2除法。例:

CRC校验步骤:CRC校验中有两个关键点,一是预先确定一个发送送端和接收端都用来作为除数的二进制比特串(或多项式),可以随机选择,也可以使用国际标准,但是最高位和最低位必须为1;二是把原始帧与上面计算出的除数进行模2除法运算,计算出CRC码。
具体步骤:1. 选择合适的除数2. 看选定除数的二进制位数,然后再要发送的数据帧上面加上这个位数-1位的0,然后用新生成的帧以模2除法的方式除上面的除数,得到的余数就是该帧的CRC校验码。注意,余数的位数一定只比除数位数少一位,也就是CRC校验码位数比除数位数少一位,如果前面位是0也不能省略。3. 将计算出来的CRC校验码附加在原数据帧后面,构建成一个新的数据帧进行发送;最后接收端在以模2除法方式除以前面选择的除数,如果没有余数,则说明数据帧在传输的过程中没有出错。
CRC校验码计算示例:现假设选择的 CRC生成多项式为 G( X) = X4 + X3 + 1,要求出二进制序列 10110011的 CRC校验码。下面是具体的计算过程 :①将多项式转化为二进制序列,由 G( X) = X4 + X3 + 1可知二进制一种有五位,第4位、第三位和第零位分别为1,则序列为11001②多项式的位数位5,则在数据帧的后面加上5-1位0,数据帧变为 101100110000,然后使用模2除法除以除数 11001,得到余数。

③将计算出来的CRC校验码添加在原始帧的后面,真正的数据帧为101100110100,再把这个数据帧发送到接收端。④接收端收到数据帧后,用上面选定的除数,用模2除法除去,验证余数是否为0,如果为0,则说明数据帧没有出错。

海明码的编码和校验方法

  海明码(也叫汉明码)具有一位纠错能力。本文以1010110这个二进制数为例解释海明码的编码和校验方法。

  编码

  确定校验码的位数x

  设数据有n位,校验码有x位。则校验码一共有2x种取值方式。其中需要一种取值方式表示数据正确,剩下2x-1种取值方式表示有一位数据出错。因为编码后的二进制串有n+x位,因此x应该满足

2x≥n+x+1   

  使不等式成立的x的最小值就是校验码的位数。在本例中,n=7,解得x=4。

  确定校验码的位置

  校验码在二进制串中的位置为2的整数幂。剩下的位置为数据。如图所示。

位置1234567891011
内容x1x21x3010x4110

  求出校验位的值

  以求x2的值为例。为了直观,将表格中的位置用二进制表示。

位置00010010001101000101011001111000100110101011
内容x1x21x3010x4110

  为了求出x2,要使所有位置的第二位是1的数据(即形如**1*的位置的数据)的异或值为0。即x2^1^1^0^1^0 = 0。因此x2 = 1。

  同理可得x1 = 0, x3 = 1, x4 = 0。

位置00010010001101000101011001111000100110101011
内容01110100110

  因此1010110的海明码为01110100110。

  校验

  假设位置为1011的数据由0变成了1,校验过程为:

  将所有位置形如***1, **1*, *1**, 1***的数据分别异或。

  ***1: 0^1^0^0^1^1 = 1

  **1*: 1^1^1^0^1^1 = 1

  *1**: 1^0^1^0 = 0 

  1***: 0^1^1^1 = 1

  以上四组中,如果一组异或值为1,说明该组中有数据出错了。***1 **1* 1***的异或都为1,说明出错数据的位置为1011。

Unity Shader模板测试用法记录

之前经常忘记模板测试的用法,现在把这些用法记录在博客上免得以后忘记了。

在Shader的Pass开头写Stencil{ }结构体,如果每个Pass都用,则可以提到外面。

Stencil {  
                Ref 2                     //参考值为2,stencilBuffer值默认为0  
                Comp always               //stencil比较方式是永远通过  
                Pass replace              //pass的处理是替换,就是拿2替换buffer 的值  
                ZFail decrWrap            //ZFail的处理是溢出型减1  
            }  

Ref 2:设置参考值。除了2,还可以设置0-255的任意数。

Comp equal:表示通过模板测试的条件。这里只有等于2的像素才算通过测试。除了equal,还有Greater、Less、Always、Never等,类似ZTest。

Pass keep:表示通过模板测试和Z测试(注意是都通过)的像素,怎么处置它的模板值,这里我们保留它的模板值。除了keep,还有Replace,IncrWrap(循环自增1,超过255为0),IncrSat(自增1,超过255还是255),DecrWrap,DecrSat等。

Fail decrWrap:表示没通过模板测试的像素, 怎么处置它的模板值。这里为循环自减。

ZFail keep:表示通过了模板测试但没通过Z测试的像素,怎么处置它的模板值。这里为保持不变。

除此之外,还有ReadMask和WriteMask语法,用来提取Ref值或模板缓存的某几位,默认是255,全部提取。

jenkins打包环境搭建的一些坑点

特色

最近为了提高版本更新的效率,搭建了jenkins环境,因为我们现在比较多的海外版本,更新频率越来越高。

导致我们前端花费了大量的时间在导UI和版本更新上,而且这些基本上都是一些重复性的工作。由于一些历史原因,每次策划的一些UI修改和调整都需要前端去导UI,然后提交SVN,测试和策划更新下来才能看到效果,非常麻烦。

所以特地花了2天的时间去做这件事,其实本身我们的框架导UI和打包都挺方便的,但是现在海外版本多,还是会比较复杂,而且人工操作容易出错。

jenkins相信做游戏的都不陌生,搭建起来其实也比较简单,去官网下载一个安装包即可,但是要适配自己的项目情况,配置一下参数是比较麻烦的,特别是要做一些自动化的操作,比如复制文件自动提交SVN等。

最麻烦的是在写批处理脚本的时候遇到的一些在jenkins上比较蛋疼的问题:

一、调用unity函数打AssetBundle的过程中发现老是会出现AB还没打完jenkins任务就执行完成了,也不是执行失败,就是提前结束了,这样导致C#里面一些自动拷贝文件的函数都没法执行下去,因为UI都没有完全导完。这个问题也是困扰了我很久,因为我特地试过脚本在BAT文件上执行是没问题的,但是就是在jenkins执行的时候出问题,后面实在是没办法就,写了一个循环每隔5秒钟去检测一次导出的LOG判断UI有没有导完,在C#导出完成的时候输入一段完成的LOG信息。

批处理调用U3D导AB的命令如下:start %UNITY_PATH% -projectPath %PROJ_PATH% -quit -batchmode -logFile %LOG_PATH% -executeMethod JenkinsExportManager.ExportHotFixOnlyJenkins

其中%LOG_PATH%即是导UI过程中unity输出的LOG路径(自己定义),在这个命令后执行循环检测:

:begin
@echo off
ping /n 5 127.0.0.1>nul
echo on
echo 正在导出,请稍后!
findstr /i /c:”export end” “%LOG_PATH%” >nul 2>nul && goto end || goto begin
:end

循环查找LOG中的“export end”字符串,如果有则UI导出完成了,可以继续往下执行,不然jenkins是无法判断导出进度的。

二、第2个坑点是我们想Jenkins上导出的UI复制到FTP共享盘上,这样策划和测试都可以用,因为Jenkins是运行在打包机上的,这台机器的配置也比较高,打包和导UI的效率比较高,他们可以使用Jenkins导出UI到FTP上,然后再从FTP复制到本地,查看UI的效果,确定没有问题了再提交SVN,这样既实现了人人可以用,又保证了安全性。

但是就在这个地方又卡了我好久,因为在Jenkins上死活识别不了共享盘的路径,明明已经做了共享盘的映射,但就是识别不了,我直接在CMD上执行是可以识别的,但是一放到Jenkins上就不行了,最后在Jenkins上执行了一次享盘映射的命令:net use X: \172.16.20.35\ftp /y 才识别上,真是奇葩。

三、第3个坑点是在Jenkins上是可以设置参数然后在命令中获取的,但是如果在设置的参数判断里面定义set命令然后给变量赋值是不起作用的,这就很奇怪了。本来是想利用不同的参数值设置不同的路径实现一些自动化操作,结果导致我只能把绝对路径全部写上。

比如在Jenkins上设置了一个region参数用来选择地区,然后在命令上做如下判断:

if "%region%"=="国内" (
   set NEWPATH=C:\ui
)

结果发现变量%NEWPATH%是空的,根本赋值不了,在if外面试可以赋值的。

对于这个问题,暂时还没有找到别的方法,只能是写死绝对路径不在Jenkins参数的判断里面去赋值。

四、第4个坑点和第一个类似,也是命令还没执行完成Jenkins就结束任务了,本来是想在导出UI完成的时候,复制一些文件到某个地方的,但是经常发现文件还没复制完成只复制了部分文件Jenkins就结束任务了。对于这个问题我也只能使用命令:

@echo off
ping /n 10 127.0.0.1>nul
echo on

来达到延迟的效果了,10代表延迟10秒才往后执行。

最终配置效果如下:

从此再也不用我们前端导UI啦!前端是万能的。

Unity中UGUI空格不自动换行处理

在UGUI的Text组件中,文字超出宽度会自动换行,如果有空格则会从空格处自动换行,在英文中这种方式是完全没有问题,但是某些语言比如泰文,是不需要在空格处换行的,那就需要特殊处理了,可以通过使用富文本的方式把空格替换掉,比如使用Replace(” “,”<color=#00000000>.</color>”)的方式把空格替换为一个透明的点。

如果不想使用富文本也可以使用unicode字符“\u00A0”,比如Replace(” “,”\u00A0”)替换我们常用的空格,或者使用全角空格替换半角空格。因为我们常用的空格是半角空格,这三种方式都可以实现替换空格后保留空格的效果并且不在空格处自动换行。

我们项目的发行商最近反馈泰文的换行问题,我暂时是使用的“\u00A0”替换字符的,感觉还不错,没出现其他问题。